From 5e573f0765570e0aaf6c9415b652b25d5bddebd9 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 00:17:09 +0100 Subject: no more simd Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/fibonacci.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'crates/benchmarks/benches') diff --git a/crates/benchmarks/benches/fibonacci.rs b/crates/benchmarks/benches/fibonacci.rs index 38bbde9..8a4dab2 100644 --- a/crates/benchmarks/benches/fibonacci.rs +++ b/crates/benchmarks/benches/fibonacci.rs @@ -17,10 +17,10 @@ fn run_wasmi(wasm: &[u8], iterations: i32, name: &str) { fn run_wasmer(wasm: &[u8], iterations: i32, name: &str) { use wasmer::*; - let engine: Engine = wasmer::Singlepass::default().into(); - let mut store = Store::default(); + let compiler = wasmer::Singlepass::default(); + let mut store = Store::new(compiler); let import_object = imports! {}; - let module = wasmer::Module::from_binary(&engine, wasm).expect("wasmer::Module::from_binary"); + let module = wasmer::Module::from_binary(&store, wasm).expect("wasmer::Module::from_binary"); let instance = Instance::new(&mut store, &module, &import_object).expect("Instance::new"); let fib = instance.exports.get_typed_function::(&store, name).expect("get_function"); fib.call(&mut store, iterations).expect("call"); -- cgit v1.3.1 From e883003b7436708e91d0971b1a8e5f74c8166261 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 01:04:34 +0100 Subject: some new instructuons Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/selfhosted.rs | 6 ++-- crates/parser/src/visit.rs | 35 ++++++++++++------------ crates/tinywasm/src/runtime/interpreter/mod.rs | 33 +++++++++++++--------- crates/tinywasm/src/runtime/stack/value_stack.rs | 9 +++--- 4 files changed, 45 insertions(+), 38 deletions(-) (limited to 'crates/benchmarks/benches') diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 94dfdce..1396fd1 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -61,10 +61,10 @@ fn criterion_benchmark(c: &mut Criterion) { { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - group.bench_function("native", |b| b.iter(run_native)); + // group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 9038567..f994e7e 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -325,10 +325,10 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { fn visit_local_get(&mut self, idx: u32) -> Self::Output { if let Some(instruction) = self.instructions.last_mut() { match instruction { - // Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), - // Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), - // Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), - // Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), + Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), + Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), + Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), + Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), _ => return self.visit(Instruction::LocalGet(idx)), }; Ok(()) @@ -338,16 +338,15 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } fn visit_local_set(&mut self, idx: u32) -> Self::Output { - // LocalGetSet - if let Some(instruction) = self.instructions.last_mut() { - match instruction { - // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), - _ => return self.visit(Instruction::LocalSet(idx)), - }; - Ok(()) - } else { - self.visit(Instruction::LocalSet(idx)) + if self.instructions.len() < 1 { + return self.visit(Instruction::I64Rotl); } + + // LocalGetSet + match self.instructions[self.instructions.len() - 1..] { + // Instruction::LocalGet(a) => *instruction = Instruction::LocalGetSet(*a, idx), + _ => return self.visit(Instruction::LocalSet(idx)), + }; } fn visit_local_tee(&mut self, idx: u32) -> Self::Output { @@ -360,11 +359,11 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } match self.instructions[self.instructions.len() - 2..] { - // [Instruction::I64Xor, Instruction::I64Const(a)] => { - // self.instructions.pop(); - // self.instructions.pop(); - // self.visit(Instruction::I64XorConstRotl(a)) - // } + [Instruction::I64Xor, Instruction::I64Const(a)] => { + self.instructions.pop(); + self.instructions.pop(); + self.visit(Instruction::I64XorConstRotl(a)) + } _ => self.visit(Instruction::I64Rotl), } } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 57e5f34..91d1859 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -680,19 +680,22 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // custom instructions LocalGet2(a, b) => { - stack.values.push(cf.get_local(a as usize)); - stack.values.push(cf.get_local(b as usize)); + stack.values.extend_from_slice(&[cf.get_local(a as usize), cf.get_local(b as usize)]); } LocalGet3(a, b, c) => { - stack.values.push(cf.get_local(a as usize)); - stack.values.push(cf.get_local(b as usize)); - stack.values.push(cf.get_local(c as usize)); + stack.values.extend_from_slice(&[ + cf.get_local(a as usize), + cf.get_local(b as usize), + cf.get_local(c as usize), + ]); } LocalGet4(a, b, c, d) => { - stack.values.push(cf.get_local(a as usize)); - stack.values.push(cf.get_local(b as usize)); - stack.values.push(cf.get_local(c as usize)); - stack.values.push(cf.get_local(d as usize)); + stack.values.extend_from_slice(&[ + cf.get_local(a as usize), + cf.get_local(b as usize), + cf.get_local(c as usize), + cf.get_local(d as usize), + ]); } LocalTeeGet(a, b) => { let last = @@ -701,10 +704,14 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.push(cf.get_local(b as usize)); } - // LocalTeeGet - // LocalGetSet - // I64XorConstRotl - // I32LocalGetConstAdd + // I64Xor + I64Const + I64RotL + I64XorConstRotl(rotate_by) => { + let val = stack.values.pop_t::()?; + let mask = stack.values.pop_t::()?; + let res = val ^ mask; + stack.values.push(res.rotate_left(rotate_by as u32).into()); + } + i => { cold(); log::error!("unimplemented instruction: {:?}", i); diff --git a/crates/tinywasm/src/runtime/stack/value_stack.rs b/crates/tinywasm/src/runtime/stack/value_stack.rs index be5b53b..c6d7918 100644 --- a/crates/tinywasm/src/runtime/stack/value_stack.rs +++ b/crates/tinywasm/src/runtime/stack/value_stack.rs @@ -25,13 +25,14 @@ impl ValueStack { #[inline] pub(crate) fn extend_from_typed(&mut self, values: &[WasmValue]) { - if values.is_empty() { - return; - } - self.stack.extend(values.iter().map(|v| RawWasmValue::from(*v))); } + #[inline] + pub(crate) fn extend_from_slice(&mut self, values: &[RawWasmValue]) { + self.stack.extend_from_slice(values); + } + #[inline] pub(crate) fn len(&self) -> usize { self.stack.len() -- cgit v1.3.1 From 4faaf5bb222947a95b854d733a3cbe3bf588b597 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 14:35:40 +0100 Subject: reduce instruction enum size even more Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/selfhosted.rs | 6 +- crates/parser/src/visit.rs | 79 ++++++---- crates/tinywasm/src/runtime/interpreter/macros.rs | 27 ++-- crates/tinywasm/src/runtime/interpreter/mod.rs | 179 +++++++++++----------- crates/tinywasm/src/runtime/stack/call_stack.rs | 4 +- crates/types/src/archive.rs | 2 - crates/types/src/instructions.rs | 156 ++++++++++++++----- crates/types/src/value.rs | 23 +++ 8 files changed, 296 insertions(+), 180 deletions(-) (limited to 'crates/benchmarks/benches') diff --git a/crates/benchmarks/benches/selfhosted.rs b/crates/benchmarks/benches/selfhosted.rs index 1396fd1..94dfdce 100644 --- a/crates/benchmarks/benches/selfhosted.rs +++ b/crates/benchmarks/benches/selfhosted.rs @@ -61,10 +61,10 @@ fn criterion_benchmark(c: &mut Criterion) { { let twasm = util::wasm_to_twasm(TINYWASM); let mut group = c.benchmark_group("selfhosted"); - // group.bench_function("native", |b| b.iter(run_native)); + group.bench_function("native", |b| b.iter(run_native)); group.bench_function("tinywasm", |b| b.iter(|| run_tinywasm(&twasm))); - // group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); - // group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); + group.bench_function("wasmi", |b| b.iter(|| run_wasmi(TINYWASM))); + group.bench_function("wasmer", |b| b.iter(|| run_wasmer(TINYWASM))); } } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 3a10a93..edebc97 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -3,7 +3,7 @@ use crate::{conversion::convert_blocktype, Result}; use crate::conversion::{convert_heaptype, convert_memarg, convert_valtype}; use alloc::string::ToString; use alloc::{boxed::Box, format, vec::Vec}; -use tinywasm_types::Instruction; +use tinywasm_types::{BlockArgsPacked, Instruction}; use wasmparser::{FuncValidator, FunctionBody, VisitOperator, WasmModuleResources}; struct ValidateThenVisit<'a, T, U>(T, &'a mut U); @@ -74,12 +74,14 @@ macro_rules! define_primitive_operands { } macro_rules! define_mem_operands { - ($($name:ident, $instr:expr),*) => { + ($($name:ident, $instr:ident),*) => { $( fn $name(&mut self, mem_arg: wasmparser::MemArg) -> Self::Output { - self.instructions.push($instr( - convert_memarg(mem_arg) - )); + let arg = convert_memarg(mem_arg); + self.instructions.push(Instruction::$instr { + offset: arg.offset, + mem_addr: arg.mem_addr, + }); Ok(()) } )* @@ -149,29 +151,29 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { } define_mem_operands! { - visit_i32_load, Instruction::I32Load, - visit_i64_load, Instruction::I64Load, - visit_f32_load, Instruction::F32Load, - visit_f64_load, Instruction::F64Load, - visit_i32_load8_s, Instruction::I32Load8S, - visit_i32_load8_u, Instruction::I32Load8U, - visit_i32_load16_s, Instruction::I32Load16S, - visit_i32_load16_u, Instruction::I32Load16U, - visit_i64_load8_s, Instruction::I64Load8S, - visit_i64_load8_u, Instruction::I64Load8U, - visit_i64_load16_s, Instruction::I64Load16S, - visit_i64_load16_u, Instruction::I64Load16U, - visit_i64_load32_s, Instruction::I64Load32S, - visit_i64_load32_u, Instruction::I64Load32U, - visit_i32_store, Instruction::I32Store, - visit_i64_store, Instruction::I64Store, - visit_f32_store, Instruction::F32Store, - visit_f64_store, Instruction::F64Store, - visit_i32_store8, Instruction::I32Store8, - visit_i32_store16, Instruction::I32Store16, - visit_i64_store8, Instruction::I64Store8, - visit_i64_store16, Instruction::I64Store16, - visit_i64_store32, Instruction::I64Store32 + visit_i32_load, I32Load, + visit_i64_load, I64Load, + visit_f32_load, F32Load, + visit_f64_load, F64Load, + visit_i32_load8_s, I32Load8S, + visit_i32_load8_u, I32Load8U, + visit_i32_load16_s, I32Load16S, + visit_i32_load16_u, I32Load16U, + visit_i64_load8_s, I64Load8S, + visit_i64_load8_u, I64Load8U, + visit_i64_load16_s, I64Load16S, + visit_i64_load16_u, I64Load16U, + visit_i64_load32_s, I64Load32S, + visit_i64_load32_u, I64Load32U, + visit_i32_store, I32Store, + visit_i64_store, I64Store, + visit_f32_store, F32Store, + visit_f64_store, F64Store, + visit_i32_store8, I32Store8, + visit_i32_store16, I32Store16, + visit_i64_store8, I64Store8, + visit_i64_store16, I64Store16, + visit_i64_store32, I64Store32 } define_operands! { @@ -327,7 +329,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { match instruction { Instruction::LocalGet(a) => *instruction = Instruction::LocalGet2(*a, idx), Instruction::LocalGet2(a, b) => *instruction = Instruction::LocalGet3(*a, *b, idx), - Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), + // Instruction::LocalGet3(a, b, c) => *instruction = Instruction::LocalGet4(*a, *b, *c, idx), Instruction::LocalTee(a) => *instruction = Instruction::LocalTeeGet(*a, idx), _ => return self.visit(Instruction::LocalGet(idx)), }; @@ -396,7 +398,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { fn visit_if(&mut self, ty: wasmparser::BlockType) -> Self::Output { self.label_ptrs.push(self.instructions.len()); - self.visit(Instruction::If(convert_blocktype(ty), None, 0)) + self.visit(Instruction::If(BlockArgsPacked::new(convert_blocktype(ty)), 0, 0)) } fn visit_else(&mut self) -> Self::Output { @@ -414,7 +416,9 @@ 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) as u32; + *else_instr_end_offset = (current_instr_ptr - label_pointer as usize) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support if blocks that large"); #[cold] fn error() -> crate::ParseError { @@ -431,13 +435,20 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder { return Err(error()); }; - *else_offset = Some((label_pointer - if_label_pointer) as u32); - *end_offset = (current_instr_ptr - if_label_pointer) as u32; + *else_offset = (label_pointer - if_label_pointer) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); + + *end_offset = (current_instr_ptr - if_label_pointer) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); } Instruction::Block(_, ref mut end_offset) | Instruction::Loop(_, ref mut end_offset) | Instruction::If(_, _, ref mut end_offset) => { - *end_offset = (current_instr_ptr - label_pointer) as u32; + *end_offset = (current_instr_ptr - label_pointer) + .try_into() + .expect("else_instr_end_offset is too large, tinywasm does not support blocks that large"); } _ => { return Err(crate::ParseError::UnsupportedOperator( diff --git a/crates/tinywasm/src/runtime/interpreter/macros.rs b/crates/tinywasm/src/runtime/interpreter/macros.rs index a13531b..9227ceb 100644 --- a/crates/tinywasm/src/runtime/interpreter/macros.rs +++ b/crates/tinywasm/src/runtime/interpreter/macros.rs @@ -11,7 +11,7 @@ // from a function, so we need to check if the label stack is empty 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 $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 { @@ -23,20 +23,22 @@ macro_rules! break_to { /// Load a value from memory macro_rules! mem_load { - ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ + ($type:ty, $arg:expr, $stack:ident, $store:ident, $module:ident) => {{ mem_load!($type, $type, $arg, $stack, $store, $module) }}; - ($load_type:ty, $target_type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ - let mem_idx = $module.resolve_mem_addr($arg.mem_addr); + ($load_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 mem_ref = mem.borrow_mut(); let addr: u64 = $stack.values.pop()?.into(); - let addr = $arg.offset.checked_add(addr).ok_or_else(|| { + let addr = offset.checked_add(addr).ok_or_else(|| { cold(); Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: $arg.offset as usize, + offset: *offset as usize, len: core::mem::size_of::<$load_type>(), max: mem_ref.max_pages(), }) @@ -45,7 +47,7 @@ macro_rules! mem_load { let addr: usize = addr.try_into().ok().ok_or_else(|| { cold(); Error::Trap(crate::Trap::MemoryOutOfBounds { - offset: $arg.offset as usize, + offset: *offset as usize, len: core::mem::size_of::<$load_type>(), max: mem_ref.max_pages(), }) @@ -59,15 +61,14 @@ macro_rules! mem_load { /// Store a value to memory macro_rules! mem_store { - ($type:ty, $arg:ident, $stack:ident, $store:ident, $module:ident) => {{ + ($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:ident, $stack:ident, $store:ident, $module:ident) => {{ - // likewise, there could be a lot of performance improvements here - let mem_idx = $module.resolve_mem_addr($arg.mem_addr); + ($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>()?; @@ -76,7 +77,7 @@ macro_rules! mem_store { let val = val as $store_type; let val = val.to_le_bytes(); - mem.borrow_mut().store(($arg.offset + addr) as usize, val.len(), &val)?; + mem.borrow_mut().store((*offset + addr) as usize, val.len(), &val)?; }}; } diff --git a/crates/tinywasm/src/runtime/interpreter/mod.rs b/crates/tinywasm/src/runtime/interpreter/mod.rs index 45e10b7..bd07051 100644 --- a/crates/tinywasm/src/runtime/interpreter/mod.rs +++ b/crates/tinywasm/src/runtime/interpreter/mod.rs @@ -113,7 +113,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M Call(v) => { // prepare the call frame - let func_idx = module.resolve_func_addr(v); + let func_idx = module.resolve_func_addr(*v); let func_inst = store.get_func(func_idx as usize)?.clone(); let wasm_func = match &func_inst.func { @@ -140,7 +140,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } CallIndirect(type_addr, table_addr) => { - let table = store.get_table(module.resolve_table_addr(table_addr) as usize)?; + let table = store.get_table(module.resolve_table_addr(*table_addr) as usize)?; let table_idx = stack.values.pop_t::()?; // verify that the table is of the right type, this should be validated by the parser already @@ -155,7 +155,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }; let func_inst = store.get_func(func_ref as usize)?.clone(); - let call_ty = module.func_ty(type_addr); + let call_ty = module.func_ty(*type_addr); let wasm_func = match func_inst.func { crate::Function::Wasm(ref f) => f.clone(), @@ -202,10 +202,10 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.enter_block( BlockFrame::new( cf.instr_ptr, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::If, - &args, + &args.unpack(), module, ), &mut stack.values, @@ -215,19 +215,19 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } // falsy value is on the top of the stack - if let Some(else_offset) = else_offset { + if *else_offset != 0 { let label = BlockFrame::new( - cf.instr_ptr + else_offset as usize, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *else_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Else, - &args, + &args.unpack(), module, ); - cf.instr_ptr += else_offset as usize; + cf.instr_ptr += *else_offset as usize; cf.enter_block(label, &mut stack.values, &mut stack.blocks); } else { - cf.instr_ptr += end_offset as usize; + cf.instr_ptr += *end_offset as usize; } } @@ -235,7 +235,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.enter_block( BlockFrame::new( cf.instr_ptr, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), BlockType::Loop, &args, @@ -250,7 +250,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M cf.enter_block( BlockFrame::new( cf.instr_ptr, - cf.instr_ptr + end_offset as usize, + cf.instr_ptr + *end_offset as usize, stack.values.len(), // - params, BlockType::Block, &args, @@ -262,7 +262,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } BrTable(default, len) => { - let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + len as usize] + let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len as usize] .iter() .map(|i| match i { BrLabel(l) => Ok(*l), @@ -273,7 +273,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M }) .collect::>>()?; - if unlikely(instr.len() != len as usize) { + if unlikely(instr.len() != *len as usize) { panic!( "Expected {} BrLabel instructions, got {}, this should have been validated by the parser", len, @@ -282,7 +282,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } let idx = stack.values.pop_t::()? as usize; - let to = *instr.get(idx).unwrap_or(&default); + let to = instr.get(idx).unwrap_or(&default); break_to!(cf, stack, to); } @@ -319,7 +319,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let res_count = block.results; stack.values.truncate_keep(block.stack_ptr, res_count); - cf.instr_ptr += end_offset as usize; + cf.instr_ptr += *end_offset as usize; } EndBlockFrame => { @@ -332,11 +332,11 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M stack.values.truncate_keep(block.stack_ptr, block.results); } - 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()?), + 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) => { cf.set_local( - local_index as usize, + *local_index as usize, stack .values .last() @@ -346,39 +346,39 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } GlobalGet(global_index) => { - let idx = module.resolve_global_addr(global_index); + let idx = module.resolve_global_addr(*global_index); let global = store.get_global_val(idx as usize)?; stack.values.push(global); } GlobalSet(global_index) => { - let idx = module.resolve_global_addr(global_index); + let idx = module.resolve_global_addr(*global_index); store.set_global_val(idx as usize, stack.values.pop()?)?; } - I32Const(val) => stack.values.push((val).into()), - I64Const(val) => stack.values.push((val).into()), - F32Const(val) => stack.values.push((val).into()), - F64Const(val) => stack.values.push((val).into()), + I32Const(val) => stack.values.push((*val).into()), + I64Const(val) => stack.values.push((*val).into()), + F32Const(val) => stack.values.push((*val).into()), + F64Const(val) => stack.values.push((*val).into()), MemorySize(addr, byte) => { - if byte != 0 { + if *byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.size with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(addr); + let mem_idx = module.resolve_mem_addr(*addr); let mem = store.get_mem(mem_idx as usize)?; stack.values.push((mem.borrow().page_count() as i32).into()); } MemoryGrow(addr, byte) => { - if byte != 0 { + if *byte != 0 { cold(); return Err(Error::UnsupportedFeature("memory.grow with byte != 0".to_string())); } - let mem_idx = module.resolve_mem_addr(addr); + let mem_idx = module.resolve_mem_addr(*addr); let mem = store.get_mem(mem_idx as usize)?; let (res, prev_size) = { @@ -399,7 +399,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let src = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - let mem = store.get_mem(module.resolve_mem_addr(from) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(*from) as usize)?; let mut mem = mem.borrow_mut(); if from == to { @@ -407,7 +407,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M mem.copy_within(dst as usize, src as usize, size as usize)?; } else { // copy between two memories - let mem2 = store.get_mem(module.resolve_mem_addr(to) as usize)?; + let mem2 = store.get_mem(module.resolve_mem_addr(*to) as usize)?; let mut mem2 = mem2.borrow_mut(); mem2.copy_from_slice(dst as usize, mem.load(src as usize, size as usize)?)?; } @@ -418,7 +418,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let val = stack.values.pop_t::()?; let dst = stack.values.pop_t::()?; - let mem = store.get_mem(module.resolve_mem_addr(addr) as usize)?; + let mem = store.get_mem(module.resolve_mem_addr(*addr) as usize)?; let mut mem = mem.borrow_mut(); mem.fill(dst as usize, size as usize, val as u8)?; } @@ -428,13 +428,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M let offset = stack.values.pop_t::()? as usize; let dst = stack.values.pop_t::()? as usize; - let data_idx = module.resolve_data_addr(data_index); + 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 mem_idx = module.resolve_mem_addr(mem_index); + let mem_idx = module.resolve_mem_addr(*mem_index); let mem = store.get_mem(mem_idx as usize)?; let data_len = data.len(); @@ -451,35 +451,35 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } DataDrop(data_index) => { - let data_idx = module.resolve_data_addr(data_index); + let data_idx = module.resolve_data_addr(*data_index); let data = store.get_data_mut(data_idx as usize)?; data.drop(); } - I32Store(arg) => mem_store!(i32, arg, stack, store, module), - I64Store(arg) => mem_store!(i64, arg, stack, store, module), - F32Store(arg) => mem_store!(f32, arg, stack, store, module), - F64Store(arg) => mem_store!(f64, arg, stack, store, module), - I32Store8(arg) => mem_store!(i8, i32, arg, stack, store, module), - I32Store16(arg) => mem_store!(i16, i32, arg, stack, store, module), - I64Store8(arg) => mem_store!(i8, i64, arg, stack, store, module), - I64Store16(arg) => mem_store!(i16, i64, arg, stack, store, module), - I64Store32(arg) => mem_store!(i32, i64, arg, stack, store, module), - - I32Load(arg) => mem_load!(i32, arg, stack, store, module), - I64Load(arg) => mem_load!(i64, arg, stack, store, module), - F32Load(arg) => mem_load!(f32, arg, stack, store, module), - F64Load(arg) => mem_load!(f64, arg, stack, store, module), - I32Load8S(arg) => mem_load!(i8, i32, arg, stack, store, module), - I32Load8U(arg) => mem_load!(u8, i32, arg, stack, store, module), - I32Load16S(arg) => mem_load!(i16, i32, arg, stack, store, module), - I32Load16U(arg) => mem_load!(u16, i32, arg, stack, store, module), - I64Load8S(arg) => mem_load!(i8, i64, arg, stack, store, module), - I64Load8U(arg) => mem_load!(u8, i64, arg, stack, store, module), - I64Load16S(arg) => mem_load!(i16, i64, arg, stack, store, module), - I64Load16U(arg) => mem_load!(u16, i64, arg, stack, store, module), - I64Load32S(arg) => mem_load!(i32, i64, arg, stack, store, module), - I64Load32U(arg) => mem_load!(u32, i64, arg, stack, store, module), + I32Store { mem_addr, offset } => mem_store!(i32, (mem_addr, offset), stack, store, module), + I64Store { mem_addr, offset } => mem_store!(i64, (mem_addr, offset), stack, store, module), + F32Store { mem_addr, offset } => mem_store!(f32, (mem_addr, offset), stack, store, module), + F64Store { mem_addr, offset } => mem_store!(f64, (mem_addr, offset), stack, store, module), + I32Store8 { mem_addr, offset } => mem_store!(i8, i32, (mem_addr, offset), stack, store, module), + I32Store16 { mem_addr, offset } => mem_store!(i16, i32, (mem_addr, offset), stack, store, module), + I64Store8 { mem_addr, offset } => mem_store!(i8, i64, (mem_addr, offset), stack, store, module), + I64Store16 { mem_addr, offset } => mem_store!(i16, i64, (mem_addr, offset), stack, store, module), + I64Store32 { mem_addr, offset } => mem_store!(i32, i64, (mem_addr, offset), stack, store, module), + + I32Load { mem_addr, offset } => mem_load!(i32, (mem_addr, offset), stack, store, module), + I64Load { mem_addr, offset } => mem_load!(i64, (mem_addr, offset), stack, store, module), + F32Load { mem_addr, offset } => mem_load!(f32, (mem_addr, offset), stack, store, module), + F64Load { mem_addr, offset } => mem_load!(f64, (mem_addr, offset), stack, store, module), + I32Load8S { mem_addr, offset } => mem_load!(i8, i32, (mem_addr, offset), stack, store, module), + I32Load8U { mem_addr, offset } => mem_load!(u8, i32, (mem_addr, offset), stack, store, module), + I32Load16S { mem_addr, offset } => mem_load!(i16, i32, (mem_addr, offset), stack, store, module), + I32Load16U { mem_addr, offset } => mem_load!(u16, i32, (mem_addr, offset), stack, store, module), + I64Load8S { mem_addr, offset } => mem_load!(i8, i64, (mem_addr, offset), stack, store, module), + I64Load8U { mem_addr, offset } => mem_load!(u8, i64, (mem_addr, offset), stack, store, module), + I64Load16S { mem_addr, offset } => mem_load!(i16, i64, (mem_addr, offset), stack, store, module), + I64Load16U { mem_addr, offset } => mem_load!(u16, i64, (mem_addr, offset), stack, store, module), + I64Load32S { mem_addr, offset } => mem_load!(i32, i64, (mem_addr, offset), stack, store, module), + I64Load32U { mem_addr, offset } => mem_load!(u32, i64, (mem_addr, offset), stack, store, module), I64Eqz => comp_zero!(==, i64, stack), I32Eqz => comp_zero!(==, i32, stack), @@ -630,7 +630,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M I64TruncF64U => checked_conv_float!(f64, u64, i64, stack), TableGet(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; let idx = stack.values.pop_t::()? as usize; let v = table.borrow().get_wasm_val(idx)?; @@ -638,7 +638,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSet(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; let val = stack.values.pop_t::()?; let idx = stack.values.pop_t::()? as usize; @@ -646,16 +646,16 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M } TableSize(table_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; stack.values.push(table.borrow().size().into()); } TableInit(table_index, elem_index) => { - let table_idx = module.resolve_table_addr(table_index); + let table_idx = module.resolve_table_addr(*table_index); let table = store.get_table(table_idx as usize)?; - let elem_idx = module.resolve_elem_addr(elem_index); + let elem_idx = module.resolve_elem_addr(*elem_index); let elem = store.get_elem(elem_idx as usize)?; if let ElementKind::Passive = elem.kind { @@ -680,43 +680,46 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M // custom instructions LocalGet2(a, b) => { - stack.values.extend_from_slice(&[cf.get_local(a as usize), cf.get_local(b as usize)]); + stack.values.extend_from_slice(&[cf.get_local(*a as usize), cf.get_local(*b as usize)]); } LocalGet3(a, b, c) => { stack.values.extend_from_slice(&[ - cf.get_local(a as usize), - cf.get_local(b as usize), - cf.get_local(c as usize), - ]); - } - LocalGet4(a, b, c, d) => { - stack.values.extend_from_slice(&[ - cf.get_local(a as usize), - cf.get_local(b as usize), - cf.get_local(c as usize), - cf.get_local(d as usize), + cf.get_local(*a as usize), + cf.get_local(*b as usize), + cf.get_local(*c as usize), ]); } + // LocalGet4(a, b, c, d) => { + // stack.values.extend_from_slice(&[ + // cf.get_local(*a as usize), + // cf.get_local(*b as usize), + // cf.get_local(*c as usize), + // cf.get_local(*d as usize), + // ]); + // } LocalTeeGet(a, b) => { - let last = - *stack.values.last().expect("localtee: stack is empty. this should have been validated by the parser"); - cf.set_local(a as usize, last); - stack.values.push(cf.get_local(b as usize)); + #[inline] + fn local_tee_get(cf: &mut CallFrame, stack: &mut Stack, a: u32, b: u32) -> Result<()> { + let last = *stack + .values + .last() + .expect("localtee: stack is empty. this should have been validated by the parser"); + cf.set_local(a as usize, last); + stack.values.push(cf.get_local(b as usize)); + Ok(()) + } + local_tee_get(cf, stack, *a, *b)?; } - LocalGetSet(a, b) => { - let a = cf.get_local(a as usize); - cf.set_local(b as usize, a); + let a = cf.get_local(*a as usize); + cf.set_local(*b as usize, a); } - - // I64Xor + I64Const + I64RotL I64XorConstRotl(rotate_by) => { let val = stack.values.pop_t::()?; let mask = stack.values.pop_t::()?; let res = val ^ mask; - stack.values.push(res.rotate_left(rotate_by as u32).into()); + stack.values.push(res.rotate_left(*rotate_by as u32).into()); } - i => { cold(); log::error!("unimplemented instruction: {:?}", i); diff --git a/crates/tinywasm/src/runtime/stack/call_stack.rs b/crates/tinywasm/src/runtime/stack/call_stack.rs index dd80bb7..14ad050 100644 --- a/crates/tinywasm/src/runtime/stack/call_stack.rs +++ b/crates/tinywasm/src/runtime/stack/call_stack.rs @@ -149,7 +149,7 @@ impl CallFrame { } #[inline(always)] - pub(crate) fn current_instruction(&self) -> Instruction { - self.func_instance.0.instructions[self.instr_ptr] + pub(crate) fn current_instruction(&self) -> &Instruction { + &self.func_instance.0.instructions[self.instr_ptr] } } diff --git a/crates/types/src/archive.rs b/crates/types/src/archive.rs index bbd2206..273d6ed 100644 --- a/crates/types/src/archive.rs +++ b/crates/types/src/archive.rs @@ -7,10 +7,8 @@ use rkyv::{ Deserialize, }; -// 16 bytes const TWASM_MAGIC_PREFIX: &[u8; 4] = b"TWAS"; const TWASM_VERSION: &[u8; 2] = b"01"; - #[rustfmt::skip] const TWASM_MAGIC: [u8; 16] = [ TWASM_MAGIC_PREFIX[0], TWASM_MAGIC_PREFIX[1], TWASM_MAGIC_PREFIX[2], TWASM_MAGIC_PREFIX[3], TWASM_VERSION[0], TWASM_VERSION[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index a19b4d2..537b8d7 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -9,14 +9,44 @@ pub enum BlockArgs { FuncType(u32), } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] +/// A packed representation of BlockArgs +/// This is needed to keep the size of the Instruction enum small. +/// Sadly, using #[repr(u8)] on BlockArgs itself is not possible because of the FuncType variant. +pub struct BlockArgsPacked([u8; 5]); // Modifying this directly can cause runtime errors, but no UB +impl BlockArgsPacked { + pub fn new(args: BlockArgs) -> Self { + let mut packed = [0; 5]; + match args { + BlockArgs::Empty => packed[0] = 0, + BlockArgs::Type(t) => { + packed[0] = 1; + packed[1] = t.to_byte(); + } + BlockArgs::FuncType(t) => { + packed[0] = 2; + packed[1..].copy_from_slice(&t.to_le_bytes()); + } + } + Self(packed) + } + pub fn unpack(&self) -> BlockArgs { + match self.0[0] { + 0 => BlockArgs::Empty, + 1 => BlockArgs::Type(ValType::from_byte(self.0[1]).unwrap()), + 2 => BlockArgs::FuncType(u32::from_le_bytes(self.0[1..].try_into().unwrap())), + _ => unreachable!(), + } + } +} + /// Represents a memory immediate in a WebAssembly memory instruction. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] pub struct MemoryArg { pub offset: u64, pub mem_addr: MemAddr, - // pub align: u8, - // pub align_max: u8, } type BrTableDefault = u32; @@ -48,21 +78,23 @@ pub enum ConstInstruction { /// This makes it easier to implement the label stack iteratively. /// /// See -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "archive", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize), archive(check_bytes))] +// should be kept as small as possible (16 bytes max) pub enum Instruction { // Custom Instructions BrLabel(LabelAddr), - //== Not implemented yet, to be determined - + // Not implemented yet // LocalGet + I32Const + I32Add // One of the most common patterns in the Rust compiler output - I32LocalGetConstAdd(LocalAddr, i32), + // I32LocalGetConstAdd(LocalAddr, i32), - // LocalGet + I32Const + I32Store - // Also common, helps us skip the stack entirely - // I32LocalGetConstStore(LocalAddr, i32, MemoryArg), // I32Store + LocalGet + I32Const + // Not implemented yet + // 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 // I64Xor + I64Const + I64RotL // Commonly used by a few crypto libraries @@ -72,13 +104,13 @@ pub enum Instruction { LocalTeeGet(LocalAddr, LocalAddr), LocalGet2(LocalAddr, LocalAddr), LocalGet3(LocalAddr, LocalAddr, LocalAddr), - LocalGet4(LocalAddr, LocalAddr, LocalAddr, LocalAddr), LocalGetSet(LocalAddr, LocalAddr), - I32AddConst(i32), - I32SubConst(i32), - I64AddConst(i64), - I64SubConst(i64), + // Not implemented yet + // I32AddConst(i32), + // I32SubConst(i32), + // I64AddConst(i64), + // I64SubConst(i64), // Control Instructions // See @@ -86,7 +118,7 @@ pub enum Instruction { Nop, Block(BlockArgs, EndOffset), Loop(BlockArgs, EndOffset), - If(BlockArgs, Option, EndOffset), + If(BlockArgsPacked, ElseOffset, EndOffset), // If else offset is 0 if there is no else block Else(EndOffset), EndBlockFrame, EndFunc, @@ -111,29 +143,29 @@ pub enum Instruction { GlobalSet(GlobalAddr), // Memory Instructions - I32Load(MemoryArg), - I64Load(MemoryArg), - F32Load(MemoryArg), - F64Load(MemoryArg), - I32Load8S(MemoryArg), - I32Load8U(MemoryArg), - I32Load16S(MemoryArg), - I32Load16U(MemoryArg), - I64Load8S(MemoryArg), - I64Load8U(MemoryArg), - I64Load16S(MemoryArg), - I64Load16U(MemoryArg), - I64Load32S(MemoryArg), - I64Load32U(MemoryArg), - I32Store(MemoryArg), - I64Store(MemoryArg), - F32Store(MemoryArg), - F64Store(MemoryArg), - I32Store8(MemoryArg), - I32Store16(MemoryArg), - I64Store8(MemoryArg), - I64Store16(MemoryArg), - I64Store32(MemoryArg), + I32Load { offset: u64, mem_addr: MemAddr }, + I64Load { offset: u64, mem_addr: MemAddr }, + F32Load { offset: u64, mem_addr: MemAddr }, + F64Load { offset: u64, mem_addr: MemAddr }, + I32Load8S { offset: u64, mem_addr: MemAddr }, + I32Load8U { offset: u64, mem_addr: MemAddr }, + I32Load16S { offset: u64, mem_addr: MemAddr }, + I32Load16U { offset: u64, mem_addr: MemAddr }, + I64Load8S { offset: u64, mem_addr: MemAddr }, + I64Load8U { offset: u64, mem_addr: MemAddr }, + I64Load16S { offset: u64, mem_addr: MemAddr }, + I64Load16U { offset: u64, mem_addr: MemAddr }, + I64Load32S { offset: u64, mem_addr: MemAddr }, + I64Load32U { offset: u64, mem_addr: MemAddr }, + I32Store { offset: u64, mem_addr: MemAddr }, + I64Store { offset: u64, mem_addr: MemAddr }, + F32Store { offset: u64, mem_addr: MemAddr }, + F64Store { offset: u64, mem_addr: MemAddr }, + I32Store8 { offset: u64, mem_addr: MemAddr }, + I32Store16 { offset: u64, mem_addr: MemAddr }, + I64Store8 { offset: u64, mem_addr: MemAddr }, + I64Store16 { offset: u64, mem_addr: MemAddr }, + I64Store32 { offset: u64, mem_addr: MemAddr }, MemorySize(MemAddr, u8), MemoryGrow(MemAddr, u8), @@ -302,3 +334,51 @@ pub enum Instruction { MemoryFill(MemAddr), DataDrop(DataAddr), } + +#[cfg(test)] +mod test_blockargs_packed { + use super::*; + + #[test] + fn test_empty() { + let args = BlockArgs::Empty; + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Empty); + } + + #[test] + fn test_val_type_i32() { + let args = BlockArgs::Type(ValType::I32); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::I32)); + } + + #[test] + fn test_val_type_i64() { + let args = BlockArgs::Type(ValType::I64); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::I64)); + } + + #[test] + fn test_val_type_f32() { + let args = BlockArgs::Type(ValType::F32); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::F32)); + } + + #[test] + fn test_val_type_f64() { + let args = BlockArgs::Type(ValType::F64); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::Type(ValType::F64)); + } + + #[test] + fn test_func_type() { + let func_type = 123; // Use an arbitrary u32 value + let args = BlockArgs::FuncType(func_type); + let packed = BlockArgsPacked::new(args); + assert_eq!(packed.unpack(), BlockArgs::FuncType(func_type)); + } +} diff --git a/crates/types/src/value.rs b/crates/types/src/value.rs index 8fd72fb..df062ce 100644 --- a/crates/types/src/value.rs +++ b/crates/types/src/value.rs @@ -141,6 +141,29 @@ impl ValType { pub fn default_value(&self) -> WasmValue { WasmValue::default_for(*self) } + + pub(crate) fn to_byte(&self) -> u8 { + match self { + ValType::I32 => 0x7F, + ValType::I64 => 0x7E, + ValType::F32 => 0x7D, + ValType::F64 => 0x7C, + ValType::RefFunc => 0x70, + ValType::RefExtern => 0x6F, + } + } + + pub(crate) fn from_byte(byte: u8) -> Option { + match byte { + 0x7F => Some(ValType::I32), + 0x7E => Some(ValType::I64), + 0x7D => Some(ValType::F32), + 0x7C => Some(ValType::F64), + 0x70 => Some(ValType::RefFunc), + 0x6F => Some(ValType::RefExtern), + _ => None, + } + } } macro_rules! impl_conversion_for_wasmvalue { -- cgit v1.3.1 From d2db1f7b3803c42619d91b85a66c12ace296bb21 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 16:08:14 +0100 Subject: chore: overall code cleanup Signed-off-by: Henry Gressmann --- crates/benchmarks/benches/argon2id.rs | 6 +- crates/parser/src/visit.rs | 46 +++++---- crates/tinywasm/src/func.rs | 73 +++++++------ crates/tinywasm/src/imports.rs | 1 - crates/tinywasm/src/runtime/interpreter/macros.rs | 79 +++++--------- crates/tinywasm/src/runtime/interpreter/mod.rs | 115 +++++++++------------ .../src/runtime/interpreter/no_std_floats.rs | 97 ++++------------- crates/tinywasm/src/runtime/interpreter/traits.rs | 44 +++----- crates/tinywasm/src/runtime/stack/call_stack.rs | 2 +- crates/tinywasm/src/runtime/stack/value_stack.rs | 12 ++- crates/tinywasm/src/store/mod.rs | 81 ++++++--------- crates/tinywasm/src/store/table.rs | 4 +- crates/types/src/instructions.rs | 2 +- crates/types/src/value.rs | 2 +- 14 files changed, 221 insertions(+), 343 deletions(-) (limited to 'crates/benchmarks/benches') 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 #[inline] pub fn call(&self, store: &mut Store, params: &[WasmValue]) -> Result> { + // 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 FuncHandleTyped { 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 ValTypesFromTuple for T1 -where - T1: ToValType, -{ +impl 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 { 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::()?; // 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::()? 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::()?; - let src = stack.values.pop_t::()?; - let dst = stack.values.pop_t::()?; + 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::()?; - let val = stack.values.pop_t::()?; - let dst = stack.values.pop_t::()?; + 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::()? as usize; let dst = stack.values.pop_t::()? 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 @@ -33,6 +33,16 @@ impl ValueStack { self.stack.extend_from_slice(values); } + #[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> { 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 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))); + 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))); + }; + + 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, -- cgit v1.3.1 From ef13c584654e243dfd4c1c9d3979a386c297dfd3 Mon Sep 17 00:00:00 2001 From: Henry Gressmann Date: Tue, 27 Feb 2024 16:42:18 +0100 Subject: chore: update benchmark results Signed-off-by: Henry Gressmann --- BENCHMARKS.md | 10 +++++----- crates/benchmarks/benches/argon2id.rs | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'crates/benchmarks/benches') diff --git a/BENCHMARKS.md b/BENCHMARKS.md index 5189b55..2cded9d 100644 --- a/BENCHMARKS.md +++ b/BENCHMARKS.md @@ -30,12 +30,12 @@ All runtimes are compiled with the following settings: | Benchmark | Native | TinyWasm\* | Wasmi | Wasmer (Single Pass) | | ------------ | -------- | ---------- | --------- | -------------------- | -| `fib` | \*\* | ` 43.81µs` | `48.60µs` | ` 43.97µs` | -| `fib-rec` | `0.26ms` | ` 20.99ms` | ` 4.64ms` | ` 0.50ms` | -| `argon2id` | `0.53ms` | `107.77ms` | `47.76ms` | ` 4.49ms` | -| `selfhosted` | `0.06ms` | ` 2.88ms` | ` 6.20ms` | `359.33ms` | +| `fib` | \*\* | ` 43.60µs` | `48.27µs` | ` 44.99µs` | +| `fib-rec` | `0.27ms` | ` 21.13ms` | ` 4.63ms` | ` 0.47ms` | +| `argon2id` | `0.53ms` | ` 99.16ms` | `45.00ms` | ` 4.59ms` | +| `selfhosted` | `0.05ms` | ` 1.84ms` | ` 6.51ms` | `446.48ms` | -_\* converting WASM to TinyWasm bytecode is not included. I takes ~7ms to convert `tinywasm.wasm` to TinyWasm bytecode._ +_\* converting WASM to TinyWasm bytecode is not included. I takes ~5.7ms to convert `tinywasm.wasm` to TinyWasm bytecode._ _\*\* essentially instant as it gets computed at compile time._ ### Fib diff --git a/crates/benchmarks/benches/argon2id.rs b/crates/benchmarks/benches/argon2id.rs index a503687..7c1ffc5 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!( -- cgit v1.3.1