diff options
| author | Henry <mail@henrygressmann.de> | 2026-03-28 14:36:00 +0100 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-03-28 14:36:00 +0100 |
| commit | af3029bdc9461b2f8876b0564c3647dba27db97b (patch) | |
| tree | f238480f6c782ec799760cb0fbb12915a209e5fd /crates | |
| parent | 430ea402911855e8b8579f0e97b10699f22f758d (diff) | |
chore: cleanup, make value & block stack fixed size
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/parser/src/visit.rs | 30 | ||||
| -rw-r--r-- | crates/tinywasm/src/engine.rs | 84 | ||||
| -rw-r--r-- | crates/tinywasm/src/error.rs | 12 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 122 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/block_stack.rs | 20 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/call_stack.rs | 40 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/value_stack.rs | 208 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/values.rs | 61 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/memory.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 80 |
10 files changed, 332 insertions, 327 deletions
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 8212763..cb9a3d2 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -157,7 +157,6 @@ macro_rules! impl_visit_operator { (@@tail_call $($rest:tt)* ) => {}; (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => { - #[cold] fn $visit(&mut self $($(,_: $argty)*)?) { self.unsupported(stringify!($visit)) } @@ -203,7 +202,11 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild wasmparser::ValType::V128 => Instruction::GlobalSet128(global_index), wasmparser::ValType::Ref(_) => Instruction::GlobalSetRef(global_index), }), - _ => self.visit_unreachable(), + _ => { + { + self.visit_unreachable(); + }; + } } } @@ -217,7 +220,9 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild wasmparser::ValType::V128 => Instruction::Drop128, wasmparser::ValType::Ref(_) => Instruction::DropRef, }), - _ => self.visit_unreachable(), + _ => { + self.visit_unreachable(); + } } } @@ -225,7 +230,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild match self.validator.get_operand_type(1) { Some(Some(t)) => self.visit_typed_select(t), _ => self.visit_unreachable(), - } + }; } fn visit_local_get(&mut self, idx: u32) -> Self::Output { @@ -245,7 +250,9 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild wasmparser::ValType::V128 => Instruction::LocalGet128(resolved_idx), wasmparser::ValType::Ref(_) => Instruction::LocalGetRef(resolved_idx), }), - _ => self.visit_unreachable(), + _ => { + self.visit_unreachable(); + } } } @@ -276,7 +283,9 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild wasmparser::ValType::V128 => Instruction::LocalCopy128(from, resolved_idx), wasmparser::ValType::Ref(_) => Instruction::LocalCopyRef(from, resolved_idx), }), - _ => self.visit_unreachable(), + _ => { + self.visit_unreachable(); + } } return; } @@ -290,7 +299,9 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild wasmparser::ValType::V128 => Instruction::LocalSet128(resolved_idx), wasmparser::ValType::Ref(_) => Instruction::LocalSetRef(resolved_idx), }), - _ => self.visit_unreachable(), + _ => { + self.visit_unreachable(); + } } } @@ -311,7 +322,9 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild wasmparser::ValType::V128 => Instruction::LocalTee128(resolved_idx), wasmparser::ValType::Ref(_) => Instruction::LocalTeeRef(resolved_idx), }), - _ => self.visit_unreachable(), + _ => { + self.visit_unreachable(); + } } } @@ -483,7 +496,6 @@ macro_rules! impl_visit_simd_operator { (@@simd $($rest:tt)* ) => {}; (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => { - #[cold] fn $visit(&mut self $($(,$arg: $argty)*)?) { self.unsupported(stringify!($visit)) } diff --git a/crates/tinywasm/src/engine.rs b/crates/tinywasm/src/engine.rs index a70ed58..261f18c 100644 --- a/crates/tinywasm/src/engine.rs +++ b/crates/tinywasm/src/engine.rs @@ -39,58 +39,40 @@ pub(crate) struct EngineInner { // pub(crate) allocator: Box<dyn Allocator + Send + Sync>, } -// pub(crate) trait Allocator {} -// pub(crate) struct DefaultAllocator; -// impl Allocator for DefaultAllocator {} - /// Default initial size for the 32-bit value stack (i32, f32 values). -pub const DEFAULT_VALUE_STACK_32_INIT_SIZE: usize = 32 * 1024; // 32KB +pub const DEFAULT_VALUE_STACK_32_SIZE: usize = 64 * 1024; // 64k slots /// Default initial size for the 64-bit value stack (i64, f64 values). -pub const DEFAULT_VALUE_STACK_64_INIT_SIZE: usize = 16 * 1024; // 16KB +pub const DEFAULT_VALUE_STACK_64_SIZE: usize = 32 * 1024; // 32k slots /// Default initial size for the 128-bit value stack (v128 values). -pub const DEFAULT_VALUE_STACK_128_INIT_SIZE: usize = 8 * 1024; // 8KB +pub const DEFAULT_VALUE_STACK_128_SIZE: usize = 4 * 1024; // 4k slots /// Default initial size for the reference value stack (funcref, externref values). -pub const DEFAULT_VALUE_STACK_REF_INIT_SIZE: usize = 1024; // 1KB +pub const DEFAULT_VALUE_STACK_REF_SIZE: usize = 4 * 1024; // 4k slots -/// Default initial size for the block stack. -pub const DEFAULT_BLOCK_STACK_INIT_SIZE: usize = 128; +/// Default initial size for the block stack (control frames). +pub const DEFAULT_BLOCK_STACK_SIZE: usize = 1024; // 1024 frames -/// Default initial size for the call stack. -pub const DEFAULT_CALL_STACK_INIT_SIZE: usize = 128; +/// Default initial size for the call stack (function frames). +pub const DEFAULT_CALL_STACK_SIZE: usize = 1024; // 1024 frames /// Configuration for the WebAssembly interpreter #[derive(Debug, Clone)] #[non_exhaustive] pub struct Config { /// Initial size of the 32-bit value stack (i32, f32 values). - pub stack_32_init_size: usize, + pub stack_32_size: usize, /// Initial size of the 64-bit value stack (i64, f64 values). - pub stack_64_init_size: usize, + pub stack_64_size: usize, /// Initial size of the 128-bit value stack (v128 values). - pub stack_128_init_size: usize, + pub stack_128_size: usize, /// Initial size of the reference value stack (funcref, externref values). - pub stack_ref_init_size: usize, - /// Optional maximum sizes for the stacks. If set, the interpreter will enforce these limits and return an error if they are exceeded. - pub stack_32_max_size: Option<usize>, - /// Optional maximum sizes for the stacks. If set, the interpreter will enforce these limits and return an error if they are exceeded. - pub stack_64_max_size: Option<usize>, - /// Optional maximum sizes for the stacks. If set, the interpreter will enforce these limits and return an error if they are exceeded. - pub stack_128_max_size: Option<usize>, - /// Optional maximum sizes for the stacks. If set, the interpreter will enforce these limits and return an error if they are exceeded. - pub stack_ref_max_size: Option<usize>, - + pub stack_ref_size: usize, /// Initial size of the call stack. - pub call_stack_init_size: usize, - /// The maximum size of the call stack. If set, the interpreter will enforce this limit and return an error if it is exceeded. - pub call_stack_max_size: Option<usize>, - + pub call_stack_size: usize, /// Initial size of the control stack (block stack). - pub block_stack_init_size: usize, - /// Optional maximum size for the control stack (block stack). If set, the interpreter will enforce this limit and return an error if it is exceeded. - pub block_stack_max_size: Option<usize>, + pub block_stack_size: usize, } impl Config { @@ -98,43 +80,17 @@ impl Config { pub fn new() -> Self { Self::default() } - - /// Set the same maximum size for all stacks. If set, the interpreter will enforce this limit and return an error if it is exceeded. - pub fn with_max_stack_size(mut self, max_size: usize) -> Self { - self.stack_32_max_size = Some(max_size); - self.stack_64_max_size = Some(max_size); - self.stack_128_max_size = Some(max_size); - self.stack_ref_max_size = Some(max_size); - self.block_stack_max_size = Some(max_size); - self - } - - /// Set the same initial size for all stacks. - pub fn with_initial_stack_size(mut self, init_size: usize) -> Self { - self.stack_32_init_size = init_size; - self.stack_64_init_size = init_size; - self.stack_128_init_size = init_size; - self.stack_ref_init_size = init_size; - self.block_stack_init_size = init_size; - self - } } impl Default for Config { fn default() -> Self { Self { - stack_32_init_size: DEFAULT_VALUE_STACK_32_INIT_SIZE, - stack_64_init_size: DEFAULT_VALUE_STACK_64_INIT_SIZE, - stack_128_init_size: DEFAULT_VALUE_STACK_128_INIT_SIZE, - stack_ref_init_size: DEFAULT_VALUE_STACK_REF_INIT_SIZE, - block_stack_init_size: DEFAULT_BLOCK_STACK_INIT_SIZE, - call_stack_init_size: DEFAULT_CALL_STACK_INIT_SIZE, - call_stack_max_size: None, - stack_32_max_size: None, - stack_64_max_size: None, - stack_128_max_size: None, - stack_ref_max_size: None, - block_stack_max_size: None, + stack_32_size: DEFAULT_VALUE_STACK_32_SIZE, + stack_64_size: DEFAULT_VALUE_STACK_64_SIZE, + stack_128_size: DEFAULT_VALUE_STACK_128_SIZE, + stack_ref_size: DEFAULT_VALUE_STACK_REF_SIZE, + call_stack_size: DEFAULT_CALL_STACK_SIZE, + block_stack_size: DEFAULT_BLOCK_STACK_SIZE, } } } diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 4b3a969..4788ac4 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -1,8 +1,8 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::{fmt::Display, ops::ControlFlow}; -use tinywasm_types::FuncType; use tinywasm_types::archive::TwasmError; +use tinywasm_types::FuncType; #[cfg(feature = "parser")] pub use tinywasm_parser::ParseError; @@ -121,6 +121,12 @@ pub enum Trap { /// Call stack overflow CallStackOverflow, + /// Block stack overflow + BlockStackOverflow, + + /// Value stack overflow + ValueStackOverflow, + /// An undefined element was encountered UndefinedElement { /// The element index @@ -153,6 +159,8 @@ impl Trap { Self::InvalidConversionToInt => "invalid conversion to integer", Self::IntegerOverflow => "integer overflow", Self::CallStackOverflow => "call stack exhausted", + Self::BlockStackOverflow => "block stack exhausted", + Self::ValueStackOverflow => "value stack exhausted", Self::UndefinedElement { .. } => "undefined element", Self::UninitializedElement { .. } => "uninitialized element", Self::IndirectCallTypeMismatch { .. } => "indirect call type mismatch", @@ -235,6 +243,8 @@ impl Display for Trap { Self::InvalidConversionToInt => write!(f, "invalid conversion to integer"), Self::IntegerOverflow => write!(f, "integer overflow"), Self::CallStackOverflow => write!(f, "call stack exhausted"), + Self::BlockStackOverflow => write!(f, "block stack exhausted"), + Self::ValueStackOverflow => write!(f, "value stack exhausted"), Self::UndefinedElement { index } => write!(f, "undefined element: index={index}"), Self::UninitializedElement { index } => { write!(f, "uninitialized element: index={index}") diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 8d2e211..dcdd2f8 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -80,33 +80,33 @@ impl<'store> Executor<'store> { Drop64 => self.store.stack.values.drop::<Value64>(), Drop128 => self.store.stack.values.drop::<Value128>(), DropRef => self.store.stack.values.drop::<ValueRef>(), - Select32 => self.store.stack.values.select::<Value32>(), - Select64 => self.store.stack.values.select::<Value64>(), - Select128 => self.store.stack.values.select::<Value128>(), - SelectRef => self.store.stack.values.select::<ValueRef>(), + Select32 => self.store.stack.values.select::<Value32>().to_cf()?, + Select64 => self.store.stack.values.select::<Value64>().to_cf()?, + Select128 => self.store.stack.values.select::<Value128>().to_cf()?, + SelectRef => self.store.stack.values.select::<ValueRef>().to_cf()?, Call(v) => return self.exec_call_direct::<false>(*v), CallIndirect(ty, table) => return self.exec_call_indirect::<false>(*ty, *table), ReturnCall(v) => return self.exec_call_direct::<true>(*v), ReturnCallIndirect(ty, table) => return self.exec_call_indirect::<true>(*ty, *table), - If(end, el) => self.exec_if(*end, *el, (StackHeight::default(), StackHeight::default())), - IfWithType(ty, end, el) => self.exec_if(*end, *el, (StackHeight::default(), (*ty).into())), - IfWithFuncType(ty, end, el) => self.exec_if(*end, *el, self.resolve_functype(*ty)), + If(end, el) => self.exec_if(*end, *el, (StackHeight::default(), StackHeight::default())).to_cf()?, + IfWithType(ty, end, el) => self.exec_if(*end, *el, (StackHeight::default(), (*ty).into())).to_cf()?, + IfWithFuncType(ty, end, el) => self.exec_if(*end, *el, self.resolve_functype(*ty)).to_cf()?, Else(end_offset) => self.exec_else(*end_offset), - Loop(end) => self.enter_block(*end, BlockType::Loop, (StackHeight::default(), StackHeight::default())), - LoopWithType(ty, end) => self.enter_block(*end, BlockType::Loop, (StackHeight::default(), (*ty).into())), - LoopWithFuncType(ty, end) => self.enter_block(*end, BlockType::Loop, self.resolve_functype(*ty)), - Block(end) => self.enter_block(*end, BlockType::Block, (StackHeight::default(), StackHeight::default())), - BlockWithType(ty, end) => self.enter_block(*end, BlockType::Block, (StackHeight::default(), (*ty).into())), - BlockWithFuncType(ty, end) => self.enter_block(*end, BlockType::Block, self.resolve_functype(*ty)), + Loop(end) => self.enter_block(*end, BlockType::Loop, (StackHeight::default(), StackHeight::default())).to_cf()?, + LoopWithType(ty, end) => self.enter_block(*end, BlockType::Loop, (StackHeight::default(), (*ty).into())).to_cf()?, + LoopWithFuncType(ty, end) => self.enter_block(*end, BlockType::Loop, self.resolve_functype(*ty)).to_cf()?, + Block(end) => self.enter_block(*end, BlockType::Block, (StackHeight::default(), StackHeight::default())).to_cf()?, + BlockWithType(ty, end) => self.enter_block(*end, BlockType::Block, (StackHeight::default(), (*ty).into())).to_cf()?, + BlockWithFuncType(ty, end) => self.enter_block(*end, BlockType::Block, self.resolve_functype(*ty)).to_cf()?, Br(v) => return self.exec_br(*v), BrIf(v) => return self.exec_br_if(*v), BrTable(default, len) => return self.exec_brtable(*default, *len), Return => return self.exec_return(), EndBlockFrame => self.exec_end_block(), - LocalGet32(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value32>(*local_index)), - LocalGet64(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value64>(*local_index)), - LocalGet128(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value128>(*local_index)), - LocalGetRef(local_index) => self.store.stack.values.push(self.cf.locals.get::<ValueRef>(*local_index)), + LocalGet32(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value32>(*local_index)).to_cf()?, + LocalGet64(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value64>(*local_index)).to_cf()?, + LocalGet128(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value128>(*local_index)).to_cf()?, + LocalGetRef(local_index) => self.store.stack.values.push(self.cf.locals.get::<ValueRef>(*local_index)).to_cf()?, LocalSet32(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<Value32>()), LocalSet64(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<Value64>()), LocalSet128(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<Value128>()), @@ -119,15 +119,15 @@ impl<'store> Executor<'store> { LocalTee64(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<Value64>()), LocalTee128(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<Value128>()), LocalTeeRef(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<ValueRef>()), - GlobalGet(global_index) => self.exec_global_get(*global_index), + GlobalGet(global_index) => self.exec_global_get(*global_index).to_cf()?, GlobalSet32(global_index) => self.exec_global_set::<Value32>(*global_index), GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index), GlobalSet128(global_index) => self.exec_global_set::<Value128>(*global_index), GlobalSetRef(global_index) => self.exec_global_set::<ValueRef>(*global_index), - I32Const(val) => self.exec_const(*val), - I64Const(val) => self.exec_const(*val), - F32Const(val) => self.exec_const(*val), - F64Const(val) => self.exec_const(*val), + I32Const(val) => self.exec_const(*val).to_cf()?, + I64Const(val) => self.exec_const(*val).to_cf()?, + F32Const(val) => self.exec_const(*val).to_cf()?, + F64Const(val) => self.exec_const(*val).to_cf()?, I64Eqz => stack_op!(unary i64 => i32, |v| i32::from(v == 0)), I32Eqz => stack_op!(unary i32, |v| i32::from(v == 0)), I32Eq => stack_op!(binary i32, |a, b| i32::from(a == b)), @@ -208,11 +208,11 @@ impl<'store> Executor<'store> { I64Popcnt => stack_op!(unary i64, |v| i64::from(v.count_ones())), // Reference types - RefFunc(func_idx) => self.exec_const::<ValueRef>(Some(*func_idx)), - RefNull(_) => self.exec_const::<ValueRef>(None), - RefIsNull => self.exec_ref_is_null(), - MemorySize(addr) => self.exec_memory_size(*addr), - MemoryGrow(addr) => self.exec_memory_grow(*addr), + RefFunc(func_idx) => self.exec_const::<ValueRef>(Some(*func_idx)).to_cf()?, + RefNull(_) => self.exec_const::<ValueRef>(None).to_cf()?, + RefIsNull => self.exec_ref_is_null().to_cf()?, + MemorySize(addr) => self.exec_memory_size(*addr).to_cf()?, + MemoryGrow(addr) => self.exec_memory_grow(*addr).to_cf()?, // Bulk memory operations MemoryCopy(from, to) => self.exec_memory_copy(*from, *to).to_cf()?, @@ -342,7 +342,7 @@ impl<'store> Executor<'store> { V128Store64Lane(arg, lane) => self.exec_mem_store_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?, V128Load32Zero(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?, V128Load64Zero(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?, - V128Const(arg) => self.exec_const::<Value128>(self.cf.data().v128_constants[*arg as usize].into()), + V128Const(arg) => self.exec_const::<Value128>(self.cf.data().v128_constants[*arg as usize].into()).to_cf()?, I8x16ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i8(*lane) as i32), I8x16ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u8(*lane) as i32), I16x8ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i16(*lane) as i32), @@ -573,7 +573,7 @@ impl<'store> Executor<'store> { let locals = self.store.stack.values.pop_locals(wasm_func.params, wasm_func.locals); let new_call_frame = CallFrame::new_raw(wasm_func, owner, locals, self.store.stack.blocks.len() as u32); self.cf.incr_instr_ptr(); // skip the call instruction - self.store.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?; + self.store.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame)).to_cf()?; } self.module.swap_with(self.cf.module_addr(), self.store); @@ -582,7 +582,7 @@ impl<'store> Executor<'store> { fn exec_call_host(&mut self, host_func: Rc<imports::HostFunction>) -> ControlFlow<Option<Error>> { let params = self.store.stack.values.pop_types(&host_func.ty.params).collect::<Box<_>>(); let res = host_func.call(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms).to_cf()?; - self.store.stack.values.extend_from_wasmvalues(&res); + self.store.stack.values.extend_from_wasmvalues(&res).to_cf()?; self.cf.incr_instr_ptr(); ControlFlow::Continue(()) } @@ -635,21 +635,26 @@ impl<'store> Executor<'store> { } } - fn exec_if(&mut self, else_offset: u32, end_offset: u32, (params, results): (StackHeight, StackHeight)) { + fn exec_if( + &mut self, + else_offset: u32, + end_offset: u32, + (params, results): (StackHeight, StackHeight), + ) -> Result<()> { // truthy value is on the top of the stack, so enter the then block if self.store.stack.values.pop::<i32>() != 0 { - self.enter_block(end_offset, BlockType::If, (params, results)); - return; + self.enter_block(end_offset, BlockType::If, (params, results))?; + return Ok(()); } // falsy value is on the top of the stack if else_offset == 0 { self.cf.jump(end_offset); - return; + return Ok(()); } self.cf.jump(else_offset); - self.enter_block(end_offset - else_offset, BlockType::Else, (params, results)); + self.enter_block(end_offset - else_offset, BlockType::Else, (params, results)) } fn exec_else(&mut self, end_offset: u32) { self.exec_end_block(); @@ -659,7 +664,12 @@ impl<'store> Executor<'store> { let ty = self.module.func_ty(idx); ((&*ty.params).into(), (&*ty.results).into()) } - fn enter_block(&mut self, end_instr_offset: u32, ty: BlockType, (params, results): (StackHeight, StackHeight)) { + fn enter_block( + &mut self, + end_instr_offset: u32, + ty: BlockType, + (params, results): (StackHeight, StackHeight), + ) -> Result<()> { self.store.stack.blocks.push(BlockFrame { instr_ptr: self.cf.instr_ptr() as u32, end_instr_offset, @@ -667,7 +677,7 @@ impl<'store> Executor<'store> { results, params, ty, - }); + }) } fn exec_br(&mut self, to: u32) -> ControlFlow<Option<Error>> { if self.cf.break_to(to, &mut self.store.stack.values, &mut self.store.stack.blocks).is_none() { @@ -730,25 +740,23 @@ impl<'store> Executor<'store> { self.store.stack.values.truncate_keep(block.stack_ptr, block.results); } - fn exec_global_get(&mut self, global_index: u32) { - self.store - .stack - .values - .push_dyn(self.store.state.get_global_val(self.module.resolve_global_addr(global_index))); + fn exec_global_get(&mut self, global_index: u32) -> Result<()> { + self.store.stack.values.push_dyn(self.store.state.get_global_val(self.module.resolve_global_addr(global_index))) } + fn exec_global_set<T: InternalValue>(&mut self, global_index: u32) { let val = self.store.stack.values.pop::<T>().into(); self.store.state.set_global_val(self.module.resolve_global_addr(global_index), val); } - fn exec_const<T: InternalValue>(&mut self, val: T) { - self.store.stack.values.push(val); + fn exec_const<T: InternalValue>(&mut self, val: T) -> Result<()> { + self.store.stack.values.push(val) } - fn exec_ref_is_null(&mut self) { + fn exec_ref_is_null(&mut self) -> Result<()> { let is_null = i32::from(self.store.stack.values.pop::<ValueRef>().is_none()); - self.store.stack.values.push::<i32>(is_null); + self.store.stack.values.push::<i32>(is_null) } - fn exec_memory_size(&mut self, addr: u32) { + fn exec_memory_size(&mut self, addr: u32) -> Result<()> { let mem = self.store.state.get_mem(self.module.resolve_mem_addr(addr)); match mem.is_64bit() { @@ -756,7 +764,7 @@ impl<'store> Executor<'store> { false => self.store.stack.values.push::<i32>(mem.page_count as i32), } } - fn exec_memory_grow(&mut self, addr: u32) { + fn exec_memory_grow(&mut self, addr: u32) -> Result<()> { let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(addr)); let prev_size = mem.page_count; @@ -772,9 +780,11 @@ impl<'store> Executor<'store> { None => -1_i64, }, ) { - (true, size) => self.store.stack.values.push::<i64>(size), - (false, size) => self.store.stack.values.push::<i32>(size as i32), + (true, size) => self.store.stack.values.push::<i64>(size)?, + (false, size) => self.store.stack.values.push::<i32>(size as i32)?, }; + + Ok(()) } fn exec_memory_copy(&mut self, from: u32, to: u32) -> Result<()> { @@ -871,7 +881,7 @@ impl<'store> Executor<'store> { let offset = lane as usize * LOAD_SIZE; imm[offset..offset + LOAD_SIZE].copy_from_slice(&val); - self.store.stack.values.push(Value128::from_mem_bytes(imm)); + self.store.stack.values.push(Value128::from_mem_bytes(imm)).to_cf()?; ControlFlow::Continue(()) } @@ -896,7 +906,7 @@ impl<'store> Executor<'store> { }))); }; let val = mem.load_as::<LOAD_SIZE, LOAD>(addr).to_cf()?; - self.store.stack.values.push(cast(val)); + self.store.stack.values.push(cast(val)).to_cf()?; ControlFlow::Continue(()) } @@ -950,7 +960,7 @@ impl<'store> Executor<'store> { let idx: i32 = self.store.stack.values.pop::<i32>(); let table = self.store.state.get_table(self.module.resolve_table_addr(table_index)); let v = table.get_wasm_val(idx as u32)?; - self.store.stack.values.push_dyn(v.into()); + self.store.stack.values.push_dyn(v.into())?; Ok(()) } fn exec_table_set(&mut self, table_index: u32) -> Result<()> { @@ -961,7 +971,7 @@ impl<'store> Executor<'store> { } fn exec_table_size(&mut self, table_index: u32) -> Result<()> { let table = self.store.state.get_table(self.module.resolve_table_addr(table_index)); - self.store.stack.values.push(table.size()); + self.store.stack.values.push(table.size())?; Ok(()) } fn exec_table_init(&mut self, elem_index: u32, table_index: u32) -> Result<()> { @@ -1012,8 +1022,8 @@ impl<'store> Executor<'store> { let val = self.store.stack.values.pop::<ValueRef>(); match table.grow(n, val.into()) { - Ok(()) => self.store.stack.values.push(sz), - Err(_) => self.store.stack.values.push(-1_i32), + Ok(()) => self.store.stack.values.push(sz)?, + Err(_) => self.store.stack.values.push(-1_i32)?, } Ok(()) diff --git a/crates/tinywasm/src/interpreter/stack/block_stack.rs b/crates/tinywasm/src/interpreter/stack/block_stack.rs index e4d4c28..68dda84 100644 --- a/crates/tinywasm/src/interpreter/stack/block_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/block_stack.rs @@ -2,30 +2,33 @@ use crate::engine::Config; use alloc::vec::Vec; use crate::interpreter::values::{StackHeight, StackLocation}; +use crate::{Result, Trap}; #[derive(Debug)] pub(crate) struct BlockStack(Vec<BlockFrame>); impl BlockStack { pub(crate) fn new(config: &Config) -> Self { - Self(Vec::with_capacity(config.block_stack_init_size)) + Self(Vec::with_capacity(config.block_stack_size)) } pub(crate) fn clear(&mut self) { self.0.clear(); } - #[inline(always)] pub(crate) fn len(&self) -> usize { self.0.len() } - #[inline(always)] - pub(crate) fn push(&mut self, block: BlockFrame) { + pub(crate) fn push(&mut self, block: BlockFrame) -> Result<()> { + if self.0.len() >= self.0.capacity() { + return Err(Trap::BlockStackOverflow.into()); + } + self.0.push(block); + Ok(()) } - #[inline] /// get the label at the given index, where 0 is the top of the stack pub(crate) fn get_relative_to(&self, index: u32, offset: u32) -> Option<&BlockFrame> { let len = (self.0.len() as u32) - offset; @@ -38,13 +41,14 @@ impl BlockStack { Some(&self.0[self.0.len() - index as usize - 1]) } - #[inline(always)] pub(crate) fn pop(&mut self) -> BlockFrame { - self.0.pop().expect("block stack underflow, this is a bug") + match self.0.pop() { + Some(frame) => frame, + None => unreachable!("Block stack underflow, this is a bug"), + } } /// keep the top `len` blocks and discard the rest - #[inline(always)] pub(crate) fn truncate(&mut self, len: u32) { self.0.truncate(len as usize); } diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index 4b0fad6..688dde8 100644 --- a/crates/tinywasm/src/interpreter/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs @@ -1,43 +1,36 @@ -use core::ops::ControlFlow; - use super::BlockType; -use crate::Trap; use crate::interpreter::{Value128, values::*}; -use crate::{Error, unlikely}; +use crate::{Result, Trap, unlikely}; use alloc::boxed::Box; use alloc::{rc::Rc, vec::Vec}; use tinywasm_types::{ArcSlice, Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction, WasmFunctionData, WasmValue}; -pub(crate) const MAX_CALL_STACK_SIZE: usize = 1024; - #[derive(Debug)] pub(crate) struct CallStack { stack: Vec<CallFrame>, } impl CallStack { - #[inline] pub(crate) fn new(config: &crate::engine::Config) -> Self { - Self { stack: Vec::with_capacity(config.call_stack_init_size) } + Self { stack: Vec::with_capacity(config.call_stack_size) } } pub(crate) fn clear(&mut self) { self.stack.clear(); } - #[inline] pub(crate) fn pop(&mut self) -> Option<CallFrame> { self.stack.pop() } - #[inline] - pub(crate) fn push(&mut self, call_frame: CallFrame) -> ControlFlow<Option<Error>> { - if unlikely((self.stack.len() + 1) >= MAX_CALL_STACK_SIZE) { - return ControlFlow::Break(Some(Trap::CallStackOverflow.into())); + pub(crate) fn push(&mut self, call_frame: CallFrame) -> Result<()> { + if unlikely(self.stack.len() >= self.stack.capacity()) { + return Err(Trap::CallStackOverflow.into()); } + self.stack.push(call_frame); - ControlFlow::Continue(()) + Ok(()) } } @@ -69,42 +62,34 @@ impl Locals { } impl CallFrame { - #[inline] pub(crate) fn instr_ptr(&self) -> usize { self.instr_ptr } - #[inline] - #[allow(dead_code)] pub(crate) fn data(&self) -> &WasmFunctionData { &self.func_instance.data } - #[inline(always)] pub(crate) fn incr_instr_ptr(&mut self) { self.instr_ptr += 1; } - #[inline] pub(crate) fn jump(&mut self, offset: u32) { self.instr_ptr += offset as usize; } - #[inline] pub(crate) fn module_addr(&self) -> ModuleInstanceAddr { self.module_addr } #[inline(always)] pub(crate) fn fetch_instr(&self) -> &Instruction { - self - .func_instance - .instructions - .get(self.instr_ptr) - .unwrap_or_else(|| unreachable!("Instruction pointer out of bounds, this is a bug")) + match self.func_instance.instructions.get(self.instr_ptr) { + Some(instr) => instr, + None => unreachable!("Instruction pointer out of bounds, this is a bug"), + } } - #[inline] pub(crate) fn block_ptr(&self) -> u32 { self.block_ptr } @@ -125,7 +110,6 @@ impl CallFrame { /// Break to a block at the given index (relative to the current frame) /// Returns `None` if there is no block at the given index (e.g. if we need to return, this is handled by the caller) - #[inline] pub(crate) fn break_to( &mut self, break_to_relative: u32, @@ -168,7 +152,6 @@ impl CallFrame { Some(()) } - #[inline] pub(crate) fn new( func_instance: Rc<WasmFunction>, module_addr: ModuleInstanceAddr, @@ -206,7 +189,6 @@ impl CallFrame { Self { instr_ptr: 0, func_instance, module_addr, block_ptr, locals } } - #[inline] pub(crate) fn new_raw( func_instance: Rc<WasmFunction>, module_addr: ModuleInstanceAddr, diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index cec08a3..b579a04 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -1,25 +1,108 @@ +use alloc::boxed::Box; use alloc::vec::Vec; use tinywasm_types::{ExternRef, FuncRef, ValType, ValueCounts, ValueCountsSmall, WasmValue}; -use crate::{Result, engine::Config, interpreter::*}; +use crate::{Result, Trap, engine::Config, interpreter::*}; use super::Locals; #[derive(Debug)] pub(crate) struct ValueStack { - pub(crate) stack_32: Vec<Value32>, - pub(crate) stack_64: Vec<Value64>, - pub(crate) stack_128: Vec<Value128>, - pub(crate) stack_ref: Vec<ValueRef>, + pub(crate) stack_32: Stack<Value32>, + pub(crate) stack_64: Stack<Value64>, + pub(crate) stack_128: Stack<Value128>, + pub(crate) stack_ref: Stack<ValueRef>, +} + +#[derive(Debug)] +pub(crate) struct Stack<T> { + data: Box<[T]>, + len: usize, +} + +impl<T: Copy + Default> Stack<T> { + pub(crate) fn with_size(size: usize) -> Self { + let mut data = Vec::with_capacity(size); + data.resize_with(size, T::default); + Self { data: data.into_boxed_slice(), len: 0 } + } + + pub(crate) fn len(&self) -> usize { + self.len + } + + pub(crate) fn clear(&mut self) { + self.len = 0; + } + + pub(crate) fn push(&mut self, value: T) -> Result<()> { + if self.len >= self.data.len() { + return Err(Trap::ValueStackOverflow.into()); + } + + self.data[self.len] = value; + self.len += 1; + Ok(()) + } + + pub(crate) fn pop(&mut self) -> T { + if self.len == 0 { + unreachable!("ValueStack underflow, this is a bug"); + } + + self.len -= 1; + self.data[self.len] + } + + pub(crate) fn last(&self) -> &T { + if self.len == 0 { + unreachable!("ValueStack underflow, this is a bug"); + } + &self.data[self.len - 1] + } + + pub(crate) fn last_mut(&mut self) -> &mut T { + if self.len == 0 { + unreachable!("ValueStack underflow, this is a bug"); + } + &mut self.data[self.len - 1] + } + + pub(crate) fn truncate_keep(&mut self, n: usize, end_keep: usize) { + if self.len <= n { + return; + } + + let keep_tail = end_keep.min(self.len - n); + if keep_tail == 0 { + self.len = n; + return; + } + + let tail_start = self.len - keep_tail; + self.data.copy_within(tail_start..self.len, n); + self.len = n + keep_tail; + } + + pub(crate) fn pop_to_locals(&mut self, param_count: usize, local_count: usize) -> Box<[T]> { + let mut locals = alloc::vec![T::default(); local_count].into_boxed_slice(); + let start = + self.len.checked_sub(param_count).unwrap_or_else(|| unreachable!("value stack underflow, this is a bug")); + debug_assert!(param_count <= local_count, "param count exceeds local count"); + + locals[..param_count].copy_from_slice(&self.data[start..self.len]); + self.len = start; + locals + } } impl ValueStack { pub(crate) fn new(config: &Config) -> Self { Self { - stack_32: Vec::with_capacity(config.stack_32_init_size), - stack_64: Vec::with_capacity(config.stack_64_init_size), - stack_128: Vec::with_capacity(config.stack_128_init_size), - stack_ref: Vec::with_capacity(config.stack_ref_init_size), + stack_32: Stack::with_size(config.stack_32_size), + stack_64: Stack::with_size(config.stack_64_size), + stack_128: Stack::with_size(config.stack_128_size), + stack_ref: Stack::with_size(config.stack_ref_size), } } @@ -43,86 +126,73 @@ impl ValueStack { self.stack_32.len() + self.stack_64.len() + self.stack_128.len() + self.stack_ref.len() } - #[inline] pub(crate) fn peek<T: InternalValue>(&self) -> T { T::stack_peek(self) } - #[inline] pub(crate) fn pop<T: InternalValue>(&mut self) -> T { T::stack_pop(self) } - #[inline] - pub(crate) fn push<T: InternalValue>(&mut self, value: T) { - T::stack_push(self, value); + pub(crate) fn push<T: InternalValue>(&mut self, value: T) -> Result<()> { + T::stack_push(self, value) } - #[inline] pub(crate) fn drop<T: InternalValue>(&mut self) { T::stack_pop(self); } - #[inline] - pub(crate) fn select<T: InternalValue>(&mut self) { + pub(crate) fn select<T: InternalValue>(&mut self) -> Result<()> { let cond: i32 = self.pop(); let val2: T = self.pop(); if cond == 0 { self.drop::<T>(); - self.push(val2); + self.push(val2)?; } + Ok(()) } - #[inline] pub(crate) fn binary_same<T: InternalValue>(&mut self, func: impl FnOnce(T, T) -> Result<T>) -> Result<()> { T::stack_calculate(self, func) } - #[inline] - #[allow(dead_code)] pub(crate) fn ternary_same<T: InternalValue>(&mut self, func: impl FnOnce(T, T, T) -> Result<T>) -> Result<()> { T::stack_calculate3(self, func) } - #[inline] pub(crate) fn binary<T: InternalValue, U: InternalValue>( &mut self, func: impl FnOnce(T, T) -> Result<U>, ) -> Result<()> { let v2 = T::stack_pop(self); let v1 = T::stack_pop(self); - U::stack_push(self, func(v1, v2)?); + U::stack_push(self, func(v1, v2)?)?; Ok(()) } - #[inline] - #[allow(dead_code)] pub(crate) fn binary_diff<A: InternalValue, B: InternalValue, RES: InternalValue>( &mut self, func: impl FnOnce(A, B) -> Result<RES>, ) -> Result<()> { let v2 = B::stack_pop(self); let v1 = A::stack_pop(self); - RES::stack_push(self, func(v1, v2)?); + RES::stack_push(self, func(v1, v2)?)?; Ok(()) } - #[inline] pub(crate) fn unary<T: InternalValue, U: InternalValue>( &mut self, func: impl FnOnce(T) -> Result<U>, ) -> Result<()> { let v1 = T::stack_pop(self); - U::stack_push(self, func(v1)?); + U::stack_push(self, func(v1)?)?; Ok(()) } - #[inline] pub(crate) fn unary_same<T: InternalValue>(&mut self, func: impl Fn(T) -> Result<T>) -> Result<()> { T::replace_top(self, func) } - #[inline] pub(crate) fn pop_types<'a>( &'a mut self, val_types: impl IntoIterator<Item = &'a ValType>, @@ -130,63 +200,30 @@ impl ValueStack { val_types.into_iter().map(|val_type| self.pop_wasmvalue(*val_type)) } - #[inline] pub(crate) fn pop_locals(&mut self, pc: ValueCountsSmall, lc: ValueCounts) -> Locals { Locals { - locals_32: { - let mut locals_32 = { alloc::vec![Value32::default(); lc.c32 as usize].into_boxed_slice() }; - locals_32[0..pc.c32 as usize] - .copy_from_slice(&self.stack_32[(self.stack_32.len() - pc.c32 as usize)..]); - self.stack_32.truncate(self.stack_32.len() - pc.c32 as usize); - locals_32 - }, - locals_64: { - let mut locals_64 = { alloc::vec![Value64::default(); lc.c64 as usize].into_boxed_slice() }; - locals_64[0..pc.c64 as usize] - .copy_from_slice(&self.stack_64[(self.stack_64.len() - pc.c64 as usize)..]); - self.stack_64.truncate(self.stack_64.len() - pc.c64 as usize); - locals_64 - }, - locals_128: { - let mut locals_128 = { alloc::vec![Value128::default(); lc.c128 as usize].into_boxed_slice() }; - locals_128[0..pc.c128 as usize] - .copy_from_slice(&self.stack_128[(self.stack_128.len() - pc.c128 as usize)..]); - self.stack_128.truncate(self.stack_128.len() - pc.c128 as usize); - locals_128 - }, - locals_ref: { - let mut locals_ref = { alloc::vec![ValueRef::default(); lc.cref as usize].into_boxed_slice() }; - locals_ref[0..pc.cref as usize] - .copy_from_slice(&self.stack_ref[(self.stack_ref.len() - pc.cref as usize)..]); - self.stack_ref.truncate(self.stack_ref.len() - pc.cref as usize); - locals_ref - }, + locals_32: self.stack_32.pop_to_locals(pc.c32 as usize, lc.c32 as usize), + locals_64: self.stack_64.pop_to_locals(pc.c64 as usize, lc.c64 as usize), + locals_128: self.stack_128.pop_to_locals(pc.c128 as usize, lc.c128 as usize), + locals_ref: self.stack_ref.pop_to_locals(pc.cref as usize, lc.cref as usize), } } pub(crate) fn truncate_keep(&mut self, to: StackLocation, keep: StackHeight) { - #[inline(always)] - fn truncate_keep<T>(data: &mut Vec<T>, n: u32, end_keep: u32) { - let len = data.len() as u32; - if len <= n { - return; // No need to truncate if the current size is already less than or equal to total_to_keep - } - data.drain((n as usize)..(len - end_keep) as usize); - } - - truncate_keep(&mut self.stack_32, to.s32, u32::from(keep.s32)); - truncate_keep(&mut self.stack_64, to.s64, u32::from(keep.s64)); - truncate_keep(&mut self.stack_128, to.s128, u32::from(keep.s128)); - truncate_keep(&mut self.stack_ref, to.sref, u32::from(keep.sref)); + self.stack_32.truncate_keep(to.s32 as usize, usize::from(keep.s32)); + self.stack_64.truncate_keep(to.s64 as usize, usize::from(keep.s64)); + self.stack_128.truncate_keep(to.s128 as usize, usize::from(keep.s128)); + self.stack_ref.truncate_keep(to.sref as usize, usize::from(keep.sref)); } - pub(crate) fn push_dyn(&mut self, value: TinyWasmValue) { + pub(crate) fn push_dyn(&mut self, value: TinyWasmValue) -> Result<()> { match value { - TinyWasmValue::Value32(v) => self.stack_32.push(v), - TinyWasmValue::Value64(v) => self.stack_64.push(v), - TinyWasmValue::Value128(v) => self.stack_128.push(v), - TinyWasmValue::ValueRef(v) => self.stack_ref.push(v), + TinyWasmValue::Value32(v) => self.stack_32.push(v)?, + TinyWasmValue::Value64(v) => self.stack_64.push(v)?, + TinyWasmValue::Value128(v) => self.stack_128.push(v)?, + TinyWasmValue::ValueRef(v) => self.stack_ref.push(v)?, } + Ok(()) } pub(crate) fn pop_wasmvalue(&mut self, val_type: ValType) -> WasmValue { @@ -201,17 +238,18 @@ impl ValueStack { } } - pub(crate) fn extend_from_wasmvalues(&mut self, values: &[WasmValue]) { + pub(crate) fn extend_from_wasmvalues(&mut self, values: &[WasmValue]) -> Result<()> { for value in values { match value { - WasmValue::I32(v) => self.stack_32.push(*v as u32), - WasmValue::I64(v) => self.stack_64.push(*v as u64), - WasmValue::F32(v) => self.stack_32.push(v.to_bits()), - WasmValue::F64(v) => self.stack_64.push(v.to_bits()), - WasmValue::RefExtern(v) => self.stack_ref.push(v.addr()), - WasmValue::RefFunc(v) => self.stack_ref.push(v.addr()), - WasmValue::V128(v) => self.stack_128.push((*v).into()), + WasmValue::I32(v) => self.stack_32.push(*v as u32)?, + WasmValue::I64(v) => self.stack_64.push(*v as u64)?, + WasmValue::F32(v) => self.stack_32.push(v.to_bits())?, + WasmValue::F64(v) => self.stack_64.push(v.to_bits())?, + WasmValue::RefExtern(v) => self.stack_ref.push(v.addr())?, + WasmValue::RefFunc(v) => self.stack_ref.push(v.addr())?, + WasmValue::V128(v) => self.stack_128.push((*v).into())?, } } + Ok(()) } } diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs index 456fb93..ac05bd4 100644 --- a/crates/tinywasm/src/interpreter/values.rs +++ b/crates/tinywasm/src/interpreter/values.rs @@ -1,4 +1,4 @@ -use crate::{Result, interpreter::value128::Value128}; +use crate::{interpreter::value128::Value128, Result}; use super::stack::{Locals, ValueStack}; use tinywasm_types::{ExternRef, FuncRef, LocalAddr, ValType, WasmValue}; @@ -100,14 +100,19 @@ impl TinyWasmValue { /// Attaches a type to the value (panics if the size of the value is not the same as the type) pub fn attach_type(&self, ty: ValType) -> WasmValue { - match ty { - ValType::I32 => WasmValue::I32(self.unwrap_32() as i32), - ValType::I64 => WasmValue::I64(self.unwrap_64() as i64), - ValType::F32 => WasmValue::F32(f32::from_bits(self.unwrap_32())), - ValType::F64 => WasmValue::F64(f64::from_bits(self.unwrap_64())), - ValType::RefExtern => WasmValue::RefExtern(ExternRef::new(self.unwrap_ref())), - ValType::RefFunc => WasmValue::RefFunc(FuncRef::new(self.unwrap_ref())), - ValType::V128 => WasmValue::V128(self.unwrap_128().into()), + match (self, ty) { + (Self::Value32(v), ValType::I32) => WasmValue::I32(*v as i32), + (Self::Value64(v), ValType::I64) => WasmValue::I64(*v as i64), + (Self::Value32(v), ValType::F32) => WasmValue::F32(f32::from_bits(*v)), + (Self::Value64(v), ValType::F64) => WasmValue::F64(f64::from_bits(*v)), + (Self::ValueRef(v), ValType::RefExtern) => WasmValue::RefExtern(ExternRef::new(*v)), + (Self::ValueRef(v), ValType::RefFunc) => WasmValue::RefFunc(FuncRef::new(*v)), + (Self::Value128(v), ValType::V128) => WasmValue::V128((*v).into()), + + (_, ValType::I32 | ValType::F32) => panic!("Expected Value32"), + (_, ValType::I64 | ValType::F64) => panic!("Expected Value64"), + (_, ValType::RefExtern | ValType::RefFunc) => panic!("Expected ValueRef"), + (_, ValType::V128) => panic!("Expected Value128"), } } } @@ -144,7 +149,7 @@ mod sealed { } pub(crate) trait InternalValue: sealed::Sealed + Into<TinyWasmValue> { - fn stack_push(stack: &mut ValueStack, value: Self); + fn stack_push(stack: &mut ValueStack, value: Self) -> Result<()>; fn replace_top(stack: &mut ValueStack, func: impl FnOnce(Self) -> Result<Self>) -> Result<()> where Self: Sized; @@ -177,63 +182,42 @@ macro_rules! impl_internalvalue { } impl InternalValue for $outer { - #[inline(always)] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.$stack.push($to_internal(value)); + fn stack_push(stack: &mut ValueStack, value: Self) -> Result<()> { + stack.$stack.push($to_internal(value)) } - #[inline(always)] fn stack_pop(stack: &mut ValueStack) -> Self { - match stack.$stack.pop() { - Some(v) => $to_outer(v), - None => unreachable!("ValueStack underflow, this is a bug"), - } + $to_outer(stack.$stack.pop()) } - #[inline(always)] fn stack_peek(stack: &ValueStack) -> Self { - match stack.$stack.last() { - Some(v) => $to_outer(*v), - None => unreachable!("ValueStack underflow, this is a bug"), - } + $to_outer(*stack.$stack.last()) } - #[inline(always)] fn stack_calculate(stack: &mut ValueStack, func: impl FnOnce(Self, Self) -> Result<Self>) -> Result<()> { let v2 = stack.$stack.pop(); let v1 = stack.$stack.last_mut(); - let (Some(v1), Some(v2)) = (v1, v2) else { - unreachable!("ValueStack underflow, this is a bug"); - }; *v1 = $to_internal(func($to_outer(*v1), $to_outer(v2))?); - return Ok(()) + Ok(()) } - #[inline(always)] fn stack_calculate3(stack: &mut ValueStack, func: impl FnOnce(Self, Self, Self) -> Result<Self>) -> Result<()> { let v3 = stack.$stack.pop(); let v2 = stack.$stack.pop(); let v1 = stack.$stack.last_mut(); - let (Some(v1), Some(v2), Some(v3)) = (v1, v2, v3) else { - unreachable!("ValueStack underflow, this is a bug"); - }; *v1 = $to_internal(func($to_outer(*v1), $to_outer(v2), $to_outer(v3))?); - return Ok(()) + Ok(()) } - #[inline(always)] fn replace_top(stack: &mut ValueStack, func: impl FnOnce(Self) -> Result<Self>) -> Result<()> { - let Some(v) = stack.$stack.last_mut() else { - unreachable!("ValueStack underflow, this is a bug"); - }; + let v = stack.$stack.last_mut(); *v = $to_internal(func($to_outer(*v))?); Ok(()) } - #[inline(always)] fn local_get(locals: &Locals, index: LocalAddr) -> Self { match locals.$locals.get(index as usize) { Some(v) => $to_outer(*v), @@ -241,7 +225,6 @@ macro_rules! impl_internalvalue { } } - #[inline(always)] fn local_set(locals: &mut Locals, index: LocalAddr, value: Self) { match locals.$locals.get_mut(index as usize) { Some(v) => *v = $to_internal(value), diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 8898212..022db0d 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -33,7 +33,7 @@ impl MemoryInstance { matches!(self.kind.arch(), MemoryArch::I64) } - #[inline(always)] + #[inline] pub(crate) fn len(&self) -> usize { self.data.len() } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 757cbb2..a0a025c 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -5,7 +5,7 @@ use tinywasm_types::*; use crate::interpreter::TinyWasmValue; use crate::interpreter::stack::Stack; -use crate::{Engine, Error, Function, ModuleInstance, Result, Trap, cold}; +use crate::{Engine, Error, Function, ModuleInstance, Result, Trap}; mod data; mod element; @@ -94,25 +94,31 @@ pub(crate) struct State { impl State { /// Get the function at the actual index in the store - #[inline] pub(crate) fn get_func(&self, addr: FuncAddr) -> &FunctionInstance { - &self.funcs[addr as usize] + match self.funcs.get(addr as usize) { + Some(func) => func, + None => unreachable!("function {addr} not found. This should be unreachable"), + } } /// Get the memory at the actual index in the store - #[inline] pub(crate) fn get_mem(&self, addr: MemAddr) -> &MemoryInstance { - &self.memories[addr as usize] + match self.memories.get(addr as usize) { + Some(mem) => mem, + None => unreachable!("memory {addr} not found. This should be unreachable"), + } } /// Get the memory at the actual index in the store #[inline(always)] pub(crate) fn get_mem_mut(&mut self, addr: MemAddr) -> &mut MemoryInstance { - &mut self.memories[addr as usize] + match self.memories.get_mut(addr as usize) { + Some(mem) => mem, + None => unreachable!("memory {addr} not found. This should be unreachable"), + } } /// Get the memory at the actual index in the store - #[inline(always)] pub(crate) fn get_mems_mut( &mut self, addr: MemAddr, @@ -120,27 +126,27 @@ impl State { ) -> Result<(&mut MemoryInstance, &mut MemoryInstance)> { match get_pair_mut(&mut self.memories, addr as usize, addr2 as usize) { Some(mems) => Ok(mems), - None => { - cold(); - Err(Self::not_found_error("memory")) - } + None => unreachable!("memory {addr} or {addr2} not found. This should be unreachable"), } } /// Get the table at the actual index in the store - #[inline] pub(crate) fn get_table(&self, addr: TableAddr) -> &TableInstance { - &self.tables[addr as usize] + match self.tables.get(addr as usize) { + Some(table) => table, + None => unreachable!("table {addr} not found. This should be unreachable"), + } } /// Get the table at the actual index in the store - #[inline] pub(crate) fn get_table_mut(&mut self, addr: TableAddr) -> &mut TableInstance { - &mut self.tables[addr as usize] + match self.tables.get_mut(addr as usize) { + Some(table) => table, + None => unreachable!("table {addr} not found. This should be unreachable"), + } } /// Get two mutable tables at the actual index in the store - #[inline] pub(crate) fn get_tables_mut( &mut self, addr: TableAddr, @@ -148,44 +154,48 @@ impl State { ) -> Result<(&mut TableInstance, &mut TableInstance)> { match get_pair_mut(&mut self.tables, addr as usize, addr2 as usize) { Some(tables) => Ok(tables), - None => { - cold(); - Err(Self::not_found_error("table")) - } + None => unreachable!("table {addr} or {addr2} not found. This should be unreachable"), } } /// Get the data at the actual index in the store - #[inline] pub(crate) fn get_data_mut(&mut self, addr: DataAddr) -> &mut DataInstance { - &mut self.data[addr as usize] + match self.data.get_mut(addr as usize) { + Some(data) => data, + None => unreachable!("data {addr} not found. This should be unreachable"), + } } /// Get the element at the actual index in the store - #[inline] pub(crate) fn get_elem_mut(&mut self, addr: ElemAddr) -> &mut ElementInstance { - &mut self.elements[addr as usize] + match self.elements.get_mut(addr as usize) { + Some(elem) => elem, + None => unreachable!("element {addr} not found. This should be unreachable"), + } } /// Get the global at the actual index in the store - #[inline] pub(crate) fn get_global(&self, addr: GlobalAddr) -> &GlobalInstance { - &self.globals[addr as usize] + match self.globals.get(addr as usize) { + Some(global) => global, + None => unreachable!("global {addr} not found. This should be unreachable"), + } } /// Get the global at the actual index in the store pub(crate) fn get_global_val(&self, addr: MemAddr) -> TinyWasmValue { - self.globals[addr as usize].value.get() + match self.globals.get(addr as usize) { + Some(global) => global.value.get(), + None => unreachable!("global {addr} not found. This should be unreachable"), + } } /// Set the global at the actual index in the store pub(crate) fn set_global_val(&mut self, addr: MemAddr, value: TinyWasmValue) { - self.globals[addr as usize].value.set(value); - } - - #[cold] - fn not_found_error(name: &str) -> Error { - Error::Other(format!("{name} not found")) + match self.globals.get_mut(addr as usize) { + Some(global) => global.value.set(value), + None => unreachable!("global {addr} not found. This should be unreachable"), + } } } @@ -421,7 +431,7 @@ impl Store { } /// Evaluate a constant expression that's either a i32 or a i64 as a global or a const instruction - pub(crate) fn eval_size_const(&self, const_instr: tinywasm_types::ConstInstruction) -> Result<i64> { + fn eval_size_const(&self, const_instr: tinywasm_types::ConstInstruction) -> Result<i64> { Ok(match const_instr { ConstInstruction::I32Const(i) => i64::from(i), ConstInstruction::I64Const(i) => i, @@ -435,7 +445,7 @@ impl Store { } /// Evaluate a constant expression - pub(crate) fn eval_const( + fn eval_const( &self, const_instr: &tinywasm_types::ConstInstruction, module_global_addrs: &[Addr], |
