summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/parser/src/lib.rs7
-rw-r--r--crates/parser/src/optimize.rs28
-rw-r--r--crates/parser/src/visit.rs12
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs193
-rw-r--r--crates/tinywasm/src/interpreter/values.rs9
-rw-r--r--crates/tinywasm/tests/testsuite/util.rs4
-rw-r--r--crates/types/src/instructions.rs12
-rw-r--r--crates/types/src/lib.rs13
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))]