wasmer_compiler_cranelift/translator/
func_state.rs

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