1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! Call frames. One per active wasm call; the function's code is held as an
//! `Arc<CompiledFunc>` so the run loop can read ops without borrowing the store
//! or the value/frame stacks.
use crate::instance::Instance;
use crate::module::code::Code;
use crate::module::op::{BranchTarget, CompiledFunc};
#[derive(Debug)]
pub(crate) struct Frame {
pub code: Code,
/// Cached copy of the function record (spans + scalar facts) so the run loop and call
/// paths never re-resolve `functions[index]` on frame switches.
pub func: CompiledFunc,
/// Resume point in `code.ops` (saved when this frame makes a call).
pub ip: u32,
/// Index into `Execution.values` where this frame's locals begin.
pub locals_base: u32,
/// The instance this frame executes in (resolves globals/memories/callees).
pub instance: Instance,
/// This function's index in its defining module — for backtraces (#29e), avoiding a
/// pointer-scan over `module.functions` at capture time.
pub func_index: u32,
/// A boundary marker, not an executable frame: it separates one (sub-)call's frames from the
/// parked outer call's on the single shared operand/frame stack. The run loop never executes
/// one (`stop_depth` stops the call above it); its `code`/`instance` are inert filler.
pub delimiter: Option<Delimiter>,
}
/// What a delimiter frame marks. `HostReentry` is a host→wasm re-entry (`Func::call` from a host
/// function) — rendered as a host-boundary marker in backtraces; `TopLevel` is the embedder→wasm
/// entry at the bottom of the stack, which carries no backtrace marker.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Delimiter {
TopLevel,
HostReentry,
}
impl super::Execution {
#[inline]
pub(super) fn push_call(&mut self, instance: Instance, func_index: u32, code: Code) {
let func = code.func(); // resolved once; the frame caches the record
let locals_base = self.values.len() as u32 - func.n_params;
for ty in code.local_types_of(&func) {
self.push_default(ty);
}
self.frames.push(Frame {
code,
func,
ip: 0,
locals_base,
instance,
func_index,
delimiter: None,
});
}
/// Pushes a [`Delimiter`] boundary marker (no operands, inert `code`/`instance` filler). The
/// next `push_call` lays the entered function's frame directly above it; `run`/`unwind` stop at
/// this frame's depth so the call below it stays parked and untouched.
#[inline]
pub(super) fn push_delimiter(&mut self, kind: Delimiter, instance: Instance, code: Code) {
let locals_base = self.values.len() as u32;
let func = code.func();
self.frames.push(Frame {
code,
func,
ip: 0,
locals_base,
instance,
func_index: 0,
delimiter: Some(kind),
});
}
/// Moves the top `keep` operands down over `pop` discarded ones, then jumps.
#[inline]
pub(super) fn take_branch(&mut self, t: BranchTarget) {
if t.pop == 0 {
return; // nothing discarded — the kept operands are already in place
}
let len = self.values.len();
let keep = usize::from(t.keep);
let src = len - keep;
let dst = src - usize::from(t.pop);
self.values.copy_within(src..len, dst);
self.values.truncate(dst + keep);
// The root shadow moves in lockstep with the cell stack (same offsets/length).
self.shadow.copy_within(src..len, dst);
self.shadow.truncate(dst + keep);
}
#[inline]
pub(super) fn top(&self) -> (Code, CompiledFunc, u32, u32, Instance) {
let f = self.frames.last().expect("current frame");
(f.code.clone(), f.func, f.ip, f.locals_base, f.instance)
}
/// Pops the current frame, moving its top `n_results` operands down to the
/// frame base. Returns true if the frame stack has fallen back to `stop_depth`
/// (this call's boundary) — i.e. the call this `run` was driving has finished.
#[inline]
pub(super) fn do_return(&mut self, n_results: u32, stop_depth: usize) -> bool {
let frame = self.frames.pop().expect("frame stack underflow");
let n = n_results as usize;
let len = self.values.len();
let dst = frame.locals_base as usize;
self.values.copy_within(len - n..len, dst);
self.values.truncate(dst + n);
self.shadow.copy_within(len - n..len, dst);
self.shadow.truncate(dst + n);
self.frames.len() == stop_depth
}
}