diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-06-28 23:19:05 +0200 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-06-28 23:19:05 +0200 |
| commit | 7e8770cc4e418ef1a8cdfd9b1f59ee14b07cc6c9 (patch) | |
| tree | 7865a95f3a5b6d32b5b26aa42ec3ed730def3d95 | |
| parent | abec4b6cec6ced6c0500348ac5990486a4fe8287 (diff) | |
chore: restructure runtime
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
28 files changed, 396 insertions, 793 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index da8359c..54725cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved support for WebAssembly 2.0 features - Simplify and optimize the interpreter loop - Use a seperate stack and locals for 32, 64 and 128 bit values and references (#21) -- Updated to latest wasmparser version +- Updated to latest `wasmparser` version ## [0.7.0] - 2024-05-15 diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs index 1d550f3..214487f 100644 --- a/crates/parser/src/conversion.rs +++ b/crates/parser/src/conversion.rs @@ -225,14 +225,6 @@ pub(crate) fn convert_module_type(ty: wasmparser::RecGroup) -> Result<FuncType> Ok(FuncType { params, results }) } -pub(crate) fn convert_blocktype(blocktype: wasmparser::BlockType) -> BlockArgs { - match blocktype { - wasmparser::BlockType::Empty => BlockArgs::Empty, - wasmparser::BlockType::Type(ty) => BlockArgs::Type(convert_valtype(&ty)), - wasmparser::BlockType::FuncType(ty) => BlockArgs::FuncType(ty), - } -} - pub(crate) fn convert_reftype(reftype: &wasmparser::RefType) -> ValType { match reftype { _ if reftype.is_func_ref() => ValType::RefFunc, diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 1db00ad..e9844aa 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -1,6 +1,6 @@ -use crate::{conversion::convert_blocktype, Result}; +use crate::Result; -use crate::conversion::convert_heaptype; +use crate::conversion::{convert_heaptype, convert_valtype}; use alloc::string::ToString; use alloc::{boxed::Box, vec::Vec}; use tinywasm_types::{Instruction, MemoryArg}; @@ -357,7 +357,13 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild } fn visit_local_get(&mut self, idx: u32) -> Self::Output { - let resolved_idx = self.local_addr_map[idx as usize]; + let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else { + self.errors.push(crate::ParseError::UnsupportedOperator( + "Local index is too large, tinywasm does not support local indexes that large".to_string(), + )); + return; + }; + match self.validator.get_local_type(idx) { Some(t) => self.instructions.push(match t { wasmparser::ValType::I32 => Instruction::LocalGet32(resolved_idx), @@ -372,7 +378,13 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - let resolved_idx = self.local_addr_map[idx as usize]; + let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else { + self.errors.push(crate::ParseError::UnsupportedOperator( + "Local index is too large, tinywasm does not support local indexes that large".to_string(), + )); + return; + }; + match self.validator.get_operand_type(0) { Some(Some(t)) => self.instructions.push(match t { wasmparser::ValType::I32 => Instruction::LocalSet32(resolved_idx), @@ -387,7 +399,13 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild } fn visit_local_tee(&mut self, idx: u32) -> Self::Output { - let resolved_idx = self.local_addr_map[idx as usize]; + let Ok(resolved_idx) = self.local_addr_map[idx as usize].try_into() else { + self.errors.push(crate::ParseError::UnsupportedOperator( + "Local index is too large, tinywasm does not support local indexes that large".to_string(), + )); + return; + }; + match self.validator.get_operand_type(0) { Some(Some(t)) => self.instructions.push(match t { wasmparser::ValType::I32 => Instruction::LocalTee32(resolved_idx), @@ -411,17 +429,29 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); - self.instructions.push(Instruction::Block(convert_blocktype(blockty), 0)) + self.instructions.push(match blockty { + wasmparser::BlockType::Empty => Instruction::Block(0), + wasmparser::BlockType::FuncType(idx) => Instruction::BlockWithFuncType(idx, 0), + wasmparser::BlockType::Type(ty) => Instruction::BlockWithType(convert_valtype(&ty), 0), + }) } fn visit_loop(&mut self, ty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); - self.instructions.push(Instruction::Loop(convert_blocktype(ty), 0)) + self.instructions.push(match ty { + wasmparser::BlockType::Empty => Instruction::Loop(0), + wasmparser::BlockType::FuncType(idx) => Instruction::LoopWithFuncType(idx, 0), + wasmparser::BlockType::Type(ty) => Instruction::LoopWithType(convert_valtype(&ty), 0), + }) } fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); - self.instructions.push(Instruction::If(convert_blocktype(ty).into(), 0, 0)) + self.instructions.push(match ty { + wasmparser::BlockType::Empty => Instruction::If(0, 0), + wasmparser::BlockType::FuncType(idx) => Instruction::IfWithFuncType(idx, 0, 0), + wasmparser::BlockType::Type(ty) => Instruction::IfWithType(convert_valtype(&ty), 0, 0), + }) } fn visit_else(&mut self) -> Self::Output { @@ -451,12 +481,18 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild }; let if_instruction = &mut self.instructions[if_label_pointer]; - let Instruction::If(_, else_offset, end_offset) = if_instruction else { - self.errors.push(crate::ParseError::UnsupportedOperator( - "Expected to end an if block, but the last label was not an if".to_string(), - )); - return; + let (else_offset, end_offset) = match if_instruction { + Instruction::If(else_offset, end_offset) + | Instruction::IfWithFuncType(_, else_offset, end_offset) + | Instruction::IfWithType(_, else_offset, end_offset) => (else_offset, end_offset), + _ => { + self.errors.push(crate::ParseError::UnsupportedOperator( + "Expected to end an if block, but the last label was not an if".to_string(), + )); + + return; + } }; *else_offset = (label_pointer - if_label_pointer) @@ -467,9 +503,15 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild .try_into() .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); } - Some(Instruction::Block(_, end_offset)) - | Some(Instruction::Loop(_, end_offset)) - | Some(Instruction::If(_, _, end_offset)) => { + Some(Instruction::Block(end_offset)) + | Some(Instruction::BlockWithType(_, end_offset)) + | Some(Instruction::BlockWithFuncType(_, end_offset)) + | Some(Instruction::Loop(end_offset)) + | Some(Instruction::LoopWithFuncType(_, end_offset)) + | Some(Instruction::LoopWithType(_, end_offset)) + | Some(Instruction::If(_, end_offset)) + | Some(Instruction::IfWithFuncType(_, _, end_offset)) + | Some(Instruction::IfWithType(_, _, end_offset)) => { *end_offset = (current_instr_ptr - label_pointer) .try_into() .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); diff --git a/crates/tinywasm/Cargo.toml b/crates/tinywasm/Cargo.toml index d82d850..4e15123 100644 --- a/crates/tinywasm/Cargo.toml +++ b/crates/tinywasm/Cargo.toml @@ -19,16 +19,6 @@ tinywasm-parser={version="0.7.0", path="../parser", default-features=false, opti tinywasm-types={version="0.7.0", path="../types", default-features=false} libm={version="0.2", default-features=false} -# maybe? -# arrayvec={version="0.7"} instead of the custom implementation -# bumpalo={version="3.16"} -# wide= for simd -# vec1= might be useful? fast .last() and .first() access -# https://github.com/lumol-org/soa-derive could be useful for the memory layout of Stacks - -#https://alic.dev/blog/dense-enums -# https://docs.rs/tagged-pointer/latest/tagged_pointer/ - [dev-dependencies] wasm-testsuite={path="../wasm-testsuite"} wast={version="212.0"} diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 650f9df..3b97d93 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,4 +1,4 @@ -use crate::runtime::{CallFrame, Stack}; +use crate::interpreter::{CallFrame, Stack}; use crate::{log, unlikely, Function}; use crate::{Error, FuncContext, Result, Store}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; @@ -59,7 +59,7 @@ impl FuncHandle { }; // 6. Let f be the dummy frame - let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, params, 0); + let call_frame = CallFrame::new(wasm_func.clone(), func_inst._owner, params, 0); // 7. Push the frame f to the call stack // & 8. Push the values to the stack (Not needed since the call frame owns the values) diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index f24ed73..8c208f0 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -348,7 +348,7 @@ impl Imports { ) -> Result<ResolvedImports> { let mut imports = ResolvedImports::new(); - for import in module.data.imports.iter() { + for import in module.0.imports.iter() { let val = self.take(store, import).ok_or_else(|| LinkingError::unknown_import(import))?; match val { @@ -368,7 +368,7 @@ impl Imports { } (Extern::Function(extern_func), ImportKind::Function(ty)) => { let import_func_type = module - .data + .0 .func_types .get(*ty as usize) .ok_or_else(|| LinkingError::incompatible_import_type(import))?; @@ -409,7 +409,7 @@ impl Imports { (ExternVal::Func(func_addr), ImportKind::Function(ty)) => { let func = store.get_func(func_addr)?; let import_func_type = module - .data + .0 .func_types .get(*ty as usize) .ok_or_else(|| LinkingError::incompatible_import_type(import))?; diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 4ad3d09..7250cf0 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -64,45 +64,39 @@ impl ModuleInstance { let idx = store.next_module_instance_idx(); let mut addrs = imports.unwrap_or_default().link(store, &module, idx)?; - let data = module.data; - addrs.funcs.extend(store.init_funcs(data.funcs.into(), idx)?); - addrs.tables.extend(store.init_tables(data.table_types.into(), idx)?); - addrs.memories.extend(store.init_memories(data.memory_types.into(), idx)?); + addrs.funcs.extend(store.init_funcs(module.0.funcs.into(), idx)?); + addrs.tables.extend(store.init_tables(module.0.table_types.into(), idx)?); + addrs.memories.extend(store.init_memories(module.0.memory_types.into(), idx)?); - let global_addrs = store.init_globals(addrs.globals, data.globals.into(), &addrs.funcs, idx)?; + let global_addrs = store.init_globals(addrs.globals, module.0.globals.into(), &addrs.funcs, idx)?; let (elem_addrs, elem_trapped) = - store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &data.elements, idx)?; - let (data_addrs, data_trapped) = store.init_datas(&addrs.memories, data.data.into(), idx)?; + store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &module.0.elements, idx)?; + let (data_addrs, data_trapped) = store.init_datas(&addrs.memories, module.0.data.into(), idx)?; let instance = ModuleInstanceInner { failed_to_instantiate: elem_trapped.is_some() || data_trapped.is_some(), store_id: store.id(), idx, - types: data.func_types, + types: module.0.func_types, func_addrs: addrs.funcs.into_boxed_slice(), table_addrs: addrs.tables.into_boxed_slice(), mem_addrs: addrs.memories.into_boxed_slice(), global_addrs: global_addrs.into_boxed_slice(), elem_addrs, data_addrs, - func_start: data.start_func, - imports: data.imports, - exports: data.exports, + func_start: module.0.start_func, + imports: module.0.imports, + exports: module.0.exports, }; let instance = ModuleInstance::new(instance); store.add_instance(instance.clone()); - if let Some(trap) = elem_trapped { - return Err(trap.into()); - }; - - if let Some(trap) = data_trapped { - return Err(trap.into()); - }; - - Ok(instance) + match (elem_trapped, data_trapped) { + (Some(trap), _) | (_, Some(trap)) => Err(trap.into()), + _ => Ok(instance), + } } /// Get a export by name @@ -224,13 +218,13 @@ impl ModuleInstance { /// Get a memory by address pub fn memory<'a>(&self, store: &'a mut Store, addr: MemAddr) -> Result<MemoryRef<'a>> { let mem = store.get_mem(self.resolve_mem_addr(addr)?)?; - Ok(MemoryRef { instance: mem.borrow() }) + Ok(MemoryRef(mem.borrow())) } /// 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(self.resolve_mem_addr(addr)?)?; - Ok(MemoryRefMut { instance: mem.borrow_mut() }) + Ok(MemoryRefMut(mem.borrow_mut())) } /// Get the start function of the module diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/interpreter/executor.rs index 8028e87..386099d 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -1,34 +1,25 @@ -mod num_helpers; - #[cfg(not(feature = "std"))] mod no_std_floats; +use interpreter::CallFrame; #[cfg(not(feature = "std"))] #[allow(unused_imports)] use no_std_floats::NoStdFloatExt; use alloc::{format, rc::Rc, string::ToString}; use core::ops::ControlFlow; -use num_helpers::*; use tinywasm_types::*; -use super::stack::{values::StackHeight, BlockFrame, BlockType}; -use super::{values::*, InterpreterRuntime, Stack}; -use crate::runtime::CallFrame; +use super::num_helpers::*; +use super::stack::{values::StackHeight, BlockFrame, BlockType, Stack}; +use super::values::*; use crate::*; -impl InterpreterRuntime { - pub(crate) fn exec(&self, store: &mut Store, stack: &mut Stack) -> Result<()> { - Executor::new(store, stack)?.run_to_completion() - } -} - -struct Executor<'store, 'stack> { - store: &'store mut Store, - stack: &'stack mut Stack, - +pub(super) struct Executor<'store, 'stack> { cf: CallFrame, module: ModuleInstance, + store: &'store mut Store, + stack: &'stack mut Stack, } impl<'store, 'stack> Executor<'store, 'stack> { @@ -68,10 +59,28 @@ impl<'store, 'stack> Executor<'store, 'stack> { Call(v) => return self.exec_call_direct(*v), CallIndirect(ty, table) => return self.exec_call_indirect(*ty, *table), - If(args, el, end) => self.exec_if((*args).into(), *el, *end)?, + 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(args, end) => self.enter_block(self.cf.instr_ptr(), *end, BlockType::Loop, *args), - Block(args, end) => self.enter_block(self.cf.instr_ptr(), *end, BlockType::Block, *args), + 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)) + } Br(v) => return self.exec_br(*v), BrIf(v) => return self.exec_br_if(*v), BrTable(default, len) => return self.exec_brtable(*default, *len), @@ -429,7 +438,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { } }; - self.exec_call(wasm_func.clone(), func_inst.owner) + self.exec_call(wasm_func.clone(), func_inst._owner) } fn exec_call_indirect(&mut self, type_addr: u32, table_addr: u32) -> Result<ControlFlow<()>> { // verify that the table is of the right type, this should be validated by the parser already @@ -467,17 +476,22 @@ impl<'store, 'stack> Executor<'store, 'stack> { }; if wasm_func.ty == *call_ty { - return self.exec_call(wasm_func.clone(), func_inst.owner); + return self.exec_call(wasm_func.clone(), func_inst._owner); } cold(); Err(Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }.into()) } - fn exec_if(&mut self, args: BlockArgs, else_offset: u32, end_offset: u32) -> Result<()> { + fn exec_if( + &mut self, + else_offset: u32, + end_offset: u32, + (params, results): (StackHeight, StackHeight), + ) -> Result<()> { // truthy value is on the top of the stack, so enter the then block if self.stack.values.pop::<i32>()? != 0 { - self.enter_block(self.cf.instr_ptr(), end_offset, BlockType::If, args); + self.enter_block(self.cf.instr_ptr(), end_offset, BlockType::If, (params, results)); return Ok(()); } @@ -489,7 +503,7 @@ impl<'store, 'stack> Executor<'store, 'stack> { 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, args); + self.enter_block(old + else_offset as usize, end_offset - else_offset, BlockType::Else, (params, results)); Ok(()) } fn exec_else(&mut self, end_offset: u32) -> Result<()> { @@ -497,16 +511,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { *self.cf.instr_ptr_mut() += end_offset as usize; Ok(()) } - fn enter_block(&mut self, instr_ptr: usize, end_instr_offset: u32, ty: BlockType, args: BlockArgs) { - let (params, results) = match args { - BlockArgs::Empty => (StackHeight::default(), StackHeight::default()), - BlockArgs::Type(t) => (StackHeight::default(), t.into()), - BlockArgs::FuncType(t) => { - let ty = self.module.func_ty(t); - ((&*ty.params).into(), (&*ty.results).into()) - } - }; - + 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), + ) { self.stack.blocks.push(BlockFrame { instr_ptr, end_instr_offset, diff --git a/crates/tinywasm/src/runtime/mod.rs b/crates/tinywasm/src/interpreter/mod.rs index fca58cc..f9096f8 100644 --- a/crates/tinywasm/src/runtime/mod.rs +++ b/crates/tinywasm/src/interpreter/mod.rs @@ -1,4 +1,5 @@ -mod interpreter; +mod executor; +mod num_helpers; pub(crate) mod stack; #[doc(hidden)] @@ -7,8 +8,16 @@ pub use stack::values::*; pub(crate) use stack::{CallFrame, Stack}; +use crate::{Result, Store}; + /// The main TinyWasm runtime. /// /// This is the default runtime used by TinyWasm. #[derive(Debug, Default)] pub struct InterpreterRuntime {} + +impl InterpreterRuntime { + pub(crate) fn exec(&self, store: &mut Store, stack: &mut Stack) -> Result<()> { + executor::Executor::new(store, stack)?.run_to_completion() + } +} diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/interpreter/no_std_floats.rs index 5b9471e..5b9471e 100644 --- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs +++ b/crates/tinywasm/src/interpreter/no_std_floats.rs diff --git a/crates/tinywasm/src/runtime/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index d0402bc..88a5e9e 100644 --- a/crates/tinywasm/src/runtime/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -11,7 +11,7 @@ where /// Rust sadly doesn't have wrapping casts for floats yet, maybe never. /// Alternatively, https://crates.io/crates/az could be used for this but /// it's not worth the dependency. -#[rustfmt::skip] +#[rustfmt::skip] macro_rules! float_min_max { (f32, i32) => {(-2147483904.0_f32, 2147483648.0_f32)}; (f64, i32) => {(-2147483649.0_f64, 2147483648.0_f64)}; diff --git a/crates/tinywasm/src/runtime/stack/block_stack.rs b/crates/tinywasm/src/interpreter/stack/block_stack.rs index cef536a..cef536a 100644 --- a/crates/tinywasm/src/runtime/stack/block_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/block_stack.rs diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index 8e9dbe7..8e9dbe7 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs diff --git a/crates/tinywasm/src/runtime/stack/mod.rs b/crates/tinywasm/src/interpreter/stack/mod.rs index 3902bd2..3902bd2 100644 --- a/crates/tinywasm/src/runtime/stack/mod.rs +++ b/crates/tinywasm/src/interpreter/stack/mod.rs diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 40376f1..fc23b48 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -106,10 +106,19 @@ impl ValueStack { } pub(crate) fn truncate_keep(&mut self, to: &StackLocation, keep: &StackHeight) { - truncate_keep(&mut self.stack_32, to.s32, keep.s32); - truncate_keep(&mut self.stack_64, to.s64, keep.s64); - truncate_keep(&mut self.stack_128, to.s128, keep.s128); - truncate_keep(&mut self.stack_ref, to.sref, keep.sref); + #[inline(always)] + fn truncate_keep<T: Copy + Default>(data: &mut Vec<T>, n: u32, end_keep: u32) { + let len = data.len() as u32; + if len <= n { + return; // No need to truncate if the current size is already less than or equal to total_to_keep + } + data.drain((n as usize)..(len - end_keep) as usize); + } + + truncate_keep(&mut self.stack_32, to.s32, keep.s32 as u32); + truncate_keep(&mut self.stack_64, to.s64, keep.s64 as u32); + truncate_keep(&mut self.stack_128, to.s128, keep.s128 as u32); + truncate_keep(&mut self.stack_ref, to.sref, keep.sref as u32); } pub(crate) fn push_dyn(&mut self, value: TinyWasmValue) { @@ -145,42 +154,3 @@ impl ValueStack { } } } - -fn truncate_keep<T: Copy + Default>(data: &mut Vec<T>, n: u32, end_keep: u32) { - let total_to_keep = n + end_keep; - let len = data.len() as u32; - crate::log::error!("truncate_keep: len: {}, total_to_keep: {}, end_keep: {}", len, total_to_keep, end_keep); - - if len <= n { - return; // No need to truncate if the current size is already less than or equal to total_to_keep - } - - data.drain((n as usize)..(len - end_keep) as usize); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_truncate_keep() { - macro_rules! test_macro { - ($( $n:expr, $end_keep:expr, $expected:expr ),*) => { - $( - let mut stack = alloc::vec![1,2,3,4,5]; - truncate_keep(&mut stack, $n, $end_keep); - assert_eq!(stack.len(), $expected); - )* - }; - } - - test_macro! { - 0, 0, 0, - 1, 0, 1, - 0, 1, 1, - 1, 1, 2, - 2, 1, 3, - 2, 2, 4 - } - } -} diff --git a/crates/tinywasm/src/interpreter/stack/values.rs b/crates/tinywasm/src/interpreter/stack/values.rs new file mode 100644 index 0000000..6e8274f --- /dev/null +++ b/crates/tinywasm/src/interpreter/stack/values.rs @@ -0,0 +1,200 @@ +#![allow(missing_docs)] +use super::{call_stack::Locals, ValueStack}; +use crate::{Error, Result}; +use tinywasm_types::{LocalAddr, ValType, WasmValue}; + +pub(crate) type Value32 = u32; +pub(crate) type Value64 = u64; +pub(crate) type Value128 = u128; +pub(crate) type ValueRef = Option<u32>; + +#[derive(Debug, Clone, Copy, PartialEq)] +/// A untyped WebAssembly value +pub enum TinyWasmValue { + Value32(Value32), + Value64(Value64), + Value128(Value128), + ValueRef(ValueRef), +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct StackLocation { + pub(crate) s32: u32, + pub(crate) s64: u32, + pub(crate) s128: u32, + pub(crate) sref: u32, +} + +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct StackHeight { + pub(crate) s32: u16, + pub(crate) s64: u16, + pub(crate) s128: u16, + pub(crate) sref: u16, +} + +impl From<ValType> for StackHeight { + fn from(value: ValType) -> Self { + match value { + ValType::I32 | ValType::F32 => Self { s32: 1, ..Default::default() }, + ValType::I64 | ValType::F64 => Self { s64: 1, ..Default::default() }, + ValType::V128 => Self { s128: 1, ..Default::default() }, + ValType::RefExtern | ValType::RefFunc => Self { sref: 1, ..Default::default() }, + } + } +} + +impl From<&[ValType]> for StackHeight { + fn from(value: &[ValType]) -> Self { + let mut s32 = 0; + let mut s64 = 0; + let mut s128 = 0; + let mut sref = 0; + for val_type in value.iter() { + match val_type { + ValType::I32 | ValType::F32 => s32 += 1, + ValType::I64 | ValType::F64 => s64 += 1, + ValType::V128 => s128 += 1, + ValType::RefExtern | ValType::RefFunc => sref += 1, + } + } + Self { s32, s64, s128, sref } + } +} + +impl TinyWasmValue { + pub fn unwrap_32(&self) -> Value32 { + match self { + TinyWasmValue::Value32(v) => *v, + _ => unreachable!("Expected Value32"), + } + } + + pub fn unwrap_64(&self) -> Value64 { + match self { + TinyWasmValue::Value64(v) => *v, + _ => unreachable!("Expected Value64"), + } + } + + pub fn unwrap_128(&self) -> Value128 { + match self { + TinyWasmValue::Value128(v) => *v, + _ => unreachable!("Expected Value128"), + } + } + + pub fn unwrap_ref(&self) -> ValueRef { + match self { + TinyWasmValue::ValueRef(v) => *v, + _ => unreachable!("Expected ValueRef"), + } + } + + pub fn attach_type(&self, ty: ValType) -> WasmValue { + match ty { + ValType::I32 => WasmValue::I32(self.unwrap_32() as i32), + ValType::I64 => WasmValue::I64(self.unwrap_64() as i64), + ValType::F32 => WasmValue::F32(f32::from_bits(self.unwrap_32())), + ValType::F64 => WasmValue::F64(f64::from_bits(self.unwrap_64())), + ValType::V128 => WasmValue::V128(self.unwrap_128()), + ValType::RefExtern => match self.unwrap_ref() { + Some(v) => WasmValue::RefExtern(v), + None => WasmValue::RefNull(ValType::RefExtern), + }, + ValType::RefFunc => match self.unwrap_ref() { + Some(v) => WasmValue::RefFunc(v), + None => WasmValue::RefNull(ValType::RefFunc), + }, + } + } +} + +impl From<&WasmValue> for TinyWasmValue { + fn from(value: &WasmValue) -> Self { + match value { + WasmValue::I32(v) => TinyWasmValue::Value32(*v as u32), + WasmValue::I64(v) => TinyWasmValue::Value64(*v as u64), + WasmValue::V128(v) => TinyWasmValue::Value128(*v), + WasmValue::F32(v) => TinyWasmValue::Value32(v.to_bits()), + WasmValue::F64(v) => TinyWasmValue::Value64(v.to_bits()), + WasmValue::RefFunc(v) => TinyWasmValue::ValueRef(Some(*v)), + WasmValue::RefExtern(v) => TinyWasmValue::ValueRef(Some(*v)), + WasmValue::RefNull(_) => TinyWasmValue::ValueRef(None), + } + } +} + +impl From<WasmValue> for TinyWasmValue { + fn from(value: WasmValue) -> Self { + TinyWasmValue::from(&value) + } +} + +mod sealed { + #[allow(unreachable_pub)] + pub trait Sealed {} +} + +pub(crate) trait InternalValue: sealed::Sealed { + fn stack_push(stack: &mut ValueStack, value: Self); + fn stack_pop(stack: &mut ValueStack) -> Result<Self> + where + Self: Sized; + fn stack_peek(stack: &ValueStack) -> Result<Self> + where + Self: Sized; + fn local_get(locals: &Locals, index: LocalAddr) -> Result<Self> + where + Self: Sized; + fn local_set(locals: &mut Locals, index: LocalAddr, value: Self) -> Result<()>; +} + +macro_rules! impl_internalvalue { + ($( $variant:ident, $stack:ident, $locals:ident, $internal:ty, $outer:ty, $to_internal:expr, $to_outer:expr )*) => { + $( + impl sealed::Sealed for $outer {} + + impl From<$outer> for TinyWasmValue { + fn from(value: $outer) -> Self { + TinyWasmValue::$variant($to_internal(value)) + } + } + + impl InternalValue for $outer { + #[inline] + fn stack_push(stack: &mut ValueStack, value: Self) { + stack.$stack.push($to_internal(value)); + } + #[inline] + fn stack_pop(stack: &mut ValueStack) -> Result<Self> { + stack.$stack.pop().ok_or(Error::ValueStackUnderflow).map($to_outer) + } + #[inline] + fn stack_peek(stack: &ValueStack) -> Result<Self> { + stack.$stack.last().copied().ok_or(Error::ValueStackUnderflow).map($to_outer) + } + #[inline] + fn local_get(locals: &Locals, index: LocalAddr) -> Result<Self> { + Ok($to_outer(locals.$locals[index as usize])) + } + #[inline] + fn local_set(locals: &mut Locals, index: LocalAddr, value: Self) -> Result<()> { + locals.$locals[index as usize] = $to_internal(value); + Ok(()) + } + } + )* + }; +} + +impl_internalvalue! { + Value32, stack_32, locals_32, u32, u32, |v| v, |v| v + Value64, stack_64, locals_64, u64, u64, |v| v, |v| v + Value32, stack_32, locals_32, u32, i32, |v| v as u32, |v: u32| v as i32 + Value64, stack_64, locals_64, u64, i64, |v| v as u64, |v| v as i64 + Value32, stack_32, locals_32, u32, f32, f32::to_bits, f32::from_bits + Value64, stack_64, locals_64, u64, f64, f64::to_bits, f64::from_bits + Value128, stack_128, locals_128, Value128, Value128, |v| v, |v| v + ValueRef, stack_ref, locals_ref, ValueRef, ValueRef, |v| v, |v| v +} diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 9c48ee0..c1c2549 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -108,8 +108,8 @@ mod reference; mod store; /// Runtime for executing WebAssembly modules. -pub mod runtime; -pub use runtime::InterpreterRuntime; +pub mod interpreter; +pub use interpreter::InterpreterRuntime; #[cfg(feature = "parser")] /// Re-export of [`tinywasm_parser`]. Requires `parser` feature. diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs index 5b5f0c6..f6e8e04 100644 --- a/crates/tinywasm/src/module.rs +++ b/crates/tinywasm/src/module.rs @@ -1,24 +1,21 @@ use crate::{Imports, ModuleInstance, Result, Store}; use tinywasm_types::TinyWasmModule; -#[derive(Debug)] /// A WebAssembly Module /// /// See <https://webassembly.github.io/spec/core/syntax/modules.html#syntax-module> -#[derive(Clone)] -pub struct Module { - pub(crate) data: TinyWasmModule, -} +#[derive(Debug, Clone)] +pub struct Module(pub(crate) TinyWasmModule); impl From<&TinyWasmModule> for Module { fn from(data: &TinyWasmModule) -> Self { - Self { data: data.clone() } + Self(data.clone()) } } impl From<TinyWasmModule> for Module { fn from(data: TinyWasmModule) -> Self { - Self { data } + Self(data) } } diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index f3acc49..870de48 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -1,45 +1,40 @@ -use core::cell::{Ref, RefCell, RefMut}; +use core::cell::{Ref, RefMut}; use core::ffi::CStr; use alloc::ffi::CString; use alloc::string::{String, ToString}; use alloc::vec::Vec; -use crate::{GlobalInstance, MemoryInstance, Result}; -use tinywasm_types::WasmValue; +use crate::{MemoryInstance, Result}; // This module essentially contains the public APIs to interact with the data stored in the store /// A reference to a memory instance #[derive(Debug)] -pub struct MemoryRef<'a> { - pub(crate) instance: Ref<'a, MemoryInstance>, -} +pub struct MemoryRef<'a>(pub(crate) Ref<'a, MemoryInstance>); /// A borrowed reference to a memory instance #[derive(Debug)] -pub struct MemoryRefMut<'a> { - pub(crate) instance: RefMut<'a, MemoryInstance>, -} +pub struct MemoryRefMut<'a>(pub(crate) RefMut<'a, MemoryInstance>); impl<'a> MemoryRefLoad for MemoryRef<'a> { /// Load a slice of memory fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { - self.instance.load(offset, len) + self.0.load(offset, len) } } impl<'a> MemoryRefLoad for MemoryRefMut<'a> { /// Load a slice of memory fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { - self.instance.load(offset, len) + self.0.load(offset, len) } } impl MemoryRef<'_> { /// Load a slice of memory pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { - self.instance.load(offset, len) + self.0.load(offset, len) } /// Load a slice of memory as a vector @@ -51,7 +46,7 @@ impl MemoryRef<'_> { impl MemoryRefMut<'_> { /// Load a slice of memory pub fn load(&self, offset: usize, len: usize) -> Result<&[u8]> { - self.instance.load(offset, len) + self.0.load(offset, len) } /// Load a slice of memory as a vector @@ -61,27 +56,27 @@ impl MemoryRefMut<'_> { /// Grow the memory by the given number of pages pub fn grow(&mut self, delta_pages: i32) -> Option<i32> { - self.instance.grow(delta_pages) + self.0.grow(delta_pages) } /// Get the current size of the memory in pages pub fn page_count(&mut self) -> usize { - self.instance.page_count() + self.0.page_count() } /// Copy a slice of memory to another place in memory pub fn copy_within(&mut self, src: usize, dst: usize, len: usize) -> Result<()> { - self.instance.copy_within(src, dst, len) + self.0.copy_within(src, dst, len) } /// Fill a slice of memory with a value pub fn fill(&mut self, offset: usize, len: usize, val: u8) -> Result<()> { - self.instance.fill(offset, len, val) + self.0.fill(offset, len, val) } /// Store a slice of memory pub fn store(&mut self, offset: usize, len: usize, data: &[u8]) -> Result<()> { - self.instance.store(offset, len, data) + self.0.store(offset, len, data) } } @@ -139,21 +134,3 @@ pub trait MemoryStringExt: MemoryRefLoad { impl MemoryStringExt for MemoryRef<'_> {} impl MemoryStringExt for MemoryRefMut<'_> {} - -/// A reference to a global instance -#[derive(Debug)] -pub struct GlobalRef { - pub(crate) instance: RefCell<GlobalInstance>, -} - -impl GlobalRef { - /// Get the value of the global - pub fn get(&self) -> WasmValue { - self.instance.borrow().get() - } - - /// Set the value of the global - pub fn set(&self, val: WasmValue) -> Result<()> { - self.instance.borrow_mut().set(val) - } -} diff --git a/crates/tinywasm/src/runtime/stack/values.rs b/crates/tinywasm/src/runtime/stack/values.rs deleted file mode 100644 index 606e13a..0000000 --- a/crates/tinywasm/src/runtime/stack/values.rs +++ /dev/null @@ -1,420 +0,0 @@ -#![allow(missing_docs)] -use tinywasm_types::{ValType, WasmValue}; - -use crate::{Error, Result}; - -use super::{call_stack::Locals, ValueStack}; - -pub type Value32 = u32; -pub type Value64 = u64; -pub type Value128 = u128; -pub type ValueRef = Option<u32>; - -#[derive(Debug, Clone, Copy)] -pub(crate) struct StackLocation { - pub(crate) s32: u32, - pub(crate) s64: u32, - pub(crate) s128: u32, - pub(crate) sref: u32, -} - -#[derive(Debug, Clone, Copy, Default)] -pub(crate) struct StackHeight { - pub(crate) s32: u32, - pub(crate) s64: u32, - pub(crate) s128: u32, - pub(crate) sref: u32, -} - -impl From<ValType> for StackHeight { - fn from(value: ValType) -> Self { - match value { - ValType::I32 | ValType::F32 => Self { s32: 1, ..Default::default() }, - ValType::I64 | ValType::F64 => Self { s64: 1, ..Default::default() }, - ValType::V128 => Self { s128: 1, ..Default::default() }, - ValType::RefExtern | ValType::RefFunc => Self { sref: 1, ..Default::default() }, - } - } -} - -impl From<&[ValType]> for StackHeight { - fn from(value: &[ValType]) -> Self { - let mut s32 = 0; - let mut s64 = 0; - let mut s128 = 0; - let mut sref = 0; - for val_type in value.iter() { - match val_type { - ValType::I32 | ValType::F32 => s32 += 1, - ValType::I64 | ValType::F64 => s64 += 1, - ValType::V128 => s128 += 1, - ValType::RefExtern | ValType::RefFunc => sref += 1, - } - } - Self { s32, s64, s128, sref } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum TinyWasmValue { - Value32(Value32), - Value64(Value64), - Value128(Value128), - ValueRef(ValueRef), -} - -impl TinyWasmValue { - pub fn unwrap_32(&self) -> Value32 { - match self { - TinyWasmValue::Value32(v) => *v, - _ => unreachable!("Expected Value32"), - } - } - - pub fn unwrap_64(&self) -> Value64 { - match self { - TinyWasmValue::Value64(v) => *v, - _ => unreachable!("Expected Value64"), - } - } - - pub fn unwrap_128(&self) -> Value128 { - match self { - TinyWasmValue::Value128(v) => *v, - _ => unreachable!("Expected Value128"), - } - } - - pub fn unwrap_ref(&self) -> ValueRef { - match self { - TinyWasmValue::ValueRef(v) => *v, - _ => unreachable!("Expected ValueRef"), - } - } - - pub fn attach_type(&self, ty: ValType) -> WasmValue { - match ty { - ValType::I32 => WasmValue::I32(self.unwrap_32() as i32), - ValType::I64 => WasmValue::I64(self.unwrap_64() as i64), - ValType::F32 => WasmValue::F32(f32::from_bits(self.unwrap_32())), - ValType::F64 => WasmValue::F64(f64::from_bits(self.unwrap_64())), - ValType::V128 => WasmValue::V128(self.unwrap_128()), - ValType::RefExtern => match self.unwrap_ref() { - Some(v) => WasmValue::RefExtern(v), - None => WasmValue::RefNull(ValType::RefExtern), - }, - ValType::RefFunc => match self.unwrap_ref() { - Some(v) => WasmValue::RefFunc(v), - None => WasmValue::RefNull(ValType::RefFunc), - }, - } - } -} - -impl Default for TinyWasmValue { - fn default() -> Self { - TinyWasmValue::Value32(0) - } -} - -impl From<WasmValue> for TinyWasmValue { - fn from(value: WasmValue) -> Self { - match value { - WasmValue::I32(v) => TinyWasmValue::Value32(v as u32), - WasmValue::I64(v) => TinyWasmValue::Value64(v as u64), - WasmValue::V128(v) => TinyWasmValue::Value128(v), - WasmValue::F32(v) => TinyWasmValue::Value32(v.to_bits()), - WasmValue::F64(v) => TinyWasmValue::Value64(v.to_bits()), - WasmValue::RefFunc(v) => TinyWasmValue::ValueRef(Some(v)), - WasmValue::RefExtern(v) => TinyWasmValue::ValueRef(Some(v)), - WasmValue::RefNull(_) => TinyWasmValue::ValueRef(None), - } - } -} - -impl From<&WasmValue> for TinyWasmValue { - fn from(value: &WasmValue) -> Self { - match value { - WasmValue::I32(v) => TinyWasmValue::Value32(*v as u32), - WasmValue::I64(v) => TinyWasmValue::Value64(*v as u64), - WasmValue::V128(v) => TinyWasmValue::Value128(*v), - WasmValue::F32(v) => TinyWasmValue::Value32(v.to_bits()), - WasmValue::F64(v) => TinyWasmValue::Value64(v.to_bits()), - WasmValue::RefFunc(v) => TinyWasmValue::ValueRef(Some(*v)), - WasmValue::RefExtern(v) => TinyWasmValue::ValueRef(Some(*v)), - WasmValue::RefNull(_) => TinyWasmValue::ValueRef(None), - } - } -} - -impl From<f32> for TinyWasmValue { - fn from(value: f32) -> Self { - TinyWasmValue::Value32(value.to_bits()) - } -} - -impl From<f64> for TinyWasmValue { - fn from(value: f64) -> Self { - TinyWasmValue::Value64(value.to_bits()) - } -} - -impl From<i32> for TinyWasmValue { - fn from(value: i32) -> Self { - TinyWasmValue::Value32(value as u32) - } -} - -impl From<u32> for TinyWasmValue { - fn from(value: u32) -> Self { - TinyWasmValue::Value32(value) - } -} - -impl From<i64> for TinyWasmValue { - fn from(value: i64) -> Self { - TinyWasmValue::Value64(value as u64) - } -} - -impl From<u64> for TinyWasmValue { - fn from(value: u64) -> Self { - TinyWasmValue::Value64(value) - } -} - -impl From<Value128> for TinyWasmValue { - fn from(value: Value128) -> Self { - TinyWasmValue::Value128(value) - } -} - -impl From<ValueRef> for TinyWasmValue { - fn from(value: ValueRef) -> Self { - TinyWasmValue::ValueRef(value) - } -} - -// TODO: this can be made a bit more maintainable by using a macro - -mod sealed { - #[allow(unreachable_pub)] - pub trait Sealed {} -} - -impl sealed::Sealed for i32 {} -impl sealed::Sealed for f32 {} -impl sealed::Sealed for i64 {} -impl sealed::Sealed for u64 {} -impl sealed::Sealed for f64 {} -impl sealed::Sealed for u32 {} -impl sealed::Sealed for Value128 {} -impl sealed::Sealed for ValueRef {} - -pub(crate) trait InternalValue: sealed::Sealed { - fn stack_push(stack: &mut ValueStack, value: Self); - fn stack_pop(stack: &mut ValueStack) -> Result<Self> - where - Self: Sized; - fn stack_peek(stack: &ValueStack) -> Result<Self> - where - Self: Sized; - - fn local_get(locals: &Locals, index: u32) -> Result<Self> - where - Self: Sized; - - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()>; -} - -impl InternalValue for i32 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_32.push(value as u32); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_32.pop().ok_or(Error::ValueStackUnderflow).map(|v| v as i32) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_32.last().ok_or(Error::ValueStackUnderflow).map(|v| *v as i32) - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(locals.locals_32[index as usize] as i32) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_32[index as usize] = value as u32; - Ok(()) - } -} - -impl InternalValue for f32 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_32.push(value.to_bits()); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_32.pop().ok_or(Error::ValueStackUnderflow).map(f32::from_bits) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_32.last().ok_or(Error::ValueStackUnderflow).map(|v| f32::from_bits(*v)) - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(f32::from_bits(locals.locals_32[index as usize])) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_32[index as usize] = value.to_bits(); - Ok(()) - } -} - -impl InternalValue for i64 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_64.push(value as u64); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_64.pop().ok_or(Error::ValueStackUnderflow).map(|v| v as i64) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_64.last().ok_or(Error::ValueStackUnderflow).map(|v| *v as i64) - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(locals.locals_64[index as usize] as i64) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_64[index as usize] = value as u64; - Ok(()) - } -} - -impl InternalValue for u64 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_64.push(value); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_64.pop().ok_or(Error::ValueStackUnderflow) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_64.last().ok_or(Error::ValueStackUnderflow).copied() - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(locals.locals_64[index as usize]) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_64[index as usize] = value; - Ok(()) - } -} - -impl InternalValue for f64 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_64.push(value.to_bits()); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_64.pop().ok_or(Error::ValueStackUnderflow).map(f64::from_bits) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_64.last().ok_or(Error::ValueStackUnderflow).map(|v| f64::from_bits(*v)) - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(f64::from_bits(locals.locals_64[index as usize])) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_64[index as usize] = value.to_bits(); - Ok(()) - } -} - -impl InternalValue for u32 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_32.push(value); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_32.pop().ok_or(Error::ValueStackUnderflow) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_32.last().ok_or(Error::ValueStackUnderflow).copied() - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(locals.locals_32[index as usize]) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_32[index as usize] = value; - Ok(()) - } -} - -impl InternalValue for Value128 { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_128.push(value); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_128.pop().ok_or(Error::ValueStackUnderflow) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_128.last().ok_or(Error::ValueStackUnderflow).copied() - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(locals.locals_128[index as usize]) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_128[index as usize] = value; - Ok(()) - } -} - -impl InternalValue for ValueRef { - #[inline] - fn stack_push(stack: &mut ValueStack, value: Self) { - stack.stack_ref.push(value); - } - #[inline] - fn stack_pop(stack: &mut ValueStack) -> Result<Self> { - stack.stack_ref.pop().ok_or(Error::ValueStackUnderflow) - } - #[inline] - fn stack_peek(stack: &ValueStack) -> Result<Self> { - stack.stack_ref.last().ok_or(Error::ValueStackUnderflow).copied() - } - #[inline] - fn local_get(locals: &Locals, index: u32) -> Result<Self> { - Ok(locals.locals_ref[index as usize]) - } - #[inline] - fn local_set(locals: &mut Locals, index: u32, value: Self) -> Result<()> { - locals.locals_ref[index as usize] = value; - Ok(()) - } -} diff --git a/crates/tinywasm/src/store/element.rs b/crates/tinywasm/src/store/element.rs index 6da73c7..40301a5 100644 --- a/crates/tinywasm/src/store/element.rs +++ b/crates/tinywasm/src/store/element.rs @@ -9,7 +9,7 @@ use tinywasm_types::*; pub(crate) struct ElementInstance { pub(crate) kind: ElementKind, pub(crate) items: Option<Vec<TableElement>>, // none is the element was dropped - _owner: ModuleInstanceAddr, // index into store.module_instances + pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances } impl ElementInstance { diff --git a/crates/tinywasm/src/store/function.rs b/crates/tinywasm/src/store/function.rs index ef370c2..f3f1df0 100644 --- a/crates/tinywasm/src/store/function.rs +++ b/crates/tinywasm/src/store/function.rs @@ -8,11 +8,11 @@ use tinywasm_types::*; /// See <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances> pub(crate) struct FunctionInstance { pub(crate) func: Function, - pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions + pub(crate) _owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions } impl FunctionInstance { pub(crate) fn new_wasm(func: WasmFunction, owner: ModuleInstanceAddr) -> Self { - Self { func: Function::Wasm(Rc::new(func)), owner } + Self { func: Function::Wasm(Rc::new(func)), _owner: owner } } } diff --git a/crates/tinywasm/src/store/global.rs b/crates/tinywasm/src/store/global.rs index a18e43d..b7a47d6 100644 --- a/crates/tinywasm/src/store/global.rs +++ b/crates/tinywasm/src/store/global.rs @@ -1,10 +1,7 @@ +use crate::interpreter::TinyWasmValue; use core::cell::Cell; - -use alloc::{format, string::ToString}; use tinywasm_types::*; -use crate::{runtime::TinyWasmValue, unlikely, Error, Result}; - /// A WebAssembly Global Instance /// /// See <https://webassembly.github.io/spec/core/exec/runtime.html#global-instances> @@ -19,55 +16,4 @@ impl GlobalInstance { pub(crate) fn new(ty: GlobalType, value: TinyWasmValue, owner: ModuleInstanceAddr) -> Self { Self { ty, value: value.into(), _owner: owner } } - - #[inline] - pub(crate) fn get(&self) -> WasmValue { - self.value.get().attach_type(self.ty.ty) - } - - pub(crate) fn set(&mut self, val: WasmValue) -> Result<()> { - if unlikely(val.val_type() != self.ty.ty) { - return Err(Error::Other(format!( - "global type mismatch: expected {:?}, got {:?}", - self.ty.ty, - val.val_type() - ))); - } - - if unlikely(!self.ty.mutable) { - return Err(Error::Other("global is immutable".to_string())); - } - - self.value.set(val.into()); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_global_instance_get_set() { - let global_type = GlobalType { ty: ValType::I32, mutable: true }; - let initial_value = TinyWasmValue::from(10i32); - let owner = 0; - - let mut global_instance = GlobalInstance::new(global_type, initial_value, owner); - - // Test `get` - assert_eq!(global_instance.get(), WasmValue::I32(10), "global value should be 10"); - - // Test `set` with correct type - assert!(global_instance.set(WasmValue::I32(20)).is_ok(), "set should succeed"); - assert_eq!(global_instance.get(), WasmValue::I32(20), "global value should be 20"); - - // Test `set` with incorrect type - assert!(matches!(global_instance.set(WasmValue::F32(1.0)), Err(Error::Other(_))), "set should fail"); - - // Test `set` on immutable global - let immutable_global_type = GlobalType { ty: ValType::I32, mutable: false }; - let mut immutable_global_instance = GlobalInstance::new(immutable_global_type, initial_value, owner); - assert!(matches!(immutable_global_instance.set(WasmValue::I32(30)), Err(Error::Other(_)))); - } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 9ebd82e..b427497 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; use core::sync::atomic::{AtomicUsize, Ordering}; use tinywasm_types::*; -use crate::runtime::{self, InterpreterRuntime, TinyWasmValue}; +use crate::interpreter::{self, InterpreterRuntime, TinyWasmValue}; use crate::{Error, Function, ModuleInstance, Result, Trap}; mod data; @@ -57,7 +57,7 @@ impl Store { } /// Create a new store with the given runtime - pub(crate) fn runtime(&self) -> runtime::InterpreterRuntime { + pub(crate) fn runtime(&self) -> interpreter::InterpreterRuntime { match self.runtime { Runtime::Default => InterpreterRuntime::default(), } @@ -381,7 +381,7 @@ impl Store { } pub(crate) fn add_func(&mut self, func: Function, idx: ModuleInstanceAddr) -> Result<FuncAddr> { - self.data.funcs.push(FunctionInstance { func, owner: idx }); + self.data.funcs.push(FunctionInstance { func, _owner: idx }); Ok(self.data.funcs.len() as FuncAddr - 1) } diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index 52146e7..0a57f4c 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -57,11 +57,7 @@ impl TinyWasmModule { /// Creates a TinyWasmModule from a slice of bytes. pub fn from_twasm(wasm: &[u8]) -> Result<TinyWasmModule, TwasmError> { let len = validate_magic(wasm)?; - let root = check_archived_root::<Self>(&wasm[len..]).map_err(|_e| { - crate::log::error!("Invalid archive: {}", _e); - TwasmError::InvalidArchive - })?; - + let root = check_archived_root::<Self>(&wasm[len..]).map_err(|_e| TwasmError::InvalidArchive)?; Ok(root.deserialize(&mut rkyv::Infallible).unwrap()) } diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index d19326d..dd201ad 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -1,50 +1,6 @@ use super::{FuncAddr, GlobalAddr, LabelAddr, LocalAddr, TableAddr, TypeAddr, ValType}; use crate::{DataAddr, ElemAddr, MemAddr}; -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] -pub enum BlockArgs { - Empty, - Type(ValType), - FuncType(u32), -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] -/// A packed representation of BlockArgs -/// This is needed to keep the size of the Instruction enum small. -/// Sadly, using #[repr(u8)] on BlockArgs itself is not possible because of the FuncType variant. -pub struct BlockArgsPacked([u8; 5]); // Modifying this directly can cause runtime errors, but no UB - -impl From<BlockArgs> for BlockArgsPacked { - fn from(args: BlockArgs) -> Self { - let mut packed = [0; 5]; - match args { - BlockArgs::Empty => packed[0] = 0, - BlockArgs::Type(t) => { - packed[0] = 1; - packed[1] = t.to_byte(); - } - BlockArgs::FuncType(t) => { - packed[0] = 2; - packed[1..].copy_from_slice(&t.to_le_bytes()); - } - } - Self(packed) - } -} - -impl From<BlockArgsPacked> for BlockArgs { - fn from(packed: BlockArgsPacked) -> Self { - match packed.0[0] { - 0 => BlockArgs::Empty, - 1 => BlockArgs::Type(ValType::from_byte(packed.0[1]).unwrap()), - 2 => BlockArgs::FuncType(u32::from_le_bytes(packed.0[1..].try_into().unwrap())), - _ => unreachable!(), - } - } -} - /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] @@ -106,9 +62,19 @@ pub enum Instruction { // See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions> Unreachable, Nop, - Block(BlockArgs, EndOffset), - Loop(BlockArgs, EndOffset), - If(BlockArgsPacked, ElseOffset, EndOffset), // If else offset is 0 if there is no else block + + Block(EndOffset), + BlockWithType(ValType, EndOffset), + BlockWithFuncType(TypeAddr, EndOffset), + + Loop(EndOffset), + LoopWithType(ValType, EndOffset), + LoopWithFuncType(TypeAddr, EndOffset), + + If(ElseOffset, EndOffset), + IfWithType(ValType, ElseOffset, EndOffset), + IfWithFuncType(TypeAddr, ElseOffset, EndOffset), + Else(EndOffset), EndBlockFrame, Br(LabelAddr), @@ -233,44 +199,3 @@ pub enum Instruction { DataDrop(DataAddr), ElemDrop(ElemAddr), } - -#[cfg(test)] -mod test_blockargs_packed { - use super::*; - - #[test] - fn test_empty() { - let packed: BlockArgsPacked = BlockArgs::Empty.into(); - assert_eq!(BlockArgs::from(packed), BlockArgs::Empty); - } - - #[test] - fn test_val_type_i32() { - let packed: BlockArgsPacked = BlockArgs::Type(ValType::I32).into(); - assert_eq!(BlockArgs::from(packed), BlockArgs::Type(ValType::I32)); - } - - #[test] - fn test_val_type_i64() { - let packed: BlockArgsPacked = BlockArgs::Type(ValType::I64).into(); - assert_eq!(BlockArgs::from(packed), BlockArgs::Type(ValType::I64)); - } - - #[test] - fn test_val_type_f32() { - let packed: BlockArgsPacked = BlockArgs::Type(ValType::F32).into(); - assert_eq!(BlockArgs::from(packed), BlockArgs::Type(ValType::F32)); - } - - #[test] - fn test_val_type_f64() { - let packed: BlockArgsPacked = BlockArgs::Type(ValType::F64).into(); - assert_eq!(BlockArgs::from(packed), BlockArgs::Type(ValType::F64)); - } - - #[test] - fn test_func_type() { - let packed: BlockArgsPacked = BlockArgs::FuncType(0x12345678).into(); - assert_eq!(BlockArgs::from(packed), BlockArgs::FuncType(0x12345678)); - } -} diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 785b13b..cdee7e6 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -14,7 +14,7 @@ use core::{fmt::Debug, ops::Range}; // log for logging (optional). #[cfg(feature = "logging")] -#[allow(clippy::single_component_path_imports)] +#[allow(clippy::single_component_path_imports, unused)] use log; #[cfg(not(feature = "logging"))] @@ -125,7 +125,7 @@ pub type ExternAddr = Addr; // additional internal addresses pub type TypeAddr = Addr; -pub type LocalAddr = Addr; +pub type LocalAddr = u16; // there can't be more than 50.000 locals in a function pub type LabelAddr = Addr; pub type ModuleInstanceAddr = Addr; diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 6c422d0..c418338 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -146,31 +146,6 @@ impl ValType { pub fn is_simd(&self) -> bool { matches!(self, ValType::V128) } - - pub(crate) fn to_byte(self) -> u8 { - match self { - ValType::I32 => 0x7F, - ValType::I64 => 0x7E, - ValType::F32 => 0x7D, - ValType::F64 => 0x7C, - ValType::V128 => 0x7B, - ValType::RefFunc => 0x70, - ValType::RefExtern => 0x6F, - } - } - - pub(crate) fn from_byte(byte: u8) -> Option<Self> { - match byte { - 0x7F => Some(ValType::I32), - 0x7E => Some(ValType::I64), - 0x7D => Some(ValType::F32), - 0x7C => Some(ValType::F64), - 0x7B => Some(ValType::V128), - 0x70 => Some(ValType::RefFunc), - 0x6F => Some(ValType::RefExtern), - _ => None, - } - } } macro_rules! impl_conversion_for_wasmvalue { @@ -202,9 +177,4 @@ macro_rules! impl_conversion_for_wasmvalue { } } -impl_conversion_for_wasmvalue! { - i32 => I32, - i64 => I64, - f32 => F32, - f64 => F64 -} +impl_conversion_for_wasmvalue! { i32 => I32, i64 => I64, f32 => F32, f64 => F64, u128 => V128 } |
