diff options
Diffstat (limited to 'crates')
21 files changed, 739 insertions, 778 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 214487f..c733dcc 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -176,7 +176,7 @@ pub(crate) fn convert_module_code( // maps a local's address to the index in the type's locals array let mut local_addr_map = Vec::with_capacity(count as usize); - let mut local_counts = LocalCounts::default(); + let mut local_counts = ValueCounts::default(); for (i, local) in locals_reader.into_iter().enumerate() { let local = local?; @@ -186,20 +186,20 @@ pub(crate) fn convert_module_code( for i in 0..validator.len_locals() { match validator.get_local_type(i) { Some(wasmparser::ValType::I32) | Some(wasmparser::ValType::F32) => { - local_addr_map.push(local_counts.local_32); - local_counts.local_32 += 1; + local_addr_map.push(local_counts.c32); + local_counts.c32 += 1; } Some(wasmparser::ValType::I64) | Some(wasmparser::ValType::F64) => { - local_addr_map.push(local_counts.local_64); - local_counts.local_64 += 1; + local_addr_map.push(local_counts.c64); + local_counts.c64 += 1; } Some(wasmparser::ValType::V128) => { - local_addr_map.push(local_counts.local_128); - local_counts.local_128 += 1; + local_addr_map.push(local_counts.c128); + local_counts.c128 += 1; } Some(wasmparser::ValType::Ref(_)) => { - local_addr_map.push(local_counts.local_ref); - local_counts.local_ref += 1; + local_addr_map.push(local_counts.cref); + local_counts.cref += 1; } None => return Err(crate::ParseError::UnsupportedOperator("Unknown local type".to_string())), } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 77349b2..40f5fe2 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -59,6 +59,7 @@ impl Parser { sign_extension: true, saturating_float_to_int: true, function_references: true, + tail_call: true, component_model: false, component_model_nested_names: false, @@ -71,7 +72,6 @@ impl Parser { memory_control: false, relaxed_simd: false, simd: false, - tail_call: false, threads: false, multi_memory: false, // should be working mostly custom_page_sizes: false, diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 3ee619f..74da1d9 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -3,12 +3,12 @@ use crate::{conversion, ParseError, Result}; use alloc::string::ToString; use alloc::{boxed::Box, format, vec::Vec}; use tinywasm_types::{ - Data, Element, Export, FuncType, Global, Import, Instruction, LocalCounts, MemoryType, TableType, TinyWasmModule, - WasmFunction, + Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, TinyWasmModule, ValType, + ValueCounts, ValueCountsSmall, WasmFunction, }; use wasmparser::{FuncValidatorAllocations, Payload, Validator}; -pub(crate) type Code = (Box<[Instruction]>, LocalCounts); +pub(crate) type Code = (Box<[Instruction]>, ValueCounts); #[derive(Default)] pub(crate) struct ModuleReader { @@ -193,10 +193,18 @@ impl ModuleReader { .code .into_iter() .zip(self.code_type_addrs) - .map(|((instructions, locals), ty_idx)| WasmFunction { - instructions, - locals, - ty: self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(), + .map(|((instructions, locals), ty_idx)| { + let mut params = ValueCountsSmall::default(); + let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(); + for param in ty.params.iter() { + match param { + ValType::I32 | ValType::F32 => params.c32 += 1, + ValType::I64 | ValType::F64 => params.c64 += 1, + ValType::V128 => params.c128 += 1, + ValType::RefExtern | ValType::RefFunc => params.cref += 1, + } + } + WasmFunction { instructions, params, locals, ty } }) .collect::<Vec<_>>() .into_boxed_slice(); diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index e9844aa..1f282f7 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -122,6 +122,7 @@ macro_rules! impl_visit_operator { (@@sign_extension $($rest:tt)* ) => {}; (@@saturating_float_to_int $($rest:tt)* ) => {}; (@@bulk_memory $($rest:tt)* ) => {}; + (@@tail_call $($rest:tt)* ) => {}; (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => { #[cold] fn $visit(&mut self $($(,$arg: $argty)*)?) { @@ -317,6 +318,14 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild visit_i64_trunc_sat_f64_u, Instruction::I64TruncSatF64U } + fn visit_return_call(&mut self, function_index: u32) -> Self::Output { + self.instructions.push(Instruction::ReturnCall(function_index)); + } + + fn visit_return_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output { + self.instructions.push(Instruction::ReturnCallIndirect(type_index, table_index)); + } + fn visit_global_set(&mut self, global_index: u32) -> Self::Output { match self.validator.get_operand_type(0) { Some(Some(t)) => self.instructions.push(match t { @@ -385,6 +394,30 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild return; }; + match self.instructions.last() { + Some(Instruction::LocalGet32(from)) + | Some(Instruction::LocalGet64(from)) + | Some(Instruction::LocalGet128(from)) + | Some(Instruction::LocalGetRef(from)) => { + let from = *from; + self.instructions.pop(); + // validation will ensure that the last instruction is the correct local.get + match self.validator.get_operand_type(0) { + Some(Some(t)) => self.instructions.push(match t { + wasmparser::ValType::I32 => Instruction::LocalCopy32(from, resolved_idx), + wasmparser::ValType::F32 => Instruction::LocalCopy32(from, resolved_idx), + wasmparser::ValType::I64 => Instruction::LocalCopy64(from, resolved_idx), + wasmparser::ValType::F64 => Instruction::LocalCopy64(from, resolved_idx), + wasmparser::ValType::V128 => Instruction::LocalCopy128(from, resolved_idx), + wasmparser::ValType::Ref(_) => Instruction::LocalCopyRef(from, resolved_idx), + }), + _ => self.visit_unreachable(), + } + return; + } + _ => {} + } + match self.validator.get_operand_type(0) { Some(Some(t)) => self.instructions.push(match t { wasmparser::ValType::I32 => Instruction::LocalSet32(resolved_idx), diff --git a/crates/tinywasm/benches/argon2id.rs b/crates/tinywasm/benches/argon2id.rs index d87c78e..b951a56 100644 --- a/crates/tinywasm/benches/argon2id.rs +++ b/crates/tinywasm/benches/argon2id.rs @@ -25,7 +25,7 @@ fn argon2id_run(module: TinyWasmModule) -> Result<()> { let mut store = Store::default(); let instance = ModuleInstance::instantiate(&mut store, module.into(), None)?; let argon2 = instance.exported_func::<(i32, i32, i32), i32>(&store, "argon2id")?; - argon2.call(&mut store, (1000, 1, 1))?; + argon2.call(&mut store, (1000, 2, 1))?; Ok(()) } diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 0c698fa..c2413a1 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -1,5 +1,5 @@ use alloc::string::{String, ToString}; -use core::fmt::Display; +use core::{fmt::Display, ops::ControlFlow}; use tinywasm_types::FuncType; #[cfg(feature = "parser")] @@ -23,15 +23,6 @@ pub enum Error { /// A function did not return a value FuncDidNotReturn, - /// The stack is empty - ValueStackUnderflow, - - /// The label stack is empty - BlockStackUnderflow, - - /// The call stack is empty - CallStackUnderflow, - /// An invalid label type was encountered InvalidLabelType, @@ -189,13 +180,10 @@ impl Display for Error { Self::Trap(trap) => write!(f, "trap: {}", trap), Self::Linker(err) => write!(f, "linking error: {}", err), - Self::CallStackUnderflow => write!(f, "call stack empty"), Self::InvalidLabelType => write!(f, "invalid label type"), Self::Other(message) => write!(f, "unknown error: {}", message), Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {}", feature), Self::FuncDidNotReturn => write!(f, "function did not return"), - Self::BlockStackUnderflow => write!(f, "label stack underflow"), - Self::ValueStackUnderflow => write!(f, "value stack underflow"), Self::InvalidStore => write!(f, "invalid store"), } } @@ -249,3 +237,16 @@ impl From<tinywasm_parser::ParseError> for Error { /// A wrapper around [`core::result::Result`] for tinywasm operations pub type Result<T, E = Error> = crate::std::result::Result<T, E>; + +pub(crate) trait Controlify<T> { + fn to_cf(self) -> ControlFlow<Option<Error>, T>; +} + +impl<T> Controlify<T> for Result<T, Error> { + fn to_cf(self) -> ControlFlow<Option<Error>, T> { + match self { + Ok(value) => ControlFlow::Continue(value), + Err(err) => ControlFlow::Break(Some(err)), + } + } +} diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 681ca7e..f2017fe 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -48,7 +48,7 @@ impl FuncHandle { return Err(Error::Other("Type mismatch".into())); } - let func_inst = store.get_func(self.addr)?; + let func_inst = store.get_func(&self.addr); let wasm_func = match &func_inst.func { Function::Host(host_func) => { let func = &host_func.clone().func; @@ -76,7 +76,7 @@ impl FuncHandle { // assert!(stack.values.len() >= result_m); // 2. Pop m values from the stack - let res = stack.values.pop_results(&func_ty.results)?; + let res = stack.values.pop_results(&func_ty.results); // The values are returned as the results of the invocation. Ok(res) diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 82ae8c8..3d29dfc 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -143,8 +143,6 @@ impl Extern { } /// Create a new typed function import - // TODO: currently, this is slower than `Extern::func` because of the type conversions. - // we should be able to optimize this and make it even faster than `Extern::func`. pub fn typed_func<P, R>(func: impl Fn(FuncContext<'_>, P) -> Result<R> + 'static) -> Self where P: FromWasmValueTuple + ValTypesFromTuple, @@ -388,23 +386,23 @@ impl Imports { match (val, &import.kind) { (ExternVal::Global(global_addr), ImportKind::Global(ty)) => { - let global = store.get_global(global_addr)?; + let global = store.get_global(&global_addr); Self::compare_types(import, &global.ty, ty)?; imports.globals.push(global_addr); } (ExternVal::Table(table_addr), ImportKind::Table(ty)) => { - let table = store.get_table(table_addr)?; + let table = store.get_table(&table_addr); Self::compare_table_types(import, &table.kind, ty)?; imports.tables.push(table_addr); } (ExternVal::Memory(memory_addr), ImportKind::Memory(ty)) => { - let mem = store.get_mem(memory_addr)?; - let (size, kind) = { (mem.page_count(), mem.kind) }; + let mem = store.get_mem(&memory_addr); + let (size, kind) = { (mem.page_count, mem.kind) }; Self::compare_memory_types(import, &kind, ty, Some(size))?; imports.memories.push(memory_addr); } (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { - let func = store.get_func(func_addr)?; + let func = store.get_func(&func_addr); let import_func_type = module .0 .func_types diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 74a1598..0f72676 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -127,45 +127,40 @@ impl ModuleInstance { &self.0.func_addrs } - #[cold] - fn not_found_error(name: &str) -> Error { - Error::Other(format!("address for {} not found", name)) - } - // resolve a function address to the global store address #[inline] - pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> Result<FuncAddr> { - self.0.func_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("function")).copied() + pub(crate) fn resolve_func_addr(&self, addr: FuncAddr) -> &FuncAddr { + &self.0.func_addrs[addr as usize] } // resolve a table address to the global store address #[inline] - pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> Result<TableAddr> { - self.0.table_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("table")).copied() + pub(crate) fn resolve_table_addr(&self, addr: TableAddr) -> &TableAddr { + &self.0.table_addrs[addr as usize] } // resolve a memory address to the global store address #[inline] - pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> Result<MemAddr> { - self.0.mem_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("mem")).copied() + pub(crate) fn resolve_mem_addr(&self, addr: MemAddr) -> &MemAddr { + &self.0.mem_addrs[addr as usize] } // resolve a data address to the global store address #[inline] - pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> Result<DataAddr> { - self.0.data_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("data")).copied() + pub(crate) fn resolve_data_addr(&self, addr: DataAddr) -> &DataAddr { + &self.0.data_addrs[addr as usize] } // resolve a memory address to the global store address #[inline] - pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> Result<ElemAddr> { - self.0.elem_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("elem")).copied() + pub(crate) fn resolve_elem_addr(&self, addr: ElemAddr) -> &ElemAddr { + &self.0.elem_addrs[addr as usize] } // resolve a global address to the global store address #[inline] - pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> Result<GlobalAddr> { - self.0.global_addrs.get(addr as usize).ok_or_else(|| Self::not_found_error("global")).copied() + pub(crate) fn resolve_global_addr(&self, addr: GlobalAddr) -> &GlobalAddr { + &self.0.global_addrs[addr as usize] } /// Get an exported function by name @@ -179,9 +174,7 @@ impl ModuleInstance { return Err(Error::Other(format!("Export is not a function: {}", name))); }; - let func_inst = store.get_func(func_addr)?; - let ty = func_inst.func.ty(); - + let ty = store.get_func(&func_addr).func.ty(); Ok(FuncHandle { addr: func_addr, module_addr: self.id(), name: Some(name.to_string()), ty: ty.clone() }) } @@ -217,13 +210,13 @@ impl ModuleInstance { /// Get a memory by address pub fn memory<'a>(&self, store: &'a Store, addr: MemAddr) -> Result<MemoryRef<'a>> { - let mem = store.get_mem(self.resolve_mem_addr(addr)?)?; + let mem = store.get_mem(self.resolve_mem_addr(addr)); Ok(MemoryRef(mem)) } /// Get a memory by address (mutable) pub fn memory_mut<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRefMut<'a>> { - let mem = store.get_mem_mut(self.resolve_mem_addr(addr)?)?; + let mem = store.get_mem_mut(self.resolve_mem_addr(addr)); Ok(MemoryRefMut(mem)) } @@ -250,8 +243,8 @@ impl ModuleInstance { } }; - let func_addr = self.0.func_addrs.get(func_index as usize).expect("No func addr for start func, this is a bug"); - let func_inst = store.get_func(*func_addr)?; + let func_addr = self.resolve_func_addr(func_index); + let func_inst = store.get_func(func_addr); let ty = func_inst.func.ty(); Ok(Some(FuncHandle { module_addr: self.id(), addr: *func_addr, ty: ty.clone(), name: None })) diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 353f778..a9516f0 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -21,7 +21,7 @@ pub(super) struct Executor<'store, 'stack> { impl<'store, 'stack> Executor<'store, 'stack> { pub(crate) fn new(store: &'store mut Store, stack: &'stack mut Stack) -> Result<Self> { - let current_frame = stack.call_stack.pop().ok_or_else(|| Error::CallStackUnderflow)?; + let current_frame = stack.call_stack.pop().expect("no call frame, this is a bug"); let current_module = store.get_module_instance_raw(current_frame.module_addr()); Ok(Self { cf: current_frame, module: current_module, stack, store }) } @@ -29,329 +29,249 @@ impl<'store, 'stack> Executor<'store, 'stack> { #[inline] pub(crate) fn run_to_completion(&mut self) -> Result<()> { loop { - // TODO: the result checking takes about 10% of the time - match self.exec_next()? { - ControlFlow::Break(..) => return Ok(()), - ControlFlow::Continue(..) => continue, - }; + if let ControlFlow::Break(res) = self.exec_next() { + return match res { + Some(e) => Err(e), + None => Ok(()), + }; + } } } #[inline(always)] - fn exec_next(&mut self) -> Result<ControlFlow<()>> { + fn exec_next(&mut self) -> ControlFlow<Option<Error>> { use tinywasm_types::Instruction::*; match self.cf.fetch_instr() { Nop => self.exec_noop(), Unreachable => self.exec_unreachable()?, - Drop32 => self.stack.values.drop::<Value32>()?, - Drop64 => self.stack.values.drop::<Value64>()?, - Drop128 => self.stack.values.drop::<Value128>()?, - DropRef => self.stack.values.drop::<ValueRef>()?, + Drop32 => self.stack.values.drop::<Value32>(), + Drop64 => self.stack.values.drop::<Value64>(), + Drop128 => self.stack.values.drop::<Value128>(), + DropRef => self.stack.values.drop::<ValueRef>(), - Select32 => self.stack.values.select::<Value32>()?, - Select64 => self.stack.values.select::<Value64>()?, - Select128 => self.stack.values.select::<Value128>()?, - SelectRef => self.stack.values.select::<ValueRef>()?, + Select32 => self.stack.values.select::<Value32>(), + Select64 => self.stack.values.select::<Value64>(), + Select128 => self.stack.values.select::<Value128>(), + SelectRef => self.stack.values.select::<ValueRef>(), Call(v) => return self.exec_call_direct(*v), CallIndirect(ty, table) => return self.exec_call_indirect(*ty, *table), - If(end, el) => self.exec_if(*end, *el, (Default::default(), Default::default()))?, - IfWithType(ty, end, el) => self.exec_if(*end, *el, (Default::default(), (*ty).into()))?, - IfWithFuncType(ty, end, el) => self.exec_if(*end, *el, self.resolve_functype(*ty))?, - Else(end_offset) => self.exec_else(*end_offset)?, - Loop(end) => { - self.enter_block(self.cf.instr_ptr(), *end, BlockType::Loop, (Default::default(), Default::default())) - } - LoopWithType(ty, end) => { - self.enter_block(self.cf.instr_ptr(), *end, BlockType::Loop, (Default::default(), (*ty).into())) - } - LoopWithFuncType(ty, end) => { - self.enter_block(self.cf.instr_ptr(), *end, BlockType::Loop, self.resolve_functype(*ty)) - } - Block(end) => { - self.enter_block(self.cf.instr_ptr(), *end, BlockType::Block, (Default::default(), Default::default())) - } - BlockWithType(ty, end) => { - self.enter_block(self.cf.instr_ptr(), *end, BlockType::Block, (Default::default(), (*ty).into())) - } - BlockWithFuncType(ty, end) => { - self.enter_block(self.cf.instr_ptr(), *end, BlockType::Block, self.resolve_functype(*ty)) - } + If(end, el) => self.exec_if(*end, *el, (Default::default(), Default::default())), + IfWithType(ty, end, el) => self.exec_if(*end, *el, (Default::default(), (*ty).into())), + IfWithFuncType(ty, end, el) => self.exec_if(*end, *el, self.resolve_functype(*ty)), + Else(end_offset) => self.exec_else(*end_offset), + Loop(end) => self.enter_block(*end, BlockType::Loop, (Default::default(), Default::default())), + LoopWithType(ty, end) => self.enter_block(*end, BlockType::Loop, (Default::default(), (*ty).into())), + LoopWithFuncType(ty, end) => self.enter_block(*end, BlockType::Loop, self.resolve_functype(*ty)), + Block(end) => self.enter_block(*end, BlockType::Block, (Default::default(), Default::default())), + BlockWithType(ty, end) => self.enter_block(*end, BlockType::Block, (Default::default(), (*ty).into())), + BlockWithFuncType(ty, end) => self.enter_block(*end, BlockType::Block, self.resolve_functype(*ty)), Br(v) => return self.exec_br(*v), BrIf(v) => return self.exec_br_if(*v), BrTable(default, len) => return self.exec_brtable(*default, *len), BrLabel(_) => {} Return => return self.exec_return(), - EndBlockFrame => self.exec_end_block()?, + EndBlockFrame => self.exec_end_block(), - LocalGet32(local_index) => self.exec_local_get::<Value32>(*local_index)?, - LocalGet64(local_index) => self.exec_local_get::<Value64>(*local_index)?, - LocalGet128(local_index) => self.exec_local_get::<Value128>(*local_index)?, - LocalGetRef(local_index) => self.exec_local_get::<ValueRef>(*local_index)?, + LocalGet32(local_index) => self.exec_local_get::<Value32>(*local_index), + LocalGet64(local_index) => self.exec_local_get::<Value64>(*local_index), + LocalGet128(local_index) => self.exec_local_get::<Value128>(*local_index), + LocalGetRef(local_index) => self.exec_local_get::<ValueRef>(*local_index), - LocalSet32(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value32>()?)?, - LocalSet64(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value64>()?)?, - LocalSet128(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<Value128>()?)?, - LocalSetRef(local_index) => self.cf.locals.set(*local_index, self.stack.values.pop::<ValueRef>()?)?, + LocalSet32(local_index) => self.exec_local_set::<Value32>(*local_index), + LocalSet64(local_index) => self.exec_local_set::<Value64>(*local_index), + LocalSet128(local_index) => self.exec_local_set::<Value128>(*local_index), + LocalSetRef(local_index) => self.exec_local_set::<ValueRef>(*local_index), - LocalTee32(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<Value32>()?)?, - LocalTee64(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<Value64>()?)?, - LocalTee128(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<Value128>()?)?, - LocalTeeRef(local_index) => self.cf.locals.set(*local_index, self.stack.values.peek::<ValueRef>()?)?, + LocalTee32(local_index) => self.exec_local_tee::<Value32>(*local_index), + LocalTee64(local_index) => self.exec_local_tee::<Value64>(*local_index), + LocalTee128(local_index) => self.exec_local_tee::<Value128>(*local_index), + LocalTeeRef(local_index) => self.exec_local_tee::<ValueRef>(*local_index), - GlobalGet(global_index) => self.exec_global_get(*global_index)?, - 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)?, + GlobalGet(global_index) => self.exec_global_get(*global_index), + 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.stack.values.push(*val), - I64Const(val) => self.stack.values.push(*val), - F32Const(val) => self.stack.values.push::<i32>(val.to_bits() as i32), - F64Const(val) => self.stack.values.push(val.to_bits() as i64), - RefFunc(func_idx) => self.stack.values.push(Some(*func_idx)), // do we need to resolve the function index? - RefNull(_) => self.stack.values.push(None), - RefIsNull => self.exec_ref_is_null()?, + I32Const(val) => self.exec_const(*val), + I64Const(val) => self.exec_const(*val), + F32Const(val) => self.exec_const(*val), + F64Const(val) => self.exec_const(*val), + 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)?, + MemorySize(addr) => self.exec_memory_size(*addr), + MemoryGrow(addr) => self.exec_memory_grow(*addr), // Bulk memory operations - MemoryCopy(from, to) => self.exec_memory_copy(*from, *to)?, - MemoryFill(addr) => self.exec_memory_fill(*addr)?, - MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx)?, - DataDrop(data_index) => self.exec_data_drop(*data_index)?, - ElemDrop(elem_index) => self.exec_elem_drop(*elem_index)?, - TableCopy { from, to } => self.exec_table_copy(*from, *to)?, - - I32Store { mem_addr, offset } => { - let v = self.stack.values.pop::<i32>()?; - self.exec_mem_store::<i32, 4>(v, *mem_addr, *offset)? - } - I64Store { mem_addr, offset } => { - let v = self.stack.values.pop::<i64>()?; - self.exec_mem_store::<i64, 8>(v, *mem_addr, *offset)? - } - F32Store { mem_addr, offset } => { - let v = self.stack.values.pop::<f32>()?; - self.exec_mem_store::<f32, 4>(v, *mem_addr, *offset)? - } - F64Store { mem_addr, offset } => { - let v = self.stack.values.pop::<f64>()?; - self.exec_mem_store::<f64, 8>(v, *mem_addr, *offset)? - } - I32Store8 { mem_addr, offset } => { - let v = self.stack.values.pop::<i32>()? as i8; - self.exec_mem_store::<i8, 1>(v, *mem_addr, *offset)? - } - I32Store16 { mem_addr, offset } => { - let v = self.stack.values.pop::<i32>()? as i16; - self.exec_mem_store::<i16, 2>(v, *mem_addr, *offset)? - } - I64Store8 { mem_addr, offset } => { - let v = self.stack.values.pop::<i64>()? as i8; - self.exec_mem_store::<i8, 1>(v, *mem_addr, *offset)? - } - I64Store16 { mem_addr, offset } => { - let v = self.stack.values.pop::<i64>()? as i16; - self.exec_mem_store::<i16, 2>(v, *mem_addr, *offset)? - } - I64Store32 { mem_addr, offset } => { - let v = self.stack.values.pop::<i64>()? as i32; - self.exec_mem_store::<i32, 4>(v, *mem_addr, *offset)? - } + MemoryCopy(from, to) => self.exec_memory_copy(*from, *to).to_cf()?, + MemoryFill(addr) => self.exec_memory_fill(*addr).to_cf()?, + MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx).to_cf()?, + DataDrop(data_index) => self.exec_data_drop(*data_index), + ElemDrop(elem_index) => self.exec_elem_drop(*elem_index), + TableCopy { from, to } => self.exec_table_copy(*from, *to).to_cf()?, - I32Load { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(|v| v, *mem_addr, *offset)?, - I64Load { mem_addr, offset } => self.exec_mem_load::<i64, 8, _>(|v| v, *mem_addr, *offset)?, - F32Load { mem_addr, offset } => self.exec_mem_load::<f32, 4, _>(|v| v, *mem_addr, *offset)?, - F64Load { mem_addr, offset } => self.exec_mem_load::<f64, 8, _>(|v| v, *mem_addr, *offset)?, - I32Load8S { mem_addr, offset } => self.exec_mem_load::<i8, 1, _>(|v| v as i32, *mem_addr, *offset)?, - I32Load8U { mem_addr, offset } => self.exec_mem_load::<u8, 1, _>(|v| v as i32, *mem_addr, *offset)?, - I32Load16S { mem_addr, offset } => self.exec_mem_load::<i16, 2, _>(|v| v as i32, *mem_addr, *offset)?, - I32Load16U { mem_addr, offset } => self.exec_mem_load::<u16, 2, _>(|v| v as i32, *mem_addr, *offset)?, - I64Load8S { mem_addr, offset } => self.exec_mem_load::<i8, 1, _>(|v| v as i64, *mem_addr, *offset)?, - I64Load8U { mem_addr, offset } => self.exec_mem_load::<u8, 1, _>(|v| v as i64, *mem_addr, *offset)?, - I64Load16S { mem_addr, offset } => self.exec_mem_load::<i16, 2, _>(|v| v as i64, *mem_addr, *offset)?, - I64Load16U { mem_addr, offset } => self.exec_mem_load::<u16, 2, _>(|v| v as i64, *mem_addr, *offset)?, - I64Load32S { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(|v| v as i64, *mem_addr, *offset)?, - I64Load32U { mem_addr, offset } => self.exec_mem_load::<u32, 4, _>(|v| v as i64, *mem_addr, *offset)?, + I32Store { mem_addr, offset } => self.exec_mem_store::<i32, i32, 4>(*mem_addr, *offset, |v| v)?, + I64Store { mem_addr, offset } => self.exec_mem_store::<i64, i64, 8>(*mem_addr, *offset, |v| v)?, + F32Store { mem_addr, offset } => self.exec_mem_store::<f32, f32, 4>(*mem_addr, *offset, |v| v)?, + F64Store { mem_addr, offset } => self.exec_mem_store::<f64, f64, 8>(*mem_addr, *offset, |v| v)?, + I32Store8 { mem_addr, offset } => self.exec_mem_store::<i32, i8, 1>(*mem_addr, *offset, |v| v as i8)?, + I32Store16 { mem_addr, offset } => self.exec_mem_store::<i32, i16, 2>(*mem_addr, *offset, |v| v as i16)?, + I64Store8 { mem_addr, offset } => self.exec_mem_store::<i64, i8, 1>(*mem_addr, *offset, |v| v as i8)?, + I64Store16 { mem_addr, offset } => self.exec_mem_store::<i64, i16, 2>(*mem_addr, *offset, |v| v as i16)?, + I64Store32 { mem_addr, offset } => self.exec_mem_store::<i64, i32, 4>(*mem_addr, *offset, |v| v as i32)?, - I64Eqz => self.stack.values.replace_top::<i64, _>(|v| Ok((v == 0) as i32))?, - I32Eqz => self.stack.values.replace_top::<i32, _>(|v| Ok((v == 0) as i32))?, - I32Eq => self.stack.values.calculate::<i32, _>(|a, b| Ok((a == b) as i32))?, - I64Eq => self.stack.values.calculate::<i64, _>(|a, b| Ok((a == b) as i32))?, - F32Eq => self.stack.values.calculate::<f32, _>(|a, b| Ok((a == b) as i32))?, - F64Eq => self.stack.values.calculate::<f64, _>(|a, b| Ok((a == b) as i32))?, + I32Load { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(*mem_addr, *offset, |v| v)?, + I64Load { mem_addr, offset } => self.exec_mem_load::<i64, 8, _>(*mem_addr, *offset, |v| v)?, + F32Load { mem_addr, offset } => self.exec_mem_load::<f32, 4, _>(*mem_addr, *offset, |v| v)?, + F64Load { mem_addr, offset } => self.exec_mem_load::<f64, 8, _>(*mem_addr, *offset, |v| v)?, + I32Load8S { mem_addr, offset } => self.exec_mem_load::<i8, 1, _>(*mem_addr, *offset, |v| v as i32)?, + I32Load8U { mem_addr, offset } => self.exec_mem_load::<u8, 1, _>(*mem_addr, *offset, |v| v as i32)?, + I32Load16S { mem_addr, offset } => self.exec_mem_load::<i16, 2, _>(*mem_addr, *offset, |v| v as i32)?, + I32Load16U { mem_addr, offset } => self.exec_mem_load::<u16, 2, _>(*mem_addr, *offset, |v| v as i32)?, + I64Load8S { mem_addr, offset } => self.exec_mem_load::<i8, 1, _>(*mem_addr, *offset, |v| v as i64)?, + I64Load8U { mem_addr, offset } => self.exec_mem_load::<u8, 1, _>(*mem_addr, *offset, |v| v as i64)?, + I64Load16S { mem_addr, offset } => self.exec_mem_load::<i16, 2, _>(*mem_addr, *offset, |v| v as i64)?, + I64Load16U { mem_addr, offset } => self.exec_mem_load::<u16, 2, _>(*mem_addr, *offset, |v| v as i64)?, + I64Load32S { mem_addr, offset } => self.exec_mem_load::<i32, 4, _>(*mem_addr, *offset, |v| v as i64)?, + I64Load32U { mem_addr, offset } => self.exec_mem_load::<u32, 4, _>(*mem_addr, *offset, |v| v as i64)?, - I32Ne => self.stack.values.calculate::<i32, _>(|a, b| Ok((a != b) as i32))?, - I64Ne => self.stack.values.calculate::<i64, _>(|a, b| Ok((a != b) as i32))?, - F32Ne => self.stack.values.calculate::<f32, _>(|a, b| Ok((a != b) as i32))?, - F64Ne => self.stack.values.calculate::<f64, _>(|a, b| Ok((a != b) as i32))?, + I64Eqz => self.stack.values.replace_top::<i64, _>(|v| Ok((v == 0) as i32)).to_cf()?, + I32Eqz => self.stack.values.replace_top_same::<i32>(|v| Ok((v == 0) as i32)).to_cf()?, + I32Eq => self.stack.values.calculate_same::<i32>(|a, b| Ok((a == b) as i32)).to_cf()?, + I64Eq => self.stack.values.calculate::<i64, _>(|a, b| Ok((a == b) as i32)).to_cf()?, + F32Eq => self.stack.values.calculate::<f32, _>(|a, b| Ok((a == b) as i32)).to_cf()?, + F64Eq => self.stack.values.calculate::<f64, _>(|a, b| Ok((a == b) as i32)).to_cf()?, - I32LtS => self.stack.values.calculate::<i32, _>(|a, b| Ok((a < b) as i32))?, - I64LtS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a < b) as i32))?, - I32LtU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a < b) as i32))?, - I64LtU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a < b) as i32))?, - F32Lt => self.stack.values.calculate::<f32, _>(|a, b| Ok((a < b) as i32))?, - F64Lt => self.stack.values.calculate::<f64, _>(|a, b| Ok((a < b) as i32))?, + I32Ne => self.stack.values.calculate_same::<i32>(|a, b| Ok((a != b) as i32)).to_cf()?, + I64Ne => self.stack.values.calculate::<i64, _>(|a, b| Ok((a != b) as i32)).to_cf()?, + F32Ne => self.stack.values.calculate::<f32, _>(|a, b| Ok((a != b) as i32)).to_cf()?, + F64Ne => self.stack.values.calculate::<f64, _>(|a, b| Ok((a != b) as i32)).to_cf()?, - I32LeS => self.stack.values.calculate::<i32, _>(|a, b| Ok((a <= b) as i32))?, - I64LeS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a <= b) as i32))?, - I32LeU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a <= b) as i32))?, - I64LeU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a <= b) as i32))?, - F32Le => self.stack.values.calculate::<f32, _>(|a, b| Ok((a <= b) as i32))?, - F64Le => self.stack.values.calculate::<f64, _>(|a, b| Ok((a <= b) as i32))?, + I32LtS => self.stack.values.calculate_same::<i32>(|a, b| Ok((a < b) as i32)).to_cf()?, + I64LtS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a < b) as i32)).to_cf()?, + I32LtU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a < b) as i32)).to_cf()?, + I64LtU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a < b) as i32)).to_cf()?, + F32Lt => self.stack.values.calculate::<f32, _>(|a, b| Ok((a < b) as i32)).to_cf()?, + F64Lt => self.stack.values.calculate::<f64, _>(|a, b| Ok((a < b) as i32)).to_cf()?, - I32GeS => self.stack.values.calculate::<i32, _>(|a, b| Ok((a >= b) as i32))?, - I64GeS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a >= b) as i32))?, - I32GeU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a >= b) as i32))?, - I64GeU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a >= b) as i32))?, - F32Ge => self.stack.values.calculate::<f32, _>(|a, b| Ok((a >= b) as i32))?, - F64Ge => self.stack.values.calculate::<f64, _>(|a, b| Ok((a >= b) as i32))?, + I32LeS => self.stack.values.calculate_same::<i32>(|a, b| Ok((a <= b) as i32)).to_cf()?, + I64LeS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a <= b) as i32)).to_cf()?, + I32LeU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a <= b) as i32)).to_cf()?, + I64LeU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a <= b) as i32)).to_cf()?, + F32Le => self.stack.values.calculate::<f32, _>(|a, b| Ok((a <= b) as i32)).to_cf()?, + F64Le => self.stack.values.calculate::<f64, _>(|a, b| Ok((a <= b) as i32)).to_cf()?, - I32GtS => self.stack.values.calculate::<i32, _>(|a, b| Ok((a > b) as i32))?, - I64GtS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a > b) as i32))?, - I32GtU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a > b) as i32))?, - I64GtU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a > b) as i32))?, - F32Gt => self.stack.values.calculate::<f32, _>(|a, b| Ok((a > b) as i32))?, - F64Gt => self.stack.values.calculate::<f64, _>(|a, b| Ok((a > b) as i32))?, + I32GeS => self.stack.values.calculate_same::<i32>(|a, b| Ok((a >= b) as i32)).to_cf()?, + I64GeS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a >= b) as i32)).to_cf()?, + I32GeU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a >= b) as i32)).to_cf()?, + I64GeU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a >= b) as i32)).to_cf()?, + F32Ge => self.stack.values.calculate::<f32, _>(|a, b| Ok((a >= b) as i32)).to_cf()?, + F64Ge => self.stack.values.calculate::<f64, _>(|a, b| Ok((a >= b) as i32)).to_cf()?, - I32Add => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wrapping_add(b)))?, - I64Add => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wrapping_add(b)))?, - F32Add => self.stack.values.calculate::<f32, _>(|a, b| Ok(a + b))?, - F64Add => self.stack.values.calculate::<f64, _>(|a, b| Ok(a + b))?, + I32GtS => self.stack.values.calculate_same::<i32>(|a, b| Ok((a > b) as i32)).to_cf()?, + I64GtS => self.stack.values.calculate::<i64, _>(|a, b| Ok((a > b) as i32)).to_cf()?, + I32GtU => self.stack.values.calculate::<u32, _>(|a, b| Ok((a > b) as i32)).to_cf()?, + I64GtU => self.stack.values.calculate::<u64, _>(|a, b| Ok((a > b) as i32)).to_cf()?, + F32Gt => self.stack.values.calculate::<f32, _>(|a, b| Ok((a > b) as i32)).to_cf()?, + F64Gt => self.stack.values.calculate::<f64, _>(|a, b| Ok((a > b) as i32)).to_cf()?, - I32Sub => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wrapping_sub(b)))?, - I64Sub => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wrapping_sub(b)))?, - F32Sub => self.stack.values.calculate::<f32, _>(|a, b| Ok(a - b))?, - F64Sub => self.stack.values.calculate::<f64, _>(|a, b| Ok(a - b))?, + I32Add => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wrapping_add(b))).to_cf()?, + I64Add => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wrapping_add(b))).to_cf()?, + F32Add => self.stack.values.calculate_same::<f32>(|a, b| Ok(a + b)).to_cf()?, + F64Add => self.stack.values.calculate_same::<f64>(|a, b| Ok(a + b)).to_cf()?, - F32Div => self.stack.values.calculate::<f32, _>(|a, b| Ok(a / b))?, - F64Div => self.stack.values.calculate::<f64, _>(|a, b| Ok(a / b))?, + I32Sub => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wrapping_sub(b))).to_cf()?, + I64Sub => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wrapping_sub(b))).to_cf()?, + F32Sub => self.stack.values.calculate_same::<f32>(|a, b| Ok(a - b)).to_cf()?, + F64Sub => self.stack.values.calculate_same::<f64>(|a, b| Ok(a - b)).to_cf()?, - I32Mul => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wrapping_mul(b)))?, - I64Mul => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wrapping_mul(b)))?, - F32Mul => self.stack.values.calculate::<f32, _>(|a, b| Ok(a * b))?, - F64Mul => self.stack.values.calculate::<f64, _>(|a, b| Ok(a * b))?, + F32Div => self.stack.values.calculate_same::<f32>(|a, b| Ok(a / b)).to_cf()?, + F64Div => self.stack.values.calculate_same::<f64>(|a, b| Ok(a / b)).to_cf()?, - // these can trap - I32DivS => self.stack.values.calculate::<i32, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, - I64DivS => self.stack.values.calculate::<i64, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, - I32DivU => self.stack.values.calculate::<u32, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, - I64DivU => self.stack.values.calculate::<u64, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_div(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, + I32Mul => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wrapping_mul(b))).to_cf()?, + I64Mul => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wrapping_mul(b))).to_cf()?, + F32Mul => self.stack.values.calculate_same::<f32>(|a, b| Ok(a * b)).to_cf()?, + F64Mul => self.stack.values.calculate_same::<f64>(|a, b| Ok(a * b)).to_cf()?, - I32RemS => self.stack.values.calculate::<i32, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, - I64RemS => self.stack.values.calculate::<i64, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, - I32RemU => self.stack.values.calculate::<u32, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, - I64RemU => self.stack.values.calculate::<u64, _>(|a, b| { - if unlikely(b == 0) { - return Err(Error::Trap(Trap::DivisionByZero)); - } - a.checked_wrapping_rem(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) - })?, + I32DivS => self.stack.values.calculate_same::<i32>(|a, b| a.wasm_checked_div(b)).to_cf()?, + I64DivS => self.stack.values.calculate_same::<i64>(|a, b| a.wasm_checked_div(b)).to_cf()?, + I32DivU => self.stack.values.calculate_same::<u32>(|a, b| a.checked_div(b).ok_or_else(trap_0)).to_cf()?, + I64DivU => self.stack.values.calculate_same::<u64>(|a, b| a.checked_div(b).ok_or_else(trap_0)).to_cf()?, + I32RemS => self.stack.values.calculate_same::<i32>(|a, b| a.checked_wrapping_rem(b)).to_cf()?, + I64RemS => self.stack.values.calculate_same::<i64>(|a, b| a.checked_wrapping_rem(b)).to_cf()?, + I32RemU => self.stack.values.calculate_same::<u32>(|a, b| a.checked_wrapping_rem(b)).to_cf()?, + I64RemU => self.stack.values.calculate_same::<u64>(|a, b| a.checked_wrapping_rem(b)).to_cf()?, - I32And => self.stack.values.calculate::<i32, _>(|a, b| Ok(a & b))?, - I64And => self.stack.values.calculate::<i64, _>(|a, b| Ok(a & b))?, - I32Or => self.stack.values.calculate::<i32, _>(|a, b| Ok(a | b))?, - I64Or => self.stack.values.calculate::<i64, _>(|a, b| Ok(a | b))?, - I32Xor => self.stack.values.calculate::<i32, _>(|a, b| Ok(a ^ b))?, - I64Xor => self.stack.values.calculate::<i64, _>(|a, b| Ok(a ^ b))?, - I32Shl => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_shl(b)))?, - I64Shl => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_shl(b)))?, - I32ShrS => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_shr(b)))?, - I64ShrS => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_shr(b)))?, - I32ShrU => self.stack.values.calculate::<u32, _>(|a, b| Ok(a.wasm_shr(b)))?, - I64ShrU => self.stack.values.calculate::<u64, _>(|a, b| Ok(a.wasm_shr(b)))?, - I32Rotl => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_rotl(b)))?, - I64Rotl => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_rotl(b)))?, - I32Rotr => self.stack.values.calculate::<i32, _>(|a, b| Ok(a.wasm_rotr(b)))?, - I64Rotr => self.stack.values.calculate::<i64, _>(|a, b| Ok(a.wasm_rotr(b)))?, + I32And => self.stack.values.calculate_same::<i32>(|a, b| Ok(a & b)).to_cf()?, + I64And => self.stack.values.calculate_same::<i64>(|a, b| Ok(a & b)).to_cf()?, + I32Or => self.stack.values.calculate_same::<i32>(|a, b| Ok(a | b)).to_cf()?, + I64Or => self.stack.values.calculate_same::<i64>(|a, b| Ok(a | b)).to_cf()?, + I32Xor => self.stack.values.calculate_same::<i32>(|a, b| Ok(a ^ b)).to_cf()?, + I64Xor => self.stack.values.calculate_same::<i64>(|a, b| Ok(a ^ b)).to_cf()?, + I32Shl => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wasm_shl(b))).to_cf()?, + I64Shl => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wasm_shl(b))).to_cf()?, + I32ShrS => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wasm_shr(b))).to_cf()?, + I64ShrS => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wasm_shr(b))).to_cf()?, + I32ShrU => self.stack.values.calculate_same::<u32>(|a, b| Ok(a.wasm_shr(b))).to_cf()?, + I64ShrU => self.stack.values.calculate_same::<u64>(|a, b| Ok(a.wasm_shr(b))).to_cf()?, + I32Rotl => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wasm_rotl(b))).to_cf()?, + I64Rotl => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wasm_rotl(b))).to_cf()?, + I32Rotr => self.stack.values.calculate_same::<i32>(|a, b| Ok(a.wasm_rotr(b))).to_cf()?, + I64Rotr => self.stack.values.calculate_same::<i64>(|a, b| Ok(a.wasm_rotr(b))).to_cf()?, - I32Clz => self.stack.values.replace_top::<i32, _>(|v| Ok(v.leading_zeros() as i32))?, - I64Clz => self.stack.values.replace_top::<i64, _>(|v| Ok(v.leading_zeros() as i64))?, - I32Ctz => self.stack.values.replace_top::<i32, _>(|v| Ok(v.trailing_zeros() as i32))?, - I64Ctz => self.stack.values.replace_top::<i64, _>(|v| Ok(v.trailing_zeros() as i64))?, - I32Popcnt => self.stack.values.replace_top::<i32, _>(|v| Ok(v.count_ones() as i32))?, - I64Popcnt => self.stack.values.replace_top::<i64, _>(|v| Ok(v.count_ones() as i64))?, + I32Clz => self.stack.values.replace_top_same::<i32>(|v| Ok(v.leading_zeros() as i32)).to_cf()?, + I64Clz => self.stack.values.replace_top_same::<i64>(|v| Ok(v.leading_zeros() as i64)).to_cf()?, + I32Ctz => self.stack.values.replace_top_same::<i32>(|v| Ok(v.trailing_zeros() as i32)).to_cf()?, + I64Ctz => self.stack.values.replace_top_same::<i64>(|v| Ok(v.trailing_zeros() as i64)).to_cf()?, + I32Popcnt => self.stack.values.replace_top_same::<i32>(|v| Ok(v.count_ones() as i32)).to_cf()?, + I64Popcnt => self.stack.values.replace_top_same::<i64>(|v| Ok(v.count_ones() as i64)).to_cf()?, - F32ConvertI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as f32))?, - F32ConvertI64S => self.stack.values.replace_top::<i64, _>(|v| Ok(v as f32))?, - F64ConvertI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as f64))?, - F64ConvertI64S => self.stack.values.replace_top::<i64, _>(|v| Ok(v as f64))?, - F32ConvertI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as f32))?, - F32ConvertI64U => self.stack.values.replace_top::<u64, _>(|v| Ok(v as f32))?, - F64ConvertI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as f64))?, - F64ConvertI64U => self.stack.values.replace_top::<u64, _>(|v| Ok(v as f64))?, + F32ConvertI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as f32)).to_cf()?, + F32ConvertI64S => self.stack.values.replace_top::<i64, _>(|v| Ok(v as f32)).to_cf()?, + F64ConvertI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as f64)).to_cf()?, + F64ConvertI64S => self.stack.values.replace_top::<i64, _>(|v| Ok(v as f64)).to_cf()?, + F32ConvertI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as f32)).to_cf()?, + F32ConvertI64U => self.stack.values.replace_top::<u64, _>(|v| Ok(v as f32)).to_cf()?, + F64ConvertI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as f64)).to_cf()?, + F64ConvertI64U => self.stack.values.replace_top::<u64, _>(|v| Ok(v as f64)).to_cf()?, - I32Extend8S => self.stack.values.replace_top::<i32, _>(|v| Ok((v as i8) as i32))?, - I32Extend16S => self.stack.values.replace_top::<i32, _>(|v| Ok((v as i16) as i32))?, - I64Extend8S => self.stack.values.replace_top::<i64, _>(|v| Ok((v as i8) as i64))?, - I64Extend16S => self.stack.values.replace_top::<i64, _>(|v| Ok((v as i16) as i64))?, - I64Extend32S => self.stack.values.replace_top::<i64, _>(|v| Ok((v as i32) as i64))?, - I64ExtendI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as i64))?, - I64ExtendI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as i64))?, - I32WrapI64 => self.stack.values.replace_top::<i64, _>(|v| Ok(v as i32))?, + I32Extend8S => self.stack.values.replace_top_same::<i32>(|v| Ok((v as i8) as i32)).to_cf()?, + I32Extend16S => self.stack.values.replace_top_same::<i32>(|v| Ok((v as i16) as i32)).to_cf()?, + I64Extend8S => self.stack.values.replace_top_same::<i64>(|v| Ok((v as i8) as i64)).to_cf()?, + I64Extend16S => self.stack.values.replace_top_same::<i64>(|v| Ok((v as i16) as i64)).to_cf()?, + I64Extend32S => self.stack.values.replace_top_same::<i64>(|v| Ok((v as i32) as i64)).to_cf()?, + I64ExtendI32U => self.stack.values.replace_top::<u32, _>(|v| Ok(v as i64)).to_cf()?, + I64ExtendI32S => self.stack.values.replace_top::<i32, _>(|v| Ok(v as i64)).to_cf()?, + I32WrapI64 => self.stack.values.replace_top::<i64, _>(|v| Ok(v as i32)).to_cf()?, - F32DemoteF64 => self.stack.values.replace_top::<f64, _>(|v| Ok(v as f32))?, - F64PromoteF32 => self.stack.values.replace_top::<f32, _>(|v| Ok(v as f64))?, + F32DemoteF64 => self.stack.values.replace_top::<f64, _>(|v| Ok(v as f32)).to_cf()?, + F64PromoteF32 => self.stack.values.replace_top::<f32, _>(|v| Ok(v as f64)).to_cf()?, - F32Abs => self.stack.values.replace_top::<f32, _>(|v| Ok(v.abs()))?, - F64Abs => self.stack.values.replace_top::<f64, _>(|v| Ok(v.abs()))?, - F32Neg => self.stack.values.replace_top::<f32, _>(|v| Ok(-v))?, - F64Neg => self.stack.values.replace_top::<f64, _>(|v| Ok(-v))?, - F32Ceil => self.stack.values.replace_top::<f32, _>(|v| Ok(v.ceil()))?, - F64Ceil => self.stack.values.replace_top::<f64, _>(|v| Ok(v.ceil()))?, - F32Floor => self.stack.values.replace_top::<f32, _>(|v| Ok(v.floor()))?, - F64Floor => self.stack.values.replace_top::<f64, _>(|v| Ok(v.floor()))?, - F32Trunc => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc()))?, - F64Trunc => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc()))?, - F32Nearest => self.stack.values.replace_top::<f32, _>(|v| Ok(v.tw_nearest()))?, - F64Nearest => self.stack.values.replace_top::<f64, _>(|v| Ok(v.tw_nearest()))?, - F32Sqrt => self.stack.values.replace_top::<f32, _>(|v| Ok(v.sqrt()))?, - F64Sqrt => self.stack.values.replace_top::<f64, _>(|v| Ok(v.sqrt()))?, - F32Min => self.stack.values.calculate::<f32, _>(|a, b| Ok(a.tw_minimum(b)))?, - F64Min => self.stack.values.calculate::<f64, _>(|a, b| Ok(a.tw_minimum(b)))?, - F32Max => self.stack.values.calculate::<f32, _>(|a, b| Ok(a.tw_maximum(b)))?, - F64Max => self.stack.values.calculate::<f64, _>(|a, b| Ok(a.tw_maximum(b)))?, - F32Copysign => self.stack.values.calculate::<f32, _>(|a, b| Ok(a.copysign(b)))?, - F64Copysign => self.stack.values.calculate::<f64, _>(|a, b| Ok(a.copysign(b)))?, + F32Abs => self.stack.values.replace_top_same::<f32>(|v| Ok(v.abs())).to_cf()?, + F64Abs => self.stack.values.replace_top_same::<f64>(|v| Ok(v.abs())).to_cf()?, + F32Neg => self.stack.values.replace_top_same::<f32>(|v| Ok(-v)).to_cf()?, + F64Neg => self.stack.values.replace_top_same::<f64>(|v| Ok(-v)).to_cf()?, + F32Ceil => self.stack.values.replace_top_same::<f32>(|v| Ok(v.ceil())).to_cf()?, + F64Ceil => self.stack.values.replace_top_same::<f64>(|v| Ok(v.ceil())).to_cf()?, + F32Floor => self.stack.values.replace_top_same::<f32>(|v| Ok(v.floor())).to_cf()?, + F64Floor => self.stack.values.replace_top_same::<f64>(|v| Ok(v.floor())).to_cf()?, + F32Trunc => self.stack.values.replace_top_same::<f32>(|v| Ok(v.trunc())).to_cf()?, + F64Trunc => self.stack.values.replace_top_same::<f64>(|v| Ok(v.trunc())).to_cf()?, + F32Nearest => self.stack.values.replace_top_same::<f32>(|v| Ok(v.tw_nearest())).to_cf()?, + F64Nearest => self.stack.values.replace_top_same::<f64>(|v| Ok(v.tw_nearest())).to_cf()?, + F32Sqrt => self.stack.values.replace_top_same::<f32>(|v| Ok(v.sqrt())).to_cf()?, + F64Sqrt => self.stack.values.replace_top_same::<f64>(|v| Ok(v.sqrt())).to_cf()?, + F32Min => self.stack.values.calculate_same::<f32>(|a, b| Ok(a.tw_minimum(b))).to_cf()?, + F64Min => self.stack.values.calculate_same::<f64>(|a, b| Ok(a.tw_minimum(b))).to_cf()?, + F32Max => self.stack.values.calculate_same::<f32>(|a, b| Ok(a.tw_maximum(b))).to_cf()?, + F64Max => self.stack.values.calculate_same::<f64>(|a, b| Ok(a.tw_maximum(b))).to_cf()?, + F32Copysign => self.stack.values.calculate_same::<f32>(|a, b| Ok(a.copysign(b))).to_cf()?, + F64Copysign => self.stack.values.calculate_same::<f64>(|a, b| Ok(a.copysign(b))).to_cf()?, // no-op instructions since types are erased at runtime I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} @@ -365,160 +285,144 @@ impl<'store, 'stack> Executor<'store, 'stack> { I64TruncF32U => checked_conv_float!(f32, u64, i64, self), I64TruncF64U => checked_conv_float!(f64, u64, i64, self), - TableGet(table_idx) => self.exec_table_get(*table_idx)?, - TableSet(table_idx) => self.exec_table_set(*table_idx)?, - TableSize(table_idx) => self.exec_table_size(*table_idx)?, - TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx)?, - TableGrow(table_idx) => self.exec_table_grow(*table_idx)?, - TableFill(table_idx) => self.exec_table_fill(*table_idx)?, + TableGet(table_idx) => self.exec_table_get(*table_idx).to_cf()?, + TableSet(table_idx) => self.exec_table_set(*table_idx).to_cf()?, + TableSize(table_idx) => self.exec_table_size(*table_idx).to_cf()?, + TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx).to_cf()?, + TableGrow(table_idx) => self.exec_table_grow(*table_idx).to_cf()?, + TableFill(table_idx) => self.exec_table_fill(*table_idx).to_cf()?, + + I32TruncSatF32S => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as i32)).to_cf()?, + I32TruncSatF32U => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as u32)).to_cf()?, + I32TruncSatF64S => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as i32)).to_cf()?, + I32TruncSatF64U => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as u32)).to_cf()?, + I64TruncSatF32S => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as i64)).to_cf()?, + I64TruncSatF32U => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as u64)).to_cf()?, + I64TruncSatF64S => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as i64)).to_cf()?, + I64TruncSatF64U => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as u64)).to_cf()?, - I32TruncSatF32S => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as i32))?, - I32TruncSatF32U => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as u32))?, - I32TruncSatF64S => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as i32))?, - I32TruncSatF64U => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as u32))?, - I64TruncSatF32S => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as i64))?, - I64TruncSatF32U => self.stack.values.replace_top::<f32, _>(|v| Ok(v.trunc() as u64))?, - I64TruncSatF64S => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as i64))?, - I64TruncSatF64U => self.stack.values.replace_top::<f64, _>(|v| Ok(v.trunc() as u64))?, - // custom instructions - // LocalGet2(a, b) => self.exec_local_get2(*a, *b), - // LocalGet3(a, b, c) => self.exec_local_get3(*a, *b, *c), - // LocalTeeGet(a, b) => self.exec_local_tee_get(*a, *b)?, - // LocalGetSet(a, b) => self.exec_local_get_set(*a, *b), - // I64XorConstRotl(rotate_by) => self.exec_i64_xor_const_rotl(*rotate_by)?, - // I32LocalGetConstAdd(local, val) => self.exec_i32_local_get_const_add(*local, *val), - // I32ConstStoreLocal { local, const_i32, offset, mem_addr } => { - // self.exec_i32_const_store_local(*local, *const_i32, *offset, *mem_addr)? - // } - // I32StoreLocal { local_a, local_b, offset, mem_addr } => { - // self.exec_i32_store_local(*local_a, *local_b, *offset, *mem_addr)? - // } + LocalCopy32(from, to) => self.exec_local_copy::<Value32>(*from, *to), + LocalCopy64(from, to) => self.exec_local_copy::<Value64>(*from, *to), + LocalCopy128(from, to) => self.exec_local_copy::<Value128>(*from, *to), + LocalCopyRef(from, to) => self.exec_local_copy::<ValueRef>(*from, *to), + + instr => { + unreachable!("unimplemented instruction: {:?}", instr); + } }; self.cf.incr_instr_ptr(); - Ok(ControlFlow::Continue(())) + ControlFlow::Continue(()) } fn exec_noop(&self) {} #[cold] - fn exec_unreachable(&self) -> Result<()> { - Err(Error::Trap(Trap::Unreachable)) + fn exec_unreachable(&self) -> ControlFlow<Option<Error>> { + ControlFlow::Break(Some(Trap::Unreachable.into())) } - fn exec_call(&mut self, wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr) -> Result<ControlFlow<()>> { - let locals = match self.stack.values.pop_locals(&wasm_func.ty.params, wasm_func.locals) { - Ok(locals) => locals, - Err(e) => { - cold(); - return Err(e); - } - }; - + fn exec_call(&mut self, wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr) -> ControlFlow<Option<Error>> { + let locals = self.stack.values.pop_locals(&wasm_func.params, &wasm_func.locals); let new_call_frame = CallFrame::new_raw(wasm_func, owner, locals, self.stack.blocks.len() as u32); self.cf.incr_instr_ptr(); // skip the call instruction self.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?; self.module.swap_with(self.cf.module_addr(), self.store); - Ok(ControlFlow::Continue(())) + ControlFlow::Continue(()) } - fn exec_call_direct(&mut self, v: u32) -> Result<ControlFlow<()>> { - let func_inst = self.store.get_func(self.module.resolve_func_addr(v)?)?; + fn exec_call_direct(&mut self, v: u32) -> ControlFlow<Option<Error>> { + let func_inst = self.store.get_func(self.module.resolve_func_addr(v)); let wasm_func = match &func_inst.func { crate::Function::Wasm(wasm_func) => wasm_func, crate::Function::Host(host_func) => { let func = &host_func.clone(); - let params = self.stack.values.pop_params(&host_func.ty.params)?; - let res = (func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms)?; + let params = self.stack.values.pop_params(&host_func.ty.params); + let res = + (func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms).to_cf()?; self.stack.values.extend_from_wasmvalues(&res); self.cf.incr_instr_ptr(); - return Ok(ControlFlow::Continue(())); + return ControlFlow::Continue(()); } }; self.exec_call(wasm_func.clone(), func_inst._owner) } - fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> Result<ControlFlow<()>> { + fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> ControlFlow<Option<Error>> { // verify that the table is of the right type, this should be validated by the parser already let func_ref = { - let table = self.store.get_table(self.module.resolve_table_addr(table_addr)?)?; - let table_idx: u32 = self.stack.values.pop::<i32>()? as u32; + let table = self.store.get_table(self.module.resolve_table_addr(table_addr)); + let table_idx: u32 = self.stack.values.pop::<i32>() as u32; assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref"); table .get(table_idx) - .map_err(|_| Error::Trap(Trap::UndefinedElement { index: table_idx as usize }))? + .map_err(|_| Error::Trap(Trap::UndefinedElement { index: table_idx as usize })) + .to_cf()? .addr() - .ok_or(Trap::UninitializedElement { index: table_idx as usize })? + .ok_or(Error::Trap(Trap::UninitializedElement { index: table_idx as usize })) + .to_cf()? }; - let func_inst = self.store.get_func(func_ref)?; + let func_inst = self.store.get_func(&func_ref); let call_ty = self.module.func_ty(type_addr); let wasm_func = match &func_inst.func { crate::Function::Wasm(f) => f, crate::Function::Host(host_func) => { if unlikely(host_func.ty != *call_ty) { - return Err(Error::Trap(Trap::IndirectCallTypeMismatch { - actual: host_func.ty.clone(), - expected: call_ty.clone(), - })); + return ControlFlow::Break(Some( + Trap::IndirectCallTypeMismatch { actual: host_func.ty.clone(), expected: call_ty.clone() } + .into(), + )); } let host_func = host_func.clone(); - let params = self.stack.values.pop_params(&host_func.ty.params)?; - let res = (host_func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms)?; + let params = self.stack.values.pop_params(&host_func.ty.params); + let res = + match (host_func.func)(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms) { + Ok(res) => res, + Err(e) => return ControlFlow::Break(Some(e)), + }; + self.stack.values.extend_from_wasmvalues(&res); self.cf.incr_instr_ptr(); - return Ok(ControlFlow::Continue(())); + return ControlFlow::Continue(()); } }; - if wasm_func.ty == *call_ty { - return self.exec_call(wasm_func.clone(), func_inst._owner); + if unlikely(wasm_func.ty != *call_ty) { + return ControlFlow::Break(Some( + Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into(), + )); } - cold(); - Err(Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into()) + self.exec_call(wasm_func.clone(), func_inst._owner) } - fn exec_if( - &mut self, - else_offset: u32, - end_offset: u32, - (params, results): (StackHeight, StackHeight), - ) -> Result<()> { + fn exec_if(&mut self, else_offset: u32, end_offset: u32, (params, results): (StackHeight, StackHeight)) { // truthy value is on the top of the stack, so enter the then block - if self.stack.values.pop::<i32>()? != 0 { - self.enter_block(self.cf.instr_ptr(), end_offset, BlockType::If, (params, results)); - return Ok(()); + if self.stack.values.pop::<i32>() != 0 { + self.enter_block(end_offset, BlockType::If, (params, results)); + return; } // falsy value is on the top of the stack if else_offset == 0 { - *self.cf.instr_ptr_mut() += end_offset as usize; - return Ok(()); + self.cf.jump(end_offset as usize); + return; } - let old = self.cf.instr_ptr(); - *self.cf.instr_ptr_mut() += else_offset as usize; - self.enter_block(old + else_offset as usize, end_offset - else_offset, BlockType::Else, (params, results)); - Ok(()) + self.cf.jump(else_offset as usize); + self.enter_block(end_offset - else_offset, BlockType::Else, (params, results)); } - fn exec_else(&mut self, end_offset: u32) -> Result<()> { - self.exec_end_block()?; - *self.cf.instr_ptr_mut() += end_offset as usize; - Ok(()) + fn exec_else(&mut self, end_offset: u32) { + self.exec_end_block(); + self.cf.jump(end_offset as usize); } fn resolve_functype(&self, idx: u32) -> (StackHeight, StackHeight) { let ty = self.module.func_ty(idx); ((&*ty.params).into(), (&*ty.results).into()) } - fn enter_block( - &mut self, - instr_ptr: usize, - 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)) { self.stack.blocks.push(BlockFrame { - instr_ptr, + instr_ptr: self.cf.instr_ptr(), end_instr_offset, stack_ptr: self.stack.values.height(), results, @@ -526,35 +430,39 @@ impl<'store, 'stack> Executor<'store, 'stack> { ty, }); } - fn exec_br(&mut self, to: u32) -> Result<ControlFlow<()>> { + fn exec_br(&mut self, to: u32) -> ControlFlow<Option<Error>> { if self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { return self.exec_return(); } self.cf.incr_instr_ptr(); - Ok(ControlFlow::Continue(())) + ControlFlow::Continue(()) } - fn exec_br_if(&mut self, to: u32) -> Result<ControlFlow<()>> { - if self.stack.values.pop::<i32>()? != 0 + fn exec_br_if(&mut self, to: u32) -> ControlFlow<Option<Error>> { + if self.stack.values.pop::<i32>() != 0 && self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { return self.exec_return(); } self.cf.incr_instr_ptr(); - Ok(ControlFlow::Continue(())) + ControlFlow::Continue(()) } - fn exec_brtable(&mut self, default: u32, len: u32) -> Result<ControlFlow<()>> { + fn exec_brtable(&mut self, default: u32, len: u32) -> ControlFlow<Option<Error>> { let start = self.cf.instr_ptr() + 1; let end = start + len as usize; if end > self.cf.instructions().len() { - return Err(Error::Other(format!("br_table out of bounds: {} >= {}", end, self.cf.instructions().len()))); + return ControlFlow::Break(Some(Error::Other(format!( + "br_table out of bounds: {} >= {}", + end, + self.cf.instructions().len() + )))); } - let idx = self.stack.values.pop::<i32>()?; + let idx = self.stack.values.pop::<i32>(); let to = match self.cf.instructions()[start..end].get(idx as usize) { None => default, Some(Instruction::BrLabel(to)) => *to, - _ => return Err(Error::Other("br_table with invalid label".to_string())), + _ => return ControlFlow::Break(Some(Error::Other("br_table out of bounds".to_string()))), }; if self.cf.break_to(to, &mut self.stack.values, &mut self.stack.blocks).is_none() { @@ -562,12 +470,12 @@ impl<'store, 'stack> Executor<'store, 'stack> { } self.cf.incr_instr_ptr(); - Ok(ControlFlow::Continue(())) + ControlFlow::Continue(()) } - fn exec_return(&mut self) -> Result<ControlFlow<()>> { + fn exec_return(&mut self) -> ControlFlow<Option<Error>> { let old = self.cf.block_ptr(); match self.stack.call_stack.pop() { - None => return Ok(ControlFlow::Break(())), + None => return ControlFlow::Break(None), Some(cf) => self.cf = cf, } @@ -576,94 +484,96 @@ impl<'store, 'stack> Executor<'store, 'stack> { } self.module.swap_with(self.cf.module_addr(), self.store); - Ok(ControlFlow::Continue(())) + ControlFlow::Continue(()) } - fn exec_end_block(&mut self) -> Result<()> { - let block = self.stack.blocks.pop()?; + fn exec_end_block(&mut self) { + let block = self.stack.blocks.pop(); self.stack.values.truncate_keep(&block.stack_ptr, &block.results); - Ok(()) } - fn exec_local_get<T: InternalValue>(&mut self, local_index: u16) -> Result<()> { - let v = self.cf.locals.get::<T>(local_index)?; + fn exec_local_get<T: InternalValue>(&mut self, local_index: u16) { + let v = self.cf.locals.get::<T>(local_index); self.stack.values.push(v); - Ok(()) + } + fn exec_local_set<T: InternalValue>(&mut self, local_index: u16) { + let v = self.stack.values.pop::<T>(); + self.cf.locals.set(local_index, v); + } + fn exec_local_tee<T: InternalValue>(&mut self, local_index: u16) { + let v = self.stack.values.peek::<T>(); + self.cf.locals.set(local_index, v); } - fn exec_global_get(&mut self, global_index: u32) -> Result<()> { - self.stack.values.push_dyn(self.store.get_global_val(self.module.resolve_global_addr(global_index)?)?); - Ok(()) + fn exec_global_get(&mut self, global_index: u32) { + self.stack.values.push_dyn(self.store.get_global_val(self.module.resolve_global_addr(global_index))); } - fn exec_global_set<T: InternalValue>(&mut self, global_index: u32) -> Result<()> - where - TinyWasmValue: From<T>, - { - self.store.set_global_val(self.module.resolve_global_addr(global_index)?, self.stack.values.pop::<T>()?.into()) + fn exec_global_set<T: InternalValue>(&mut self, global_index: u32) { + self.store.set_global_val(self.module.resolve_global_addr(global_index), self.stack.values.pop::<T>().into()); } - fn exec_ref_is_null(&mut self) -> Result<()> { - let is_null = self.stack.values.pop::<ValueRef>()?.is_none() as i32; + fn exec_const<T: InternalValue>(&mut self, val: T) { + self.stack.values.push(val); + } + fn exec_ref_is_null(&mut self) { + let is_null = self.stack.values.pop::<ValueRef>().is_none() as i32; self.stack.values.push::<i32>(is_null); - Ok(()) } - fn exec_memory_size(&mut self, addr: u32) -> Result<()> { - let mem = self.store.get_mem(self.module.resolve_mem_addr(addr)?)?; - self.stack.values.push::<i32>(mem.page_count() as i32); - Ok(()) + fn exec_memory_size(&mut self, addr: u32) { + let mem = self.store.get_mem(self.module.resolve_mem_addr(addr)); + self.stack.values.push::<i32>(mem.page_count as i32); } - fn exec_memory_grow(&mut self, addr: u32) -> Result<()> { - let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(addr)?)?; - let prev_size = mem.page_count() as i32; - let pages_delta = self.stack.values.pop::<i32>()?; + fn exec_memory_grow(&mut self, addr: u32) { + let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(addr)); + let prev_size = mem.page_count as i32; + let pages_delta = self.stack.values.pop::<i32>(); self.stack.values.push::<i32>(match mem.grow(pages_delta) { Some(_) => prev_size, None => -1, }); - Ok(()) } fn exec_memory_copy(&mut self, from: u32, to: u32) -> Result<()> { - let size = self.stack.values.pop::<i32>()?; - let src = self.stack.values.pop::<i32>()?; - let dst = self.stack.values.pop::<i32>()?; + let size: i32 = self.stack.values.pop(); + let src: i32 = self.stack.values.pop(); + let dst: i32 = self.stack.values.pop(); if from == to { - let mem_from = self.store.get_mem_mut(self.module.resolve_mem_addr(from)?)?; + let mem_from = self.store.get_mem_mut(self.module.resolve_mem_addr(from)); // copy within the same memory mem_from.copy_within(dst as usize, src as usize, size as usize)?; } else { // copy between two memories let (mem_from, mem_to) = - self.store.get_mems_mut(self.module.resolve_mem_addr(from)?, self.module.resolve_mem_addr(to)?)?; + self.store.get_mems_mut(self.module.resolve_mem_addr(from), self.module.resolve_mem_addr(to))?; + mem_to.copy_from_slice(dst as usize, mem_from.load(src as usize, size as usize)?)?; } Ok(()) } fn exec_memory_fill(&mut self, addr: u32) -> Result<()> { - let size = self.stack.values.pop::<i32>()?; - let val = self.stack.values.pop::<i32>()?; - let dst = self.stack.values.pop::<i32>()?; + let size: i32 = self.stack.values.pop(); + let val: i32 = self.stack.values.pop(); + let dst: i32 = self.stack.values.pop(); - let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(addr)?)?; - mem.fill(dst as usize, size as usize, val as u8)?; - Ok(()) + let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(addr)); + mem.fill(dst as usize, size as usize, val as u8) } fn exec_memory_init(&mut self, data_index: u32, mem_index: u32) -> Result<()> { - let size = self.stack.values.pop::<i32>()?; // n - let offset = self.stack.values.pop::<i32>()?; // s - let dst = self.stack.values.pop::<i32>()?; // d + let size: i32 = self.stack.values.pop(); + let offset: i32 = self.stack.values.pop(); + let dst: i32 = self.stack.values.pop(); let data = self .store .data .datas - .get(self.module.resolve_data_addr(data_index)? as usize) + .get(*self.module.resolve_data_addr(data_index) as usize) .ok_or_else(|| Error::Other("data not found".to_string()))?; let mem = self .store .data .memories - .get_mut(self.module.resolve_mem_addr(mem_index)? as usize) + .get_mut(*self.module.resolve_mem_addr(mem_index) as usize) .ok_or_else(|| Error::Other("memory not found".to_string()))?; let data_len = data.data.as_ref().map(|d| d.len()).unwrap_or(0); @@ -681,32 +591,30 @@ impl<'store, 'stack> Executor<'store, 'stack> { None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()), }; - mem.store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)])?; - Ok(()) + mem.store(dst as usize, size as usize, &data[offset as usize..((offset + size) as usize)]) } - fn exec_data_drop(&mut self, data_index: u32) -> Result<()> { - self.store.get_data_mut(self.module.resolve_data_addr(data_index)?).map(|d| d.drop()) + fn exec_data_drop(&mut self, data_index: u32) { + self.store.get_data_mut(self.module.resolve_data_addr(data_index)).drop() } - fn exec_elem_drop(&mut self, elem_index: u32) -> Result<()> { - self.store.get_elem_mut(self.module.resolve_elem_addr(elem_index)?).map(|e| e.drop()) + fn exec_elem_drop(&mut self, elem_index: u32) { + self.store.get_elem_mut(self.module.resolve_elem_addr(elem_index)).drop() } fn exec_table_copy(&mut self, from: u32, to: u32) -> Result<()> { - let size: i32 = self.stack.values.pop::<i32>()?; - let src: i32 = self.stack.values.pop::<i32>()?; - let dst: i32 = self.stack.values.pop::<i32>()?; + let size: i32 = self.stack.values.pop(); + let src: i32 = self.stack.values.pop(); + let dst: i32 = self.stack.values.pop(); if from == to { // copy within the same memory - self.store.get_table_mut(self.module.resolve_table_addr(from)?)?.copy_within( + self.store.get_table_mut(self.module.resolve_table_addr(from)).copy_within( dst as usize, src as usize, size as usize, )?; } else { // copy between two memories - let (table_from, table_to) = self - .store - .get_tables_mut(self.module.resolve_table_addr(from)?, self.module.resolve_table_addr(to)?)?; + let (table_from, table_to) = + self.store.get_tables_mut(self.module.resolve_table_addr(from), self.module.resolve_table_addr(to))?; table_to.copy_from_slice(dst as usize, table_from.load(src as usize, size as usize)?)?; } Ok(()) @@ -714,54 +622,55 @@ impl<'store, 'stack> Executor<'store, 'stack> { fn exec_mem_load<LOAD: MemLoadable<LOAD_SIZE>, const LOAD_SIZE: usize, TARGET: InternalValue>( &mut self, - cast: fn(LOAD) -> TARGET, mem_addr: tinywasm_types::MemAddr, offset: u64, - ) -> Result<()> { - let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr)?)?; - - let val = self.stack.values.pop::<i32>()? as u64; + cast: fn(LOAD) -> TARGET, + ) -> ControlFlow<Option<Error>> { + let mem = self.store.get_mem(self.module.resolve_mem_addr(mem_addr)); + let val = self.stack.values.pop::<i32>() as u64; let Some(Ok(addr)) = offset.checked_add(val).map(|a| a.try_into()) else { cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: offset as usize, + return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds { + offset: val as usize, len: LOAD_SIZE, - max: mem.max_pages(), - })); + max: 0, + }))); }; - let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?; + let val = mem.load_as::<LOAD_SIZE, LOAD>(addr).to_cf()?; self.stack.values.push(cast(val)); - Ok(()) + ControlFlow::Continue(()) } - fn exec_mem_store<T: MemStorable<N>, const N: usize>( + fn exec_mem_store<T: InternalValue, U: MemStorable<N>, const N: usize>( &mut self, - val: T, mem_addr: tinywasm_types::MemAddr, offset: u64, - ) -> Result<()> { - let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(mem_addr)?)?; - let val = val.to_mem_bytes(); - let addr = self.stack.values.pop::<i32>()? as u64; - mem.store((offset + addr) as usize, val.len(), &val)?; - Ok(()) + cast: fn(T) -> U, + ) -> ControlFlow<Option<Error>> { + let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(mem_addr)); + let val = self.stack.values.pop::<T>(); + let val = (cast(val)).to_mem_bytes(); + let addr = self.stack.values.pop::<i32>() as u64; + if let Err(e) = mem.store((offset + addr) as usize, val.len(), &val) { + return ControlFlow::Break(Some(e)); + } + ControlFlow::Continue(()) } fn exec_table_get(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; - let idx: i32 = self.stack.values.pop::<i32>()?; + let table = self.store.get_table(self.module.resolve_table_addr(table_index)); + let idx: i32 = self.stack.values.pop::<i32>(); let v = table.get_wasm_val(idx as u32)?; self.stack.values.push_dyn(v.into()); Ok(()) } fn exec_table_set(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)?)?; - let val = self.stack.values.pop::<ValueRef>()?; - let idx = self.stack.values.pop::<i32>()? as u32; - table.set(idx, val.into())?; - Ok(()) + let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)); + let val = self.stack.values.pop::<ValueRef>(); + let idx = self.stack.values.pop::<i32>() as u32; + table.set(idx, val.into()) } fn exec_table_size(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table(self.module.resolve_table_addr(table_index)?)?; + let table = self.store.get_table(self.module.resolve_table_addr(table_index)); self.stack.values.push_dyn(table.size().into()); Ok(()) } @@ -770,22 +679,22 @@ impl<'store, 'stack> Executor<'store, 'stack> { .store .data .elements - .get(self.module.resolve_elem_addr(elem_index)? as usize) + .get(*self.module.resolve_elem_addr(elem_index) as usize) .ok_or_else(|| Error::Other("element not found".to_string()))?; let table = self .store .data .tables - .get_mut(self.module.resolve_table_addr(table_index)? as usize) + .get_mut(*self.module.resolve_table_addr(table_index) as usize) .ok_or_else(|| Error::Other("table not found".to_string()))?; let elem_len = elem.items.as_ref().map(|items| items.len()).unwrap_or(0); let table_len = table.size(); - let size: i32 = self.stack.values.pop::<i32>()?; // n - let offset: i32 = self.stack.values.pop::<i32>()?; // s - let dst: i32 = self.stack.values.pop::<i32>()?; // d + let size: i32 = self.stack.values.pop(); // n + let offset: i32 = self.stack.values.pop(); // s + let dst: i32 = self.stack.values.pop(); // d if unlikely(((size + offset) as usize > elem_len) || ((dst + size) > table_len)) { return Err(Trap::TableOutOfBounds { offset: offset as usize, len: size as usize, max: elem_len }.into()); @@ -803,29 +712,28 @@ impl<'store, 'stack> Executor<'store, 'stack> { return Err(Trap::TableOutOfBounds { offset: 0, len: 0, max: 0 }.into()); }; - table.init(self.module.func_addrs(), dst, &items[offset as usize..(offset + size) as usize])?; - Ok(()) + table.init(self.module.func_addrs(), dst, &items[offset as usize..(offset + size) as usize]) } fn exec_table_grow(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)?)?; + let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)); let sz = table.size(); - let n = self.stack.values.pop::<i32>()?; - let val = self.stack.values.pop::<ValueRef>()?; + let n = self.stack.values.pop::<i32>(); + let val = self.stack.values.pop::<ValueRef>(); match table.grow(n, val.into()) { - Ok(_) => self.stack.values.push_dyn(sz.into()), - Err(_) => self.stack.values.push_dyn((-1_i32).into()), + Ok(_) => self.stack.values.push(sz), + Err(_) => self.stack.values.push(-1_i32), } Ok(()) } fn exec_table_fill(&mut self, table_index: u32) -> Result<()> { - let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)?)?; + let table = self.store.get_table_mut(self.module.resolve_table_addr(table_index)); - let n = self.stack.values.pop::<i32>()?; - let val = self.stack.values.pop::<ValueRef>()?; - let i = self.stack.values.pop::<i32>()?; + let n = self.stack.values.pop::<i32>(); + let val = self.stack.values.pop::<ValueRef>(); + let i = self.stack.values.pop::<i32>(); if unlikely(i + n > table.size()) { return Err(Error::Trap(Trap::TableOutOfBounds { @@ -839,7 +747,11 @@ impl<'store, 'stack> Executor<'store, 'stack> { return Ok(()); } - table.fill(self.module.func_addrs(), i as usize, n as usize, val.into())?; - Ok(()) + table.fill(self.module.func_addrs(), i as usize, n as usize, val.into()) + } + + fn exec_local_copy<T: InternalValue>(&mut self, from: u16, to: u16) { + let v = self.cf.locals.get::<T>(from); + self.cf.locals.set(to, v); } } diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index 88a5e9e..89a8002 100644 --- a/crates/tinywasm/src/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -1,8 +1,9 @@ -pub(crate) trait CheckedWrappingRem +pub(crate) trait TinywasmIntExt where Self: Sized, { - fn checked_wrapping_rem(self, rhs: Self) -> Option<Self>; + fn checked_wrapping_rem(self, rhs: Self) -> Result<Self>; + fn wasm_checked_div(self, rhs: Self) -> Result<Self>; } /// Doing the actual conversion from float to int is a bit tricky, because @@ -33,28 +34,37 @@ macro_rules! checked_conv_float { }; // Conversion with an intermediate unsigned type and error checking (three types) ($from:tt, $intermediate:tt, $to:tt, $self:expr) => { - $self.stack.values.replace_top::<$from, $to>(|v| { - let (min, max) = float_min_max!($from, $intermediate); - if unlikely(v.is_nan()) { - return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); - } - if unlikely(v <= min || v >= max) { - return Err(Error::Trap(crate::Trap::IntegerOverflow)); - } - Ok((v as $intermediate as $to).into()) - })? + $self + .stack + .values + .replace_top::<$from, $to>(|v| { + let (min, max) = float_min_max!($from, $intermediate); + if unlikely(v.is_nan()) { + return Err(Error::Trap(crate::Trap::InvalidConversionToInt)); + } + if unlikely(v <= min || v >= max) { + return Err(Error::Trap(crate::Trap::IntegerOverflow)); + } + Ok((v as $intermediate as $to).into()) + }) + .to_cf()? }; } pub(crate) use checked_conv_float; pub(crate) use float_min_max; +pub(super) fn trap_0() -> Error { + Error::Trap(crate::Trap::DivisionByZero) +} pub(crate) trait TinywasmFloatExt { fn tw_minimum(self, other: Self) -> Self; fn tw_maximum(self, other: Self) -> Self; fn tw_nearest(self) -> Self; } +use crate::{Error, Result}; + #[cfg(not(feature = "std"))] use super::no_std_floats::NoStdFloatExt; @@ -147,13 +157,22 @@ impl_wrapping_self_sh! { i32 i64 u32 u64 } macro_rules! impl_checked_wrapping_rem { ($($t:ty)*) => ($( - impl CheckedWrappingRem for $t { + impl TinywasmIntExt for $t { + #[inline] + fn checked_wrapping_rem(self, rhs: Self) -> Result<Self> { + if rhs == 0 { + Err(Error::Trap(crate::Trap::DivisionByZero)) + } else { + Ok(self.wrapping_rem(rhs)) + } + } + #[inline] - fn checked_wrapping_rem(self, rhs: Self) -> Option<Self> { + fn wasm_checked_div(self, rhs: Self) -> Result<Self> { if rhs == 0 { - None + Err(Error::Trap(crate::Trap::DivisionByZero)) } else { - Some(self.wrapping_rem(rhs)) + self.checked_div(rhs).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow)) } } } diff --git a/crates/tinywasm/src/interpreter/stack/block_stack.rs b/crates/tinywasm/src/interpreter/stack/block_stack.rs index 6121bec..2267194 100644 --- a/crates/tinywasm/src/interpreter/stack/block_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/block_stack.rs @@ -1,4 +1,4 @@ -use crate::{cold, unlikely, Error, Result}; +use crate::unlikely; use alloc::vec::Vec; use crate::interpreter::values::{StackHeight, StackLocation}; @@ -37,14 +37,8 @@ impl BlockStack { } #[inline(always)] - pub(crate) fn pop(&mut self) -> Result<BlockFrame> { - match self.0.pop() { - Some(frame) => Ok(frame), - None => { - cold(); - Err(Error::BlockStackUnderflow) - } - } + pub(crate) fn pop(&mut self) -> BlockFrame { + self.0.pop().expect("block stack underflow, this is a bug") } /// keep the top `len` blocks and discard the rest diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index a7c6a74..03885f9 100644 --- a/crates/tinywasm/src/interpreter/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs @@ -1,7 +1,9 @@ +use core::ops::ControlFlow; + use super::BlockType; use crate::interpreter::values::*; -use crate::unlikely; -use crate::{Result, Trap}; +use crate::Trap; +use crate::{unlikely, Error}; use alloc::boxed::Box; use alloc::{rc::Rc, vec, vec::Vec}; @@ -26,21 +28,21 @@ impl CallStack { } #[inline(always)] - pub(crate) fn push(&mut self, call_frame: CallFrame) -> Result<()> { + pub(crate) fn push(&mut self, call_frame: CallFrame) -> ControlFlow<Option<Error>> { if unlikely((self.stack.len() + 1) >= MAX_CALL_STACK_SIZE) { - return Err(Trap::CallStackOverflow.into()); + return ControlFlow::Break(Some(Trap::CallStackOverflow.into())); } self.stack.push(call_frame); - Ok(()) + ControlFlow::Continue(()) } } #[derive(Debug)] pub(crate) struct CallFrame { - pub(crate) instr_ptr: usize, - pub(crate) block_ptr: u32, - pub(crate) func_instance: Rc<WasmFunction>, - pub(crate) module_addr: ModuleInstanceAddr, + instr_ptr: usize, + func_instance: Rc<WasmFunction>, + block_ptr: u32, + module_addr: ModuleInstanceAddr, pub(crate) locals: Locals, } @@ -53,13 +55,11 @@ pub(crate) struct Locals { } impl Locals { - // TODO: locals get_set - - pub(crate) fn get<T: InternalValue>(&self, local_index: LocalAddr) -> Result<T> { + pub(crate) fn get<T: InternalValue>(&self, local_index: LocalAddr) -> T { T::local_get(self, local_index) } - pub(crate) fn set<T: InternalValue>(&mut self, local_index: LocalAddr, value: T) -> Result<()> { + pub(crate) fn set<T: InternalValue>(&mut self, local_index: LocalAddr, value: T) { T::local_set(self, local_index, value) } } @@ -71,13 +71,13 @@ impl CallFrame { } #[inline(always)] - pub(crate) fn instr_ptr_mut(&mut self) -> &mut usize { - &mut self.instr_ptr + pub(crate) fn incr_instr_ptr(&mut self) { + self.instr_ptr += 1; } #[inline(always)] - pub(crate) fn incr_instr_ptr(&mut self) { - self.instr_ptr += 1; + pub(crate) fn jump(&mut self, offset: usize) { + self.instr_ptr += offset; } #[inline(always)] @@ -149,13 +149,13 @@ impl CallFrame { ) -> Self { let locals = { let mut locals_32 = Vec::new(); - locals_32.reserve_exact(wasm_func_inst.locals.local_32 as usize); + locals_32.reserve_exact(wasm_func_inst.locals.c32 as usize); let mut locals_64 = Vec::new(); - locals_64.reserve_exact(wasm_func_inst.locals.local_64 as usize); + locals_64.reserve_exact(wasm_func_inst.locals.c64 as usize); let mut locals_128 = Vec::new(); - locals_128.reserve_exact(wasm_func_inst.locals.local_128 as usize); + locals_128.reserve_exact(wasm_func_inst.locals.c128 as usize); let mut locals_ref = Vec::new(); - locals_ref.reserve_exact(wasm_func_inst.locals.local_ref as usize); + locals_ref.reserve_exact(wasm_func_inst.locals.cref as usize); for p in params { match p.into() { @@ -166,10 +166,10 @@ impl CallFrame { } } - locals_32.resize_with(wasm_func_inst.locals.local_32 as usize, Default::default); - locals_64.resize_with(wasm_func_inst.locals.local_64 as usize, Default::default); - locals_128.resize_with(wasm_func_inst.locals.local_128 as usize, Default::default); - locals_ref.resize_with(wasm_func_inst.locals.local_ref as usize, Default::default); + locals_32.resize_with(wasm_func_inst.locals.c32 as usize, Default::default); + locals_64.resize_with(wasm_func_inst.locals.c64 as usize, Default::default); + locals_128.resize_with(wasm_func_inst.locals.c128 as usize, Default::default); + locals_ref.resize_with(wasm_func_inst.locals.cref as usize, Default::default); Locals { locals_32: locals_32.into_boxed_slice(), diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 02d35c1..178c9ec 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -1,12 +1,12 @@ use alloc::vec::Vec; -use tinywasm_types::{LocalCounts, ValType, WasmValue}; +use tinywasm_types::{ValType, ValueCounts, ValueCountsSmall, WasmValue}; -use crate::{interpreter::values::*, Result}; +use crate::{interpreter::*, Result}; use super::Locals; -pub(crate) const STACK_32_SIZE: usize = 1024 * 128; -pub(crate) const STACK_64_SIZE: usize = 1024 * 128; -pub(crate) const STACK_128_SIZE: usize = 1024 * 128; +pub(crate) const STACK_32_SIZE: usize = 1024 * 32; +pub(crate) const STACK_64_SIZE: usize = 1024 * 16; +pub(crate) const STACK_128_SIZE: usize = 1024 * 8; pub(crate) const STACK_REF_SIZE: usize = 1024; #[derive(Debug)] @@ -37,12 +37,12 @@ impl ValueStack { } #[inline] - pub(crate) fn peek<T: InternalValue>(&self) -> Result<T> { + pub(crate) fn peek<T: InternalValue>(&self) -> T { T::stack_peek(self) } #[inline] - pub(crate) fn pop<T: InternalValue>(&mut self) -> Result<T> { + pub(crate) fn pop<T: InternalValue>(&mut self) -> T { T::stack_pop(self) } @@ -51,93 +51,88 @@ impl ValueStack { T::stack_push(self, value) } - pub(crate) fn drop<T: InternalValue>(&mut self) -> Result<()> { - T::stack_pop(self).map(|_| ()) + #[inline] + pub(crate) fn drop<T: InternalValue>(&mut self) { + T::stack_pop(self); } - // TODO: this needs to re-introduce the top replacement optimization - pub(crate) fn select<T: InternalValue>(&mut self) -> Result<()> { - let cond: i32 = self.pop()?; - let val2: T = self.pop()?; + #[inline] + pub(crate) fn select<T: InternalValue>(&mut self) { + let cond: i32 = self.pop(); + let val2: T = self.pop(); if cond == 0 { - self.drop::<T>()?; + self.drop::<T>(); self.push(val2); } - Ok(()) } - // TODO: this needs to re-introduce the top replacement optimization + #[inline] + pub(crate) fn calculate_same<T: InternalValue>(&mut self, func: fn(T, T) -> Result<T>) -> Result<()> { + T::stack_calculate(self, func) + } + + #[inline] pub(crate) fn calculate<T: InternalValue, U: InternalValue>(&mut self, func: fn(T, T) -> Result<U>) -> Result<()> { - let v2 = T::stack_pop(self)?; - let v1 = T::stack_pop(self)?; + let v2 = T::stack_pop(self); + let v1 = T::stack_pop(self); U::stack_push(self, func(v1, v2)?); Ok(()) } - // TODO: this needs to re-introduce the top replacement optimization + #[inline] pub(crate) fn replace_top<T: InternalValue, U: InternalValue>(&mut self, func: fn(T) -> Result<U>) -> Result<()> { - let v1 = T::stack_pop(self)?; + let v1 = T::stack_pop(self); U::stack_push(self, func(v1)?); Ok(()) } - pub(crate) fn pop_dyn(&mut self, val_type: ValType) -> Result<TinyWasmValue> { - match val_type { - ValType::I32 => self.pop().map(TinyWasmValue::Value32), - ValType::I64 => self.pop().map(TinyWasmValue::Value64), - ValType::V128 => self.pop().map(TinyWasmValue::Value128), - ValType::RefExtern => self.pop().map(TinyWasmValue::ValueRef), - ValType::RefFunc => self.pop().map(TinyWasmValue::ValueRef), - ValType::F32 => self.pop().map(TinyWasmValue::Value32), - ValType::F64 => self.pop().map(TinyWasmValue::Value64), - } + #[inline] + pub(crate) fn replace_top_same<T: InternalValue>(&mut self, func: fn(T) -> Result<T>) -> Result<()> { + T::replace_top(self, func) } - pub(crate) fn pop_params(&mut self, val_types: &[ValType]) -> Result<Vec<WasmValue>> { - val_types.iter().map(|val_type| self.pop_wasmvalue(*val_type)).collect::<Result<Vec<_>>>() + pub(crate) fn pop_params(&mut self, val_types: &[ValType]) -> Vec<WasmValue> { + val_types.iter().map(|val_type| self.pop_wasmvalue(*val_type)).collect::<Vec<_>>() } - pub(crate) fn pop_results(&mut self, val_types: &[ValType]) -> Result<Vec<WasmValue>> { - val_types.iter().rev().map(|val_type| self.pop_wasmvalue(*val_type)).collect::<Result<Vec<_>>>().map(|mut v| { - v.reverse(); - v - }) + pub(crate) fn pop_results(&mut self, val_types: &[ValType]) -> Vec<WasmValue> { + let mut results = val_types.iter().rev().map(|val_type| self.pop_wasmvalue(*val_type)).collect::<Vec<_>>(); + results.reverse(); + results } - // TODO: a lot of optimization potential here - pub(crate) fn pop_locals(&mut self, val_types: &[ValType], lc: LocalCounts) -> Result<Locals> { - let mut locals_32 = Vec::new(); - locals_32.reserve_exact(lc.local_32 as usize); - let mut locals_64 = Vec::new(); - locals_64.reserve_exact(lc.local_64 as usize); - let mut locals_128 = Vec::new(); - locals_128.reserve_exact(lc.local_128 as usize); - let mut locals_ref = Vec::new(); - locals_ref.reserve_exact(lc.local_ref as usize); - - for ty in val_types { - match self.pop_dyn(*ty)? { - TinyWasmValue::Value32(v) => locals_32.push(v), - TinyWasmValue::Value64(v) => locals_64.push(v), - TinyWasmValue::Value128(v) => locals_128.push(v), - TinyWasmValue::ValueRef(v) => locals_ref.push(v), - } + #[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.reverse(); - locals_32.resize_with(lc.local_32 as usize, Default::default); - locals_64.reverse(); - locals_64.resize_with(lc.local_64 as usize, Default::default); - locals_128.reverse(); - locals_128.resize_with(lc.local_128 as usize, Default::default); - locals_ref.reverse(); - locals_ref.resize_with(lc.local_ref as usize, Default::default); - - Ok(Locals { - locals_32: locals_32.into_boxed_slice(), - locals_64: locals_64.into_boxed_slice(), - locals_128: locals_128.into_boxed_slice(), - locals_ref: locals_ref.into_boxed_slice(), - }) } pub(crate) fn truncate_keep(&mut self, to: &StackLocation, keep: &StackHeight) { @@ -165,21 +160,21 @@ impl ValueStack { } } - pub(crate) fn pop_wasmvalue(&mut self, val_type: ValType) -> Result<WasmValue> { + pub(crate) fn pop_wasmvalue(&mut self, val_type: ValType) -> WasmValue { match val_type { - ValType::I32 => self.pop().map(WasmValue::I32), - ValType::I64 => self.pop().map(WasmValue::I64), - ValType::V128 => self.pop().map(WasmValue::V128), - ValType::F32 => self.pop().map(WasmValue::F32), - ValType::F64 => self.pop().map(WasmValue::F64), - ValType::RefExtern => self.pop().map(|v| match v { + ValType::I32 => WasmValue::I32(self.pop()), + ValType::I64 => WasmValue::I64(self.pop()), + ValType::V128 => WasmValue::V128(self.pop()), + ValType::F32 => WasmValue::F32(self.pop()), + ValType::F64 => WasmValue::F64(self.pop()), + ValType::RefExtern => match self.pop() { Some(v) => WasmValue::RefExtern(v), None => WasmValue::RefNull(ValType::RefExtern), - }), - ValType::RefFunc => self.pop().map(|v| match v { + }, + ValType::RefFunc => match self.pop() { Some(v) => WasmValue::RefFunc(v), None => WasmValue::RefNull(ValType::RefFunc), - }), + }, } } diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs index c934e51..35341fc 100644 --- a/crates/tinywasm/src/interpreter/values.rs +++ b/crates/tinywasm/src/interpreter/values.rs @@ -1,5 +1,5 @@ #![allow(missing_docs)] -use crate::{Error, Result}; +use crate::Result; use tinywasm_types::{LocalAddr, ValType, WasmValue}; use super::stack::{Locals, ValueStack}; @@ -137,18 +137,23 @@ mod sealed { pub trait Sealed {} } -pub(crate) trait InternalValue: sealed::Sealed { +pub(crate) trait InternalValue: sealed::Sealed + Into<TinyWasmValue> { fn stack_push(stack: &mut ValueStack, value: Self); - fn stack_pop(stack: &mut ValueStack) -> Result<Self> + fn replace_top(stack: &mut ValueStack, func: fn(Self) -> Result<Self>) -> Result<()> where Self: Sized; - fn stack_peek(stack: &ValueStack) -> Result<Self> + fn stack_calculate(stack: &mut ValueStack, func: fn(Self, Self) -> Result<Self>) -> Result<()> where Self: Sized; - fn local_get(locals: &Locals, index: LocalAddr) -> Result<Self> + + fn stack_pop(stack: &mut ValueStack) -> Self + where + Self: Sized; + fn stack_peek(stack: &ValueStack) -> Self where Self: Sized; - fn local_set(locals: &mut Locals, index: LocalAddr, value: Self) -> Result<()>; + fn local_get(locals: &Locals, index: LocalAddr) -> Self; + fn local_set(locals: &mut Locals, index: LocalAddr, value: Self); } macro_rules! impl_internalvalue { @@ -169,33 +174,43 @@ macro_rules! impl_internalvalue { stack.$stack.push($to_internal(value)); } #[inline(always)] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - match stack.$stack.pop() { - Some(v) => Ok($to_outer(v)), - None => { - crate::cold(); - Err(Error::ValueStackUnderflow) - }, + fn stack_pop(stack: &mut ValueStack) -> Self { + ($to_outer)(stack.$stack.pop().expect("ValueStack underflow, this is a bug")) + } + #[inline(always)] + fn stack_peek(stack: &ValueStack) -> Self { + ($to_outer)(*stack.$stack.last().expect("ValueStack underflow, this is a bug")) + } + + #[inline(always)] + fn stack_calculate(stack: &mut ValueStack, func: fn(Self, Self) -> Result<Self>) -> Result<()> { + let v2 = stack.$stack.pop(); + let v1 = stack.$stack.last_mut(); + if let (Some(v1), Some(v2)) = (v1, v2) { + *v1 = $to_internal(func($to_outer(*v1), $to_outer(v2))?); + } else { + unreachable!("ValueStack underflow, this is a bug"); } + Ok(()) } + #[inline(always)] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - match stack.$stack.last() { - Some(v) => Ok($to_outer(*v)), - None => { - crate::cold(); - Err(Error::ValueStackUnderflow) - }, + fn replace_top(stack: &mut ValueStack, func: fn(Self) -> Result<Self>) -> Result<()> { + if let Some(v) = stack.$stack.last_mut() { + *v = $to_internal(func($to_outer(*v))?); + Ok(()) + } else { + unreachable!("ValueStack underflow, this is a bug"); } } + #[inline(always)] - fn local_get(locals: &Locals, index: LocalAddr) -> Result<Self> { - Ok($to_outer(locals.$locals[index as usize])) + fn local_get(locals: &Locals, index: LocalAddr) -> Self { + $to_outer(locals.$locals[index as usize]) } #[inline(always)] - fn local_set(locals: &mut Locals, index: LocalAddr, value: Self) -> Result<()> { + fn local_set(locals: &mut Locals, index: LocalAddr, value: Self) { locals.$locals[index as usize] = $to_internal(value); - Ok(()) } } )* diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index 60ed61a..b9d65bc 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -60,7 +60,7 @@ impl MemoryRefMut<'_> { /// Get the current size of the memory in pages pub fn page_count(&mut self) -> usize { - self.0.page_count() + self.0.page_count } /// Copy a slice of memory to another place in memory diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index eb2bccf..b86435d 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -75,36 +75,21 @@ impl MemoryInstance { Ok(&self.data[addr..end]) } - // this is a workaround since we can't use generic const expressions yet (https://github.com/rust-lang/rust/issues/76560) pub(crate) fn load_as<const SIZE: usize, T: MemLoadable<SIZE>>(&self, addr: usize) -> Result<T> { let Some(end) = addr.checked_add(SIZE) else { - cold(); return Err(self.trap_oob(addr, SIZE)); }; if end > self.data.len() { - cold(); return Err(self.trap_oob(addr, SIZE)); } Ok(T::from_le_bytes(match self.data[addr..end].try_into() { Ok(bytes) => bytes, - Err(_) => { - cold(); - return Err(Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: addr, - len: SIZE, - max: self.data.len(), - })); - } + Err(_) => return Err(self.trap_oob(addr, SIZE)), })) } - #[inline] - pub(crate) fn page_count(&self) -> usize { - self.page_count - } - pub(crate) fn fill(&mut self, addr: usize, len: usize, val: u8) -> Result<()> { let end = addr.checked_add(len).ok_or_else(|| self.trap_oob(addr, len))?; if end > self.data.len() { @@ -144,7 +129,7 @@ impl MemoryInstance { #[inline] pub(crate) fn grow(&mut self, pages_delta: i32) -> Option<i32> { - let current_pages = self.page_count(); + let current_pages = self.page_count; let new_pages = current_pages as i64 + pages_delta as i64; debug_assert!(new_pages <= i32::MAX as i64, "page count should never be greater than i32::MAX"); @@ -256,9 +241,9 @@ mod memory_instance_tests { #[test] fn test_memory_grow() { let mut memory = create_test_memory(); - let original_pages = memory.page_count(); + let original_pages = memory.page_count; assert_eq!(memory.grow(1), Some(original_pages as i32)); - assert_eq!(memory.page_count(), original_pages + 1); + assert_eq!(memory.page_count, original_pages + 1); } #[test] diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 7fc8460..bc4055d 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -1,4 +1,5 @@ use alloc::{boxed::Box, format, string::ToString, vec::Vec}; +use core::fmt::Debug; use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; @@ -26,7 +27,6 @@ static STORE_ID: AtomicUsize = AtomicUsize::new(0); /// functions, you should create a new store and then drop it when you're done (e.g. in a request handler) /// /// See <https://webassembly.github.io/spec/core/exec/runtime.html#store> -#[derive(Debug)] pub struct Store { id: usize, module_instances: Vec<ModuleInstance>, @@ -35,6 +35,16 @@ pub struct Store { pub(crate) runtime: Runtime, } +impl Debug for Store { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Store") + .field("id", &self.id) + .field("module_instances", &self.module_instances) + .field("data", &"...") + .finish() + } +} + #[derive(Debug, Clone, Copy)] pub(crate) enum Runtime { Default, @@ -76,7 +86,7 @@ impl Default for Store { } } -#[derive(Debug, Default)] +#[derive(Default)] /// Global state that can be manipulated by WebAssembly programs /// /// Data should only be addressable by the module that owns it @@ -112,42 +122,30 @@ impl Store { /// Get the function at the actual index in the store #[inline] - pub(crate) fn get_func(&self, addr: FuncAddr) -> Result<&FunctionInstance> { - self.data.funcs.get(addr as usize).ok_or_else(|| Self::not_found_error("function")) + pub(crate) fn get_func(&self, addr: &FuncAddr) -> &FunctionInstance { + &self.data.funcs[*addr as usize] } /// Get the memory at the actual index in the store #[inline] - pub(crate) fn get_mem(&self, addr: MemAddr) -> Result<&MemoryInstance> { - match self.data.memories.get(addr as usize) { - Some(mem) => Ok(mem), - None => { - cold(); - Err(Self::not_found_error("memory")) - } - } + pub(crate) fn get_mem(&self, addr: &MemAddr) -> &MemoryInstance { + &self.data.memories[*addr as usize] } /// Get the memory at the actual index in the store - #[inline] - pub(crate) fn get_mem_mut(&mut self, addr: MemAddr) -> Result<&mut MemoryInstance> { - match self.data.memories.get_mut(addr as usize) { - Some(mem) => Ok(mem), - None => { - cold(); - Err(Self::not_found_error("memory")) - } - } + #[inline(always)] + pub(crate) fn get_mem_mut(&mut self, addr: &MemAddr) -> &mut MemoryInstance { + &mut self.data.memories[*addr as usize] } /// Get the memory at the actual index in the store - #[inline] + #[inline(always)] pub(crate) fn get_mems_mut( &mut self, - addr: MemAddr, - addr2: MemAddr, + addr: &MemAddr, + addr2: &MemAddr, ) -> Result<(&mut MemoryInstance, &mut MemoryInstance)> { - match get_pair_mut(&mut self.data.memories, addr as usize, addr2 as usize) { + match get_pair_mut(&mut self.data.memories, *addr as usize, *addr2 as usize) { Some(mems) => Ok(mems), None => { cold(); @@ -158,24 +156,24 @@ impl Store { /// Get the table at the actual index in the store #[inline] - pub(crate) fn get_table(&self, addr: TableAddr) -> Result<&TableInstance> { - self.data.tables.get(addr as usize).ok_or_else(|| Self::not_found_error("table")) + pub(crate) fn get_table(&self, addr: &TableAddr) -> &TableInstance { + &self.data.tables[*addr as usize] } /// Get the table at the actual index in the store #[inline] - pub(crate) fn get_table_mut(&mut self, addr: TableAddr) -> Result<&mut TableInstance> { - self.data.tables.get_mut(addr as usize).ok_or_else(|| Self::not_found_error("table")) + pub(crate) fn get_table_mut(&mut self, addr: &TableAddr) -> &mut TableInstance { + &mut self.data.tables[*addr as usize] } /// Get two mutable tables at the actual index in the store #[inline] pub(crate) fn get_tables_mut( &mut self, - addr: TableAddr, - addr2: TableAddr, + addr: &TableAddr, + addr2: &TableAddr, ) -> Result<(&mut TableInstance, &mut TableInstance)> { - match get_pair_mut(&mut self.data.tables, addr as usize, addr2 as usize) { + match get_pair_mut(&mut self.data.tables, *addr as usize, *addr2 as usize) { Some(tables) => Ok(tables), None => { cold(); @@ -186,37 +184,32 @@ impl Store { /// Get the data at the actual index in the store #[inline] - pub(crate) fn get_data_mut(&mut self, addr: DataAddr) -> Result<&mut DataInstance> { - self.data.datas.get_mut(addr as usize).ok_or_else(|| Self::not_found_error("data")) + pub(crate) fn get_data_mut(&mut self, addr: &DataAddr) -> &mut DataInstance { + &mut self.data.datas[*addr as usize] } /// Get the element at the actual index in the store #[inline] - pub(crate) fn get_elem_mut(&mut self, addr: ElemAddr) -> Result<&mut ElementInstance> { - self.data.elements.get_mut(addr as usize).ok_or_else(|| Self::not_found_error("element")) + pub(crate) fn get_elem_mut(&mut self, addr: &ElemAddr) -> &mut ElementInstance { + &mut self.data.elements[*addr as usize] } /// Get the global at the actual index in the store #[inline] - pub(crate) fn get_global(&self, addr: GlobalAddr) -> Result<&GlobalInstance> { - self.data.globals.get(addr as usize).ok_or_else(|| Self::not_found_error("global")) + pub(crate) fn get_global(&self, addr: &GlobalAddr) -> &GlobalInstance { + &self.data.globals[*addr as usize] } /// Get the global at the actual index in the store #[doc(hidden)] - pub fn get_global_val(&self, addr: MemAddr) -> Result<TinyWasmValue> { - self.data - .globals - .get(addr as usize) - .ok_or_else(|| Self::not_found_error("global")) - .map(|global| global.value.get()) + pub fn get_global_val(&self, addr: &MemAddr) -> TinyWasmValue { + self.data.globals[*addr as usize].value.get() } /// Set the global at the actual index in the store #[doc(hidden)] - pub fn set_global_val(&mut self, addr: MemAddr, value: TinyWasmValue) -> Result<()> { - let global = self.data.globals.get(addr as usize).ok_or_else(|| Self::not_found_error("global")); - global.map(|global| global.value.set(value)) + pub fn set_global_val(&mut self, addr: &MemAddr, value: TinyWasmValue) { + self.data.globals[*addr as usize].value.set(value); } } diff --git a/crates/tinywasm/tests/testsuite/run.rs b/crates/tinywasm/tests/testsuite/run.rs index c005b9a..0c5f3ff 100644 --- a/crates/tinywasm/tests/testsuite/run.rs +++ b/crates/tinywasm/tests/testsuite/run.rs @@ -414,9 +414,7 @@ impl TestSuite { }; let module_global = match match module.export_addr(global) { - Some(ExternVal::Global(addr)) => { - store.get_global_val(addr).map_err(|_| eyre!("failed to get global")) - } + Some(ExternVal::Global(addr)) => Ok(store.get_global_val(&addr)), _ => Err(eyre!("no module to get global from")), } { Ok(module_global) => module_global, diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index dd201ad..d77d66d 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -57,16 +57,21 @@ pub enum Instruction { // LocalGet2(LocalAddr, LocalAddr), // LocalGet3(LocalAddr, LocalAddr, LocalAddr), // LocalGetSet(LocalAddr, LocalAddr), + + // LocalGetGet32(LocalAddr, LocalAddr), LocalGetGet64(LocalAddr, LocalAddr), LocalGetGet128(LocalAddr, LocalAddr), + // LocalTeeGet32(LocalAddr, LocalAddr), LocalTeeGet64(LocalAddr, LocalAddr), LocalTeeGet128(LocalAddr, LocalAddr), + LocalCopy32(LocalAddr, LocalAddr), LocalCopy64(LocalAddr, LocalAddr), LocalCopy128(LocalAddr, LocalAddr), LocalCopy128Ref(LocalAddr, LocalAddr), LocalCopyRef(LocalAddr, LocalAddr), + LocalsStore32(LocalAddr, LocalAddr, u32, MemAddr), LocalsStore64(LocalAddr, LocalAddr, u32, MemAddr), LocalsStore128(LocalAddr, LocalAddr, u32, MemAddr), LocalsStoreRef(LocalAddr, LocalAddr, u32, MemAddr), // > Control Instructions // See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions> Unreachable, Nop, - - Block(EndOffset), + + Block(EndOffset), BlockWithType(ValType, EndOffset), BlockWithFuncType(TypeAddr, EndOffset), - + Loop(EndOffset), LoopWithType(ValType, EndOffset), LoopWithFuncType(TypeAddr, EndOffset), @@ -84,6 +89,8 @@ pub enum Instruction { Return, Call(FuncAddr), CallIndirect(TypeAddr, TableAddr), + ReturnCall(FuncAddr), + ReturnCallIndirect(TypeAddr, TableAddr), // > Parametric Instructions // See <https://webassembly.github.io/spec/core/binary/instructions.html#parametric-instructions> diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index db39689..a4029e2 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -179,18 +179,28 @@ pub struct FuncType { #[derive(Debug, Default, Clone, Copy, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] -pub struct LocalCounts { - pub local_32: u32, - pub local_64: u32, - pub local_128: u32, - pub local_ref: u32, +pub struct ValueCounts { + pub c32: u32, + pub c64: u32, + pub c128: u32, + pub cref: u32, } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] +pub struct ValueCountsSmall { + pub c32: u16, + pub c64: u16, + pub c128: u16, + pub cref: u16, +} + +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct WasmFunction { pub instructions: Box<[Instruction]>, - pub locals: LocalCounts, + pub locals: ValueCounts, + pub params: ValueCountsSmall, pub ty: FuncType, } |
