diff options
| author | Henry <mail@henrygressmann.de> | 2026-04-25 18:30:53 +0200 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-04-25 18:30:53 +0200 |
| commit | 165b19813067140e3f594740affda804dbdb0564 (patch) | |
| tree | 0368fb2b1da998b24753537ccc5d77b85de6d4a0 /crates | |
| parent | e6691d035b6948452d6073317451b3f1ff0a24f8 (diff) | |
chore: add global bin op and acc superinstructions, cleanup executor
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/parser/src/lib.rs | 7 | ||||
| -rw-r--r-- | crates/parser/src/optimize.rs | 28 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 12 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 193 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/values.rs | 9 | ||||
| -rw-r--r-- | crates/tinywasm/tests/testsuite/util.rs | 4 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 12 | ||||
| -rw-r--r-- | crates/types/src/lib.rs | 13 |
8 files changed, 180 insertions, 98 deletions
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 684e79b..4eda84b 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -55,11 +55,7 @@ pub struct ParserOptions { impl Default for ParserOptions { fn default() -> Self { - Self { - optimize_local_memory_allocation: true, - optimize_rewrite: true, - optimize_remove_nop: true, - } + Self { optimize_local_memory_allocation: true, optimize_rewrite: true, optimize_remove_nop: true } } } @@ -96,7 +92,6 @@ impl ParserOptions { pub const fn optimize_remove_nop(&self) -> bool { self.optimize_remove_nop } - } /// A WebAssembly parser diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs index 19abd7c..a5b793e 100644 --- a/crates/parser/src/optimize.rs +++ b/crates/parser/src/optimize.rs @@ -17,12 +17,7 @@ pub(crate) fn optimize_instructions( track_local_memory_usage: bool, ) -> OptimizeResult { let uses_local_memory = if options.optimize_rewrite() { - rewrite( - &mut instructions, - self_func_addr, - imported_memory_count, - track_local_memory_usage, - ) + rewrite(&mut instructions, self_func_addr, imported_memory_count, track_local_memory_usage) } else { track_local_memory_usage && instructions.iter().any(|instr| instr.memory_addr().is_some_and(|mem| mem >= imported_memory_count)) @@ -55,28 +50,43 @@ fn rewrite( rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b)); rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c)); rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c)); + rewrite!(instrs, i, [GlobalGet(global)] => [Nop, BinOpStackGlobal32(op, global)]); if matches!(op, BinOp::IAdd) { rewrite!(instrs, i, [Const32(c)] => AddConst32(c)); + rewrite!(instrs, i, [I32Add] => [Nop, I32Add3]); } } instr @ (I32Sub | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr) => { let Some(op) = int_bin_op_32(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b)); rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c)); + rewrite!(instrs, i, [GlobalGet(global)] => [Nop, BinOpStackGlobal32(op, global)]); + if matches!(op, BinOp::IShrS) { + rewrite!(instrs, i, [BinOpLocalConst32(BinOp::IShl, local, 8), Const32(8)] => [Nop, LocalGet32(local), I32Extend8S]); + rewrite!(instrs, i, [BinOpLocalConst32(BinOp::IShl, local, 16), Const32(16)] => [Nop, LocalGet32(local), I32Extend16S]); + } } instr @ (I64Add | I64Mul | I64And | I64Or | I64Xor) => { let Some(op) = int_bin_op_64(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b)); rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c)); rewrite!(instrs, i, [Const64(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c)); + rewrite!(instrs, i, [GlobalGet(global)] => [Nop, BinOpStackGlobal64(op, global)]); if matches!(op, BinOp::IAdd) { rewrite!(instrs, i, [Const64(c)] => AddConst64(c)); + rewrite!(instrs, i, [I64Add] => [Nop, I64Add3]); } } instr @ (I64Sub | I64Shl | I64ShrS | I64ShrU | I64Rotl | I64Rotr) => { let Some(op) = int_bin_op_64(instr) else { unreachable!() }; rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b)); rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c)); + rewrite!(instrs, i, [GlobalGet(global)] => [Nop, BinOpStackGlobal64(op, global)]); + if matches!(op, BinOp::IShrS) { + rewrite!(instrs, i, [BinOpLocalConst64(BinOp::IShl, local, 8), Const64(8)] => [Nop, LocalGet64(local), I64Extend8S]); + rewrite!(instrs, i, [BinOpLocalConst64(BinOp::IShl, local, 16), Const64(16)] => [Nop, LocalGet64(local), I64Extend16S]); + rewrite!(instrs, i, [BinOpLocalConst64(BinOp::IShl, local, 32), Const64(32)] => [Nop, LocalGet64(local), I64Extend32S]); + } } instr @ (F32Add | F32Mul | F32Min | F32Max) => { let Some(op) = float_bin_op_32(instr) else { unreachable!() }; @@ -111,6 +121,7 @@ fn rewrite( rewrite!(instrs, i, [LocalGet128(local), Const128(c)] => BinOpLocalConst128(BinOp128::AndNot, local, c)); } I32Store(memarg) | F32Store(memarg) => { + rewrite!(instrs, i, [F32Mul, F32Add] => [Nop, Nop, FMaStoreF32(memarg)]); rewrite!(instrs, i, [LocalGet32(addr_local), LocalGet32(value_local)] if (let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))) => @@ -118,6 +129,7 @@ fn rewrite( ); } I64Store(memarg) | F64Store(memarg) => { + rewrite!(instrs, i, [F64Mul, F64Add] => [Nop, Nop, FMaStoreF64(memarg)]); rewrite!(instrs, i, [LocalGet32(addr_local), LocalGet64(value_local)] if (let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))) => @@ -156,6 +168,8 @@ fn rewrite( _ => Instruction::BinOpLocalConstSet32(op, lhs, imm, dst), } ); + rewrite!(instrs, i, [I32Mul, LocalGet32(acc), I32Add] if (acc == dst) => [Nop, Nop, Nop, MulAccLocal32(dst)]); + rewrite!(instrs, i, [F32Mul, LocalGet32(acc), F32Add] if (acc == dst) => [Nop, Nop, Nop, FMulAccLocal32(dst)]); rewrite_local_set_direct!( instrs, i, @@ -193,6 +207,8 @@ fn rewrite( _ => Instruction::BinOpLocalConstSet64(op, lhs, imm, dst), } ); + rewrite!(instrs, i, [I64Mul, LocalGet64(acc), I64Add] if (acc == dst) => [Nop, Nop, Nop, MulAccLocal64(dst)]); + rewrite!(instrs, i, [F64Mul, LocalGet64(acc), F64Add] if (acc == dst) => [Nop, Nop, Nop, FMulAccLocal64(dst)]); rewrite_local_set_direct!( instrs, i, diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index de0c8fb..996f728 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -227,10 +227,18 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild if let Some(Instruction::LocalGet64(src)) = last { Some(*src) } else { None } } wasmparser::ValType::V128 => { - if let Some(Instruction::LocalGet128(src)) = last { Some(*src) } else { None } + if let Some(Instruction::LocalGet128(src)) = last { + Some(*src) + } else { + None + } } wasmparser::ValType::Ref(_) => { - if let Some(Instruction::LocalGet32(src)) = last { Some(*src) } else { None } + if let Some(Instruction::LocalGet32(src)) = last { + Some(*src) + } else { + None + } } }; diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 856f1f0..7811e6e 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -108,15 +108,6 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { } #[inline(always)] - fn get_v128_const(&self, idx: ConstIdx) -> Value128 { - let Some(val) = self.func.data.v128_constants.get(idx as usize) else { - cold_path(); - unreachable!("invalid v128 constant index"); - }; - Value128(*val) - } - - #[inline(always)] fn exec(&mut self) -> Result<Option<()>, Trap> { macro_rules! stack_op { (unary $ty:ty, |$v:ident| $expr:expr) => {{ @@ -182,6 +173,55 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { }}; } + macro_rules! binop { + (local_local $vt:ty, $exec:ident, $op:ident, $a:ident, $b:ident) => {{ + self.store.value_stack.push(self.$exec( + *$op, + <$vt>::local_get(&self.store.value_stack, &self.cf, *$a), + <$vt>::local_get(&self.store.value_stack, &self.cf, *$b), + ))? + }}; + (local_local_set $vt:ty, $exec:ident, $op:ident, $a:ident, $b:ident, $dst:ident) => {{ + let value = self.$exec( + *$op, + <$vt>::local_get(&self.store.value_stack, &self.cf, *$a), + <$vt>::local_get(&self.store.value_stack, &self.cf, *$b), + ); + <$vt>::local_set(&mut self.store.value_stack, &self.cf, *$dst, value); + }}; + (local_local_tee $vt:ty, $exec:ident, $op:ident, $a:ident, $b:ident, $dst:ident) => {{ + let value = self.$exec( + *$op, + <$vt>::local_get(&self.store.value_stack, &self.cf, *$a), + <$vt>::local_get(&self.store.value_stack, &self.cf, *$b), + ); + <$vt>::local_set(&mut self.store.value_stack, &self.cf, *$dst, value); + self.store.value_stack.push(value)?; + }}; + (local_const $vt:ty, $exec:ident, $op:ident, $local:ident, $rhs:expr) => {{ + self.store.value_stack.push(self.$exec( + *$op, + <$vt>::local_get(&self.store.value_stack, &self.cf, *$local), + $rhs, + ))? + }}; + (local_const_set $vt:ty, $exec:ident, $op:ident, $local:ident, $rhs:expr, $dst:ident) => {{ + let value = self.$exec(*$op, <$vt>::local_get(&self.store.value_stack, &self.cf, *$local), $rhs); + <$vt>::local_set(&mut self.store.value_stack, &self.cf, *$dst, value); + }}; + (local_const_tee $vt:ty, $exec:ident, $op:ident, $local:ident, $rhs:expr, $dst:ident) => {{ + let value = self.$exec(*$op, <$vt>::local_get(&self.store.value_stack, &self.cf, *$local), $rhs); + <$vt>::local_set(&mut self.store.value_stack, &self.cf, *$dst, value); + self.store.value_stack.push(value)?; + }}; + (stack_global $vt:ty, $as_fn:ident, $exec:ident, $op:ident, $global:ident) => {{ + let global_val = + self.store.state.get_global_val(self.module.resolve_global_addr(*$global)).$as_fn().unwrap(); + let stack_val = <$vt>::stack_pop(&mut self.store.value_stack); + self.store.value_stack.push(self.$exec(*$op, stack_val, global_val))?; + }}; + } + let next = match self.func.instructions.get(self.cf.instr_ptr as usize) { Some(instr) => instr, None => { @@ -249,80 +289,37 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { LocalCopy128(from, to) => Value128::local_copy(&mut self.store.value_stack, &self.cf, *from, *to), AddConst32(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)), AddConst64(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)), - IncLocal32(local_index, delta) => { - let value = Value32::local_get(&self.store.value_stack, &self.cf, *local_index).wrapping_add(*delta as u32); - Value32::local_set(&mut self.store.value_stack, &self.cf, *local_index, value); - } - IncLocal64(local_index, delta) => { - let value = Value64::local_get(&self.store.value_stack, &self.cf, *local_index).wrapping_add(*delta as u64); - Value64::local_set(&mut self.store.value_stack, &self.cf, *local_index, value); - } - BinOpLocalLocal32(op, a, b) => self.store.value_stack.push(self.exec_binop_32(*op, Value32::local_get(&self.store.value_stack, &self.cf, *a), Value32::local_get(&self.store.value_stack, &self.cf, *b)))?, - BinOpLocalLocal64(op, a, b) => self.store.value_stack.push(self.exec_binop_64(*op, Value64::local_get(&self.store.value_stack, &self.cf, *a), Value64::local_get(&self.store.value_stack, &self.cf, *b)))?, - BinOpLocalLocal128(op, a, b) => self.store.value_stack.push(self.exec_binop_128(*op, Value128::local_get(&self.store.value_stack, &self.cf, *a), Value128::local_get(&self.store.value_stack, &self.cf, *b)))?, - BinOpLocalLocalSet32(op, a, b, dst) => { - let value = self.exec_binop_32(*op, Value32::local_get(&self.store.value_stack, &self.cf, *a), Value32::local_get(&self.store.value_stack, &self.cf, *b)); - Value32::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - } - BinOpLocalLocalSet64(op, a, b, dst) => { - let value = self.exec_binop_64(*op, Value64::local_get(&self.store.value_stack, &self.cf, *a), Value64::local_get(&self.store.value_stack, &self.cf, *b)); - Value64::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - } - BinOpLocalLocalSet128(op, a, b, dst) => { - let value = self.exec_binop_128(*op, Value128::local_get(&self.store.value_stack, &self.cf, *a), Value128::local_get(&self.store.value_stack, &self.cf, *b)); - Value128::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - } - BinOpLocalLocalTee32(op, a, b, dst) => { - let value = self.exec_binop_32(*op, Value32::local_get(&self.store.value_stack, &self.cf, *a), Value32::local_get(&self.store.value_stack, &self.cf, *b)); - Value32::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - self.store.value_stack.push(value)?; - } - BinOpLocalLocalTee64(op, a, b, dst) => { - let value = self.exec_binop_64(*op, Value64::local_get(&self.store.value_stack, &self.cf, *a), Value64::local_get(&self.store.value_stack, &self.cf, *b)); - Value64::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - self.store.value_stack.push(value)?; - } - BinOpLocalLocalTee128(op, a, b, dst) => { - let value = self.exec_binop_128(*op, Value128::local_get(&self.store.value_stack, &self.cf, *a), Value128::local_get(&self.store.value_stack, &self.cf, *b)); - Value128::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - self.store.value_stack.push(value)?; - } - BinOpLocalConst32(op, local_index, c) => self.store.value_stack.push(self.exec_binop_32(*op, Value32::local_get(&self.store.value_stack, &self.cf, *local_index), *c as u32))?, - BinOpLocalConst64(op, local_index, c) => self.store.value_stack.push(self.exec_binop_64(*op, Value64::local_get(&self.store.value_stack, &self.cf, *local_index), *c as u64))?, - BinOpLocalConst128(op, local_index, c) => self.store.value_stack.push(self.exec_binop_128(*op, Value128::local_get(&self.store.value_stack, &self.cf, *local_index), self.get_v128_const(*c)))?, - BinOpLocalConstSet32(op, local_index, c, dst) => { - let value = self.exec_binop_32(*op, Value32::local_get(&self.store.value_stack, &self.cf, *local_index), *c as u32); - Value32::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - }, - BinOpLocalConstSet64(op, local_index, c, dst) => { - let value = self.exec_binop_64(*op, Value64::local_get(&self.store.value_stack, &self.cf, *local_index), *c as u64); - Value64::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - }, - BinOpLocalConstSet128(op, local_index, c, dst) => { - let value = self.exec_binop_128(*op, Value128::local_get(&self.store.value_stack, &self.cf, *local_index), self.get_v128_const(*c)); - Value128::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - }, - BinOpLocalConstTee32(op, local_index, c, dst) => { - let value = self.exec_binop_32(*op, Value32::local_get(&self.store.value_stack, &self.cf, *local_index), *c as u32); - Value32::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - self.store.value_stack.push(value)?; - } - BinOpLocalConstTee64(op, local_index, c, dst) => { - let value = self.exec_binop_64(*op, Value64::local_get(&self.store.value_stack, &self.cf, *local_index), *c as u64); - Value64::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - self.store.value_stack.push(value)?; - } - BinOpLocalConstTee128(op, local_index, c, dst) => { - let value = self.exec_binop_128(*op, Value128::local_get(&self.store.value_stack, &self.cf, *local_index), self.get_v128_const(*c)); - Value128::local_set(&mut self.store.value_stack, &self.cf, *dst, value); - self.store.value_stack.push(value)?; - } + IncLocal32(local_index, delta) => i32::local_update(&mut self.store.value_stack, &self.cf, *local_index, |v| v.wrapping_add(*delta)), + IncLocal64(local_index, delta) => i64::local_update(&mut self.store.value_stack, &self.cf, *local_index, |v| v.wrapping_add(*delta )), + I32Add3 => stack_op!(ternary i32, |a, b, c| a.wrapping_add(b).wrapping_add(c)), + I64Add3 => stack_op!(ternary i64, |a, b, c| a.wrapping_add(b).wrapping_add(c)), + MulAccLocal32(acc) => self.exec_binop_acc_local::<i32, _, _>(*acc, |a, b| a.wrapping_mul(b), |a, b| a.wrapping_add(b)), + MulAccLocal64(acc) => self.exec_binop_acc_local::<i64, _, _>(*acc, |a, b| a.wrapping_mul(b), |a, b| a.wrapping_add(b)), + FMulAccLocal32(acc) => self.exec_binop_acc_local::<f32, _, _>(*acc, |a, b| a * b, |a, b| a + b), + FMulAccLocal64(acc) => self.exec_binop_acc_local::<f64, _, _>(*acc, |a, b| a * b, |a, b| a + b), + BinOpLocalLocal32(op, a, b) => binop!(local_local Value32, exec_binop_32, op, a, b), + BinOpLocalLocal64(op, a, b) => binop!(local_local Value64, exec_binop_64, op, a, b), + BinOpLocalLocal128(op, a, b) => binop!(local_local Value128, exec_binop_128, op, a, b), + BinOpLocalLocalSet32(op, a, b, dst) => binop!(local_local_set Value32, exec_binop_32, op, a, b, dst), + BinOpLocalLocalSet64(op, a, b, dst) => binop!(local_local_set Value64, exec_binop_64, op, a, b, dst), + BinOpLocalLocalSet128(op, a, b, dst) => binop!(local_local_set Value128, exec_binop_128, op, a, b, dst), + BinOpLocalLocalTee32(op, a, b, dst) => binop!(local_local_tee Value32, exec_binop_32, op, a, b, dst), + BinOpLocalLocalTee64(op, a, b, dst) => binop!(local_local_tee Value64, exec_binop_64, op, a, b, dst), + BinOpLocalLocalTee128(op, a, b, dst) => binop!(local_local_tee Value128, exec_binop_128, op, a, b, dst), + BinOpLocalConst32(op, local_index, c) => binop!(local_const Value32, exec_binop_32, op, local_index, *c as u32), + BinOpLocalConst64(op, local_index, c) => binop!(local_const Value64, exec_binop_64, op, local_index, *c as u64), + BinOpLocalConst128(op, local_index, c) => binop!(local_const Value128, exec_binop_128, op, local_index, Value128(self.func.data.v128_const(*c))), + BinOpLocalConstSet32(op, local_index, c, dst) => binop!(local_const_set Value32, exec_binop_32, op, local_index, *c as u32, dst), + BinOpLocalConstSet64(op, local_index, c, dst) => binop!(local_const_set Value64, exec_binop_64, op, local_index, *c as u64, dst), + BinOpLocalConstSet128(op, local_index, c, dst) => binop!(local_const_set Value128, exec_binop_128, op, local_index, Value128(self.func.data.v128_const(*c)), dst), + BinOpLocalConstTee32(op, local_index, c, dst) => binop!(local_const_tee Value32, exec_binop_32, op, local_index, *c as u32, dst), + BinOpLocalConstTee64(op, local_index, c, dst) => binop!(local_const_tee Value64, exec_binop_64, op, local_index, *c as u64, dst), + BinOpLocalConstTee128(op, local_index, c, dst) => binop!(local_const_tee Value128, exec_binop_128, op, local_index, Value128(self.func.data.v128_const(*c)), dst), + BinOpStackGlobal32(op, global_index) => binop!(stack_global Value32, as_32, exec_binop_32, op, global_index), + BinOpStackGlobal64(op, global_index) => binop!(stack_global Value64, as_64, exec_binop_64, op, global_index), SetLocalConst32(local_index, c) => i32::local_set(&mut self.store.value_stack, &self.cf, *local_index, *c), SetLocalConst64(local_index, c) => i64::local_set(&mut self.store.value_stack, &self.cf, *local_index, *c), - SetLocalConst128(local_index, c) => { - let value = self.get_v128_const(*c); - Value128::local_set(&mut self.store.value_stack, &self.cf, *local_index, value); - } + SetLocalConst128(local_index, c) => Value128::local_set(&mut self.store.value_stack, &self.cf, *local_index, Value128(self.func.data.v128_const(*c))), StoreLocalLocal32(m, addr_local, value_local) => self.exec_store_local_local::<u32, 4>(*m, *addr_local, *value_local)?, StoreLocalLocal64(m, addr_local, value_local) => self.exec_store_local_local::<i64, 8>(*m, *addr_local, *value_local)?, StoreLocalLocal128(m, addr_local, value_local) => self.exec_store_local_local::<Value128, 16>(*m, *addr_local, *value_local)?, @@ -472,6 +469,8 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { I64Store(m) => self.exec_mem_store::<i64, i64, 8>(m.mem_addr(), m.offset(), |v| v)?, F32Store(m) => self.exec_mem_store::<f32, f32, 4>(m.mem_addr(), m.offset(), |v| v)?, F64Store(m) => self.exec_mem_store::<f64, f64, 8>(m.mem_addr(), m.offset(), |v| v)?, + FMaStoreF32(m) => self.exec_fma_store::<f32, 4>(*m)?, + FMaStoreF64(m) => self.exec_fma_store::<f64, 8>(*m)?, I32Store8(m) => self.exec_mem_store::<i32, i8, 1>(m.mem_addr(), m.offset(), |v| v as i8)?, I32Store16(m) => self.exec_mem_store::<i32, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?, I64Store8(m) => self.exec_mem_store::<i64, i8, 1>(m.mem_addr(), m.offset(), |v| v as i8)?, @@ -580,7 +579,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { V128Store64Lane(arg, lane) => self.exec_mem_store_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?, V128Load32Zero(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?, V128Load64Zero(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?, - Const128(arg) => self.exec_const(self.get_v128_const(*arg))?, + Const128(arg) => self.exec_const(Value128(self.func.data.v128_const(*arg)))?, I8x16ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i8(*lane) as i32), I8x16ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u8(*lane) as i32), I16x8ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i16(*lane) as i32), @@ -1079,6 +1078,36 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { Ok(()) } + #[inline(always)] + fn exec_fma_store< + T: InternalValue + MemValue<N> + core::ops::Add<Output = T> + core::ops::Mul<Output = T>, + const N: usize, + >( + &mut self, + m: MemoryArg, + ) -> Result<(), Trap> { + let rhs = T::stack_pop(&mut self.store.value_stack); + let lhs = T::stack_pop(&mut self.store.value_stack); + let acc = T::stack_pop(&mut self.store.value_stack); + let addr = i32::stack_pop(&mut self.store.value_stack); + let fma = acc + lhs * rhs; + let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(m.mem_addr())); + mem.store(addr as u32 as u64, m.offset(), fma.to_mem_bytes())?; + Ok(()) + } + + #[inline(always)] + fn exec_binop_acc_local<T, M, A>(&mut self, acc: LocalAddr, mul: M, add: A) + where + T: InternalValue, + M: Fn(T, T) -> T, + A: Fn(T, T) -> T, + { + let rhs = T::stack_pop(&mut self.store.value_stack); + let lhs = T::stack_pop(&mut self.store.value_stack); + T::local_update(&mut self.store.value_stack, &self.cf, acc, |v| add(mul(lhs, rhs), v)); + } + fn exec_load_local_value<T: MemValue<N>, const N: usize>( &self, memarg: MemoryArg, diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs index 0e4ae5a..a73528a 100644 --- a/crates/tinywasm/src/interpreter/values.rs +++ b/crates/tinywasm/src/interpreter/values.rs @@ -152,6 +152,7 @@ pub(crate) trait InternalValue: sealed::Sealed + Into<TinyWasmValue> + Copy + De fn stack_select(stack: &mut ValueStack) -> Result<(), crate::Trap>; fn local_get(stack: &ValueStack, frame: &CallFrame, index: LocalAddr) -> Self; fn local_set(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, value: Self); + fn local_update(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, f: impl FnOnce(Self) -> Self); fn local_copy(stack: &mut ValueStack, frame: &CallFrame, from: LocalAddr, to: LocalAddr); } @@ -196,6 +197,14 @@ macro_rules! impl_internalvalue { } #[inline(always)] + fn local_update(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, f: impl FnOnce(Self) -> Self) { + let abs_index = frame.locals_base.$stack_base as usize + index as usize; + let $from_stack_v = *stack.$stack.get(abs_index); + let $to_stack_v = f($from_stack); + stack.$stack.set(abs_index, $to_stack); + } + + #[inline(always)] fn local_copy(stack: &mut ValueStack, frame: &CallFrame, from: LocalAddr, to: LocalAddr) { let val = stack.$stack.get(frame.locals_base.$stack_base as usize + from as usize); stack.$stack.set(frame.locals_base.$stack_base as usize + to as usize, *val); diff --git a/crates/tinywasm/tests/testsuite/util.rs b/crates/tinywasm/tests/testsuite/util.rs index 61dfc97..1714543 100644 --- a/crates/tinywasm/tests/testsuite/util.rs +++ b/crates/tinywasm/tests/testsuite/util.rs @@ -7,8 +7,8 @@ use tinywasm_types::{ExternRef, FuncRef, Module, ModuleInstanceAddr, WasmType, W use wasm_testsuite::wast; use wasm_testsuite::wast::{QuoteWat, core::AbstractHeapType}; -const TEST_TIME_SLICE: Duration = Duration::from_millis(10); -const TEST_MAX_SUSPENSIONS: u32 = 100; +const TEST_TIME_SLICE: Duration = Duration::from_millis(20); +const TEST_MAX_SUSPENSIONS: u32 = 1000; fn exec_with_budget( func: &tinywasm::Function, diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index a1bc0fe..4c76046 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -132,6 +132,8 @@ pub enum Instruction { BinOpLocalConstTee32(BinOp, LocalAddr, i32, LocalAddr), BinOpLocalConstTee64(BinOp, LocalAddr, i64, LocalAddr), BinOpLocalConstTee128(BinOp128, LocalAddr, ConstIdx, LocalAddr), + BinOpStackGlobal32(BinOp, u32), + BinOpStackGlobal64(BinOp, u32), SetLocalConst32(LocalAddr, i32), SetLocalConst64(LocalAddr, i64), SetLocalConst128(LocalAddr, ConstIdx), StoreLocalLocal32(MemoryArg, u8, u8), StoreLocalLocal64(MemoryArg, u8, u8), @@ -145,6 +147,14 @@ pub enum Instruction { SubConstTee32(i32, LocalAddr), AndConstTee64(i64, LocalAddr), SubConstTee64(i64, LocalAddr), + MulAccLocal32(LocalAddr), + MulAccLocal64(LocalAddr), + FMulAccLocal32(LocalAddr), + FMulAccLocal64(LocalAddr), + I32Add3, + I64Add3, + FMaStoreF32(MemoryArg), + FMaStoreF64(MemoryArg), // > Control Instructions (jump-oriented, lowered from structured control during parsing) // See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions> @@ -385,6 +395,8 @@ impl Instruction { | Self::I64Store(arg) | Self::F32Store(arg) | Self::F64Store(arg) + | Self::FMaStoreF32(arg) + | Self::FMaStoreF64(arg) | Self::I32Store8(arg) | Self::I32Store16(arg) | Self::I64Store8(arg) diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 29eb709..b65e4b7 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -10,6 +10,7 @@ extern crate alloc; use alloc::{boxed::Box, sync::Arc}; +use core::hint::cold_path; use core::ops::{Deref, Range}; // Memory defaults @@ -430,6 +431,18 @@ pub struct WasmFunctionData { pub branch_table_targets: Box<[u32]>, } +impl WasmFunctionData { + /// Panics if `idx` is out of bounds. + #[inline(always)] + pub fn v128_const(&self, idx: ConstIdx) -> [u8; 16] { + let Some(val) = self.v128_constants.get(idx as usize) else { + cold_path(); + unreachable!("invalid v128 constant index"); + }; + *val + } +} + /// A WebAssembly Module Export #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "debug", derive(Debug))] |
