From 468a600d029d6aa45447c4346b11c9b9371050ca Mon Sep 17 00:00:00 2001 From: Henry Date: Fri, 17 Apr 2026 20:57:17 +0200 Subject: feat: refactor optimizer + executor, add v128 super instructions Signed-off-by: Henry --- Cargo.lock | 16 +- Cargo.toml | 6 +- crates/parser/src/conversion.rs | 3 +- crates/parser/src/error.rs | 3 +- crates/parser/src/lib.rs | 16 +- crates/parser/src/macros.rs | 220 ++++ crates/parser/src/module.rs | 7 +- crates/parser/src/optimize.rs | 611 +++------ crates/parser/src/visit.rs | 175 +-- crates/tinywasm/src/interpreter/executor.rs | 1352 ++++++++++---------- .../tinywasm/src/interpreter/simd/instructions.rs | 4 +- crates/tinywasm/src/interpreter/simd/mod.rs | 6 +- crates/tinywasm/src/interpreter/simd/tests.rs | 4 +- .../tinywasm/src/interpreter/stack/call_stack.rs | 7 +- crates/types/src/instructions.rs | 11 + 15 files changed, 1227 insertions(+), 1214 deletions(-) create mode 100644 crates/parser/src/macros.rs diff --git a/Cargo.lock b/Cargo.lock index d6cf03c..d12516a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -726,9 +726,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.246.2" +version = "0.247.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fb705ce81adde29d2a8e99d87995e39a6e927358c91398f374474746070ef7" +checksum = "30b6733b8b91d010a6ac5b0fb237dc46a19650bc4c67db66857e2e787d437204" dependencies = [ "leb128fmt", "wasmparser", @@ -746,9 +746,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.246.2" +version = "0.247.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71cde4757396defafd25417cfb36aa3161027d06d865b0c24baaae229aac005d" +checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80" dependencies = [ "bitflags", "indexmap", @@ -757,9 +757,9 @@ dependencies = [ [[package]] name = "wast" -version = "246.0.2" +version = "247.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3fe8e3bf88ad96d031b4181ddbd64634b17cb0d06dfc3de589ef43591a9a62" +checksum = "579d2d47eb33b0cdf9b14723cb115f1e1b7d6e77aac6f0816e5b7c7aeaa418ff" dependencies = [ "bumpalo", "leb128fmt", @@ -770,9 +770,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.246.2" +version = "1.247.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd7fda1199b94fff395c2d19a153f05dbe7807630316fa9673367666fd2ad8c" +checksum = "f3f4091c56437e86f2b57fa2fac72c4f528957a605b3f44f7c0b3b19a17ac5ee" dependencies = [ "wast", ] diff --git a/Cargo.toml b/Cargo.toml index ca977f9..499a0c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ default-members=[".", "crates/tinywasm", "crates/types", "crates/parser"] resolver="2" [workspace.dependencies] -wast="246" -wat="1.246" -wasmparser={version="0.246", default-features=false} +wast="247" +wat="1.247" +wasmparser={version="0.247", default-features=false} eyre="0.6" log="0.4" pretty_env_logger="0.5" 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 { + pub(crate) fn into_module(self, _options: &ParserOptions) -> Result { 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 { - 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, function_data: &mut WasmFunctionData, self_func_addr: u32, - options: &ParserOptions, ) -> Vec { 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, function_data: &mut WasmFunctionData) { +fn cmp_op(instr: Instruction) -> Option { + 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 { + 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, function_data: &mut WasmFunctionData) { let old_len = instructions.len(); if old_len == 0 { return; @@ -467,8 +268,12 @@ fn dce(instructions: &mut Vec, 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); -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( 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 { validator: FuncValidator, instructions: Vec, @@ -151,28 +88,6 @@ pub(crate) struct FunctionBuilder { errors: Vec, } -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 { 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) -> 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(&mut self) -> Result> { + fn exec(&mut self) -> Result> { 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::(), - Drop64 => self.store.stack.values.drop::(), - Drop128 => self.store.stack.values.drop::(), - Select32 => self.store.stack.values.select::()?, - Select64 => self.store.stack.values.select::()?, - Select128 => self.store.stack.values.select::()?, - SelectMulti(counts) => self.store.stack.values.select_multi(*counts), - Call(v) => { self.exec_call_direct::(*v)?; continue; } - CallSelf => { self.exec_call_self::()?; continue; } - CallIndirect(ty, table) => { self.exec_call_indirect::(*ty, *table)?; continue; } - ReturnCall(v) => { self.exec_call_direct::(*v)?; continue; } - ReturnCallSelf => { self.exec_call_self::()?; continue; } - ReturnCallIndirect(ty, table) => { self.exec_call_indirect::(*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::(&self.cf, *local_index))?, - LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *local_index))?, - LocalGet128(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::(&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::(&self.cf, *from)), - LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::(&self.cf, *from)), - LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::(&self.cf, *from)), - AddLocalLocal32(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::(&self.cf, *b)))?, - AddLocalLocal64(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::(&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::(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u32)), - AddLocalConst64(local_index, c) => self.store.stack.values.local_update::(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u64)), - SetLocalConst32(local_index, c) => self.store.stack.values.local_set::(&self.cf, *local_index, *c), - SetLocalConst64(local_index, c) => self.store.stack.values.local_set::(&self.cf, *local_index, *c), - StoreLocalLocal32(m, addr_local, value_local) => self.exec_store_local_local::(*m, *addr_local, *value_local, |v| v)?, - StoreLocalLocal64(m, addr_local, value_local) =>self.exec_store_local_local::(*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::(*global_index), - GlobalSet128(global_index) => self.exec_global_set::(*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)?, - - // 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)?, - - // Core memory load/store operations - I32Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, - I64Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, - F32Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, - F64Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, - I32Store8(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i8)?, - I32Store16(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i16)?, - I64Store8(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i8)?, - I64Store16(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i16)?, - I64Store32(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i32)?, - I32Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, - I64Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, - F32Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, - F64Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, - I32Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, - I32Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, - I32Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, - I32Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, - I64Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, - I64Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, - I64Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, - I64Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, - I64Load32S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, - I64Load32U(m) => self.exec_mem_load::(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), - - // 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), - - // 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::(arg.mem_addr(), arg.offset(), |v| v)?, - V128Load8x8S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?, - V128Load8x8U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?, - V128Load16x4S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?, - V128Load16x4U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?, - V128Load32x2S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?, - V128Load32x2U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?, - V128Load8Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i8)?, - V128Load16Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i16)?, - V128Load32Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i32)?, - V128Load64Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i64)?, - V128Store(arg) => self.exec_mem_store::(arg.mem_addr(), arg.offset(), |v| v)?, - V128Store8Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Store16Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Store32Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Store64Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load32Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?, - V128Load64Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?, - V128Const(arg) => self.exec_const::(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::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load16Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load32Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load64Lane(arg, lane) => self.exec_mem_load_lane::(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()), - }; + use tinywasm_types::Instruction::*; + #[rustfmt::skip] + match next { + Nop => {} + Unreachable => return Err(Trap::Unreachable.into()), + Drop32 => self.store.stack.values.drop::(), + Drop64 => self.store.stack.values.drop::(), + Drop128 => self.store.stack.values.drop::(), + Select32 => self.store.stack.values.select::()?, + Select64 => self.store.stack.values.select::()?, + Select128 => self.store.stack.values.select::()?, + SelectMulti(counts) => self.store.stack.values.select_multi(*counts), + Call(v) => { self.exec_call_direct::(*v)?; return Ok(None); } + CallSelf => { self.exec_call_self::()?; return Ok(None); } + CallIndirect(ty, table) => { self.exec_call_indirect::(*ty, *table)?; return Ok(None); } + ReturnCall(v) => { self.exec_call_direct::(*v)?; return Ok(None); } + ReturnCallSelf => { self.exec_call_self::()?; return Ok(None); } + ReturnCallIndirect(ty, table) => { self.exec_call_indirect::(*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::(&self.cf, *local_index))?, + LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *local_index))?, + LocalGet128(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::(&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::(&self.cf, *from)), + LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::(&self.cf, *from)), + LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::(&self.cf, *from)), + AddLocalLocal32(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::(&self.cf, *b)))?, + AddLocalLocal64(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::(&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::(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u32)), + AddLocalConst64(local_index, c) => self.store.stack.values.local_update::(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u64)), + SetLocalConst32(local_index, c) => self.store.stack.values.local_set::(&self.cf, *local_index, *c), + SetLocalConst64(local_index, c) => self.store.stack.values.local_set::(&self.cf, *local_index, *c), + StoreLocalLocal32(m, addr_local, value_local) => self.exec_store_local_local::(*m, *addr_local, *value_local)?, + StoreLocalLocal64(m, addr_local, value_local) => self.exec_store_local_local::(*m, *addr_local, *value_local)?, + StoreLocalLocal128(m, addr_local, value_local) => self.exec_store_local_local::(*m, *addr_local, *value_local)?, + LoadLocal32(m, addr_local) => self.exec_load_local::(*m, *addr_local)?, + LoadLocalTee32(m, addr_local, dst_local) => self.exec_load_local_tee::(*m, *addr_local, *dst_local)?, + LoadLocalSet32(m, addr_local, dst_local) => self.exec_load_local_set::(*m, *addr_local, *dst_local)?, + LoadLocalTee128(m, addr_local, dst_local) => self.exec_load_local_tee::(*m, *addr_local, *dst_local)?, + LoadLocalSet128(m, addr_local, dst_local) => self.exec_load_local_set::(*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::(*global_index), + GlobalSet128(global_index) => self.exec_global_set::(*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)?, + + // 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)?, + + // Core memory load/store operations + I32Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, + I64Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, + F32Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, + F64Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, + I32Store8(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i8)?, + I32Store16(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i16)?, + I64Store8(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i8)?, + I64Store16(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i16)?, + I64Store32(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v as i32)?, + I32Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, + I64Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, + F32Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, + F64Load(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), |v| v)?, + I32Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I32Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I32Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I32Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i32::from)?, + I64Load8S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load8U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load16S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load16U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load32S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, + I64Load32U(m) => self.exec_mem_load::(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), + + // 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), + + // 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::(arg.mem_addr(), arg.offset(), |v| v)?, + V128Load8x8S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?, + V128Load8x8U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?, + V128Load16x4S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?, + V128Load16x4U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?, + V128Load32x2S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?, + V128Load32x2U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?, + V128Load8Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i8)?, + V128Load16Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i16)?, + V128Load32Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i32)?, + V128Load64Splat(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::splat_i64)?, + V128Store(arg) => self.exec_mem_store::(arg.mem_addr(), arg.offset(), |v| v)?, + V128Store8Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Store16Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Store32Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Store64Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load32Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?, + V128Load64Zero(arg) => self.exec_mem_load::(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::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load16Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load32Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load64Lane(arg, lane) => self.exec_mem_load_lane::(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::() == 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::() == 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::() != 0 { - self.cf.instr_ptr = ip; - return true; - } - false + let condition = self.store.stack.values.pop::() != 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::(), 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::(), 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::(&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::(&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::(&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::(&self.cf, left); let rhs = self.store.stack.values.local_get::(&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::(&self.cf, left); + let rhs = self.store.stack.values.local_get::(&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(&self, memarg: MemoryArg, addr_local: u8) -> Result { let addr = u64::from(self.store.stack.values.local_get::(&self.cf, u16::from(addr_local))); - let Some(Ok(addr)) = memarg.offset().checked_add(addr).map(|a| a.try_into()) else { + Self::effective_addr::(addr, memarg.offset()) + } + + #[cfg(target_pointer_width = "64")] + fn effective_addr(addr: u64, offset: u64) -> Result { + 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(addr: u64, offset: u64) -> Result { + 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, const N: usize>( + fn pop_mem_addr(&mut self, is_64bit: bool) -> u64 { + if is_64bit { + self.store.stack.values.pop::() as u64 + } else { + self.store.stack.values.pop::() as u32 as u64 + } + } + + fn exec_store_local_local, 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::(memarg, addr_local)?; + let value = self.store.stack.values.local_get::(&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::(&self.cf, u16::from(addr_local))); - let value = cast(self.store.stack.values.local_get::(&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 { + fn exec_load_local_value, const N: usize>(&self, memarg: MemoryArg, addr_local: u8) -> Result { 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::(self.local_mem_addr::(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, const N: usize>( + &mut self, + memarg: MemoryArg, + addr_local: u8, + ) -> Result<()> { + let value = self.exec_load_local_value::(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, const N: usize>( + &mut self, + memarg: MemoryArg, + addr_local: u8, + dst_local: u8, + ) -> Result<()> { + let value = self.exec_load_local_value::(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, const N: usize>( + &mut self, + memarg: MemoryArg, + addr_local: u8, + dst_local: u8, + ) -> Result<()> { + let value = self.exec_load_local_value::(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(&mut self, global_index: u32) { - let val = self.store.stack.values.pop::().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::().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::(); - 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::().to_mem_bytes(); let val = self.store.stack.values.pop::() 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::(val, offset)?; let val = mem.load_as::(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::() as u64 } else { self.store.stack.values.pop::() 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::(base, offset)?; let val = mem.load_as::(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::().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::() as u64, - false => self.store.stack.values.pop::() 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::(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::(); - 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::() as u64, - false => u64::from(self.store.stack.values.pop::() 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::(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 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), -- cgit v1.3.1