diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | crates/parser/src/module.rs | 32 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 6 | ||||
| -rw-r--r-- | crates/tinywasm/src/func.rs | 4 | ||||
| -rw-r--r-- | crates/tinywasm/src/imports.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/instance.rs | 25 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 92 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/mod.rs | 6 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/block_stack.rs | 6 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/call_stack.rs | 62 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/stack/mod.rs | 5 | ||||
| -rw-r--r-- | crates/tinywasm/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/module.rs | 6 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 24 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 4 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 71 |
16 files changed, 189 insertions, 159 deletions
@@ -9,3 +9,4 @@ flamegraph.svg /.idea /*.iml profile.json +profile.json.gz diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs index 4754d31..d498143 100644 --- a/crates/parser/src/module.rs +++ b/crates/parser/src/module.rs @@ -1,14 +1,15 @@ use crate::log::debug; use crate::{ParseError, Result, conversion}; use alloc::string::ToString; -use alloc::{boxed::Box, format, vec::Vec}; +use alloc::sync::Arc; +use alloc::{format, vec::Vec}; use tinywasm_types::{ - Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, TinyWasmModule, ValueCounts, - ValueCountsSmall, WasmFunction, WasmFunctionData, + ArcSlice, Data, Element, Export, FuncType, Global, Import, Instruction, MemoryType, TableType, TinyWasmModule, + ValueCounts, ValueCountsSmall, WasmFunction, WasmFunctionData, }; use wasmparser::{FuncValidatorAllocations, Payload, Validator}; -pub(crate) type Code = (Box<[Instruction]>, WasmFunctionData, ValueCounts); +pub(crate) type Code = (Arc<[Instruction]>, WasmFunctionData, ValueCounts); #[derive(Default)] pub(crate) struct ModuleReader { @@ -195,25 +196,24 @@ impl ModuleReader { .map(|((instructions, data, locals), ty_idx)| { let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone(); let params = ValueCountsSmall::from(&ty.params); - WasmFunction { instructions, data, locals, params, ty } + WasmFunction { instructions: ArcSlice(instructions), data, locals, params, ty } }) - .collect::<Vec<_>>() - .into_boxed_slice(); + .collect::<Vec<_>>(); let globals = self.globals; let table_types = self.table_types; Ok(TinyWasmModule { - funcs, - func_types: self.func_types.into_boxed_slice(), - globals: globals.into_boxed_slice(), - table_types: table_types.into_boxed_slice(), - imports: self.imports.into_boxed_slice(), + funcs: funcs.into(), + func_types: self.func_types.into(), + globals: globals.into(), + table_types: table_types.into(), + imports: self.imports.into(), start_func: self.start_func, - data: self.data.into_boxed_slice(), - exports: self.exports.into_boxed_slice(), - elements: self.elements.into_boxed_slice(), - memory_types: self.memory_types.into_boxed_slice(), + data: self.data.into(), + exports: self.exports.into(), + elements: self.elements.into(), + memory_types: self.memory_types.into(), }) } } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 9b9ab75..8212763 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -2,7 +2,7 @@ use crate::Result; use crate::conversion::{convert_heaptype, convert_valtype}; use alloc::string::ToString; -use alloc::{boxed::Box, vec::Vec}; +use alloc::vec::Vec; use tinywasm_types::{Instruction, MemoryArg, WasmFunctionData}; use wasmparser::{ FuncValidator, FuncValidatorAllocations, FunctionBody, VisitOperator, VisitSimdOperator, WasmModuleResources, @@ -37,7 +37,7 @@ pub(crate) fn process_operators_and_validate<R: WasmModuleResources>( validator: FuncValidator<R>, body: FunctionBody<'_>, local_addr_map: Vec<u32>, -) -> Result<(Box<[Instruction]>, WasmFunctionData, FuncValidatorAllocations)> { +) -> Result<(alloc::sync::Arc<[Instruction]>, WasmFunctionData, FuncValidatorAllocations)> { let mut reader = body.get_operators_reader()?; let remaining = reader.get_binary_reader().bytes_remaining(); let mut builder = FunctionBuilder::new(remaining, validator, local_addr_map); @@ -52,7 +52,7 @@ pub(crate) fn process_operators_and_validate<R: WasmModuleResources>( } Ok(( - builder.instructions.into_boxed_slice(), + alloc::sync::Arc::from(builder.instructions), WasmFunctionData { v128_constants: builder.v128_constants.into_boxed_slice() }, builder.validator.into_allocations(), )) diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 5d5b56b..3a3985b 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -60,10 +60,10 @@ impl FuncHandle { // 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) - store.stack.initialize(callframe); + store.stack.clear(); // 9. Invoke the function instance - InterpreterRuntime::exec(store)?; + InterpreterRuntime::exec(store, callframe)?; // Once the function returns: // 1. Assert: m values are on the top of the stack (Ensured by validation) diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index aa05ae9..dcd5e12 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -363,7 +363,7 @@ impl Imports { ) -> Result<ResolvedImports> { let mut imports = ResolvedImports::new(); - for import in &module.0.imports { + for import in &*module.0.imports { let val = self.take(store, import).ok_or_else(|| LinkingError::unknown_import(import))?; match val { diff --git a/crates/tinywasm/src/instance.rs b/crates/tinywasm/src/instance.rs index 02ef531..7eddb10 100644 --- a/crates/tinywasm/src/instance.rs +++ b/crates/tinywasm/src/instance.rs @@ -1,4 +1,5 @@ -use alloc::{boxed::Box, format, rc::Rc}; +use alloc::boxed::Box; +use alloc::{format, rc::Rc}; use tinywasm_types::*; use crate::func::{FromWasmValueTuple, IntoWasmValueTuple}; @@ -20,7 +21,7 @@ pub(crate) struct ModuleInstanceInner { pub(crate) store_id: usize, pub(crate) idx: ModuleInstanceAddr, - pub(crate) types: Box<[FuncType]>, + pub(crate) types: ArcSlice<FuncType>, pub(crate) func_addrs: Box<[FuncAddr]>, pub(crate) table_addrs: Box<[TableAddr]>, @@ -30,8 +31,8 @@ pub(crate) struct ModuleInstanceInner { pub(crate) data_addrs: Box<[DataAddr]>, pub(crate) func_start: Option<FuncAddr>, - pub(crate) imports: Box<[Import]>, - pub(crate) exports: Box<[Export]>, + pub(crate) imports: ArcSlice<Import>, + pub(crate) exports: ArcSlice<Export>, } impl ModuleInstance { @@ -65,20 +66,20 @@ impl ModuleInstance { let idx = store.next_module_instance_idx(); let mut addrs = imports.unwrap_or_default().link(store, &module, 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)?); + addrs.funcs.extend(store.init_funcs(&module.0.funcs, idx)?); + addrs.tables.extend(store.init_tables(&module.0.table_types, idx)?); + addrs.memories.extend(store.init_memories(&module.0.memory_types, idx)?); - let global_addrs = store.init_globals(addrs.globals, module.0.globals.into(), &addrs.funcs, idx)?; + let global_addrs = store.init_globals(addrs.globals, &module.0.globals, &addrs.funcs, idx)?; let (elem_addrs, elem_trapped) = store.init_elements(&addrs.tables, &addrs.funcs, &global_addrs, &module.0.elements, idx)?; - let (data_addrs, data_trapped) = store.init_data(&addrs.memories, module.0.data.into(), idx)?; + let (data_addrs, data_trapped) = store.init_data(&addrs.memories, &module.0.data, idx)?; let instance = ModuleInstanceInner { failed_to_instantiate: elem_trapped.is_some() || data_trapped.is_some(), store_id: store.id(), idx, - types: module.0.func_types, + types: module.0.func_types.clone(), func_addrs: addrs.funcs.into_boxed_slice(), table_addrs: addrs.tables.into_boxed_slice(), mem_addrs: addrs.memories.into_boxed_slice(), @@ -86,8 +87,8 @@ impl ModuleInstance { elem_addrs, data_addrs, func_start: module.0.start_func, - imports: module.0.imports, - exports: module.0.exports, + imports: module.0.imports.clone(), + exports: module.0.exports.clone(), }; let instance = Self::new(instance); diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index dbeceb9..8d2e211 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -22,10 +22,9 @@ pub(crate) struct Executor<'store> { } impl<'store> Executor<'store> { - pub(crate) fn new(store: &'store mut Store) -> Result<Self> { - let current_frame = store.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, store }) + pub(crate) fn new(store: &'store mut Store, cf: CallFrame) -> Result<Self> { + let module = store.get_module_instance_raw(cf.module_addr()); + Ok(Self { module, store, cf }) } pub(crate) fn run_to_completion(&mut self) -> Result<()> { @@ -104,22 +103,22 @@ impl<'store> Executor<'store> { BrTable(default, len) => return self.exec_brtable(*default, *len), Return => return self.exec_return(), 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), - 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), - 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), - 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), + LocalGet32(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value32>(*local_index)), + LocalGet64(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value64>(*local_index)), + LocalGet128(local_index) => self.store.stack.values.push(self.cf.locals.get::<Value128>(*local_index)), + LocalGetRef(local_index) => self.store.stack.values.push(self.cf.locals.get::<ValueRef>(*local_index)), + LocalSet32(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<Value32>()), + LocalSet64(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<Value64>()), + LocalSet128(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<Value128>()), + LocalSetRef(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.pop::<ValueRef>()), + LocalCopy32(from, to) => self.cf.locals.set(*to, self.cf.locals.get::<Value32>(*from)), + LocalCopy64(from, to) => self.cf.locals.set(*to, self.cf.locals.get::<Value64>(*from)), + LocalCopy128(from, to) => self.cf.locals.set(*to, self.cf.locals.get::<Value128>(*from)), + LocalCopyRef(from, to) => self.cf.locals.set(*to, self.cf.locals.get::<ValueRef>(*from)), + LocalTee32(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<Value32>()), + LocalTee64(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<Value64>()), + LocalTee128(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<Value128>()), + LocalTeeRef(local_index) => self.cf.locals.set(*local_index, self.store.stack.values.peek::<ValueRef>()), GlobalGet(global_index) => self.exec_global_get(*global_index), GlobalSet32(global_index) => self.exec_global_set::<Value32>(*global_index), GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index), @@ -567,11 +566,11 @@ impl<'store> Executor<'store> { wasm_func: Rc<WasmFunction>, owner: ModuleInstanceAddr, ) -> ControlFlow<Option<Error>> { - let locals = self.store.stack.values.pop_locals(wasm_func.params, wasm_func.locals); - if IS_RETURN_CALL { + let locals = self.store.stack.values.pop_locals(wasm_func.params, wasm_func.locals); self.cf.reuse_for(wasm_func, locals, self.store.stack.blocks.len() as u32, owner); } else { + let locals = self.store.stack.values.pop_locals(wasm_func.params, wasm_func.locals); let new_call_frame = CallFrame::new_raw(wasm_func, owner, locals, self.store.stack.blocks.len() as u32); self.cf.incr_instr_ptr(); // skip the call instruction self.store.stack.call_stack.push(core::mem::replace(&mut self.cf, new_call_frame))?; @@ -580,7 +579,7 @@ impl<'store> Executor<'store> { self.module.swap_with(self.cf.module_addr(), self.store); ControlFlow::Continue(()) } - fn exec_call_host(&mut self, host_func: &Rc<imports::HostFunction>) -> ControlFlow<Option<Error>> { + fn exec_call_host(&mut self, host_func: Rc<imports::HostFunction>) -> ControlFlow<Option<Error>> { let params = self.store.stack.values.pop_types(&host_func.ty.params).collect::<Box<_>>(); let res = host_func.call(FuncContext { store: self.store, module_addr: self.module.id() }, ¶ms).to_cf()?; self.store.stack.values.extend_from_wasmvalues(&res); @@ -589,9 +588,9 @@ impl<'store> Executor<'store> { } fn exec_call_direct<const IS_RETURN_CALL: bool>(&mut self, v: u32) -> ControlFlow<Option<Error>> { let func_inst = self.store.state.get_func(self.module.resolve_func_addr(v)); - match func_inst.func.clone() { - crate::Function::Wasm(wasm_func) => self.exec_call::<IS_RETURN_CALL>(wasm_func, func_inst.owner), - crate::Function::Host(host_func) => self.exec_call_host(&host_func), + match &func_inst.func { + crate::Function::Wasm(wasm_func) => self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), func_inst.owner), + crate::Function::Host(host_func) => self.exec_call_host(host_func.clone()), } } fn exec_call_indirect<const IS_RETURN_CALL: bool>( @@ -612,26 +611,26 @@ impl<'store> Executor<'store> { let func_inst = self.store.state.get_func(func_ref); let call_ty = self.module.func_ty(type_addr); - match func_inst.func.clone() { + match &func_inst.func { crate::Function::Wasm(wasm_func) => { - if unlikely(wasm_func.ty != *call_ty) { + if wasm_func.ty != *call_ty { return ControlFlow::Break(Some( Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() } .into(), )); } - self.exec_call::<IS_RETURN_CALL>(wasm_func, func_inst.owner) + self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), func_inst.owner) } crate::Function::Host(host_func) => { - if unlikely(host_func.ty != *call_ty) { + if host_func.ty != *call_ty { return ControlFlow::Break(Some( Trap::IndirectCallTypeMismatch { actual: host_func.ty.clone(), expected: call_ty.clone() } .into(), )); } - self.exec_call_host(&host_func) + self.exec_call_host(host_func.clone()) } } } @@ -645,16 +644,16 @@ impl<'store> Executor<'store> { // falsy value is on the top of the stack if else_offset == 0 { - self.cf.jump(end_offset as usize); + self.cf.jump(end_offset); return; } - self.cf.jump(else_offset as usize); + self.cf.jump(else_offset); self.enter_block(end_offset - else_offset, BlockType::Else, (params, results)); } fn exec_else(&mut self, end_offset: u32) { self.exec_end_block(); - self.cf.jump(end_offset as usize); + self.cf.jump(end_offset); } fn resolve_functype(&self, idx: u32) -> (StackHeight, StackHeight) { let ty = self.module.func_ty(idx); @@ -662,7 +661,7 @@ impl<'store> Executor<'store> { } fn enter_block(&mut self, end_instr_offset: u32, ty: BlockType, (params, results): (StackHeight, StackHeight)) { self.store.stack.blocks.push(BlockFrame { - instr_ptr: self.cf.instr_ptr(), + instr_ptr: self.cf.instr_ptr() as u32, end_instr_offset, stack_ptr: self.store.stack.values.height(), results, @@ -730,18 +729,6 @@ impl<'store> Executor<'store> { let block = self.store.stack.blocks.pop(); self.store.stack.values.truncate_keep(block.stack_ptr, block.results); } - fn exec_local_get<T: InternalValue>(&mut self, local_index: u16) { - let v = self.cf.locals.get::<T>(local_index); - self.store.stack.values.push(v); - } - fn exec_local_set<T: InternalValue>(&mut self, local_index: u16) { - let v = self.store.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.store.stack.values.peek::<T>(); - self.cf.locals.set(local_index, v); - } fn exec_global_get(&mut self, global_index: u32) { self.store @@ -830,7 +817,7 @@ impl<'store> Executor<'store> { let data_len = data.data.as_ref().map_or(0, |d| d.len()); - if unlikely(((size + offset) as usize > data_len) || ((dst + size) as usize > mem.len())) { + if ((size + offset) as usize > data_len) || ((dst + size) as usize > mem.len()) { return Err(Trap::MemoryOutOfBounds { offset: offset as usize, len: size as usize, max: data_len }.into()); } @@ -999,7 +986,7 @@ impl<'store> Executor<'store> { let elem_len = elem.items.as_ref().map_or(0, alloc::vec::Vec::len); let table_len = table.size(); - if unlikely(size < 0 || ((size + offset) as usize > elem_len) || ((dst + size) > table_len)) { + if size < 0 || ((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()); } @@ -1038,7 +1025,7 @@ impl<'store> Executor<'store> { let val = self.store.stack.values.pop::<ValueRef>(); let i = self.store.stack.values.pop::<i32>(); - if unlikely(i + n > table.size()) { + if i + n > table.size() { return Err(Error::Trap(Trap::TableOutOfBounds { offset: i as usize, len: n as usize, @@ -1052,9 +1039,4 @@ impl<'store> Executor<'store> { 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/mod.rs b/crates/tinywasm/src/interpreter/mod.rs index 6f296ce..1a9a0cc 100644 --- a/crates/tinywasm/src/interpreter/mod.rs +++ b/crates/tinywasm/src/interpreter/mod.rs @@ -7,7 +7,7 @@ pub(crate) mod values; #[cfg(not(feature = "std"))] mod no_std_floats; -use crate::{Result, Store}; +use crate::{Result, Store, interpreter::stack::CallFrame}; pub(crate) use value128::*; pub(crate) use values::*; @@ -18,7 +18,7 @@ pub(crate) use values::*; pub(crate) struct InterpreterRuntime; impl InterpreterRuntime { - pub(crate) fn exec(store: &mut Store) -> Result<()> { - executor::Executor::new(store)?.run_to_completion() + pub(crate) fn exec(store: &mut Store, cf: CallFrame) -> Result<()> { + executor::Executor::new(store, cf)?.run_to_completion() } } diff --git a/crates/tinywasm/src/interpreter/stack/block_stack.rs b/crates/tinywasm/src/interpreter/stack/block_stack.rs index b35ad94..e4d4c28 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::{engine::Config, unlikely}; +use crate::engine::Config; use alloc::vec::Vec; use crate::interpreter::values::{StackHeight, StackLocation}; @@ -31,7 +31,7 @@ impl BlockStack { let len = (self.0.len() as u32) - offset; // the vast majority of wasm functions don't use break to return - if unlikely(index >= len) { + if index >= len { return None; } @@ -52,7 +52,7 @@ impl BlockStack { #[derive(Debug)] pub(crate) struct BlockFrame { - pub(crate) instr_ptr: usize, // position of the instruction pointer when the block was entered + pub(crate) instr_ptr: u32, // position of the instruction pointer when the block was entered pub(crate) end_instr_offset: u32, // position of the end instruction of the block pub(crate) stack_ptr: StackLocation, // stack pointer when the block was entered diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index ba74d3a..4b0fad6 100644 --- a/crates/tinywasm/src/interpreter/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs @@ -7,7 +7,7 @@ use crate::{Error, unlikely}; use alloc::boxed::Box; use alloc::{rc::Rc, vec::Vec}; -use tinywasm_types::{Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction, WasmFunctionData, WasmValue}; +use tinywasm_types::{ArcSlice, Instruction, LocalAddr, ModuleInstanceAddr, WasmFunction, WasmFunctionData, WasmValue}; pub(crate) const MAX_CALL_STACK_SIZE: usize = 1024; @@ -22,9 +22,8 @@ impl CallStack { Self { stack: Vec::with_capacity(config.call_stack_init_size) } } - pub(crate) fn reset(&mut self, call_frame: CallFrame) { + pub(crate) fn clear(&mut self) { self.stack.clear(); - self.stack.push(call_frame); } #[inline] @@ -81,14 +80,14 @@ impl CallFrame { &self.func_instance.data } - #[inline] + #[inline(always)] pub(crate) fn incr_instr_ptr(&mut self) { self.instr_ptr += 1; } #[inline] - pub(crate) fn jump(&mut self, offset: usize) { - self.instr_ptr += offset; + pub(crate) fn jump(&mut self, offset: u32) { + self.instr_ptr += offset as usize; } #[inline] @@ -96,19 +95,20 @@ impl CallFrame { self.module_addr } + #[inline(always)] + pub(crate) fn fetch_instr(&self) -> &Instruction { + self + .func_instance + .instructions + .get(self.instr_ptr) + .unwrap_or_else(|| unreachable!("Instruction pointer out of bounds, this is a bug")) + } + #[inline] pub(crate) fn block_ptr(&self) -> u32 { self.block_ptr } - #[inline(always)] - pub(crate) fn fetch_instr(&self) -> &Instruction { - match self.func_instance.instructions.get(self.instr_ptr) { - Some(instr) => instr, - None => unreachable!("Instruction out of bounds, this is a bug"), - } - } - pub(crate) fn reuse_for( &mut self, func: Rc<WasmFunction>, @@ -139,7 +139,7 @@ impl CallFrame { match break_to.ty { BlockType::Loop => { // this is a loop, so we want to jump back to the start of the loop - self.instr_ptr = break_to.instr_ptr; + self.instr_ptr = break_to.instr_ptr as usize; // We also want to push the params to the stack values.truncate_keep(break_to.stack_ptr, break_to.params); @@ -158,7 +158,7 @@ impl CallFrame { values.truncate_keep(break_to.stack_ptr, break_to.results); // (the inst_ptr will be incremented by 1 before the next instruction is executed) - self.instr_ptr = break_to.instr_ptr + break_to.end_instr_offset as usize; + self.instr_ptr = (break_to.instr_ptr + break_to.end_instr_offset) as usize; // we also want to trim the label stack, including the block blocks.truncate(blocks.len() as u32 - (break_to_relative + 1)); @@ -170,16 +170,16 @@ impl CallFrame { #[inline] pub(crate) fn new( - wasm_func_inst: Rc<WasmFunction>, - owner: ModuleInstanceAddr, + func_instance: Rc<WasmFunction>, + module_addr: ModuleInstanceAddr, params: &[WasmValue], block_ptr: u32, ) -> Self { let locals = { - let mut locals_32 = Vec::with_capacity(wasm_func_inst.locals.c32 as usize); - let mut locals_64 = Vec::with_capacity(wasm_func_inst.locals.c64 as usize); - let mut locals_128 = Vec::with_capacity(wasm_func_inst.locals.c128 as usize); - let mut locals_ref = Vec::with_capacity(wasm_func_inst.locals.cref as usize); + let mut locals_32 = Vec::with_capacity(func_instance.locals.c32 as usize); + let mut locals_64 = Vec::with_capacity(func_instance.locals.c64 as usize); + let mut locals_128 = Vec::with_capacity(func_instance.locals.c128 as usize); + let mut locals_ref = Vec::with_capacity(func_instance.locals.cref as usize); for p in params { match p.into() { @@ -190,10 +190,10 @@ impl CallFrame { } } - 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_32.resize_with(func_instance.locals.c32 as usize, Default::default); + locals_64.resize_with(func_instance.locals.c64 as usize, Default::default); + locals_128.resize_with(func_instance.locals.c128 as usize, Default::default); + locals_ref.resize_with(func_instance.locals.cref as usize, Default::default); Locals { locals_32: locals_32.into_boxed_slice(), @@ -203,21 +203,21 @@ impl CallFrame { } }; - Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, block_ptr, locals } + Self { instr_ptr: 0, func_instance, module_addr, block_ptr, locals } } #[inline] pub(crate) fn new_raw( - wasm_func_inst: Rc<WasmFunction>, - owner: ModuleInstanceAddr, + func_instance: Rc<WasmFunction>, + module_addr: ModuleInstanceAddr, locals: Locals, block_ptr: u32, ) -> Self { - Self { instr_ptr: 0, func_instance: wasm_func_inst, module_addr: owner, block_ptr, locals } + Self { instr_ptr: 0, func_instance, module_addr, block_ptr, locals } } #[inline] - pub(crate) fn instructions(&self) -> &[Instruction] { + pub(crate) fn instructions(&self) -> &ArcSlice<Instruction> { &self.func_instance.instructions } } diff --git a/crates/tinywasm/src/interpreter/stack/mod.rs b/crates/tinywasm/src/interpreter/stack/mod.rs index 0635bae..68b9164 100644 --- a/crates/tinywasm/src/interpreter/stack/mod.rs +++ b/crates/tinywasm/src/interpreter/stack/mod.rs @@ -21,10 +21,9 @@ impl Stack { Self { values: ValueStack::new(config), blocks: BlockStack::new(config), call_stack: CallStack::new(config) } } - /// Initialize the stack with the given call frame (used for starting execution) - pub(crate) fn initialize(&mut self, callframe: CallFrame) { + pub(crate) fn clear(&mut self) { self.values.clear(); self.blocks.clear(); - self.call_stack.reset(callframe); + self.call_stack.clear(); } } diff --git a/crates/tinywasm/src/lib.rs b/crates/tinywasm/src/lib.rs index 07748b9..31d52f7 100644 --- a/crates/tinywasm/src/lib.rs +++ b/crates/tinywasm/src/lib.rs @@ -4,7 +4,7 @@ attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_assignments, unused_variables)) ))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms, unreachable_pub)] -#![forbid(unsafe_code)] +#![deny(unsafe_code)] //! A tiny WebAssembly Runtime written in Rust //! diff --git a/crates/tinywasm/src/module.rs b/crates/tinywasm/src/module.rs index 26cea9c..e0685eb 100644 --- a/crates/tinywasm/src/module.rs +++ b/crates/tinywasm/src/module.rs @@ -5,17 +5,17 @@ use tinywasm_types::TinyWasmModule; /// /// See <https://webassembly.github.io/spec/core/syntax/modules.html#syntax-module> #[derive(Debug, Clone)] -pub struct Module(pub(crate) TinyWasmModule); +pub struct Module(pub(crate) alloc::sync::Arc<TinyWasmModule>); impl From<&TinyWasmModule> for Module { fn from(data: &TinyWasmModule) -> Self { - Self(data.clone()) + Self(alloc::sync::Arc::new(data.clone())) } } impl From<TinyWasmModule> for Module { fn from(data: TinyWasmModule) -> Self { - Self(data) + Self(alloc::sync::Arc::new(data)) } } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index 70a2b01..757cbb2 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -220,33 +220,33 @@ impl Store { // Linking related functions impl Store { /// Add functions to the store, returning their addresses in the store - pub(crate) fn init_funcs(&mut self, funcs: Vec<WasmFunction>, idx: ModuleInstanceAddr) -> Result<Vec<FuncAddr>> { + pub(crate) fn init_funcs(&mut self, funcs: &[WasmFunction], idx: ModuleInstanceAddr) -> Result<Vec<FuncAddr>> { let func_count = self.state.funcs.len(); let mut func_addrs = Vec::with_capacity(func_count); - for (i, func) in funcs.into_iter().enumerate() { - self.state.funcs.push(FunctionInstance::new_wasm(func, idx)); + for (i, func) in funcs.iter().enumerate() { + self.state.funcs.push(FunctionInstance::new_wasm(func.clone(), idx)); func_addrs.push((i + func_count) as FuncAddr); } Ok(func_addrs) } /// Add tables to the store, returning their addresses in the store - pub(crate) fn init_tables(&mut self, tables: Vec<TableType>, idx: ModuleInstanceAddr) -> Result<Vec<TableAddr>> { + pub(crate) fn init_tables(&mut self, tables: &[TableType], idx: ModuleInstanceAddr) -> Result<Vec<TableAddr>> { let table_count = self.state.tables.len(); let mut table_addrs = Vec::with_capacity(table_count); - for (i, table) in tables.into_iter().enumerate() { - self.state.tables.push(TableInstance::new(table, idx)); + for (i, table) in tables.iter().enumerate() { + self.state.tables.push(TableInstance::new(table.clone(), idx)); table_addrs.push((i + table_count) as TableAddr); } Ok(table_addrs) } /// Add memories to the store, returning their addresses in the store - pub(crate) fn init_memories(&mut self, memories: Vec<MemoryType>, idx: ModuleInstanceAddr) -> Result<Vec<MemAddr>> { + pub(crate) fn init_memories(&mut self, memories: &[MemoryType], idx: ModuleInstanceAddr) -> Result<Vec<MemAddr>> { let mem_count = self.state.memories.len(); let mut mem_addrs = Vec::with_capacity(mem_count); - for (i, mem) in memories.into_iter().enumerate() { - self.state.memories.push(MemoryInstance::new(mem, idx)); + for (i, mem) in memories.iter().enumerate() { + self.state.memories.push(MemoryInstance::new(*mem, idx)); mem_addrs.push((i + mem_count) as MemAddr); } Ok(mem_addrs) @@ -256,7 +256,7 @@ impl Store { pub(crate) fn init_globals( &mut self, mut imported_globals: Vec<GlobalAddr>, - new_globals: Vec<Global>, + new_globals: &[Global], func_addrs: &[FuncAddr], idx: ModuleInstanceAddr, ) -> Result<Vec<Addr>> { @@ -363,12 +363,12 @@ impl Store { pub(crate) fn init_data( &mut self, mem_addrs: &[MemAddr], - data: Vec<Data>, + data: &[Data], idx: ModuleInstanceAddr, ) -> Result<(Box<[Addr]>, Option<Trap>)> { let data_count = self.state.data.len(); let mut data_addrs = Vec::with_capacity(data_count); - for (i, data) in data.into_iter().enumerate() { + for (i, data) in data.iter().enumerate() { let data_val = match data.kind { tinywasm_types::DataKind::Active { mem: mem_addr, offset } => { let Some(mem_addr) = mem_addrs.get(mem_addr as usize) else { diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index aa57dfa..ee99601 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -68,7 +68,7 @@ pub enum Instruction { Block(EndOffset), BlockWithType(ValType, EndOffset), BlockWithFuncType(TypeAddr, EndOffset), - + Loop(EndOffset), LoopWithType(ValType, EndOffset), LoopWithFuncType(TypeAddr, EndOffset), @@ -141,7 +141,7 @@ pub enum Instruction { RefNull(ValType), RefFunc(FuncAddr), RefIsNull, - + // > Numeric Instructions // See <https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions> I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU, diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index beaa828..395c056 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -9,8 +9,11 @@ //! Types used by [`tinywasm`](https://docs.rs/tinywasm) and [`tinywasm_parser`](https://docs.rs/tinywasm_parser). extern crate alloc; -use alloc::boxed::Box; -use core::{fmt::Debug, ops::Range}; +use alloc::{boxed::Box, sync::Arc}; +use core::{ + fmt::Debug, + ops::{Deref, Range}, +}; // Memory defaults const MEM_PAGE_SIZE: u64 = 65536; @@ -73,47 +76,47 @@ pub struct TinyWasmModule { /// Optimized and validated WebAssembly functions /// /// Contains data from to the `code`, `func`, and `type` sections of the original WebAssembly module. - pub funcs: Box<[WasmFunction]>, + pub funcs: ArcSlice<WasmFunction>, /// A vector of type definitions, indexed by `TypeAddr` /// /// Corresponds to the `type` section of the original WebAssembly module. - pub func_types: Box<[FuncType]>, + pub func_types: ArcSlice<FuncType>, /// Exported items of the WebAssembly module. /// /// Corresponds to the `export` section of the original WebAssembly module. - pub exports: Box<[Export]>, + pub exports: ArcSlice<Export>, /// Global components of the WebAssembly module. /// /// Corresponds to the `global` section of the original WebAssembly module. - pub globals: Box<[Global]>, + pub globals: ArcSlice<Global>, /// Table components of the WebAssembly module used to initialize tables. /// /// Corresponds to the `table` section of the original WebAssembly module. - pub table_types: Box<[TableType]>, + pub table_types: ArcSlice<TableType>, /// Memory components of the WebAssembly module used to initialize memories. /// /// Corresponds to the `memory` section of the original WebAssembly module. - pub memory_types: Box<[MemoryType]>, + pub memory_types: ArcSlice<MemoryType>, /// Imports of the WebAssembly module. /// /// Corresponds to the `import` section of the original WebAssembly module. - pub imports: Box<[Import]>, + pub imports: ArcSlice<Import>, /// Data segments of the WebAssembly module. /// /// Corresponds to the `data` section of the original WebAssembly module. - pub data: Box<[Data]>, + pub data: ArcSlice<Data>, /// Element segments of the WebAssembly module. /// /// Corresponds to the `elem` section of the original WebAssembly module. - pub elements: Box<[Element]>, + pub elements: ArcSlice<Element>, } /// A WebAssembly External Kind. @@ -249,13 +252,57 @@ impl<'a, T: IntoIterator<Item = &'a ValType>> From<T> for ValueCountsSmall { #[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct WasmFunction { - pub instructions: Box<[Instruction]>, + pub instructions: ArcSlice<Instruction>, pub data: WasmFunctionData, pub locals: ValueCounts, pub params: ValueCountsSmall, pub ty: FuncType, } +#[derive(Clone, PartialEq)] +#[doc(hidden)] +// wrapper around Arc<[T]> to support serde serialization and deserialization +pub struct ArcSlice<T>(pub Arc<[T]>); + +impl<T> From<alloc::vec::Vec<T>> for ArcSlice<T> { + fn from(vec: alloc::vec::Vec<T>) -> Self { + Self(Arc::from(vec)) + } +} + +impl<T> Default for ArcSlice<T> { + fn default() -> Self { + Self(Arc::from([])) + } +} + +impl<T> Deref for ArcSlice<T> { + type Target = [T]; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl<T: Debug> Debug for ArcSlice<T> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.as_ref().fmt(f) + } +} + +impl<T: serde::Serialize + Debug> serde::Serialize for ArcSlice<T> { + fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + self.0.as_ref().serialize(serializer) + } +} + +impl<'de, T: serde::Deserialize<'de> + Debug> serde::Deserialize<'de> for ArcSlice<T> { + fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { + let vec: alloc::vec::Vec<T> = alloc::vec::Vec::deserialize(deserializer)?; + Ok(Self(Arc::from(vec))) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Default)] #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub struct WasmFunctionData { |
