diff options
| author | Henry Gressmann <mail@henrygressmann.de> | 2024-02-27 16:08:14 +0100 |
|---|---|---|
| committer | Henry Gressmann <mail@henrygressmann.de> | 2024-02-27 16:08:14 +0100 |
| commit | d2db1f7b3803c42619d91b85a66c12ace296bb21 (patch) | |
| tree | 035db998b17a2446965ba199e092b49389cd268e | |
| parent | 4faaf5bb222947a95b854d733a3cbe3bf588b597 (diff) | |
chore: overall code cleanup
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
| -rw-r--r-- | crates/benchmarks/benches/argon2id.rs | 6 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 46 | ||||
| -rw-r--r-- | crates/tinywasm/src/func.rs | 73 | ||||
| -rw-r--r-- | crates/tinywasm/src/imports.rs | 1 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/macros.rs | 79 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/mod.rs | 115 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/no_std_floats.rs | 97 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/interpreter/traits.rs | 44 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/call_stack.rs | 2 | ||||
| -rw-r--r-- | crates/tinywasm/src/runtime/stack/value_stack.rs | 12 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/mod.rs | 79 | ||||
| -rw-r--r-- | crates/tinywasm/src/store/table.rs | 4 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 2 | ||||
| -rw-r--r-- | crates/types/src/value.rs | 2 |
14 files changed, 220 insertions, 342 deletions
diff --git a/crates/benchmarks/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs index 7c1ffc5..a503687 100644 --- a/crates/benchmarks/benches/argon2id.rs +++ b/crates/benchmarks/benches/argon2id.rs @@ -45,10 +45,10 @@ fn criterion_benchmark(c: &mut Criterion) { group.measurement_time(std::time::Duration::from_secs(7)); group.sample_size(10); - group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); + // group.bench_function("native", |b| b.iter(|| run_native(black_box(params)))); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm, black_box(params), "argon2id"))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); + // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(ARGON2ID, black_box(params), "argon2id"))); + // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(ARGON2ID, black_box(params), "argon2id"))); } criterion_group!( diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index edebc97..88b6ba5 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -340,16 +340,17 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - if let Some(instruction) = self.instructions.last_mut() { - match instruction { - // Needs more testing, seems to make performance worse - // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), - _ => return self.visit(Instruction::LocalSet(idx)), - }; - // Ok(()) - } else { - self.visit(Instruction::LocalSet(idx)) - } + self.visit(Instruction::LocalSet(idx)) + // if let Some(instruction) = self.instructions.last_mut() { + // match instruction { + // // Needs more testing, seems to make performance worse + // // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), + // _ => return self.visit(Instruction::LocalSet(idx)), + // }; + // // Ok(()) + // } else { + // self.visit(Instruction::LocalSet(idx)) + // } } fn visit_local_tee(&mut self, idx: u32) -> Self::Output { @@ -372,18 +373,19 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_i32_add(&mut self) -> Self::Output { - if self.instructions.len() < 2 { - return self.visit(Instruction::I32Add); - } + self.visit(Instruction::I32Add) + // if self.instructions.len() < 2 { + // return self.visit(Instruction::I32Add); + // } - match self.instructions[self.instructions.len() - 2..] { - // [Instruction::LocalGet(a), Instruction::I32Const(b)] => { - // self.instructions.pop(); - // self.instructions.pop(); - // self.visit(Instruction::I32LocalGetConstAdd(a, b)) - // } - _ => self.visit(Instruction::I32Add), - } + // match self.instructions[self.instructions.len() - 2..] { + // // [Instruction::LocalGet(a), Instruction::I32Const(b)] => { + // // self.instructions.pop(); + // // self.instructions.pop(); + // // self.visit(Instruction::I32LocalGetConstAdd(a, b)) + // // } + // _ => self.visit(Instruction::I32Add), + // } } fn visit_block(&mut self, blockty: wasmparser::BlockType) -> Self::Output { @@ -416,7 +418,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { match self.instructions[label_pointer] { Instruction::Else(ref mut else_instr_end_offset) => { - *else_instr_end_offset = (current_instr_ptr - label_pointer as usize) + *else_instr_end_offset = (current_instr_ptr - label_pointer) .try_into() .expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large"); diff --git a/crates/tinywasm/src/func.rs b/crates/tinywasm/src/func.rs index 757cb85..d7f7ca1 100644 --- a/crates/tinywasm/src/func.rs +++ b/crates/tinywasm/src/func.rs @@ -22,6 +22,9 @@ impl FuncHandle { /// See <https://webassembly.github.io/spec/core/exec/modules.html#invocation> #[inline] pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result<Vec<WasmValue>> { + // Comments are ordered by the steps in the spec + // In this implementation, some steps are combined and ordered differently for performance reasons + // 3. Let func_ty be the function type let func_ty = &self.ty; @@ -35,7 +38,7 @@ impl FuncHandle { } // 5. For each value type and the corresponding value, check if types match - if !unlikely(func_ty.params.iter().zip(params).enumerate().all(|(i, (ty, param))| { + if !(func_ty.params.iter().zip(params).enumerate().all(|(i, (ty, param))| { if ty != ¶m.val_type() { log::error!("param type mismatch at index {}: expected {:?}, got {:?}", i, ty, param); false @@ -57,8 +60,8 @@ impl FuncHandle { }; // 6. Let f be the dummy frame - let call_frame = - CallFrame::new(wasm_func.clone(), func_inst.owner, params.iter().map(|v| RawWasmValue::from(*v)), 0); + let call_frame_params = params.iter().map(|v| RawWasmValue::from(*v)); + let call_frame = CallFrame::new(wasm_func.clone(), func_inst.owner, call_frame_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) @@ -113,6 +116,7 @@ impl<P: IntoWasmValueTuple, R: FromWasmValueTuple> FuncHandleTyped<P, R> { R::from_wasm_value_tuple(&result) } } + macro_rules! impl_into_wasm_value_tuple { ($($T:ident),*) => { impl<$($T),*> IntoWasmValueTuple for ($($T,)*) @@ -140,21 +144,6 @@ macro_rules! impl_into_wasm_value_tuple_single { }; } -impl_into_wasm_value_tuple_single!(i32); -impl_into_wasm_value_tuple_single!(i64); -impl_into_wasm_value_tuple_single!(f32); -impl_into_wasm_value_tuple_single!(f64); - -impl_into_wasm_value_tuple!(); -impl_into_wasm_value_tuple!(T1); -impl_into_wasm_value_tuple!(T1, T2); -impl_into_wasm_value_tuple!(T1, T2, T3); -impl_into_wasm_value_tuple!(T1, T2, T3, T4); -impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5); -impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6); -impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7); -impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7, T8); - macro_rules! impl_from_wasm_value_tuple { ($($T:ident),*) => { impl<$($T),*> FromWasmValueTuple for ($($T,)*) @@ -200,21 +189,6 @@ macro_rules! impl_from_wasm_value_tuple_single { }; } -impl_from_wasm_value_tuple_single!(i32); -impl_from_wasm_value_tuple_single!(i64); -impl_from_wasm_value_tuple_single!(f32); -impl_from_wasm_value_tuple_single!(f64); - -impl_from_wasm_value_tuple!(); -impl_from_wasm_value_tuple!(T1); -impl_from_wasm_value_tuple!(T1, T2); -impl_from_wasm_value_tuple!(T1, T2, T3); -impl_from_wasm_value_tuple!(T1, T2, T3, T4); -impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5); -impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6); -impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7); -impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6, T7, T8); - pub trait ValTypesFromTuple { fn val_types() -> Box<[ValType]>; } @@ -268,19 +242,42 @@ impl ValTypesFromTuple for () { } } -impl<T1> ValTypesFromTuple for T1 -where - T1: ToValType, -{ +impl<T: ToValType> ValTypesFromTuple for T { #[inline] fn val_types() -> Box<[ValType]> { - Box::new([T1::to_val_type()]) + Box::new([T::to_val_type()]) } } +impl_from_wasm_value_tuple_single!(i32); +impl_from_wasm_value_tuple_single!(i64); +impl_from_wasm_value_tuple_single!(f32); +impl_from_wasm_value_tuple_single!(f64); + +impl_into_wasm_value_tuple_single!(i32); +impl_into_wasm_value_tuple_single!(i64); +impl_into_wasm_value_tuple_single!(f32); +impl_into_wasm_value_tuple_single!(f64); + impl_val_types_from_tuple!(T1); impl_val_types_from_tuple!(T1, T2); impl_val_types_from_tuple!(T1, T2, T3); impl_val_types_from_tuple!(T1, T2, T3, T4); impl_val_types_from_tuple!(T1, T2, T3, T4, T5); impl_val_types_from_tuple!(T1, T2, T3, T4, T5, T6); + +impl_from_wasm_value_tuple!(); +impl_from_wasm_value_tuple!(T1); +impl_from_wasm_value_tuple!(T1, T2); +impl_from_wasm_value_tuple!(T1, T2, T3); +impl_from_wasm_value_tuple!(T1, T2, T3, T4); +impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5); +impl_from_wasm_value_tuple!(T1, T2, T3, T4, T5, T6); + +impl_into_wasm_value_tuple!(); +impl_into_wasm_value_tuple!(T1); +impl_into_wasm_value_tuple!(T1, T2); +impl_into_wasm_value_tuple!(T1, T2, T3); +impl_into_wasm_value_tuple!(T1, T2, T3, T4); +impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5); +impl_into_wasm_value_tuple!(T1, T2, T3, T4, T5, T6); diff --git a/crates/tinywasm/src/imports.rs b/crates/tinywasm/src/imports.rs index 522a82d..1c5ca98 100644 --- a/crates/tinywasm/src/imports.rs +++ b/crates/tinywasm/src/imports.rs @@ -157,7 +157,6 @@ impl Extern { }; let ty = tinywasm_types::FuncType { params: P::val_types(), results: R::val_types() }; - Self::Function(Function::Host(Rc::new(HostFunction { func: Box::new(inner_func), ty }))) } diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index 9227ceb..30f34fc 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -12,10 +12,9 @@ macro_rules! break_to { ($cf:ident, $stack:ident, $break_to_relative:ident) => {{ if $cf.break_to(*$break_to_relative, &mut $stack.values, &mut $stack.blocks).is_none() { - if $stack.call_stack.is_empty() { - return Ok(ExecResult::Return); - } else { - return Ok(ExecResult::Call); + match $stack.call_stack.is_empty() { + true => return Ok(ExecResult::Return), + false => return Ok(ExecResult::Call), } } }}; @@ -62,21 +61,15 @@ macro_rules! mem_load { /// Store a value to memory macro_rules! mem_store { ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ - log::debug!("mem_store!({}, {:?})", stringify!($type), $arg); mem_store!($type, $type, $arg, $stack, $store, $module) }}; ($store_type:ty, $target_type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ let (mem_addr, offset) = $arg; - let mem_idx = $module.resolve_mem_addr(*mem_addr); - let mem = $store.get_mem(mem_idx as usize)?; - - let val = $stack.values.pop_t::<$store_type>()?; - let addr: u64 = $stack.values.pop()?.into(); - - let val = val as $store_type; + let mem = $store.get_mem($module.resolve_mem_addr(*mem_addr) as usize)?; + let val: $store_type = $stack.values.pop()?.into(); let val = val.to_le_bytes(); - + let addr: u64 = $stack.values.pop()?.into(); mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?; }}; } @@ -103,13 +96,11 @@ macro_rules! float_min_max { /// Convert a value on the stack macro_rules! conv { - ($from:ty, $intermediate:ty, $to:ty, $stack:ident) => {{ - let a = $stack.values.pop_t::<$from>()? as $intermediate; - $stack.values.push((a as $to).into()); - }}; ($from:ty, $to:ty, $stack:ident) => {{ - let a = $stack.values.pop_t::<$from>()?; - $stack.values.push((a as $to).into()); + $stack.values.replace_top(|v| { + let a: $from = v.into(); + (a as $to).into() + }); }}; } @@ -138,13 +129,9 @@ macro_rules! checked_conv_float { /// Compare two values on the stack macro_rules! comp { - ($op:tt, $ty:ty, $stack:ident) => {{ - comp!($op, $ty, $ty, $stack) - }}; - - ($op:tt, $intermediate:ty, $to:ty, $stack:ident) => {{ - let b = $stack.values.pop_t::<$intermediate>()? as $to; - let a = $stack.values.pop_t::<$intermediate>()? as $to; + ($op:tt, $to:ty, $stack:ident) => {{ + let b: $to = $stack.values.pop()?.into(); + let a: $to = $stack.values.pop()?.into(); $stack.values.push(((a $op b) as i32).into()); }}; } @@ -152,62 +139,52 @@ macro_rules! comp { /// Compare a value on the stack to zero macro_rules! comp_zero { ($op:tt, $ty:ty, $stack:ident) => {{ - let a = $stack.values.pop_t::<$ty>()?; + let a: $ty = $stack.values.pop()?.into(); $stack.values.push(((a $op 0) as i32).into()); }}; } /// Apply an arithmetic method to two values on the stack macro_rules! arithmetic { - ($op:ident, $ty:ty, $stack:ident) => { - arithmetic!($op, $ty, $ty, $stack) - }; + ($op:ident, $to:ty, $stack:ident) => {{ + let b: $to = $stack.values.pop()?.into(); + let a: $to = $stack.values.pop()?.into(); + $stack.values.push((a.$op(b) as $to).into()); + }}; // also allow operators such as +, - ($op:tt, $ty:ty, $stack:ident) => {{ - let b: $ty = $stack.values.pop_t()?; - let a: $ty = $stack.values.pop_t()?; + let b: $ty = $stack.values.pop()?.into(); + let a: $ty = $stack.values.pop()?.into(); $stack.values.push((a $op b).into()); }}; - - ($op:ident, $intermediate:ty, $to:ty, $stack:ident) => {{ - let b = $stack.values.pop_t::<$to>()? as $intermediate; - let a = $stack.values.pop_t::<$to>()? as $intermediate; - $stack.values.push((a.$op(b) as $to).into()); - }}; } /// Apply an arithmetic method to a single value on the stack macro_rules! arithmetic_single { ($op:ident, $ty:ty, $stack:ident) => {{ - let a = $stack.values.pop_t::<$ty>()?; + let a: $ty = $stack.values.pop()?.into(); $stack.values.push((a.$op() as $ty).into()); }}; ($op:ident, $from:ty, $to:ty, $stack:ident) => {{ - let a = $stack.values.pop_t::<$from>()?; + let a: $from = $stack.values.pop()?.into(); $stack.values.push((a.$op() as $to).into()); }}; } /// Apply an arithmetic operation to two values on the stack with error checking macro_rules! checked_int_arithmetic { - // Direct conversion with error checking (two types) - ($from:tt, $to:tt, $stack:ident) => {{ - checked_int_arithmetic!($from, $to, $to, $stack) - }}; - - ($op:ident, $from:ty, $to:ty, $stack:ident) => {{ - let b = $stack.values.pop_t::<$from>()? as $to; - let a = $stack.values.pop_t::<$from>()? as $to; + ($op:ident, $to:ty, $stack:ident) => {{ + let b: $to = $stack.values.pop()?.into(); + let a: $to = $stack.values.pop()?.into(); - if b == 0 { + if unlikely(b == 0) { return Err(Error::Trap(crate::Trap::DivisionByZero)); } let result = a.$op(b).ok_or_else(|| Error::Trap(crate::Trap::IntegerOverflow))?; - // Cast back to original type if different - $stack.values.push((result as $from).into()); + $stack.values.push((result).into()); }}; } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index bd07051..007acaa 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -79,14 +79,14 @@ enum ExecResult { #[inline(always)] fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result<ExecResult> { let instrs = &cf.func_instance.0.instructions; + if unlikely(cf.instr_ptr >= instrs.len() || instrs.is_empty()) { - cold(); log::error!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len()); return Err(Error::Other(format!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len()))); } // A match statement is probably the fastest way to do this without - // unreasonable complexity + // unreasonable complexity. This *should* be optimized to a jump table. // See https://pliniker.github.io/post/dispatchers/ use tinywasm_types::Instruction::*; match cf.current_instruction() { @@ -144,14 +144,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let table_idx = stack.values.pop_t::<u32>()?; // verify that the table is of the right type, this should be validated by the parser already - assert!(table.borrow().kind.element_type == ValType::RefFunc, "table is not of type funcref"); - let func_ref = { - table - .borrow() - .get(table_idx as usize)? - .addr() - .ok_or(Trap::UninitializedElement { index: table_idx as usize })? + let table = table.borrow(); + assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref"); + table.get(table_idx as usize)?.addr().ok_or(Trap::UninitializedElement { index: table_idx as usize })? }; let func_inst = store.get_func(func_ref as usize)?.clone(); @@ -238,7 +234,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Loop, - &args, + args, module, ), &mut stack.values, @@ -251,9 +247,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M BlockFrame::new( cf.instr_ptr, cf.instr_ptr + *end_offset as usize, - stack.values.len(), // - params, + stack.values.len(), BlockType::Block, - &args, + args, module, ), &mut stack.values, @@ -282,7 +278,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let idx = stack.values.pop_t::<i32>()? as usize; - let to = instr.get(idx).unwrap_or(&default); + let to = instr.get(idx).unwrap_or(default); break_to!(cf, stack, to); } @@ -335,13 +331,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M LocalGet(local_index) => stack.values.push(cf.get_local(*local_index as usize)), LocalSet(local_index) => cf.set_local(*local_index as usize, stack.values.pop()?), LocalTee(local_index) => { + let local = stack.values.last(); cf.set_local( *local_index as usize, - stack - .values - .last() - .expect("localtee: stack is empty. this should have been validated by the parser") - .clone(), + *local.expect("localtee: stack is empty. this should have been validated by the parser"), ); } @@ -362,8 +355,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M F64Const(val) => stack.values.push((*val).into()), MemorySize(addr, byte) => { - if *byte != 0 { - cold(); + if unlikely(*byte != 0) { return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); } @@ -373,8 +365,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } MemoryGrow(addr, byte) => { - if *byte != 0 { - cold(); + if unlikely(*byte != 0) { return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); } @@ -395,9 +386,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // Bulk memory operations MemoryCopy(from, to) => { - let size = stack.values.pop_t::<i32>()?; - let src = stack.values.pop_t::<i32>()?; - let dst = stack.values.pop_t::<i32>()?; + let size: i32 = stack.values.pop()?.into(); + let src: i32 = stack.values.pop()?.into(); + let dst: i32 = stack.values.pop()?.into(); let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?; let mut mem = mem.borrow_mut(); @@ -414,9 +405,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } MemoryFill(addr) => { - let size = stack.values.pop_t::<i32>()?; - let val = stack.values.pop_t::<i32>()?; - let dst = stack.values.pop_t::<i32>()?; + let size: i32 = stack.values.pop()?.into(); + let val: i32 = stack.values.pop()?.into(); + let dst: i32 = stack.values.pop()?.into(); let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?; let mut mem = mem.borrow_mut(); @@ -428,26 +419,20 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::<i32>()? as usize; let dst = stack.values.pop_t::<i32>()? as usize; - let data_idx = module.resolve_data_addr(*data_index); - let Some(ref data) = store.get_data(data_idx as usize)?.data else { - cold(); - return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()); + let data = match &store.get_data(module.resolve_data_addr(*data_index) as usize)?.data { + Some(data) => data, + None => return Err(Trap::MemoryOutOfBounds { offset: 0, len: 0, max: 0 }.into()), }; - let mem_idx = module.resolve_mem_addr(*mem_index); - let mem = store.get_mem(mem_idx as usize)?; - - let data_len = data.len(); - if offset + size > data_len { - cold(); - return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data_len }.into()); + if unlikely(offset + size > data.len()) { + return Err(Trap::MemoryOutOfBounds { offset, len: size, max: data.len() }.into()); } + let mem = store.get_mem(module.resolve_mem_addr(*mem_index) as usize)?; let mut mem = mem.borrow_mut(); - let data = &data[offset..(offset + size)]; // mem.store checks bounds - mem.store(dst, size, data)?; + mem.store(dst, size, &data[offset..(offset + size)])?; } DataDrop(data_index) => { @@ -496,29 +481,29 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I32LtS => comp!(<, i32, stack), I64LtS => comp!(<, i64, stack), - I32LtU => comp!(<, i32, u32, stack), - I64LtU => comp!(<, i64, u64, stack), + I32LtU => comp!(<, u32, stack), + I64LtU => comp!(<, u64, stack), F32Lt => comp!(<, f32, stack), F64Lt => comp!(<, f64, stack), I32LeS => comp!(<=, i32, stack), I64LeS => comp!(<=, i64, stack), - I32LeU => comp!(<=, i32, u32, stack), - I64LeU => comp!(<=, i64, u64, stack), + I32LeU => comp!(<=, u32, stack), + I64LeU => comp!(<=, u64, stack), F32Le => comp!(<=, f32, stack), F64Le => comp!(<=, f64, stack), I32GeS => comp!(>=, i32, stack), I64GeS => comp!(>=, i64, stack), - I32GeU => comp!(>=, i32, u32, stack), - I64GeU => comp!(>=, i64, u64, stack), + I32GeU => comp!(>=, u32, stack), + I64GeU => comp!(>=, u64, stack), F32Ge => comp!(>=, f32, stack), F64Ge => comp!(>=, f64, stack), I32GtS => comp!(>, i32, stack), I64GtS => comp!(>, i64, stack), - I32GtU => comp!(>, i32, u32, stack), - I64GtU => comp!(>, i64, u64, stack), + I32GtU => comp!(>, u32, stack), + I64GtU => comp!(>, u64, stack), F32Gt => comp!(>, f32, stack), F64Gt => comp!(>, f64, stack), @@ -543,13 +528,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // these can trap I32DivS => checked_int_arithmetic!(checked_div, i32, stack), I64DivS => checked_int_arithmetic!(checked_div, i64, stack), - I32DivU => checked_int_arithmetic!(checked_div, i32, u32, stack), - I64DivU => checked_int_arithmetic!(checked_div, i64, u64, stack), + I32DivU => checked_int_arithmetic!(checked_div, u32, stack), + I64DivU => checked_int_arithmetic!(checked_div, u64, stack), I32RemS => checked_int_arithmetic!(checked_wrapping_rem, i32, stack), I64RemS => checked_int_arithmetic!(checked_wrapping_rem, i64, stack), - I32RemU => checked_int_arithmetic!(checked_wrapping_rem, i32, u32, stack), - I64RemU => checked_int_arithmetic!(checked_wrapping_rem, i64, u64, stack), + I32RemU => checked_int_arithmetic!(checked_wrapping_rem, u32, stack), + I64RemU => checked_int_arithmetic!(checked_wrapping_rem, u64, stack), I32And => arithmetic!(bitand, i32, stack), I64And => arithmetic!(bitand, i64, stack), @@ -561,8 +546,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I64Shl => arithmetic!(wasm_shl, i64, stack), I32ShrS => arithmetic!(wasm_shr, i32, stack), I64ShrS => arithmetic!(wasm_shr, i64, stack), - I32ShrU => arithmetic!(wasm_shr, u32, i32, stack), - I64ShrU => arithmetic!(wasm_shr, u64, i64, stack), + I32ShrU => arithmetic!(wasm_shr, u32, stack), + I64ShrU => arithmetic!(wasm_shr, u64, stack), I32Rotl => arithmetic!(wasm_rotl, i32, stack), I64Rotl => arithmetic!(wasm_rotl, i64, stack), I32Rotr => arithmetic!(wasm_rotr, i32, stack), @@ -579,16 +564,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M F32ConvertI64S => conv!(i64, f32, stack), F64ConvertI32S => conv!(i32, f64, stack), F64ConvertI64S => conv!(i64, f64, stack), - F32ConvertI32U => conv!(i32, u32, f32, stack), - F32ConvertI64U => conv!(i64, u64, f32, stack), - F64ConvertI32U => conv!(i32, u32, f64, stack), - F64ConvertI64U => conv!(i64, u64, f64, stack), - I32Extend8S => conv!(i32, i8, i32, stack), - I32Extend16S => conv!(i32, i16, i32, stack), - I64Extend8S => conv!(i64, i8, i64, stack), - I64Extend16S => conv!(i64, i16, i64, stack), - I64Extend32S => conv!(i64, i32, i64, stack), - I64ExtendI32U => conv!(i32, u32, i64, stack), + F32ConvertI32U => conv!(u32, f32, stack), + F32ConvertI64U => conv!(u64, f32, stack), + F64ConvertI32U => conv!(u32, f64, stack), + F64ConvertI64U => conv!(u64, f64, stack), + I32Extend8S => conv!(i8, i32, stack), + I32Extend16S => conv!(i16, i32, stack), + I64Extend8S => conv!(i8, i64, stack), + I64Extend16S => conv!(i16, i64, stack), + I64Extend32S => conv!(i32, i64, stack), + I64ExtendI32U => conv!(u32, i64, stack), I64ExtendI32S => conv!(i32, i64, stack), I32WrapI64 => conv!(i64, i32, stack), diff --git a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs index 91c74b5..5b9471e 100644 --- a/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs +++ b/crates/tinywasm/src/runtime/interpreter/no_std_floats.rs @@ -9,85 +9,26 @@ pub(super) trait NoStdFloatExt { fn copysign(self, other: Self) -> Self; } +#[rustfmt::skip] impl NoStdFloatExt for f64 { - #[inline] - fn round(self) -> Self { - libm::round(self) - } - - #[inline] - fn abs(self) -> Self { - libm::fabs(self) - } - - #[inline] - fn signum(self) -> Self { - libm::copysign(1.0, self) - } - - #[inline] - fn ceil(self) -> Self { - libm::ceil(self) - } - - #[inline] - fn floor(self) -> Self { - libm::floor(self) - } - - #[inline] - fn trunc(self) -> Self { - libm::trunc(self) - } - - #[inline] - fn sqrt(self) -> Self { - libm::sqrt(self) - } - - #[inline] - fn copysign(self, other: Self) -> Self { - libm::copysign(self, other) - } + #[inline] fn round(self) -> Self { libm::round(self) } + #[inline] fn abs(self) -> Self { libm::fabs(self) } + #[inline] fn signum(self) -> Self { libm::copysign(1.0, self) } + #[inline] fn ceil(self) -> Self { libm::ceil(self) } + #[inline] fn floor(self) -> Self { libm::floor(self) } + #[inline] fn trunc(self) -> Self { libm::trunc(self) } + #[inline] fn sqrt(self) -> Self { libm::sqrt(self) } + #[inline] fn copysign(self, other: Self) -> Self { libm::copysign(self, other) } } -impl NoStdFloatExt for f32 { - #[inline] - fn round(self) -> Self { - libm::roundf(self) - } - #[inline] - fn abs(self) -> Self { - libm::fabsf(self) - } - - #[inline] - fn signum(self) -> Self { - libm::copysignf(1.0, self) - } - - #[inline] - fn ceil(self) -> Self { - libm::ceilf(self) - } - - #[inline] - fn floor(self) -> Self { - libm::floorf(self) - } - - #[inline] - fn trunc(self) -> Self { - libm::truncf(self) - } - - #[inline] - fn sqrt(self) -> Self { - libm::sqrtf(self) - } - - #[inline] - fn copysign(self, other: Self) -> Self { - libm::copysignf(self, other) - } +#[rustfmt::skip] +impl NoStdFloatExt for f32 { + #[inline] fn round(self) -> Self { libm::roundf(self) } + #[inline] fn abs(self) -> Self { libm::fabsf(self) } + #[inline] fn signum(self) -> Self { libm::copysignf(1.0, self) } + #[inline] fn ceil(self) -> Self { libm::ceilf(self) } + #[inline] fn floor(self) -> Self { libm::floorf(self) } + #[inline] fn trunc(self) -> Self { libm::truncf(self) } + #[inline] fn sqrt(self) -> Self { libm::sqrtf(self) } + #[inline] fn copysign(self, other: Self) -> Self { libm::copysignf(self, other) } } diff --git a/crates/tinywasm/src/runtime/interpreter/traits.rs b/crates/tinywasm/src/runtime/interpreter/traits.rs index 523265b..7aeb3b7 100644 --- a/crates/tinywasm/src/runtime/interpreter/traits.rs +++ b/crates/tinywasm/src/runtime/interpreter/traits.rs @@ -24,23 +24,15 @@ macro_rules! impl_wasm_float_ops { x if x.is_infinite() || x == 0.0 => x, // preserve infinities and zeros x if (0.0..=0.5).contains(&x) => 0.0, x if (-0.5..0.0).contains(&x) => -0.0, - // x => x.round(), x => { // Handle normal and halfway cases let rounded = x.round(); let diff = (x - rounded).abs(); - - if diff == 0.5 { - // Halfway case: round to even - if rounded % 2.0 == 0.0 { - rounded // Already even - } else { - rounded - x.signum() // Make even - } - } else { - // Normal case - rounded + if diff != 0.5 || rounded % 2.0 == 0.0 { + return rounded } + + rounded - x.signum() // Make even } } } @@ -49,15 +41,11 @@ macro_rules! impl_wasm_float_ops { // Based on f32::minimum (which is not yet stable) #[inline] fn tw_minimum(self, other: Self) -> Self { - if self < other { - self - } else if other < self { - other - } else if self == other { - if self.is_sign_negative() && other.is_sign_positive() { self } else { other } - } else { - // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - self + other + match self.partial_cmp(&other) { + Some(core::cmp::Ordering::Less) => self, + Some(core::cmp::Ordering::Greater) => other, + Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { self } else { other }, + None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. } } @@ -65,15 +53,11 @@ macro_rules! impl_wasm_float_ops { // Based on f32::maximum (which is not yet stable) #[inline] fn tw_maximum(self, other: Self) -> Self { - if self > other { - self - } else if other > self { - other - } else if self == other { - if self.is_sign_negative() && other.is_sign_positive() { other } else { self } - } else { - // At least one input is NaN. Use `+` to perform NaN propagation and quieting. - self + other + match self.partial_cmp(&other) { + Some(core::cmp::Ordering::Greater) => self, + Some(core::cmp::Ordering::Less) => other, + Some(core::cmp::Ordering::Equal) => if self.is_sign_negative() && other.is_sign_positive() { other } else { self }, + None => self + other, // At least one input is NaN. Use `+` to perform NaN propagation and quieting. } } } diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index 14ad050..12270d1 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -143,7 +143,7 @@ impl CallFrame { self.locals[local_index] } - #[inline] + #[inline(always)] pub(crate) fn instructions(&self) -> &[Instruction] { &self.func_instance.0.instructions } diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index c6d7918..5903228 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -34,6 +34,16 @@ impl ValueStack { } #[inline] + pub(crate) fn replace_top(&mut self, func: impl FnOnce(RawWasmValue) -> RawWasmValue) { + let len = self.stack.len(); + if unlikely(len == 0) { + return; + } + let top = self.stack[len - 1]; + self.stack[len - 1] = func(top); + } + + #[inline] pub(crate) fn len(&self) -> usize { self.stack.len() } @@ -53,7 +63,7 @@ impl ValueStack { self.stack.drain(remove_start_index..remove_end_index); } - #[inline] + #[inline(always)] pub(crate) fn push(&mut self, value: RawWasmValue) { self.stack.push(value); } diff --git a/crates/tinywasm/src/store/mod.rs b/crates/tinywasm/src/store/mod.rs index a3d99fe..f4e0df9 100644 --- a/crates/tinywasm/src/store/mod.rs +++ b/crates/tinywasm/src/store/mod.rs @@ -172,11 +172,8 @@ impl Store { /// Set the global at the actual index in the store #[inline] pub(crate) fn set_global_val(&mut self, addr: usize, value: RawWasmValue) -> Result<()> { - self.data - .globals - .get(addr) - .ok_or_else(|| Self::not_found_error("global")) - .map(|global| global.borrow_mut().value = value) + let global = self.data.globals.get(addr).ok_or_else(|| Self::not_found_error("global")); + global.map(|global| global.borrow_mut().value = value) } } @@ -190,12 +187,10 @@ impl Store { ) -> Result<Vec<FuncAddr>> { let func_count = self.data.funcs.len(); let mut func_addrs = Vec::with_capacity(func_count); - for (i, func) in funcs.into_iter().enumerate() { self.data.funcs.push(FunctionInstance::new_wasm(func.wasm_function, idx)); func_addrs.push((i + func_count) as FuncAddr); } - Ok(func_addrs) } @@ -264,10 +259,9 @@ impl Store { let val = i64::from(global.borrow().value); // check if the global is actually a null reference - if val < 0 { - None - } else { - Some(val as u32) + match val < 0 { + true => None, + false => Some(val as u32), } } _ => return Err(Error::UnsupportedFeature(format!("const expression other than ref: {:?}", item))), @@ -300,10 +294,7 @@ impl Store { ElementKind::Passive => Some(init), // this one is not available to the runtime but needs to be initialized to declare references - ElementKind::Declared => { - // a. Execute the instruction elm.drop i - None - } + ElementKind::Declared => None, // a. Execute the instruction elm.drop i // this one is active, so we need to initialize it (essentially a `table.init` instruction) ElementKind::Active { offset, table } => { @@ -313,17 +304,17 @@ impl Store { .copied() .ok_or_else(|| Error::Other(format!("table {} not found for element {}", table, i)))?; - if let Some(table) = self.data.tables.get_mut(table_addr as usize) { - // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet, - // when using a partially initialized active element segments. - // This isn't mentioned in the spec, but the "unofficial" testsuite has a test for it: - // https://github.com/WebAssembly/testsuite/blob/5a1a590603d81f40ef471abba70a90a9ae5f4627/linking.wast#L264-L276 - // I have NO IDEA why this is allowed, but it is. - if let Err(Error::Trap(trap)) = table.borrow_mut().init_raw(offset, &init) { - return Ok((elem_addrs.into_boxed_slice(), Some(trap))); - } - } else { + let Some(table) = self.data.tables.get_mut(table_addr as usize) else { return Err(Error::Other(format!("table {} not found for element {}", table, i))); + }; + + // In wasm 2.0, it's possible to call a function that hasn't been instantiated yet, + // when using a partially initialized active element segments. + // This isn't mentioned in the spec, but the "unofficial" testsuite has a test for it: + // https://github.com/WebAssembly/testsuite/blob/5a1a590603d81f40ef471abba70a90a9ae5f4627/linking.wast#L264-L276 + // I have NO IDEA why this is allowed, but it is. + if let Err(Error::Trap(trap)) = table.borrow_mut().init_raw(offset, &init) { + return Ok((elem_addrs.into_boxed_slice(), Some(trap))); } // f. Execute the instruction elm.drop i @@ -356,26 +347,20 @@ impl Store { return Err(Error::UnsupportedFeature("data segments for non-zero memories".to_string())); } - let mem_addr = mem_addrs - .get(mem_addr as usize) - .copied() - .ok_or_else(|| Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)))?; + let Some(mem_addr) = mem_addrs.get(mem_addr as usize) else { + return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))); + }; let offset = self.eval_i32_const(&offset)?; + let Some(mem) = self.data.memories.get_mut(*mem_addr as usize) else { + return Err(Error::Other(format!("memory {} not found for data segment {}", mem_addr, i))); + }; - let mem = - self.data.memories.get_mut(mem_addr as usize).ok_or_else(|| { - Error::Other(format!("memory {} not found for data segment {}", mem_addr, i)) - })?; - - // See comment for active element sections in the function above why we need to do this here - if let Err(Error::Trap(trap)) = mem.borrow_mut().store(offset as usize, data.data.len(), &data.data) - { - return Ok((data_addrs.into_boxed_slice(), Some(trap))); + match mem.borrow_mut().store(offset as usize, data.data.len(), &data.data) { + Ok(()) => None, + Err(Error::Trap(trap)) => return Ok((data_addrs.into_boxed_slice(), Some(trap))), + Err(e) => return Err(e), } - - // drop the data - None } tinywasm_types::DataKind::Passive => Some(data.data.to_vec()), }; @@ -417,10 +402,8 @@ impl Store { let val = match const_instr { I32Const(i) => *i, GlobalGet(addr) => { - let addr = *addr as usize; - let global = self.data.globals[addr].clone(); - let val = global.borrow().value; - i32::from(val) + let global = self.data.globals[*addr as usize].borrow(); + i32::from(global.value) } _ => return Err(Error::Other("expected i32".to_string())), }; @@ -441,17 +424,17 @@ impl Store { I32Const(i) => RawWasmValue::from(*i), I64Const(i) => RawWasmValue::from(*i), GlobalGet(addr) => { - let addr = module_global_addrs.get(*addr as usize).copied().ok_or_else(|| { + let addr = module_global_addrs.get(*addr as usize).ok_or_else(|| { Error::Other(format!("global {} not found. This should have been caught by the validator", addr)) })?; let global = - self.data.globals.get(addr as usize).expect("global not found. This should be unreachable"); + self.data.globals.get(*addr as usize).expect("global not found. This should be unreachable"); global.borrow().value } RefNull(t) => RawWasmValue::from(t.default_value()), - RefFunc(idx) => RawWasmValue::from(module_func_addrs.get(*idx as usize).copied().ok_or_else(|| { + RefFunc(idx) => RawWasmValue::from(*module_func_addrs.get(*idx as usize).ok_or_else(|| { Error::Other(format!("function {} not found. This should have been caught by the validator", idx)) })?), }; diff --git a/crates/tinywasm/src/store/table.rs b/crates/tinywasm/src/store/table.rs index 1b31999..d9dd9ad 100644 --- a/crates/tinywasm/src/store/table.rs +++ b/crates/tinywasm/src/store/table.rs @@ -1,4 +1,4 @@ -use crate::log; +use crate::{log, unlikely}; use crate::{Error, Result, Trap}; use alloc::{vec, vec::Vec}; use tinywasm_types::*; @@ -40,7 +40,7 @@ impl TableInstance { pub(crate) fn grow_to_fit(&mut self, new_size: usize) -> Result<()> { if new_size > self.elements.len() { - if new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize { + if unlikely(new_size > self.kind.size_max.unwrap_or(MAX_TABLE_SIZE) as usize) { return Err(crate::Trap::TableOutOfBounds { offset: new_size, len: 1, max: self.elements.len() }.into()); } diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 537b8d7..9923116 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -94,7 +94,7 @@ pub enum Instruction { // LocalGet + I32Const + I32Store => I32LocalGetConstStore + I32Const // Also common, helps us skip the stack entirely. // Has to be followed by an I32Const instruction - // I32LocalGetConstStore { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I32Store + LocalGet + I32Const + // I32StoreLocal { local: LocalAddr, offset: i32, mem_addr: MemAddr }, // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index df062ce..bcd43e5 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -142,7 +142,7 @@ impl ValType { WasmValue::default_for(*self) } - pub(crate) fn to_byte(&self) -> u8 { + pub(crate) fn to_byte(self) -> u8 { match self { ValType::I32 => 0x7F, ValType::I64 => 0x7E, |
