summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry <mail@henrygressmann.de>2026-04-24 00:13:23 +0200
committerHenry <mail@henrygressmann.de>2026-04-24 00:13:23 +0200
commit19038bfea6a65e2aad89ec6a8fa51c41da35b219 (patch)
treeb4f33812059e60f55a6f85a78789b83476269908
parent5b318177aa4f79246e772c5fcef6ee4bac5bdaa0 (diff)
feat: new binopt superinstructions
Signed-off-by: Henry <mail@henrygressmann.de>
-rw-r--r--crates/parser/src/optimize.rs362
-rw-r--r--crates/parser/src/visit.rs12
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs183
-rw-r--r--crates/tinywasm/src/interpreter/stack/value_stack.rs18
-rw-r--r--crates/tinywasm/src/interpreter/values.rs7
-rw-r--r--crates/types/src/instructions.rs64
6 files changed, 519 insertions, 127 deletions
diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs
index beb944d..51d0944 100644
--- a/crates/parser/src/optimize.rs
+++ b/crates/parser/src/optimize.rs
@@ -1,6 +1,6 @@
use crate::macros::optimize::*;
use alloc::vec::Vec;
-use tinywasm_types::{CmpOp, Instruction, WasmFunctionData};
+use tinywasm_types::{BinOp, BinOp128, CmpOp, Instruction, WasmFunctionData};
pub(crate) struct OptimizeResult {
pub(crate) instructions: Vec<Instruction>,
@@ -35,17 +35,66 @@ fn rewrite(
LocalCopy128(a, b) if a == b => instrs[i] = Nop,
Call(addr) if addr == self_func_addr => instrs[i] = CallSelf,
ReturnCall(addr) if addr == self_func_addr => instrs[i] = ReturnCallSelf,
- I32Add => {
- rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => AddLocalLocal32(a, b));
- rewrite!(instrs, i, [LocalGet32(local), I32Const(c)] => [ Nop, LocalGet32(local), AddConst32(c)]);
- rewrite!(instrs, i, [I32Const(c)] => AddConst32(c));
+ instr @ (I32Add | I32Mul | I32And | I32Or | I32Xor) => {
+ 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), I32Const(c)] => BinOpLocalConst32(op, local, c));
+ rewrite!(instrs, i, [I32Const(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c));
+ if matches!(op, BinOp::IAdd) {
+ rewrite!(instrs, i, [I32Const(c)] => AddConst32(c));
+ }
}
- I64Add => {
- rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => AddLocalLocal64(a, b));
- rewrite!(instrs, i, [LocalGet64(local), I64Const(c)] => [ Nop, LocalGet64(local), AddConst64(c)]);
- rewrite!(instrs, i, [I64Const(c)] => AddConst64(c));
+ 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), I32Const(c)] => BinOpLocalConst32(op, local, c));
+ }
+ 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), I64Const(c)] => BinOpLocalConst64(op, local, c));
+ rewrite!(instrs, i, [I64Const(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c));
+ if matches!(op, BinOp::IAdd) {
+ rewrite!(instrs, i, [I64Const(c)] => AddConst64(c));
+ }
+ }
+ 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), I64Const(c)] => BinOpLocalConst64(op, local, c));
+ }
+ instr @ (F32Add | F32Mul | F32Min | F32Max) => {
+ let Some(op) = float_bin_op_32(instr) else { unreachable!() };
+ rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
+ rewrite!(instrs, i, [LocalGet32(local), F32Const(c)] => BinOpLocalConst32(op, local, f32_const_bits(c)));
+ rewrite!(instrs, i, [F32Const(c), LocalGet32(local)] => BinOpLocalConst32(op, local, f32_const_bits(c)));
+ }
+ instr @ (F32Sub | F32Div | F32Copysign) => {
+ let Some(op) = float_bin_op_32(instr) else { unreachable!() };
+ rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
+ rewrite!(instrs, i, [LocalGet32(local), F32Const(c)] => BinOpLocalConst32(op, local, f32_const_bits(c)));
+ }
+ instr @ (F64Add | F64Mul | F64Min | F64Max) => {
+ let Some(op) = float_bin_op_64(instr) else { unreachable!() };
+ rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b));
+ rewrite!(instrs, i, [LocalGet64(local), F64Const(c)] => BinOpLocalConst64(op, local, f64_const_bits(c)));
+ rewrite!(instrs, i, [F64Const(c), LocalGet64(local)] => BinOpLocalConst64(op, local, f64_const_bits(c)));
+ }
+ instr @ (F64Sub | F64Div | F64Copysign) => {
+ let Some(op) = float_bin_op_64(instr) else { unreachable!() };
+ rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b));
+ rewrite!(instrs, i, [LocalGet64(local), F64Const(c)] => BinOpLocalConst64(op, local, f64_const_bits(c)));
+ }
+ instr @ (V128And | V128Or | V128Xor | I64x2Add | I64x2Mul) => {
+ let Some(op) = bin_op_128(instr) else { unreachable!() };
+ rewrite!(instrs, i, [LocalGet128(a), LocalGet128(b)] => BinOpLocalLocal128(op, a, b));
+ rewrite!(instrs, i, [LocalGet128(local), V128Const(c)] => BinOpLocalConst128(op, local, c));
+ rewrite!(instrs, i, [V128Const(c), LocalGet128(local)] => BinOpLocalConst128(op, local, c));
+ }
+ V128AndNot => {
+ rewrite!(instrs, i, [LocalGet128(a), LocalGet128(b)] => BinOpLocalLocal128(BinOp128::AndNot, a, b));
+ rewrite!(instrs, i, [LocalGet128(local), V128Const(c)] => BinOpLocalConst128(BinOp128::AndNot, local, c));
}
- I64Rotl => rewrite!(instrs, i, [I64Xor, I64Const(c)] => XorRotlConst64(c)),
I32Store(memarg) => {
rewrite!(instrs, i,
[LocalGet32(addr_local), LocalGet32(value_local)] if
@@ -80,11 +129,35 @@ fn rewrite(
LocalGet64(dst) => rewrite!(instrs, i, [LocalSet64(src)] if (src == dst) => [LocalTee64(src), Nop]),
LocalGet128(dst) => rewrite!(instrs, i, [LocalSet128(src)] if (src == dst) => [LocalTee128(src), Nop]),
LocalSet32(dst) => {
+ if let Some([(lhs_idx, lhs_src), (rhs_idx, rhs_src), (op_idx, raw_op)]) = previous_non_nop_3(instrs, i)
+ && let Some((lhs_instr, lhs)) = stack_source_local_32(lhs_src)
+ && let Some(op) = scalar_bin_op_32(raw_op)
+ {
+ if let Some((rhs_instr, rhs)) = stack_source_local_32(rhs_src) {
+ instrs[lhs_idx] = lhs_instr;
+ instrs[rhs_idx] = rhs_instr;
+ instrs[op_idx] = Nop;
+ instrs[i] = BinOpLocalLocalSet32(op, lhs, rhs, dst);
+ } else if let Some(imm) = scalar_const_32(rhs_src, raw_op) {
+ instrs[lhs_idx] = lhs_instr;
+ instrs[rhs_idx] = Nop;
+ instrs[op_idx] = Nop;
+ instrs[i] = match (dst == lhs, op) {
+ (true, BinOp::IAdd) => IncLocal32(dst, imm),
+ (true, BinOp::ISub) => IncLocal32(dst, imm.wrapping_neg()),
+ _ => BinOpLocalConstSet32(op, lhs, imm, dst),
+ };
+ }
+ }
rewrite!(instrs, i, [LocalGet32(src)] => if src == dst { Nop } else { LocalCopy32(src, dst) });
rewrite!(instrs, i, [I32Const(c)] => SetLocalConst32(dst, c));
rewrite!(instrs, i, [F32Const(c)] => SetLocalConst32(dst, i32::from_ne_bytes(c.to_bits().to_ne_bytes())));
- rewrite!(instrs, i, [AddLocalLocal32(a, b)] => AddLocalLocalSet32(a, b, dst));
- rewrite!(instrs, i, [LocalGet32(src), AddConst32(c)] if (src == dst) => AddLocalConst32(dst, c));
+ rewrite!(instrs, i, [BinOpLocalLocal32(op, a, b)] => BinOpLocalLocalSet32(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConst32(op, src, c)] => match (dst == src, op) {
+ (true, BinOp::IAdd) => IncLocal32(dst, c),
+ (true, BinOp::ISub) => IncLocal32(dst, c.wrapping_neg()),
+ _ => BinOpLocalConstSet32(op, src, c, dst),
+ });
rewrite!(instrs, i, [LoadLocal32(memarg, addr)] if (let Ok(dst) = u8::try_from(dst)) => LoadLocalSet32(memarg, addr, dst));
rewrite!(instrs, i,
[LocalGet32(addr), I32Load(memarg)] if
@@ -94,16 +167,31 @@ fn rewrite(
}
LocalSet64(dst) => {
rewrite!(instrs, i, [LocalGet64(src)] => if src == dst { Nop } else { LocalCopy64(src, dst) });
- rewrite!(instrs, i, [I64Const(c)] => SetLocalConst64(dst, c));
- rewrite!(instrs, i, [F64Const(c)] => SetLocalConst64(dst, i64::from_ne_bytes(c.to_bits().to_ne_bytes())));
- rewrite!(instrs, i, [AddLocalLocal64(a, b)] => AddLocalLocalSet64(a, b, dst));
rewrite!(instrs, i,
- [LocalGet64(src), AddConst64(c)] if (src == dst) =>
- AddLocalConst64(dst, c)
+ [LocalTee64(src), I64Const(c), instr] if (let Some(op) = int_bin_op_64(instr)) =>
+ [LocalSet64(src), Nop, Nop, match (dst == src, op) {
+ (true, BinOp::IAdd) => IncLocal64(dst, c),
+ (true, BinOp::ISub) => IncLocal64(dst, c.wrapping_neg()),
+ _ => BinOpLocalConstSet64(op, src, c, dst),
+ }]
);
+ rewrite!(instrs, i,
+ [LocalTee64(src), F64Const(c), instr] if (let Some(op) = float_bin_op_64(instr)) =>
+ [LocalSet64(src), Nop, Nop, BinOpLocalConstSet64(op, src, f64_const_bits(c), dst)]
+ );
+ rewrite!(instrs, i, [I64Const(c)] => SetLocalConst64(dst, c));
+ rewrite!(instrs, i, [F64Const(c)] => SetLocalConst64(dst, i64::from_ne_bytes(c.to_bits().to_ne_bytes())));
+ rewrite!(instrs, i, [BinOpLocalLocal64(op, a, b)] => BinOpLocalLocalSet64(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConst64(op, src, c)] => match (dst == src, op) {
+ (true, BinOp::IAdd) => IncLocal64(dst, c),
+ (true, BinOp::ISub) => IncLocal64(dst, c.wrapping_neg()),
+ _ => BinOpLocalConstSet64(op, src, c, dst),
+ });
}
LocalSet128(dst) => {
rewrite!(instrs, i, [LocalGet128(src)] => if src == dst { Nop } else { LocalCopy128(src, dst) });
+ rewrite!(instrs, i, [BinOpLocalLocal128(op, a, b)] => BinOpLocalLocalSet128(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConst128(op, src, c)] => BinOpLocalConstSet128(op, src, c, dst));
rewrite!(instrs, i,
[LocalGet32(addr), V128Load(memarg)] if
(let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
@@ -111,7 +199,25 @@ fn rewrite(
);
}
LocalTee32(dst) => {
+ if let Some([(lhs_idx, lhs_src), (rhs_idx, rhs_src), (op_idx, raw_op)]) = previous_non_nop_3(instrs, i)
+ && let Some((lhs_instr, lhs)) = stack_source_local_32(lhs_src)
+ && let Some(op) = scalar_bin_op_32(raw_op)
+ {
+ if let Some((rhs_instr, rhs)) = stack_source_local_32(rhs_src) {
+ instrs[lhs_idx] = lhs_instr;
+ instrs[rhs_idx] = rhs_instr;
+ instrs[op_idx] = Nop;
+ instrs[i] = BinOpLocalLocalTee32(op, lhs, rhs, dst);
+ } else if let Some(imm) = scalar_const_32(rhs_src, raw_op) {
+ instrs[lhs_idx] = lhs_instr;
+ instrs[rhs_idx] = Nop;
+ instrs[op_idx] = Nop;
+ instrs[i] = BinOpLocalConstTee32(op, lhs, imm, dst);
+ }
+ }
rewrite!(instrs, i, [LocalGet32(src)] if (src == dst) => [LocalGet32(src), Nop]);
+ rewrite!(instrs, i, [BinOpLocalLocal32(op, a, b)] => BinOpLocalLocalTee32(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConst32(op, src, c)] => BinOpLocalConstTee32(op, src, c, dst));
rewrite!(instrs, i, [I32Const(c), I32And] => AndConstTee32(c, dst));
rewrite!(instrs, i, [I32Const(c), I32Sub] => SubConstTee32(c, dst));
rewrite!(instrs, i,
@@ -126,24 +232,43 @@ fn rewrite(
}
LocalTee64(dst) => {
rewrite!(instrs, i, [LocalGet64(src)] if (src == dst) => [LocalGet64(src), Nop]);
+ rewrite!(instrs, i, [BinOpLocalLocal64(op, a, b)] => BinOpLocalLocalTee64(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConst64(op, src, c)] => BinOpLocalConstTee64(op, src, c, dst));
rewrite!(instrs, i, [I64Const(c), I64And] => AndConstTee64(c, dst));
rewrite!(instrs, i, [I64Const(c), I64Sub] => SubConstTee64(c, dst));
- rewrite!(instrs, i, [XorRotlConst64(c)] => XorRotlConstTee64(c, dst));
}
LocalTee128(dst) => {
rewrite!(instrs, i, [LocalGet128(src)] if (src == dst) => [LocalGet128(src), Nop]);
+ rewrite!(instrs, i, [BinOpLocalLocal128(op, a, b)] => BinOpLocalLocalTee128(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConst128(op, src, c)] => BinOpLocalConstTee128(op, src, c, dst));
rewrite!(instrs, i,
[LocalGet32(addr), V128Load(memarg)] if
(let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
LoadLocalTee128(memarg, addr, dst)
);
}
- Drop32 => rewrite!(instrs, i, [LocalTee32(local)] => [LocalSet32(local), Nop]),
- Drop64 => rewrite!(instrs, i, [LocalTee64(local)] => [LocalSet64(local), Nop]),
- Drop128 => rewrite!(instrs, i, [LocalTee128(local)] => [LocalSet128(local), Nop]),
+ Drop32 => {
+ rewrite!(instrs, i, [LocalTee32(local)] => [LocalSet32(local), Nop]);
+ rewrite!(instrs, i, [BinOpLocalLocalTee32(op, a, b, dst)] => BinOpLocalLocalSet32(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConstTee32(op, src, c, dst)] => BinOpLocalConstSet32(op, src, c, dst));
+ }
+ Drop64 => {
+ rewrite!(instrs, i, [LocalTee64(local)] => [LocalSet64(local), Nop]);
+ rewrite!(instrs, i, [BinOpLocalLocalTee64(op, a, b, dst)] => BinOpLocalLocalSet64(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConstTee64(op, src, c, dst)] => BinOpLocalConstSet64(op, src, c, dst));
+ }
+ Drop128 => {
+ rewrite!(instrs, i, [LocalTee128(local)] => [LocalSet128(local), Nop]);
+ rewrite!(instrs, i, [BinOpLocalLocalTee128(op, a, b, dst)] => BinOpLocalLocalSet128(op, a, b, dst));
+ rewrite!(instrs, i, [BinOpLocalConstTee128(op, src, c, dst)] => BinOpLocalConstSet128(op, src, c, dst));
+ }
JumpIfZero(ip) => {
rewrite!(instrs, i, [I32Eqz] => {
- replace!(instrs, i, 1 => [Nop, JumpIfNonZero(ip)]);
+ replace!(instrs, i, 1 => [Nop, JumpIfNonZero32(ip)]);
+ continue;
+ });
+ rewrite!(instrs, i, [I64Eqz] => {
+ replace!(instrs, i, 1 => [Nop, JumpIfNonZero64(ip)]);
continue;
});
rewrite!(instrs, i,
@@ -163,16 +288,24 @@ fn rewrite(
[LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) =>
JumpCmpLocalLocal64 { target_ip: ip, left, right, op: inverse_cmp_op(op) }
);
- rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) =>
- JumpCmpStackConst32 { target_ip: ip, imm, op: inverse_cmp_op(op) }
- );
- rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) =>
- JumpCmpStackConst64 { target_ip: ip, imm, op: inverse_cmp_op(op) }
- );
+ rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, inverse_cmp_op(op)) {
+ (0, CmpOp::Eq) => JumpIfZero32(ip),
+ (0, CmpOp::Ne) => JumpIfNonZero32(ip),
+ (imm, op) => JumpCmpStackConst32 { target_ip: ip, imm, op },
+ });
+ rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, inverse_cmp_op(op)) {
+ (0, CmpOp::Eq) => JumpIfZero64(ip),
+ (0, CmpOp::Ne) => JumpIfNonZero64(ip),
+ (imm, op) => JumpCmpStackConst64 { target_ip: ip, imm, op },
+ });
}
JumpIfNonZero(ip) => {
rewrite!(instrs, i, [I32Eqz] => {
- replace!(instrs, i, 1 => [Nop, JumpIfZero(ip)]);
+ replace!(instrs, i, 1 => [Nop, JumpIfZero32(ip)]);
+ continue;
+ });
+ rewrite!(instrs, i, [I64Eqz] => {
+ replace!(instrs, i, 1 => [Nop, JumpIfZero64(ip)]);
continue;
});
rewrite!(instrs, i,
@@ -192,13 +325,27 @@ fn rewrite(
[LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) =>
JumpCmpLocalLocal64 { target_ip: ip, left, right, op }
);
- rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) =>
- JumpCmpStackConst32 { target_ip: ip, imm, op }
- );
- rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) =>
- JumpCmpStackConst64 { target_ip: ip, imm, op }
- );
+ rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, op) {
+ (0, CmpOp::Eq) => JumpIfZero32(ip),
+ (0, CmpOp::Ne) => JumpIfNonZero32(ip),
+ (imm, op) => JumpCmpStackConst32 { target_ip: ip, imm, op },
+ });
+ rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, op) {
+ (0, CmpOp::Eq) => JumpIfZero64(ip),
+ (0, CmpOp::Ne) => JumpIfNonZero64(ip),
+ (imm, op) => JumpCmpStackConst64 { target_ip: ip, imm, op },
+ });
}
+ JumpCmpStackConst32 { target_ip, imm: 0, op } => match op {
+ CmpOp::Eq => instrs[i] = JumpIfZero32(target_ip),
+ CmpOp::Ne => instrs[i] = JumpIfNonZero32(target_ip),
+ _ => {}
+ },
+ JumpCmpStackConst64 { target_ip, imm: 0, op } => match op {
+ CmpOp::Eq => instrs[i] = JumpIfZero64(target_ip),
+ CmpOp::Ne => instrs[i] = JumpIfNonZero64(target_ip),
+ _ => {}
+ },
_ => {}
}
@@ -226,6 +373,115 @@ fn cmp_op(instr: Instruction) -> Option<CmpOp> {
})
}
+fn int_bin_op_32(instr: Instruction) -> Option<BinOp> {
+ Some(match instr {
+ Instruction::I32Add => BinOp::IAdd,
+ Instruction::I32Sub => BinOp::ISub,
+ Instruction::I32Mul => BinOp::IMul,
+ Instruction::I32And => BinOp::IAnd,
+ Instruction::I32Or => BinOp::IOr,
+ Instruction::I32Xor => BinOp::IXor,
+ Instruction::I32Shl => BinOp::IShl,
+ Instruction::I32ShrS => BinOp::IShrS,
+ Instruction::I32ShrU => BinOp::IShrU,
+ Instruction::I32Rotl => BinOp::IRotl,
+ Instruction::I32Rotr => BinOp::IRotr,
+ _ => return None,
+ })
+}
+
+fn int_bin_op_64(instr: Instruction) -> Option<BinOp> {
+ Some(match instr {
+ Instruction::I64Add => BinOp::IAdd,
+ Instruction::I64Sub => BinOp::ISub,
+ Instruction::I64Mul => BinOp::IMul,
+ Instruction::I64And => BinOp::IAnd,
+ Instruction::I64Or => BinOp::IOr,
+ Instruction::I64Xor => BinOp::IXor,
+ Instruction::I64Shl => BinOp::IShl,
+ Instruction::I64ShrS => BinOp::IShrS,
+ Instruction::I64ShrU => BinOp::IShrU,
+ Instruction::I64Rotl => BinOp::IRotl,
+ Instruction::I64Rotr => BinOp::IRotr,
+ _ => return None,
+ })
+}
+
+fn float_bin_op_32(instr: Instruction) -> Option<BinOp> {
+ Some(match instr {
+ Instruction::F32Add => BinOp::FAdd,
+ Instruction::F32Sub => BinOp::FSub,
+ Instruction::F32Mul => BinOp::FMul,
+ Instruction::F32Div => BinOp::FDiv,
+ Instruction::F32Min => BinOp::FMin,
+ Instruction::F32Max => BinOp::FMax,
+ Instruction::F32Copysign => BinOp::FCopysign,
+ _ => return None,
+ })
+}
+
+fn float_bin_op_64(instr: Instruction) -> Option<BinOp> {
+ Some(match instr {
+ Instruction::F64Add => BinOp::FAdd,
+ Instruction::F64Sub => BinOp::FSub,
+ Instruction::F64Mul => BinOp::FMul,
+ Instruction::F64Div => BinOp::FDiv,
+ Instruction::F64Min => BinOp::FMin,
+ Instruction::F64Max => BinOp::FMax,
+ Instruction::F64Copysign => BinOp::FCopysign,
+ _ => return None,
+ })
+}
+
+fn scalar_bin_op_32(instr: Instruction) -> Option<BinOp> {
+ int_bin_op_32(instr).or_else(|| float_bin_op_32(instr))
+}
+
+fn stack_source_local_32(instr: Instruction) -> Option<(Instruction, u16)> {
+ Some(match instr {
+ Instruction::LocalGet32(local) => (Instruction::Nop, local),
+ Instruction::LocalTee32(local) => (Instruction::LocalSet32(local), local),
+ Instruction::BinOpLocalLocalTee32(op, a, b, local) => {
+ (Instruction::BinOpLocalLocalSet32(op, a, b, local), local)
+ }
+ Instruction::BinOpLocalConstTee32(op, src, c, local) => {
+ (Instruction::BinOpLocalConstSet32(op, src, c, local), local)
+ }
+ Instruction::LoadLocalTee32(memarg, addr, local) => {
+ (Instruction::LoadLocalSet32(memarg, addr, local), local.into())
+ }
+ _ => return None,
+ })
+}
+
+fn scalar_const_32(instr: Instruction, op_instr: Instruction) -> Option<i32> {
+ match instr {
+ Instruction::I32Const(c) if int_bin_op_32(op_instr).is_some() => Some(c),
+ Instruction::F32Const(c) if float_bin_op_32(op_instr).is_some() => Some(f32_const_bits(c)),
+ _ => None,
+ }
+}
+
+fn bin_op_128(instr: Instruction) -> Option<BinOp128> {
+ Some(match instr {
+ Instruction::V128And => BinOp128::And,
+ Instruction::V128AndNot => BinOp128::AndNot,
+ Instruction::V128Or => BinOp128::Or,
+ Instruction::V128Xor => BinOp128::Xor,
+ Instruction::I64x2Add => BinOp128::I64x2Add,
+ Instruction::I64x2Mul => BinOp128::I64x2Mul,
+ _ => return None,
+ })
+}
+
+fn f32_const_bits(value: f32) -> i32 {
+ i32::from_ne_bytes(value.to_bits().to_ne_bytes())
+}
+
+fn f64_const_bits(value: f64) -> i64 {
+ i64::from_ne_bytes(value.to_bits().to_ne_bytes())
+}
+
fn cmp_op_64(instr: Instruction) -> Option<CmpOp> {
Some(match instr {
Instruction::I64Eq => CmpOp::Eq,
@@ -257,6 +513,29 @@ fn inverse_cmp_op(op: CmpOp) -> CmpOp {
}
}
+fn previous_non_nop_3(instrs: &[Instruction], read: usize) -> Option<[(usize, Instruction); 3]> {
+ let mut out = [(0usize, Instruction::Nop); 3];
+ let mut filled = 0usize;
+
+ for idx in (0..read).rev() {
+ let instr = instrs[idx];
+ if matches!(instr, Instruction::MergeBarrier) {
+ return None;
+ }
+ if matches!(instr, Instruction::Nop) {
+ continue;
+ }
+
+ out[2 - filled] = (idx, instr);
+ filled += 1;
+ if filled == 3 {
+ return Some(out);
+ }
+ }
+
+ None
+}
+
fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData) {
let old_len = instructions.len();
if old_len == 0 {
@@ -266,7 +545,8 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct
let mut removed_before = Vec::with_capacity(old_len + 1);
removed_before.push(0u32);
instructions.iter().for_each(|instr| {
- let removed = removed_before.last().copied().unwrap_or(0) + u32::from(matches!(instr, Instruction::Nop));
+ let removed = removed_before.last().copied().unwrap_or(0)
+ + u32::from(matches!(instr, Instruction::Nop | Instruction::MergeBarrier));
removed_before.push(removed);
});
@@ -290,6 +570,10 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct
Instruction::Jump(ip)
| Instruction::JumpIfZero(ip)
| Instruction::JumpIfNonZero(ip)
+ | Instruction::JumpIfZero32(ip)
+ | Instruction::JumpIfNonZero32(ip)
+ | Instruction::JumpIfZero64(ip)
+ | Instruction::JumpIfNonZero64(ip)
| Instruction::JumpCmpStackConst32 { target_ip: ip, .. }
| Instruction::JumpCmpStackConst64 { target_ip: ip, .. }
| Instruction::JumpCmpLocalConst32 { target_ip: ip, .. }
@@ -297,16 +581,16 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct
| Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. }
| Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. }
| Instruction::BranchTable(ip, _, _) => ip,
- _ => return !matches!(instr, Instruction::Nop),
+ _ => return !matches!(instr, Instruction::Nop | Instruction::MergeBarrier),
};
let old_target = *ip as usize;
if old_target > old_len {
- return !matches!(instr, Instruction::Nop);
+ return !matches!(instr, Instruction::Nop | Instruction::MergeBarrier);
}
*ip -= removed_before[old_target];
debug_assert!(*ip < compacted_len, "remapped jump target points past end of function");
- !matches!(instr, Instruction::Nop)
+ !matches!(instr, Instruction::Nop | Instruction::MergeBarrier)
});
}
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index 5c99df2..5e1ca15 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -240,8 +240,8 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_loop(&mut self, _ty: wasmparser::BlockType) -> Self::Output {
- if !matches!(self.instructions.last(), Some(Instruction::Nop)) {
- self.instructions.push(Instruction::Nop); // add nop to ensure that no superinstruction can be merged across block boundaries
+ if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) {
+ self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries
}
let start_ip = self.instructions.len();
self.ctx_stack.push(LoweringCtx { kind: BlockKind::Loop, has_else: false, start_ip, branch_jumps: Vec::new() });
@@ -266,8 +266,8 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
ctx.has_else = true;
ctx.branch_jumps.push(jump_ip);
self.patch_jump_if_zero(cond_jump_ip, self.instructions.len());
- if !matches!(self.instructions.last(), Some(Instruction::Nop)) {
- self.instructions.push(Instruction::Nop); // add nop to ensure that no superinstruction can be merged across block boundaries
+ if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) {
+ self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries
}
};
};
@@ -276,8 +276,8 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
fn visit_end(&mut self) -> Self::Output {
if let Some(ctx) = self.ctx_stack.pop() {
self.patch_end_jumps(ctx, self.instructions.len());
- if !matches!(self.instructions.last(), Some(Instruction::Nop)) {
- self.instructions.push(Instruction::Nop); // add nop to ensure that no superinstruction can be merged across block boundaries
+ if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) {
+ self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries
}
} else {
self.instructions.push(Instruction::Return);
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 4cacea6..a800433 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -48,6 +48,75 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
#[inline(always)]
+ fn exec_binop_32(&self, op: BinOp, lhs: Value32, rhs: Value32) -> Value32 {
+ match op {
+ BinOp::IAdd => ((lhs as i32).wrapping_add(rhs as i32)) as u32,
+ BinOp::ISub => ((lhs as i32).wrapping_sub(rhs as i32)) as u32,
+ BinOp::IMul => ((lhs as i32).wrapping_mul(rhs as i32)) as u32,
+ BinOp::IAnd => lhs & rhs,
+ BinOp::IOr => lhs | rhs,
+ BinOp::IXor => lhs ^ rhs,
+ BinOp::IShl => ((lhs as i32).wasm_shl(rhs as i32)) as u32,
+ BinOp::IShrS => ((lhs as i32).wasm_shr(rhs as i32)) as u32,
+ BinOp::IShrU => lhs.wasm_shr(rhs),
+ BinOp::IRotl => ((lhs as i32).wasm_rotl(rhs as i32)) as u32,
+ BinOp::IRotr => ((lhs as i32).wasm_rotr(rhs as i32)) as u32,
+ BinOp::FAdd => (f32::from_bits(lhs) + f32::from_bits(rhs)).to_bits(),
+ BinOp::FSub => (f32::from_bits(lhs) - f32::from_bits(rhs)).to_bits(),
+ BinOp::FMul => (f32::from_bits(lhs) * f32::from_bits(rhs)).to_bits(),
+ BinOp::FDiv => (f32::from_bits(lhs) / f32::from_bits(rhs)).to_bits(),
+ BinOp::FMin => f32::from_bits(lhs).tw_minimum(f32::from_bits(rhs)).to_bits(),
+ BinOp::FMax => f32::from_bits(lhs).tw_maximum(f32::from_bits(rhs)).to_bits(),
+ BinOp::FCopysign => f32::from_bits(lhs).copysign(f32::from_bits(rhs)).to_bits(),
+ }
+ }
+
+ #[inline(always)]
+ fn exec_binop_64(&self, op: BinOp, lhs: Value64, rhs: Value64) -> Value64 {
+ match op {
+ BinOp::IAdd => ((lhs as i64).wrapping_add(rhs as i64)) as u64,
+ BinOp::ISub => ((lhs as i64).wrapping_sub(rhs as i64)) as u64,
+ BinOp::IMul => ((lhs as i64).wrapping_mul(rhs as i64)) as u64,
+ BinOp::IAnd => lhs & rhs,
+ BinOp::IOr => lhs | rhs,
+ BinOp::IXor => lhs ^ rhs,
+ BinOp::IShl => ((lhs as i64).wasm_shl(rhs as i64)) as u64,
+ BinOp::IShrS => ((lhs as i64).wasm_shr(rhs as i64)) as u64,
+ BinOp::IShrU => lhs.wasm_shr(rhs),
+ BinOp::IRotl => ((lhs as i64).wasm_rotl(rhs as i64)) as u64,
+ BinOp::IRotr => ((lhs as i64).wasm_rotr(rhs as i64)) as u64,
+ BinOp::FAdd => (f64::from_bits(lhs) + f64::from_bits(rhs)).to_bits(),
+ BinOp::FSub => (f64::from_bits(lhs) - f64::from_bits(rhs)).to_bits(),
+ BinOp::FMul => (f64::from_bits(lhs) * f64::from_bits(rhs)).to_bits(),
+ BinOp::FDiv => (f64::from_bits(lhs) / f64::from_bits(rhs)).to_bits(),
+ BinOp::FMin => f64::from_bits(lhs).tw_minimum(f64::from_bits(rhs)).to_bits(),
+ BinOp::FMax => f64::from_bits(lhs).tw_maximum(f64::from_bits(rhs)).to_bits(),
+ BinOp::FCopysign => f64::from_bits(lhs).copysign(f64::from_bits(rhs)).to_bits(),
+ }
+ }
+
+ #[inline(always)]
+ fn exec_binop_128(&self, op: BinOp128, lhs: Value128, rhs: Value128) -> Value128 {
+ match op {
+ BinOp128::And => lhs.v128_and(rhs),
+ BinOp128::AndNot => lhs.v128_andnot(rhs),
+ BinOp128::Or => lhs.v128_or(rhs),
+ BinOp128::Xor => lhs.v128_xor(rhs),
+ BinOp128::I64x2Add => lhs.i64x2_add(rhs),
+ BinOp128::I64x2Mul => lhs.i64x2_mul(rhs),
+ }
+ }
+
+ #[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) => {{
@@ -128,7 +197,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
use tinywasm_types::Instruction::*;
#[rustfmt::skip]
match next {
- Nop => {}
+ Nop | MergeBarrier => {}
Unreachable => return Err(Trap::Unreachable),
Drop32 => self.store.value_stack.drop::<Value32>(),
Drop64 => self.store.value_stack.drop::<Value64>(),
@@ -144,8 +213,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
ReturnCallSelf => { self.exec_return_call_self()?; return Ok(None); }
ReturnCallIndirect(ty, table) => { self.exec_call_indirect::<true>(*ty, *table)?; return Ok(None); }
Jump(ip) => { self.exec_jump(*ip); return Ok(None); }
- JumpIfZero(ip) => if self.exec_jump_if_zero(*ip) { return Ok(None) },
- JumpIfNonZero(ip) => if self.exec_jump_if_non_zero(*ip) { return Ok(None) },
+ JumpIfZero(ip) => { let condition = self.store.value_stack.pop::<i32>() == 0; if self.jump_if(condition, *ip) { return Ok(None) }}
+ JumpIfNonZero(ip) => { let condition = self.store.value_stack.pop::<i32>() != 0; if self.jump_if(condition, *ip) { return Ok(None) }}
+ JumpIfZero32(ip) => { let condition = self.store.value_stack.pop::<Value32>() == 0; if self.jump_if(condition, *ip) { return Ok(None) }}
+ JumpIfNonZero32(ip) => { let condition = self.store.value_stack.pop::<Value32>() != 0; if self.jump_if(condition, *ip) { return Ok(None) }}
+ JumpIfZero64(ip) => { let condition = self.store.value_stack.pop::<Value64>() == 0; if self.jump_if(condition, *ip) { return Ok(None) }}
+ JumpIfNonZero64(ip) => { let condition = self.store.value_stack.pop::<Value64>() != 0; if self.jump_if(condition, *ip) { return Ok(None) }}
JumpCmpStackConst32 { target_ip, imm, op } => if self.exec_jump_cmp_stack_const_32(*target_ip, *imm, *op) { return Ok(None) },
JumpCmpStackConst64 { target_ip, imm, op } => if self.exec_jump_cmp_stack_const_64(*target_ip, *imm, *op) { return Ok(None) },
JumpCmpLocalConst32 { target_ip, local, imm, op } => if self.exec_jump_cmp_local_const_32(*target_ip, *local, *imm, *op) { return Ok(None) },
@@ -170,14 +243,58 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
LocalCopy32(from, to) => self.store.value_stack.local_set(&self.cf, *to, self.store.value_stack.local_get::<Value32>(&self.cf, *from)),
LocalCopy64(from, to) => self.store.value_stack.local_set(&self.cf, *to, self.store.value_stack.local_get::<Value64>(&self.cf, *from)),
LocalCopy128(from, to) => self.store.value_stack.local_set(&self.cf, *to, self.store.value_stack.local_get::<Value128>(&self.cf, *from)),
- AddLocalLocal32(a, b) => self.store.value_stack.push(self.store.value_stack.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.value_stack.local_get::<i32>(&self.cf, *b)))?,
- AddLocalLocal64(a, b) => self.store.value_stack.push(self.store.value_stack.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.value_stack.local_get::<i64>(&self.cf, *b)))?,
- AddLocalLocalSet32(a, b, dst) => self.store.value_stack.local_set::<i32>(&self.cf, *dst, self.store.value_stack.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.value_stack.local_get::<i32>(&self.cf, *b))),
- AddLocalLocalSet64(a, b, dst) => self.store.value_stack.local_set::<i64>(&self.cf, *dst, self.store.value_stack.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.value_stack.local_get::<i64>(&self.cf, *b))),
AddConst32(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)),
AddConst64(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)),
- AddLocalConst32(local_index, c) => self.store.value_stack.local_update::<Value32>(&self.cf, *local_index, |local| local.wrapping_add(*c as u32)),
- AddLocalConst64(local_index, c) => self.store.value_stack.local_update::<Value64>(&self.cf, *local_index, |local| local.wrapping_add(*c as u64)),
+ IncLocal32(local_index, delta) => {
+ let value = self.store.value_stack.local_get::<Value32>(&self.cf, *local_index).wrapping_add(*delta as u32);
+ self.store.value_stack.local_set::<Value32>(&self.cf, *local_index, value);
+ }
+ IncLocal64(local_index, delta) => {
+ let value = self.store.value_stack.local_get::<Value64>(&self.cf, *local_index).wrapping_add(*delta as u64);
+ self.store.value_stack.local_set::<Value64>(&self.cf, *local_index, value);
+ }
+ BinOpLocalLocal32(op, a, b) => self.store.value_stack.push(self.exec_binop_32(*op, self.store.value_stack.local_get::<Value32>(&self.cf, *a), self.store.value_stack.local_get::<Value32>(&self.cf, *b)))?,
+ BinOpLocalLocal64(op, a, b) => self.store.value_stack.push(self.exec_binop_64(*op, self.store.value_stack.local_get::<Value64>(&self.cf, *a), self.store.value_stack.local_get::<Value64>(&self.cf, *b)))?,
+ BinOpLocalLocal128(op, a, b) => self.store.value_stack.push(self.exec_binop_128(*op, self.store.value_stack.local_get::<Value128>(&self.cf, *a), self.store.value_stack.local_get::<Value128>(&self.cf, *b)))?,
+ BinOpLocalLocalSet32(op, a, b, dst) => self.store.value_stack.local_set::<Value32>(&self.cf, *dst, self.exec_binop_32(*op, self.store.value_stack.local_get::<Value32>(&self.cf, *a), self.store.value_stack.local_get::<Value32>(&self.cf, *b))),
+ BinOpLocalLocalSet64(op, a, b, dst) => self.store.value_stack.local_set::<Value64>(&self.cf, *dst, self.exec_binop_64(*op, self.store.value_stack.local_get::<Value64>(&self.cf, *a), self.store.value_stack.local_get::<Value64>(&self.cf, *b))),
+ BinOpLocalLocalSet128(op, a, b, dst) => self.store.value_stack.local_set::<Value128>(&self.cf, *dst, self.exec_binop_128(*op, self.store.value_stack.local_get::<Value128>(&self.cf, *a), self.store.value_stack.local_get::<Value128>(&self.cf, *b))),
+ BinOpLocalLocalTee32(op, a, b, dst) => {
+ let value = self.exec_binop_32(*op, self.store.value_stack.local_get::<Value32>(&self.cf, *a), self.store.value_stack.local_get::<Value32>(&self.cf, *b));
+ self.store.value_stack.local_set::<Value32>(&self.cf, *dst, value);
+ self.store.value_stack.push(value)?;
+ }
+ BinOpLocalLocalTee64(op, a, b, dst) => {
+ let value = self.exec_binop_64(*op, self.store.value_stack.local_get::<Value64>(&self.cf, *a), self.store.value_stack.local_get::<Value64>(&self.cf, *b));
+ self.store.value_stack.local_set::<Value64>(&self.cf, *dst, value);
+ self.store.value_stack.push(value)?;
+ }
+ BinOpLocalLocalTee128(op, a, b, dst) => {
+ let value = self.exec_binop_128(*op, self.store.value_stack.local_get::<Value128>(&self.cf, *a), self.store.value_stack.local_get::<Value128>(&self.cf, *b));
+ self.store.value_stack.local_set::<Value128>(&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, self.store.value_stack.local_get::<Value32>(&self.cf, *local_index), *c as u32))?,
+ BinOpLocalConst64(op, local_index, c) => self.store.value_stack.push(self.exec_binop_64(*op, self.store.value_stack.local_get::<Value64>(&self.cf, *local_index), *c as u64))?,
+ BinOpLocalConst128(op, local_index, c) => self.store.value_stack.push(self.exec_binop_128(*op, self.store.value_stack.local_get::<Value128>(&self.cf, *local_index), self.get_v128_const(*c)))?,
+ BinOpLocalConstSet32(op, local_index, c, dst) => self.store.value_stack.local_set::<Value32>(&self.cf, *dst, self.exec_binop_32(*op, self.store.value_stack.local_get::<Value32>(&self.cf, *local_index), *c as u32)),
+ BinOpLocalConstSet64(op, local_index, c, dst) => self.store.value_stack.local_set::<Value64>(&self.cf, *dst, self.exec_binop_64(*op, self.store.value_stack.local_get::<Value64>(&self.cf, *local_index), *c as u64)),
+ BinOpLocalConstSet128(op, local_index, c, dst) => self.store.value_stack.local_set::<Value128>(&self.cf, *dst, self.exec_binop_128(*op, self.store.value_stack.local_get::<Value128>(&self.cf, *local_index), self.get_v128_const(*c))),
+ BinOpLocalConstTee32(op, local_index, c, dst) => {
+ let value = self.exec_binop_32(*op, self.store.value_stack.local_get::<Value32>(&self.cf, *local_index), *c as u32);
+ self.store.value_stack.local_set::<Value32>(&self.cf, *dst, value);
+ self.store.value_stack.push(value)?;
+ }
+ BinOpLocalConstTee64(op, local_index, c, dst) => {
+ let value = self.exec_binop_64(*op, self.store.value_stack.local_get::<Value64>(&self.cf, *local_index), *c as u64);
+ self.store.value_stack.local_set::<Value64>(&self.cf, *dst, value);
+ self.store.value_stack.push(value)?;
+ }
+ BinOpLocalConstTee128(op, local_index, c, dst) => {
+ let value = self.exec_binop_128(*op, self.store.value_stack.local_get::<Value128>(&self.cf, *local_index), self.get_v128_const(*c));
+ self.store.value_stack.local_set::<Value128>(&self.cf, *dst, value);
+ self.store.value_stack.push(value)?;
+ }
SetLocalConst32(local_index, c) => self.store.value_stack.local_set::<i32>(&self.cf, *local_index, *c),
SetLocalConst64(local_index, c) => self.store.value_stack.local_set::<i64>(&self.cf, *local_index, *c),
StoreLocalLocal32(m, addr_local, value_local) => self.exec_store_local_local::<u32, 4>(*m, *addr_local, *value_local)?,
@@ -188,27 +305,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
LoadLocalSet32(m, addr_local, dst_local) => self.exec_load_local_set::<i32, 4>(*m, *addr_local, *dst_local)?,
LoadLocalTee128(m, addr_local, dst_local) => self.exec_load_local_tee::<Value128, 16>(*m, *addr_local, *dst_local)?,
LoadLocalSet128(m, addr_local, dst_local) => self.exec_load_local_set::<Value128, 16>(*m, *addr_local, *dst_local)?,
- AndConstTee32(c, local_index) => {
- stack_op!(unary i32, |v| v & *c);
- stack_op!(local_tee i32, local_index);
- }
- SubConstTee32(c, local_index) => {
- stack_op!(unary i32, |v| v.wrapping_sub(*c));
- stack_op!(local_tee i32, local_index);
- }
- AndConstTee64(c, local_index) => {
- stack_op!(unary i64, |v| v & *c);
- stack_op!(local_tee i64, local_index);
- }
- SubConstTee64(c, local_index) => {
- stack_op!(unary i64, |v| v.wrapping_sub(*c));
- stack_op!(local_tee i64, local_index);
- }
- XorRotlConst64(c) => stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)),
- XorRotlConstTee64(c, local_index) => {
- stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32));
- stack_op!(local_tee i64, local_index);
- }
+ AndConstTee32(c, local_index) => { stack_op!(unary i32, |v| v & *c); stack_op!(local_tee i32, local_index); }
+ SubConstTee32(c, local_index) => { stack_op!(unary i32, |v| v.wrapping_sub(*c)); stack_op!(local_tee i32, local_index); }
+ AndConstTee64(c, local_index) => { stack_op!(unary i64, |v| v & *c); stack_op!(local_tee i64, local_index); }
+ SubConstTee64(c, local_index) => { stack_op!(unary i64, |v| v.wrapping_sub(*c)); stack_op!(local_tee i64, local_index); }
LocalTee32(local_index) => stack_op!(local_tee Value32, local_index),
LocalTee64(local_index) => stack_op!(local_tee Value64, local_index),
LocalTee128(local_index) => stack_op!(local_tee Value128, local_index),
@@ -456,16 +556,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]))?,
- V128Const(arg) => {
- let val = match self.func.data.v128_constants.get(*arg as usize) {
- Some(val) => *val,
- None => {
- cold_path();
- unreachable!("invalid v128 constant index");
- }
- };
- self.exec_const(Value128(val))?
- },
+ V128Const(arg) => self.exec_const(self.get_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),
@@ -721,18 +812,6 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
#[inline(always)]
- fn exec_jump_if_zero(&mut self, ip: u32) -> bool {
- let condition = self.store.value_stack.pop::<i32>() == 0;
- self.jump_if(condition, ip)
- }
-
- #[inline(always)]
- fn exec_jump_if_non_zero(&mut self, ip: u32) -> bool {
- let condition = self.store.value_stack.pop::<i32>() != 0;
- self.jump_if(condition, ip)
- }
-
- #[inline(always)]
fn exec_jump_cmp_stack_const_32(&mut self, target_ip: u32, imm: i32, op: CmpOp) -> bool {
let condition = cmp_i32(self.store.value_stack.pop::<i32>(), imm, op);
self.jump_if(condition, target_ip)
diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs
index 80ea044..631fd00 100644
--- a/crates/tinywasm/src/interpreter/stack/value_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs
@@ -77,14 +77,6 @@ impl<T: Copy + Default> Stack<T> {
}
#[inline(always)]
- pub(crate) fn get_mut(&mut self, index: usize) -> &mut T {
- self.data.get_mut(index).unwrap_or_else(|| {
- cold_path();
- unreachable!("Stack index out of bounds, this is a bug");
- })
- }
-
- #[inline(always)]
pub(crate) fn truncate_keep(&mut self, n: usize, end_keep: usize) {
let len = self.data.len();
debug_assert!(n <= len);
@@ -247,16 +239,6 @@ impl ValueStack {
}
#[inline]
- pub(crate) fn local_update<T: InternalValue>(
- &mut self,
- frame: &CallFrame,
- index: LocalAddr,
- func: impl FnOnce(T) -> T,
- ) {
- T::local_update(self, frame, index, func)
- }
-
- #[inline]
pub(crate) fn local_set<T: InternalValue>(&mut self, frame: &CallFrame, index: LocalAddr, value: T) {
T::local_set(self, frame, index, value);
}
diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs
index 1971279..ac5ca07 100644
--- a/crates/tinywasm/src/interpreter/values.rs
+++ b/crates/tinywasm/src/interpreter/values.rs
@@ -148,7 +148,6 @@ mod sealed {
pub(crate) trait InternalValue: sealed::Sealed + Into<TinyWasmValue> + Copy + Default {
fn stack_push(stack: &mut ValueStack, value: Self) -> Result<(), crate::Trap>;
fn local_get(stack: &ValueStack, frame: &CallFrame, index: LocalAddr) -> Self;
- fn local_update(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, func: impl FnOnce(Self) -> Self);
fn local_set(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, value: Self);
fn stack_pop(stack: &mut ValueStack) -> Self;
fn stack_peek(stack: &ValueStack) -> Self;
@@ -177,12 +176,6 @@ macro_rules! impl_internalvalue {
}
#[inline(always)]
- fn local_update(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, func: impl FnOnce(Self) -> Self) {
- let slot = stack.$stack.get_mut(frame.locals_base.$stack_base as usize + index as usize);
- *slot = $to_stack(func($from_stack(*slot)));
- }
-
- #[inline(always)]
fn local_set(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, value: Self) {
stack.$stack.set(frame.locals_base.$stack_base as usize + index as usize, $to_stack(value));
}
diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs
index b26a51a..48158de 100644
--- a/crates/types/src/instructions.rs
+++ b/crates/types/src/instructions.rs
@@ -65,6 +65,42 @@ pub enum CmpOp {
GeU,
}
+#[derive(Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "debug", derive(Debug))]
+#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
+pub enum BinOp {
+ IAdd,
+ ISub,
+ IMul,
+ IAnd,
+ IOr,
+ IXor,
+ IShl,
+ IShrS,
+ IShrU,
+ IRotl,
+ IRotr,
+ FAdd,
+ FSub,
+ FMul,
+ FDiv,
+ FMin,
+ FMax,
+ FCopysign,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "debug", derive(Debug))]
+#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
+pub enum BinOp128 {
+ And,
+ AndNot,
+ Or,
+ Xor,
+ I64x2Add,
+ I64x2Mul,
+}
+
/// A WebAssembly Instruction
///
/// These are our own internal bytecode instructions so they may not match the spec exactly.
@@ -77,10 +113,25 @@ pub enum CmpOp {
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
pub enum Instruction {
LocalCopy32(LocalAddr, LocalAddr), LocalCopy64(LocalAddr, LocalAddr), LocalCopy128(LocalAddr, LocalAddr),
- AddLocalLocal32(LocalAddr, LocalAddr), AddLocalLocal64(LocalAddr, LocalAddr),
- AddLocalLocalSet32(LocalAddr, LocalAddr, LocalAddr), AddLocalLocalSet64(LocalAddr, LocalAddr, LocalAddr),
AddConst32(i32), AddConst64(i64),
- AddLocalConst32(LocalAddr, i32), AddLocalConst64(LocalAddr, i64),
+ IncLocal32(LocalAddr, i32), IncLocal64(LocalAddr, i64),
+ // The 32/64 suffix describes the operand width. Future compare-style ops may still yield i32 results.
+ BinOpLocalLocal32(BinOp, LocalAddr, LocalAddr), BinOpLocalLocal64(BinOp, LocalAddr, LocalAddr),
+ BinOpLocalLocal128(BinOp128, LocalAddr, LocalAddr),
+ BinOpLocalLocalSet32(BinOp, LocalAddr, LocalAddr, LocalAddr),
+ BinOpLocalLocalSet64(BinOp, LocalAddr, LocalAddr, LocalAddr),
+ BinOpLocalLocalSet128(BinOp128, LocalAddr, LocalAddr, LocalAddr),
+ BinOpLocalLocalTee32(BinOp, LocalAddr, LocalAddr, LocalAddr),
+ BinOpLocalLocalTee64(BinOp, LocalAddr, LocalAddr, LocalAddr),
+ BinOpLocalLocalTee128(BinOp128, LocalAddr, LocalAddr, LocalAddr),
+ BinOpLocalConst32(BinOp, LocalAddr, i32), BinOpLocalConst64(BinOp, LocalAddr, i64),
+ BinOpLocalConst128(BinOp128, LocalAddr, ConstIdx),
+ BinOpLocalConstSet32(BinOp, LocalAddr, i32, LocalAddr),
+ BinOpLocalConstSet64(BinOp, LocalAddr, i64, LocalAddr),
+ BinOpLocalConstSet128(BinOp128, LocalAddr, ConstIdx, LocalAddr),
+ BinOpLocalConstTee32(BinOp, LocalAddr, i32, LocalAddr),
+ BinOpLocalConstTee64(BinOp, LocalAddr, i64, LocalAddr),
+ BinOpLocalConstTee128(BinOp128, LocalAddr, ConstIdx, LocalAddr),
SetLocalConst32(LocalAddr, i32), SetLocalConst64(LocalAddr, i64),
StoreLocalLocal32(MemoryArg, u8, u8),
StoreLocalLocal64(MemoryArg, u8, u8),
@@ -94,16 +145,19 @@ pub enum Instruction {
SubConstTee32(i32, LocalAddr),
AndConstTee64(i64, LocalAddr),
SubConstTee64(i64, LocalAddr),
- XorRotlConst64(i64),
- XorRotlConstTee64(i64, LocalAddr),
// > Control Instructions (jump-oriented, lowered from structured control during parsing)
// See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions>
Unreachable,
Nop,
+ MergeBarrier,
Jump(u32),
JumpIfZero(u32),
JumpIfNonZero(u32),
+ JumpIfZero32(u32),
+ JumpIfNonZero32(u32),
+ JumpIfZero64(u32),
+ JumpIfNonZero64(u32),
JumpCmpStackConst32 { target_ip: u32, imm: i32, op: CmpOp },
JumpCmpStackConst64 { target_ip: u32, imm: i64, op: CmpOp },
JumpCmpLocalConst32 { target_ip: u32, local: LocalAddr, imm: i32, op: CmpOp },