1 //! WebAssembly function translation state.
2 //!
3 //! The `TranslationState` struct defined in this module is used to keep track of the WebAssembly
4 //! value and control stacks during the translation of a single function.
5 
6 use super::{HashMap, Occupied, Vacant};
7 use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
8 use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
9 use cranelift_codegen::ir::{self, Ebb, Inst, Value};
10 use std::vec::Vec;
11 
12 /// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
13 /// fields:
14 ///
15 /// - `destination`: reference to the `Ebb` that will hold the code after the control block;
16 /// - `num_return_values`: number of values returned by the control block;
17 /// - `original_stack_size`: size of the value stack at the beginning of the control block.
18 ///
19 /// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
20 /// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
21 /// the `Ebb` that contains the beginning of the body of the loop.
22 #[derive(Debug)]
23 pub enum ControlStackFrame {
24     If {
25         destination: Ebb,
26         branch_inst: Inst,
27         num_return_values: usize,
28         original_stack_size: usize,
29         exit_is_branched_to: bool,
30         reachable_from_top: bool,
31     },
32     Block {
33         destination: Ebb,
34         num_return_values: usize,
35         original_stack_size: usize,
36         exit_is_branched_to: bool,
37     },
38     Loop {
39         destination: Ebb,
40         header: Ebb,
41         num_return_values: usize,
42         original_stack_size: usize,
43     },
44 }
45 
46 /// Helper methods for the control stack objects.
47 impl ControlStackFrame {
num_return_values(&self) -> usize48     pub fn num_return_values(&self) -> usize {
49         match *self {
50             ControlStackFrame::If {
51                 num_return_values, ..
52             }
53             | ControlStackFrame::Block {
54                 num_return_values, ..
55             }
56             | ControlStackFrame::Loop {
57                 num_return_values, ..
58             } => num_return_values,
59         }
60     }
following_code(&self) -> Ebb61     pub fn following_code(&self) -> Ebb {
62         match *self {
63             ControlStackFrame::If { destination, .. }
64             | ControlStackFrame::Block { destination, .. }
65             | ControlStackFrame::Loop { destination, .. } => destination,
66         }
67     }
br_destination(&self) -> Ebb68     pub fn br_destination(&self) -> Ebb {
69         match *self {
70             ControlStackFrame::If { destination, .. }
71             | ControlStackFrame::Block { destination, .. } => destination,
72             ControlStackFrame::Loop { header, .. } => header,
73         }
74     }
original_stack_size(&self) -> usize75     pub fn original_stack_size(&self) -> usize {
76         match *self {
77             ControlStackFrame::If {
78                 original_stack_size,
79                 ..
80             }
81             | ControlStackFrame::Block {
82                 original_stack_size,
83                 ..
84             }
85             | ControlStackFrame::Loop {
86                 original_stack_size,
87                 ..
88             } => original_stack_size,
89         }
90     }
is_loop(&self) -> bool91     pub fn is_loop(&self) -> bool {
92         match *self {
93             ControlStackFrame::If { .. } | ControlStackFrame::Block { .. } => false,
94             ControlStackFrame::Loop { .. } => true,
95         }
96     }
97 
exit_is_branched_to(&self) -> bool98     pub fn exit_is_branched_to(&self) -> bool {
99         match *self {
100             ControlStackFrame::If {
101                 exit_is_branched_to,
102                 ..
103             }
104             | ControlStackFrame::Block {
105                 exit_is_branched_to,
106                 ..
107             } => exit_is_branched_to,
108             ControlStackFrame::Loop { .. } => false,
109         }
110     }
111 
set_branched_to_exit(&mut self)112     pub fn set_branched_to_exit(&mut self) {
113         match *self {
114             ControlStackFrame::If {
115                 ref mut exit_is_branched_to,
116                 ..
117             }
118             | ControlStackFrame::Block {
119                 ref mut exit_is_branched_to,
120                 ..
121             } => *exit_is_branched_to = true,
122             ControlStackFrame::Loop { .. } => {}
123         }
124     }
125 }
126 
127 /// VisibleTranslationState wraps a TranslationState with an interface appropriate for users
128 /// outside this `cranelift-wasm`.
129 ///
130 /// VisibleTranslationState is currently very minimal (only exposing reachability information), but
131 /// is anticipated to grow in the future, with functions to inspect or modify the wasm operand
132 /// stack for example.
133 pub struct VisibleTranslationState<'a> {
134     state: &'a TranslationState,
135 }
136 
137 impl<'a> VisibleTranslationState<'a> {
138     /// Build a VisibleTranslationState from an existing TranslationState
new(state: &'a TranslationState) -> Self139     pub fn new(state: &'a TranslationState) -> Self {
140         VisibleTranslationState { state }
141     }
142 
143     /// True if the current translation state expresses reachable code, false if it is unreachable
reachable(&self) -> bool144     pub fn reachable(&self) -> bool {
145         self.state.reachable
146     }
147 }
148 
149 /// Contains information passed along during the translation and that records:
150 ///
151 /// - The current value and control stacks.
152 /// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
153 ///   unreachable code;
154 pub struct TranslationState {
155     /// A stack of values corresponding to the active values in the input wasm function at this
156     /// point.
157     pub stack: Vec<Value>,
158     /// A stack of active control flow operations at this point in the input wasm function.
159     pub control_stack: Vec<ControlStackFrame>,
160     /// Is the current translation state still reachable? This is false when translating operators
161     /// like End, Return, or Unreachable.
162     pub reachable: bool,
163 
164     // Map of global variables that have already been created by `FuncEnvironment::make_global`.
165     globals: HashMap<GlobalIndex, GlobalVariable>,
166 
167     // Map of heaps that have been created by `FuncEnvironment::make_heap`.
168     heaps: HashMap<MemoryIndex, ir::Heap>,
169 
170     // Map of tables that have been created by `FuncEnvironment::make_table`.
171     tables: HashMap<TableIndex, ir::Table>,
172 
173     // Map of indirect call signatures that have been created by
174     // `FuncEnvironment::make_indirect_sig()`.
175     // Stores both the signature reference and the number of WebAssembly arguments
176     signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
177 
178     // Imported and local functions that have been created by
179     // `FuncEnvironment::make_direct_func()`.
180     // Stores both the function reference and the number of WebAssembly arguments
181     functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
182 }
183 
184 impl TranslationState {
185     /// Construct a new, empty, `TranslationState`
new() -> Self186     pub fn new() -> Self {
187         Self {
188             stack: Vec::new(),
189             control_stack: Vec::new(),
190             reachable: true,
191             globals: HashMap::new(),
192             heaps: HashMap::new(),
193             tables: HashMap::new(),
194             signatures: HashMap::new(),
195             functions: HashMap::new(),
196         }
197     }
198 
clear(&mut self)199     fn clear(&mut self) {
200         debug_assert!(self.stack.is_empty());
201         debug_assert!(self.control_stack.is_empty());
202         self.reachable = true;
203         self.globals.clear();
204         self.heaps.clear();
205         self.tables.clear();
206         self.signatures.clear();
207         self.functions.clear();
208     }
209 
210     /// Initialize the state for compiling a function with the given signature.
211     ///
212     /// This resets the state to containing only a single block representing the whole function.
213     /// The exit block is the last block in the function which will contain the return instruction.
initialize(&mut self, sig: &ir::Signature, exit_block: Ebb)214     pub fn initialize(&mut self, sig: &ir::Signature, exit_block: Ebb) {
215         self.clear();
216         self.push_block(
217             exit_block,
218             sig.returns
219                 .iter()
220                 .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
221                 .count(),
222         );
223     }
224 
225     /// Push a value.
push1(&mut self, val: Value)226     pub fn push1(&mut self, val: Value) {
227         self.stack.push(val);
228     }
229 
230     /// Push multiple values.
pushn(&mut self, vals: &[Value])231     pub fn pushn(&mut self, vals: &[Value]) {
232         self.stack.extend_from_slice(vals);
233     }
234 
235     /// Pop one value.
pop1(&mut self) -> Value236     pub fn pop1(&mut self) -> Value {
237         self.stack.pop().unwrap()
238     }
239 
240     /// Peek at the top of the stack without popping it.
peek1(&self) -> Value241     pub fn peek1(&self) -> Value {
242         *self.stack.last().unwrap()
243     }
244 
245     /// Pop two values. Return them in the order they were pushed.
pop2(&mut self) -> (Value, Value)246     pub fn pop2(&mut self) -> (Value, Value) {
247         let v2 = self.stack.pop().unwrap();
248         let v1 = self.stack.pop().unwrap();
249         (v1, v2)
250     }
251 
252     /// Pop three values. Return them in the order they were pushed.
pop3(&mut self) -> (Value, Value, Value)253     pub fn pop3(&mut self) -> (Value, Value, Value) {
254         let v3 = self.stack.pop().unwrap();
255         let v2 = self.stack.pop().unwrap();
256         let v1 = self.stack.pop().unwrap();
257         (v1, v2, v3)
258     }
259 
260     /// Pop the top `n` values on the stack.
261     ///
262     /// The popped values are not returned. Use `peekn` to look at them before popping.
popn(&mut self, n: usize)263     pub fn popn(&mut self, n: usize) {
264         let new_len = self.stack.len() - n;
265         self.stack.truncate(new_len);
266     }
267 
268     /// Peek at the top `n` values on the stack in the order they were pushed.
peekn(&self, n: usize) -> &[Value]269     pub fn peekn(&self, n: usize) -> &[Value] {
270         &self.stack[self.stack.len() - n..]
271     }
272 
273     /// Push a block on the control stack.
push_block(&mut self, following_code: Ebb, num_result_types: usize)274     pub fn push_block(&mut self, following_code: Ebb, num_result_types: usize) {
275         self.control_stack.push(ControlStackFrame::Block {
276             destination: following_code,
277             original_stack_size: self.stack.len(),
278             num_return_values: num_result_types,
279             exit_is_branched_to: false,
280         });
281     }
282 
283     /// Push a loop on the control stack.
push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize)284     pub fn push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize) {
285         self.control_stack.push(ControlStackFrame::Loop {
286             header,
287             destination: following_code,
288             original_stack_size: self.stack.len(),
289             num_return_values: num_result_types,
290         });
291     }
292 
293     /// Push an if on the control stack.
push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize)294     pub fn push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize) {
295         self.control_stack.push(ControlStackFrame::If {
296             branch_inst,
297             destination: following_code,
298             original_stack_size: self.stack.len(),
299             num_return_values: num_result_types,
300             exit_is_branched_to: false,
301             reachable_from_top: self.reachable,
302         });
303     }
304 }
305 
306 /// Methods for handling entity references.
307 impl TranslationState {
308     /// Get the `GlobalVariable` reference that should be used to access the global variable
309     /// `index`. Create the reference if necessary.
310     /// Also return the WebAssembly type of the global.
get_global<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<GlobalVariable>311     pub fn get_global<FE: FuncEnvironment + ?Sized>(
312         &mut self,
313         func: &mut ir::Function,
314         index: u32,
315         environ: &mut FE,
316     ) -> WasmResult<GlobalVariable> {
317         let index = GlobalIndex::from_u32(index);
318         match self.globals.entry(index) {
319             Occupied(entry) => Ok(*entry.get()),
320             Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
321         }
322     }
323 
324     /// Get the `Heap` reference that should be used to access linear memory `index`.
325     /// Create the reference if necessary.
get_heap<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<ir::Heap>326     pub fn get_heap<FE: FuncEnvironment + ?Sized>(
327         &mut self,
328         func: &mut ir::Function,
329         index: u32,
330         environ: &mut FE,
331     ) -> WasmResult<ir::Heap> {
332         let index = MemoryIndex::from_u32(index);
333         match self.heaps.entry(index) {
334             Occupied(entry) => Ok(*entry.get()),
335             Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
336         }
337     }
338 
339     /// Get the `Table` reference that should be used to access table `index`.
340     /// Create the reference if necessary.
get_table<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<ir::Table>341     pub fn get_table<FE: FuncEnvironment + ?Sized>(
342         &mut self,
343         func: &mut ir::Function,
344         index: u32,
345         environ: &mut FE,
346     ) -> WasmResult<ir::Table> {
347         let index = TableIndex::from_u32(index);
348         match self.tables.entry(index) {
349             Occupied(entry) => Ok(*entry.get()),
350             Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)),
351         }
352     }
353 
354     /// Get the `SigRef` reference that should be used to make an indirect call with signature
355     /// `index`. Also return the number of WebAssembly arguments in the signature.
356     ///
357     /// Create the signature if necessary.
get_indirect_sig<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<(ir::SigRef, usize)>358     pub fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
359         &mut self,
360         func: &mut ir::Function,
361         index: u32,
362         environ: &mut FE,
363     ) -> WasmResult<(ir::SigRef, usize)> {
364         let index = SignatureIndex::from_u32(index);
365         match self.signatures.entry(index) {
366             Occupied(entry) => Ok(*entry.get()),
367             Vacant(entry) => {
368                 let sig = environ.make_indirect_sig(func, index)?;
369                 Ok(*entry.insert((sig, normal_args(&func.dfg.signatures[sig]))))
370             }
371         }
372     }
373 
374     /// Get the `FuncRef` reference that should be used to make a direct call to function
375     /// `index`. Also return the number of WebAssembly arguments in the signature.
376     ///
377     /// Create the function reference if necessary.
get_direct_func<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<(ir::FuncRef, usize)>378     pub fn get_direct_func<FE: FuncEnvironment + ?Sized>(
379         &mut self,
380         func: &mut ir::Function,
381         index: u32,
382         environ: &mut FE,
383     ) -> WasmResult<(ir::FuncRef, usize)> {
384         let index = FuncIndex::from_u32(index);
385         match self.functions.entry(index) {
386             Occupied(entry) => Ok(*entry.get()),
387             Vacant(entry) => {
388                 let fref = environ.make_direct_func(func, index)?;
389                 let sig = func.dfg.ext_funcs[fref].signature;
390                 Ok(*entry.insert((fref, normal_args(&func.dfg.signatures[sig]))))
391             }
392         }
393     }
394 }
395 
396 /// Count the number of normal parameters in a signature.
397 /// Exclude special-purpose parameters that represent runtime stuff and not WebAssembly arguments.
normal_args(sig: &ir::Signature) -> usize398 fn normal_args(sig: &ir::Signature) -> usize {
399     sig.params
400         .iter()
401         .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
402         .count()
403 }
404