Skip to main content

wasmi/engine/translator/func/stack/
mod.rs

1mod control;
2mod locals;
3mod operand;
4mod operands;
5
6use self::{
7    control::ControlStack,
8    locals::LocalsHead,
9    operands::{OperandStack, StackOperand},
10};
11pub use self::{
12    control::{
13        AcquiredTarget,
14        BlockControlFrame,
15        ControlFrame,
16        ControlFrameBase,
17        ControlFrameKind,
18        ElseControlFrame,
19        ElseReachability,
20        IfControlFrame,
21        IfReachability,
22        LoopControlFrame,
23    },
24    operand::{ImmediateOperand, Operand, TempOperand},
25    operands::{OperandIdx, PreservedAllLocalsIter, PreservedLocalsIter},
26};
27use super::{Reset, ReusableAllocations};
28use crate::{
29    core::TypedVal,
30    engine::{
31        translator::{
32            func::{stack::operands::PeekedOperands, LocalIdx},
33            labels::LabelRef,
34            utils::Instr,
35        },
36        BlockType,
37    },
38    Engine,
39    Error,
40    ValType,
41};
42use alloc::vec::Vec;
43
44#[cfg(doc)]
45use crate::ir::Op;
46
47/// The Wasm value stack during translation from Wasm to Wasmi bytecode.
48#[derive(Debug)]
49pub struct Stack {
50    /// The underlying [`Engine`].
51    engine: Engine,
52    /// The Wasm value stack.
53    operands: OperandStack,
54    /// The Wasm control stack.
55    controls: ControlStack,
56}
57
58/// Reusable heap allocations for the [`Stack`].
59#[derive(Debug, Default)]
60pub struct StackAllocations {
61    /// The Wasm value stack.
62    operands: OperandStack,
63    /// The Wasm control stack.
64    controls: ControlStack,
65}
66
67impl Reset for StackAllocations {
68    fn reset(&mut self) {
69        self.operands.reset();
70        self.controls.reset();
71    }
72}
73
74impl ReusableAllocations for Stack {
75    type Allocations = StackAllocations;
76
77    fn into_allocations(self) -> StackAllocations {
78        StackAllocations {
79            operands: self.operands,
80            controls: self.controls,
81        }
82    }
83}
84
85impl Stack {
86    /// Creates a new empty [`Stack`] from the given `engine`.
87    pub fn new(engine: &Engine, alloc: StackAllocations) -> Self {
88        let StackAllocations { operands, controls } = alloc.into_reset();
89        Self {
90            engine: engine.clone(),
91            operands,
92            controls,
93        }
94    }
95
96    /// Slot `amount` local variables.
97    ///
98    /// # Errors
99    ///
100    /// If too many local variables are being registered.
101    pub fn register_locals(&mut self, amount: usize) -> Result<(), Error> {
102        self.operands.register_locals(amount)
103    }
104
105    /// Returns `true` if the control stack is empty.
106    pub fn is_control_empty(&self) -> bool {
107        self.controls.is_empty()
108    }
109
110    /// Returns the current height of the [`Stack`].
111    ///
112    /// # Note
113    ///
114    /// The height is equal to the number of [`Operand`]s on the [`Stack`].
115    pub fn height(&self) -> usize {
116        self.operands.height()
117    }
118
119    /// Returns the maximum height of the [`Stack`].
120    ///
121    /// # Note
122    ///
123    /// The height is equal to the number of [`Operand`]s on the [`Stack`].
124    pub fn max_height(&self) -> usize {
125        self.operands.max_height()
126    }
127
128    /// Truncates `self` to the target `height`.
129    ///
130    /// All operands above `height` are dropped.
131    ///
132    /// # Panic
133    ///
134    /// If `height` is greater than the current height of `self`.
135    pub fn trunc(&mut self, height: usize) {
136        debug_assert!(height <= self.height());
137        while self.height() > height {
138            self.pop();
139        }
140    }
141
142    /// Returns `true` is fuel metering is enabled for the associated [`Engine`].
143    fn is_fuel_metering_enabled(&self) -> bool {
144        self.engine.config().get_consume_fuel()
145    }
146
147    /// Pushes the function enclosing Wasm `block` onto the [`Stack`].
148    ///
149    /// # Note
150    ///
151    /// - If `consume_fuel` is `None` fuel metering is expected to be disabled.
152    /// - If `consume_fuel` is `Some` fuel metering is expected to be enabled.
153    ///
154    /// # Errors
155    ///
156    /// If the stack height exceeds the maximum height.
157    pub fn push_func_block(
158        &mut self,
159        ty: BlockType,
160        label: LabelRef,
161        consume_fuel: Option<Instr>,
162    ) -> Result<(), Error> {
163        debug_assert!(self.controls.is_empty());
164        debug_assert!(self.is_fuel_metering_enabled() == consume_fuel.is_some());
165        self.controls.push_block(ty, 0, label, consume_fuel);
166        Ok(())
167    }
168
169    /// Pushes a Wasm `block` onto the [`Stack`].
170    ///
171    /// # Note
172    ///
173    /// This inherits the `consume_fuel` [`Instr`] from the parent [`ControlFrame`].
174    ///
175    /// # Errors
176    ///
177    /// If the stack height exceeds the maximum height.
178    pub fn push_block(&mut self, ty: BlockType, label: LabelRef) -> Result<(), Error> {
179        debug_assert!(!self.controls.is_empty());
180        let len_params = usize::from(ty.len_params(&self.engine));
181        let block_height = self.height() - len_params;
182        let consume_fuel = self.consume_fuel_instr();
183        self.controls
184            .push_block(ty, block_height, label, consume_fuel);
185        Ok(())
186    }
187
188    /// Pushes a Wasm `loop` onto the [`Stack`].
189    ///
190    /// # Panics (debug)
191    ///
192    /// - If `consume_fuel` is `None` and fuel metering is enabled.
193    /// - If any of the Wasm `loop` operand parameters are _not_ [`Operand::Temp`].
194    ///
195    /// # Errors
196    ///
197    /// If the stack height exceeds the maximum height.
198    pub fn push_loop(
199        &mut self,
200        ty: BlockType,
201        label: LabelRef,
202        consume_fuel: Option<Instr>,
203    ) -> Result<(), Error> {
204        debug_assert!(!self.controls.is_empty());
205        debug_assert!(self.is_fuel_metering_enabled() == consume_fuel.is_some());
206        let len_params = usize::from(ty.len_params(&self.engine));
207        let block_height = self.height() - len_params;
208        debug_assert!(self
209            .operands
210            .peek(len_params)
211            .all(|operand| operand.is_temp()));
212        self.controls
213            .push_loop(ty, block_height, label, consume_fuel);
214        Ok(())
215    }
216
217    /// Pushes a Wasm `if` onto the [`Stack`].
218    ///
219    /// # Panics (debug)
220    ///
221    /// If `consume_fuel` is `None` and fuel metering is enabled.
222    ///
223    /// # Errors
224    ///
225    /// If the stack height exceeds the maximum height.
226    pub fn push_if(
227        &mut self,
228        ty: BlockType,
229        label: LabelRef,
230        reachability: IfReachability,
231        consume_fuel: Option<Instr>,
232    ) -> Result<(), Error> {
233        debug_assert!(!self.controls.is_empty());
234        debug_assert!(self.is_fuel_metering_enabled() == consume_fuel.is_some());
235        let len_params = usize::from(ty.len_params(&self.engine));
236        let block_height = self.height() - len_params;
237        let else_operands = self.operands.peek(len_params);
238        debug_assert!(len_params == else_operands.len());
239        self.controls.push_if(
240            ty,
241            block_height,
242            label,
243            consume_fuel,
244            reachability,
245            else_operands,
246        );
247        Ok(())
248    }
249
250    /// Pushes a Wasm `else` onto the [`Stack`].
251    ///
252    /// # Panics (debug)
253    ///
254    /// If `consume_fuel` is `None` and fuel metering is enabled.
255    ///
256    /// # Errors
257    ///
258    /// If the stack height exceeds the maximum height.
259    pub fn push_else(
260        &mut self,
261        if_frame: IfControlFrame,
262        is_end_of_then_reachable: bool,
263        consume_fuel: Option<Instr>,
264    ) -> Result<(), Error> {
265        debug_assert!(self.is_fuel_metering_enabled() == consume_fuel.is_some());
266        self.push_else_operands(&if_frame)?;
267        self.controls
268            .push_else(if_frame, consume_fuel, is_end_of_then_reachable);
269        Ok(())
270    }
271
272    /// Pushes an unreachable Wasm control onto the [`Stack`].
273    ///
274    /// # Errors
275    ///
276    /// If the stack height exceeds the maximum height.
277    pub fn push_unreachable(&mut self, kind: ControlFrameKind) -> Result<(), Error> {
278        self.controls.push_unreachable(kind);
279        Ok(())
280    }
281
282    /// Pops the top-most control frame from the control stack and returns it.
283    ///
284    /// # Panics
285    ///
286    /// If the control stack is empty.
287    pub fn pop_control(&mut self) -> ControlFrame {
288        self.controls
289            .pop()
290            .unwrap_or_else(|| panic!("tried to pop control from empty control stack"))
291    }
292
293    /// Pushes the top-most `else` operands from the control stack onto the operand stack.
294    ///
295    /// # Panics (Debug)
296    ///
297    /// If the `else` operands are not in orphaned state.
298    pub fn push_else_operands(&mut self, frame: &IfControlFrame) -> Result<(), Error> {
299        match frame.reachability() {
300            IfReachability::Both { .. } => {}
301            IfReachability::OnlyThen | IfReachability::OnlyElse => return Ok(()),
302        };
303        self.trunc(frame.height());
304        for else_operand in self.controls.pop_else_operands() {
305            self.operands.push_operand(else_operand)?;
306        }
307        Ok(())
308    }
309
310    /// Returns a shared reference to the [`ControlFrame`] at `depth`.
311    ///
312    /// # Panics
313    ///
314    /// If `depth` is out of bounds for `self`.
315    pub fn peek_control(&self, depth: usize) -> &ControlFrame {
316        self.controls.get(depth)
317    }
318
319    /// Returns an exclusive reference to the [`ControlFrame`] at `depth`.
320    ///
321    /// # Note
322    ///
323    /// This returns an [`AcquiredTarget`] to differentiate between the function
324    /// body Wasm `block` and other control frames in order to know whether a branching
325    /// target returns or branches.
326    ///
327    /// # Panics
328    ///
329    /// If `depth` is out of bounds for `self`.
330    pub fn peek_control_mut(&mut self, depth: usize) -> AcquiredTarget<'_> {
331        self.controls.acquire_target(depth)
332    }
333
334    /// Pushes the [`Operand`] back to the [`Stack`].
335    ///
336    /// Returns the new [`OperandIdx`].
337    ///
338    /// # Errors
339    ///
340    /// - If too many operands have been pushed onto the [`Stack`].
341    /// - If the local with `local_idx` does not exist.
342    pub fn push_operand(&mut self, operand: Operand) -> Result<OperandIdx, Error> {
343        self.operands.push_operand(operand)
344    }
345
346    /// Pushes a local variable with index `local_idx` to the [`Stack`].
347    ///
348    /// # Errors
349    ///
350    /// - If too many operands have been pushed onto the [`Stack`].
351    /// - If the local with `local_idx` does not exist.
352    pub fn push_local(&mut self, local_index: LocalIdx, ty: ValType) -> Result<OperandIdx, Error> {
353        self.operands.push_local(local_index, ty)
354    }
355
356    /// Pushes a temporary with type `ty` on the [`Stack`].
357    ///
358    /// # Errors
359    ///
360    /// If too many operands have been pushed onto the [`Stack`].
361    #[inline]
362    pub fn push_temp(&mut self, ty: ValType, instr: Option<Instr>) -> Result<OperandIdx, Error> {
363        self.operands.push_temp(ty, instr)
364    }
365
366    /// Pushes an immediate `value` on the [`Stack`].
367    ///
368    /// # Errors
369    ///
370    /// If too many operands have been pushed onto the [`Stack`].
371    #[inline]
372    pub fn push_immediate(&mut self, value: impl Into<TypedVal>) -> Result<OperandIdx, Error> {
373        self.operands.push_immediate(value)
374    }
375
376    /// Peeks the [`Operand`] at `depth`.
377    ///
378    /// # Note
379    ///
380    /// A depth of 0 peeks the top-most [`Operand`] on `self`.
381    ///
382    /// # Panics
383    ///
384    /// If `depth` is out of bounds for `self`.
385    #[inline]
386    pub fn peek(&self, depth: usize) -> Operand {
387        self.operands.get(depth)
388    }
389
390    /// Peeks the 2 top-most [`Operand`]s.
391    ///
392    /// # Panics
393    ///
394    /// If there aren't at least 2 [`Operand`]s on the [`Stack`].
395    #[inline]
396    pub fn peek2(&self) -> (Operand, Operand) {
397        let v0 = self.peek(1);
398        let v1 = self.peek(0);
399        (v0, v1)
400    }
401
402    /// Peeks the 3 top-most [`Operand`]s.
403    ///
404    /// # Panics
405    ///
406    /// If there aren't at least 2 [`Operand`]s on the [`Stack`].
407    pub fn peek3(&self) -> (Operand, Operand, Operand) {
408        let v0 = self.peek(2);
409        let v1 = self.peek(1);
410        let v2 = self.peek(0);
411        (v0, v1, v2)
412    }
413
414    /// Returns an iterator yielding the top-most `len` operands from the stack.
415    ///
416    /// Operands are yieleded in insertion order.
417    pub fn peek_n(&self, len: usize) -> PeekedOperands<'_> {
418        self.operands.peek(len)
419    }
420
421    /// Pops the top-most [`Operand`] from the [`Stack`].
422    ///
423    /// # Panics
424    ///
425    /// If `self` is empty.
426    #[inline]
427    pub fn pop(&mut self) -> Operand {
428        self.operands.pop()
429    }
430
431    /// Pops the two top-most [`Operand`] from the [`Stack`].
432    ///
433    /// # Note
434    ///
435    /// The last returned [`Operand`] is the top-most one.
436    ///
437    /// # Panics
438    ///
439    /// If `self` does not contain enough operands to pop.
440    #[inline]
441    pub fn pop2(&mut self) -> (Operand, Operand) {
442        let o2 = self.pop();
443        let o1 = self.pop();
444        (o1, o2)
445    }
446
447    /// Pops the two top-most [`Operand`] from the [`Stack`].
448    ///
449    /// # Note
450    ///
451    /// The last returned [`Operand`] is the top-most one.
452    ///
453    /// # Panics
454    ///
455    /// If `self` does not contain enough operands to pop.
456    pub fn pop3(&mut self) -> (Operand, Operand, Operand) {
457        let o3 = self.pop();
458        let o2 = self.pop();
459        let o1 = self.pop();
460        (o1, o2, o3)
461    }
462
463    /// Pops `len` operands from the stack and store them into `buffer`.
464    ///
465    /// Operands stored into the buffer are placed in order.
466    pub fn pop_n(&mut self, len: usize, buffer: &mut Vec<Operand>) {
467        buffer.clear();
468        for _ in 0..len {
469            let operand = self.pop();
470            buffer.push(operand);
471        }
472        buffer.reverse();
473    }
474
475    /// Preserve all locals on the [`Stack`] that refer to `local_index`.
476    ///
477    /// This is done by converting those locals to [`Operand::Temp`] and yielding them.
478    ///
479    /// # Note
480    ///
481    /// The users must fully consume all items yielded by the returned iterator in order
482    /// for the local preservation to take full effect.
483    ///
484    /// # Panics
485    ///
486    /// If the local at `local_index` is out of bounds.
487    #[must_use]
488    pub fn preserve_locals(&mut self, local_index: LocalIdx) -> PreservedLocalsIter<'_> {
489        self.operands.preserve_locals(local_index)
490    }
491
492    /// Preserve all locals on the [`OperandStack`].
493    ///
494    /// This is done by converting those locals to [`StackOperand::Temp`] and yielding them.
495    ///
496    /// # Note
497    ///
498    /// The users must fully consume all items yielded by the returned iterator in order
499    /// for the local preservation to take full effect.
500    #[must_use]
501    pub fn preserve_all_locals(&mut self) -> PreservedAllLocalsIter<'_> {
502        self.operands.preserve_all_locals()
503    }
504
505    /// Converts and returns the [`Operand`] at `depth` into a [`Operand::Temp`].
506    ///
507    /// # Note
508    ///
509    /// - Returns the [`Operand`] at `depth` before being converted to an [`Operand::Temp`].
510    /// - [`Operand::Temp`] will have their optional `instr` set to `None`.
511    ///
512    /// # Panics
513    ///
514    /// If `depth` is out of bounds for the [`Stack`] of operands.
515    #[must_use]
516    pub fn operand_to_temp(&mut self, depth: usize) -> Operand {
517        self.operands.operand_to_temp(depth)
518    }
519
520    /// Returns the current [`Op::ConsumeFuel`] if fuel metering is enabled.
521    ///
522    /// Returns `None` otherwise.
523    #[inline]
524    pub fn consume_fuel_instr(&self) -> Option<Instr> {
525        self.controls.consume_fuel_instr()
526    }
527}