diff options
| -rw-r--r-- | crates/tinywasm/src/func.rs | 4 | ||||
| -rw-r--r-- | crates/tinywasm/src/imports.rs | 4 | ||||
| -rw-r--r-- | crates/tinywasm/src/reference.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/mod.rs | 79 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/blocks.rs | 60 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/call_stack.rs | 63 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/value_stack.rs | 8 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/value.rs | 1 | ||||
| -rw-r--r-- | crates/tinywasm/src/store.rs | 20 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 9 |
11 files changed, 128 insertions, 124 deletions
diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 9c9938b..1c5129b 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -1,4 +1,4 @@ -use crate::log; +use crate::{log, runtime::RawWasmValue}; use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec}; use tinywasm_types::{FuncAddr, FuncType, ValType, WasmValue}; @@ -63,7 +63,7 @@ impl FuncHandle { // 6. Let f be the dummy frame log::debug!("locals: {:?}", locals); - let call_frame = CallFrame::new(func_inst, params, locals); + let call_frame = CallFrame::new(func_inst, params.iter().map(|v| RawWasmValue::from(*v)), locals); // 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 28ea0ec..32140a7 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -154,7 +154,7 @@ impl Extern { let inner_func = move |ctx: FuncContext<'_>, args: &[WasmValue]| -> Result<Vec<WasmValue>> { let args = P::from_wasm_value_tuple(args)?; let result = func(ctx, args)?; - Ok(result.into_wasm_value_tuple()) + Ok(result.into_wasm_value_tuple().to_vec()) }; let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() }; @@ -383,7 +383,7 @@ impl Imports { .ok_or_else(|| LinkingError::incompatible_import_type(import))?; Self::compare_types(import, extern_func.ty(), import_func_type)?; - imports.funcs.push(store.add_func(extern_func, *ty, idx)?); + imports.funcs.push(store.add_func(extern_func, idx)?); } _ => return Err(LinkingError::incompatible_import_type(import).into()), }, diff --git a/crates/tinywasm/src/reference.rs b/crates/tinywasm/src/reference.rs index fdaae18..ed09530 100644 --- a/crates/tinywasm/src/reference.rs +++ b/crates/tinywasm/src/reference.rs @@ -83,7 +83,7 @@ impl MemoryRef { /// Load a UTF-8 string from memory pub fn load_string(&self, offset: usize, len: usize) -> Result<String> { let bytes = self.load_vec(offset, len)?; - Ok(String::from_utf8(bytes).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string()))?) + String::from_utf8(bytes).map_err(|_| crate::Error::Other("Invalid UTF-8 string".to_string())) } /// Load a C-style string from memory diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 285ba8d..a59b544 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -1,7 +1,7 @@ use super::{InterpreterRuntime, Stack}; use crate::{cold, log, unlikely}; use crate::{ - runtime::{BlockType, CallFrame, LabelArgs, LabelFrame}, + runtime::{BlockType, CallFrame, LabelFrame}, Error, FuncContext, ModuleInstance, Result, Store, Trap, }; use alloc::format; @@ -29,13 +29,12 @@ impl InterpreterRuntime { let mut cf = stack.call_stack.pop()?; let mut func_inst = cf.func_instance.clone(); - let mut wasm_func = func_inst.assert_wasm().expect("exec expected wasm function"); + let mut wasm_func = func_inst.assert_wasm()?; // The function to execute, gets updated from ExecResult::Call let mut instrs = &wasm_func.instructions; let mut instr_count = instrs.len(); - - let mut current_module = store.get_module_instance(func_inst.owner).unwrap().clone(); + let mut current_module = store.get_module_instance_raw(func_inst.owner); loop { if unlikely(cf.instr_ptr >= instr_count) { @@ -164,8 +163,8 @@ fn exec_one( } }; - let params = stack.values.pop_n_rev(ty.params.len())?.collect::<Vec<_>>(); - let call_frame = CallFrame::new_raw(func_inst, ¶ms, locals); + let params = stack.values.pop_n_rev(ty.params.len())?; + let call_frame = CallFrame::new(func_inst, params, locals); // push the call frame cf.instr_ptr += 1; // skip the call instruction @@ -213,8 +212,8 @@ fn exec_one( } }; - let params = stack.values.pop_n_rev(func_ty.params.len())?.collect::<Vec<_>>(); - let call_frame = CallFrame::new_raw(func_inst, ¶ms, locals); + let params = stack.values.pop_n_rev(func_ty.params.len())?; + let call_frame = CallFrame::new(func_inst, params, locals); // push the call frame cf.instr_ptr += 1; // skip the call instruction @@ -229,13 +228,14 @@ fn exec_one( // truthy value is on the top of the stack, so enter the then block if stack.values.pop_t::<i32>()? != 0 { cf.enter_label( - LabelFrame { - instr_ptr: cf.instr_ptr, - end_instr_ptr: cf.instr_ptr + *end_offset, - stack_ptr: stack.values.len(), // - params, - args: LabelArgs::new(*args, module)?, - ty: BlockType::If, - }, + LabelFrame::new( + cf.instr_ptr, + cf.instr_ptr + *end_offset, + stack.values.len(), // - params, + BlockType::If, + args, + module, + ), &mut stack.values, ); return Ok(ExecResult::Ok); @@ -244,13 +244,14 @@ fn exec_one( // falsy value is on the top of the stack if let Some(else_offset) = else_offset { cf.enter_label( - LabelFrame { - instr_ptr: cf.instr_ptr + *else_offset, - end_instr_ptr: cf.instr_ptr + *end_offset, - stack_ptr: stack.values.len(), // - params, - args: LabelArgs::new(*args, module)?, - ty: BlockType::Else, - }, + LabelFrame::new( + cf.instr_ptr + *else_offset, + cf.instr_ptr + *end_offset, + stack.values.len(), // - params, + BlockType::Else, + args, + module, + ), &mut stack.values, ); cf.instr_ptr += *else_offset; @@ -261,26 +262,28 @@ fn exec_one( Loop(args, end_offset) => { cf.enter_label( - LabelFrame { - instr_ptr: cf.instr_ptr, - end_instr_ptr: cf.instr_ptr + *end_offset, - stack_ptr: stack.values.len(), // - params, - args: LabelArgs::new(*args, module)?, - ty: BlockType::Loop, - }, + LabelFrame::new( + cf.instr_ptr, + cf.instr_ptr + *end_offset, + stack.values.len(), // - params, + BlockType::Loop, + args, + module, + ), &mut stack.values, ); } Block(args, end_offset) => { cf.enter_label( - LabelFrame { - instr_ptr: cf.instr_ptr, - end_instr_ptr: cf.instr_ptr + *end_offset, - stack_ptr: stack.values.len(), //- params, - args: LabelArgs::new(*args, module)?, - ty: BlockType::Block, - }, + LabelFrame::new( + cf.instr_ptr, + cf.instr_ptr + *end_offset, + stack.values.len(), // - params, + BlockType::Block, + args, + module, + ), &mut stack.values, ); } @@ -341,7 +344,7 @@ fn exec_one( panic!("else: no label to end, this should have been validated by the parser"); }; - let res_count = block.args.results; + let res_count = block.results; stack.values.truncate_keep(block.stack_ptr, res_count); cf.instr_ptr += *end_offset; } @@ -352,7 +355,7 @@ fn exec_one( cold(); panic!("end: no label to end, this should have been validated by the parser"); }; - stack.values.truncate_keep(block.stack_ptr, block.args.results) + stack.values.truncate_keep(block.stack_ptr, block.results) } LocalGet(local_index) => stack.values.push(cf.get_local(*local_index as usize)), diff --git a/crates/tinywasm/src/runtime/stack.rs b/crates/tinywasm/src/runtime/stack.rs index 07d9316..285d967 100644 --- a/crates/tinywasm/src/runtime/stack.rs +++ b/crates/tinywasm/src/runtime/stack.rs @@ -3,7 +3,7 @@ mod call_stack; mod value_stack; use self::{call_stack::CallStack, value_stack::ValueStack}; -pub(crate) use blocks::{BlockType, LabelArgs, LabelFrame}; +pub(crate) use blocks::{BlockType, LabelFrame}; pub(crate) use call_stack::CallFrame; /// A WebAssembly Stack diff --git a/crates/tinywasm/src/runtime/stack/blocks.rs b/crates/tinywasm/src/runtime/stack/blocks.rs index ab2d28c..ff77cf8 100644 --- a/crates/tinywasm/src/runtime/stack/blocks.rs +++ b/crates/tinywasm/src/runtime/stack/blocks.rs @@ -1,12 +1,17 @@ use alloc::vec::Vec; use tinywasm_types::BlockArgs; -use crate::{ModuleInstance, Result}; +use crate::{unlikely, ModuleInstance}; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub(crate) struct Labels(Vec<LabelFrame>); impl Labels { + pub(crate) fn new() -> Self { + // this is somehow a lot faster than Vec::with_capacity(128) or even using Default::default() in the benchmarks + Self(Vec::new()) + } + pub(crate) fn len(&self) -> usize { self.0.len() } @@ -19,7 +24,12 @@ impl Labels { #[inline] /// get the label at the given index, where 0 is the top of the stack pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&LabelFrame> { - self.0.get(self.0.len() - index - 1) + // the vast majority of wasm functions don't use break to return + if unlikely(index >= self.0.len()) { + return None; + } + + Some(&self.0[self.0.len() - index - 1]) } #[inline] @@ -43,10 +53,34 @@ pub(crate) struct LabelFrame { // position of the stack pointer when the block was entered pub(crate) stack_ptr: usize, - pub(crate) args: LabelArgs, + pub(crate) results: usize, + pub(crate) params: usize, pub(crate) ty: BlockType, } +impl LabelFrame { + #[inline] + pub(crate) fn new( + instr_ptr: usize, + end_instr_ptr: usize, + stack_ptr: usize, + ty: BlockType, + args: &BlockArgs, + module: &ModuleInstance, + ) -> Self { + let (params, results) = match args { + BlockArgs::Empty => (0, 0), + BlockArgs::Type(_) => (0, 1), + BlockArgs::FuncType(t) => { + let ty = module.func_ty(*t); + (ty.params.len(), ty.results.len()) + } + }; + + Self { instr_ptr, end_instr_ptr, stack_ptr, results, params, ty } + } +} + #[derive(Debug, Copy, Clone)] #[allow(dead_code)] pub(crate) enum BlockType { @@ -55,21 +89,3 @@ pub(crate) enum BlockType { Else, Block, } - -#[derive(Debug, Clone, Default)] -pub(crate) struct LabelArgs { - pub(crate) params: usize, - pub(crate) results: usize, -} - -impl LabelArgs { - pub(crate) fn new(args: BlockArgs, module: &ModuleInstance) -> Result<Self> { - Ok(match args { - BlockArgs::Empty => LabelArgs { params: 0, results: 0 }, - BlockArgs::Type(_) => LabelArgs { params: 0, results: 1 }, - BlockArgs::FuncType(t) => { - LabelArgs { params: module.func_ty(t).params.len(), results: module.func_ty(t).results.len() } - } - }) - } -} diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index 46c745a..ebec142 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -1,16 +1,15 @@ -use crate::log; +use crate::unlikely; use crate::{ runtime::{BlockType, RawWasmValue}, Error, FunctionInstance, Result, Trap, }; -use alloc::vec; use alloc::{boxed::Box, rc::Rc, vec::Vec}; -use tinywasm_types::{ValType, WasmValue}; +use tinywasm_types::ValType; use super::{blocks::Labels, LabelFrame}; // minimum call stack size -const CALL_STACK_SIZE: usize = 256; +const CALL_STACK_SIZE: usize = 128; const CALL_STACK_MAX_SIZE: usize = 1024; #[derive(Debug)] @@ -32,16 +31,17 @@ impl CallStack { #[inline] pub(crate) fn pop(&mut self) -> Result<CallFrame> { - self.stack.pop().ok_or_else(|| Error::CallStackEmpty) + match self.stack.pop() { + Some(frame) => Ok(frame), + None => Err(Error::CallStackEmpty), + } } #[inline] pub(crate) fn push(&mut self, call_frame: CallFrame) -> Result<()> { - log::debug!("stack size: {}", self.stack.len()); - if self.stack.len() >= CALL_STACK_MAX_SIZE { + if unlikely(self.stack.len() >= CALL_STACK_MAX_SIZE) { return Err(Trap::CallStackOverflow.into()); } - self.stack.push(call_frame); Ok(()) } @@ -55,15 +55,14 @@ pub(crate) struct CallFrame { pub(crate) labels: Labels, pub(crate) locals: Box<[RawWasmValue]>, - pub(crate) local_count: usize, } impl CallFrame { #[inline] /// Push a new label to the label stack and ensure the stack has the correct values pub(crate) fn enter_label(&mut self, label_frame: LabelFrame, stack: &mut super::ValueStack) { - if label_frame.args.params > 0 { - stack.extend_from_within((label_frame.stack_ptr - label_frame.args.params)..label_frame.stack_ptr); + if label_frame.params > 0 { + stack.extend_from_within((label_frame.stack_ptr - label_frame.params)..label_frame.stack_ptr); } self.labels.push(label_frame); @@ -80,7 +79,7 @@ impl CallFrame { BlockType::Loop => { // this is a loop, so we want to jump back to the start of the loop // We also want to push the params to the stack - value_stack.break_to(break_to.stack_ptr, break_to.args.params); + value_stack.break_to(break_to.stack_ptr, break_to.params); self.instr_ptr = break_to.instr_ptr; @@ -90,7 +89,7 @@ impl CallFrame { BlockType::Block | BlockType::If | BlockType::Else => { // this is a block, so we want to jump to the next instruction after the block ends // We also want to push the block's results to the stack - value_stack.break_to(break_to.stack_ptr, break_to.args.results); + value_stack.break_to(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.end_instr_ptr; @@ -103,46 +102,30 @@ impl CallFrame { Some(()) } - // TOOD: perf: this function is pretty hot - // Especially the two `extend` calls - pub(crate) fn new_raw( - func_instance_ptr: Rc<FunctionInstance>, - params: &[RawWasmValue], - local_types: Vec<ValType>, - ) -> Self { - let mut locals = vec![RawWasmValue::default(); local_types.len() + params.len()]; - locals[..params.len()].copy_from_slice(params); - - Self { - instr_ptr: 0, - func_instance: func_instance_ptr, - local_count: locals.len(), - locals: locals.into_boxed_slice(), - labels: Labels::default(), - } - } - + #[inline] pub(crate) fn new( func_instance_ptr: Rc<FunctionInstance>, - params: &[WasmValue], + params: impl Iterator<Item = RawWasmValue> + ExactSizeIterator, local_types: Vec<ValType>, ) -> Self { - CallFrame::new_raw( - func_instance_ptr, - ¶ms.iter().map(|v| RawWasmValue::from(*v)).collect::<Vec<_>>(), - local_types, - ) + let locals = { + let total_size = local_types.len() + params.len(); + let mut locals = Vec::with_capacity(total_size); + locals.extend(params); + locals.resize_with(total_size, RawWasmValue::default); + locals.into_boxed_slice() + }; + + Self { instr_ptr: 0, func_instance: func_instance_ptr, locals, labels: Labels::new() } } #[inline] pub(crate) fn set_local(&mut self, local_index: usize, value: RawWasmValue) { - assert!(local_index < self.local_count, "Invalid local index"); self.locals[local_index] = value; } #[inline] pub(crate) fn get_local(&self, local_index: usize) -> RawWasmValue { - assert!(local_index < self.local_count, "Invalid local index"); self.locals[local_index] } } diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index e1c3107..3c5f48b 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -1,11 +1,12 @@ use core::ops::Range; use crate::{cold, runtime::RawWasmValue, unlikely, Error, Result}; +use alloc::vec; use alloc::vec::Vec; use tinywasm_types::{ValType, WasmValue}; -// minimum stack size -pub(crate) const STACK_SIZE: usize = 1024; +pub(crate) const MIN_VALUE_STACK_SIZE: usize = 1024; +// pub(crate) const MAX_VALUE_STACK_SIZE: usize = 1024 * 1024; #[derive(Debug)] pub(crate) struct ValueStack { @@ -14,7 +15,7 @@ pub(crate) struct ValueStack { impl Default for ValueStack { fn default() -> Self { - Self { stack: Vec::with_capacity(STACK_SIZE) } + Self { stack: vec![RawWasmValue::default(); MIN_VALUE_STACK_SIZE] } } } @@ -97,6 +98,7 @@ impl ValueStack { Ok(res) } + #[inline] pub(crate) fn break_to(&mut self, new_stack_size: usize, result_count: usize) { self.stack.drain(new_stack_size..(self.stack.len() - result_count)); } diff --git a/crates/tinywasm/src/runtime/value.rs b/crates/tinywasm/src/runtime/value.rs index 7230830..329a60b 100644 --- a/crates/tinywasm/src/runtime/value.rs +++ b/crates/tinywasm/src/runtime/value.rs @@ -8,6 +8,7 @@ use tinywasm_types::{ValType, WasmValue}; /// /// See [`WasmValue`] for the public representation. #[derive(Clone, Copy, Default, PartialEq, Eq)] +#[repr(transparent)] pub struct RawWasmValue(u64); impl Debug for RawWasmValue { diff --git a/crates/tinywasm/src/store.rs b/crates/tinywasm/src/store.rs index ff40b33..dd8112d 100644 --- a/crates/tinywasm/src/store.rs +++ b/crates/tinywasm/src/store.rs @@ -46,10 +46,13 @@ impl Store { /// Get a module instance by the internal id pub fn get_module_instance(&self, addr: ModuleInstanceAddr) -> Option<&ModuleInstance> { - log::debug!("existing module instances: {:?}", self.module_instances.len()); self.module_instances.get(addr as usize) } + pub(crate) fn get_module_instance_raw(&self, addr: ModuleInstanceAddr) -> ModuleInstance { + self.module_instances[addr as usize].clone() + } + /// Create a new store with the given runtime pub(crate) fn runtime(&self) -> runtime::InterpreterRuntime { match self.runtime { @@ -118,12 +121,8 @@ impl Store { let func_count = self.data.funcs.len(); let mut func_addrs = Vec::with_capacity(func_count); - for (i, (type_idx, func)) in funcs.into_iter().enumerate() { - self.data.funcs.push(Rc::new(FunctionInstance { - func: Function::Wasm(func), - _type_idx: type_idx, - owner: idx, - })); + for (i, (_, func)) in funcs.into_iter().enumerate() { + self.data.funcs.push(Rc::new(FunctionInstance { func: Function::Wasm(func), owner: idx })); func_addrs.push((i + func_count) as FuncAddr); } @@ -222,7 +221,7 @@ impl Store { ) -> Result<(Box<[Addr]>, Option<Trap>)> { let elem_count = self.data.elements.len(); let mut elem_addrs = Vec::with_capacity(elem_count); - for (i, element) in elements.into_iter().enumerate() { + for (i, element) in elements.iter().enumerate() { let init = element .items .iter() @@ -344,8 +343,8 @@ impl Store { Ok(self.data.memories.len() as MemAddr - 1) } - pub(crate) fn add_func(&mut self, func: Function, type_idx: TypeAddr, idx: ModuleInstanceAddr) -> Result<FuncAddr> { - self.data.funcs.push(Rc::new(FunctionInstance { func, _type_idx: type_idx, owner: idx })); + pub(crate) fn add_func(&mut self, func: Function, idx: ModuleInstanceAddr) -> Result<FuncAddr> { + self.data.funcs.push(Rc::new(FunctionInstance { func, owner: idx })); Ok(self.data.funcs.len() as FuncAddr - 1) } @@ -445,7 +444,6 @@ impl Store { /// See <https://webassembly.github.io/spec/core/exec/runtime.html#function-instances> pub(crate) struct FunctionInstance { pub(crate) func: Function, - pub(crate) _type_idx: TypeAddr, pub(crate) owner: ModuleInstanceAddr, // index into store.module_instances, none for host functions } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 9af8e11..2ccf869 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -12,6 +12,7 @@ extern crate alloc; // log for logging (optional). #[cfg(feature = "logging")] +#[allow(clippy::single_component_path_imports)] use log; #[cfg(not(feature = "logging"))] @@ -173,7 +174,7 @@ impl TryFrom<WasmValue> for i32 { match value { WasmValue::I32(i) => Ok(i), _ => { - log::error!("i32: try_from failed: {:?}", value); + crate::log::error!("i32: try_from failed: {:?}", value); Err(()) } } @@ -187,7 +188,7 @@ impl TryFrom<WasmValue> for i64 { match value { WasmValue::I64(i) => Ok(i), _ => { - log::error!("i64: try_from failed: {:?}", value); + crate::log::error!("i64: try_from failed: {:?}", value); Err(()) } } @@ -201,7 +202,7 @@ impl TryFrom<WasmValue> for f32 { match value { WasmValue::F32(i) => Ok(i), _ => { - log::error!("f32: try_from failed: {:?}", value); + crate::log::error!("f32: try_from failed: {:?}", value); Err(()) } } @@ -215,7 +216,7 @@ impl TryFrom<WasmValue> for f64 { match value { WasmValue::F64(i) => Ok(i), _ => { - log::error!("f64: try_from failed: {:?}", value); + crate::log::error!("f64: try_from failed: {:?}", value); Err(()) } } |
