summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenry <mail@henrygressmann.de>2026-04-03 19:45:12 +0200
committerHenry <mail@henrygressmann.de>2026-04-03 19:45:12 +0200
commit303abc7ddaab670299e300b39b0c8f24a0aed139 (patch)
treea60b30e6f3b52b2b17928c57761c9e897a4f8690
parent3ebed3310b2a548a8e9c13883ca03e1749547f78 (diff)
chore: refactor interpreter loop
Signed-off-by: Henry <mail@henrygressmann.de>
-rw-r--r--crates/tinywasm/benches/tinywasm_modes.rs16
-rw-r--r--crates/tinywasm/src/error.rs16
-rw-r--r--crates/tinywasm/src/interpreter/executor.rs1260
-rw-r--r--crates/tinywasm/src/interpreter/mod.rs2
-rw-r--r--crates/tinywasm/src/interpreter/num_helpers.rs25
-rw-r--r--crates/tinywasm/tests/host_func_signature_check.rs3
-rwxr-xr-xexamples/rust/build.sh2
7 files changed, 642 insertions, 682 deletions
diff --git a/crates/tinywasm/benches/tinywasm_modes.rs b/crates/tinywasm/benches/tinywasm_modes.rs
index bf6cbfc..edb472d 100644
--- a/crates/tinywasm/benches/tinywasm_modes.rs
+++ b/crates/tinywasm/benches/tinywasm_modes.rs
@@ -58,14 +58,6 @@ fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("tinywasm_modes");
group.measurement_time(BENCH_MEASUREMENT_TIME);
- group.bench_function("call", |b| {
- b.iter_batched_ref(
- || setup_typed_func(module.clone(), None).expect("setup call"),
- |(store, func)| run_call(store, func).expect("run call"),
- BatchSize::LargeInput,
- )
- });
-
let per_instruction_engine = Engine::new(Config::new().fuel_policy(FuelPolicy::PerInstruction));
group.bench_function("resume_fuel_per_instruction", |b| {
b.iter_batched_ref(
@@ -95,6 +87,14 @@ fn criterion_benchmark(c: &mut Criterion) {
)
});
+ group.bench_function("call", |b| {
+ b.iter_batched_ref(
+ || setup_typed_func(module.clone(), None).expect("setup call"),
+ |(store, func)| run_call(store, func).expect("run call"),
+ BatchSize::LargeInput,
+ )
+ });
+
group.finish();
}
diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs
index ade5089..591ba99 100644
--- a/crates/tinywasm/src/error.rs
+++ b/crates/tinywasm/src/error.rs
@@ -1,7 +1,7 @@
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::fmt::Debug;
-use core::{fmt::Display, ops::ControlFlow};
+use core::fmt::Display;
use tinywasm_types::FuncType;
use tinywasm_types::archive::TwasmError;
@@ -274,17 +274,3 @@ impl From<tinywasm_parser::ParseError> for Error {
/// A wrapper around [`core::result::Result`] for tinywasm operations
pub type Result<T, E = Error> = crate::std::result::Result<T, E>;
-
-pub(crate) trait Controlify<T> {
- fn to_cf(self) -> ControlFlow<Option<Error>, T>;
-}
-
-impl<T> Controlify<T> for Result<T, Error> {
- #[inline(always)]
- fn to_cf(self) -> ControlFlow<Option<Error>, T> {
- match self {
- Ok(value) => ControlFlow::Continue(value),
- Err(err) => ControlFlow::Break(Some(err)),
- }
- }
-}
diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs
index 2e2bd0c..76464cb 100644
--- a/crates/tinywasm/src/interpreter/executor.rs
+++ b/crates/tinywasm/src/interpreter/executor.rs
@@ -4,7 +4,6 @@ use super::no_std_floats::NoStdFloatExt;
use alloc::boxed::Box;
use alloc::{rc::Rc, string::ToString};
-use core::ops::ControlFlow;
use interpreter::stack::CallFrame;
use tinywasm_types::*;
@@ -17,9 +16,9 @@ use crate::instance::ModuleInstanceInner;
use crate::interpreter::Value128;
use crate::*;
-const FUEL_ACCOUNTING_INTERVAL: u32 = 1024;
#[cfg(feature = "std")]
-const TIME_BUDGET_CHECK_INTERVAL: u32 = 2048;
+const TIME_BUDGET_CHECK_INTERVAL: usize = 2048;
+const FUEL_ACCOUNTING_INTERVAL: usize = 1024;
const FUEL_COST_CALL_TOTAL: u32 = 5;
pub(crate) struct Executor<'store, const BUDGETED: bool> {
@@ -49,20 +48,21 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
#[inline(always)]
- fn exec_next(&mut self) -> ControlFlow<Option<Error>> {
- use tinywasm_types::Instruction::*;
+ fn exec<const ITERATIONS: usize>(&mut self) -> Result<Option<()>> {
+ for _ in 0..ITERATIONS {
+ use tinywasm_types::Instruction::*;
- macro_rules! stack_op {
+ macro_rules! stack_op {
(simd_unary $method:ident) => { stack_op!(unary Value128, |v| v.$method()) };
(simd_binary $method:ident) => { stack_op!(binary Value128, |a, b| a.$method(b)) };
- (unary $ty:ty, |$v:ident| $expr:expr) => { self.store.stack.values.unary::<$ty>(|$v| Ok($expr)).to_cf()? };
- (binary $ty:ty, |$a:ident, $b:ident| $expr:expr) => { self.store.stack.values.binary::<$ty>(|$a, $b| Ok($expr)).to_cf()? };
- (binary try $ty:ty, |$a:ident, $b:ident| $expr:expr) => { self.store.stack.values.binary::<$ty>(|$a, $b| $expr).to_cf()? };
- (unary $from:ty => $to:ty, |$v:ident| $expr:expr) => { self.store.stack.values.unary_into::<$from, $to>(|$v| Ok($expr)).to_cf()? };
- (binary $from:ty => $to:ty, |$a:ident, $b:ident| $expr:expr) => { self.store.stack.values.binary_into::<$from, $to>(|$a, $b| Ok($expr)).to_cf()? };
+ (unary $ty:ty, |$v:ident| $expr:expr) => { self.store.stack.values.unary::<$ty>(|$v| Ok($expr))? };
+ (binary $ty:ty, |$a:ident, $b:ident| $expr:expr) => { self.store.stack.values.binary::<$ty>(|$a, $b| Ok($expr))? };
+ (binary try $ty:ty, |$a:ident, $b:ident| $expr:expr) => { self.store.stack.values.binary::<$ty>(|$a, $b| $expr)? };
+ (unary $from:ty => $to:ty, |$v:ident| $expr:expr) => { self.store.stack.values.unary_into::<$from, $to>(|$v| Ok($expr))? };
+ (binary $from:ty => $to:ty, |$a:ident, $b:ident| $expr:expr) => { self.store.stack.values.binary_into::<$from, $to>(|$a, $b| Ok($expr))? };
(binary $a:ty, $b:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { stack_op!(binary $a, $b => $b, |$lhs, $rhs| $expr) };
- (binary $a:ty, $b:ty => $res:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { self.store.stack.values.binary_mixed::<$a, $b, $res>(|$lhs, $rhs| Ok($expr)).to_cf()? };
- (ternary $ty:ty, |$a:ident, $b:ident, $c:ident| $expr:expr) => { self.store.stack.values.ternary::<$ty>(|$a, $b, $c| Ok($expr)).to_cf()? };
+ (binary $a:ty, $b:ty => $res:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { self.store.stack.values.binary_mixed::<$a, $b, $res>(|$lhs, $rhs| Ok($expr))? };
+ (ternary $ty:ty, |$a:ident, $b:ident, $c:ident| $expr:expr) => { self.store.stack.values.ternary::<$ty>(|$a, $b, $c| Ok($expr))? };
(local_set_pop $ty:ty, $local_index:expr) => {{
let val = self.store.stack.values.pop::<$ty>();
self.store.stack.values.local_set(&self.cf, *$local_index, val);
@@ -73,567 +73,568 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}};
}
- let next = match self.func.instructions.0.get(self.cf.instr_ptr as usize) {
- Some(instr) => instr,
- None => 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 => unreachable!(
+ "Instruction pointer out of bounds: {} ({} instructions)",
+ self.cf.instr_ptr,
+ self.func.instructions.0.len()
+ ),
+ };
- #[rustfmt::skip]
- match next {
- Nop | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {}
- Unreachable => return ControlFlow::Break(Some(Trap::Unreachable.into())),
- Drop32 => self.store.stack.values.drop::<Value32>(),
- Drop64 => self.store.stack.values.drop::<Value64>(),
- Drop128 => self.store.stack.values.drop::<Value128>(),
- DropRef => self.store.stack.values.drop::<ValueRef>(),
- Select32 => self.store.stack.values.select::<Value32>().to_cf()?,
- Select64 => self.store.stack.values.select::<Value64>().to_cf()?,
- Select128 => self.store.stack.values.select::<Value128>().to_cf()?,
- SelectRef => self.store.stack.values.select::<ValueRef>().to_cf()?,
- SelectMulti(counts) => self.store.stack.values.select_multi(*counts),
- Call(v) => return self.exec_call_direct::<false>(*v),
- CallSelf => return self.exec_call_self::<false>(),
- CallIndirect(ty, table) => return self.exec_call_indirect::<false>(*ty, *table),
- ReturnCall(v) => return self.exec_call_direct::<true>(*v),
- ReturnCallSelf => return self.exec_call_self::<true>(),
- ReturnCallIndirect(ty, table) => return self.exec_call_indirect::<true>(*ty, *table),
- Jump(ip) => return self.exec_jump(*ip),
- JumpIfZero(ip) => if self.exec_jump_if_zero(*ip) { return ControlFlow::Continue(()); },
- DropKeepSmall { base32, keep32, base64, keep64, base128, keep128, base_ref, keep_ref } => {
- let b32 = self.cf.stack_base().s32 + *base32 as u32;
- let k32 = *keep32 as usize;
- self.store.stack.values.stack_32.truncate_keep(b32 as usize, k32);
- let b64 = self.cf.stack_base().s64 + *base64 as u32;
- let k64 = *keep64 as usize;
- self.store.stack.values.stack_64.truncate_keep(b64 as usize, k64);
- let b128 = self.cf.stack_base().s128 + *base128 as u32;
- let k128 = *keep128 as usize;
- self.store.stack.values.stack_128.truncate_keep(b128 as usize, k128);
- let bref = self.cf.stack_base().sref + *base_ref as u32;
- let kref = *keep_ref as usize;
- self.store.stack.values.stack_ref.truncate_keep(bref as usize, kref);
- }
- DropKeep32(base, keep) => {
- let b = self.cf.stack_base().s32 + *base as u32;
- let k = *keep as usize;
- self.store.stack.values.stack_32.truncate_keep(b as usize, k);
- }
- DropKeep64(base, keep) => {
- let b = self.cf.stack_base().s64 + *base as u32;
- let k = *keep as usize;
- self.store.stack.values.stack_64.truncate_keep(b as usize, k);
- }
- DropKeep128(base, keep) => {
- let b = self.cf.stack_base().s128 + *base as u32;
- let k = *keep as usize;
- self.store.stack.values.stack_128.truncate_keep(b as usize, k);
- }
- DropKeepRef(base, keep) => {
- let b = self.cf.stack_base().sref + *base as u32;
- let k = *keep as usize;
- self.store.stack.values.stack_ref.truncate_keep(b as usize, k);
- }
- BranchTable(default_ip, len) => return self.exec_branch_table(*default_ip, *len),
- BranchTableTarget {..} => {},
- Return => return self.exec_return(),
- LocalGet32(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value32>(&self.cf, *local_index)).to_cf()?,
- LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value64>(&self.cf, *local_index)).to_cf()?,
- LocalGet128(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value128>(&self.cf, *local_index)).to_cf()?,
- LocalGetRef(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<ValueRef>(&self.cf, *local_index)).to_cf()?,
- 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),
- LocalSetRef(local_index) => stack_op!(local_set_pop ValueRef, local_index),
- LocalCopy32(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value32>(&self.cf, *from)),
- LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value64>(&self.cf, *from)),
- LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value128>(&self.cf, *from)),
- LocalCopyRef(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<ValueRef>(&self.cf, *from)),
- I32AddLocals(a, b) => self.store.stack.values.push(
- self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)),
- ).to_cf()?,
- I64AddLocals(a, b) => self.store.stack.values.push(
- self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i64>(&self.cf, *b)),
- ).to_cf()?,
- I32AddConst(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)),
- I64AddConst(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)),
- I32StoreLocalLocal(m, addr_local, value_local) => {
- let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(m.mem_addr()));
- let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, *addr_local));
- let value = self.store.stack.values.local_get::<u32>(&self.cf, *value_local).to_mem_bytes();
- if let Err(e) = mem.store((m.offset() + addr) as usize, value.len(), &value) {
- return ControlFlow::Break(Some(e));
+ #[rustfmt::skip]
+ match next {
+ Nop | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {}
+ Unreachable => return Err(Trap::Unreachable.into()),
+ Drop32 => self.store.stack.values.drop::<Value32>(),
+ Drop64 => self.store.stack.values.drop::<Value64>(),
+ Drop128 => self.store.stack.values.drop::<Value128>(),
+ DropRef => self.store.stack.values.drop::<ValueRef>(),
+ Select32 => self.store.stack.values.select::<Value32>()?,
+ Select64 => self.store.stack.values.select::<Value64>()?,
+ Select128 => self.store.stack.values.select::<Value128>()?,
+ SelectRef => self.store.stack.values.select::<ValueRef>()?,
+ SelectMulti(counts) => self.store.stack.values.select_multi(*counts),
+ Call(v) => { self.exec_call_direct::<false>(*v)?; continue; }
+ CallSelf => { self.exec_call_self::<false>()?; continue; }
+ CallIndirect(ty, table) => { self.exec_call_indirect::<false>(*ty, *table)?; continue; }
+ ReturnCall(v) => { self.exec_call_direct::<true>(*v)?; continue; }
+ ReturnCallSelf => { self.exec_call_self::<true>()?; continue; }
+ ReturnCallIndirect(ty, table) => { self.exec_call_indirect::<true>(*ty, *table)?; continue; }
+ Jump(ip) => { self.exec_jump(*ip); continue; }
+ JumpIfZero(ip) => if self.exec_jump_if_zero(*ip) { continue; },
+ DropKeepSmall { base32, keep32, base64, keep64, base128, keep128, base_ref, keep_ref } => {
+ let b32 = self.cf.stack_base().s32 + *base32 as u32;
+ let k32 = *keep32 as usize;
+ self.store.stack.values.stack_32.truncate_keep(b32 as usize, k32);
+ let b64 = self.cf.stack_base().s64 + *base64 as u32;
+ let k64 = *keep64 as usize;
+ self.store.stack.values.stack_64.truncate_keep(b64 as usize, k64);
+ let b128 = self.cf.stack_base().s128 + *base128 as u32;
+ let k128 = *keep128 as usize;
+ self.store.stack.values.stack_128.truncate_keep(b128 as usize, k128);
+ let bref = self.cf.stack_base().sref + *base_ref as u32;
+ let kref = *keep_ref as usize;
+ self.store.stack.values.stack_ref.truncate_keep(bref as usize, kref);
}
- }
- I32LoadLocalTee(m, addr_local, dst_local) => {
- let mem = self.store.state.get_mem(self.module.resolve_mem_addr(m.mem_addr()));
- let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, *addr_local));
- let Some(Ok(addr)) = m.offset().checked_add(addr).map(|a| a.try_into()) else {
- return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds {
- offset: addr as usize,
- len: 4,
- max: 0,
- })));
- };
- let value = mem.load_as::<4, i32>(addr).to_cf()?;
- self.store.stack.values.local_set(&self.cf, *dst_local, value);
- self.store.stack.values.push(value).to_cf()?;
- }
- I64XorRotlConst(c) => stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)),
- I64XorRotlConstTee(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),
- LocalTeeRef(local_index) => stack_op!(local_tee ValueRef, local_index),
- GlobalGet(global_index) => self.exec_global_get(*global_index).to_cf()?,
- GlobalSet32(global_index) => self.exec_global_set::<Value32>(*global_index),
- GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index),
- GlobalSet128(global_index) => self.exec_global_set::<Value128>(*global_index),
- GlobalSetRef(global_index) => self.exec_global_set::<ValueRef>(*global_index),
- I32Const(val) => self.exec_const(*val).to_cf()?,
- I64Const(val) => self.exec_const(*val).to_cf()?,
- F32Const(val) => self.exec_const(*val).to_cf()?,
- F64Const(val) => self.exec_const(*val).to_cf()?,
- 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)),
- 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())),
+ DropKeep32(base, keep) => {
+ let b = self.cf.stack_base().s32 + *base as u32;
+ let k = *keep as usize;
+ self.store.stack.values.stack_32.truncate_keep(b as usize, k);
+ }
+ DropKeep64(base, keep) => {
+ let b = self.cf.stack_base().s64 + *base as u32;
+ let k = *keep as usize;
+ self.store.stack.values.stack_64.truncate_keep(b as usize, k);
+ }
+ DropKeep128(base, keep) => {
+ let b = self.cf.stack_base().s128 + *base as u32;
+ let k = *keep as usize;
+ self.store.stack.values.stack_128.truncate_keep(b as usize, k);
+ }
+ DropKeepRef(base, keep) => {
+ let b = self.cf.stack_base().sref + *base as u32;
+ let k = *keep as usize;
+ self.store.stack.values.stack_ref.truncate_keep(b as usize, k);
+ }
+ BranchTable(default_ip, len) => { self.exec_branch_table(*default_ip, *len); continue; }
+ BranchTableTarget {..} => {},
+ Return => { if self.exec_return() { return Ok(Some(())); } continue; }
+ LocalGet32(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value32>(&self.cf, *local_index))?,
+ LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value64>(&self.cf, *local_index))?,
+ LocalGet128(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<Value128>(&self.cf, *local_index))?,
+ LocalGetRef(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::<ValueRef>(&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),
+ LocalSetRef(local_index) => stack_op!(local_set_pop ValueRef, local_index),
+ LocalCopy32(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value32>(&self.cf, *from)),
+ LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value64>(&self.cf, *from)),
+ LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value128>(&self.cf, *from)),
+ LocalCopyRef(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<ValueRef>(&self.cf, *from)),
+ I32AddLocals(a, b) => self.store.stack.values.push(
+ self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)),
+ )?,
+ I64AddLocals(a, b) => self.store.stack.values.push(
+ self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i64>(&self.cf, *b)),
+ )?,
+ I32AddConst(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)),
+ I64AddConst(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)),
+ I32StoreLocalLocal(m, addr_local, value_local) => {
+ let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(m.mem_addr()));
+ let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, *addr_local));
+ let value = self.store.stack.values.local_get::<u32>(&self.cf, *value_local).to_mem_bytes();
+ if let Err(e) = mem.store((m.offset() + addr) as usize, value.len(), &value) {
+ return Err(e);
+ }
+ }
+ I32LoadLocalTee(m, addr_local, dst_local) => {
+ let mem = self.store.state.get_mem(self.module.resolve_mem_addr(m.mem_addr()));
+ let addr = u64::from(self.store.stack.values.local_get::<u32>(&self.cf, *addr_local));
+ let Some(Ok(addr)) = m.offset().checked_add(addr).map(|a| a.try_into()) else {
+ return Err(Error::Trap(Trap::MemoryOutOfBounds {
+ offset: addr as usize,
+ len: 4,
+ max: 0,
+ }));
+ };
+ let value = mem.load_as::<4, i32>(addr)?;
+ self.store.stack.values.local_set(&self.cf, *dst_local, value);
+ self.store.stack.values.push(value)?;
+ }
+ I64XorRotlConst(c) => stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)),
+ I64XorRotlConstTee(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),
+ LocalTeeRef(local_index) => stack_op!(local_tee ValueRef, local_index),
+ GlobalGet(global_index) => self.exec_global_get(*global_index)?,
+ GlobalSet32(global_index) => self.exec_global_set::<Value32>(*global_index),
+ GlobalSet64(global_index) => self.exec_global_set::<Value64>(*global_index),
+ GlobalSet128(global_index) => self.exec_global_set::<Value128>(*global_index),
+ GlobalSetRef(global_index) => self.exec_global_set::<ValueRef>(*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)),
+ 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>(Some(*func_idx)).to_cf()?,
- RefNull(_) => self.exec_const::<ValueRef>(None).to_cf()?,
- RefIsNull => self.exec_ref_is_null().to_cf()?,
- MemorySize(addr) => self.exec_memory_size(*addr).to_cf()?,
- MemoryGrow(addr) => self.exec_memory_grow(*addr).to_cf()?,
+ // Reference types
+ RefFunc(func_idx) => self.exec_const::<ValueRef>(Some(*func_idx))?,
+ RefNull(_) => self.exec_const::<ValueRef>(None)?,
+ RefIsNull => self.exec_ref_is_null()?,
+ MemorySize(addr) => self.exec_memory_size(*addr)?,
+ MemoryGrow(addr) => self.exec_memory_grow(*addr)?,
- // Bulk memory operations
- MemoryCopy(from, to) => self.exec_memory_copy(*from, *to).to_cf()?,
- MemoryFill(addr) => self.exec_memory_fill(*addr).to_cf()?,
- MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx).to_cf()?,
- DataDrop(data_index) => self.store.state.get_data_mut(self.module.resolve_data_addr(*data_index)).drop(),
- ElemDrop(elem_index) => self.store.state.get_elem_mut(self.module.resolve_elem_addr(*elem_index)).drop(),
+ // Bulk memory operations
+ MemoryCopy(from, to) => self.exec_memory_copy(*from, *to)?,
+ MemoryFill(addr) => self.exec_memory_fill(*addr)?,
+ 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).to_cf()?,
- TableSet(table_idx) => self.exec_table_set(*table_idx).to_cf()?,
- TableSize(table_idx) => self.exec_table_size(*table_idx).to_cf()?,
- TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx).to_cf()?,
- TableGrow(table_idx) => self.exec_table_grow(*table_idx).to_cf()?,
- TableFill(table_idx) => self.exec_table_fill(*table_idx).to_cf()?,
- TableCopy { from, to } => self.exec_table_copy(*from, *to).to_cf()?,
+ // 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 { from, to } => self.exec_table_copy(*from, *to)?,
- // Core memory load/store operations
- I32Store(m) => self.exec_mem_store::<i32, i32, 4>(m.mem_addr(), m.offset(), |v| v)?,
- I64Store(m) => self.exec_mem_store::<i64, i64, 8>(m.mem_addr(), m.offset(), |v| v)?,
- F32Store(m) => self.exec_mem_store::<f32, f32, 4>(m.mem_addr(), m.offset(), |v| v)?,
- F64Store(m) => self.exec_mem_store::<f64, f64, 8>(m.mem_addr(), m.offset(), |v| v)?,
- I32Store8(m) => self.exec_mem_store::<i32, i8, 1>(m.mem_addr(), m.offset(), |v| v as i8)?,
- I32Store16(m) => self.exec_mem_store::<i32, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?,
- I64Store8(m) => self.exec_mem_store::<i64, i8, 1>(m.mem_addr(), m.offset(), |v| v as i8)?,
- I64Store16(m) => self.exec_mem_store::<i64, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?,
- I64Store32(m) => self.exec_mem_store::<i64, i32, 4>(m.mem_addr(), m.offset(), |v| v as i32)?,
- I32Load(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
- I64Load(m) => self.exec_mem_load::<i64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
- F32Load(m) => self.exec_mem_load::<f32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
- F64Load(m) => self.exec_mem_load::<f64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
- I32Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
- I32Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
- I32Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
- I32Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
- I64Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load32S(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
- I64Load32U(m) => self.exec_mem_load::<u32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
+ // Core memory load/store operations
+ I32Store(m) => self.exec_mem_store::<i32, i32, 4>(m.mem_addr(), m.offset(), |v| v)?,
+ I64Store(m) => self.exec_mem_store::<i64, i64, 8>(m.mem_addr(), m.offset(), |v| v)?,
+ F32Store(m) => self.exec_mem_store::<f32, f32, 4>(m.mem_addr(), m.offset(), |v| v)?,
+ F64Store(m) => self.exec_mem_store::<f64, f64, 8>(m.mem_addr(), m.offset(), |v| v)?,
+ I32Store8(m) => self.exec_mem_store::<i32, i8, 1>(m.mem_addr(), m.offset(), |v| v as i8)?,
+ I32Store16(m) => self.exec_mem_store::<i32, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?,
+ I64Store8(m) => self.exec_mem_store::<i64, i8, 1>(m.mem_addr(), m.offset(), |v| v as i8)?,
+ I64Store16(m) => self.exec_mem_store::<i64, i16, 2>(m.mem_addr(), m.offset(), |v| v as i16)?,
+ I64Store32(m) => self.exec_mem_store::<i64, i32, 4>(m.mem_addr(), m.offset(), |v| v as i32)?,
+ I32Load(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
+ I64Load(m) => self.exec_mem_load::<i64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
+ F32Load(m) => self.exec_mem_load::<f32, 4, _>(m.mem_addr(), m.offset(), |v| v)?,
+ F64Load(m) => self.exec_mem_load::<f64, 8, _>(m.mem_addr(), m.offset(), |v| v)?,
+ I32Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I32Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I32Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I32Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i32::from)?,
+ I64Load8S(m) => self.exec_mem_load::<i8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load8U(m) => self.exec_mem_load::<u8, 1, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load16S(m) => self.exec_mem_load::<i16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load16U(m) => self.exec_mem_load::<u16, 2, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load32S(m) => self.exec_mem_load::<i32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
+ I64Load32U(m) => self.exec_mem_load::<u32, 4, _>(m.mem_addr(), m.offset(), i64::from)?,
- // Numeric conversion operations
- F32ConvertI32S => stack_op!(unary i32 => f32, |v| v as f32),
- F32ConvertI64S => stack_op!(unary i64 => f32, |v| v as f32),
- F64ConvertI32S => stack_op!(unary i32 => f64, |v| f64::from(v)),
- F64ConvertI64S => stack_op!(unary i64 => f64, |v| v as f64),
- F32ConvertI32U => stack_op!(unary u32 => f32, |v| v as f32),
- F32ConvertI64U => stack_op!(unary u64 => f32, |v| v as f32),
- F64ConvertI32U => stack_op!(unary u32 => f64, |v| f64::from(v)),
- F64ConvertI64U => stack_op!(unary u64 => f64, |v| v as f64),
+ // Numeric conversion operations
+ F32ConvertI32S => stack_op!(unary i32 => f32, |v| v as f32),
+ F32ConvertI64S => stack_op!(unary i64 => f32, |v| v as f32),
+ F64ConvertI32S => stack_op!(unary i32 => f64, |v| f64::from(v)),
+ F64ConvertI64S => stack_op!(unary i64 => f64, |v| v as f64),
+ F32ConvertI32U => stack_op!(unary u32 => f32, |v| v as f32),
+ F32ConvertI64U => stack_op!(unary u64 => f32, |v| v as f32),
+ F64ConvertI32U => stack_op!(unary u32 => f64, |v| f64::from(v)),
+ F64ConvertI64U => stack_op!(unary u64 => f64, |v| v as f64),
- // Sign-extension operations
- I32Extend8S => stack_op!(unary i32, |v| i32::from(v as i8)),
- I32Extend16S => stack_op!(unary i32, |v| i32::from(v as i16)),
- I64Extend8S => stack_op!(unary i64, |v| i64::from(v as i8)),
- I64Extend16S => stack_op!(unary i64, |v| i64::from(v as i16)),
- I64Extend32S => stack_op!(unary i64, |v| i64::from(v as i32)),
- I64ExtendI32U => stack_op!(unary u32 => i64, |v| i64::from(v)),
- I64ExtendI32S => stack_op!(unary i32 => i64, |v| i64::from(v)),
- I32WrapI64 => stack_op!(unary i64 => i32, |v| v as i32),
- F32DemoteF64 => stack_op!(unary f64 => f32, |v| v as f32),
- F64PromoteF32 => stack_op!(unary f32 => f64, |v| f64::from(v)),
- F32Abs => stack_op!(unary f32, |v| v.abs()),
- F64Abs => stack_op!(unary f64, |v| v.abs()),
- F32Neg => stack_op!(unary f32, |v| -v),
- F64Neg => stack_op!(unary f64, |v| -v),
- F32Ceil => stack_op!(unary f32, |v| v.ceil()),
- F64Ceil => stack_op!(unary f64, |v| v.ceil()),
- F32Floor => stack_op!(unary f32, |v| v.floor()),
- F64Floor => stack_op!(unary f64, |v| v.floor()),
- F32Trunc => stack_op!(unary f32, |v| v.trunc()),
- F64Trunc => stack_op!(unary f64, |v| v.trunc()),
- F32Nearest => stack_op!(unary f32, |v| v.tw_nearest()),
- F64Nearest => stack_op!(unary f64, |v| v.tw_nearest()),
- F32Sqrt => stack_op!(unary f32, |v| v.sqrt()),
- F64Sqrt => stack_op!(unary f64, |v| v.sqrt()),
- F32Min => stack_op!(binary f32, |a, b| a.tw_minimum(b)),
- F64Min => stack_op!(binary f64, |a, b| a.tw_minimum(b)),
- F32Max => stack_op!(binary f32, |a, b| a.tw_maximum(b)),
- F64Max => stack_op!(binary f64, |a, b| a.tw_maximum(b)),
- F32Copysign => stack_op!(binary f32, |a, b| a.copysign(b)),
- F64Copysign => stack_op!(binary f64, |a, b| a.copysign(b)),
- I32TruncF32S => checked_conv_float!(f32, i32, self),
- I32TruncF64S => checked_conv_float!(f64, i32, self),
- I32TruncF32U => checked_conv_float!(f32, u32, i32, self),
- I32TruncF64U => checked_conv_float!(f64, u32, i32, self),
- I64TruncF32S => checked_conv_float!(f32, i64, self),
- I64TruncF64S => checked_conv_float!(f64, i64, self),
- I64TruncF32U => checked_conv_float!(f32, u64, i64, self),
- I64TruncF64U => checked_conv_float!(f64, u64, i64, self),
+ // Sign-extension operations
+ I32Extend8S => stack_op!(unary i32, |v| i32::from(v as i8)),
+ I32Extend16S => stack_op!(unary i32, |v| i32::from(v as i16)),
+ I64Extend8S => stack_op!(unary i64, |v| i64::from(v as i8)),
+ I64Extend16S => stack_op!(unary i64, |v| i64::from(v as i16)),
+ I64Extend32S => stack_op!(unary i64, |v| i64::from(v as i32)),
+ I64ExtendI32U => stack_op!(unary u32 => i64, |v| i64::from(v)),
+ I64ExtendI32S => stack_op!(unary i32 => i64, |v| i64::from(v)),
+ I32WrapI64 => stack_op!(unary i64 => i32, |v| v as i32),
+ F32DemoteF64 => stack_op!(unary f64 => f32, |v| v as f32),
+ F64PromoteF32 => stack_op!(unary f32 => f64, |v| f64::from(v)),
+ F32Abs => stack_op!(unary f32, |v| v.abs()),
+ F64Abs => stack_op!(unary f64, |v| v.abs()),
+ F32Neg => stack_op!(unary f32, |v| -v),
+ F64Neg => stack_op!(unary f64, |v| -v),
+ F32Ceil => stack_op!(unary f32, |v| v.ceil()),
+ F64Ceil => stack_op!(unary f64, |v| v.ceil()),
+ F32Floor => stack_op!(unary f32, |v| v.floor()),
+ F64Floor => stack_op!(unary f64, |v| v.floor()),
+ F32Trunc => stack_op!(unary f32, |v| v.trunc()),
+ F64Trunc => stack_op!(unary f64, |v| v.trunc()),
+ F32Nearest => stack_op!(unary f32, |v| v.tw_nearest()),
+ F64Nearest => stack_op!(unary f64, |v| v.tw_nearest()),
+ F32Sqrt => stack_op!(unary f32, |v| v.sqrt()),
+ F64Sqrt => stack_op!(unary f64, |v| v.sqrt()),
+ F32Min => stack_op!(binary f32, |a, b| a.tw_minimum(b)),
+ F64Min => stack_op!(binary f64, |a, b| a.tw_minimum(b)),
+ F32Max => stack_op!(binary f32, |a, b| a.tw_maximum(b)),
+ F64Max => stack_op!(binary f64, |a, b| a.tw_maximum(b)),
+ F32Copysign => stack_op!(binary f32, |a, b| a.copysign(b)),
+ F64Copysign => stack_op!(binary f64, |a, b| a.copysign(b)),
+ I32TruncF32S => checked_conv_float!(f32, i32, self),
+ I32TruncF64S => checked_conv_float!(f64, i32, self),
+ I32TruncF32U => checked_conv_float!(f32, u32, i32, self),
+ I32TruncF64U => checked_conv_float!(f64, u32, i32, self),
+ I64TruncF32S => checked_conv_float!(f32, i64, self),
+ I64TruncF64S => checked_conv_float!(f64, i64, self),
+ I64TruncF32U => checked_conv_float!(f32, u64, i64, self),
+ I64TruncF64U => checked_conv_float!(f64, u64, i64, self),
- // Non-trapping float-to-int conversions
- I32TruncSatF32S => stack_op!(unary f32 => i32, |v| v.trunc() as i32),
- I32TruncSatF32U => stack_op!(unary f32 => u32, |v| v.trunc() as u32),
- I32TruncSatF64S => stack_op!(unary f64 => i32, |v| v.trunc() as i32),
- I32TruncSatF64U => stack_op!(unary f64 => u32, |v| v.trunc() as u32),
- I64TruncSatF32S => stack_op!(unary f32 => i64, |v| v.trunc() as i64),
- I64TruncSatF32U => stack_op!(unary f32 => u64, |v| v.trunc() as u64),
- I64TruncSatF64S => stack_op!(unary f64 => i64, |v| v.trunc() as i64),
- I64TruncSatF64U => stack_op!(unary f64 => u64, |v| v.trunc() as u64),
+ // Non-trapping float-to-int conversions
+ I32TruncSatF32S => stack_op!(unary f32 => i32, |v| v.trunc() as i32),
+ I32TruncSatF32U => stack_op!(unary f32 => u32, |v| v.trunc() as u32),
+ I32TruncSatF64S => stack_op!(unary f64 => i32, |v| v.trunc() as i32),
+ I32TruncSatF64U => stack_op!(unary f64 => u32, |v| v.trunc() as u32),
+ I64TruncSatF32S => stack_op!(unary f32 => i64, |v| v.trunc() as i64),
+ I64TruncSatF32U => stack_op!(unary f32 => u64, |v| v.trunc() as u64),
+ I64TruncSatF64S => stack_op!(unary f64 => i64, |v| v.trunc() as i64),
+ I64TruncSatF64U => stack_op!(unary f64 => u64, |v| v.trunc() as u64),
- // SIMD extension
- V128Not => stack_op!(unary Value128, |v| v.v128_not()),
- V128And => stack_op!(binary Value128, |a, b| a.v128_and(b)),
- V128AndNot => stack_op!(binary Value128, |a, b| a.v128_andnot(b)),
- V128Or => stack_op!(binary Value128, |a, b| a.v128_or(b)),
- V128Xor => stack_op!(binary Value128, |a, b| a.v128_xor(b)),
- V128Bitselect => stack_op!(ternary Value128, |v1, v2, c| Value128::v128_bitselect(v1, v2, 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)),
- V128Load(arg) => self.exec_mem_load::<Value128, 16, _>(arg.mem_addr(), arg.offset(), |v| v)?,
- V128Load8x8S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?,
- V128Load8x8U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?,
- V128Load16x4S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?,
- V128Load16x4U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?,
- V128Load32x2S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?,
- V128Load32x2U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?,
- V128Load8Splat(arg) => self.exec_mem_load::<i8, 1, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i8)?,
- V128Load16Splat(arg) => self.exec_mem_load::<i16, 2, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i16)?,
- V128Load32Splat(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i32)?,
- V128Load64Splat(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i64)?,
- V128Store(arg) => self.exec_mem_store::<Value128, Value128, 16>(arg.mem_addr(), arg.offset(), |v| v)?,
- V128Store8Lane(arg, lane) => self.exec_mem_store_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Store16Lane(arg, lane) => self.exec_mem_store_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Store32Lane(arg, lane) => self.exec_mem_store_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Store64Lane(arg, lane) => self.exec_mem_store_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load32Zero(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?,
- V128Load64Zero(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?,
- V128Const(arg) => self.exec_const::<Value128>(self.func.data.v128_constants[*arg as usize].into()).to_cf()?,
- I8x16ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i8(*lane) as i32),
- I8x16ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u8(*lane) as i32),
- I16x8ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i16(*lane) as i32),
- I16x8ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u16(*lane) as i32),
- I32x4ExtractLane(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i32(*lane)),
- I64x2ExtractLane(lane) => stack_op!(unary Value128 => i64, |v| v.extract_lane_i64(*lane)),
- F32x4ExtractLane(lane) => stack_op!(unary Value128 => f32, |v| v.extract_lane_f32(*lane)),
- F64x2ExtractLane(lane) => stack_op!(unary Value128 => f64, |v| v.extract_lane_f64(*lane)),
- V128Load8Lane(arg, lane) => self.exec_mem_load_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load16Lane(arg, lane) => self.exec_mem_load_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load32Lane(arg, lane) => self.exec_mem_load_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
- V128Load64Lane(arg, lane) => self.exec_mem_load_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?,
- I8x16ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i8x16_replace_lane(*lane, value as i8)),
- I16x8ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i16x8_replace_lane(*lane, value as i16)),
- I32x4ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i32x4_replace_lane(*lane, value)),
- I64x2ReplaceLane(lane) => stack_op!(binary i64, Value128, |value, vec| vec.i64x2_replace_lane(*lane, value)),
- F32x4ReplaceLane(lane) => stack_op!(binary f32, Value128, |value, vec| vec.f32x4_replace_lane(*lane, value)),
- F64x2ReplaceLane(lane) => stack_op!(binary f64, Value128, |value, vec| vec.f64x2_replace_lane(*lane, value)),
- I8x16Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i8(v as i8)),
- I16x8Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i16(v as i16)),
- I32x4Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i32(v)),
- I64x2Splat => stack_op!(unary i64 => Value128, |v| Value128::splat_i64(v)),
- F32x4Splat => stack_op!(unary f32 => Value128, |v| Value128::splat_f32(v)),
- F64x2Splat => stack_op!(unary f64 => Value128, |v| Value128::splat_f64(v)),
- I8x16Eq => stack_op!(binary Value128, |a, b| a.i8x16_eq(b)),
- I16x8Eq => stack_op!(binary Value128, |a, b| a.i16x8_eq(b)),
- I32x4Eq => stack_op!(binary Value128, |a, b| a.i32x4_eq(b)),
- I64x2Eq => stack_op!(binary Value128, |a, b| a.i64x2_eq(b)),
- F32x4Eq => stack_op!(binary Value128, |a, b| a.f32x4_eq(b)),
- F64x2Eq => stack_op!(binary Value128, |a, b| a.f64x2_eq(b)),
- I8x16Ne => stack_op!(binary Value128, |a, b| a.i8x16_ne(b)),
- I16x8Ne => stack_op!(binary Value128, |a, b| a.i16x8_ne(b)),
- I32x4Ne => stack_op!(binary Value128, |a, b| a.i32x4_ne(b)),
- I64x2Ne => stack_op!(binary Value128, |a, b| a.i64x2_ne(b)),
- F32x4Ne => stack_op!(binary Value128, |a, b| a.f32x4_ne(b)),
- F64x2Ne => stack_op!(binary Value128, |a, b| a.f64x2_ne(b)),
- I8x16LtS => stack_op!(binary Value128, |a, b| a.i8x16_lt_s(b)),
- I16x8LtS => stack_op!(binary Value128, |a, b| a.i16x8_lt_s(b)),
- I32x4LtS => stack_op!(binary Value128, |a, b| a.i32x4_lt_s(b)),
- I64x2LtS => stack_op!(binary Value128, |a, b| a.i64x2_lt_s(b)),
- I8x16LtU => stack_op!(binary Value128, |a, b| a.i8x16_lt_u(b)),
- I16x8LtU => stack_op!(binary Value128, |a, b| a.i16x8_lt_u(b)),
- I32x4LtU => stack_op!(binary Value128, |a, b| a.i32x4_lt_u(b)),
- F32x4Lt => stack_op!(binary Value128, |a, b| a.f32x4_lt(b)),
- F64x2Lt => stack_op!(binary Value128, |a, b| a.f64x2_lt(b)),
- F32x4Gt => stack_op!(binary Value128, |a, b| a.f32x4_gt(b)),
- F64x2Gt => stack_op!(binary Value128, |a, b| a.f64x2_gt(b)),
- I8x16GtS => stack_op!(binary Value128, |a, b| a.i8x16_gt_s(b)),
- I16x8GtS => stack_op!(binary Value128, |a, b| a.i16x8_gt_s(b)),
- I32x4GtS => stack_op!(binary Value128, |a, b| a.i32x4_gt_s(b)),
- I64x2GtS => stack_op!(binary Value128, |a, b| a.i64x2_gt_s(b)),
- I64x2LeS => stack_op!(binary Value128, |a, b| a.i64x2_le_s(b)),
- F32x4Le => stack_op!(binary Value128, |a, b| a.f32x4_le(b)),
- F64x2Le => stack_op!(binary Value128, |a, b| a.f64x2_le(b)),
- I8x16GtU => stack_op!(binary Value128, |a, b| a.i8x16_gt_u(b)),
- I16x8GtU => stack_op!(binary Value128, |a, b| a.i16x8_gt_u(b)),
- I32x4GtU => stack_op!(binary Value128, |a, b| a.i32x4_gt_u(b)),
- F32x4Ge => stack_op!(binary Value128, |a, b| a.f32x4_ge(b)),
- F64x2Ge => stack_op!(binary Value128, |a, b| a.f64x2_ge(b)),
- I8x16LeS => stack_op!(binary Value128, |a, b| a.i8x16_le_s(b)),
- I16x8LeS => stack_op!(binary Value128, |a, b| a.i16x8_le_s(b)),
- I32x4LeS => stack_op!(binary Value128, |a, b| a.i32x4_le_s(b)),
- I8x16LeU => stack_op!(binary Value128, |a, b| a.i8x16_le_u(b)),
- I16x8LeU => stack_op!(binary Value128, |a, b| a.i16x8_le_u(b)),
- I32x4LeU => stack_op!(binary Value128, |a, b| a.i32x4_le_u(b)),
- I8x16GeS => stack_op!(binary Value128, |a, b| a.i8x16_ge_s(b)),
- I16x8GeS => stack_op!(binary Value128, |a, b| a.i16x8_ge_s(b)),
- I32x4GeS => stack_op!(binary Value128, |a, b| a.i32x4_ge_s(b)),
- I64x2GeS => stack_op!(binary Value128, |a, b| a.i64x2_ge_s(b)),
- I8x16GeU => stack_op!(binary Value128, |a, b| a.i8x16_ge_u(b)),
- I16x8GeU => stack_op!(binary Value128, |a, b| a.i16x8_ge_u(b)),
- I32x4GeU => stack_op!(binary Value128, |a, b| a.i32x4_ge_u(b)),
- I8x16Abs => stack_op!(unary Value128, |a| a.i8x16_abs()),
- I16x8Abs => stack_op!(unary Value128, |a| a.i16x8_abs()),
- I32x4Abs => stack_op!(unary Value128, |a| a.i32x4_abs()),
- I64x2Abs => stack_op!(unary Value128, |a| a.i64x2_abs()),
- I8x16Neg => stack_op!(unary Value128, |a| a.i8x16_neg()),
- I16x8Neg => stack_op!(unary Value128, |a| a.i16x8_neg()),
- I32x4Neg => stack_op!(unary Value128, |a| a.i32x4_neg()),
- I64x2Neg => stack_op!(unary Value128, |a| a.i64x2_neg()),
- I8x16AllTrue => stack_op!(unary Value128 => i32, |v| v.i8x16_all_true() as i32),
- I16x8AllTrue => stack_op!(unary Value128 => i32, |v| v.i16x8_all_true() as i32),
- I32x4AllTrue => stack_op!(unary Value128 => i32, |v| v.i32x4_all_true() as i32),
- I64x2AllTrue => stack_op!(unary Value128 => i32, |v| v.i64x2_all_true() as i32),
- I8x16Bitmask => stack_op!(unary Value128 => i32, |v| v.i8x16_bitmask() as i32),
- I16x8Bitmask => stack_op!(unary Value128 => i32, |v| v.i16x8_bitmask() as i32),
- I32x4Bitmask => stack_op!(unary Value128 => i32, |v| v.i32x4_bitmask() as i32),
- I64x2Bitmask => stack_op!(unary Value128 => i32, |v| v.i64x2_bitmask() as i32),
- I8x16Shl => stack_op!(binary i32, Value128, |a, b| b.i8x16_shl(a as u32)),
- I16x8Shl => stack_op!(binary i32, Value128, |a, b| b.i16x8_shl(a as u32)),
- I32x4Shl => stack_op!(binary i32, Value128, |a, b| b.i32x4_shl(a as u32)),
- I64x2Shl => stack_op!(binary i32, Value128, |a, b| b.i64x2_shl(a as u32)),
- I8x16ShrS => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_s(a as u32)),
- I16x8ShrS => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_s(a as u32)),
- I32x4ShrS => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_s(a as u32)),
- I64x2ShrS => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_s(a as u32)),
- I8x16ShrU => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_u(a as u32)),
- I16x8ShrU => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_u(a as u32)),
- I32x4ShrU => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_u(a as u32)),
- I64x2ShrU => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_u(a as u32)),
- I8x16Add => stack_op!(binary Value128, |a, b| a.i8x16_add(b)),
- I16x8Add => stack_op!(binary Value128, |a, b| a.i16x8_add(b)),
- I32x4Add => stack_op!(binary Value128, |a, b| a.i32x4_add(b)),
- I64x2Add => stack_op!(binary Value128, |a, b| a.i64x2_add(b)),
- I8x16Sub => stack_op!(binary Value128, |a, b| a.i8x16_sub(b)),
- I16x8Sub => stack_op!(binary Value128, |a, b| a.i16x8_sub(b)),
- I32x4Sub => stack_op!(binary Value128, |a, b| a.i32x4_sub(b)),
- I64x2Sub => stack_op!(binary Value128, |a, b| a.i64x2_sub(b)),
- I8x16MinS => stack_op!(binary Value128, |a, b| a.i8x16_min_s(b)),
- I16x8MinS => stack_op!(binary Value128, |a, b| a.i16x8_min_s(b)),
- I32x4MinS => stack_op!(binary Value128, |a, b| a.i32x4_min_s(b)),
- I8x16MinU => stack_op!(binary Value128, |a, b| a.i8x16_min_u(b)),
- I16x8MinU => stack_op!(binary Value128, |a, b| a.i16x8_min_u(b)),
- I32x4MinU => stack_op!(binary Value128, |a, b| a.i32x4_min_u(b)),
- I8x16MaxS => stack_op!(binary Value128, |a, b| a.i8x16_max_s(b)),
- I16x8MaxS => stack_op!(binary Value128, |a, b| a.i16x8_max_s(b)),
- I32x4MaxS => stack_op!(binary Value128, |a, b| a.i32x4_max_s(b)),
- I8x16MaxU => stack_op!(binary Value128, |a, b| a.i8x16_max_u(b)),
- I16x8MaxU => stack_op!(binary Value128, |a, b| a.i16x8_max_u(b)),
- I32x4MaxU => stack_op!(binary Value128, |a, b| a.i32x4_max_u(b)),
- I64x2Mul => stack_op!(binary Value128, |a, b| a.i64x2_mul(b)),
- I16x8Mul => stack_op!(binary Value128, |a, b| a.i16x8_mul(b)),
- I32x4Mul => stack_op!(binary Value128, |a, b| a.i32x4_mul(b)),
- I8x16NarrowI16x8S => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_s(a, b)),
- I8x16NarrowI16x8U => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_u(a, b)),
- I16x8NarrowI32x4S => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_s(a, b)),
- I16x8NarrowI32x4U => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_u(a, b)),
- I8x16AddSatS => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_s(b)),
- I16x8AddSatS => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_s(b)),
- I8x16AddSatU => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_u(b)),
- I16x8AddSatU => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_u(b)),
- I8x16SubSatS => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_s(b)),
- I16x8SubSatS => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_s(b)),
- I8x16SubSatU => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_u(b)),
- I16x8SubSatU => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_u(b)),
- I8x16AvgrU => stack_op!(binary Value128, |a, b| a.i8x16_avgr_u(b)),
- I16x8AvgrU => stack_op!(binary Value128, |a, b| a.i16x8_avgr_u(b)),
- I16x8ExtAddPairwiseI8x16S => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_s()),
- I16x8ExtAddPairwiseI8x16U => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_u()),
- I32x4ExtAddPairwiseI16x8S => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_s()),
- I32x4ExtAddPairwiseI16x8U => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_u()),
- I16x8ExtMulLowI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_s(b)),
- I16x8ExtMulLowI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_u(b)),
- I16x8ExtMulHighI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_s(b)),
- I16x8ExtMulHighI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_u(b)),
- I32x4ExtMulLowI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_s(b)),
- I32x4ExtMulLowI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_u(b)),
- I32x4ExtMulHighI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_s(b)),
- I32x4ExtMulHighI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_u(b)),
- I64x2ExtMulLowI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_s(b)),
- I64x2ExtMulLowI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_u(b)),
- I64x2ExtMulHighI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_s(b)),
- I64x2ExtMulHighI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_u(b)),
- I16x8ExtendLowI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_s()),
- I16x8ExtendLowI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_u()),
- I16x8ExtendHighI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_s()),
- I16x8ExtendHighI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_u()),
- I32x4ExtendLowI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_s()),
- I32x4ExtendLowI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_u()),
- I32x4ExtendHighI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_s()),
- I32x4ExtendHighI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_u()),
- I64x2ExtendLowI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_s()),
- I64x2ExtendLowI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_u()),
- I64x2ExtendHighI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_s()),
- I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()),
- I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()),
- I8x16Shuffle(idx) => { let idx = self.func.data.v128_constants[*idx as usize].to_le_bytes(); stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, idx)) }
- I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)),
- I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)),
- F32x4Ceil => stack_op!(simd_unary f32x4_ceil),
- F64x2Ceil => stack_op!(simd_unary f64x2_ceil),
- F32x4Floor => stack_op!(simd_unary f32x4_floor),
- F64x2Floor => stack_op!(simd_unary f64x2_floor),
- F32x4Trunc => stack_op!(simd_unary f32x4_trunc),
- F64x2Trunc => stack_op!(simd_unary f64x2_trunc),
- F32x4Nearest => stack_op!(simd_unary f32x4_nearest),
- F64x2Nearest => stack_op!(simd_unary f64x2_nearest),
- F32x4Abs => stack_op!(simd_unary f32x4_abs),
- F64x2Abs => stack_op!(simd_unary f64x2_abs),
- F32x4Neg => stack_op!(simd_unary f32x4_neg),
- F64x2Neg => stack_op!(simd_unary f64x2_neg),
- F32x4Sqrt => stack_op!(simd_unary f32x4_sqrt),
- F64x2Sqrt => stack_op!(simd_unary f64x2_sqrt),
- F32x4Add => stack_op!(simd_binary f32x4_add),
- F64x2Add => stack_op!(simd_binary f64x2_add),
- F32x4Sub => stack_op!(simd_binary f32x4_sub),
- F64x2Sub => stack_op!(simd_binary f64x2_sub),
- F32x4Mul => stack_op!(simd_binary f32x4_mul),
- F64x2Mul => stack_op!(simd_binary f64x2_mul),
- F32x4Div => stack_op!(simd_binary f32x4_div),
- F64x2Div => stack_op!(simd_binary f64x2_div),
- F32x4Min => stack_op!(simd_binary f32x4_min),
- F64x2Min => stack_op!(simd_binary f64x2_min),
- F32x4Max => stack_op!(simd_binary f32x4_max),
- F64x2Max => stack_op!(simd_binary f64x2_max),
- F32x4PMin => stack_op!(simd_binary f32x4_pmin),
- F32x4PMax => stack_op!(simd_binary f32x4_pmax),
- F64x2PMin => stack_op!(simd_binary f64x2_pmin),
- F64x2PMax => stack_op!(simd_binary f64x2_pmax),
- 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()),
- };
+ // 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, |v1, v2, c| Value128::v128_bitselect(v1, v2, 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)),
+ V128Load(arg) => self.exec_mem_load::<Value128, 16, _>(arg.mem_addr(), arg.offset(), |v| v)?,
+ V128Load8x8S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?,
+ V128Load8x8U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?,
+ V128Load16x4S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?,
+ V128Load16x4U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?,
+ V128Load32x2S(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?,
+ V128Load32x2U(arg) => self.exec_mem_load::<u64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?,
+ V128Load8Splat(arg) => self.exec_mem_load::<i8, 1, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i8)?,
+ V128Load16Splat(arg) => self.exec_mem_load::<i16, 2, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i16)?,
+ V128Load32Splat(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i32)?,
+ V128Load64Splat(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), Value128::splat_i64)?,
+ V128Store(arg) => self.exec_mem_store::<Value128, Value128, 16>(arg.mem_addr(), arg.offset(), |v| v)?,
+ V128Store8Lane(arg, lane) => self.exec_mem_store_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Store16Lane(arg, lane) => self.exec_mem_store_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Store32Lane(arg, lane) => self.exec_mem_store_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Store64Lane(arg, lane) => self.exec_mem_store_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load32Zero(arg) => self.exec_mem_load::<i32, 4, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?,
+ V128Load64Zero(arg) => self.exec_mem_load::<i64, 8, Value128>(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?,
+ V128Const(arg) => self.exec_const::<Value128>(self.func.data.v128_constants[*arg as usize].into())?,
+ I8x16ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i8(*lane) as i32),
+ I8x16ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u8(*lane) as i32),
+ I16x8ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i16(*lane) as i32),
+ I16x8ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u16(*lane) as i32),
+ I32x4ExtractLane(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i32(*lane)),
+ I64x2ExtractLane(lane) => stack_op!(unary Value128 => i64, |v| v.extract_lane_i64(*lane)),
+ F32x4ExtractLane(lane) => stack_op!(unary Value128 => f32, |v| v.extract_lane_f32(*lane)),
+ F64x2ExtractLane(lane) => stack_op!(unary Value128 => f64, |v| v.extract_lane_f64(*lane)),
+ V128Load8Lane(arg, lane) => self.exec_mem_load_lane::<i8, 1>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load16Lane(arg, lane) => self.exec_mem_load_lane::<i16, 2>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load32Lane(arg, lane) => self.exec_mem_load_lane::<i32, 4>(arg.mem_addr(), arg.offset(), *lane)?,
+ V128Load64Lane(arg, lane) => self.exec_mem_load_lane::<i64, 8>(arg.mem_addr(), arg.offset(), *lane)?,
+ I8x16ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i8x16_replace_lane(*lane, value as i8)),
+ I16x8ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i16x8_replace_lane(*lane, value as i16)),
+ I32x4ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i32x4_replace_lane(*lane, value)),
+ I64x2ReplaceLane(lane) => stack_op!(binary i64, Value128, |value, vec| vec.i64x2_replace_lane(*lane, value)),
+ F32x4ReplaceLane(lane) => stack_op!(binary f32, Value128, |value, vec| vec.f32x4_replace_lane(*lane, value)),
+ F64x2ReplaceLane(lane) => stack_op!(binary f64, Value128, |value, vec| vec.f64x2_replace_lane(*lane, value)),
+ I8x16Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i8(v as i8)),
+ I16x8Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i16(v as i16)),
+ I32x4Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i32(v)),
+ I64x2Splat => stack_op!(unary i64 => Value128, |v| Value128::splat_i64(v)),
+ F32x4Splat => stack_op!(unary f32 => Value128, |v| Value128::splat_f32(v)),
+ F64x2Splat => stack_op!(unary f64 => Value128, |v| Value128::splat_f64(v)),
+ I8x16Eq => stack_op!(binary Value128, |a, b| a.i8x16_eq(b)),
+ I16x8Eq => stack_op!(binary Value128, |a, b| a.i16x8_eq(b)),
+ I32x4Eq => stack_op!(binary Value128, |a, b| a.i32x4_eq(b)),
+ I64x2Eq => stack_op!(binary Value128, |a, b| a.i64x2_eq(b)),
+ F32x4Eq => stack_op!(binary Value128, |a, b| a.f32x4_eq(b)),
+ F64x2Eq => stack_op!(binary Value128, |a, b| a.f64x2_eq(b)),
+ I8x16Ne => stack_op!(binary Value128, |a, b| a.i8x16_ne(b)),
+ I16x8Ne => stack_op!(binary Value128, |a, b| a.i16x8_ne(b)),
+ I32x4Ne => stack_op!(binary Value128, |a, b| a.i32x4_ne(b)),
+ I64x2Ne => stack_op!(binary Value128, |a, b| a.i64x2_ne(b)),
+ F32x4Ne => stack_op!(binary Value128, |a, b| a.f32x4_ne(b)),
+ F64x2Ne => stack_op!(binary Value128, |a, b| a.f64x2_ne(b)),
+ I8x16LtS => stack_op!(binary Value128, |a, b| a.i8x16_lt_s(b)),
+ I16x8LtS => stack_op!(binary Value128, |a, b| a.i16x8_lt_s(b)),
+ I32x4LtS => stack_op!(binary Value128, |a, b| a.i32x4_lt_s(b)),
+ I64x2LtS => stack_op!(binary Value128, |a, b| a.i64x2_lt_s(b)),
+ I8x16LtU => stack_op!(binary Value128, |a, b| a.i8x16_lt_u(b)),
+ I16x8LtU => stack_op!(binary Value128, |a, b| a.i16x8_lt_u(b)),
+ I32x4LtU => stack_op!(binary Value128, |a, b| a.i32x4_lt_u(b)),
+ F32x4Lt => stack_op!(binary Value128, |a, b| a.f32x4_lt(b)),
+ F64x2Lt => stack_op!(binary Value128, |a, b| a.f64x2_lt(b)),
+ F32x4Gt => stack_op!(binary Value128, |a, b| a.f32x4_gt(b)),
+ F64x2Gt => stack_op!(binary Value128, |a, b| a.f64x2_gt(b)),
+ I8x16GtS => stack_op!(binary Value128, |a, b| a.i8x16_gt_s(b)),
+ I16x8GtS => stack_op!(binary Value128, |a, b| a.i16x8_gt_s(b)),
+ I32x4GtS => stack_op!(binary Value128, |a, b| a.i32x4_gt_s(b)),
+ I64x2GtS => stack_op!(binary Value128, |a, b| a.i64x2_gt_s(b)),
+ I64x2LeS => stack_op!(binary Value128, |a, b| a.i64x2_le_s(b)),
+ F32x4Le => stack_op!(binary Value128, |a, b| a.f32x4_le(b)),
+ F64x2Le => stack_op!(binary Value128, |a, b| a.f64x2_le(b)),
+ I8x16GtU => stack_op!(binary Value128, |a, b| a.i8x16_gt_u(b)),
+ I16x8GtU => stack_op!(binary Value128, |a, b| a.i16x8_gt_u(b)),
+ I32x4GtU => stack_op!(binary Value128, |a, b| a.i32x4_gt_u(b)),
+ F32x4Ge => stack_op!(binary Value128, |a, b| a.f32x4_ge(b)),
+ F64x2Ge => stack_op!(binary Value128, |a, b| a.f64x2_ge(b)),
+ I8x16LeS => stack_op!(binary Value128, |a, b| a.i8x16_le_s(b)),
+ I16x8LeS => stack_op!(binary Value128, |a, b| a.i16x8_le_s(b)),
+ I32x4LeS => stack_op!(binary Value128, |a, b| a.i32x4_le_s(b)),
+ I8x16LeU => stack_op!(binary Value128, |a, b| a.i8x16_le_u(b)),
+ I16x8LeU => stack_op!(binary Value128, |a, b| a.i16x8_le_u(b)),
+ I32x4LeU => stack_op!(binary Value128, |a, b| a.i32x4_le_u(b)),
+ I8x16GeS => stack_op!(binary Value128, |a, b| a.i8x16_ge_s(b)),
+ I16x8GeS => stack_op!(binary Value128, |a, b| a.i16x8_ge_s(b)),
+ I32x4GeS => stack_op!(binary Value128, |a, b| a.i32x4_ge_s(b)),
+ I64x2GeS => stack_op!(binary Value128, |a, b| a.i64x2_ge_s(b)),
+ I8x16GeU => stack_op!(binary Value128, |a, b| a.i8x16_ge_u(b)),
+ I16x8GeU => stack_op!(binary Value128, |a, b| a.i16x8_ge_u(b)),
+ I32x4GeU => stack_op!(binary Value128, |a, b| a.i32x4_ge_u(b)),
+ I8x16Abs => stack_op!(unary Value128, |a| a.i8x16_abs()),
+ I16x8Abs => stack_op!(unary Value128, |a| a.i16x8_abs()),
+ I32x4Abs => stack_op!(unary Value128, |a| a.i32x4_abs()),
+ I64x2Abs => stack_op!(unary Value128, |a| a.i64x2_abs()),
+ I8x16Neg => stack_op!(unary Value128, |a| a.i8x16_neg()),
+ I16x8Neg => stack_op!(unary Value128, |a| a.i16x8_neg()),
+ I32x4Neg => stack_op!(unary Value128, |a| a.i32x4_neg()),
+ I64x2Neg => stack_op!(unary Value128, |a| a.i64x2_neg()),
+ I8x16AllTrue => stack_op!(unary Value128 => i32, |v| v.i8x16_all_true() as i32),
+ I16x8AllTrue => stack_op!(unary Value128 => i32, |v| v.i16x8_all_true() as i32),
+ I32x4AllTrue => stack_op!(unary Value128 => i32, |v| v.i32x4_all_true() as i32),
+ I64x2AllTrue => stack_op!(unary Value128 => i32, |v| v.i64x2_all_true() as i32),
+ I8x16Bitmask => stack_op!(unary Value128 => i32, |v| v.i8x16_bitmask() as i32),
+ I16x8Bitmask => stack_op!(unary Value128 => i32, |v| v.i16x8_bitmask() as i32),
+ I32x4Bitmask => stack_op!(unary Value128 => i32, |v| v.i32x4_bitmask() as i32),
+ I64x2Bitmask => stack_op!(unary Value128 => i32, |v| v.i64x2_bitmask() as i32),
+ I8x16Shl => stack_op!(binary i32, Value128, |a, b| b.i8x16_shl(a as u32)),
+ I16x8Shl => stack_op!(binary i32, Value128, |a, b| b.i16x8_shl(a as u32)),
+ I32x4Shl => stack_op!(binary i32, Value128, |a, b| b.i32x4_shl(a as u32)),
+ I64x2Shl => stack_op!(binary i32, Value128, |a, b| b.i64x2_shl(a as u32)),
+ I8x16ShrS => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_s(a as u32)),
+ I16x8ShrS => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_s(a as u32)),
+ I32x4ShrS => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_s(a as u32)),
+ I64x2ShrS => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_s(a as u32)),
+ I8x16ShrU => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_u(a as u32)),
+ I16x8ShrU => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_u(a as u32)),
+ I32x4ShrU => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_u(a as u32)),
+ I64x2ShrU => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_u(a as u32)),
+ I8x16Add => stack_op!(binary Value128, |a, b| a.i8x16_add(b)),
+ I16x8Add => stack_op!(binary Value128, |a, b| a.i16x8_add(b)),
+ I32x4Add => stack_op!(binary Value128, |a, b| a.i32x4_add(b)),
+ I64x2Add => stack_op!(binary Value128, |a, b| a.i64x2_add(b)),
+ I8x16Sub => stack_op!(binary Value128, |a, b| a.i8x16_sub(b)),
+ I16x8Sub => stack_op!(binary Value128, |a, b| a.i16x8_sub(b)),
+ I32x4Sub => stack_op!(binary Value128, |a, b| a.i32x4_sub(b)),
+ I64x2Sub => stack_op!(binary Value128, |a, b| a.i64x2_sub(b)),
+ I8x16MinS => stack_op!(binary Value128, |a, b| a.i8x16_min_s(b)),
+ I16x8MinS => stack_op!(binary Value128, |a, b| a.i16x8_min_s(b)),
+ I32x4MinS => stack_op!(binary Value128, |a, b| a.i32x4_min_s(b)),
+ I8x16MinU => stack_op!(binary Value128, |a, b| a.i8x16_min_u(b)),
+ I16x8MinU => stack_op!(binary Value128, |a, b| a.i16x8_min_u(b)),
+ I32x4MinU => stack_op!(binary Value128, |a, b| a.i32x4_min_u(b)),
+ I8x16MaxS => stack_op!(binary Value128, |a, b| a.i8x16_max_s(b)),
+ I16x8MaxS => stack_op!(binary Value128, |a, b| a.i16x8_max_s(b)),
+ I32x4MaxS => stack_op!(binary Value128, |a, b| a.i32x4_max_s(b)),
+ I8x16MaxU => stack_op!(binary Value128, |a, b| a.i8x16_max_u(b)),
+ I16x8MaxU => stack_op!(binary Value128, |a, b| a.i16x8_max_u(b)),
+ I32x4MaxU => stack_op!(binary Value128, |a, b| a.i32x4_max_u(b)),
+ I64x2Mul => stack_op!(binary Value128, |a, b| a.i64x2_mul(b)),
+ I16x8Mul => stack_op!(binary Value128, |a, b| a.i16x8_mul(b)),
+ I32x4Mul => stack_op!(binary Value128, |a, b| a.i32x4_mul(b)),
+ I8x16NarrowI16x8S => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_s(a, b)),
+ I8x16NarrowI16x8U => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_u(a, b)),
+ I16x8NarrowI32x4S => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_s(a, b)),
+ I16x8NarrowI32x4U => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_u(a, b)),
+ I8x16AddSatS => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_s(b)),
+ I16x8AddSatS => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_s(b)),
+ I8x16AddSatU => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_u(b)),
+ I16x8AddSatU => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_u(b)),
+ I8x16SubSatS => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_s(b)),
+ I16x8SubSatS => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_s(b)),
+ I8x16SubSatU => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_u(b)),
+ I16x8SubSatU => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_u(b)),
+ I8x16AvgrU => stack_op!(binary Value128, |a, b| a.i8x16_avgr_u(b)),
+ I16x8AvgrU => stack_op!(binary Value128, |a, b| a.i16x8_avgr_u(b)),
+ I16x8ExtAddPairwiseI8x16S => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_s()),
+ I16x8ExtAddPairwiseI8x16U => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_u()),
+ I32x4ExtAddPairwiseI16x8S => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_s()),
+ I32x4ExtAddPairwiseI16x8U => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_u()),
+ I16x8ExtMulLowI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_s(b)),
+ I16x8ExtMulLowI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_u(b)),
+ I16x8ExtMulHighI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_s(b)),
+ I16x8ExtMulHighI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_u(b)),
+ I32x4ExtMulLowI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_s(b)),
+ I32x4ExtMulLowI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_u(b)),
+ I32x4ExtMulHighI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_s(b)),
+ I32x4ExtMulHighI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_u(b)),
+ I64x2ExtMulLowI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_s(b)),
+ I64x2ExtMulLowI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_u(b)),
+ I64x2ExtMulHighI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_s(b)),
+ I64x2ExtMulHighI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_u(b)),
+ I16x8ExtendLowI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_s()),
+ I16x8ExtendLowI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_u()),
+ I16x8ExtendHighI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_s()),
+ I16x8ExtendHighI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_u()),
+ I32x4ExtendLowI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_s()),
+ I32x4ExtendLowI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_u()),
+ I32x4ExtendHighI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_s()),
+ I32x4ExtendHighI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_u()),
+ I64x2ExtendLowI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_s()),
+ I64x2ExtendLowI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_u()),
+ I64x2ExtendHighI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_s()),
+ I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()),
+ I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()),
+ I8x16Shuffle(idx) => { let idx = self.func.data.v128_constants[*idx as usize].to_le_bytes(); stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, idx)) }
+ I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)),
+ I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)),
+ F32x4Ceil => stack_op!(simd_unary f32x4_ceil),
+ F64x2Ceil => stack_op!(simd_unary f64x2_ceil),
+ F32x4Floor => stack_op!(simd_unary f32x4_floor),
+ F64x2Floor => stack_op!(simd_unary f64x2_floor),
+ F32x4Trunc => stack_op!(simd_unary f32x4_trunc),
+ F64x2Trunc => stack_op!(simd_unary f64x2_trunc),
+ F32x4Nearest => stack_op!(simd_unary f32x4_nearest),
+ F64x2Nearest => stack_op!(simd_unary f64x2_nearest),
+ F32x4Abs => stack_op!(simd_unary f32x4_abs),
+ F64x2Abs => stack_op!(simd_unary f64x2_abs),
+ F32x4Neg => stack_op!(simd_unary f32x4_neg),
+ F64x2Neg => stack_op!(simd_unary f64x2_neg),
+ F32x4Sqrt => stack_op!(simd_unary f32x4_sqrt),
+ F64x2Sqrt => stack_op!(simd_unary f64x2_sqrt),
+ F32x4Add => stack_op!(simd_binary f32x4_add),
+ F64x2Add => stack_op!(simd_binary f64x2_add),
+ F32x4Sub => stack_op!(simd_binary f32x4_sub),
+ F64x2Sub => stack_op!(simd_binary f64x2_sub),
+ F32x4Mul => stack_op!(simd_binary f32x4_mul),
+ F64x2Mul => stack_op!(simd_binary f64x2_mul),
+ F32x4Div => stack_op!(simd_binary f32x4_div),
+ F64x2Div => stack_op!(simd_binary f64x2_div),
+ F32x4Min => stack_op!(simd_binary f32x4_min),
+ F64x2Min => stack_op!(simd_binary f64x2_min),
+ F32x4Max => stack_op!(simd_binary f32x4_max),
+ F64x2Max => stack_op!(simd_binary f64x2_max),
+ F32x4PMin => stack_op!(simd_binary f32x4_pmin),
+ F32x4PMax => stack_op!(simd_binary f32x4_pmax),
+ F64x2PMin => stack_op!(simd_binary f64x2_pmin),
+ F64x2PMax => stack_op!(simd_binary f64x2_pmax),
+ 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()),
+ };
- self.cf.incr_instr_ptr();
- ControlFlow::Continue(())
+ self.cf.incr_instr_ptr();
+ }
+
+ Ok(None)
}
#[inline(always)]
- fn exec_jump(&mut self, ip: u32) -> ControlFlow<Option<Error>> {
+ fn exec_jump(&mut self, ip: u32) {
self.cf.instr_ptr = ip;
- ControlFlow::Continue(())
}
#[inline(always)]
@@ -646,7 +647,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
#[inline(always)]
- fn exec_branch_table(&mut self, default_ip: u32, len: u32) -> ControlFlow<Option<Error>> {
+ fn exec_branch_table(&mut self, default_ip: u32, len: u32) {
let idx = self.store.stack.values.pop::<i32>();
let start = self.cf.instr_ptr + 1;
@@ -660,7 +661,6 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
};
self.cf.instr_ptr = target_ip;
- ControlFlow::Continue(())
}
fn exec_call<const IS_RETURN_CALL: bool>(
@@ -668,7 +668,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
wasm_func: Rc<WasmFunction>,
func_addr: FuncAddr,
owner: ModuleInstanceAddr,
- ) -> ControlFlow<Option<Error>> {
+ ) -> Result<()> {
if !Rc::ptr_eq(&self.func, &wasm_func) {
self.func = wasm_func.clone();
}
@@ -681,9 +681,9 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
match self.store.stack.values.enter_locals(wasm_func.params, wasm_func.locals) {
Ok(v) => v,
Err(Error::Trap(Trap::ValueStackOverflow)) if !IS_RETURN_CALL => {
- return ControlFlow::Break(Some(Trap::CallStackOverflow.into()));
+ return Err(Trap::CallStackOverflow.into());
}
- Err(err) => return ControlFlow::Break(Some(err)),
+ Err(err) => return Err(err),
};
let new_call_frame = CallFrame::new(func_addr, owner, locals_base, stack_offset);
@@ -692,7 +692,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
self.cf = new_call_frame;
} else {
self.cf.incr_instr_ptr(); // skip the call instruction
- self.store.stack.call_stack.push(self.cf).to_cf()?;
+ self.store.stack.call_stack.push(self.cf)?;
self.cf = new_call_frame;
}
@@ -700,16 +700,16 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
self.module = self.store.get_module_instance_raw(self.cf.module_addr).clone();
}
- ControlFlow::Continue(())
+ Ok(())
}
- fn exec_call_host(&mut self, host_func: Rc<imports::HostFunction>) -> ControlFlow<Option<Error>> {
+ fn exec_call_host(&mut self, host_func: Rc<imports::HostFunction>) -> Result<()> {
let params = self.store.stack.values.pop_types(&host_func.ty.params).collect::<Box<_>>();
- let res = host_func.call(FuncContext { store: self.store, module_addr: self.module.idx }, &params).to_cf()?;
- self.store.stack.values.extend_from_wasmvalues(&res).to_cf()?;
+ let res = host_func.call(FuncContext { store: self.store, module_addr: self.module.idx }, &params)?;
+ self.store.stack.values.extend_from_wasmvalues(&res)?;
self.cf.incr_instr_ptr();
- ControlFlow::Continue(())
+ Ok(())
}
- fn exec_call_direct<const IS_RETURN_CALL: bool>(&mut self, v: u32) -> ControlFlow<Option<Error>> {
+ fn exec_call_direct<const IS_RETURN_CALL: bool>(&mut self, v: u32) -> Result<()> {
self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
let addr = self.module.resolve_func_addr(v);
let func_inst = self.store.state.get_func(addr);
@@ -721,7 +721,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
}
- fn exec_call_self<const IS_RETURN_CALL: bool>(&mut self) -> ControlFlow<Option<Error>> {
+ fn exec_call_self<const IS_RETURN_CALL: bool>(&mut self) -> Result<()> {
self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
let params = self.func.params;
let locals = self.func.locals;
@@ -733,9 +733,9 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
let (locals_base, _stack_base, stack_offset) = match self.store.stack.values.enter_locals(params, locals) {
Ok(v) => v,
Err(Error::Trap(Trap::ValueStackOverflow)) if !IS_RETURN_CALL => {
- return ControlFlow::Break(Some(Trap::CallStackOverflow.into()));
+ return Err(Trap::CallStackOverflow.into());
}
- Err(err) => return ControlFlow::Break(Some(err)),
+ Err(err) => return Err(err),
};
let new_call_frame = CallFrame::new(self.cf.func_addr, self.cf.module_addr, locals_base, stack_offset);
@@ -744,27 +744,23 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
self.cf = new_call_frame;
} else {
self.cf.incr_instr_ptr();
- self.store.stack.call_stack.push(self.cf).to_cf()?;
+ self.store.stack.call_stack.push(self.cf)?;
self.cf = new_call_frame;
}
- ControlFlow::Continue(())
+ Ok(())
}
- fn exec_call_indirect<const IS_RETURN_CALL: bool>(
- &mut self,
- type_addr: u32,
- table_addr: u32,
- ) -> ControlFlow<Option<Error>> {
+ fn exec_call_indirect<const IS_RETURN_CALL: bool>(&mut self, type_addr: u32, table_addr: u32) -> Result<()> {
self.charge_call_fuel(FUEL_COST_CALL_TOTAL);
// verify that the table is of the right type, this should be validated by the parser already
let func_ref = {
let table_idx: u32 = self.store.stack.values.pop::<i32>() as u32;
let table = self.store.state.get_table(self.module.resolve_table_addr(table_addr));
assert!(table.kind.element_type == ValType::RefFunc, "table is not of type funcref");
- let table = table.get(table_idx).map_err(|_| Trap::UndefinedElement { index: table_idx as usize }.into());
- let table = table.to_cf()?;
- table.addr().ok_or(Trap::UninitializedElement { index: table_idx as usize }.into()).to_cf()?
+ let table =
+ table.get(table_idx).map_err(|_| Error::from(Trap::UndefinedElement { index: table_idx as usize }))?;
+ table.addr().ok_or_else(|| Error::from(Trap::UninitializedElement { index: table_idx as usize }))?
};
let func_inst = self.store.state.get_func(func_ref);
@@ -773,20 +769,22 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
match &func_inst.func {
crate::Function::Wasm(wasm_func) => {
if wasm_func.ty != *call_ty {
- return ControlFlow::Break(Some(
- Trap::IndirectCallTypeMismatch { actual: wasm_func.ty.clone(), expected: call_ty.clone() }
- .into(),
- ));
+ return Err(Trap::IndirectCallTypeMismatch {
+ actual: wasm_func.ty.clone(),
+ expected: call_ty.clone(),
+ }
+ .into());
}
self.exec_call::<IS_RETURN_CALL>(wasm_func.clone(), func_ref, func_inst.owner)
}
crate::Function::Host(host_func) => {
if host_func.ty != *call_ty {
- return ControlFlow::Break(Some(
- Trap::IndirectCallTypeMismatch { actual: host_func.ty.clone(), expected: call_ty.clone() }
- .into(),
- ));
+ return Err(Trap::IndirectCallTypeMismatch {
+ actual: host_func.ty.clone(),
+ expected: call_ty.clone(),
+ }
+ .into());
}
self.exec_call_host(host_func.clone())
@@ -794,11 +792,11 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
}
- fn exec_return(&mut self) -> ControlFlow<Option<Error>> {
+ fn exec_return(&mut self) -> bool {
let result_counts = ValueCountsSmall::from(self.func.ty.results.iter());
self.store.stack.values.truncate_keep_counts(self.cf.locals_base, result_counts);
- let Some(cf) = self.store.stack.call_stack.pop() else { return ControlFlow::Break(None) };
+ let Some(cf) = self.store.stack.call_stack.pop() else { return true };
if cf.func_addr != self.cf.func_addr {
self.func = self.store.state.get_wasm_func(cf.func_addr).clone();
@@ -808,7 +806,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
}
}
self.cf = cf;
- ControlFlow::Continue(())
+ false
}
fn exec_global_get(&mut self, global_index: u32) -> Result<()> {
self.store.stack.values.push_dyn(self.store.state.get_global_val(self.module.resolve_global_addr(global_index)))
@@ -928,24 +926,20 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
mem_addr: tinywasm_types::MemAddr,
offset: u64,
lane: u8,
- ) -> ControlFlow<Option<Error>> {
+ ) -> Result<()> {
let mut imm = self.store.stack.values.pop::<Value128>().to_mem_bytes();
let val = self.store.stack.values.pop::<i32>() as u64;
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
let Some(Ok(addr)) = offset.checked_add(val).map(TryInto::try_into) else {
- return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds {
- offset: val as usize,
- len: LOAD_SIZE,
- max: 0,
- })));
+ return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: val as usize, len: LOAD_SIZE, max: 0 }));
};
- let val = mem.load_as::<LOAD_SIZE, LOAD>(addr).to_cf()?.to_mem_bytes();
+ let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?.to_mem_bytes();
let offset = lane as usize * LOAD_SIZE;
imm[offset..offset + LOAD_SIZE].copy_from_slice(&val);
- self.store.stack.values.push(Value128::from_mem_bytes(imm)).to_cf()?;
- ControlFlow::Continue(())
+ self.store.stack.values.push(Value128::from_mem_bytes(imm))?;
+ Ok(())
}
#[inline(always)]
@@ -954,7 +948,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
mem_addr: tinywasm_types::MemAddr,
offset: u64,
cast: fn(LOAD) -> TARGET,
- ) -> ControlFlow<Option<Error>> {
+ ) -> Result<()> {
let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr));
let base: u64 = if mem.is_64bit() {
@@ -964,24 +958,16 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
};
let Some(addr) = base.checked_add(offset) else {
- return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds {
- offset: base as usize,
- len: LOAD_SIZE,
- max: 0,
- })));
+ return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: base as usize, len: LOAD_SIZE, max: 0 }));
};
let Ok(addr) = usize::try_from(addr) else {
- return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds {
- offset: base as usize,
- len: LOAD_SIZE,
- max: 0,
- })));
+ return Err(Error::Trap(Trap::MemoryOutOfBounds { offset: base as usize, len: LOAD_SIZE, max: 0 }));
};
- let val = mem.load_as::<LOAD_SIZE, LOAD>(addr).to_cf()?;
- self.store.stack.values.push(cast(val)).to_cf()?;
- ControlFlow::Continue(())
+ let val = mem.load_as::<LOAD_SIZE, LOAD>(addr)?;
+ self.store.stack.values.push(cast(val))?;
+ Ok(())
}
fn exec_mem_store_lane<U: MemValue<N> + Copy, const N: usize>(
@@ -989,7 +975,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
mem_addr: tinywasm_types::MemAddr,
offset: u64,
lane: u8,
- ) -> ControlFlow<Option<Error>> {
+ ) -> Result<()> {
let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(mem_addr));
let bytes = self.store.stack.values.pop::<Value128>().to_mem_bytes();
let lane_offset = lane as usize * N;
@@ -1002,10 +988,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
};
if let Err(e) = mem.store((offset + addr) as usize, val.len(), &val) {
- return ControlFlow::Break(Some(e));
+ return Err(e);
}
- ControlFlow::Continue(())
+ Ok(())
}
fn exec_mem_store<T: InternalValue, U: MemValue<N>, const N: usize>(
@@ -1013,7 +999,7 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
mem_addr: tinywasm_types::MemAddr,
offset: u64,
cast: fn(T) -> U,
- ) -> ControlFlow<Option<Error>> {
+ ) -> Result<()> {
let val = self.store.stack.values.pop::<T>();
let mem = self.store.state.get_mem_mut(self.module.resolve_mem_addr(mem_addr));
let val = (cast(val)).to_mem_bytes();
@@ -1024,10 +1010,10 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> {
};
if let Err(e) = mem.store((offset + addr) as usize, val.len(), &val) {
- return ControlFlow::Break(Some(e));
+ return Err(e);
}
- ControlFlow::Continue(())
+ Ok(())
}
fn exec_table_get(&mut self, table_index: u32) -> Result<()> {
@@ -1129,16 +1115,16 @@ impl<'store> Executor<'store, false> {
#[inline(always)]
pub(crate) fn run_to_completion(&mut self) -> Result<()> {
loop {
- match self.exec_next() {
- ControlFlow::Continue(()) => continue,
- ControlFlow::Break(res) => break res.map_or(Ok(()), Err),
+ // for some reason, using a iteration count of 4096 here seems to be a sweet spot for performance
+ if self.exec::<1024>()?.is_some() {
+ return Ok(());
}
}
}
#[cfg(feature = "std")]
#[inline(always)]
- pub(crate) fn run_with_time_budget(&mut self, time_budget: crate::std::time::Duration) -> Result<ExecState> {
+ pub(crate) fn run_with_time_budget(&mut self, time_budget: core::time::Duration) -> Result<ExecState> {
use crate::std::time::Instant;
let start = Instant::now();
if time_budget.is_zero() {
@@ -1146,12 +1132,8 @@ impl<'store> Executor<'store, false> {
}
loop {
- for _ in 0..TIME_BUDGET_CHECK_INTERVAL {
- match self.exec_next() {
- ControlFlow::Continue(()) => {}
- ControlFlow::Break(None) => return Ok(ExecState::Completed),
- ControlFlow::Break(Some(e)) => return Err(e),
- }
+ if self.exec::<TIME_BUDGET_CHECK_INTERVAL>()?.is_some() {
+ return Ok(ExecState::Completed);
}
if start.elapsed() >= time_budget {
@@ -1170,15 +1152,11 @@ impl<'store> Executor<'store, true> {
}
loop {
- for _ in 0..FUEL_ACCOUNTING_INTERVAL {
- match self.exec_next() {
- ControlFlow::Continue(()) => {}
- ControlFlow::Break(None) => return Ok(ExecState::Completed),
- ControlFlow::Break(Some(e)) => return Err(e),
- }
+ if self.exec::<FUEL_ACCOUNTING_INTERVAL>()?.is_some() {
+ return Ok(ExecState::Completed);
}
- self.store.execution_fuel = self.store.execution_fuel.saturating_sub(FUEL_ACCOUNTING_INTERVAL);
+ self.store.execution_fuel = self.store.execution_fuel.saturating_sub(FUEL_ACCOUNTING_INTERVAL as u32);
if self.store.execution_fuel == 0 {
return Ok(ExecState::Suspended(self.cf));
}
diff --git a/crates/tinywasm/src/interpreter/mod.rs b/crates/tinywasm/src/interpreter/mod.rs
index 933e782..1af7d2f 100644
--- a/crates/tinywasm/src/interpreter/mod.rs
+++ b/crates/tinywasm/src/interpreter/mod.rs
@@ -38,7 +38,7 @@ impl InterpreterRuntime {
pub(crate) fn exec_with_time_budget(
store: &mut Store,
cf: CallFrame,
- time_budget: crate::std::time::Duration,
+ time_budget: core::time::Duration,
) -> Result<ExecState> {
executor::Executor::<false>::new(store, cf)?.run_with_time_budget(time_budget)
}
diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs
index cf13260..5ae81ef 100644
--- a/crates/tinywasm/src/interpreter/num_helpers.rs
+++ b/crates/tinywasm/src/interpreter/num_helpers.rs
@@ -31,21 +31,16 @@ macro_rules! checked_conv_float {
};
// Conversion with an intermediate unsigned type and error checking (three types)
($from:tt, $intermediate:tt, $to:tt, $self:expr) => {
- $self
- .store
- .stack
- .values
- .unary_into::<$from, $to>(|v| {
- let (min, max) = float_min_max!($from, $intermediate);
- if unlikely(v.is_nan()) {
- return Err(Error::Trap(crate::Trap::InvalidConversionToInt));
- }
- if unlikely(v <= min || v >= max) {
- return Err(Error::Trap(crate::Trap::IntegerOverflow));
- }
- Ok((v as $intermediate as $to).into())
- })
- .to_cf()?
+ $self.store.stack.values.unary_into::<$from, $to>(|v| {
+ let (min, max) = float_min_max!($from, $intermediate);
+ if unlikely(v.is_nan()) {
+ return Err(Error::Trap(crate::Trap::InvalidConversionToInt));
+ }
+ if unlikely(v <= min || v >= max) {
+ return Err(Error::Trap(crate::Trap::IntegerOverflow));
+ }
+ Ok((v as $intermediate as $to).into())
+ })?
};
}
diff --git a/crates/tinywasm/tests/host_func_signature_check.rs b/crates/tinywasm/tests/host_func_signature_check.rs
index 9b0a530..0e4ba69 100644
--- a/crates/tinywasm/tests/host_func_signature_check.rs
+++ b/crates/tinywasm/tests/host_func_signature_check.rs
@@ -100,7 +100,8 @@ fn test_linking_invalid_typed_func() -> Result<()> {
let mut store = Store::default();
let mut imports = Imports::new();
imports.define("host", "hfn", typed_fn).unwrap();
- module.clone().instantiate(&mut store, Some(imports))?;
+ let link_failure = module.clone().instantiate(&mut store, Some(imports));
+ assert!(link_failure.is_err(), "Expected linking to fail for mismatched typed func, but it succeeded");
}
}
diff --git a/examples/rust/build.sh b/examples/rust/build.sh
index 4574d23..d51840b 100755
--- a/examples/rust/build.sh
+++ b/examples/rust/build.sh
@@ -7,7 +7,7 @@ out_dir="./target/wasm32-unknown-unknown/wasm"
dest_dir="out"
rust_features="+simd128,+reference-types,+bulk-memory,+mutable-globals,+multivalue,+sign-ext,+nontrapping-fptoint"
-wasmopt_features="--enable-simd --enable-reference-types --enable-bulk-memory --enable-mutable-globals --enable-multivalue --enable-sign-ext --enable-nontrapping-float-to-int --duplicate-function-elimination"
+wasmopt_features="--enable-simd --enable-tail-call --enable-extended-const --enable-reference-types --enable-bulk-memory --enable-mutable-globals --enable-multivalue --enable-sign-ext --enable-nontrapping-float-to-int --duplicate-function-elimination"
# ensure out dir exists
mkdir -p "$dest_dir"