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}