wasmtime_cranelift/translate/
state.rs

1//! WebAssembly module and function translation state.
2//!
3//! The `FuncTranslationState` 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
6use crate::func_environ::FuncEnvironment;
7use crate::translate::environ::GlobalVariable;
8use crate::translate::Heap;
9use cranelift_codegen::ir::{self, Block, Inst, Value};
10use std::collections::hash_map::{Entry::Occupied, Entry::Vacant, HashMap};
11use std::vec::Vec;
12use wasmtime_environ::{FuncIndex, GlobalIndex, MemoryIndex, TypeIndex, WasmResult};
13
14/// Information about the presence of an associated `else` for an `if`, or the
15/// lack thereof.
16#[derive(Debug)]
17pub enum ElseData {
18    /// The `if` does not already have an `else` block.
19    ///
20    /// This doesn't mean that it will never have an `else`, just that we
21    /// haven't seen it yet.
22    NoElse {
23        /// If we discover that we need an `else` block, this is the jump
24        /// instruction that needs to be fixed up to point to the new `else`
25        /// block rather than the destination block after the `if...end`.
26        branch_inst: Inst,
27
28        /// The placeholder block we're replacing.
29        placeholder: Block,
30    },
31
32    /// We have already allocated an `else` block.
33    ///
34    /// Usually we don't know whether we will hit an `if .. end` or an `if
35    /// .. else .. end`, but sometimes we can tell based on the block's type
36    /// signature that the signature is not valid if there isn't an `else`. In
37    /// these cases, we pre-allocate the `else` block.
38    WithElse {
39        /// This is the `else` block.
40        else_block: Block,
41    },
42}
43
44/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
45/// fields:
46///
47/// - `destination`: reference to the `Block` that will hold the code after the control block;
48/// - `num_return_values`: number of values returned by the control block;
49/// - `original_stack_size`: size of the value stack at the beginning of the control block.
50///
51/// The `loop` frame has a `header` field that references the `Block` that contains the beginning
52/// of the body of the loop.
53#[derive(Debug)]
54pub enum ControlStackFrame {
55    If {
56        destination: Block,
57        else_data: ElseData,
58        num_param_values: usize,
59        num_return_values: usize,
60        original_stack_size: usize,
61        exit_is_branched_to: bool,
62        blocktype: wasmparser::BlockType,
63        /// Was the head of the `if` reachable?
64        head_is_reachable: bool,
65        /// What was the reachability at the end of the consequent?
66        ///
67        /// This is `None` until we're finished translating the consequent, and
68        /// is set to `Some` either by hitting an `else` when we will begin
69        /// translating the alternative, or by hitting an `end` in which case
70        /// there is no alternative.
71        consequent_ends_reachable: Option<bool>,
72        // Note: no need for `alternative_ends_reachable` because that is just
73        // `state.reachable` when we hit the `end` in the `if .. else .. end`.
74    },
75    Block {
76        destination: Block,
77        num_param_values: usize,
78        num_return_values: usize,
79        original_stack_size: usize,
80        exit_is_branched_to: bool,
81    },
82    Loop {
83        destination: Block,
84        header: Block,
85        num_param_values: usize,
86        num_return_values: usize,
87        original_stack_size: usize,
88    },
89}
90
91/// Helper methods for the control stack objects.
92impl ControlStackFrame {
93    pub fn num_return_values(&self) -> usize {
94        match *self {
95            Self::If {
96                num_return_values, ..
97            }
98            | Self::Block {
99                num_return_values, ..
100            }
101            | Self::Loop {
102                num_return_values, ..
103            } => num_return_values,
104        }
105    }
106    pub fn num_param_values(&self) -> usize {
107        match *self {
108            Self::If {
109                num_param_values, ..
110            }
111            | Self::Block {
112                num_param_values, ..
113            }
114            | Self::Loop {
115                num_param_values, ..
116            } => num_param_values,
117        }
118    }
119    pub fn following_code(&self) -> Block {
120        match *self {
121            Self::If { destination, .. }
122            | Self::Block { destination, .. }
123            | Self::Loop { destination, .. } => destination,
124        }
125    }
126    pub fn br_destination(&self) -> Block {
127        match *self {
128            Self::If { destination, .. } | Self::Block { destination, .. } => destination,
129            Self::Loop { header, .. } => header,
130        }
131    }
132    /// Private helper. Use `truncate_value_stack_to_else_params()` or
133    /// `truncate_value_stack_to_original_size()` to restore value-stack state.
134    fn original_stack_size(&self) -> usize {
135        match *self {
136            Self::If {
137                original_stack_size,
138                ..
139            }
140            | Self::Block {
141                original_stack_size,
142                ..
143            }
144            | Self::Loop {
145                original_stack_size,
146                ..
147            } => original_stack_size,
148        }
149    }
150    pub fn is_loop(&self) -> bool {
151        match *self {
152            Self::If { .. } | Self::Block { .. } => false,
153            Self::Loop { .. } => true,
154        }
155    }
156
157    pub fn exit_is_branched_to(&self) -> bool {
158        match *self {
159            Self::If {
160                exit_is_branched_to,
161                ..
162            }
163            | Self::Block {
164                exit_is_branched_to,
165                ..
166            } => exit_is_branched_to,
167            Self::Loop { .. } => false,
168        }
169    }
170
171    pub fn set_branched_to_exit(&mut self) {
172        match *self {
173            Self::If {
174                ref mut exit_is_branched_to,
175                ..
176            }
177            | Self::Block {
178                ref mut exit_is_branched_to,
179                ..
180            } => *exit_is_branched_to = true,
181            Self::Loop { .. } => {}
182        }
183    }
184
185    /// Pop values from the value stack so that it is left at the
186    /// input-parameters to an else-block.
187    pub fn truncate_value_stack_to_else_params(&self, stack: &mut Vec<Value>) {
188        debug_assert!(matches!(self, &ControlStackFrame::If { .. }));
189        stack.truncate(self.original_stack_size());
190    }
191
192    /// Pop values from the value stack so that it is left at the state it was
193    /// before this control-flow frame.
194    pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec<Value>) {
195        // The "If" frame pushes its parameters twice, so they're available to the else block
196        // (see also `FuncTranslationState::push_if`).
197        // Yet, the original_stack_size member accounts for them only once, so that the else
198        // block can see the same number of parameters as the consequent block. As a matter of
199        // fact, we need to subtract an extra number of parameter values for if blocks.
200        let num_duplicated_params = match self {
201            &ControlStackFrame::If {
202                num_param_values, ..
203            } => {
204                debug_assert!(num_param_values <= self.original_stack_size());
205                num_param_values
206            }
207            _ => 0,
208        };
209        stack.truncate(self.original_stack_size() - num_duplicated_params);
210    }
211}
212
213/// Contains information passed along during a function's translation and that records:
214///
215/// - The current value and control stacks.
216/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
217///   unreachable code;
218pub struct FuncTranslationState {
219    /// A stack of values corresponding to the active values in the input wasm function at this
220    /// point.
221    pub(crate) stack: Vec<Value>,
222    /// A stack of active control flow operations at this point in the input wasm function.
223    pub(crate) control_stack: Vec<ControlStackFrame>,
224    /// Is the current translation state still reachable? This is false when translating operators
225    /// like End, Return, or Unreachable.
226    pub(crate) reachable: bool,
227
228    // Map of global variables that have already been created by `FuncEnvironment::make_global`.
229    globals: HashMap<GlobalIndex, GlobalVariable>,
230
231    // Map of heaps that have been created by `FuncEnvironment::make_heap`.
232    memory_to_heap: HashMap<MemoryIndex, Heap>,
233
234    // Map of indirect call signatures that have been created by
235    // `FuncEnvironment::make_indirect_sig()`.
236    // Stores both the signature reference and the number of WebAssembly arguments
237    signatures: HashMap<TypeIndex, (ir::SigRef, usize)>,
238
239    // Imported and local functions that have been created by
240    // `FuncEnvironment::make_direct_func()`.
241    // Stores both the function reference and the number of WebAssembly arguments
242    functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
243}
244
245// Public methods that are exposed to non- API consumers.
246impl FuncTranslationState {
247    /// True if the current translation state expresses reachable code, false if it is unreachable.
248    #[inline]
249    pub fn reachable(&self) -> bool {
250        self.reachable
251    }
252}
253
254impl FuncTranslationState {
255    /// Construct a new, empty, `FuncTranslationState`
256    pub(crate) fn new() -> Self {
257        Self {
258            stack: Vec::new(),
259            control_stack: Vec::new(),
260            reachable: true,
261            globals: HashMap::new(),
262            memory_to_heap: HashMap::new(),
263            signatures: HashMap::new(),
264            functions: HashMap::new(),
265        }
266    }
267
268    fn clear(&mut self) {
269        debug_assert!(self.stack.is_empty());
270        debug_assert!(self.control_stack.is_empty());
271        self.reachable = true;
272        self.globals.clear();
273        self.memory_to_heap.clear();
274        self.signatures.clear();
275        self.functions.clear();
276    }
277
278    /// Initialize the state for compiling a function with the given signature.
279    ///
280    /// This resets the state to containing only a single block representing the whole function.
281    /// The exit block is the last block in the function which will contain the return instruction.
282    pub(crate) fn initialize(&mut self, sig: &ir::Signature, exit_block: Block) {
283        self.clear();
284        self.push_block(
285            exit_block,
286            0,
287            sig.returns
288                .iter()
289                .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
290                .count(),
291        );
292    }
293
294    /// Push a value.
295    pub(crate) fn push1(&mut self, val: Value) {
296        self.stack.push(val);
297    }
298
299    /// Push two values.
300    pub(crate) fn push2(&mut self, val1: Value, val2: Value) {
301        self.stack.push(val1);
302        self.stack.push(val2);
303    }
304
305    /// Push multiple values.
306    pub(crate) fn pushn(&mut self, vals: &[Value]) {
307        self.stack.extend_from_slice(vals);
308    }
309
310    /// Pop one value.
311    pub(crate) fn pop1(&mut self) -> Value {
312        self.stack
313            .pop()
314            .expect("attempted to pop a value from an empty stack")
315    }
316
317    /// Peek at the top of the stack without popping it.
318    pub(crate) fn peek1(&self) -> Value {
319        *self
320            .stack
321            .last()
322            .expect("attempted to peek at a value on an empty stack")
323    }
324
325    /// Pop two values. Return them in the order they were pushed.
326    pub(crate) fn pop2(&mut self) -> (Value, Value) {
327        let v2 = self.stack.pop().unwrap();
328        let v1 = self.stack.pop().unwrap();
329        (v1, v2)
330    }
331
332    /// Pop three values. Return them in the order they were pushed.
333    pub(crate) fn pop3(&mut self) -> (Value, Value, Value) {
334        let v3 = self.stack.pop().unwrap();
335        let v2 = self.stack.pop().unwrap();
336        let v1 = self.stack.pop().unwrap();
337        (v1, v2, v3)
338    }
339
340    /// Pop four values. Return them in the order they were pushed.
341    pub(crate) fn pop4(&mut self) -> (Value, Value, Value, Value) {
342        let v4 = self.stack.pop().unwrap();
343        let v3 = self.stack.pop().unwrap();
344        let v2 = self.stack.pop().unwrap();
345        let v1 = self.stack.pop().unwrap();
346        (v1, v2, v3, v4)
347    }
348
349    /// Pop five values. Return them in the order they were pushed.
350    pub(crate) fn pop5(&mut self) -> (Value, Value, Value, Value, Value) {
351        let v5 = self.stack.pop().unwrap();
352        let v4 = self.stack.pop().unwrap();
353        let v3 = self.stack.pop().unwrap();
354        let v2 = self.stack.pop().unwrap();
355        let v1 = self.stack.pop().unwrap();
356        (v1, v2, v3, v4, v5)
357    }
358
359    /// Helper to ensure the stack size is at least as big as `n`; note that due to
360    /// `debug_assert` this will not execute in non-optimized builds.
361    #[inline]
362    fn ensure_length_is_at_least(&self, n: usize) {
363        debug_assert!(
364            n <= self.stack.len(),
365            "attempted to access {} values but stack only has {} values",
366            n,
367            self.stack.len()
368        )
369    }
370
371    /// Pop the top `n` values on the stack.
372    ///
373    /// The popped values are not returned. Use `peekn` to look at them before popping.
374    pub(crate) fn popn(&mut self, n: usize) {
375        self.ensure_length_is_at_least(n);
376        let new_len = self.stack.len() - n;
377        self.stack.truncate(new_len);
378    }
379
380    /// Peek at the top `n` values on the stack in the order they were pushed.
381    pub(crate) fn peekn(&self, n: usize) -> &[Value] {
382        self.ensure_length_is_at_least(n);
383        &self.stack[self.stack.len() - n..]
384    }
385
386    /// Peek at the top `n` values on the stack in the order they were pushed.
387    pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] {
388        self.ensure_length_is_at_least(n);
389        let len = self.stack.len();
390        &mut self.stack[len - n..]
391    }
392
393    /// Push a block on the control stack.
394    pub(crate) fn push_block(
395        &mut self,
396        following_code: Block,
397        num_param_types: usize,
398        num_result_types: usize,
399    ) {
400        debug_assert!(num_param_types <= self.stack.len());
401        self.control_stack.push(ControlStackFrame::Block {
402            destination: following_code,
403            original_stack_size: self.stack.len() - num_param_types,
404            num_param_values: num_param_types,
405            num_return_values: num_result_types,
406            exit_is_branched_to: false,
407        });
408    }
409
410    /// Push a loop on the control stack.
411    pub(crate) fn push_loop(
412        &mut self,
413        header: Block,
414        following_code: Block,
415        num_param_types: usize,
416        num_result_types: usize,
417    ) {
418        debug_assert!(num_param_types <= self.stack.len());
419        self.control_stack.push(ControlStackFrame::Loop {
420            header,
421            destination: following_code,
422            original_stack_size: self.stack.len() - num_param_types,
423            num_param_values: num_param_types,
424            num_return_values: num_result_types,
425        });
426    }
427
428    /// Push an if on the control stack.
429    pub(crate) fn push_if(
430        &mut self,
431        destination: Block,
432        else_data: ElseData,
433        num_param_types: usize,
434        num_result_types: usize,
435        blocktype: wasmparser::BlockType,
436    ) {
437        debug_assert!(num_param_types <= self.stack.len());
438
439        // Push a second copy of our `if`'s parameters on the stack. This lets
440        // us avoid saving them on the side in the `ControlStackFrame` for our
441        // `else` block (if it exists), which would require a second heap
442        // allocation. See also the comment in `translate_operator` for
443        // `Operator::Else`.
444        self.stack.reserve(num_param_types);
445        for i in (self.stack.len() - num_param_types)..self.stack.len() {
446            let val = self.stack[i];
447            self.stack.push(val);
448        }
449
450        self.control_stack.push(ControlStackFrame::If {
451            destination,
452            else_data,
453            original_stack_size: self.stack.len() - num_param_types,
454            num_param_values: num_param_types,
455            num_return_values: num_result_types,
456            exit_is_branched_to: false,
457            head_is_reachable: self.reachable,
458            consequent_ends_reachable: None,
459            blocktype,
460        });
461    }
462}
463
464/// Methods for handling entity references.
465impl FuncTranslationState {
466    /// Get the `GlobalVariable` reference that should be used to access the global variable
467    /// `index`. Create the reference if necessary.
468    /// Also return the WebAssembly type of the global.
469    pub(crate) fn get_global(
470        &mut self,
471        func: &mut ir::Function,
472        index: u32,
473        environ: &mut FuncEnvironment<'_>,
474    ) -> WasmResult<GlobalVariable> {
475        let index = GlobalIndex::from_u32(index);
476        match self.globals.entry(index) {
477            Occupied(entry) => Ok(*entry.get()),
478            Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
479        }
480    }
481
482    /// Get the `Heap` reference that should be used to access linear memory `index`.
483    /// Create the reference if necessary.
484    pub(crate) fn get_heap(
485        &mut self,
486        func: &mut ir::Function,
487        index: u32,
488        environ: &mut FuncEnvironment<'_>,
489    ) -> WasmResult<Heap> {
490        let index = MemoryIndex::from_u32(index);
491        match self.memory_to_heap.entry(index) {
492            Occupied(entry) => Ok(*entry.get()),
493            Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
494        }
495    }
496
497    /// Get the `SigRef` reference that should be used to make an indirect call with signature
498    /// `index`. Also return the number of WebAssembly arguments in the signature.
499    ///
500    /// Create the signature if necessary.
501    pub(crate) fn get_indirect_sig(
502        &mut self,
503        func: &mut ir::Function,
504        index: u32,
505        environ: &mut FuncEnvironment<'_>,
506    ) -> WasmResult<(ir::SigRef, usize)> {
507        let index = TypeIndex::from_u32(index);
508        match self.signatures.entry(index) {
509            Occupied(entry) => Ok(*entry.get()),
510            Vacant(entry) => {
511                let sig = environ.make_indirect_sig(func, index)?;
512                Ok(*entry.insert((sig, num_wasm_parameters(environ, &func.dfg.signatures[sig]))))
513            }
514        }
515    }
516
517    /// Get the `FuncRef` reference that should be used to make a direct call to function
518    /// `index`. Also return the number of WebAssembly arguments in the signature.
519    ///
520    /// Create the function reference if necessary.
521    pub(crate) fn get_direct_func(
522        &mut self,
523        func: &mut ir::Function,
524        index: u32,
525        environ: &mut FuncEnvironment<'_>,
526    ) -> WasmResult<(ir::FuncRef, usize)> {
527        let index = FuncIndex::from_u32(index);
528        match self.functions.entry(index) {
529            Occupied(entry) => Ok(*entry.get()),
530            Vacant(entry) => {
531                let fref = environ.make_direct_func(func, index)?;
532                let sig = func.dfg.ext_funcs[fref].signature;
533                Ok(*entry.insert((
534                    fref,
535                    num_wasm_parameters(environ, &func.dfg.signatures[sig]),
536                )))
537            }
538        }
539    }
540}
541
542fn num_wasm_parameters(environ: &FuncEnvironment<'_>, signature: &ir::Signature) -> usize {
543    (0..signature.params.len())
544        .filter(|index| environ.is_wasm_parameter(signature, *index))
545        .count()
546}