use super::{HashMap, Occupied, Vacant};
use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
use cranelift_codegen::ir::{self, Ebb, Inst, Value};
use std::vec::Vec;
#[derive(Debug)]
pub enum ControlStackFrame {
If {
destination: Ebb,
branch_inst: Inst,
num_return_values: usize,
original_stack_size: usize,
exit_is_branched_to: bool,
reachable_from_top: bool,
},
Block {
destination: Ebb,
num_return_values: usize,
original_stack_size: usize,
exit_is_branched_to: bool,
},
Loop {
destination: Ebb,
header: Ebb,
num_return_values: usize,
original_stack_size: usize,
},
}
impl ControlStackFrame {
pub fn num_return_values(&self) -> usize {
match *self {
ControlStackFrame::If {
num_return_values, ..
}
| ControlStackFrame::Block {
num_return_values, ..
}
| ControlStackFrame::Loop {
num_return_values, ..
} => num_return_values,
}
}
pub fn following_code(&self) -> Ebb {
match *self {
ControlStackFrame::If { destination, .. }
| ControlStackFrame::Block { destination, .. }
| ControlStackFrame::Loop { destination, .. } => destination,
}
}
pub fn br_destination(&self) -> Ebb {
match *self {
ControlStackFrame::If { destination, .. }
| ControlStackFrame::Block { destination, .. } => destination,
ControlStackFrame::Loop { header, .. } => header,
}
}
pub fn original_stack_size(&self) -> usize {
match *self {
ControlStackFrame::If {
original_stack_size,
..
}
| ControlStackFrame::Block {
original_stack_size,
..
}
| ControlStackFrame::Loop {
original_stack_size,
..
} => original_stack_size,
}
}
pub fn is_loop(&self) -> bool {
match *self {
ControlStackFrame::If { .. } | ControlStackFrame::Block { .. } => false,
ControlStackFrame::Loop { .. } => true,
}
}
pub fn exit_is_branched_to(&self) -> bool {
match *self {
ControlStackFrame::If {
exit_is_branched_to,
..
}
| ControlStackFrame::Block {
exit_is_branched_to,
..
} => exit_is_branched_to,
ControlStackFrame::Loop { .. } => false,
}
}
pub fn set_branched_to_exit(&mut self) {
match *self {
ControlStackFrame::If {
ref mut exit_is_branched_to,
..
}
| ControlStackFrame::Block {
ref mut exit_is_branched_to,
..
} => *exit_is_branched_to = true,
ControlStackFrame::Loop { .. } => {}
}
}
}
pub struct TranslationState {
pub stack: Vec<Value>,
pub control_stack: Vec<ControlStackFrame>,
pub reachable: bool,
globals: HashMap<GlobalIndex, GlobalVariable>,
heaps: HashMap<MemoryIndex, ir::Heap>,
tables: HashMap<TableIndex, ir::Table>,
signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
}
impl TranslationState {
pub fn new() -> Self {
Self {
stack: Vec::new(),
control_stack: Vec::new(),
reachable: true,
globals: HashMap::new(),
heaps: HashMap::new(),
tables: HashMap::new(),
signatures: HashMap::new(),
functions: HashMap::new(),
}
}
fn clear(&mut self) {
debug_assert!(self.stack.is_empty());
debug_assert!(self.control_stack.is_empty());
self.reachable = true;
self.globals.clear();
self.heaps.clear();
self.tables.clear();
self.signatures.clear();
self.functions.clear();
}
pub fn initialize(&mut self, sig: &ir::Signature, exit_block: Ebb) {
self.clear();
self.push_block(
exit_block,
sig.returns
.iter()
.filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
.count(),
);
}
pub fn push1(&mut self, val: Value) {
self.stack.push(val);
}
pub fn pushn(&mut self, vals: &[Value]) {
self.stack.extend_from_slice(vals);
}
pub fn pop1(&mut self) -> Value {
self.stack.pop().unwrap()
}
pub fn peek1(&self) -> Value {
*self.stack.last().unwrap()
}
pub fn pop2(&mut self) -> (Value, Value) {
let v2 = self.stack.pop().unwrap();
let v1 = self.stack.pop().unwrap();
(v1, v2)
}
pub fn pop3(&mut self) -> (Value, Value, Value) {
let v3 = self.stack.pop().unwrap();
let v2 = self.stack.pop().unwrap();
let v1 = self.stack.pop().unwrap();
(v1, v2, v3)
}
pub fn popn(&mut self, n: usize) {
let new_len = self.stack.len() - n;
self.stack.truncate(new_len);
}
pub fn peekn(&self, n: usize) -> &[Value] {
&self.stack[self.stack.len() - n..]
}
pub fn push_block(&mut self, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::Block {
destination: following_code,
original_stack_size: self.stack.len(),
num_return_values: num_result_types,
exit_is_branched_to: false,
});
}
pub fn push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::Loop {
header,
destination: following_code,
original_stack_size: self.stack.len(),
num_return_values: num_result_types,
});
}
pub fn push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize) {
self.control_stack.push(ControlStackFrame::If {
branch_inst,
destination: following_code,
original_stack_size: self.stack.len(),
num_return_values: num_result_types,
exit_is_branched_to: false,
reachable_from_top: self.reachable,
});
}
}
impl TranslationState {
pub fn get_global<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<GlobalVariable> {
let index = GlobalIndex::from_u32(index);
match self.globals.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
}
}
pub fn get_heap<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<ir::Heap> {
let index = MemoryIndex::from_u32(index);
match self.heaps.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
}
}
pub fn get_table<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<ir::Table> {
let index = TableIndex::from_u32(index);
match self.tables.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)),
}
}
pub fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<(ir::SigRef, usize)> {
let index = SignatureIndex::from_u32(index);
match self.signatures.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => {
let sig = environ.make_indirect_sig(func, index)?;
Ok(*entry.insert((sig, normal_args(&func.dfg.signatures[sig]))))
}
}
}
pub fn get_direct_func<FE: FuncEnvironment + ?Sized>(
&mut self,
func: &mut ir::Function,
index: u32,
environ: &mut FE,
) -> WasmResult<(ir::FuncRef, usize)> {
let index = FuncIndex::from_u32(index);
match self.functions.entry(index) {
Occupied(entry) => Ok(*entry.get()),
Vacant(entry) => {
let fref = environ.make_direct_func(func, index)?;
let sig = func.dfg.ext_funcs[fref].signature;
Ok(*entry.insert((fref, normal_args(&func.dfg.signatures[sig]))))
}
}
}
}
fn normal_args(sig: &ir::Signature) -> usize {
sig.params
.iter()
.filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
.count()
}