radix_wasmi/engine/stack/
mod.rs1mod frames;
2mod values;
3
4pub use self::{
5 frames::{CallStack, FuncFrame},
6 values::{ValueStack, ValueStackRef},
7};
8use super::{
9 code_map::{CodeMap, InstructionPtr},
10 func_types::FuncTypeRegistry,
11 FuncParams,
12};
13use crate::{
14 core::UntypedValue,
15 func::{HostFuncEntity, WasmFuncEntity},
16 AsContext,
17 AsContextMut,
18 Instance,
19};
20use core::{
21 fmt::{self, Display},
22 mem::size_of,
23};
24use wasmi_core::{Trap, TrapCode};
25
26const DEFAULT_MIN_VALUE_STACK_HEIGHT: usize = 1024;
28
29const DEFAULT_MAX_VALUE_STACK_HEIGHT: usize = 1024 * DEFAULT_MIN_VALUE_STACK_HEIGHT;
31
32const DEFAULT_MAX_RECURSION_DEPTH: usize = 1024;
34
35#[cold]
37fn err_stack_overflow() -> TrapCode {
38 TrapCode::StackOverflow
39}
40
41#[derive(Debug, Copy, Clone)]
43pub struct StackLimits {
44 initial_value_stack_height: usize,
46 maximum_value_stack_height: usize,
48 maximum_recursion_depth: usize,
50}
51
52#[derive(Debug)]
54pub enum LimitsError {
55 InitialValueStackExceedsMaximum,
57}
58
59impl Display for LimitsError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 LimitsError::InitialValueStackExceedsMaximum => {
63 write!(f, "initial value stack heihgt exceeds maximum stack height")
64 }
65 }
66 }
67}
68
69impl StackLimits {
70 pub fn new(
76 initial_value_stack_height: usize,
77 maximum_value_stack_height: usize,
78 maximum_recursion_depth: usize,
79 ) -> Result<Self, LimitsError> {
80 if initial_value_stack_height > maximum_value_stack_height {
81 return Err(LimitsError::InitialValueStackExceedsMaximum);
82 }
83 Ok(Self {
84 initial_value_stack_height,
85 maximum_value_stack_height,
86 maximum_recursion_depth,
87 })
88 }
89}
90
91impl Default for StackLimits {
92 fn default() -> Self {
93 let register_len = size_of::<UntypedValue>();
94 let initial_value_stack_height = DEFAULT_MIN_VALUE_STACK_HEIGHT / register_len;
95 let maximum_value_stack_height = DEFAULT_MAX_VALUE_STACK_HEIGHT / register_len;
96 Self {
97 initial_value_stack_height,
98 maximum_value_stack_height,
99 maximum_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
100 }
101 }
102}
103
104#[derive(Debug, Default)]
106pub struct Stack {
107 pub(crate) values: ValueStack,
109 frames: CallStack,
111}
112
113impl Stack {
114 pub fn new(limits: StackLimits) -> Self {
118 let frames = CallStack::new(limits.maximum_recursion_depth);
119 let values = ValueStack::new(
120 limits.initial_value_stack_height,
121 limits.maximum_value_stack_height,
122 );
123 Self { values, frames }
124 }
125
126 pub(crate) fn empty() -> Self {
132 Self {
133 values: ValueStack::empty(),
134 frames: CallStack::default(),
135 }
136 }
137
138 pub(crate) fn is_empty(&self) -> bool {
144 self.values.is_empty()
145 }
146
147 pub(super) fn push_frame(&mut self, frame: FuncFrame) -> Result<(), TrapCode> {
156 self.frames.push(frame)
157 }
158
159 pub(super) fn pop_frame(&mut self) -> Option<FuncFrame> {
171 self.frames.pop()
172 }
173
174 pub(crate) fn call_wasm_root(
176 &mut self,
177 wasm_func: &WasmFuncEntity,
178 code_map: &CodeMap,
179 ) -> Result<FuncFrame, TrapCode> {
180 let iref = self.call_wasm_impl(wasm_func, code_map)?;
181 let instance = wasm_func.instance();
182 Ok(self.frames.init(iref, instance))
183 }
184
185 pub(crate) fn call_wasm(
187 &mut self,
188 caller: &FuncFrame,
189 wasm_func: &WasmFuncEntity,
190 code_map: &CodeMap,
191 ) -> Result<FuncFrame, TrapCode> {
192 let ip = self.call_wasm_impl(wasm_func, code_map)?;
193 self.frames.push(*caller)?;
194 let instance = wasm_func.instance();
195 let frame = FuncFrame::new(ip, instance);
196 Ok(frame)
197 }
198
199 pub(crate) fn call_wasm_impl(
201 &mut self,
202 wasm_func: &WasmFuncEntity,
203 code_map: &CodeMap,
204 ) -> Result<InstructionPtr, TrapCode> {
205 let header = code_map.header(wasm_func.func_body());
206 let max_stack_height = header.max_stack_height();
207 self.values.reserve(max_stack_height)?;
208 let len_locals = header.len_locals();
209 self.values
210 .extend_zeros(len_locals)
211 .expect("stack overflow is unexpected due to previous stack reserve");
212 let iref = header.iref();
213 let ip = code_map.instr_ptr(iref);
214 Ok(ip)
215 }
216
217 pub fn return_wasm(&mut self) -> Option<FuncFrame> {
221 self.frames.pop()
222 }
223
224 pub(crate) fn call_host_root<C>(
226 &mut self,
227 ctx: C,
228 host_func: HostFuncEntity<<C as AsContext>::UserState>,
229 func_types: &FuncTypeRegistry,
230 ) -> Result<(), Trap>
231 where
232 C: AsContextMut,
233 {
234 self.call_host_impl(ctx, host_func, None, func_types)
235 }
236
237 pub(crate) fn call_host<C>(
239 &mut self,
240 ctx: C,
241 caller: &FuncFrame,
242 host_func: HostFuncEntity<<C as AsContext>::UserState>,
243 func_types: &FuncTypeRegistry,
244 ) -> Result<(), Trap>
245 where
246 C: AsContextMut,
247 {
248 let instance = caller.instance();
249 self.call_host_impl(ctx, host_func, Some(instance), func_types)
250 }
251
252 #[inline(never)]
259 fn call_host_impl<C>(
260 &mut self,
261 mut ctx: C,
262 host_func: HostFuncEntity<<C as AsContext>::UserState>,
263 instance: Option<Instance>,
264 func_types: &FuncTypeRegistry,
265 ) -> Result<(), Trap>
266 where
267 C: AsContextMut,
268 {
269 let (input_types, output_types) = func_types
272 .resolve_func_type(host_func.ty_dedup())
273 .params_results();
274 let len_inputs = input_types.len();
277 let len_outputs = output_types.len();
278 let max_inout = len_inputs.max(len_outputs);
279 self.values.reserve(max_inout)?;
280 if len_outputs > len_inputs {
281 let delta = len_outputs - len_inputs;
282 self.values.extend_zeros(delta)?;
283 }
284 let params_results = FuncParams::new(
285 self.values.peek_as_slice_mut(max_inout),
286 len_inputs,
287 len_outputs,
288 );
289 host_func.call(&mut ctx, instance, params_results)?;
293 if len_outputs < len_inputs {
296 let delta = len_inputs - len_outputs;
297 self.values.drop(delta);
298 }
299 Ok(())
303 }
304
305 pub fn clear(&mut self) {
307 self.values.clear();
308 self.frames.clear();
309 }
310}