summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/parser/src/conversion.rs3
-rw-r--r--crates/parser/src/error.rs3
-rw-r--r--crates/parser/src/lib.rs16
-rw-r--r--crates/parser/src/macros.rs220
-rw-r--r--crates/parser/src/module.rs7
-rw-r--r--crates/parser/src/optimize.rs611
-rw-r--r--crates/parser/src/visit.rs175
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs1336
-rw-r--r--crates/tinywasm/src/interpreter/simd/instructions.rs4
-rw-r--r--crates/tinywasm/src/interpreter/simd/mod.rs6
-rw-r--r--crates/tinywasm/src/interpreter/simd/tests.rs4
-rw-r--r--crates/tinywasm/src/interpreter/stack/call_stack.rs7
-rw-r--r--crates/types/src/instructions.rs11
13 files changed, 1208 insertions, 1195 deletions
diff --git a/crates/parser/src/conversion.rs b/crates/parser/src/conversion.rs
index 32f2944..5de3a73 100644
--- a/crates/parser/src/conversion.rs
+++ b/crates/parser/src/conversion.rs
@@ -1,5 +1,4 @@
-use crate::Result;
-use crate::{module::Code, visit::process_operators_and_validate};
+use crate::{Result, module::Code, visit::process_operators_and_validate};
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
use tinywasm_types::*;
use wasmparser::{FuncValidator, FuncValidatorAllocations, OperatorsReader, ValidatorResources};
diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs
index 9ff2f79..22edd39 100644
--- a/crates/parser/src/error.rs
+++ b/crates/parser/src/error.rs
@@ -1,6 +1,5 @@
-use core::fmt::{Debug, Display};
-
use alloc::string::{String, ToString};
+use core::fmt::{Debug, Display};
use wasmparser::Encoding;
#[derive(Debug)]
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 850042c..ad58cc2 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -31,6 +31,7 @@ pub(crate) mod log {
mod conversion;
mod error;
+mod macros;
mod module;
mod optimize;
mod visit;
@@ -42,19 +43,8 @@ pub use tinywasm_types::TinyWasmModule;
/// Parser optimization and lowering options.
#[non_exhaustive]
-#[derive(Debug, Clone)]
-pub struct ParserOptions {
- /// Enable post-lowering DCE pass.
- /// Should be enabled by default, since the parser performs some optimizations that can result in dead code.
- /// Disabling this may result in larger modules, but faster parsing time.
- pub dce: bool,
-}
-
-impl Default for ParserOptions {
- fn default() -> Self {
- Self { dce: true }
- }
-}
+#[derive(Debug, Clone, Default)]
+pub struct ParserOptions {}
/// A WebAssembly parser
#[derive(Debug, Default)]
diff --git a/crates/parser/src/macros.rs b/crates/parser/src/macros.rs
new file mode 100644
index 0000000..97f0080
--- /dev/null
+++ b/crates/parser/src/macros.rs
@@ -0,0 +1,220 @@
+pub(crate) mod visit {
+ macro_rules! validate_then_visit {
+ ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {$(
+ fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
+ self.1.$visit($($($arg.clone()),*)?);
+ self.1.validator_visitor(self.0).$visit($($($arg),*)?)?;
+ Ok(())
+ }
+ )*};
+ }
+
+ macro_rules! define_operand {
+ ($name:ident($instr:expr, $ty:ty)) => {
+ fn $name(&mut self, arg: $ty) -> Self::Output {
+ self.instructions.push($instr(arg).into());
+ }
+ };
+
+ ($name:ident($instr:expr, $ty:ty, $ty2:ty)) => {
+ fn $name(&mut self, arg: $ty, arg2: $ty2) -> Self::Output {
+ self.instructions.push($instr(arg, arg2).into());
+ }
+ };
+
+ ($name:ident($instr:expr)) => {
+ fn $name(&mut self) -> Self::Output {
+ self.instructions.push($instr.into());
+ }
+ };
+ }
+
+ macro_rules! define_operands {
+ ($($name:ident($instr:ident $(,$ty:ty)*)),*) => {$(
+ define_operand!($name(Instruction::$instr $(,$ty)*));
+ )*};
+ }
+
+ macro_rules! define_mem_operands {
+ ($($name:ident($instr:ident)),*) => {$(
+ fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+ self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory)));
+ }
+ )*};
+ }
+
+ macro_rules! define_mem_operands_simd {
+ ($($name:ident($instr:ident)),*) => {$(
+ fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
+ self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory)).into());
+ }
+ )*};
+ }
+
+ macro_rules! define_mem_operands_simd_lane {
+ ($($name:ident($instr:ident)),*) => {$(
+ fn $name(&mut self, memarg: wasmparser::MemArg, lane: u8) -> Self::Output {
+ self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory), lane).into());
+ }
+ )*};
+ }
+
+ macro_rules! impl_visit_operator {
+ ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
+ $(impl_visit_operator!(@@$proposal $op $({ $($arg: $argty),* })? => $visit ($($ann:tt)*));)*
+ };
+
+ (@@mvp $($rest:tt)* ) => {};
+ (@@reference_types $($rest:tt)* ) => {};
+ (@@sign_extension $($rest:tt)* ) => {};
+ (@@saturating_float_to_int $($rest:tt)* ) => {};
+ (@@bulk_memory $($rest:tt)* ) => {};
+ (@@simd $($rest:tt)* ) => {};
+ (@@wide_arithmetic $($rest:tt)* ) => {};
+ (@@relaxed_simd $($rest:tt)* ) => {};
+ (@@tail_call $($rest:tt)* ) => {};
+
+ (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => {
+ fn $visit(&mut self $($(,_: $argty)*)?) {
+ self.unsupported(stringify!($visit))
+ }
+ };
+ }
+
+ pub(crate) use {
+ define_mem_operands, define_mem_operands_simd, define_mem_operands_simd_lane, define_operand, define_operands,
+ impl_visit_operator, validate_then_visit,
+ };
+}
+
+pub(crate) mod optimize {
+ macro_rules! replace {
+ ($instructions:ident, $read:ident, 1 => [$a:expr $(,)?]) => {{
+ $instructions[$read - 1] = Instruction::Nop;
+ $instructions[$read] = $a;
+ }};
+ ($instructions:ident, $read:ident, 1 => [$a:expr, $b:expr $(,)?]) => {{
+ $instructions[$read - 1] = $a;
+ $instructions[$read] = $b;
+ }};
+ ($instructions:ident, $read:ident, 2 => [$a:expr $(,)?]) => {{
+ $instructions[$read - 2] = Instruction::Nop;
+ $instructions[$read - 1] = Instruction::Nop;
+ $instructions[$read] = $a;
+ }};
+ ($instructions:ident, $read:ident, 2 => [$a:expr, $b:expr $(,)?]) => {{
+ $instructions[$read - 2] = Instruction::Nop;
+ $instructions[$read - 1] = $a;
+ $instructions[$read] = $b;
+ }};
+ ($instructions:ident, $read:ident, 2 => [$a:expr, $b:expr, $c:expr $(,)?]) => {{
+ $instructions[$read - 2] = $a;
+ $instructions[$read - 1] = $b;
+ $instructions[$read] = $c;
+ }};
+ ($instructions:ident, $read:ident, 3 => [$a:expr $(,)?]) => {{
+ $instructions[$read - 3] = Instruction::Nop;
+ $instructions[$read - 2] = Instruction::Nop;
+ $instructions[$read - 1] = Instruction::Nop;
+ $instructions[$read] = $a;
+ }};
+ ($instructions:ident, $read:ident, 3 => [$a:expr, $b:expr $(,)?]) => {{
+ $instructions[$read - 3] = Instruction::Nop;
+ $instructions[$read - 2] = Instruction::Nop;
+ $instructions[$read - 1] = $a;
+ $instructions[$read] = $b;
+ }};
+ ($instructions:ident, $read:ident, 3 => [$a:expr, $b:expr, $c:expr $(,)?]) => {{
+ $instructions[$read - 3] = Instruction::Nop;
+ $instructions[$read - 2] = $a;
+ $instructions[$read - 1] = $b;
+ $instructions[$read] = $c;
+ }};
+ ($instructions:ident, $read:ident, 3 => [$a:expr, $b:expr, $c:expr, $d:expr $(,)?]) => {{
+ $instructions[$read - 3] = $a;
+ $instructions[$read - 2] = $b;
+ $instructions[$read - 1] = $c;
+ $instructions[$read] = $d;
+ }};
+ ($instructions:ident, $read:ident, 1 => $out:expr) => {
+ replace!($instructions, $read, 1 => [$out]);
+ };
+ ($instructions:ident, $read:ident, 2 => $out:expr) => {
+ replace!($instructions, $read, 2 => [$out]);
+ };
+ ($instructions:ident, $read:ident, 3 => $out:expr) => {
+ replace!($instructions, $read, 3 => [$out]);
+ };
+ }
+
+ macro_rules! rewrite {
+ ($instructions:ident, $read:ident, [$a:pat] if ($($guard:tt)+) => [$($out:expr),+ $(,)?]) => {
+ rewrite!($instructions, $read, [$a] if ($($guard)+) => { replace!($instructions, $read, 1 => [$($out),+]); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat] if ($($guard:tt)+) => [$($out:expr),+ $(,)?]) => {
+ rewrite!($instructions, $read, [$a, $b] if ($($guard)+) => { replace!($instructions, $read, 2 => [$($out),+]); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] if ($($guard:tt)+) => [$($out:expr),+ $(,)?]) => {
+ rewrite!($instructions, $read, [$a, $b, $c] if ($($guard)+) => { replace!($instructions, $read, 3 => [$($out),+]); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat] => [$($out:expr),+ $(,)?]) => {
+ rewrite!($instructions, $read, [$a] => { replace!($instructions, $read, 1 => [$($out),+]); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat] => [$($out:expr),+ $(,)?]) => {
+ rewrite!($instructions, $read, [$a, $b] => { replace!($instructions, $read, 2 => [$($out),+]); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] => [$($out:expr),+ $(,)?]) => {
+ rewrite!($instructions, $read, [$a, $b, $c] => { replace!($instructions, $read, 3 => [$($out),+]); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat] if ($($guard:tt)+) => $body:block $(,)?) => {
+ if $read > 0 && let $a = $instructions[$read - 1] && $($guard)+ {
+ $body
+ }
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat] if ($($guard:tt)+) => $body:block $(,)?) => {
+ if $read > 1 && let ($a, $b) = ($instructions[$read - 2], $instructions[$read - 1]) && $($guard)+ {
+ $body
+ }
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] if ($($guard:tt)+) => $body:block $(,)?) => {
+ if $read > 2 && let ($a, $b, $c) = ($instructions[$read - 3], $instructions[$read - 2], $instructions[$read - 1]) && $($guard)+ {
+ $body
+ }
+ };
+ ($instructions:ident, $read:ident, [$a:pat] => $body:block $(,)?) => {
+ if $read > 0 && let $a = $instructions[$read - 1] {
+ $body
+ }
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat] => $body:block $(,)?) => {
+ if $read > 1 && let ($a, $b) = ($instructions[$read - 2], $instructions[$read - 1]) {
+ $body
+ }
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] => $body:block $(,)?) => {
+ if $read > 2 && let ($a, $b, $c) = ($instructions[$read - 3], $instructions[$read - 2], $instructions[$read - 1]) {
+ $body
+ }
+ };
+ ($instructions:ident, $read:ident, [$a:pat] if ($($guard:tt)+) => $out:expr $(,)?) => {
+ rewrite!($instructions, $read, [$a] if ($($guard)+) => { replace!($instructions, $read, 1 => $out); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat] if ($($guard:tt)+) => $out:expr $(,)?) => {
+ rewrite!($instructions, $read, [$a, $b] if ($($guard)+) => { replace!($instructions, $read, 2 => $out); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] if ($($guard:tt)+) => $out:expr $(,)?) => {
+ rewrite!($instructions, $read, [$a, $b, $c] if ($($guard)+) => { replace!($instructions, $read, 3 => $out); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat] => $out:expr $(,)?) => {
+ rewrite!($instructions, $read, [$a] => { replace!($instructions, $read, 1 => $out); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat] => $out:expr $(,)?) => {
+ rewrite!($instructions, $read, [$a, $b] => { replace!($instructions, $read, 2 => $out); })
+ };
+ ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] => $out:expr $(,)?) => {
+ rewrite!($instructions, $read, [$a, $b, $c] => { replace!($instructions, $read, 3 => $out); })
+ };
+ }
+
+ pub(crate) use {replace, rewrite};
+}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index 081af2b..461f79f 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -1,7 +1,6 @@
use crate::log::debug;
use crate::{ParseError, ParserOptions, Result, conversion, optimize};
-use alloc::string::ToString;
-use alloc::{format, vec::Vec};
+use alloc::{format, string::ToString, vec::Vec};
use tinywasm_types::*;
use wasmparser::{FuncValidatorAllocations, Payload, Validator};
@@ -168,7 +167,7 @@ impl ModuleReader {
Ok(())
}
- pub(crate) fn into_module(self, options: &ParserOptions) -> Result<TinyWasmModule> {
+ pub(crate) fn into_module(self, _options: &ParserOptions) -> Result<TinyWasmModule> {
if !self.end_reached {
return Err(ParseError::EndNotReached);
}
@@ -183,7 +182,7 @@ impl ModuleReader {
let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone();
let params = ValueCounts::from_iter(ty.params());
let self_func = (imported_func_count + func_idx) as u32;
- let instructions = optimize::optimize_instructions(instructions, &mut data, self_func, options);
+ let instructions = optimize::optimize_instructions(instructions, &mut data, self_func);
WasmFunction { instructions: ArcSlice::from(instructions), data, locals, params, ty }
},
);
diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs
index 7308262..0539b61 100644
--- a/crates/parser/src/optimize.rs
+++ b/crates/parser/src/optimize.rs
@@ -1,440 +1,241 @@
-use crate::ParserOptions;
+use crate::macros::optimize::*;
use alloc::vec::Vec;
use tinywasm_types::{CmpOp, Instruction, WasmFunctionData};
-fn cmp_op(instr: Instruction) -> Option<CmpOp> {
- Some(match instr {
- Instruction::I32Eq => CmpOp::Eq,
- Instruction::I32Ne => CmpOp::Ne,
- Instruction::I32LtS => CmpOp::LtS,
- Instruction::I32LtU => CmpOp::LtU,
- Instruction::I32GtS => CmpOp::GtS,
- Instruction::I32GtU => CmpOp::GtU,
- Instruction::I32LeS => CmpOp::LeS,
- Instruction::I32LeU => CmpOp::LeU,
- Instruction::I32GeS => CmpOp::GeS,
- Instruction::I32GeU => CmpOp::GeU,
- _ => return None,
- })
-}
-
-fn inverse_cmp_op(op: CmpOp) -> CmpOp {
- match op {
- CmpOp::Eq => CmpOp::Ne,
- CmpOp::Ne => CmpOp::Eq,
- CmpOp::LtS => CmpOp::GeS,
- CmpOp::LtU => CmpOp::GeU,
- CmpOp::GtS => CmpOp::LeS,
- CmpOp::GtU => CmpOp::LeU,
- CmpOp::LeS => CmpOp::GtS,
- CmpOp::LeU => CmpOp::GtU,
- CmpOp::GeS => CmpOp::LtS,
- CmpOp::GeU => CmpOp::LtU,
- }
-}
-
pub(crate) fn optimize_instructions(
mut instructions: Vec<Instruction>,
function_data: &mut WasmFunctionData,
self_func_addr: u32,
- options: &ParserOptions,
) -> Vec<Instruction> {
rewrite(&mut instructions, self_func_addr);
- if options.dce {
- dce(&mut instructions, function_data);
- }
+ remove_nop(&mut instructions, function_data);
instructions
}
-fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) {
- for read in 0..instructions.len() {
- match instructions[read] {
- Instruction::LocalCopy32(a, b) if a == b => instructions[read] = Instruction::Nop,
- Instruction::LocalCopy64(a, b) if a == b => instructions[read] = Instruction::Nop,
- Instruction::LocalCopy128(a, b) if a == b => instructions[read] = Instruction::Nop,
- Instruction::Call(addr) if addr == self_func_addr => instructions[read] = Instruction::CallSelf,
- Instruction::ReturnCall(addr) if addr == self_func_addr => instructions[read] = Instruction::ReturnCallSelf,
- Instruction::I32Add => {
- if read > 1
- && let (Instruction::LocalGet32(a), Instruction::LocalGet32(b)) =
- (instructions[read - 2], instructions[read - 1])
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddLocalLocal32(a, b);
- }
-
- if read > 0 {
- match instructions[read - 1] {
- Instruction::I32Const(c) if read > 1 => {
- if let Instruction::LocalGet32(local) = instructions[read - 2] {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::LocalGet32(local);
- instructions[read] = Instruction::AddConst32(c);
- } else {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddConst32(c);
- }
- }
- Instruction::I32Const(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddConst32(c);
- }
- _ => {}
- }
- }
- }
- Instruction::I64Add => {
- if read > 1
- && let (Instruction::LocalGet64(a), Instruction::LocalGet64(b)) =
- (instructions[read - 2], instructions[read - 1])
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddLocalLocal64(a, b);
- }
-
- if read > 0 {
- match instructions[read - 1] {
- Instruction::I64Const(c) if read > 1 => {
- if let Instruction::LocalGet64(local) = instructions[read - 2] {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::LocalGet64(local);
- instructions[read] = Instruction::AddConst64(c);
- } else {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddConst64(c);
- }
- }
- Instruction::I64Const(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddConst64(c);
- }
- _ => {}
- }
- }
+fn rewrite(instrs: &mut [Instruction], self_func_addr: u32) {
+ use Instruction::*;
+ for i in 0..instrs.len() {
+ match instrs[i] {
+ LocalCopy32(a, b) if a == b => instrs[i] = Nop,
+ LocalCopy64(a, b) if a == b => instrs[i] = Nop,
+ 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, [I32Const(c)] => AddConst32(c));
+ rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => AddLocalLocal32(a, b));
+ rewrite!(instrs, i, [LocalGet32(local), I32Const(c)] => [ Nop, LocalGet32(local), AddConst32(c)]);
}
- Instruction::I64Rotl => {
- if read > 1
- && let (Instruction::I64Xor, Instruction::I64Const(c)) =
- (instructions[read - 2], instructions[read - 1])
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::XorRotlConst64(c);
- }
+ I64Add => {
+ rewrite!(instrs, i, [I64Const(c)] => AddConst64(c));
+ rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => AddLocalLocal64(a, b));
+ rewrite!(instrs, i, [LocalGet64(local), I64Const(c)] => [ Nop, LocalGet64(local), AddConst64(c)]);
}
- Instruction::I32Store(memarg) => {
- if read > 1
- && let (Instruction::LocalGet32(addr_local), Instruction::LocalGet32(value_local)) =
- (instructions[read - 2], instructions[read - 1])
- && let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::StoreLocalLocal32(memarg, addr_local, value_local);
- }
+ I64Rotl => rewrite!(instrs, i, [I64Xor, I64Const(c)] => XorRotlConst64(c)),
+ I32Store(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))) =>
+ StoreLocalLocal32(memarg, addr_local, value_local)
+ );
}
- Instruction::I64Store(memarg) => {
- if read > 1
- && let (Instruction::LocalGet32(addr_local), Instruction::LocalGet64(value_local)) =
- (instructions[read - 2], instructions[read - 1])
- && let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::StoreLocalLocal64(memarg, addr_local, value_local);
- }
+ I64Store(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))) =>
+ StoreLocalLocal64(memarg, addr_local, value_local)
+ );
}
- Instruction::I32Load(memarg) => {
- if read > 0
- && let Instruction::LocalGet32(addr_local) = instructions[read - 1]
- && let Ok(addr_local) = u8::try_from(addr_local)
- {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::LoadLocal32(memarg, addr_local);
- }
+ V128Store(memarg) => {
+ rewrite!(instrs, i,
+ [LocalGet32(addr_local), LocalGet128(value_local)] if
+ (let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))) =>
+ StoreLocalLocal128(memarg, addr_local, value_local)
+ );
}
- Instruction::MemoryFill(mem) => {
- if read > 1
- && let (Instruction::I32Const(val), Instruction::I32Const(size)) =
- (instructions[read - 2], instructions[read - 1])
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::MemoryFillImm(mem, val as u8, size);
- }
- }
-
- Instruction::LocalGet32(dst) => {
- if read > 0
- && let Instruction::LocalSet32(src) = instructions[read - 1]
- && src == dst
- {
- instructions[read - 1] = Instruction::LocalTee32(src);
- instructions[read] = Instruction::Nop;
- }
+ I32Load(memarg) => {
+ rewrite!(instrs, i,
+ [LocalGet32(addr_local)] if (let Ok(addr_local) = u8::try_from(addr_local)) =>
+ LoadLocal32(memarg, addr_local)
+ );
}
- Instruction::LocalGet64(dst) => {
- if read > 0
- && let Instruction::LocalSet64(src) = instructions[read - 1]
- && src == dst
- {
- instructions[read - 1] = Instruction::LocalTee64(src);
- instructions[read] = Instruction::Nop;
- }
+ MemoryFill(mem) => {
+ rewrite!(instrs, i, [I32Const(val), I32Const(size)] => MemoryFillImm(mem, val as u8, size))
}
- Instruction::LocalGet128(dst) => {
- if read > 0
- && let Instruction::LocalSet128(src) = instructions[read - 1]
- && src == dst
- {
- instructions[read - 1] = Instruction::LocalTee128(src);
- instructions[read] = Instruction::Nop;
- }
+ LocalGet32(dst) => rewrite!(instrs, i, [LocalSet32(src)] if (src == dst) => [LocalTee32(src), Nop]),
+ 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) => {
+ 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, [LocalGet32(src), AddConst32(c)] if (src == dst) => AddLocalConst32(dst, c));
+ 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
+ (let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
+ LoadLocalSet32(memarg, addr, dst)
+ );
}
- Instruction::LocalSet32(dst) => {
- if read > 0 {
- match instructions[read - 1] {
- Instruction::LocalGet32(src) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] =
- if src == dst { Instruction::Nop } else { Instruction::LocalCopy32(src, dst) };
- }
- Instruction::I32Const(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::SetLocalConst32(dst, c);
- }
- Instruction::F32Const(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] =
- Instruction::SetLocalConst32(dst, i32::from_ne_bytes(c.to_bits().to_ne_bytes()));
- }
- _ => {}
- }
- }
-
- if read > 1 {
- match (instructions[read - 2], instructions[read - 1]) {
- (Instruction::LocalGet32(src), Instruction::AddConst32(c)) if src == dst => {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddLocalConst32(dst, c);
- }
- (Instruction::LocalGet32(addr), Instruction::I32Load(memarg)) => {
- if let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst)) {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::LoadLocalSet32(memarg, addr, dst);
- }
- }
- _ => {}
- }
- }
-
- if read > 0
- && let Instruction::LoadLocal32(memarg, addr) = instructions[read - 1]
- && let Ok(dst) = u8::try_from(dst)
- {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::LoadLocalSet32(memarg, addr, dst);
- }
+ 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,
+ [LocalGet64(src), AddConst64(c)] if (src == dst) =>
+ AddLocalConst64(dst, c)
+ );
}
- Instruction::LocalSet64(dst) => {
- if read > 0 {
- match instructions[read - 1] {
- Instruction::LocalGet64(src) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] =
- if src == dst { Instruction::Nop } else { Instruction::LocalCopy64(src, dst) };
- }
- Instruction::I64Const(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::SetLocalConst64(dst, c);
- }
- Instruction::F64Const(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] =
- Instruction::SetLocalConst64(dst, i64::from_ne_bytes(c.to_bits().to_ne_bytes()));
- }
- _ => {}
- }
- }
-
- if read > 1
- && let (Instruction::LocalGet64(src), Instruction::AddConst64(c)) =
- (instructions[read - 2], instructions[read - 1])
- && src == dst
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::AddLocalConst64(dst, c);
- }
+ LocalSet128(dst) => {
+ rewrite!(instrs, i, [LocalGet128(src)] => if src == dst { Nop } else { LocalCopy128(src, dst) });
+ rewrite!(instrs, i,
+ [LocalGet32(addr), V128Load(memarg)] if
+ (let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
+ LoadLocalSet128(memarg, addr, dst)
+ );
}
- Instruction::LocalSet128(dst) => {
- if read > 0
- && let Instruction::LocalGet128(src) = instructions[read - 1]
- {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] =
- if src == dst { Instruction::Nop } else { Instruction::LocalCopy128(src, dst) };
- }
+ LocalTee32(dst) => {
+ rewrite!(instrs, i, [LocalGet32(src)] if (src == dst) => [LocalGet32(src), Nop]);
+ rewrite!(instrs, i, [I32Const(c), I32And] => AndConstTee32(c, dst));
+ rewrite!(instrs, i, [I32Const(c), I32Sub] => SubConstTee32(c, dst));
+ rewrite!(instrs, i,
+ [LocalGet32(addr), I32Load(memarg)] if
+ (let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
+ LoadLocalTee32(memarg, addr, dst)
+ );
+ rewrite!(instrs, i,
+ [LoadLocal32(memarg, addr)] if (let Ok(dst) = u8::try_from(dst)) =>
+ LoadLocalTee32(memarg, addr, dst)
+ );
}
- Instruction::LocalTee32(dst) => {
- if read > 0
- && let Instruction::LocalGet32(src) = instructions[read - 1]
- && src == dst
- {
- instructions[read] = Instruction::Nop;
- }
-
- if read > 1
- && let (Instruction::LocalGet32(addr), Instruction::I32Load(memarg)) =
- (instructions[read - 2], instructions[read - 1])
- && let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))
- {
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::LoadLocalTee32(memarg, addr, dst);
- }
-
- if read > 0
- && let Instruction::LoadLocal32(memarg, addr) = instructions[read - 1]
- && let Ok(dst) = u8::try_from(dst)
- {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::LoadLocalTee32(memarg, addr, dst);
- }
+ LocalTee64(dst) => {
+ rewrite!(instrs, i, [LocalGet64(src)] if (src == dst) => [LocalGet64(src), Nop]);
+ 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));
}
- Instruction::LocalTee64(dst) if read > 0 => match instructions[read - 1] {
- Instruction::LocalGet64(src) if src == dst => {
- instructions[read] = Instruction::Nop;
- }
- Instruction::XorRotlConst64(c) => {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::XorRotlConstTee64(c, dst);
- }
- _ => {}
- },
- Instruction::LocalTee128(dst) => {
- if read > 0
- && let Instruction::LocalGet128(src) = instructions[read - 1]
- && src == dst
- {
- instructions[read] = Instruction::Nop;
- }
+ LocalTee128(dst) => {
+ rewrite!(instrs, i, [LocalGet128(src)] if (src == dst) => [LocalGet128(src), Nop]);
+ 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)
+ );
}
- Instruction::Drop32 => {
- if read > 0
- && let Instruction::LocalTee32(local) = instructions[read - 1]
- {
- instructions[read - 1] = Instruction::LocalSet32(local);
- instructions[read] = Instruction::Nop;
- }
- }
- Instruction::Drop64 => {
- if read > 0
- && let Instruction::LocalTee64(local) = instructions[read - 1]
- {
- instructions[read - 1] = Instruction::LocalSet64(local);
- instructions[read] = Instruction::Nop;
- }
- }
- Instruction::Drop128 => {
- if read > 0
- && let Instruction::LocalTee128(local) = instructions[read - 1]
- {
- instructions[read - 1] = Instruction::LocalSet128(local);
- instructions[read] = Instruction::Nop;
- }
- }
- Instruction::JumpIfZero(ip) => {
- if read > 0 && instructions[read - 1] == Instruction::I32Eqz {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::JumpIfNonZero(ip);
+ 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]),
+ JumpIfZero(ip) => {
+ rewrite!(instrs, i, [I32Eqz] => {
+ replace!(instrs, i, 1 => [Nop, JumpIfNonZero(ip)]);
continue;
- }
-
- if read > 2 {
- match (instructions[read - 2], instructions[read - 1]) {
- (Instruction::I32Const(imm), cmp) => {
- if read > 3
- && let Instruction::LocalGet32(local) = instructions[read - 3]
- && let Some(op) = cmp_op(cmp)
- {
- instructions[read - 3] = Instruction::Nop;
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::JumpCmpLocalConst32 {
- target_ip: ip,
- local,
- imm,
- op: inverse_cmp_op(op),
- };
- }
- }
- (Instruction::LocalGet32(right), cmp) => {
- if read > 3
- && let Instruction::LocalGet32(left) = instructions[read - 3]
- && let Some(op) = cmp_op(cmp)
- {
- instructions[read - 3] = Instruction::Nop;
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::JumpCmpLocalLocal32 {
- target_ip: ip,
- left,
- right,
- op: inverse_cmp_op(op),
- };
- }
- }
- _ => {}
- }
- }
+ });
+ rewrite!(instrs, i, [cmp, I32Const(imm)] if (let Some(op) = cmp_op(cmp)) =>
+ JumpCmpStackConst32 { target_ip: ip, imm, op: inverse_cmp_op(op) }
+ );
+ rewrite!(instrs, i, [cmp, I64Const(imm)] if (let Some(op) = cmp_op_64(cmp)) =>
+ JumpCmpStackConst64 { target_ip: ip, imm, op: inverse_cmp_op(op) }
+ );
+ rewrite!(instrs, i,
+ [LocalGet32(local), cmp, I32Const(imm)] if (let Some(op) = cmp_op(cmp)) =>
+ JumpCmpLocalConst32 { target_ip: ip, local, imm, op: inverse_cmp_op(op) }
+ );
+ rewrite!(instrs, i,
+ [LocalGet64(local), cmp, I64Const(imm)] if
+ (let Some(op) = cmp_op_64(cmp) && let Ok(imm) = i32::try_from(imm)) =>
+ JumpCmpLocalConst64 { target_ip: ip, local, imm, op: inverse_cmp_op(op) }
+ );
+ rewrite!(instrs, i,
+ [LocalGet32(left), cmp, LocalGet32(right)] if (let Some(op) = cmp_op(cmp)) =>
+ JumpCmpLocalLocal32 { target_ip: ip, left, right, op: inverse_cmp_op(op) }
+ );
+ rewrite!(instrs, i,
+ [LocalGet64(left), cmp, LocalGet64(right)] if (let Some(op) = cmp_op_64(cmp)) =>
+ JumpCmpLocalLocal64 { target_ip: ip, left, right, op: inverse_cmp_op(op) }
+ );
}
- Instruction::JumpIfNonZero(ip) => {
- if read > 0 && instructions[read - 1] == Instruction::I32Eqz {
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::JumpIfZero(ip);
+ JumpIfNonZero(ip) => {
+ rewrite!(instrs, i, [I32Eqz] => {
+ replace!(instrs, i, 1 => [Nop, JumpIfZero(ip)]);
continue;
- }
-
- if read > 2 {
- match (instructions[read - 2], instructions[read - 1]) {
- (Instruction::I32Const(imm), cmp) => {
- if read > 3
- && let Instruction::LocalGet32(local) = instructions[read - 3]
- && let Some(op) = cmp_op(cmp)
- {
- instructions[read - 3] = Instruction::Nop;
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] = Instruction::JumpCmpLocalConst32 { target_ip: ip, local, imm, op };
- }
- }
- (Instruction::LocalGet32(right), cmp) => {
- if read > 3
- && let Instruction::LocalGet32(left) = instructions[read - 3]
- && let Some(op) = cmp_op(cmp)
- {
- instructions[read - 3] = Instruction::Nop;
- instructions[read - 2] = Instruction::Nop;
- instructions[read - 1] = Instruction::Nop;
- instructions[read] =
- Instruction::JumpCmpLocalLocal32 { target_ip: ip, left, right, op };
- }
- }
- _ => {}
- }
- }
+ });
+ rewrite!(instrs, i, [cmp, I32Const(imm)] if (let Some(op) = cmp_op(cmp)) =>
+ JumpCmpStackConst32 { target_ip: ip, imm, op }
+ );
+ rewrite!(instrs, i, [cmp, I64Const(imm)] if (let Some(op) = cmp_op_64(cmp)) =>
+ JumpCmpStackConst64 { target_ip: ip, imm, op }
+ );
+ rewrite!(instrs, i,
+ [LocalGet32(local), cmp, I32Const(imm)] if (let Some(op) = cmp_op(cmp)) =>
+ JumpCmpLocalConst32 { target_ip: ip, local, imm, op }
+ );
+ rewrite!(instrs, i,
+ [LocalGet64(local), cmp, I64Const(imm)] if
+ (let Some(op) = cmp_op_64(cmp) && let Ok(imm) = i32::try_from(imm)) =>
+ JumpCmpLocalConst64 { target_ip: ip, local, imm, op }
+ );
+ rewrite!(instrs, i,
+ [LocalGet32(left), cmp, LocalGet32(right)] if (let Some(op) = cmp_op(cmp)) =>
+ JumpCmpLocalLocal32 { target_ip: ip, left, right, op }
+ );
+ rewrite!(instrs, i,
+ [LocalGet64(left), cmp, LocalGet64(right)] if (let Some(op) = cmp_op_64(cmp)) =>
+ JumpCmpLocalLocal64 { target_ip: ip, left, right, op }
+ );
}
_ => {}
}
}
}
-fn dce(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData) {
+fn cmp_op(instr: Instruction) -> Option<CmpOp> {
+ Some(match instr {
+ Instruction::I32Eq => CmpOp::Eq,
+ Instruction::I32Ne => CmpOp::Ne,
+ Instruction::I32LtS => CmpOp::LtS,
+ Instruction::I32LtU => CmpOp::LtU,
+ Instruction::I32GtS => CmpOp::GtS,
+ Instruction::I32GtU => CmpOp::GtU,
+ Instruction::I32LeS => CmpOp::LeS,
+ Instruction::I32LeU => CmpOp::LeU,
+ Instruction::I32GeS => CmpOp::GeS,
+ Instruction::I32GeU => CmpOp::GeU,
+ _ => return None,
+ })
+}
+
+fn cmp_op_64(instr: Instruction) -> Option<CmpOp> {
+ Some(match instr {
+ Instruction::I64Eq => CmpOp::Eq,
+ Instruction::I64Ne => CmpOp::Ne,
+ Instruction::I64LtS => CmpOp::LtS,
+ Instruction::I64LtU => CmpOp::LtU,
+ Instruction::I64GtS => CmpOp::GtS,
+ Instruction::I64GtU => CmpOp::GtU,
+ Instruction::I64LeS => CmpOp::LeS,
+ Instruction::I64LeU => CmpOp::LeU,
+ Instruction::I64GeS => CmpOp::GeS,
+ Instruction::I64GeU => CmpOp::GeU,
+ _ => return None,
+ })
+}
+
+fn inverse_cmp_op(op: CmpOp) -> CmpOp {
+ match op {
+ CmpOp::Eq => CmpOp::Ne,
+ CmpOp::Ne => CmpOp::Eq,
+ CmpOp::LtS => CmpOp::GeS,
+ CmpOp::LtU => CmpOp::GeU,
+ CmpOp::GtS => CmpOp::LeS,
+ CmpOp::GtU => CmpOp::LeU,
+ CmpOp::LeS => CmpOp::GtS,
+ CmpOp::LeU => CmpOp::GtU,
+ CmpOp::GeS => CmpOp::LtS,
+ CmpOp::GeU => CmpOp::LtU,
+ }
+}
+
+fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData) {
let old_len = instructions.len();
if old_len == 0 {
return;
@@ -467,8 +268,12 @@ fn dce(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData
Instruction::Jump(ip)
| Instruction::JumpIfZero(ip)
| Instruction::JumpIfNonZero(ip)
+ | Instruction::JumpCmpStackConst32 { target_ip: ip, .. }
+ | Instruction::JumpCmpStackConst64 { target_ip: ip, .. }
| Instruction::JumpCmpLocalConst32 { target_ip: ip, .. }
+ | Instruction::JumpCmpLocalConst64 { target_ip: ip, .. }
| Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. }
+ | Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. }
| Instruction::BranchTable(ip, _, _) => ip,
_ => return !matches!(instr, Instruction::Nop),
};
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index e6f415b..3000b9b 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -1,9 +1,6 @@
-use crate::Result;
-
-use crate::conversion::convert_heaptype;
+use crate::{Result, conversion::convert_heaptype, macros::visit::*};
use alloc::string::ToString;
-use alloc::vec;
-use alloc::vec::Vec;
+use alloc::{vec, vec::Vec};
use tinywasm_types::{Instruction, MemoryArg, WasmFunctionData};
use wasmparser::{
FrameKind, FuncValidator, FuncValidatorAllocations, FunctionBody, VisitOperator, VisitSimdOperator,
@@ -48,16 +45,6 @@ impl FunctionDataBuilder {
struct ValidateThenVisit<'a, R: WasmModuleResources>(usize, &'a mut FunctionBuilder<R>);
-macro_rules! validate_then_visit {
- ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {$(
- fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
- self.1.$visit($($($arg.clone()),*)?);
- self.1.validator_visitor(self.0).$visit($($($arg),*)?)?;
- Ok(())
- }
- )*};
-}
-
impl<'a, R: WasmModuleResources> VisitOperator<'a> for ValidateThenVisit<'_, R> {
type Output = Result<()>;
wasmparser::for_each_visit_operator!(validate_then_visit);
@@ -92,56 +79,6 @@ pub(crate) fn process_operators_and_validate<R: WasmModuleResources>(
Ok((builder.instructions, builder.data.finish(), builder.validator.into_allocations()))
}
-macro_rules! define_operand {
- ($name:ident($instr:expr, $ty:ty)) => {
- fn $name(&mut self, arg: $ty) -> Self::Output {
- self.instructions.push($instr(arg).into());
- }
- };
-
- ($name:ident($instr:expr, $ty:ty, $ty2:ty)) => {
- fn $name(&mut self, arg: $ty, arg2: $ty2) -> Self::Output {
- self.instructions.push($instr(arg, arg2).into());
- }
- };
-
- ($name:ident($instr:expr)) => {
- fn $name(&mut self) -> Self::Output {
- self.instructions.push($instr.into());
- }
- };
-}
-
-macro_rules! define_operands {
- ($($name:ident($instr:ident $(,$ty:ty)*)),*) => {$(
- define_operand!($name(Instruction::$instr $(,$ty)*));
- )*};
-}
-
-macro_rules! define_mem_operands {
- ($($name:ident($instr:ident)),*) => {$(
- fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
- self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory)));
- }
- )*};
-}
-
-macro_rules! define_mem_operands_simd {
- ($($name:ident($instr:ident)),*) => {$(
- fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output {
- self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory)).into());
- }
- )*};
-}
-
-macro_rules! define_mem_operands_simd_lane {
- ($($name:ident($instr:ident)),*) => {$(
- fn $name(&mut self, memarg: wasmparser::MemArg, lane: u8) -> Self::Output {
- self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory), lane).into());
- }
- )*};
-}
-
pub(crate) struct FunctionBuilder<R: WasmModuleResources> {
validator: FuncValidator<R>,
instructions: Vec<Instruction>,
@@ -151,28 +88,6 @@ pub(crate) struct FunctionBuilder<R: WasmModuleResources> {
errors: Vec<crate::ParseError>,
}
-macro_rules! impl_visit_operator {
- ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
- $(impl_visit_operator!(@@$proposal $op $({ $($arg: $argty),* })? => $visit ($($ann:tt)*));)*
- };
-
- (@@mvp $($rest:tt)* ) => {};
- (@@reference_types $($rest:tt)* ) => {};
- (@@sign_extension $($rest:tt)* ) => {};
- (@@saturating_float_to_int $($rest:tt)* ) => {};
- (@@bulk_memory $($rest:tt)* ) => {};
- (@@simd $($rest:tt)* ) => {};
- (@@wide_arithmetic $($rest:tt)* ) => {};
- (@@relaxed_simd $($rest:tt)* ) => {};
- (@@tail_call $($rest:tt)* ) => {};
-
- (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => {
- fn $visit(&mut self $($(,_: $argty)*)?) {
- self.unsupported(stringify!($visit))
- }
- };
-}
-
impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuilder<R> {
type Output = ();
wasmparser::for_each_visit_operator!(impl_visit_operator);
@@ -182,24 +97,59 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
define_mem_operands! {
- visit_i32_load(I32Load), visit_i64_load(I64Load), visit_f32_load(F32Load), visit_f64_load(F64Load), visit_i32_load8_s(I32Load8S), visit_i32_load8_u(I32Load8U), visit_i32_load16_s(I32Load16S), visit_i32_load16_u(I32Load16U), visit_i64_load8_s(I64Load8S), visit_i64_load8_u(I64Load8U), visit_i64_load16_s(I64Load16S), visit_i64_load16_u(I64Load16U), visit_i64_load32_s(I64Load32S), visit_i64_load32_u(I64Load32U), visit_f32_store(F32Store), visit_f64_store(F64Store), visit_i32_store8(I32Store8), visit_i32_store16(I32Store16), visit_i64_store8(I64Store8), visit_i64_store16(I64Store16), visit_i64_store32(I64Store32), visit_i32_store(I32Store), visit_i64_store(I64Store)
+ visit_i32_load(I32Load), visit_i64_load(I64Load), visit_f32_load(F32Load), visit_f64_load(F64Load),
+ visit_i32_load8_s(I32Load8S), visit_i32_load8_u(I32Load8U), visit_i32_load16_s(I32Load16S),
+ visit_i32_load16_u(I32Load16U), visit_i64_load8_s(I64Load8S), visit_i64_load8_u(I64Load8U),
+ visit_i64_load16_s(I64Load16S), visit_i64_load16_u(I64Load16U), visit_i64_load32_s(I64Load32S),
+ visit_i64_load32_u(I64Load32U), visit_f32_store(F32Store), visit_f64_store(F64Store), visit_i32_store8(I32Store8),
+ visit_i32_store16(I32Store16), visit_i64_store8(I64Store8), visit_i64_store16(I64Store16),
+ visit_i64_store32(I64Store32), visit_i32_store(I32Store), visit_i64_store(I64Store)
}
define_operands! {
// basic instructions
- visit_global_get(GlobalGet, u32), visit_i32_const(I32Const, i32), visit_i64_const(I64Const, i64), visit_call(Call, u32), visit_return_call(ReturnCall, u32), visit_memory_size(MemorySize, u32), visit_memory_grow(MemoryGrow, u32), visit_unreachable(Unreachable), visit_nop(Nop), visit_i32_eqz(I32Eqz), visit_i32_eq(I32Eq), visit_i32_ne(I32Ne), visit_i32_lt_s(I32LtS), visit_i32_lt_u(I32LtU), visit_i32_gt_s(I32GtS), visit_i32_gt_u(I32GtU), visit_i32_le_s(I32LeS), visit_i32_le_u(I32LeU), visit_i32_ge_s(I32GeS), visit_i32_ge_u(I32GeU), visit_i64_eqz(I64Eqz), visit_i64_eq(I64Eq), visit_i64_ne(I64Ne), visit_i64_lt_s(I64LtS), visit_i64_lt_u(I64LtU), visit_i64_gt_s(I64GtS), visit_i64_gt_u(I64GtU), visit_i64_le_s(I64LeS), visit_i64_le_u(I64LeU), visit_i64_ge_s(I64GeS), visit_i64_ge_u(I64GeU), visit_f32_eq(F32Eq), visit_f32_ne(F32Ne), visit_f32_lt(F32Lt), visit_f32_gt(F32Gt), visit_f32_le(F32Le), visit_f32_ge(F32Ge), visit_f64_eq(F64Eq), visit_f64_ne(F64Ne), visit_f64_lt(F64Lt), visit_f64_gt(F64Gt), visit_f64_le(F64Le), visit_f64_ge(F64Ge), visit_i32_clz(I32Clz), visit_i32_ctz(I32Ctz), visit_i32_popcnt(I32Popcnt), visit_i32_sub(I32Sub), visit_i32_mul(I32Mul), visit_i32_div_s(I32DivS), visit_i32_div_u(I32DivU), visit_i32_rem_s(I32RemS), visit_i32_rem_u(I32RemU), visit_i32_and(I32And), visit_i32_or(I32Or), visit_i32_xor(I32Xor), visit_i32_shl(I32Shl), visit_i32_shr_s(I32ShrS), visit_i32_shr_u(I32ShrU), visit_i32_rotl(I32Rotl), visit_i32_rotr(I32Rotr), visit_i64_clz(I64Clz), visit_i64_ctz(I64Ctz), visit_i64_popcnt(I64Popcnt), visit_i64_sub(I64Sub), visit_i64_mul(I64Mul), visit_i64_div_s(I64DivS), visit_i64_div_u(I64DivU), visit_i64_rem_s(I64RemS), visit_i64_rem_u(I64RemU), visit_i64_and(I64And), visit_i64_or(I64Or), visit_i64_xor(I64Xor), visit_i64_shl(I64Shl), visit_i64_shr_s(I64ShrS), visit_i64_shr_u(I64ShrU), visit_i64_rotr(I64Rotr), visit_f32_abs(F32Abs), visit_f32_neg(F32Neg), visit_f32_ceil(F32Ceil), visit_f32_floor(F32Floor), visit_f32_trunc(F32Trunc), visit_f32_nearest(F32Nearest), visit_f32_sqrt(F32Sqrt), visit_f32_add(F32Add), visit_f32_sub(F32Sub), visit_f32_mul(F32Mul), visit_f32_div(F32Div), visit_f32_min(F32Min), visit_f32_max(F32Max), visit_f32_copysign(F32Copysign), visit_f64_abs(F64Abs), visit_f64_neg(F64Neg), visit_f64_ceil(F64Ceil), visit_f64_floor(F64Floor), visit_f64_trunc(F64Trunc), visit_f64_nearest(F64Nearest), visit_f64_sqrt(F64Sqrt), visit_f64_add(F64Add), visit_f64_sub(F64Sub), visit_f64_mul(F64Mul), visit_f64_div(F64Div), visit_f64_min(F64Min), visit_f64_max(F64Max), visit_f64_copysign(F64Copysign), visit_i32_wrap_i64(I32WrapI64), visit_i32_trunc_f32_s(I32TruncF32S), visit_i32_trunc_f32_u(I32TruncF32U), visit_i32_trunc_f64_s(I32TruncF64S), visit_i32_trunc_f64_u(I32TruncF64U), visit_i64_extend_i32_s(I64ExtendI32S), visit_i64_extend_i32_u(I64ExtendI32U), visit_i64_trunc_f32_s(I64TruncF32S), visit_i64_trunc_f32_u(I64TruncF32U), visit_i64_trunc_f64_s(I64TruncF64S), visit_i64_trunc_f64_u(I64TruncF64U), visit_f32_convert_i32_s(F32ConvertI32S), visit_f32_convert_i32_u(F32ConvertI32U), visit_f32_convert_i64_s(F32ConvertI64S), visit_f32_convert_i64_u(F32ConvertI64U), visit_f32_demote_f64(F32DemoteF64), visit_f64_convert_i32_s(F64ConvertI32S), visit_f64_convert_i32_u(F64ConvertI32U), visit_f64_convert_i64_s(F64ConvertI64S), visit_f64_convert_i64_u(F64ConvertI64U), visit_f64_promote_f32(F64PromoteF32), visit_i32_add(I32Add), visit_i64_add(I64Add), visit_i64_rotl(I64Rotl),
+ visit_global_get(GlobalGet, u32), visit_i32_const(I32Const, i32), visit_i64_const(I64Const, i64), visit_return(Return),
+ visit_call(Call, u32), visit_call_indirect(CallIndirect, u32, u32), visit_return_call_indirect(ReturnCallIndirect, u32, u32),
+ visit_return_call(ReturnCall, u32), visit_memory_size(MemorySize, u32), visit_memory_grow(MemoryGrow, u32), visit_unreachable(Unreachable),
+ visit_nop(Nop), visit_i32_eqz(I32Eqz), visit_i32_eq(I32Eq), visit_i32_ne(I32Ne), visit_i32_lt_s(I32LtS), visit_i32_lt_u(I32LtU),
+ visit_i32_gt_s(I32GtS), visit_i32_gt_u(I32GtU), visit_i32_le_s(I32LeS), visit_i32_le_u(I32LeU), visit_i32_ge_s(I32GeS),
+ visit_i32_ge_u(I32GeU), visit_i64_eqz(I64Eqz), visit_i64_eq(I64Eq), visit_i64_ne(I64Ne), visit_i64_lt_s(I64LtS), visit_i64_lt_u(I64LtU),
+ visit_i64_gt_s(I64GtS), visit_i64_gt_u(I64GtU), visit_i64_le_s(I64LeS), visit_i64_le_u(I64LeU), visit_i64_ge_s(I64GeS), visit_i64_ge_u(I64GeU),
+ visit_f32_eq(F32Eq), visit_f32_ne(F32Ne), visit_f32_lt(F32Lt), visit_f32_gt(F32Gt), visit_f32_le(F32Le), visit_f32_ge(F32Ge), visit_f64_eq(F64Eq),
+ visit_f64_ne(F64Ne), visit_f64_lt(F64Lt), visit_f64_gt(F64Gt), visit_f64_le(F64Le), visit_f64_ge(F64Ge), visit_i32_clz(I32Clz), visit_i32_ctz(I32Ctz),
+ visit_i32_popcnt(I32Popcnt), visit_i32_sub(I32Sub), visit_i32_mul(I32Mul), visit_i32_div_s(I32DivS), visit_i32_div_u(I32DivU), visit_i32_rem_s(I32RemS),
+ visit_i32_rem_u(I32RemU), visit_i32_and(I32And), visit_i32_or(I32Or), visit_i32_xor(I32Xor), visit_i32_shl(I32Shl), visit_i32_shr_s(I32ShrS),
+ visit_i32_shr_u(I32ShrU), visit_i32_rotl(I32Rotl), visit_i32_rotr(I32Rotr), visit_i64_clz(I64Clz), visit_i64_ctz(I64Ctz), visit_i64_popcnt(I64Popcnt),
+ visit_i64_sub(I64Sub), visit_i64_mul(I64Mul), visit_i64_div_s(I64DivS), visit_i64_div_u(I64DivU), visit_i64_rem_s(I64RemS), visit_i64_rem_u(I64RemU),
+ visit_i64_and(I64And), visit_i64_or(I64Or), visit_i64_xor(I64Xor), visit_i64_shl(I64Shl), visit_i64_shr_s(I64ShrS), visit_i64_shr_u(I64ShrU),
+ visit_i64_rotr(I64Rotr), visit_f32_abs(F32Abs), visit_f32_neg(F32Neg), visit_f32_ceil(F32Ceil), visit_f32_floor(F32Floor), visit_f32_trunc(F32Trunc),
+ visit_f32_nearest(F32Nearest), visit_f32_sqrt(F32Sqrt), visit_f32_add(F32Add), visit_f32_sub(F32Sub), visit_f32_mul(F32Mul), visit_f32_div(F32Div),
+ visit_f32_min(F32Min), visit_f32_max(F32Max), visit_f32_copysign(F32Copysign), visit_f64_abs(F64Abs), visit_f64_neg(F64Neg), visit_f64_ceil(F64Ceil),
+ visit_f64_floor(F64Floor), visit_f64_trunc(F64Trunc), visit_f64_nearest(F64Nearest), visit_f64_sqrt(F64Sqrt), visit_f64_add(F64Add), visit_f64_sub(F64Sub),
+ visit_f64_mul(F64Mul), visit_f64_div(F64Div), visit_f64_min(F64Min), visit_f64_max(F64Max), visit_f64_copysign(F64Copysign), visit_i32_wrap_i64(I32WrapI64),
+ visit_i32_trunc_f32_s(I32TruncF32S), visit_i32_trunc_f32_u(I32TruncF32U), visit_i32_trunc_f64_s(I32TruncF64S), visit_i32_trunc_f64_u(I32TruncF64U),
+ visit_i64_extend_i32_s(I64ExtendI32S), visit_i64_extend_i32_u(I64ExtendI32U), visit_i64_trunc_f32_s(I64TruncF32S), visit_i64_trunc_f32_u(I64TruncF32U),
+ visit_i64_trunc_f64_s(I64TruncF64S), visit_i64_trunc_f64_u(I64TruncF64U), visit_f32_convert_i32_s(F32ConvertI32S), visit_f32_convert_i32_u(F32ConvertI32U),
+ visit_f32_convert_i64_s(F32ConvertI64S), visit_f32_convert_i64_u(F32ConvertI64U), visit_f32_demote_f64(F32DemoteF64), visit_f64_convert_i32_s(F64ConvertI32S),
+ visit_f64_convert_i32_u(F64ConvertI32U), visit_f64_convert_i64_s(F64ConvertI64S), visit_f64_convert_i64_u(F64ConvertI64U), visit_f64_promote_f32(F64PromoteF32),
+ visit_i32_add(I32Add), visit_i64_add(I64Add), visit_i64_rotl(I64Rotl),
// sign_extension
- visit_i32_extend8_s(I32Extend8S), visit_i32_extend16_s(I32Extend16S), visit_i64_extend8_s(I64Extend8S), visit_i64_extend16_s(I64Extend16S), visit_i64_extend32_s(I64Extend32S),
+ visit_i32_extend8_s(I32Extend8S), visit_i32_extend16_s(I32Extend16S), visit_i64_extend8_s(I64Extend8S), visit_i64_extend16_s(I64Extend16S),
+ visit_i64_extend32_s(I64Extend32S),
// Non-trapping Float-to-int Conversions
- visit_i32_trunc_sat_f32_s(I32TruncSatF32S), visit_i32_trunc_sat_f32_u(I32TruncSatF32U), visit_i32_trunc_sat_f64_s(I32TruncSatF64S), visit_i32_trunc_sat_f64_u(I32TruncSatF64U), visit_i64_trunc_sat_f32_s(I64TruncSatF32S), visit_i64_trunc_sat_f32_u(I64TruncSatF32U), visit_i64_trunc_sat_f64_s(I64TruncSatF64S), visit_i64_trunc_sat_f64_u(I64TruncSatF64U),
+ visit_i32_trunc_sat_f32_s(I32TruncSatF32S), visit_i32_trunc_sat_f32_u(I32TruncSatF32U), visit_i32_trunc_sat_f64_s(I32TruncSatF64S),
+ visit_i32_trunc_sat_f64_u(I32TruncSatF64U), visit_i64_trunc_sat_f32_s(I64TruncSatF32S), visit_i64_trunc_sat_f32_u(I64TruncSatF32U),
+ visit_i64_trunc_sat_f64_s(I64TruncSatF64S), visit_i64_trunc_sat_f64_u(I64TruncSatF64U),
// Reference Types
- visit_ref_func(RefFunc, u32), visit_table_fill(TableFill, u32), visit_table_get(TableGet, u32), visit_table_set(TableSet, u32), visit_table_grow(TableGrow, u32), visit_table_size(TableSize, u32),
+ visit_ref_func(RefFunc, u32), visit_table_fill(TableFill, u32), visit_table_get(TableGet, u32), visit_table_set(TableSet, u32),
+ visit_table_grow(TableGrow, u32), visit_table_size(TableSize, u32), visit_ref_is_null(RefIsNull),
// Bulk Memory
- visit_memory_init(MemoryInit, u32, u32), visit_memory_fill(MemoryFill, u32), visit_table_init(TableInit, u32, u32), visit_data_drop(DataDrop, u32), visit_elem_drop(ElemDrop, u32),
+ visit_memory_init(MemoryInit, u32, u32), visit_memory_fill(MemoryFill, u32), visit_table_init(TableInit, u32, u32),
+ visit_data_drop(DataDrop, u32), visit_elem_drop(ElemDrop, u32),
// Wide Arithmetic
visit_i64_add128(I64Add128), visit_i64_sub128(I64Sub128), visit_i64_mul_wide_s(I64MulWideS), visit_i64_mul_wide_u(I64MulWideU)
@@ -238,26 +188,15 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
};
}
- fn visit_return(&mut self) -> Self::Output {
- self.instructions.push(Instruction::Return);
- }
-
fn visit_local_get(&mut self, idx: u32) -> Self::Output {
let resolved_idx = self.local_addr_map[idx as usize];
+ use wasmparser::ValType::*;
if let Some(t) = self.validator.get_local_type(idx) {
match t {
- wasmparser::ValType::I32 | wasmparser::ValType::F32 => {
- self.instructions.push(Instruction::LocalGet32(resolved_idx));
- }
- wasmparser::ValType::I64 | wasmparser::ValType::F64 => {
- self.instructions.push(Instruction::LocalGet64(resolved_idx));
- }
- wasmparser::ValType::V128 => {
- self.instructions.push(Instruction::LocalGet128(resolved_idx));
- }
- wasmparser::ValType::Ref(_) => {
- self.instructions.push(Instruction::LocalGet32(resolved_idx));
- }
+ I32 | F32 => self.instructions.push(Instruction::LocalGet32(resolved_idx)),
+ I64 | F64 => self.instructions.push(Instruction::LocalGet64(resolved_idx)),
+ V128 => self.instructions.push(Instruction::LocalGet128(resolved_idx)),
+ Ref(_) => self.instructions.push(Instruction::LocalGet32(resolved_idx)),
}
}
}
@@ -309,14 +248,12 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_if(&mut self, _ty: wasmparser::BlockType) -> Self::Output {
- let cond_jump_ip = self.instructions.len();
self.instructions.push(Instruction::JumpIfZero(0));
- let start_ip = self.instructions.len();
self.ctx_stack.push(LoweringCtx {
kind: BlockKind::If,
has_else: false,
- start_ip,
- branch_jumps: alloc::vec![cond_jump_ip],
+ start_ip: self.instructions.len(),
+ branch_jumps: alloc::vec![self.instructions.len() - 1],
});
}
@@ -422,14 +359,6 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
}
- fn visit_call_indirect(&mut self, ty: u32, table: u32) -> Self::Output {
- self.instructions.push(Instruction::CallIndirect(ty, table));
- }
-
- fn visit_return_call_indirect(&mut self, ty: u32, table: u32) -> Self::Output {
- self.instructions.push(Instruction::ReturnCallIndirect(ty, table));
- }
-
fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output {
self.instructions.push(Instruction::F32Const(f32::from_bits(val.bits())));
}
@@ -451,10 +380,6 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
self.instructions.push(Instruction::RefNull(convert_heaptype(ty)));
}
- fn visit_ref_is_null(&mut self) -> Self::Output {
- self.instructions.push(Instruction::RefIsNull);
- }
-
fn visit_typed_select_multi(&mut self, tys: Vec<wasmparser::ValType>) -> Self::Output {
let (c32, c64, c128) = Self::label_keep_counts(&tys);
self.instructions.push(Instruction::SelectMulti(tinywasm_types::ValueCounts { c32, c64, c128 }));
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 2f59349..03b3d3e 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -48,7 +48,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
#[inline(always)]
- fn exec<const ITERATIONS: usize>(&mut self) -> Result<Option<()>> {
+ fn exec(&mut self) -> Result<Option<()>> {
macro_rules! stack_op {
(unary $ty:ty, |$v:ident| $expr:expr) => {{
let $v = self.store.stack.values.pop::<$ty>();
@@ -113,563 +113,582 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}};
}
- for _ in 0..ITERATIONS {
- use tinywasm_types::Instruction::*;
-
- let next = match self.func.instructions.0.get(self.cf.instr_ptr as usize) {
- Some(instr) => instr,
- None => {
- cold_path();
- unreachable!(
- "Instruction pointer out of bounds: {} ({} instructions)",
- self.cf.instr_ptr,
- self.func.instructions.0.len()
- )
- }
- };
+ let next = match self.func.instructions.0.get(self.cf.instr_ptr as usize) {
+ Some(instr) => instr,
+ None => {
+ cold_path();
+ unreachable!(
+ "Instruction pointer out of bounds: {} ({} instructions)",
+ self.cf.instr_ptr,
+ self.func.instructions.0.len()
+ )
+ }
+ };
- #[rustfmt::skip]
- match next {
- Nop => {}
- Unreachable => return Err(Trap::Unreachable.into()),
- Drop32 => self.store.stack.values.drop::<Value32>(),
- Drop64 => self.store.stack.values.drop::<Value64>(),
- Drop128 => self.store.stack.values.drop::<Value128>(),
- Select32 => self.store.stack.values.select::<Value32>()?,
- Select64 => self.store.stack.values.select::<Value64>()?,
- Select128 => self.store.stack.values.select::<Value128>()?,
- SelectMulti(counts) => self.store.stack.values.select_multi(*counts),
- Call(v) => { self.exec_call_direct::<false>(*v)?; continue; }
- CallSelf => { self.exec_call_self::<false>()?; continue; }
- CallIndirect(ty, table) => { self.exec_call_indirect::<false>(*ty, *table)?; continue; }
- ReturnCall(v) => { self.exec_call_direct::<true>(*v)?; continue; }
- ReturnCallSelf => { self.exec_call_self::<true>()?; continue; }
- ReturnCallIndirect(ty, table) => { self.exec_call_indirect::<true>(*ty, *table)?; continue; }
- Jump(ip) => { self.exec_jump(*ip); continue; }
- JumpIfZero(ip) => if self.exec_jump_if_zero(*ip) { continue; },
- JumpIfNonZero(ip) => if self.exec_jump_if_non_zero(*ip) { continue; },
- JumpCmpLocalConst32 { target_ip, local, imm, op } => {
- if self.exec_jump_cmp_local_const_32(*target_ip, *local, *imm, *op) { continue; }
- }
- JumpCmpLocalLocal32 { target_ip, left, right, op } => {
- if self.exec_jump_cmp_local_local_32(*target_ip, *left, *right, *op) { continue; }
- }
- DropKeep { base32, keep32, base64, keep64, base128, keep128 } => {
- let mut base = self.cf.stack_base(); base.s32 += *base32 as u32; base.s64 += *base64 as u32; base.s128 += *base128 as u32;
- self.store.stack.values.truncate_keep_counts(base, ValueCounts { c32: *keep32 as u16, c64: *keep64 as u16, c128: *keep128 as u16 });
- }
- DropKeep32(base, keep) => self.store.stack.values.stack_32.truncate_keep((self.cf.stack_base().s32 + *base as u32) as usize, *keep as usize),
- DropKeep64(base, keep) => self.store.stack.values.stack_64.truncate_keep((self.cf.stack_base().s64 + *base as u32) as usize, *keep as usize),
- DropKeep128(base, keep) => self.store.stack.values.stack_128.truncate_keep((self.cf.stack_base().s128 + *base as u32) as usize, *keep as usize),
- BranchTable(default_ip, start, len) => { self.exec_branch_table(*default_ip, *start, *len); continue; }
- Return => { if self.exec_return() { return Ok(Some(())); } continue; }
- LocalGet32(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value32>(&self.cf, *local_index))?,
- LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value64>(&self.cf, *local_index))?,
- LocalGet128(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value128>(&self.cf, *local_index))?,
- LocalSet32(local_index) => stack_op!(local_set_pop Value32, local_index),
- LocalSet64(local_index) => stack_op!(local_set_pop Value64, local_index),
- LocalSet128(local_index) => stack_op!(local_set_pop Value128, local_index),
- LocalCopy32(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value32>(&self.cf, *from)),
- LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value64>(&self.cf, *from)),
- LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value128>(&self.cf, *from)),
- AddLocalLocal32(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)))?,
- AddLocalLocal64(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.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.stack.values.local_update::<Value32>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u32)),
- AddLocalConst64(local_index, c) => self.store.stack.values.local_update::<Value64>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u64)),
- SetLocalConst32(local_index, c) => self.store.stack.values.local_set::<i32>(&self.cf, *local_index, *c),
- SetLocalConst64(local_index, c) => self.store.stack.values.local_set::<i64>(&self.cf, *local_index, *c),
- StoreLocalLocal32(m, addr_local, value_local) => self.exec_store_local_local::<u32, u32, 4>(*m, *addr_local, *value_local, |v| v)?,
- StoreLocalLocal64(m, addr_local, value_local) =>self.exec_store_local_local::<i64, i64, 8>(*m, *addr_local, *value_local, |v| v)?,
- LoadLocal32(m, addr_local) => self.exec_i32_load_local(*m, *addr_local)?,
- LoadLocalTee32(m, addr_local, dst_local) => self.exec_i32_load_local_tee(*m, *addr_local, *dst_local)?,
- LoadLocalSet32(m, addr_local, dst_local) => self.exec_i32_load_local_set(*m, *addr_local, *dst_local)?,
- 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);
- }
- 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),
- GlobalGet(global_index) => self.exec_global_get(*global_index)?,
- GlobalSet32(global_index) => self.exec_global_set_32(*global_index),
- GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index),
- GlobalSet128(global_index) => self.exec_global_set::<Value128>(*global_index),
- I32Const(val) => self.exec_const(*val)?,
- I64Const(val) => self.exec_const(*val)?,
- F32Const(val) => self.exec_const(*val)?,
- F64Const(val) => self.exec_const(*val)?,
- I64Eqz => stack_op!(unary i64 => i32, |v| i32::from(v == 0)),
- I32Eqz => stack_op!(unary i32, |v| i32::from(v == 0)),
- I32Eq => stack_op!(binary i32, |a, b| i32::from(a == b)),
- I64Eq => stack_op!(binary i64 => i32, |a, b| i32::from(a == b)),
- F32Eq => stack_op!(binary f32 => i32, |a, b| i32::from(a == b)),
- F64Eq => stack_op!(binary f64 => i32, |a, b| i32::from(a == b)),
- I32Ne => stack_op!(binary i32, |a, b| i32::from(a != b)),
- I64Ne => stack_op!(binary i64 => i32, |a, b| i32::from(a != b)),
- F32Ne => stack_op!(binary f32 => i32, |a, b| i32::from(a != b)),
- F64Ne => stack_op!(binary f64 => i32, |a, b| i32::from(a != b)),
- I32LtS => stack_op!(binary i32, |a, b| i32::from(a < b)),
- I64LtS => stack_op!(binary i64 => i32, |a, b| i32::from(a < b)),
- I32LtU => stack_op!(binary u32 => i32, |a, b| i32::from(a < b)),
- I64LtU => stack_op!(binary u64 => i32, |a, b| i32::from(a < b)),
- F32Lt => stack_op!(binary f32 => i32, |a, b| i32::from(a < b)),
- F64Lt => stack_op!(binary f64 => i32, |a, b| i32::from(a < b)),
- I32LeS => stack_op!(binary i32, |a, b| i32::from(a <= b)),
- I64LeS => stack_op!(binary i64 => i32, |a, b| i32::from(a <= b)),
- I32LeU => stack_op!(binary u32 => i32, |a, b| i32::from(a <= b)),
- I64LeU => stack_op!(binary u64 => i32, |a, b| i32::from(a <= b)),
- F32Le => stack_op!(binary f32 => i32, |a, b| i32::from(a <= b)),
- F64Le => stack_op!(binary f64 => i32, |a, b| i32::from(a <= b)),
- I32GeS => stack_op!(binary i32, |a, b| i32::from(a >= b)),
- I64GeS => stack_op!(binary i64 => i32, |a, b| i32::from(a >= b)),
- I32GeU => stack_op!(binary u32 => i32, |a, b| i32::from(a >= b)),
- I64GeU => stack_op!(binary u64 => i32, |a, b| i32::from(a >= b)),
- F32Ge => stack_op!(binary f32 => i32, |a, b| i32::from(a >= b)),
- F64Ge => stack_op!(binary f64 => i32, |a, b| i32::from(a >= b)),
- I32GtS => stack_op!(binary i32, |a, b| i32::from(a > b)),
- I64GtS => stack_op!(binary i64 => i32, |a, b| i32::from(a > b)),
- I32GtU => stack_op!(binary u32 => i32, |a, b| i32::from(a > b)),
- I64GtU => stack_op!(binary u64 => i32, |a, b| i32::from(a > b)),
- F32Gt => stack_op!(binary f32 => i32, |a, b| i32::from(a > b)),
- F64Gt => stack_op!(binary f64 => i32, |a, b| i32::from(a > b)),
- I32Add => stack_op!(binary i32, |a, b| a.wrapping_add(b)),
- I64Add => stack_op!(binary i64, |a, b| a.wrapping_add(b)),
- F32Add => stack_op!(binary f32, |a, b| a + b),
- F64Add => stack_op!(binary f64, |a, b| a + b),
- I32Sub => stack_op!(binary i32, |a, b| a.wrapping_sub(b)),
- I64Sub => stack_op!(binary i64, |a, b| a.wrapping_sub(b)),
- F32Sub => stack_op!(binary f32, |a, b| a - b),
- F64Sub => stack_op!(binary f64, |a, b| a - b),
- F32Div => stack_op!(binary f32, |a, b| a / b),
- F64Div => stack_op!(binary f64, |a, b| a / b),
- I32Mul => stack_op!(binary i32, |a, b| a.wrapping_mul(b)),
- I64Mul => stack_op!(binary i64, |a, b| a.wrapping_mul(b)),
- F32Mul => stack_op!(binary f32, |a, b| a * b),
- F64Mul => stack_op!(binary f64, |a, b| a * b),
- I32DivS => stack_op!(binary try i32, |a, b| a.wasm_checked_div(b)),
- I64DivS => stack_op!(binary try i64, |a, b| a.wasm_checked_div(b)),
- I32DivU => stack_op!(binary try u32, |a, b| a.checked_div(b).ok_or_else(trap_0)),
- I64DivU => stack_op!(binary try u64, |a, b| a.checked_div(b).ok_or_else(trap_0)),
- I32RemS => stack_op!(binary try i32, |a, b| a.checked_wrapping_rem(b)),
- I64RemS => stack_op!(binary try i64, |a, b| a.checked_wrapping_rem(b)),
- I32RemU => stack_op!(binary try u32, |a, b| a.checked_wrapping_rem(b)),
- I64RemU => stack_op!(binary try u64, |a, b| a.checked_wrapping_rem(b)),
- I32And => stack_op!(binary i32, |a, b| a & b),
- I64And => stack_op!(binary i64, |a, b| a & b),
- I32Or => stack_op!(binary i32, |a, b| a | b),
- I64Or => stack_op!(binary i64, |a, b| a | b),
- I32Xor => stack_op!(binary i32, |a, b| a ^ b),
- I64Xor => stack_op!(binary i64, |a, b| a ^ b),
- I32Shl => stack_op!(binary i32, |a, b| a.wasm_shl(b)),
- I64Shl => stack_op!(binary i64, |a, b| a.wasm_shl(b)),
- I32ShrS => stack_op!(binary i32, |a, b| a.wasm_shr(b)),
- I64ShrS => stack_op!(binary i64, |a, b| a.wasm_shr(b)),
- I32ShrU => stack_op!(binary u32, |a, b| a.wasm_shr(b)),
- I64ShrU => stack_op!(binary u64, |a, b| a.wasm_shr(b)),
- I32Rotl => stack_op!(binary i32, |a, b| a.wasm_rotl(b)),
- I64Rotl => stack_op!(binary i64, |a, b| a.wasm_rotl(b)),
- I32Rotr => stack_op!(binary i32, |a, b| a.wasm_rotr(b)),
- I64Rotr => stack_op!(binary i64, |a, b| a.wasm_rotr(b)),
- I64Add128 => stack_op!(quaternary_into2 i64 => i64, |a_lo, a_hi, b_lo, b_hi| {
- let lo = a_lo.wrapping_add(b_lo);
- let carry = u64::from((lo as u64) < (a_lo as u64));
- let hi = a_hi.wrapping_add(b_hi).wrapping_add(carry as i64);
- (lo, hi)
- }),
- I64Sub128 => stack_op!(quaternary_into2 i64 => i64, |a_lo, a_hi, b_lo, b_hi| {
- let lo = a_lo.wrapping_sub(b_lo);
- let borrow = u64::from((a_lo as u64) < (b_lo as u64));
- let hi = a_hi.wrapping_sub(b_hi).wrapping_sub(borrow as i64);
- (lo, hi)
- }),
- I64MulWideS => stack_op!(binary_into2 i64 => i64, |a, b| {
- let product = (a as i128).wrapping_mul(b as i128);
- (product as i64, (product >> 64) as i64)
- }),
- I64MulWideU => stack_op!(binary_into2 i64 => i64, |a, b| {
- let product = (a as u64 as u128).wrapping_mul(b as u64 as u128);
- (product as u64 as i64, (product >> 64) as u64 as i64)
- }),
- I32Clz => stack_op!(unary i32, |v| v.leading_zeros() as i32),
- I64Clz => stack_op!(unary i64, |v| i64::from(v.leading_zeros())),
- I32Ctz => stack_op!(unary i32, |v| v.trailing_zeros() as i32),
- I64Ctz => stack_op!(unary i64, |v| i64::from(v.trailing_zeros())),
- I32Popcnt => stack_op!(unary i32, |v| v.count_ones() as i32),
- I64Popcnt => stack_op!(unary i64, |v| i64::from(v.count_ones())),
+ use tinywasm_types::Instruction::*;
+ #[rustfmt::skip]
+ match next {
+ Nop => {}
+ Unreachable => return Err(Trap::Unreachable.into()),
+ Drop32 => self.store.stack.values.drop::<Value32>(),
+ Drop64 => self.store.stack.values.drop::<Value64>(),
+ Drop128 => self.store.stack.values.drop::<Value128>(),
+ Select32 => self.store.stack.values.select::<Value32>()?,
+ Select64 => self.store.stack.values.select::<Value64>()?,
+ Select128 => self.store.stack.values.select::<Value128>()?,
+ SelectMulti(counts) => self.store.stack.values.select_multi(*counts),
+ Call(v) => { self.exec_call_direct::<false>(*v)?; return Ok(None); }
+ CallSelf => { self.exec_call_self::<false>()?; return Ok(None); }
+ CallIndirect(ty, table) => { self.exec_call_indirect::<false>(*ty, *table)?; return Ok(None); }
+ ReturnCall(v) => { self.exec_call_direct::<true>(*v)?; return Ok(None); }
+ ReturnCallSelf => { self.exec_call_self::<true>()?; 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) },
+ 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) },
+ JumpCmpLocalConst64 { target_ip, local, imm, op } => if self.exec_jump_cmp_local_const_64(*target_ip, *local, *imm, *op) { return Ok(None) },
+ JumpCmpLocalLocal32 { target_ip, left, right, op } => if self.exec_jump_cmp_local_local_32(*target_ip, *left, *right, *op) { return Ok(None) },
+ JumpCmpLocalLocal64 { target_ip, left, right, op } => if self.exec_jump_cmp_local_local_64(*target_ip, *left, *right, *op) { return Ok(None) },
+ DropKeep { base32, keep32, base64, keep64, base128, keep128 } => {
+ let mut base = self.cf.stack_base(); base.s32 += *base32 as u32; base.s64 += *base64 as u32; base.s128 += *base128 as u32;
+ self.store.stack.values.truncate_keep_counts(base, ValueCounts { c32: *keep32 as u16, c64: *keep64 as u16, c128: *keep128 as u16 });
+ }
+ DropKeep32(base, keep) => self.store.stack.values.stack_32.truncate_keep((self.cf.stack_base().s32 + *base as u32) as usize, *keep as usize),
+ DropKeep64(base, keep) => self.store.stack.values.stack_64.truncate_keep((self.cf.stack_base().s64 + *base as u32) as usize, *keep as usize),
+ DropKeep128(base, keep) => self.store.stack.values.stack_128.truncate_keep((self.cf.stack_base().s128 + *base as u32) as usize, *keep as usize),
+ BranchTable(default_ip, start, len) => { self.exec_branch_table(*default_ip, *start, *len); return Ok(None); }
+ Return => { if self.exec_return() { return Ok(Some(())); } return Ok(None); }
+ LocalGet32(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value32>(&self.cf, *local_index))?,
+ LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value64>(&self.cf, *local_index))?,
+ LocalGet128(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value128>(&self.cf, *local_index))?,
+ LocalSet32(local_index) => stack_op!(local_set_pop Value32, local_index),
+ LocalSet64(local_index) => stack_op!(local_set_pop Value64, local_index),
+ LocalSet128(local_index) => stack_op!(local_set_pop Value128, local_index),
+ LocalCopy32(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value32>(&self.cf, *from)),
+ LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value64>(&self.cf, *from)),
+ LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value128>(&self.cf, *from)),
+ AddLocalLocal32(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)))?,
+ AddLocalLocal64(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.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.stack.values.local_update::<Value32>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u32)),
+ AddLocalConst64(local_index, c) => self.store.stack.values.local_update::<Value64>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u64)),
+ SetLocalConst32(local_index, c) => self.store.stack.values.local_set::<i32>(&self.cf, *local_index, *c),
+ SetLocalConst64(local_index, c) => self.store.stack.values.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)?,
+ 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)?,
+ LoadLocal32(m, addr_local) => self.exec_load_local::<i32, 4>(*m, *addr_local)?,
+ LoadLocalTee32(m, addr_local, dst_local) => self.exec_load_local_tee::<i32, 4>(*m, *addr_local, *dst_local)?,
+ 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);
+ }
+ 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),
+ GlobalGet(global_index) => self.exec_global_get(*global_index)?,
+ GlobalSet32(global_index) => self.exec_global_set_32(*global_index),
+ GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index),
+ GlobalSet128(global_index) => self.exec_global_set::<Value128>(*global_index),
+ I32Const(val) => self.exec_const(*val)?,
+ I64Const(val) => self.exec_const(*val)?,
+ F32Const(val) => self.exec_const(*val)?,
+ F64Const(val) => self.exec_const(*val)?,
+ I64Eqz => stack_op!(unary i64 => i32, |v| i32::from(v == 0)),
+ I32Eqz => stack_op!(unary i32, |v| i32::from(v == 0)),
+ I32Eq => stack_op!(binary i32, |a, b| i32::from(a == b)),
+ I64Eq => stack_op!(binary i64 => i32, |a, b| i32::from(a == b)),
+ F32Eq => stack_op!(binary f32 => i32, |a, b| i32::from(a == b)),
+ F64Eq => stack_op!(binary f64 => i32, |a, b| i32::from(a == b)),
+ I32Ne => stack_op!(binary i32, |a, b| i32::from(a != b)),
+ I64Ne => stack_op!(binary i64 => i32, |a, b| i32::from(a != b)),
+ F32Ne => stack_op!(binary f32 => i32, |a, b| i32::from(a != b)),
+ F64Ne => stack_op!(binary f64 => i32, |a, b| i32::from(a != b)),
+ I32LtS => stack_op!(binary i32, |a, b| i32::from(a < b)),
+ I64LtS => stack_op!(binary i64 => i32, |a, b| i32::from(a < b)),
+ I32LtU => stack_op!(binary u32 => i32, |a, b| i32::from(a < b)),
+ I64LtU => stack_op!(binary u64 => i32, |a, b| i32::from(a < b)),
+ F32Lt => stack_op!(binary f32 => i32, |a, b| i32::from(a < b)),
+ F64Lt => stack_op!(binary f64 => i32, |a, b| i32::from(a < b)),
+ I32LeS => stack_op!(binary i32, |a, b| i32::from(a <= b)),
+ I64LeS => stack_op!(binary i64 => i32, |a, b| i32::from(a <= b)),
+ I32LeU => stack_op!(binary u32 => i32, |a, b| i32::from(a <= b)),
+ I64LeU => stack_op!(binary u64 => i32, |a, b| i32::from(a <= b)),
+ F32Le => stack_op!(binary f32 => i32, |a, b| i32::from(a <= b)),
+ F64Le => stack_op!(binary f64 => i32, |a, b| i32::from(a <= b)),
+ I32GeS => stack_op!(binary i32, |a, b| i32::from(a >= b)),
+ I64GeS => stack_op!(binary i64 => i32, |a, b| i32::from(a >= b)),
+ I32GeU => stack_op!(binary u32 => i32, |a, b| i32::from(a >= b)),
+ I64GeU => stack_op!(binary u64 => i32, |a, b| i32::from(a >= b)),
+ F32Ge => stack_op!(binary f32 => i32, |a, b| i32::from(a >= b)),
+ F64Ge => stack_op!(binary f64 => i32, |a, b| i32::from(a >= b)),
+ I32GtS => stack_op!(binary i32, |a, b| i32::from(a > b)),
+ I64GtS => stack_op!(binary i64 => i32, |a, b| i32::from(a > b)),
+ I32GtU => stack_op!(binary u32 => i32, |a, b| i32::from(a > b)),
+ I64GtU => stack_op!(binary u64 => i32, |a, b| i32::from(a > b)),
+ F32Gt => stack_op!(binary f32 => i32, |a, b| i32::from(a > b)),
+ F64Gt => stack_op!(binary f64 => i32, |a, b| i32::from(a > b)),
+ I32Add => stack_op!(binary i32, |a, b| a.wrapping_add(b)),
+ I64Add => stack_op!(binary i64, |a, b| a.wrapping_add(b)),
+ F32Add => stack_op!(binary f32, |a, b| a + b),
+ F64Add => stack_op!(binary f64, |a, b| a + b),
+ I32Sub => stack_op!(binary i32, |a, b| a.wrapping_sub(b)),
+ I64Sub => stack_op!(binary i64, |a, b| a.wrapping_sub(b)),
+ F32Sub => stack_op!(binary f32, |a, b| a - b),
+ F64Sub => stack_op!(binary f64, |a, b| a - b),
+ F32Div => stack_op!(binary f32, |a, b| a / b),
+ F64Div => stack_op!(binary f64, |a, b| a / b),
+ I32Mul => stack_op!(binary i32, |a, b| a.wrapping_mul(b)),
+ I64Mul => stack_op!(binary i64, |a, b| a.wrapping_mul(b)),
+ F32Mul => stack_op!(binary f32, |a, b| a * b),
+ F64Mul => stack_op!(binary f64, |a, b| a * b),
+ I32DivS => stack_op!(binary try i32, |a, b| a.wasm_checked_div(b)),
+ I64DivS => stack_op!(binary try i64, |a, b| a.wasm_checked_div(b)),
+ I32DivU => stack_op!(binary try u32, |a, b| a.checked_div(b).ok_or_else(trap_0)),
+ I64DivU => stack_op!(binary try u64, |a, b| a.checked_div(b).ok_or_else(trap_0)),
+ I32RemS => stack_op!(binary try i32, |a, b| a.checked_wrapping_rem(b)),
+ I64RemS => stack_op!(binary try i64, |a, b| a.checked_wrapping_rem(b)),
+ I32RemU => stack_op!(binary try u32, |a, b| a.checked_wrapping_rem(b)),
+ I64RemU => stack_op!(binary try u64, |a, b| a.checked_wrapping_rem(b)),
+ I32And => stack_op!(binary i32, |a, b| a & b),
+ I64And => stack_op!(binary i64, |a, b| a & b),
+ I32Or => stack_op!(binary i32, |a, b| a | b),
+ I64Or => stack_op!(binary i64, |a, b| a | b),
+ I32Xor => stack_op!(binary i32, |a, b| a ^ b),
+ I64Xor => stack_op!(binary i64, |a, b| a ^ b),
+ I32Shl => stack_op!(binary i32, |a, b| a.wasm_shl(b)),
+ I64Shl => stack_op!(binary i64, |a, b| a.wasm_shl(b)),
+ I32ShrS => stack_op!(binary i32, |a, b| a.wasm_shr(b)),
+ I64ShrS => stack_op!(binary i64, |a, b| a.wasm_shr(b)),
+ I32ShrU => stack_op!(binary u32, |a, b| a.wasm_shr(b)),
+ I64ShrU => stack_op!(binary u64, |a, b| a.wasm_shr(b)),
+ I32Rotl => stack_op!(binary i32, |a, b| a.wasm_rotl(b)),
+ I64Rotl => stack_op!(binary i64, |a, b| a.wasm_rotl(b)),
+ I32Rotr => stack_op!(binary i32, |a, b| a.wasm_rotr(b)),
+ I64Rotr => stack_op!(binary i64, |a, b| a.wasm_rotr(b)),
+ I64Add128 => stack_op!(quaternary_into2 i64 => i64, |a_lo, a_hi, b_lo, b_hi| {
+ let lo = a_lo.wrapping_add(b_lo);
+ let carry = u64::from((lo as u64) < (a_lo as u64));
+ let hi = a_hi.wrapping_add(b_hi).wrapping_add(carry as i64);
+ (lo, hi)
+ }),
+ I64Sub128 => stack_op!(quaternary_into2 i64 => i64, |a_lo, a_hi, b_lo, b_hi| {
+ let lo = a_lo.wrapping_sub(b_lo);
+ let borrow = u64::from((a_lo as u64) < (b_lo as u64));
+ let hi = a_hi.wrapping_sub(b_hi).wrapping_sub(borrow as i64);
+ (lo, hi)
+ }),
+ I64MulWideS => stack_op!(binary_into2 i64 => i64, |a, b| {
+ let product = (a as i128).wrapping_mul(b as i128);
+ (product as i64, (product >> 64) as i64)
+ }),
+ I64MulWideU => stack_op!(binary_into2 i64 => i64, |a, b| {
+ let product = (a as u64 as u128).wrapping_mul(b as u64 as u128);
+ (product as u64 as i64, (product >> 64) as u64 as i64)
+ }),
+ I32Clz => stack_op!(unary i32, |v| v.leading_zeros() as i32),
+ I64Clz => stack_op!(unary i64, |v| i64::from(v.leading_zeros())),
+ I32Ctz => stack_op!(unary i32, |v| v.trailing_zeros() as i32),
+ I64Ctz => stack_op!(unary i64, |v| i64::from(v.trailing_zeros())),
+ I32Popcnt => stack_op!(unary i32, |v| v.count_ones() as i32),
+ I64Popcnt => stack_op!(unary i64, |v| i64::from(v.count_ones())),
- // Reference types
- RefFunc(func_idx) => self.exec_const(ValueRef::from_addr(Some(self.module.resolve_func_addr(*func_idx))))?,
- RefNull(_) => self.exec_const(ValueRef::NULL)?,
- RefIsNull => self.exec_ref_is_null()?,
- MemorySize(addr) => self.exec_memory_size(*addr)?,
- MemoryGrow(addr) => self.exec_memory_grow(*addr)?,
+ // Reference types
+ RefFunc(func_idx) => self.exec_const(ValueRef::from_addr(Some(self.module.resolve_func_addr(*func_idx))))?,
+ RefNull(_) => self.exec_const(ValueRef::NULL)?,
+ RefIsNull => self.exec_ref_is_null()?,
+ MemorySize(addr) => self.exec_memory_size(*addr)?,
+ MemoryGrow(addr) => self.exec_memory_grow(*addr)?,
- // Bulk memory operations
- MemoryCopy { dst_mem, src_mem } => self.exec_memory_copy(*dst_mem, *src_mem)?,
- MemoryFill(addr) => self.exec_memory_fill(*addr)?,
- MemoryFillImm(addr, val, size) => self.exec_memory_fill_imm(*addr, *val, *size)?,
- MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx)?,
- DataDrop(data_index) => self.store.state.get_data_mut(self.module.resolve_data_addr(*data_index)).drop(),
- ElemDrop(elem_index) => self.store.state.get_elem_mut(self.module.resolve_elem_addr(*elem_index)).drop(),
+ // Bulk memory operations
+ MemoryCopy { dst_mem, src_mem } => self.exec_memory_copy(*dst_mem, *src_mem)?,
+ MemoryFill(addr) => self.exec_memory_fill(*addr)?,
+ MemoryFillImm(addr, val, size) => self.exec_memory_fill_imm(*addr, *val, *size)?,
+ MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx)?,
+ DataDrop(data_index) => self.store.state.get_data_mut(self.module.resolve_data_addr(*data_index)).drop(),
+ ElemDrop(elem_index) => self.store.state.get_elem_mut(self.module.resolve_elem_addr(*elem_index)).drop(),
- // Table instructions
- TableGet(table_idx) => self.exec_table_get(*table_idx)?,
- TableSet(table_idx) => self.exec_table_set(*table_idx)?,
- TableSize(table_idx) => self.exec_table_size(*table_idx)?,
- TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx)?,
- TableGrow(table_idx) => self.exec_table_grow(*table_idx)?,
- TableFill(table_idx) => self.exec_table_fill(*table_idx)?,
- TableCopy { dst_table, src_table } => self.exec_table_copy(*dst_table, *src_table)?,
+ // Table instructions
+ TableGet(table_idx) => self.exec_table_get(*table_idx)?,
+ TableSet(table_idx) => self.exec_table_set(*table_idx)?,
+ TableSize(table_idx) => self.exec_table_size(*table_idx)?,
+ TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx)?,
+ TableGrow(table_idx) => self.exec_table_grow(*table_idx)?,
+ TableFill(table_idx) => self.exec_table_fill(*table_idx)?,
+ TableCopy { dst_table, src_table } => self.exec_table_copy(*dst_table, *src_table)?,
- // Core memory load/store operations
- I32Store(m) => self.exec_mem_store::<i32, i32, 4>(m.mem_addr(), m.offset(), |v| v)?,
- 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)?,
- 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)?,
- I64Store16(m) => self.exec_mem_store::<i64, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?,
- I64Store32(m) => self.exec_mem_store::<i64, i32, 4>(m.mem_addr(), m.offset(), |v| v as i32)?,
- I32Load(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
- I64Load(m) => self.exec_mem_load::<i64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
- F32Load(m) => self.exec_mem_load::<f32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
- F64Load(m) => self.exec_mem_load::<f64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
- I32Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
- I32Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
- I32Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
- I32Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
- I64Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load32S(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load32U(m) => self.exec_mem_load::<u32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
+ // Core memory load/store operations
+ I32Store(m) => self.exec_mem_store::<i32, i32, 4>(m.mem_addr(), m.offset(), |v| v)?,
+ 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)?,
+ 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)?,
+ I64Store16(m) => self.exec_mem_store::<i64, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?,
+ I64Store32(m) => self.exec_mem_store::<i64, i32, 4>(m.mem_addr(), m.offset(), |v| v as i32)?,
+ I32Load(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
+ I64Load(m) => self.exec_mem_load::<i64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
+ F32Load(m) => self.exec_mem_load::<f32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
+ F64Load(m) => self.exec_mem_load::<f64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
+ I32Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I32Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I32Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I32Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I64Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load32S(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load32U(m) => self.exec_mem_load::<u32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
- // Numeric conversion operations
- F32ConvertI32S => stack_op!(unary i32 => f32, |v| v as f32),
- F32ConvertI64S => stack_op!(unary i64 => f32, |v| v as f32),
- F64ConvertI32S => stack_op!(unary i32 => f64, |v| f64::from(v)),
- F64ConvertI64S => stack_op!(unary i64 => f64, |v| v as f64),
- F32ConvertI32U => stack_op!(unary u32 => f32, |v| v as f32),
- F32ConvertI64U => stack_op!(unary u64 => f32, |v| v as f32),
- F64ConvertI32U => stack_op!(unary u32 => f64, |v| f64::from(v)),
- F64ConvertI64U => stack_op!(unary u64 => f64, |v| v as f64),
+ // Numeric conversion operations
+ F32ConvertI32S => stack_op!(unary i32 => f32, |v| v as f32),
+ F32ConvertI64S => stack_op!(unary i64 => f32, |v| v as f32),
+ F64ConvertI32S => stack_op!(unary i32 => f64, |v| f64::from(v)),
+ F64ConvertI64S => stack_op!(unary i64 => f64, |v| v as f64),
+ F32ConvertI32U => stack_op!(unary u32 => f32, |v| v as f32),
+ F32ConvertI64U => stack_op!(unary u64 => f32, |v| v as f32),
+ F64ConvertI32U => stack_op!(unary u32 => f64, |v| f64::from(v)),
+ F64ConvertI64U => stack_op!(unary u64 => f64, |v| v as f64),
- // Sign-extension operations
- I32Extend8S => stack_op!(unary i32, |v| i32::from(v as i8)),
- I32Extend16S => stack_op!(unary i32, |v| i32::from(v as i16)),
- I64Extend8S => stack_op!(unary i64, |v| i64::from(v as i8)),
- I64Extend16S => stack_op!(unary i64, |v| i64::from(v as i16)),
- I64Extend32S => stack_op!(unary i64, |v| i64::from(v as i32)),
- I64ExtendI32U => stack_op!(unary u32 => i64, |v| i64::from(v)),
- I64ExtendI32S => stack_op!(unary i32 => i64, |v| i64::from(v)),
- I32WrapI64 => stack_op!(unary i64 => i32, |v| v as i32),
- F32DemoteF64 => stack_op!(unary f64 => f32, |v| v as f32),
- F64PromoteF32 => stack_op!(unary f32 => f64, |v| f64::from(v)),
- F32Abs => stack_op!(unary f32, |v| v.abs()),
- F64Abs => stack_op!(unary f64, |v| v.abs()),
- F32Neg => stack_op!(unary f32, |v| -v),
- F64Neg => stack_op!(unary f64, |v| -v),
- F32Ceil => stack_op!(unary f32, |v| v.ceil()),
- F64Ceil => stack_op!(unary f64, |v| v.ceil()),
- F32Floor => stack_op!(unary f32, |v| v.floor()),
- F64Floor => stack_op!(unary f64, |v| v.floor()),
- F32Trunc => stack_op!(unary f32, |v| v.trunc()),
- F64Trunc => stack_op!(unary f64, |v| v.trunc()),
- F32Nearest => stack_op!(unary f32, |v| v.tw_nearest()),
- F64Nearest => stack_op!(unary f64, |v| v.tw_nearest()),
- F32Sqrt => stack_op!(unary f32, |v| v.sqrt()),
- F64Sqrt => stack_op!(unary f64, |v| v.sqrt()),
- F32Min => stack_op!(binary f32, |a, b| a.tw_minimum(b)),
- F64Min => stack_op!(binary f64, |a, b| a.tw_minimum(b)),
- F32Max => stack_op!(binary f32, |a, b| a.tw_maximum(b)),
- F64Max => stack_op!(binary f64, |a, b| a.tw_maximum(b)),
- F32Copysign => stack_op!(binary f32, |a, b| a.copysign(b)),
- F64Copysign => stack_op!(binary f64, |a, b| a.copysign(b)),
- I32TruncF32S => checked_conv_float!(f32, i32, self),
- I32TruncF64S => checked_conv_float!(f64, i32, self),
- I32TruncF32U => checked_conv_float!(f32, u32, i32, self),
- I32TruncF64U => checked_conv_float!(f64, u32, i32, self),
- I64TruncF32S => checked_conv_float!(f32, i64, self),
- I64TruncF64S => checked_conv_float!(f64, i64, self),
- I64TruncF32U => checked_conv_float!(f32, u64, i64, self),
- I64TruncF64U => checked_conv_float!(f64, u64, i64, self),
+ // Sign-extension operations
+ I32Extend8S => stack_op!(unary i32, |v| i32::from(v as i8)),
+ I32Extend16S => stack_op!(unary i32, |v| i32::from(v as i16)),
+ I64Extend8S => stack_op!(unary i64, |v| i64::from(v as i8)),
+ I64Extend16S => stack_op!(unary i64, |v| i64::from(v as i16)),
+ I64Extend32S => stack_op!(unary i64, |v| i64::from(v as i32)),
+ I64ExtendI32U => stack_op!(unary u32 => i64, |v| i64::from(v)),
+ I64ExtendI32S => stack_op!(unary i32 => i64, |v| i64::from(v)),
+ I32WrapI64 => stack_op!(unary i64 => i32, |v| v as i32),
+ F32DemoteF64 => stack_op!(unary f64 => f32, |v| v as f32),
+ F64PromoteF32 => stack_op!(unary f32 => f64, |v| f64::from(v)),
+ F32Abs => stack_op!(unary f32, |v| v.abs()),
+ F64Abs => stack_op!(unary f64, |v| v.abs()),
+ F32Neg => stack_op!(unary f32, |v| -v),
+ F64Neg => stack_op!(unary f64, |v| -v),
+ F32Ceil => stack_op!(unary f32, |v| v.ceil()),
+ F64Ceil => stack_op!(unary f64, |v| v.ceil()),
+ F32Floor => stack_op!(unary f32, |v| v.floor()),
+ F64Floor => stack_op!(unary f64, |v| v.floor()),
+ F32Trunc => stack_op!(unary f32, |v| v.trunc()),
+ F64Trunc => stack_op!(unary f64, |v| v.trunc()),
+ F32Nearest => stack_op!(unary f32, |v| v.tw_nearest()),
+ F64Nearest => stack_op!(unary f64, |v| v.tw_nearest()),
+ F32Sqrt => stack_op!(unary f32, |v| v.sqrt()),
+ F64Sqrt => stack_op!(unary f64, |v| v.sqrt()),
+ F32Min => stack_op!(binary f32, |a, b| a.tw_minimum(b)),
+ F64Min => stack_op!(binary f64, |a, b| a.tw_minimum(b)),
+ F32Max => stack_op!(binary f32, |a, b| a.tw_maximum(b)),
+ F64Max => stack_op!(binary f64, |a, b| a.tw_maximum(b)),
+ F32Copysign => stack_op!(binary f32, |a, b| a.copysign(b)),
+ F64Copysign => stack_op!(binary f64, |a, b| a.copysign(b)),
+ I32TruncF32S => checked_conv_float!(f32, i32, self),
+ I32TruncF64S => checked_conv_float!(f64, i32, self),
+ I32TruncF32U => checked_conv_float!(f32, u32, i32, self),
+ I32TruncF64U => checked_conv_float!(f64, u32, i32, self),
+ I64TruncF32S => checked_conv_float!(f32, i64, self),
+ I64TruncF64S => checked_conv_float!(f64, i64, self),
+ I64TruncF32U => checked_conv_float!(f32, u64, i64, self),
+ I64TruncF64U => checked_conv_float!(f64, u64, i64, self),
- // Non-trapping float-to-int conversions
- I32TruncSatF32S => stack_op!(unary f32 => i32, |v| v.trunc() as i32),
- I32TruncSatF32U => stack_op!(unary f32 => u32, |v| v.trunc() as u32),
- I32TruncSatF64S => stack_op!(unary f64 => i32, |v| v.trunc() as i32),
- I32TruncSatF64U => stack_op!(unary f64 => u32, |v| v.trunc() as u32),
- I64TruncSatF32S => stack_op!(unary f32 => i64, |v| v.trunc() as i64),
- I64TruncSatF32U => stack_op!(unary f32 => u64, |v| v.trunc() as u64),
- I64TruncSatF64S => stack_op!(unary f64 => i64, |v| v.trunc() as i64),
- I64TruncSatF64U => stack_op!(unary f64 => u64, |v| v.trunc() as u64),
+ // Non-trapping float-to-int conversions
+ I32TruncSatF32S => stack_op!(unary f32 => i32, |v| v.trunc() as i32),
+ I32TruncSatF32U => stack_op!(unary f32 => u32, |v| v.trunc() as u32),
+ I32TruncSatF64S => stack_op!(unary f64 => i32, |v| v.trunc() as i32),
+ I32TruncSatF64U => stack_op!(unary f64 => u32, |v| v.trunc() as u32),
+ I64TruncSatF32S => stack_op!(unary f32 => i64, |v| v.trunc() as i64),
+ I64TruncSatF32U => stack_op!(unary f32 => u64, |v| v.trunc() as u64),
+ I64TruncSatF64S => stack_op!(unary f64 => i64, |v| v.trunc() as i64),
+ I64TruncSatF64U => stack_op!(unary f64 => u64, |v| v.trunc() as u64),
- // SIMD extension
- V128Not => stack_op!(unary Value128, |v| v.v128_not()),
- V128And => stack_op!(binary Value128, |a, b| a.v128_and(b)),
- V128AndNot => stack_op!(binary Value128, |a, b| a.v128_andnot(b)),
- V128Or => stack_op!(binary Value128, |a, b| a.v128_or(b)),
- V128Xor => stack_op!(binary Value128, |a, b| a.v128_xor(b)),
- V128Bitselect => stack_op!(ternary Value128, |a, b, c| Value128::v128_bitselect(a, b, c)),
- V128AnyTrue => stack_op!(unary Value128 => i32, |v| v.v128_any_true() as i32),
- I8x16Swizzle => stack_op!(binary Value128, |a, s| a.i8x16_swizzle(s)),
- I8x16RelaxedSwizzle => stack_op!(binary Value128, |a, s| a.i8x16_relaxed_swizzle(s)),
- V128Load(arg) => self.exec_mem_load::<Value128, 16, _>(arg.mem_addr(), arg.offset(), |v| v)?,
- V128Load8x8S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?,
- V128Load8x8U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?,
- V128Load16x4S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?,
- V128Load16x4U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?,
- V128Load32x2S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?,
- V128Load32x2U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?,
- V128Load8Splat(arg) => self.exec_mem_load::<i8, 1, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i8)?,
- V128Load16Splat(arg) => self.exec_mem_load::<i16, 2, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i16)?,
- V128Load32Splat(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i32)?,
- V128Load64Splat(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i64)?,
- V128Store(arg) => self.exec_mem_store::<Value128, Value128, 16>(arg.mem_addr(), arg.offset(), |v| v)?,
- V128Store8Lane(arg, lane) => self.exec_mem_store_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Store16Lane(arg, lane) => self.exec_mem_store_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Store32Lane(arg, lane) => self.exec_mem_store_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
- 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) => self.exec_const::<Value128>(self.func.data.v128_constants[*arg as usize].into())?,
- 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),
- I16x8ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u16(*lane) as i32),
- I32x4ExtractLane(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i32(*lane)),
- I64x2ExtractLane(lane) => stack_op!(unary Value128 => i64, |v| v.extract_lane_i64(*lane)),
- F32x4ExtractLane(lane) => stack_op!(unary Value128 => f32, |v| v.extract_lane_f32(*lane)),
- F64x2ExtractLane(lane) => stack_op!(unary Value128 => f64, |v| v.extract_lane_f64(*lane)),
- V128Load8Lane(arg, lane) => self.exec_mem_load_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load16Lane(arg, lane) => self.exec_mem_load_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load32Lane(arg, lane) => self.exec_mem_load_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load64Lane(arg, lane) => self.exec_mem_load_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?,
- I8x16ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i8x16_replace_lane(*lane, value as i8)),
- I16x8ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i16x8_replace_lane(*lane, value as i16)),
- I32x4ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i32x4_replace_lane(*lane, value)),
- I64x2ReplaceLane(lane) => stack_op!(binary i64, Value128, |value, vec| vec.i64x2_replace_lane(*lane, value)),
- F32x4ReplaceLane(lane) => stack_op!(binary f32, Value128, |value, vec| vec.f32x4_replace_lane(*lane, value)),
- F64x2ReplaceLane(lane) => stack_op!(binary f64, Value128, |value, vec| vec.f64x2_replace_lane(*lane, value)),
- I8x16Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i8(v as i8)),
- I16x8Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i16(v as i16)),
- I32x4Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i32(v)),
- I64x2Splat => stack_op!(unary i64 => Value128, |v| Value128::splat_i64(v)),
- F32x4Splat => stack_op!(unary f32 => Value128, |v| Value128::splat_f32(v)),
- F64x2Splat => stack_op!(unary f64 => Value128, |v| Value128::splat_f64(v)),
- I8x16Eq => stack_op!(binary Value128, |a, b| a.i8x16_eq(b)),
- I16x8Eq => stack_op!(binary Value128, |a, b| a.i16x8_eq(b)),
- I32x4Eq => stack_op!(binary Value128, |a, b| a.i32x4_eq(b)),
- I64x2Eq => stack_op!(binary Value128, |a, b| a.i64x2_eq(b)),
- F32x4Eq => stack_op!(binary Value128, |a, b| a.f32x4_eq(b)),
- F64x2Eq => stack_op!(binary Value128, |a, b| a.f64x2_eq(b)),
- I8x16Ne => stack_op!(binary Value128, |a, b| a.i8x16_ne(b)),
- I16x8Ne => stack_op!(binary Value128, |a, b| a.i16x8_ne(b)),
- I32x4Ne => stack_op!(binary Value128, |a, b| a.i32x4_ne(b)),
- I64x2Ne => stack_op!(binary Value128, |a, b| a.i64x2_ne(b)),
- F32x4Ne => stack_op!(binary Value128, |a, b| a.f32x4_ne(b)),
- F64x2Ne => stack_op!(binary Value128, |a, b| a.f64x2_ne(b)),
- I8x16LtS => stack_op!(binary Value128, |a, b| a.i8x16_lt_s(b)),
- I16x8LtS => stack_op!(binary Value128, |a, b| a.i16x8_lt_s(b)),
- I32x4LtS => stack_op!(binary Value128, |a, b| a.i32x4_lt_s(b)),
- I64x2LtS => stack_op!(binary Value128, |a, b| a.i64x2_lt_s(b)),
- I8x16LtU => stack_op!(binary Value128, |a, b| a.i8x16_lt_u(b)),
- I16x8LtU => stack_op!(binary Value128, |a, b| a.i16x8_lt_u(b)),
- I32x4LtU => stack_op!(binary Value128, |a, b| a.i32x4_lt_u(b)),
- F32x4Lt => stack_op!(binary Value128, |a, b| a.f32x4_lt(b)),
- F64x2Lt => stack_op!(binary Value128, |a, b| a.f64x2_lt(b)),
- F32x4Gt => stack_op!(binary Value128, |a, b| a.f32x4_gt(b)),
- F64x2Gt => stack_op!(binary Value128, |a, b| a.f64x2_gt(b)),
- I8x16GtS => stack_op!(binary Value128, |a, b| a.i8x16_gt_s(b)),
- I16x8GtS => stack_op!(binary Value128, |a, b| a.i16x8_gt_s(b)),
- I32x4GtS => stack_op!(binary Value128, |a, b| a.i32x4_gt_s(b)),
- I64x2GtS => stack_op!(binary Value128, |a, b| a.i64x2_gt_s(b)),
- I64x2LeS => stack_op!(binary Value128, |a, b| a.i64x2_le_s(b)),
- F32x4Le => stack_op!(binary Value128, |a, b| a.f32x4_le(b)),
- F64x2Le => stack_op!(binary Value128, |a, b| a.f64x2_le(b)),
- I8x16GtU => stack_op!(binary Value128, |a, b| a.i8x16_gt_u(b)),
- I16x8GtU => stack_op!(binary Value128, |a, b| a.i16x8_gt_u(b)),
- I32x4GtU => stack_op!(binary Value128, |a, b| a.i32x4_gt_u(b)),
- F32x4Ge => stack_op!(binary Value128, |a, b| a.f32x4_ge(b)),
- F64x2Ge => stack_op!(binary Value128, |a, b| a.f64x2_ge(b)),
- I8x16LeS => stack_op!(binary Value128, |a, b| a.i8x16_le_s(b)),
- I16x8LeS => stack_op!(binary Value128, |a, b| a.i16x8_le_s(b)),
- I32x4LeS => stack_op!(binary Value128, |a, b| a.i32x4_le_s(b)),
- I8x16LeU => stack_op!(binary Value128, |a, b| a.i8x16_le_u(b)),
- I16x8LeU => stack_op!(binary Value128, |a, b| a.i16x8_le_u(b)),
- I32x4LeU => stack_op!(binary Value128, |a, b| a.i32x4_le_u(b)),
- I8x16GeS => stack_op!(binary Value128, |a, b| a.i8x16_ge_s(b)),
- I16x8GeS => stack_op!(binary Value128, |a, b| a.i16x8_ge_s(b)),
- I32x4GeS => stack_op!(binary Value128, |a, b| a.i32x4_ge_s(b)),
- I64x2GeS => stack_op!(binary Value128, |a, b| a.i64x2_ge_s(b)),
- I8x16GeU => stack_op!(binary Value128, |a, b| a.i8x16_ge_u(b)),
- I16x8GeU => stack_op!(binary Value128, |a, b| a.i16x8_ge_u(b)),
- I32x4GeU => stack_op!(binary Value128, |a, b| a.i32x4_ge_u(b)),
- I8x16Abs => stack_op!(unary Value128, |a| a.i8x16_abs()),
- I16x8Abs => stack_op!(unary Value128, |a| a.i16x8_abs()),
- I32x4Abs => stack_op!(unary Value128, |a| a.i32x4_abs()),
- I64x2Abs => stack_op!(unary Value128, |a| a.i64x2_abs()),
- I8x16Neg => stack_op!(unary Value128, |a| a.i8x16_neg()),
- I16x8Neg => stack_op!(unary Value128, |a| a.i16x8_neg()),
- I32x4Neg => stack_op!(unary Value128, |a| a.i32x4_neg()),
- I64x2Neg => stack_op!(unary Value128, |a| a.i64x2_neg()),
- I8x16AllTrue => stack_op!(unary Value128 => i32, |v| v.i8x16_all_true() as i32),
- I16x8AllTrue => stack_op!(unary Value128 => i32, |v| v.i16x8_all_true() as i32),
- I32x4AllTrue => stack_op!(unary Value128 => i32, |v| v.i32x4_all_true() as i32),
- I64x2AllTrue => stack_op!(unary Value128 => i32, |v| v.i64x2_all_true() as i32),
- I8x16Bitmask => stack_op!(unary Value128 => i32, |v| v.i8x16_bitmask() as i32),
- I16x8Bitmask => stack_op!(unary Value128 => i32, |v| v.i16x8_bitmask() as i32),
- I32x4Bitmask => stack_op!(unary Value128 => i32, |v| v.i32x4_bitmask() as i32),
- I64x2Bitmask => stack_op!(unary Value128 => i32, |v| v.i64x2_bitmask() as i32),
- I8x16Shl => stack_op!(binary i32, Value128, |a, b| b.i8x16_shl(a as u32)),
- I16x8Shl => stack_op!(binary i32, Value128, |a, b| b.i16x8_shl(a as u32)),
- I32x4Shl => stack_op!(binary i32, Value128, |a, b| b.i32x4_shl(a as u32)),
- I64x2Shl => stack_op!(binary i32, Value128, |a, b| b.i64x2_shl(a as u32)),
- I8x16ShrS => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_s(a as u32)),
- I16x8ShrS => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_s(a as u32)),
- I32x4ShrS => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_s(a as u32)),
- I64x2ShrS => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_s(a as u32)),
- I8x16ShrU => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_u(a as u32)),
- I16x8ShrU => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_u(a as u32)),
- I32x4ShrU => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_u(a as u32)),
- I64x2ShrU => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_u(a as u32)),
- I8x16Add => stack_op!(binary Value128, |a, b| a.i8x16_add(b)),
- I16x8Add => stack_op!(binary Value128, |a, b| a.i16x8_add(b)),
- I32x4Add => stack_op!(binary Value128, |a, b| a.i32x4_add(b)),
- I64x2Add => stack_op!(binary Value128, |a, b| a.i64x2_add(b)),
- I8x16Sub => stack_op!(binary Value128, |a, b| a.i8x16_sub(b)),
- I16x8Sub => stack_op!(binary Value128, |a, b| a.i16x8_sub(b)),
- I32x4Sub => stack_op!(binary Value128, |a, b| a.i32x4_sub(b)),
- I64x2Sub => stack_op!(binary Value128, |a, b| a.i64x2_sub(b)),
- I8x16MinS => stack_op!(binary Value128, |a, b| a.i8x16_min_s(b)),
- I16x8MinS => stack_op!(binary Value128, |a, b| a.i16x8_min_s(b)),
- I32x4MinS => stack_op!(binary Value128, |a, b| a.i32x4_min_s(b)),
- I8x16MinU => stack_op!(binary Value128, |a, b| a.i8x16_min_u(b)),
- I16x8MinU => stack_op!(binary Value128, |a, b| a.i16x8_min_u(b)),
- I32x4MinU => stack_op!(binary Value128, |a, b| a.i32x4_min_u(b)),
- I8x16MaxS => stack_op!(binary Value128, |a, b| a.i8x16_max_s(b)),
- I16x8MaxS => stack_op!(binary Value128, |a, b| a.i16x8_max_s(b)),
- I32x4MaxS => stack_op!(binary Value128, |a, b| a.i32x4_max_s(b)),
- I8x16MaxU => stack_op!(binary Value128, |a, b| a.i8x16_max_u(b)),
- I16x8MaxU => stack_op!(binary Value128, |a, b| a.i16x8_max_u(b)),
- I32x4MaxU => stack_op!(binary Value128, |a, b| a.i32x4_max_u(b)),
- I64x2Mul => stack_op!(binary Value128, |a, b| a.i64x2_mul(b)),
- I16x8Mul => stack_op!(binary Value128, |a, b| a.i16x8_mul(b)),
- I32x4Mul => stack_op!(binary Value128, |a, b| a.i32x4_mul(b)),
- I8x16NarrowI16x8S => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_s(a, b)),
- I8x16NarrowI16x8U => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_u(a, b)),
- I16x8NarrowI32x4S => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_s(a, b)),
- I16x8NarrowI32x4U => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_u(a, b)),
- I8x16AddSatS => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_s(b)),
- I16x8AddSatS => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_s(b)),
- I8x16AddSatU => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_u(b)),
- I16x8AddSatU => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_u(b)),
- I8x16SubSatS => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_s(b)),
- I16x8SubSatS => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_s(b)),
- I8x16SubSatU => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_u(b)),
- I16x8SubSatU => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_u(b)),
- I8x16AvgrU => stack_op!(binary Value128, |a, b| a.i8x16_avgr_u(b)),
- I16x8AvgrU => stack_op!(binary Value128, |a, b| a.i16x8_avgr_u(b)),
- I16x8ExtAddPairwiseI8x16S => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_s()),
- I16x8ExtAddPairwiseI8x16U => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_u()),
- I32x4ExtAddPairwiseI16x8S => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_s()),
- I32x4ExtAddPairwiseI16x8U => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_u()),
- I16x8ExtMulLowI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_s(b)),
- I16x8ExtMulLowI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_u(b)),
- I16x8ExtMulHighI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_s(b)),
- I16x8ExtMulHighI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_u(b)),
- I32x4ExtMulLowI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_s(b)),
- I32x4ExtMulLowI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_u(b)),
- I32x4ExtMulHighI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_s(b)),
- I32x4ExtMulHighI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_u(b)),
- I64x2ExtMulLowI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_s(b)),
- I64x2ExtMulLowI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_u(b)),
- I64x2ExtMulHighI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_s(b)),
- I64x2ExtMulHighI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_u(b)),
- I16x8ExtendLowI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_s()),
- I16x8ExtendLowI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_u()),
- I16x8ExtendHighI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_s()),
- I16x8ExtendHighI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_u()),
- I32x4ExtendLowI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_s()),
- I32x4ExtendLowI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_u()),
- I32x4ExtendHighI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_s()),
- I32x4ExtendHighI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_u()),
- I64x2ExtendLowI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_s()),
- I64x2ExtendLowI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_u()),
- I64x2ExtendHighI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_s()),
- I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()),
- I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()),
- I8x16Shuffle(idx) => { let idx = self.func.data.v128_constants[*idx as usize].to_le_bytes(); stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, idx)) }
- I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)),
- I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)),
- I8x16RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i8x16_relaxed_laneselect(a, b, c)),
- I16x8RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i16x8_relaxed_laneselect(a, b, c)),
- I32x4RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i32x4_relaxed_laneselect(a, b, c)),
- I64x2RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i64x2_relaxed_laneselect(a, b, c)),
- I16x8RelaxedQ15mulrS => stack_op!(binary Value128, |a, b| a.i16x8_relaxed_q15mulr_s(b)),
- I16x8RelaxedDotI8x16I7x16S => stack_op!(binary Value128, |a, b| a.i16x8_relaxed_dot_i8x16_i7x16_s(b)),
- I32x4RelaxedDotI8x16I7x16AddS => stack_op!(ternary Value128, |a, b, c| a.i32x4_relaxed_dot_i8x16_i7x16_add_s(b, c)),
- F32x4Ceil => stack_op!(unary Value128, |v| v.f32x4_ceil()),
- F64x2Ceil => stack_op!(unary Value128, |v| v.f64x2_ceil()),
- F32x4Floor => stack_op!(unary Value128, |v| v.f32x4_floor()),
- F64x2Floor => stack_op!(unary Value128, |v| v.f64x2_floor()),
- F32x4Trunc => stack_op!(unary Value128, |v| v.f32x4_trunc()),
- F64x2Trunc => stack_op!(unary Value128, |v| v.f64x2_trunc()),
- F32x4Nearest => stack_op!(unary Value128, |v| v.f32x4_nearest()),
- F64x2Nearest => stack_op!(unary Value128, |v| v.f64x2_nearest()),
- F32x4Abs => stack_op!(unary Value128, |v| v.f32x4_abs()),
- F64x2Abs => stack_op!(unary Value128, |v| v.f64x2_abs()),
- F32x4Neg => stack_op!(unary Value128, |v| v.f32x4_neg()),
- F64x2Neg => stack_op!(unary Value128, |v| v.f64x2_neg()),
- F32x4Sqrt => stack_op!(unary Value128, |v| v.f32x4_sqrt()),
- F64x2Sqrt => stack_op!(unary Value128, |v| v.f64x2_sqrt()),
- F32x4Add => stack_op!(binary Value128, |a, b| a.f32x4_add(b)),
- F64x2Add => stack_op!(binary Value128, |a, b| a.f64x2_add(b)),
- F32x4Sub => stack_op!(binary Value128, |a, b| a.f32x4_sub(b)),
- F64x2Sub => stack_op!(binary Value128, |a, b| a.f64x2_sub(b)),
- F32x4Mul => stack_op!(binary Value128, |a, b| a.f32x4_mul(b)),
- F64x2Mul => stack_op!(binary Value128, |a, b| a.f64x2_mul(b)),
- F32x4Div => stack_op!(binary Value128, |a, b| a.f32x4_div(b)),
- F64x2Div => stack_op!(binary Value128, |a, b| a.f64x2_div(b)),
- F32x4Min => stack_op!(binary Value128, |a, b| a.f32x4_min(b)),
- F64x2Min => stack_op!(binary Value128, |a, b| a.f64x2_min(b)),
- F32x4Max => stack_op!(binary Value128, |a, b| a.f32x4_max(b)),
- F64x2Max => stack_op!(binary Value128, |a, b| a.f64x2_max(b)),
- F32x4PMin => stack_op!(binary Value128, |a, b| a.f32x4_pmin(b)),
- F32x4PMax => stack_op!(binary Value128, |a, b| a.f32x4_pmax(b)),
- F64x2PMin => stack_op!(binary Value128, |a, b| a.f64x2_pmin(b)),
- F64x2PMax => stack_op!(binary Value128, |a, b| a.f64x2_pmax(b)),
- F32x4RelaxedMadd => stack_op!(ternary Value128, |a, b, c| a.f32x4_relaxed_madd(b, c)),
- F32x4RelaxedNmadd => stack_op!(ternary Value128, |a, b, c| a.f32x4_relaxed_nmadd(b, c)),
- F64x2RelaxedMadd => stack_op!(ternary Value128, |a, b, c| a.f64x2_relaxed_madd(b, c)),
- F64x2RelaxedNmadd => stack_op!(ternary Value128, |a, b, c| a.f64x2_relaxed_nmadd(b, c)),
- F32x4RelaxedMin => stack_op!(binary Value128, |a, b| a.f32x4_relaxed_min(b)),
- F32x4RelaxedMax => stack_op!(binary Value128, |a, b| a.f32x4_relaxed_max(b)),
- F64x2RelaxedMin => stack_op!(binary Value128, |a, b| a.f64x2_relaxed_min(b)),
- F64x2RelaxedMax => stack_op!(binary Value128, |a, b| a.f64x2_relaxed_max(b)),
- I32x4TruncSatF32x4S => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_s()),
- I32x4TruncSatF32x4U => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_u()),
- F32x4ConvertI32x4S => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_s()),
- F32x4ConvertI32x4U => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_u()),
- F64x2ConvertLowI32x4S => stack_op!(unary Value128, |v| v.f64x2_convert_low_i32x4_s()),
- F64x2ConvertLowI32x4U => stack_op!(unary Value128, |v| v.f64x2_convert_low_i32x4_u()),
- F32x4DemoteF64x2Zero => stack_op!(unary Value128, |v| v.f32x4_demote_f64x2_zero()),
- F64x2PromoteLowF32x4 => stack_op!(unary Value128, |v| v.f64x2_promote_low_f32x4()),
- I32x4TruncSatF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_s_zero()),
- I32x4TruncSatF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_u_zero()),
- I32x4RelaxedTruncF32x4S => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f32x4_s()),
- I32x4RelaxedTruncF32x4U => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f32x4_u()),
- I32x4RelaxedTruncF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_s_zero()),
- I32x4RelaxedTruncF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_u_zero()),
- };
+ // SIMD extension
+ V128Not => stack_op!(unary Value128, |v| v.v128_not()),
+ V128And => stack_op!(binary Value128, |a, b| a.v128_and(b)),
+ V128AndNot => stack_op!(binary Value128, |a, b| a.v128_andnot(b)),
+ V128Or => stack_op!(binary Value128, |a, b| a.v128_or(b)),
+ V128Xor => stack_op!(binary Value128, |a, b| a.v128_xor(b)),
+ V128Bitselect => stack_op!(ternary Value128, |a, b, c| Value128::v128_bitselect(a, b, c)),
+ V128AnyTrue => stack_op!(unary Value128 => i32, |v| v.v128_any_true() as i32),
+ I8x16Swizzle => stack_op!(binary Value128, |a, s| a.i8x16_swizzle(s)),
+ I8x16RelaxedSwizzle => stack_op!(binary Value128, |a, s| a.i8x16_relaxed_swizzle(s)),
+ V128Load(arg) => self.exec_mem_load::<Value128, 16, _>(arg.mem_addr(), arg.offset(), |v| v)?,
+ V128Load8x8S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?,
+ V128Load8x8U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?,
+ V128Load16x4S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?,
+ V128Load16x4U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?,
+ V128Load32x2S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?,
+ V128Load32x2U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?,
+ V128Load8Splat(arg) => self.exec_mem_load::<i8, 1, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i8)?,
+ V128Load16Splat(arg) => self.exec_mem_load::<i16, 2, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i16)?,
+ V128Load32Splat(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i32)?,
+ V128Load64Splat(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i64)?,
+ V128Store(arg) => self.exec_mem_store::<Value128, Value128, 16>(arg.mem_addr(), arg.offset(), |v| v)?,
+ V128Store8Lane(arg, lane) => self.exec_mem_store_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Store16Lane(arg, lane) => self.exec_mem_store_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Store32Lane(arg, lane) => self.exec_mem_store_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
+ 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) => self.exec_const(Value128(*self.func.data.v128_constants.get(*arg as usize).unwrap_or_else(|| {cold_path(); unreachable!("invalid v128 constant index") })))?,
+ 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),
+ I16x8ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u16(*lane) as i32),
+ I32x4ExtractLane(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i32(*lane)),
+ I64x2ExtractLane(lane) => stack_op!(unary Value128 => i64, |v| v.extract_lane_i64(*lane)),
+ F32x4ExtractLane(lane) => stack_op!(unary Value128 => f32, |v| v.extract_lane_f32(*lane)),
+ F64x2ExtractLane(lane) => stack_op!(unary Value128 => f64, |v| v.extract_lane_f64(*lane)),
+ V128Load8Lane(arg, lane) => self.exec_mem_load_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load16Lane(arg, lane) => self.exec_mem_load_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load32Lane(arg, lane) => self.exec_mem_load_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load64Lane(arg, lane) => self.exec_mem_load_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?,
+ I8x16ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i8x16_replace_lane(*lane, value as i8)),
+ I16x8ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i16x8_replace_lane(*lane, value as i16)),
+ I32x4ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i32x4_replace_lane(*lane, value)),
+ I64x2ReplaceLane(lane) => stack_op!(binary i64, Value128, |value, vec| vec.i64x2_replace_lane(*lane, value)),
+ F32x4ReplaceLane(lane) => stack_op!(binary f32, Value128, |value, vec| vec.f32x4_replace_lane(*lane, value)),
+ F64x2ReplaceLane(lane) => stack_op!(binary f64, Value128, |value, vec| vec.f64x2_replace_lane(*lane, value)),
+ I8x16Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i8(v as i8)),
+ I16x8Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i16(v as i16)),
+ I32x4Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i32(v)),
+ I64x2Splat => stack_op!(unary i64 => Value128, |v| Value128::splat_i64(v)),
+ F32x4Splat => stack_op!(unary f32 => Value128, |v| Value128::splat_f32(v)),
+ F64x2Splat => stack_op!(unary f64 => Value128, |v| Value128::splat_f64(v)),
+ I8x16Eq => stack_op!(binary Value128, |a, b| a.i8x16_eq(b)),
+ I16x8Eq => stack_op!(binary Value128, |a, b| a.i16x8_eq(b)),
+ I32x4Eq => stack_op!(binary Value128, |a, b| a.i32x4_eq(b)),
+ I64x2Eq => stack_op!(binary Value128, |a, b| a.i64x2_eq(b)),
+ F32x4Eq => stack_op!(binary Value128, |a, b| a.f32x4_eq(b)),
+ F64x2Eq => stack_op!(binary Value128, |a, b| a.f64x2_eq(b)),
+ I8x16Ne => stack_op!(binary Value128, |a, b| a.i8x16_ne(b)),
+ I16x8Ne => stack_op!(binary Value128, |a, b| a.i16x8_ne(b)),
+ I32x4Ne => stack_op!(binary Value128, |a, b| a.i32x4_ne(b)),
+ I64x2Ne => stack_op!(binary Value128, |a, b| a.i64x2_ne(b)),
+ F32x4Ne => stack_op!(binary Value128, |a, b| a.f32x4_ne(b)),
+ F64x2Ne => stack_op!(binary Value128, |a, b| a.f64x2_ne(b)),
+ I8x16LtS => stack_op!(binary Value128, |a, b| a.i8x16_lt_s(b)),
+ I16x8LtS => stack_op!(binary Value128, |a, b| a.i16x8_lt_s(b)),
+ I32x4LtS => stack_op!(binary Value128, |a, b| a.i32x4_lt_s(b)),
+ I64x2LtS => stack_op!(binary Value128, |a, b| a.i64x2_lt_s(b)),
+ I8x16LtU => stack_op!(binary Value128, |a, b| a.i8x16_lt_u(b)),
+ I16x8LtU => stack_op!(binary Value128, |a, b| a.i16x8_lt_u(b)),
+ I32x4LtU => stack_op!(binary Value128, |a, b| a.i32x4_lt_u(b)),
+ F32x4Lt => stack_op!(binary Value128, |a, b| a.f32x4_lt(b)),
+ F64x2Lt => stack_op!(binary Value128, |a, b| a.f64x2_lt(b)),
+ F32x4Gt => stack_op!(binary Value128, |a, b| a.f32x4_gt(b)),
+ F64x2Gt => stack_op!(binary Value128, |a, b| a.f64x2_gt(b)),
+ I8x16GtS => stack_op!(binary Value128, |a, b| a.i8x16_gt_s(b)),
+ I16x8GtS => stack_op!(binary Value128, |a, b| a.i16x8_gt_s(b)),
+ I32x4GtS => stack_op!(binary Value128, |a, b| a.i32x4_gt_s(b)),
+ I64x2GtS => stack_op!(binary Value128, |a, b| a.i64x2_gt_s(b)),
+ I64x2LeS => stack_op!(binary Value128, |a, b| a.i64x2_le_s(b)),
+ F32x4Le => stack_op!(binary Value128, |a, b| a.f32x4_le(b)),
+ F64x2Le => stack_op!(binary Value128, |a, b| a.f64x2_le(b)),
+ I8x16GtU => stack_op!(binary Value128, |a, b| a.i8x16_gt_u(b)),
+ I16x8GtU => stack_op!(binary Value128, |a, b| a.i16x8_gt_u(b)),
+ I32x4GtU => stack_op!(binary Value128, |a, b| a.i32x4_gt_u(b)),
+ F32x4Ge => stack_op!(binary Value128, |a, b| a.f32x4_ge(b)),
+ F64x2Ge => stack_op!(binary Value128, |a, b| a.f64x2_ge(b)),
+ I8x16LeS => stack_op!(binary Value128, |a, b| a.i8x16_le_s(b)),
+ I16x8LeS => stack_op!(binary Value128, |a, b| a.i16x8_le_s(b)),
+ I32x4LeS => stack_op!(binary Value128, |a, b| a.i32x4_le_s(b)),
+ I8x16LeU => stack_op!(binary Value128, |a, b| a.i8x16_le_u(b)),
+ I16x8LeU => stack_op!(binary Value128, |a, b| a.i16x8_le_u(b)),
+ I32x4LeU => stack_op!(binary Value128, |a, b| a.i32x4_le_u(b)),
+ I8x16GeS => stack_op!(binary Value128, |a, b| a.i8x16_ge_s(b)),
+ I16x8GeS => stack_op!(binary Value128, |a, b| a.i16x8_ge_s(b)),
+ I32x4GeS => stack_op!(binary Value128, |a, b| a.i32x4_ge_s(b)),
+ I64x2GeS => stack_op!(binary Value128, |a, b| a.i64x2_ge_s(b)),
+ I8x16GeU => stack_op!(binary Value128, |a, b| a.i8x16_ge_u(b)),
+ I16x8GeU => stack_op!(binary Value128, |a, b| a.i16x8_ge_u(b)),
+ I32x4GeU => stack_op!(binary Value128, |a, b| a.i32x4_ge_u(b)),
+ I8x16Abs => stack_op!(unary Value128, |a| a.i8x16_abs()),
+ I16x8Abs => stack_op!(unary Value128, |a| a.i16x8_abs()),
+ I32x4Abs => stack_op!(unary Value128, |a| a.i32x4_abs()),
+ I64x2Abs => stack_op!(unary Value128, |a| a.i64x2_abs()),
+ I8x16Neg => stack_op!(unary Value128, |a| a.i8x16_neg()),
+ I16x8Neg => stack_op!(unary Value128, |a| a.i16x8_neg()),
+ I32x4Neg => stack_op!(unary Value128, |a| a.i32x4_neg()),
+ I64x2Neg => stack_op!(unary Value128, |a| a.i64x2_neg()),
+ I8x16AllTrue => stack_op!(unary Value128 => i32, |v| v.i8x16_all_true() as i32),
+ I16x8AllTrue => stack_op!(unary Value128 => i32, |v| v.i16x8_all_true() as i32),
+ I32x4AllTrue => stack_op!(unary Value128 => i32, |v| v.i32x4_all_true() as i32),
+ I64x2AllTrue => stack_op!(unary Value128 => i32, |v| v.i64x2_all_true() as i32),
+ I8x16Bitmask => stack_op!(unary Value128 => i32, |v| v.i8x16_bitmask() as i32),
+ I16x8Bitmask => stack_op!(unary Value128 => i32, |v| v.i16x8_bitmask() as i32),
+ I32x4Bitmask => stack_op!(unary Value128 => i32, |v| v.i32x4_bitmask() as i32),
+ I64x2Bitmask => stack_op!(unary Value128 => i32, |v| v.i64x2_bitmask() as i32),
+ I8x16Shl => stack_op!(binary i32, Value128, |a, b| b.i8x16_shl(a as u32)),
+ I16x8Shl => stack_op!(binary i32, Value128, |a, b| b.i16x8_shl(a as u32)),
+ I32x4Shl => stack_op!(binary i32, Value128, |a, b| b.i32x4_shl(a as u32)),
+ I64x2Shl => stack_op!(binary i32, Value128, |a, b| b.i64x2_shl(a as u32)),
+ I8x16ShrS => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_s(a as u32)),
+ I16x8ShrS => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_s(a as u32)),
+ I32x4ShrS => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_s(a as u32)),
+ I64x2ShrS => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_s(a as u32)),
+ I8x16ShrU => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_u(a as u32)),
+ I16x8ShrU => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_u(a as u32)),
+ I32x4ShrU => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_u(a as u32)),
+ I64x2ShrU => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_u(a as u32)),
+ I8x16Add => stack_op!(binary Value128, |a, b| a.i8x16_add(b)),
+ I16x8Add => stack_op!(binary Value128, |a, b| a.i16x8_add(b)),
+ I32x4Add => stack_op!(binary Value128, |a, b| a.i32x4_add(b)),
+ I64x2Add => stack_op!(binary Value128, |a, b| a.i64x2_add(b)),
+ I8x16Sub => stack_op!(binary Value128, |a, b| a.i8x16_sub(b)),
+ I16x8Sub => stack_op!(binary Value128, |a, b| a.i16x8_sub(b)),
+ I32x4Sub => stack_op!(binary Value128, |a, b| a.i32x4_sub(b)),
+ I64x2Sub => stack_op!(binary Value128, |a, b| a.i64x2_sub(b)),
+ I8x16MinS => stack_op!(binary Value128, |a, b| a.i8x16_min_s(b)),
+ I16x8MinS => stack_op!(binary Value128, |a, b| a.i16x8_min_s(b)),
+ I32x4MinS => stack_op!(binary Value128, |a, b| a.i32x4_min_s(b)),
+ I8x16MinU => stack_op!(binary Value128, |a, b| a.i8x16_min_u(b)),
+ I16x8MinU => stack_op!(binary Value128, |a, b| a.i16x8_min_u(b)),
+ I32x4MinU => stack_op!(binary Value128, |a, b| a.i32x4_min_u(b)),
+ I8x16MaxS => stack_op!(binary Value128, |a, b| a.i8x16_max_s(b)),
+ I16x8MaxS => stack_op!(binary Value128, |a, b| a.i16x8_max_s(b)),
+ I32x4MaxS => stack_op!(binary Value128, |a, b| a.i32x4_max_s(b)),
+ I8x16MaxU => stack_op!(binary Value128, |a, b| a.i8x16_max_u(b)),
+ I16x8MaxU => stack_op!(binary Value128, |a, b| a.i16x8_max_u(b)),
+ I32x4MaxU => stack_op!(binary Value128, |a, b| a.i32x4_max_u(b)),
+ I64x2Mul => stack_op!(binary Value128, |a, b| a.i64x2_mul(b)),
+ I16x8Mul => stack_op!(binary Value128, |a, b| a.i16x8_mul(b)),
+ I32x4Mul => stack_op!(binary Value128, |a, b| a.i32x4_mul(b)),
+ I8x16NarrowI16x8S => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_s(a, b)),
+ I8x16NarrowI16x8U => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_u(a, b)),
+ I16x8NarrowI32x4S => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_s(a, b)),
+ I16x8NarrowI32x4U => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_u(a, b)),
+ I8x16AddSatS => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_s(b)),
+ I16x8AddSatS => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_s(b)),
+ I8x16AddSatU => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_u(b)),
+ I16x8AddSatU => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_u(b)),
+ I8x16SubSatS => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_s(b)),
+ I16x8SubSatS => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_s(b)),
+ I8x16SubSatU => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_u(b)),
+ I16x8SubSatU => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_u(b)),
+ I8x16AvgrU => stack_op!(binary Value128, |a, b| a.i8x16_avgr_u(b)),
+ I16x8AvgrU => stack_op!(binary Value128, |a, b| a.i16x8_avgr_u(b)),
+ I16x8ExtAddPairwiseI8x16S => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_s()),
+ I16x8ExtAddPairwiseI8x16U => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_u()),
+ I32x4ExtAddPairwiseI16x8S => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_s()),
+ I32x4ExtAddPairwiseI16x8U => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_u()),
+ I16x8ExtMulLowI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_s(b)),
+ I16x8ExtMulLowI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_u(b)),
+ I16x8ExtMulHighI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_s(b)),
+ I16x8ExtMulHighI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_u(b)),
+ I32x4ExtMulLowI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_s(b)),
+ I32x4ExtMulLowI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_u(b)),
+ I32x4ExtMulHighI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_s(b)),
+ I32x4ExtMulHighI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_u(b)),
+ I64x2ExtMulLowI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_s(b)),
+ I64x2ExtMulLowI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_u(b)),
+ I64x2ExtMulHighI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_s(b)),
+ I64x2ExtMulHighI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_u(b)),
+ I16x8ExtendLowI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_s()),
+ I16x8ExtendLowI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_u()),
+ I16x8ExtendHighI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_s()),
+ I16x8ExtendHighI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_u()),
+ I32x4ExtendLowI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_s()),
+ I32x4ExtendLowI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_u()),
+ I32x4ExtendHighI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_s()),
+ I32x4ExtendHighI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_u()),
+ I64x2ExtendLowI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_s()),
+ I64x2ExtendLowI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_u()),
+ I64x2ExtendHighI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_s()),
+ I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()),
+ I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()),
+ I8x16Shuffle(idx) => {
+ let mask = self.func.data.v128_constants.get(*idx as usize).unwrap_or_else(|| {cold_path(); unreachable!("invalid i128 constant index")});
+ stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, *mask))
+ },
+ I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)),
+ I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)),
+ I8x16RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i8x16_relaxed_laneselect(a, b, c)),
+ I16x8RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i16x8_relaxed_laneselect(a, b, c)),
+ I32x4RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i32x4_relaxed_laneselect(a, b, c)),
+ I64x2RelaxedLaneselect => stack_op!(ternary Value128, |a, b, c| Value128::i64x2_relaxed_laneselect(a, b, c)),
+ I16x8RelaxedQ15mulrS => stack_op!(binary Value128, |a, b| a.i16x8_relaxed_q15mulr_s(b)),
+ I16x8RelaxedDotI8x16I7x16S => stack_op!(binary Value128, |a, b| a.i16x8_relaxed_dot_i8x16_i7x16_s(b)),
+ I32x4RelaxedDotI8x16I7x16AddS => stack_op!(ternary Value128, |a, b, c| a.i32x4_relaxed_dot_i8x16_i7x16_add_s(b, c)),
+ F32x4Ceil => stack_op!(unary Value128, |v| v.f32x4_ceil()),
+ F64x2Ceil => stack_op!(unary Value128, |v| v.f64x2_ceil()),
+ F32x4Floor => stack_op!(unary Value128, |v| v.f32x4_floor()),
+ F64x2Floor => stack_op!(unary Value128, |v| v.f64x2_floor()),
+ F32x4Trunc => stack_op!(unary Value128, |v| v.f32x4_trunc()),
+ F64x2Trunc => stack_op!(unary Value128, |v| v.f64x2_trunc()),
+ F32x4Nearest => stack_op!(unary Value128, |v| v.f32x4_nearest()),
+ F64x2Nearest => stack_op!(unary Value128, |v| v.f64x2_nearest()),
+ F32x4Abs => stack_op!(unary Value128, |v| v.f32x4_abs()),
+ F64x2Abs => stack_op!(unary Value128, |v| v.f64x2_abs()),
+ F32x4Neg => stack_op!(unary Value128, |v| v.f32x4_neg()),
+ F64x2Neg => stack_op!(unary Value128, |v| v.f64x2_neg()),
+ F32x4Sqrt => stack_op!(unary Value128, |v| v.f32x4_sqrt()),
+ F64x2Sqrt => stack_op!(unary Value128, |v| v.f64x2_sqrt()),
+ F32x4Add => stack_op!(binary Value128, |a, b| a.f32x4_add(b)),
+ F64x2Add => stack_op!(binary Value128, |a, b| a.f64x2_add(b)),
+ F32x4Sub => stack_op!(binary Value128, |a, b| a.f32x4_sub(b)),
+ F64x2Sub => stack_op!(binary Value128, |a, b| a.f64x2_sub(b)),
+ F32x4Mul => stack_op!(binary Value128, |a, b| a.f32x4_mul(b)),
+ F64x2Mul => stack_op!(binary Value128, |a, b| a.f64x2_mul(b)),
+ F32x4Div => stack_op!(binary Value128, |a, b| a.f32x4_div(b)),
+ F64x2Div => stack_op!(binary Value128, |a, b| a.f64x2_div(b)),
+ F32x4Min => stack_op!(binary Value128, |a, b| a.f32x4_min(b)),
+ F64x2Min => stack_op!(binary Value128, |a, b| a.f64x2_min(b)),
+ F32x4Max => stack_op!(binary Value128, |a, b| a.f32x4_max(b)),
+ F64x2Max => stack_op!(binary Value128, |a, b| a.f64x2_max(b)),
+ F32x4PMin => stack_op!(binary Value128, |a, b| a.f32x4_pmin(b)),
+ F32x4PMax => stack_op!(binary Value128, |a, b| a.f32x4_pmax(b)),
+ F64x2PMin => stack_op!(binary Value128, |a, b| a.f64x2_pmin(b)),
+ F64x2PMax => stack_op!(binary Value128, |a, b| a.f64x2_pmax(b)),
+ F32x4RelaxedMadd => stack_op!(ternary Value128, |a, b, c| a.f32x4_relaxed_madd(b, c)),
+ F32x4RelaxedNmadd => stack_op!(ternary Value128, |a, b, c| a.f32x4_relaxed_nmadd(b, c)),
+ F64x2RelaxedMadd => stack_op!(ternary Value128, |a, b, c| a.f64x2_relaxed_madd(b, c)),
+ F64x2RelaxedNmadd => stack_op!(ternary Value128, |a, b, c| a.f64x2_relaxed_nmadd(b, c)),
+ F32x4RelaxedMin => stack_op!(binary Value128, |a, b| a.f32x4_relaxed_min(b)),
+ F32x4RelaxedMax => stack_op!(binary Value128, |a, b| a.f32x4_relaxed_max(b)),
+ F64x2RelaxedMin => stack_op!(binary Value128, |a, b| a.f64x2_relaxed_min(b)),
+ F64x2RelaxedMax => stack_op!(binary Value128, |a, b| a.f64x2_relaxed_max(b)),
+ I32x4TruncSatF32x4S => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_s()),
+ I32x4TruncSatF32x4U => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_u()),
+ F32x4ConvertI32x4S => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_s()),
+ F32x4ConvertI32x4U => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_u()),
+ F64x2ConvertLowI32x4S => stack_op!(unary Value128, |v| v.f64x2_convert_low_i32x4_s()),
+ F64x2ConvertLowI32x4U => stack_op!(unary Value128, |v| v.f64x2_convert_low_i32x4_u()),
+ F32x4DemoteF64x2Zero => stack_op!(unary Value128, |v| v.f32x4_demote_f64x2_zero()),
+ F64x2PromoteLowF32x4 => stack_op!(unary Value128, |v| v.f64x2_promote_low_f32x4()),
+ I32x4TruncSatF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_s_zero()),
+ I32x4TruncSatF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_u_zero()),
+ I32x4RelaxedTruncF32x4S => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f32x4_s()),
+ I32x4RelaxedTruncF32x4U => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f32x4_u()),
+ I32x4RelaxedTruncF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_s_zero()),
+ I32x4RelaxedTruncF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_relaxed_trunc_f64x2_u_zero()),
+ };
- self.cf.incr_instr_ptr();
- }
+ self.cf.incr_instr_ptr();
Ok(None)
}
@@ -680,42 +699,59 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
#[inline(always)]
- fn exec_jump_if_zero(&mut self, ip: u32) -> bool {
- if self.store.stack.values.pop::<i32>() == 0 {
+ fn jump_if(&mut self, condition: bool, ip: u32) -> bool {
+ if condition {
self.cf.instr_ptr = ip;
- return true;
}
- false
+ condition
+ }
+
+ #[inline(always)]
+ fn exec_jump_if_zero(&mut self, ip: u32) -> bool {
+ let condition = self.store.stack.values.pop::<i32>() == 0;
+ self.jump_if(condition, ip)
}
#[inline(always)]
fn exec_jump_if_non_zero(&mut self, ip: u32) -> bool {
- if self.store.stack.values.pop::<i32>() != 0 {
- self.cf.instr_ptr = ip;
- return true;
- }
- false
+ let condition = self.store.stack.values.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.stack.values.pop::<i32>(), imm, op);
+ self.jump_if(condition, target_ip)
+ }
+
+ #[inline(always)]
+ fn exec_jump_cmp_stack_const_64(&mut self, target_ip: u32, imm: i64, op: CmpOp) -> bool {
+ let condition = cmp_i64(self.store.stack.values.pop::<i64>(), imm, op);
+ self.jump_if(condition, target_ip)
}
#[inline(always)]
fn exec_jump_cmp_local_const_32(&mut self, target_ip: u32, local: LocalAddr, imm: i32, op: CmpOp) -> bool {
- let lhs = self.store.stack.values.local_get::<i32>(&self.cf, local);
- if cmp_i32(lhs, imm, op) {
- self.cf.instr_ptr = target_ip;
- return true;
- }
- false
+ self.jump_if(cmp_i32(self.store.stack.values.local_get::<i32>(&self.cf, local), imm, op), target_ip)
+ }
+
+ #[inline(always)]
+ fn exec_jump_cmp_local_const_64(&mut self, target_ip: u32, local: LocalAddr, imm: i32, op: CmpOp) -> bool {
+ self.jump_if(cmp_i64(self.store.stack.values.local_get::<i64>(&self.cf, local), i64::from(imm), op), target_ip)
}
#[inline(always)]
fn exec_jump_cmp_local_local_32(&mut self, target_ip: u32, left: LocalAddr, right: LocalAddr, op: CmpOp) -> bool {
let lhs = self.store.stack.values.local_get::<i32>(&self.cf, left);
let rhs = self.store.stack.values.local_get::<i32>(&self.cf, right);
- if cmp_i32(lhs, rhs, op) {
- self.cf.instr_ptr = target_ip;
- return true;
- }
- false
+ self.jump_if(cmp_i32(lhs, rhs, op), target_ip)
+ }
+
+ #[inline(always)]
+ fn exec_jump_cmp_local_local_64(&mut self, target_ip: u32, left: LocalAddr, right: LocalAddr, op: CmpOp) -> bool {
+ let lhs = self.store.stack.values.local_get::<i64>(&self.cf, left);
+ let rhs = self.store.stack.values.local_get::<i64>(&self.cf, right);
+ self.jump_if(cmp_i64(lhs, rhs, op), target_ip)
}
#[inline(always)]
@@ -867,62 +903,83 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
false
}
- #[inline(always)]
fn local_mem_addr<const N: usize>(&self, memarg: MemoryArg, addr_local: u8) -> Result<usize> {
let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, u16::from(addr_local)));
- let Some(Ok(addr)) = memarg.offset().checked_add(addr).map(|a| a.try_into()) else {
+ Self::effective_addr::<N>(addr, memarg.offset())
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ fn effective_addr<const N: usize>(addr: u64, offset: u64) -> Result<usize> {
+ let Some(addr) = offset.checked_add(addr) else {
+ cold_path();
+ return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
+ };
+ Ok(addr as usize)
+ }
+
+ #[cfg(not(target_pointer_width = "64"))]
+ fn effective_addr<const N: usize>(addr: u64, offset: u64) -> Result<usize> {
+ let Some(Ok(addr)) = offset.checked_add(addr).map(|a| a.try_into()) else {
cold_path();
return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
};
Ok(addr)
}
- #[inline(always)]
- fn exec_store_local_local<T: InternalValue, U: MemValue<N>, const N: usize>(
+ fn pop_mem_addr(&mut self, is_64bit: bool) -> u64 {
+ if is_64bit {
+ self.store.stack.values.pop::<i64>() as u64
+ } else {
+ self.store.stack.values.pop::<i32>() as u32 as u64
+ }
+ }
+
+ fn exec_store_local_local<T: InternalValue + MemValue<N>, const N: usize>(
&mut self,
memarg: MemoryArg,
addr_local: u8,
value_local: u8,
- cast: fn(T) -> U,
) -> Result<()> {
+ let effective_addr = self.local_mem_addr::<N>(memarg, addr_local)?;
+ let value = self.store.stack.values.local_get::<T>(&self.cf, u16::from(value_local)).to_mem_bytes();
let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(memarg.mem_addr()));
- let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, u16::from(addr_local)));
- let value = cast(self.store.stack.values.local_get::<T>(&self.cf, u16::from(value_local))).to_mem_bytes();
- let Some(effective_addr) = memarg.offset().checked_add(addr) else {
- cold_path();
- return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
- };
- let Ok(effective_addr) = usize::try_from(effective_addr) else {
- cold_path();
- return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
- };
mem.store(effective_addr, value.len(), &value)?;
Ok(())
}
- #[inline(always)]
- fn exec_i32_load_local_value(&self, memarg: MemoryArg, addr_local: u8) -> Result<i32> {
+ fn exec_load_local_value<T: MemValue<N>, const N: usize>(&self, memarg: MemoryArg, addr_local: u8) -> Result<T> {
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(memarg.mem_addr()));
- mem.load_as::<4, i32>(self.local_mem_addr::<4>(memarg, addr_local)?)
+ mem.load_as::<N, T>(self.local_mem_addr::<N>(memarg, addr_local)?)
}
- #[inline(always)]
- fn exec_i32_load_local(&mut self, memarg: MemoryArg, addr_local: u8) -> Result<()> {
- let value = self.exec_i32_load_local_value(memarg, addr_local)?;
+ fn exec_load_local<T: InternalValue + MemValue<N>, const N: usize>(
+ &mut self,
+ memarg: MemoryArg,
+ addr_local: u8,
+ ) -> Result<()> {
+ let value = self.exec_load_local_value::<T, N>(memarg, addr_local)?;
self.store.stack.values.push(value)
}
- #[inline(always)]
- fn exec_i32_load_local_tee(&mut self, memarg: MemoryArg, addr_local: u8, dst_local: u8) -> Result<()> {
- let value = self.exec_i32_load_local_value(memarg, addr_local)?;
+ fn exec_load_local_tee<T: InternalValue + MemValue<N>, const N: usize>(
+ &mut self,
+ memarg: MemoryArg,
+ addr_local: u8,
+ dst_local: u8,
+ ) -> Result<()> {
+ let value = self.exec_load_local_value::<T, N>(memarg, addr_local)?;
self.store.stack.values.local_set(&self.cf, u16::from(dst_local), value);
self.store.stack.values.push(value)?;
Ok(())
}
- #[inline(always)]
- fn exec_i32_load_local_set(&mut self, memarg: MemoryArg, addr_local: u8, dst_local: u8) -> Result<()> {
- let value = self.exec_i32_load_local_value(memarg, addr_local)?;
+ fn exec_load_local_set<T: InternalValue + MemValue<N>, const N: usize>(
+ &mut self,
+ memarg: MemoryArg,
+ addr_local: u8,
+ dst_local: u8,
+ ) -> Result<()> {
+ let value = self.exec_load_local_value::<T, N>(memarg, addr_local)?;
self.store.stack.values.local_set(&self.cf, u16::from(dst_local), value);
Ok(())
}
@@ -932,15 +989,15 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
fn exec_global_set<T: InternalValue>(&mut self, global_index: u32) {
- let val = self.store.stack.values.pop::<T>().into();
- self.store.state.set_global_val(self.module.resolve_global_addr(global_index), val);
+ let global_addr = self.module.resolve_global_addr(global_index);
+ let value = self.store.stack.values.pop::<T>().into();
+ self.store.state.set_global_val(global_addr, value);
}
fn exec_global_set_32(&mut self, global_index: u32) {
let global_addr = self.module.resolve_global_addr(global_index);
let raw = self.store.stack.values.pop::<Value32>();
- let ty = self.store.state.get_global(global_addr).ty.ty;
- let value = match ty {
+ let value = match self.store.state.get_global(global_addr).ty.ty {
WasmType::I32 | WasmType::F32 => TinyWasmValue::Value32(raw),
WasmType::RefExtern | WasmType::RefFunc => TinyWasmValue::ValueRef(ValueRef::from_raw(raw)),
WasmType::I64 | WasmType::F64 | WasmType::V128 => unreachable!("invalid global.set.32 target type"),
@@ -1004,13 +1061,15 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let size: i32 = self.store.stack.values.pop();
let val: i32 = self.store.stack.values.pop();
let dst: i32 = self.store.stack.values.pop();
-
- let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(addr));
- mem.fill(dst as usize, size as usize, val as u8)
+ self.exec_memory_fill_impl(addr, dst, val as u8, size)
}
fn exec_memory_fill_imm(&mut self, addr: u32, val: u8, size: i32) -> Result<()> {
let dst: i32 = self.store.stack.values.pop();
+ self.exec_memory_fill_impl(addr, dst, val, size)
+ }
+
+ fn exec_memory_fill_impl(&mut self, addr: u32, dst: i32, val: u8, size: i32) -> Result<()> {
let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(addr));
mem.fill(dst as usize, size as usize, val)
}
@@ -1076,10 +1135,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let mut imm = self.store.stack.values.pop::<Value128>().to_mem_bytes();
let val = self.store.stack.values.pop::<i32>() as u64;
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
- let Some(Ok(addr)) = offset.checked_add(val).map(TryInto::try_into) else {
- cold_path();
- return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: val as usize, len: LOAD_SIZE, max: 0 }));
- };
+ let addr = Self::effective_addr::<LOAD_SIZE>(val, offset)?;
let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?.to_mem_bytes();
let offset = lane as usize * LOAD_SIZE;
@@ -1097,17 +1153,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
cast: fn(LOAD) -> TARGET,
) -> Result<()> {
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
-
- let base: u64 = if mem.is_64bit() {
+ let base = if mem.is_64bit() {
self.store.stack.values.pop::<i64>() as u64
} else {
self.store.stack.values.pop::<i32>() as u32 as u64
};
-
- let Some(Ok(addr)) = base.checked_add(offset).map(usize::try_from) else {
- cold_path();
- return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: base as usize, len: LOAD_SIZE, max: 0 }));
- };
+ let addr = Self::effective_addr::<LOAD_SIZE>(base, offset)?;
let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?;
self.store.stack.values.push(cast(val))?;
@@ -1120,21 +1171,15 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
offset: u64,
lane: u8,
) -> Result<()> {
- let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(mem_addr));
let bytes = self.store.stack.values.pop::<Value128>().to_mem_bytes();
let lane_offset = lane as usize * N;
let mut val = [0u8; N];
val.copy_from_slice(&bytes[lane_offset..lane_offset + N]);
- let addr = match mem.is_64bit() {
- true => self.store.stack.values.pop::<i64>() as u64,
- false => self.store.stack.values.pop::<i32>() as u32 as u64,
- };
-
- let Some(Ok(effective_addr)) = offset.checked_add(addr).map(usize::try_from) else {
- cold_path();
- return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
- };
+ let mem_addr = self.module.resolve_mem_addr(mem_addr);
+ let is_64bit = self.store.state.get_mem(mem_addr).is_64bit();
+ let effective_addr = Self::effective_addr::<N>(self.pop_mem_addr(is_64bit), offset)?;
+ let mem = self.store.state.get_mem_mut(mem_addr);
mem.store(effective_addr, val.len(), &val)?;
@@ -1148,18 +1193,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
cast: fn(T) -> U,
) -> Result<()> {
let val = self.store.stack.values.pop::<T>();
- let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(mem_addr));
let val = (cast(val)).to_mem_bytes();
- let addr = match mem.is_64bit() {
- true => self.store.stack.values.pop::<i64>() as u64,
- false => u64::from(self.store.stack.values.pop::<i32>() as u32),
- };
-
- let Some(Ok(effective_addr)) = offset.checked_add(addr).map(usize::try_from) else {
- cold_path();
- return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: addr as usize, len: N, max: 0 }));
- };
+ let mem_addr = self.module.resolve_mem_addr(mem_addr);
+ let is_64bit = self.store.state.get_mem(mem_addr).is_64bit();
+ let effective_addr = Self::effective_addr::<N>(self.pop_mem_addr(is_64bit), offset)?;
+ let mem = self.store.state.get_mem_mut(mem_addr);
mem.store(effective_addr, val.len(), &val)?;
Ok(())
@@ -1267,10 +1306,9 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
impl<'store> Executor<'store, false> {
#[inline(always)]
pub(crate) fn run_to_completion(&mut self) -> Result<()> {
- // direct loop+match has worse codegen than a fixed for loop here for some reason (like ~5% worse)
// ideally we use `loop_match` / `become` once thats stabilized
loop {
- if self.exec::<1024>()?.is_some() {
+ if self.exec()?.is_some() {
return Ok(());
}
}
@@ -1286,8 +1324,10 @@ impl<'store> Executor<'store, false> {
}
loop {
- if self.exec::<1024>()?.is_some() {
- return Ok(ExecState::Completed);
+ for _ in 0..1024 {
+ if self.exec()?.is_some() {
+ return Ok(ExecState::Completed);
+ }
}
if start.elapsed() >= time_budget {
@@ -1306,8 +1346,10 @@ impl<'store> Executor<'store, true> {
}
loop {
- if self.exec::<1024>()?.is_some() {
- return Ok(ExecState::Completed);
+ for _ in 0..1024 {
+ if self.exec()?.is_some() {
+ return Ok(ExecState::Completed);
+ }
}
self.store.execution_fuel = self.store.execution_fuel.saturating_sub(1024_u32);
@@ -1333,3 +1375,19 @@ fn cmp_i32(lhs: i32, rhs: i32, op: CmpOp) -> bool {
CmpOp::GeU => (lhs as u32) >= (rhs as u32),
}
}
+
+#[inline(always)]
+fn cmp_i64(lhs: i64, rhs: i64, op: CmpOp) -> bool {
+ match op {
+ CmpOp::Eq => lhs == rhs,
+ CmpOp::Ne => lhs != rhs,
+ CmpOp::LtS => lhs < rhs,
+ CmpOp::LtU => (lhs as u64) < (rhs as u64),
+ CmpOp::GtS => lhs > rhs,
+ CmpOp::GtU => (lhs as u64) > (rhs as u64),
+ CmpOp::LeS => lhs <= rhs,
+ CmpOp::LeU => (lhs as u64) <= (rhs as u64),
+ CmpOp::GeS => lhs >= rhs,
+ CmpOp::GeU => (lhs as u64) >= (rhs as u64),
+ }
+}
diff --git a/crates/tinywasm/src/interpreter/simd/instructions.rs b/crates/tinywasm/src/interpreter/simd/instructions.rs
index 44e50df..ba519b5 100644
--- a/crates/tinywasm/src/interpreter/simd/instructions.rs
+++ b/crates/tinywasm/src/interpreter/simd/instructions.rs
@@ -189,11 +189,12 @@ impl Value128 {
}
#[doc(alias = "i8x16.shuffle")]
- pub fn i8x16_shuffle(a: Self, b: Self, idx: [u8; 16]) -> Self {
+ pub fn i8x16_shuffle(a: Self, b: Self, idx: i128) -> Self {
simd_impl! {
x86 => {
let a_bytes = a.to_le_bytes();
let b_bytes = b.to_le_bytes();
+ let idx = idx.to_le_bytes();
let mut mask_a = [0u8; 16];
let mut mask_b = [0u8; 16];
for i in 0..16 {
@@ -221,6 +222,7 @@ impl Value128 {
generic => {
let a_bytes = a.to_le_bytes();
let b_bytes = b.to_le_bytes();
+ let idx = idx.to_le_bytes();
let mut out = [0u8; 16];
for i in 0..16 {
let j = idx[i] & 31;
diff --git a/crates/tinywasm/src/interpreter/simd/mod.rs b/crates/tinywasm/src/interpreter/simd/mod.rs
index 4bf9d93..7320304 100644
--- a/crates/tinywasm/src/interpreter/simd/mod.rs
+++ b/crates/tinywasm/src/interpreter/simd/mod.rs
@@ -14,7 +14,7 @@ use core::arch::wasm64 as wasm;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
/// A 128-bit SIMD value
-pub struct Value128(i128);
+pub struct Value128(pub(crate) i128);
impl From<Value128> for i128 {
fn from(val: Value128) -> Self {
@@ -46,12 +46,12 @@ impl Value128 {
Self::from_le_bytes([ wasm::u8x16_extract_lane::<0>(value), wasm::u8x16_extract_lane::<1>(value), wasm::u8x16_extract_lane::<2>(value), wasm::u8x16_extract_lane::<3>(value), wasm::u8x16_extract_lane::<4>(value), wasm::u8x16_extract_lane::<5>(value), wasm::u8x16_extract_lane::<6>(value), wasm::u8x16_extract_lane::<7>(value), wasm::u8x16_extract_lane::<8>(value), wasm::u8x16_extract_lane::<9>(value), wasm::u8x16_extract_lane::<10>(value), wasm::u8x16_extract_lane::<11>(value), wasm::u8x16_extract_lane::<12>(value), wasm::u8x16_extract_lane::<13>(value), wasm::u8x16_extract_lane::<14>(value), wasm::u8x16_extract_lane::<15>(value)])
}
- #[inline]
+ #[inline(always)]
pub const fn from_le_bytes(bytes: [u8; 16]) -> Self {
Self(i128::from_le_bytes(bytes))
}
- #[inline]
+ #[inline(always)]
pub const fn to_le_bytes(self) -> [u8; 16] {
self.0.to_le_bytes()
}
diff --git a/crates/tinywasm/src/interpreter/simd/tests.rs b/crates/tinywasm/src/interpreter/simd/tests.rs
index 98a60cf..70ceac6 100644
--- a/crates/tinywasm/src/interpreter/simd/tests.rs
+++ b/crates/tinywasm/src/interpreter/simd/tests.rs
@@ -53,7 +53,9 @@ fn shuffle_matches_reference() {
*byte = (x & 0xff) as u8;
}
- let got = Value128::i8x16_shuffle(Value128::from_le_bytes(a), Value128::from_le_bytes(b), idx).to_le_bytes();
+ let got =
+ Value128::i8x16_shuffle(Value128::from_le_bytes(a), Value128::from_le_bytes(b), i128::from_le_bytes(idx))
+ .to_le_bytes();
let expected = ref_shuffle(a, b, idx);
assert_eq!(got, expected, "seed={seed}");
}
diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs
index fbf5ca5..40f162e 100644
--- a/crates/tinywasm/src/interpreter/stack/call_stack.rs
+++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs
@@ -1,4 +1,5 @@
-use crate::{Result, Trap, unlikely};
+use crate::{Result, Trap};
+use core::hint::cold_path;
use alloc::vec::Vec;
use tinywasm_types::{FuncAddr, ModuleInstanceAddr, ValueCounts};
@@ -24,9 +25,11 @@ impl CallStack {
#[inline(always)]
pub(crate) fn push(&mut self, call_frame: CallFrame) -> Result<()> {
- if unlikely(self.stack.len() == self.stack.capacity()) {
+ if self.stack.len() == self.stack.capacity() {
+ cold_path();
return Err(Trap::CallStackOverflow.into());
}
+
self.stack.push(call_frame);
Ok(())
}
diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs
index 8cfb097..696477a 100644
--- a/crates/types/src/instructions.rs
+++ b/crates/types/src/instructions.rs
@@ -83,9 +83,16 @@ pub enum Instruction {
SetLocalConst32(LocalAddr, i32), SetLocalConst64(LocalAddr, i64),
StoreLocalLocal32(MemoryArg, u8, u8),
StoreLocalLocal64(MemoryArg, u8, u8),
+ StoreLocalLocal128(MemoryArg, u8, u8),
LoadLocal32(MemoryArg, u8),
LoadLocalTee32(MemoryArg, u8, u8),
LoadLocalSet32(MemoryArg, u8, u8),
+ LoadLocalTee128(MemoryArg, u8, u8),
+ LoadLocalSet128(MemoryArg, u8, u8),
+ AndConstTee32(i32, LocalAddr),
+ SubConstTee32(i32, LocalAddr),
+ AndConstTee64(i64, LocalAddr),
+ SubConstTee64(i64, LocalAddr),
XorRotlConst64(i64),
XorRotlConstTee64(i64, LocalAddr),
@@ -96,8 +103,12 @@ pub enum Instruction {
Jump(u32),
JumpIfZero(u32),
JumpIfNonZero(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 },
+ JumpCmpLocalConst64 { target_ip: u32, local: LocalAddr, imm: i32, op: CmpOp },
JumpCmpLocalLocal32 { target_ip: u32, left: LocalAddr, right: LocalAddr, op: CmpOp },
+ JumpCmpLocalLocal64 { target_ip: u32, left: LocalAddr, right: LocalAddr, op: CmpOp },
DropKeep { base32: u16, keep32: u8, base64: u16, keep64: u8, base128: u16, keep128: u8 },
DropKeep32(u16, u16),
DropKeep64(u16, u16),