From bfeefda25407d29a843d2aec713943843f8f1239 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 2 Apr 2026 00:54:43 +0200 Subject: chore: clean up interpreter Signed-off-by: Henry --- crates/tinywasm/src/error.rs | 1 + crates/tinywasm/src/interpreter/executor.rs | 162 +- crates/tinywasm/src/interpreter/num_helpers.rs | 2 +- .../tinywasm/src/interpreter/stack/call_stack.rs | 1 + .../tinywasm/src/interpreter/stack/value_stack.rs | 58 +- crates/tinywasm/src/interpreter/value128.rs | 2995 +++++--------------- crates/tinywasm/src/interpreter/values.rs | 24 +- crates/tinywasm/src/store/memory.rs | 6 +- crates/tinywasm/tests/host_func_signature_check.rs | 103 +- examples/funcref_callbacks.rs | 71 +- 10 files changed, 859 insertions(+), 2564 deletions(-) diff --git a/crates/tinywasm/src/error.rs b/crates/tinywasm/src/error.rs index 106f9d1..24912e3 100644 --- a/crates/tinywasm/src/error.rs +++ b/crates/tinywasm/src/error.rs @@ -268,6 +268,7 @@ pub(crate) trait Controlify { } impl Controlify for Result { + #[inline(always)] fn to_cf(self) -> ControlFlow, T> { match self { Ok(value) => ControlFlow::Continue(value), diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index 91234cc..3457120 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -14,7 +14,6 @@ use super::values::*; use crate::instance::ModuleInstanceInner; use crate::interpreter::Value128; use crate::*; - pub(crate) struct Executor<'store> { cf: CallFrame, func: Rc, @@ -45,37 +44,24 @@ impl<'store> Executor<'store> { use tinywasm_types::Instruction::*; macro_rules! stack_op { - (simd_unary $method:ident) => { - self.store.stack.values.unary_same::(|v| Ok(v.$method())).to_cf()? - }; - (simd_binary $method:ident) => { - self.store.stack.values.binary_same::(|a, b| Ok(a.$method(b))).to_cf()? - }; - (unary $ty:ty, |$v:ident| $expr:expr) => { - self.store.stack.values.unary_same::<$ty>(|$v| Ok($expr)).to_cf()? - }; - (binary $ty:ty, |$a:ident, $b:ident| $expr:expr) => { - self.store.stack.values.binary_same::<$ty>(|$a, $b| Ok($expr)).to_cf()? - }; - (binary_try $ty:ty, |$a:ident, $b:ident| $expr:expr) => { - self.store.stack.values.binary_same::<$ty>(|$a, $b| $expr).to_cf()? - }; - (unary $from:ty => $to:ty, |$v:ident| $expr:expr) => { - self.store.stack.values.unary::<$from, $to>(|$v| Ok($expr)).to_cf()? - }; - (binary $from:ty => $to:ty, |$a:ident, $b:ident| $expr:expr) => { - self.store.stack.values.binary::<$from, $to>(|$a, $b| Ok($expr)).to_cf()? - }; - (binary $a:ty, $b:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { - self.store.stack.values.binary_diff::<$a, $b, $b>(|$lhs, $rhs| Ok($expr)).to_cf()? - }; - (binary $a:ty, $b:ty => $res:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { - self.store.stack.values.binary_diff::<$a, $b, $res>(|$lhs, $rhs| Ok($expr)).to_cf()? - }; + (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()? }; + (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()? }; (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); }}; + (local_tee $ty:ty, $local_index:expr) => {{ + let val = self.store.stack.values.peek::<$ty>(); + self.store.stack.values.local_set(&self.cf, *$local_index, val); + }}; } let next = match self.func.instructions.0.get(self.cf.instr_ptr as usize) { @@ -89,7 +75,7 @@ impl<'store> Executor<'store> { #[rustfmt::skip] match next { - Nop | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 | BranchTableTarget {..} => {} + Nop | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} Unreachable => return ControlFlow::Break(Some(Trap::Unreachable.into())), Drop32 => self.store.stack.values.drop::(), Drop64 => self.store.stack.values.drop::(), @@ -106,20 +92,8 @@ impl<'store> Executor<'store> { ReturnCall(v) => return self.exec_call_direct::(*v), ReturnCallSelf => return self.exec_call_self::(), ReturnCallIndirect(ty, table) => return self.exec_call_indirect::(*ty, *table), - Jump(ip) => { - self.cf.instr_ptr = *ip; - return ControlFlow::Continue(()); - } - JumpIfZero(ip) => { - let cond = self.store.stack.values.pop::(); - - if cond == 0 { - self.cf.instr_ptr = *ip; - } else { - self.cf.incr_instr_ptr(); - } - return ControlFlow::Continue(()); - } + 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; @@ -154,21 +128,8 @@ impl<'store> Executor<'store> { let k = *keep as usize; self.store.stack.values.stack_ref.truncate_keep(b as usize, k); } - BranchTable(default_ip, len) => { - let idx = self.store.stack.values.pop::(); - let start = self.cf.instr_ptr + 1; - - let target_ip = if idx >= 0 && (idx as u32) < *len { - match self.func.instructions.0.get((start + idx as u32) as usize) { - Some(Instruction::BranchTableTarget(ip)) => *ip, - _ => *default_ip, - } - } else { - *default_ip - }; - self.cf.instr_ptr = target_ip; - return ControlFlow::Continue(()); - } + 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::(&self.cf, *local_index)).to_cf()?, LocalGet64(local_index) => self.store.stack.values.push(self.store.stack.values.local_get::(&self.cf, *local_index)).to_cf()?, @@ -214,14 +175,13 @@ impl<'store> Executor<'store> { } I64XorRotlConst(c) => stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)), I64XorRotlConstTee(c, local_index) => { - self.store.stack.values.binary_same::(|lhs, rhs| Ok((lhs ^ rhs).rotate_left(*c as u32))).to_cf()?; - let val = self.store.stack.values.peek::(); - self.store.stack.values.local_set(&self.cf, *local_index, val); + stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)); + stack_op!(local_tee i64, local_index); } - LocalTee32(local_index) => self.store.stack.values.local_set(&self.cf, *local_index, self.store.stack.values.peek::()), - LocalTee64(local_index) => self.store.stack.values.local_set(&self.cf, *local_index, self.store.stack.values.peek::()), - LocalTee128(local_index) => self.store.stack.values.local_set(&self.cf, *local_index, self.store.stack.values.peek::()), - LocalTeeRef(local_index) => self.store.stack.values.local_set(&self.cf, *local_index, self.store.stack.values.peek::()), + 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::(*global_index), GlobalSet64(global_index) => self.exec_global_set::(*global_index), @@ -279,14 +239,14 @@ impl<'store> Executor<'store> { 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)), + 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), @@ -424,7 +384,7 @@ impl<'store> Executor<'store> { 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 => self.store.stack.values.ternary_same::(|v1, v2, c| Ok(Value128::v128_bitselect(v1, v2, c))).to_cf()?, + 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::(arg.mem_addr(), arg.offset(), |v| v)?, @@ -661,6 +621,39 @@ impl<'store> Executor<'store> { ControlFlow::Continue(()) } + #[inline(always)] + fn exec_jump(&mut self, ip: u32) -> ControlFlow> { + self.cf.instr_ptr = ip; + ControlFlow::Continue(()) + } + + #[inline(always)] + fn exec_jump_if_zero(&mut self, ip: u32) -> bool { + if self.store.stack.values.pop::() == 0 { + self.cf.instr_ptr = ip; + return true; + } + false + } + + #[inline(always)] + fn exec_branch_table(&mut self, default_ip: u32, len: u32) -> ControlFlow> { + let idx = self.store.stack.values.pop::(); + let start = self.cf.instr_ptr + 1; + + let target_ip = if idx >= 0 && (idx as u32) < len { + match self.func.instructions.0.get((start + idx as u32) as usize) { + Some(Instruction::BranchTableTarget(ip)) => *ip, + _ => default_ip, + } + } else { + default_ip + }; + + self.cf.instr_ptr = target_ip; + ControlFlow::Continue(()) + } + fn exec_call( &mut self, wasm_func: Rc, @@ -943,6 +936,7 @@ impl<'store> Executor<'store> { ControlFlow::Continue(()) } + #[inline(always)] fn exec_mem_load, const LOAD_SIZE: usize, TARGET: InternalValue>( &mut self, mem_addr: tinywasm_types::MemAddr, @@ -951,18 +945,28 @@ impl<'store> Executor<'store> { ) -> ControlFlow> { let mem = self.store.state.get_mem(self.module.resolve_mem_addr(mem_addr)); - let addr = match mem.is_64bit() { - true => self.store.stack.values.pop::() as u64, - false => u64::from(self.store.stack.values.pop::() as u32), + let base: u64 = if mem.is_64bit() { + self.store.stack.values.pop::() as u64 + } else { + self.store.stack.values.pop::() as u32 as u64 }; - let Some(Ok(addr)) = offset.checked_add(addr).map(|a| a.try_into()) else { + let Some(addr) = base.checked_add(offset) else { return ControlFlow::Break(Some(Error::Trap(Trap::MemoryOutOfBounds { - offset: addr as usize, + 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, + }))); + }; + let val = mem.load_as::(addr).to_cf()?; self.store.stack.values.push(cast(val)).to_cf()?; ControlFlow::Continue(()) diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index f800575..cf13260 100644 --- a/crates/tinywasm/src/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -35,7 +35,7 @@ macro_rules! checked_conv_float { .store .stack .values - .unary::<$from, $to>(|v| { + .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)); diff --git a/crates/tinywasm/src/interpreter/stack/call_stack.rs b/crates/tinywasm/src/interpreter/stack/call_stack.rs index 1b8117c..133f910 100644 --- a/crates/tinywasm/src/interpreter/stack/call_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/call_stack.rs @@ -69,6 +69,7 @@ impl CallFrame { } } + #[inline(always)] pub(crate) fn incr_instr_ptr(&mut self) { self.instr_ptr += 1; } diff --git a/crates/tinywasm/src/interpreter/stack/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 81a5c5d..7fa0502 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -172,7 +172,7 @@ impl ValueStack { T::stack_pop(self); } - #[inline] + #[inline(always)] pub(crate) fn select(&mut self) -> Result<()> { let cond: i32 = self.pop(); let val2: T = self.pop(); @@ -192,51 +192,51 @@ impl ValueStack { self.stack_ref.select_many(counts.cref as usize, condition); } - #[inline] - pub(crate) fn binary_same(&mut self, func: impl FnOnce(T, T) -> Result) -> Result<()> { - T::stack_calculate(self, func) - } - - #[inline] - pub(crate) fn ternary_same(&mut self, func: impl FnOnce(T, T, T) -> Result) -> Result<()> { - T::stack_calculate3(self, func) + #[inline(always)] + pub(crate) fn unary(&mut self, func: impl FnOnce(T) -> Result) -> Result<()> { + T::stack_apply1(self, func) } - #[inline] - pub(crate) fn binary( + #[inline(always)] + pub(crate) fn unary_into( &mut self, - func: impl FnOnce(T, T) -> Result, + func: impl FnOnce(IN) -> Result, ) -> Result<()> { - let v2 = T::stack_pop(self); - let v1 = T::stack_pop(self); - U::stack_push(self, func(v1, v2)?)?; + let v = IN::stack_pop(self); + OUT::stack_push(self, func(v)?)?; Ok(()) } - #[inline] - pub(crate) fn binary_diff( + #[inline(always)] + pub(crate) fn binary(&mut self, func: impl FnOnce(T, T) -> Result) -> Result<()> { + T::stack_apply2(self, func) + } + + #[inline(always)] + pub(crate) fn binary_into( &mut self, - func: impl FnOnce(A, B) -> Result, + func: impl FnOnce(IN, IN) -> Result, ) -> Result<()> { - let v2 = B::stack_pop(self); - let v1 = A::stack_pop(self); - RES::stack_push(self, func(v1, v2)?)?; + let rhs = IN::stack_pop(self); + let lhs = IN::stack_pop(self); + OUT::stack_push(self, func(lhs, rhs)?)?; Ok(()) } - #[inline] - pub(crate) fn unary( + #[inline(always)] + pub(crate) fn binary_mixed( &mut self, - func: impl FnOnce(T) -> Result, + func: impl FnOnce(A, B) -> Result, ) -> Result<()> { - let v1 = T::stack_pop(self); - U::stack_push(self, func(v1)?)?; + let rhs = B::stack_pop(self); + let lhs = A::stack_pop(self); + OUT::stack_push(self, func(lhs, rhs)?)?; Ok(()) } - #[inline] - pub(crate) fn unary_same(&mut self, func: impl Fn(T) -> Result) -> Result<()> { - T::replace_top(self, func) + #[inline(always)] + pub(crate) fn ternary(&mut self, func: impl FnOnce(T, T, T) -> Result) -> Result<()> { + T::stack_apply3(self, func) } pub(crate) fn pop_types<'a>( diff --git a/crates/tinywasm/src/interpreter/value128.rs b/crates/tinywasm/src/interpreter/value128.rs index bb2c686..7de4dca 100644 --- a/crates/tinywasm/src/interpreter/value128.rs +++ b/crates/tinywasm/src/interpreter/value128.rs @@ -8,8 +8,281 @@ use core::arch::wasm32 as wasm; use core::arch::wasm64 as wasm; #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +/// A 128-bit SIMD value pub struct Value128(i128); +impl From for i128 { + fn from(val: Value128) -> Self { + val.0 + } +} + +impl From for Value128 { + fn from(value: i128) -> Self { + Self(value) + } +} + +macro_rules! simd_wrapping_binop { + ($name:ident, $doc:literal, $wasm_op:ident, $lane_ty:ty, $lane_count:expr, $as_lanes:ident, $from_lanes:ident, $op:ident) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + return Self::from_wasm_v128(wasm::$wasm_op(self.to_wasm_v128(), rhs.to_wasm_v128())); + + let a = self.$as_lanes(); + let b = rhs.$as_lanes(); + Self::$from_lanes(core::array::from_fn(|i| a[i].$op(b[i]))) + } + }; +} + +macro_rules! simd_sat_binop { + ($name:ident, $doc:literal, $wasm_op:ident, $lane_ty:ty, $lane_count:expr, $as_lanes:ident, $from_lanes:ident, $op:ident) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + return Self::from_wasm_v128(wasm::$wasm_op(self.to_wasm_v128(), rhs.to_wasm_v128())); + + let a = self.$as_lanes(); + let b = rhs.$as_lanes(); + Self::$from_lanes(core::array::from_fn(|i| a[i].$op(b[i]))) + } + }; +} + +macro_rules! simd_all_true { + ($name:ident, $doc:literal, $as_lanes:ident, $count:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self) -> bool { + let lanes = self.$as_lanes(); + let mut i = 0; + while i < $count { + if lanes[i] == 0 { + return false; + } + i += 1; + } + true + } + }; +} + +macro_rules! simd_bitmask { + ($name:ident, $doc:literal, $as_lanes:ident, $count:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self) -> u32 { + let lanes = self.$as_lanes(); + let mut mask = 0u32; + let mut i = 0; + while i < $count { + mask |= ((lanes[i] < 0) as u32) << i; + i += 1; + } + mask + } + }; +} + +macro_rules! simd_shift_left { + ($name:ident, $doc:literal, $lane_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident, $mask:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self, shift: u32) -> Self { + let lanes = self.$as_lanes(); + let s = shift & $mask; + let mut out = [0 as $lane_ty; $count]; + let mut i = 0; + while i < $count { + out[i] = lanes[i].wrapping_shl(s); + i += 1; + } + Self::$from_lanes(out) + } + }; +} + +macro_rules! simd_shift_right { + ($name:ident, $doc:literal, $lane_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident, $mask:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self, shift: u32) -> Self { + let lanes = self.$as_lanes(); + let s = shift & $mask; + let mut out = [0 as $lane_ty; $count]; + let mut i = 0; + while i < $count { + out[i] = lanes[i] >> s; + i += 1; + } + Self::$from_lanes(out) + } + }; +} + +macro_rules! simd_avgr_u { + ($name:ident, $doc:literal, $wasm_op:ident, $lane_ty:ty, $wide_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + return Self::from_wasm_v128(wasm::$wasm_op(self.to_wasm_v128(), rhs.to_wasm_v128())); + + let a = self.$as_lanes(); + let b = rhs.$as_lanes(); + Self::$from_lanes(core::array::from_fn(|i| ((a[i] as $wide_ty + b[i] as $wide_ty + 1) >> 1) as $lane_ty)) + } + }; +} + +macro_rules! simd_extend_cast { + ($name:ident, $doc:literal, $src_as:ident, $dst_from:ident, $dst_ty:ty, $dst_count:expr, $offset:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self) -> Self { + let lanes = self.$src_as(); + let mut out = [0 as $dst_ty; $dst_count]; + let mut i = 0; + while i < $dst_count { + out[i] = lanes[i + $offset] as $dst_ty; + i += 1; + } + Self::$dst_from(out) + } + }; +} + +macro_rules! simd_extmul_signed { + ($name:ident, $doc:literal, $src_as:ident, $dst_from:ident, $dst_ty:ty, $dst_count:expr, $offset:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self, rhs: Self) -> Self { + let a = self.$src_as(); + let b = rhs.$src_as(); + let mut out = [0 as $dst_ty; $dst_count]; + let mut i = 0; + while i < $dst_count { + out[i] = (a[i + $offset] as $dst_ty).wrapping_mul(b[i + $offset] as $dst_ty); + i += 1; + } + Self::$dst_from(out) + } + }; +} + +macro_rules! simd_extmul_unsigned { + ($name:ident, $doc:literal, $src_as:ident, $dst_from:ident, $dst_ty:ty, $dst_count:expr, $offset:expr) => { + #[doc(alias = $doc)] + pub const fn $name(self, rhs: Self) -> Self { + let a = self.$src_as(); + let b = rhs.$src_as(); + let mut out = [0 as $dst_ty; $dst_count]; + let mut i = 0; + while i < $dst_count { + out[i] = (a[i + $offset] as $dst_ty) * (b[i + $offset] as $dst_ty); + i += 1; + } + Self::$dst_from(out) + } + }; +} + +macro_rules! simd_cmp_mask { + ($name:ident, $doc:literal, $wasm_op:ident, $out_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident, $cmp:tt) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + return Self::from_wasm_v128(wasm::$wasm_op(self.to_wasm_v128(), rhs.to_wasm_v128())); + + let a = self.$as_lanes(); + let b = rhs.$as_lanes(); + Self::$from_lanes(core::array::from_fn(|i| if a[i] $cmp b[i] { -1 } else { 0 })) + } + }; +} + +macro_rules! simd_cmp_mask_const { + ($name:ident, $doc:literal, $out_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident, $cmp:tt) => { + #[doc(alias = $doc)] + pub const fn $name(self, rhs: Self) -> Self { + let a = self.$as_lanes(); + let b = rhs.$as_lanes(); + let mut out = [0 as $out_ty; $count]; + let mut i = 0; + while i < $count { + out[i] = if a[i] $cmp b[i] { -1 } else { 0 }; + i += 1; + } + Self::$from_lanes(out) + } + }; +} + +macro_rules! simd_cmp_delegate { + ($name:ident, $doc:literal, $delegate:ident) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + rhs.$delegate(self) + } + }; +} + +macro_rules! simd_abs_const { + ($name:ident, $doc:literal, $lane_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident) => { + #[doc(alias = $doc)] + pub const fn $name(self) -> Self { + let a = self.$as_lanes(); + let mut out = [0 as $lane_ty; $count]; + let mut i = 0; + while i < $count { + out[i] = a[i].wrapping_abs(); + i += 1; + } + Self::$from_lanes(out) + } + }; +} + +macro_rules! simd_neg { + ($name:ident, $doc:literal, $wasm_op:ident, $lane_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident) => { + #[doc(alias = $doc)] + pub fn $name(self) -> Self { + #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + return Self::from_wasm_v128(wasm::$wasm_op(self.to_wasm_v128())); + + let a = self.$as_lanes(); + Self::$from_lanes(core::array::from_fn(|i| a[i].wrapping_neg())) + } + }; +} + +macro_rules! simd_minmax { + ($name:ident, $doc:literal, $wasm_op:ident, $lane_ty:ty, $count:expr, $as_lanes:ident, $from_lanes:ident, $cmp:tt) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + return Self::from_wasm_v128(wasm::$wasm_op(self.to_wasm_v128(), rhs.to_wasm_v128())); + + let a = self.$as_lanes(); + let b = rhs.$as_lanes(); + Self::$from_lanes(core::array::from_fn(|i| if a[i] $cmp b[i] { a[i] } else { b[i] })) + } + }; +} + +macro_rules! simd_float_unary { + ($name:ident, $doc:literal, $map:ident, $op:expr) => { + #[doc(alias = $doc)] + pub fn $name(self) -> Self { + self.$map($op) + } + }; +} + +macro_rules! simd_float_binary { + ($name:ident, $doc:literal, $zip:ident, $op:expr) => { + #[doc(alias = $doc)] + pub fn $name(self, rhs: Self) -> Self { + self.$zip(rhs, $op) + } + }; +} + #[cfg_attr(any(target_arch = "wasm32", target_arch = "wasm64"), allow(unreachable_code))] impl Value128 { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] @@ -24,24 +297,8 @@ impl Value128 { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] #[inline(always)] fn from_wasm_v128(value: wasm::v128) -> Self { - Self::from_le_bytes([ - wasm::u8x16_extract_lane::<0>(value), - wasm::u8x16_extract_lane::<1>(value), - wasm::u8x16_extract_lane::<2>(value), - wasm::u8x16_extract_lane::<3>(value), - wasm::u8x16_extract_lane::<4>(value), - wasm::u8x16_extract_lane::<5>(value), - wasm::u8x16_extract_lane::<6>(value), - wasm::u8x16_extract_lane::<7>(value), - wasm::u8x16_extract_lane::<8>(value), - wasm::u8x16_extract_lane::<9>(value), - wasm::u8x16_extract_lane::<10>(value), - wasm::u8x16_extract_lane::<11>(value), - wasm::u8x16_extract_lane::<12>(value), - wasm::u8x16_extract_lane::<13>(value), - wasm::u8x16_extract_lane::<14>(value), - wasm::u8x16_extract_lane::<15>(value), - ]) + #[rustfmt::skip] + Self::from_le_bytes([ wasm::u8x16_extract_lane::<0>(value), wasm::u8x16_extract_lane::<1>(value), wasm::u8x16_extract_lane::<2>(value), wasm::u8x16_extract_lane::<3>(value), wasm::u8x16_extract_lane::<4>(value), wasm::u8x16_extract_lane::<5>(value), wasm::u8x16_extract_lane::<6>(value), wasm::u8x16_extract_lane::<7>(value), wasm::u8x16_extract_lane::<8>(value), wasm::u8x16_extract_lane::<9>(value), wasm::u8x16_extract_lane::<10>(value), wasm::u8x16_extract_lane::<11>(value), wasm::u8x16_extract_lane::<12>(value), wasm::u8x16_extract_lane::<13>(value), wasm::u8x16_extract_lane::<14>(value), wasm::u8x16_extract_lane::<15>(value)]) } #[inline] @@ -54,163 +311,163 @@ impl Value128 { self.0.to_le_bytes() } - #[rustfmt::skip] #[inline] - pub const fn as_i8x16(self) -> [i8; 16] { + #[rustfmt::skip] + const fn as_i8x16(self) -> [i8; 16] { let b = self.to_le_bytes(); [b[0] as i8, b[1] as i8, b[2] as i8, b[3] as i8, b[4] as i8, b[5] as i8, b[6] as i8, b[7] as i8, b[8] as i8, b[9] as i8, b[10] as i8, b[11] as i8, b[12] as i8, b[13] as i8, b[14] as i8, b[15] as i8] } - #[rustfmt::skip] #[inline] - pub const fn as_u8x16(self) -> [u8; 16] { + #[rustfmt::skip] + const fn as_u8x16(self) -> [u8; 16] { self.to_le_bytes() } - #[rustfmt::skip] #[inline] - pub const fn from_i8x16(x: [i8; 16]) -> Self { + #[rustfmt::skip] + const fn from_i8x16(x: [i8; 16]) -> Self { Self::from_le_bytes([x[0] as u8, x[1] as u8, x[2] as u8, x[3] as u8, x[4] as u8, x[5] as u8, x[6] as u8, x[7] as u8, x[8] as u8, x[9] as u8, x[10] as u8, x[11] as u8, x[12] as u8, x[13] as u8, x[14] as u8, x[15] as u8]) } - #[rustfmt::skip] #[inline] - pub const fn from_u8x16(x: [u8; 16]) -> Self { + #[rustfmt::skip] + const fn from_u8x16(x: [u8; 16]) -> Self { Self::from_le_bytes(x) } - #[rustfmt::skip] #[inline] - pub const fn as_i16x8(self) -> [i16; 8] { + #[rustfmt::skip] + const fn as_i16x8(self) -> [i16; 8] { let b = self.to_le_bytes(); [i16::from_le_bytes([b[0], b[1]]), i16::from_le_bytes([b[2], b[3]]), i16::from_le_bytes([b[4], b[5]]), i16::from_le_bytes([b[6], b[7]]), i16::from_le_bytes([b[8], b[9]]), i16::from_le_bytes([b[10], b[11]]), i16::from_le_bytes([b[12], b[13]]), i16::from_le_bytes([b[14], b[15]])] } - #[rustfmt::skip] #[inline] - pub const fn as_u16x8(self) -> [u16; 8] { + #[rustfmt::skip] + const fn as_u16x8(self) -> [u16; 8] { let b = self.to_le_bytes(); [u16::from_le_bytes([b[0], b[1]]), u16::from_le_bytes([b[2], b[3]]), u16::from_le_bytes([b[4], b[5]]), u16::from_le_bytes([b[6], b[7]]), u16::from_le_bytes([b[8], b[9]]), u16::from_le_bytes([b[10], b[11]]), u16::from_le_bytes([b[12], b[13]]), u16::from_le_bytes([b[14], b[15]])] } - #[rustfmt::skip] #[inline] - pub const fn from_i16x8(x: [i16; 8]) -> Self { + #[rustfmt::skip] + const fn from_i16x8(x: [i16; 8]) -> Self { Self::from_le_bytes([x[0].to_le_bytes()[0], x[0].to_le_bytes()[1], x[1].to_le_bytes()[0], x[1].to_le_bytes()[1], x[2].to_le_bytes()[0], x[2].to_le_bytes()[1], x[3].to_le_bytes()[0], x[3].to_le_bytes()[1], x[4].to_le_bytes()[0], x[4].to_le_bytes()[1], x[5].to_le_bytes()[0], x[5].to_le_bytes()[1], x[6].to_le_bytes()[0], x[6].to_le_bytes()[1], x[7].to_le_bytes()[0], x[7].to_le_bytes()[1]]) } - #[rustfmt::skip] #[inline] - pub const fn from_u16x8(x: [u16; 8]) -> Self { + #[rustfmt::skip] + const fn from_u16x8(x: [u16; 8]) -> Self { Self::from_le_bytes([x[0].to_le_bytes()[0], x[0].to_le_bytes()[1], x[1].to_le_bytes()[0], x[1].to_le_bytes()[1], x[2].to_le_bytes()[0], x[2].to_le_bytes()[1], x[3].to_le_bytes()[0], x[3].to_le_bytes()[1], x[4].to_le_bytes()[0], x[4].to_le_bytes()[1], x[5].to_le_bytes()[0], x[5].to_le_bytes()[1], x[6].to_le_bytes()[0], x[6].to_le_bytes()[1], x[7].to_le_bytes()[0], x[7].to_le_bytes()[1]]) } - #[rustfmt::skip] #[inline] - pub const fn as_i32x4(self) -> [i32; 4] { + #[rustfmt::skip] + const fn as_i32x4(self) -> [i32; 4] { let b = self.to_le_bytes(); [i32::from_le_bytes([b[0], b[1], b[2], b[3]]), i32::from_le_bytes([b[4], b[5], b[6], b[7]]), i32::from_le_bytes([b[8], b[9], b[10], b[11]]), i32::from_le_bytes([b[12], b[13], b[14], b[15]])] } - #[rustfmt::skip] #[inline] - pub const fn as_u32x4(self) -> [u32; 4] { + #[rustfmt::skip] + const fn as_u32x4(self) -> [u32; 4] { let b = self.to_le_bytes(); [u32::from_le_bytes([b[0], b[1], b[2], b[3]]), u32::from_le_bytes([b[4], b[5], b[6], b[7]]), u32::from_le_bytes([b[8], b[9], b[10], b[11]]), u32::from_le_bytes([b[12], b[13], b[14], b[15]])] } - #[rustfmt::skip] #[inline] - pub const fn as_f32x4(self) -> [f32; 4] { + #[rustfmt::skip] + const fn as_f32x4(self) -> [f32; 4] { let b = self.to_le_bytes(); [f32::from_bits(u32::from_le_bytes([b[0], b[1], b[2], b[3]])), f32::from_bits(u32::from_le_bytes([b[4], b[5], b[6], b[7]])), f32::from_bits(u32::from_le_bytes([b[8], b[9], b[10], b[11]])), f32::from_bits(u32::from_le_bytes([b[12], b[13], b[14], b[15]]))] } - #[rustfmt::skip] #[inline] + #[rustfmt::skip] pub const fn from_i32x4(x: [i32; 4]) -> Self { Self::from_le_bytes([x[0].to_le_bytes()[0], x[0].to_le_bytes()[1], x[0].to_le_bytes()[2], x[0].to_le_bytes()[3], x[1].to_le_bytes()[0], x[1].to_le_bytes()[1], x[1].to_le_bytes()[2], x[1].to_le_bytes()[3], x[2].to_le_bytes()[0], x[2].to_le_bytes()[1], x[2].to_le_bytes()[2], x[2].to_le_bytes()[3], x[3].to_le_bytes()[0], x[3].to_le_bytes()[1], x[3].to_le_bytes()[2], x[3].to_le_bytes()[3]]) } - #[rustfmt::skip] #[inline] - pub const fn from_u32x4(x: [u32; 4]) -> Self { + #[rustfmt::skip] + const fn from_u32x4(x: [u32; 4]) -> Self { Self::from_le_bytes([x[0].to_le_bytes()[0], x[0].to_le_bytes()[1], x[0].to_le_bytes()[2], x[0].to_le_bytes()[3], x[1].to_le_bytes()[0], x[1].to_le_bytes()[1], x[1].to_le_bytes()[2], x[1].to_le_bytes()[3], x[2].to_le_bytes()[0], x[2].to_le_bytes()[1], x[2].to_le_bytes()[2], x[2].to_le_bytes()[3], x[3].to_le_bytes()[0], x[3].to_le_bytes()[1], x[3].to_le_bytes()[2], x[3].to_le_bytes()[3]]) } - #[rustfmt::skip] #[inline] - pub const fn from_f32x4(x: [f32; 4]) -> Self { + #[rustfmt::skip] + const fn from_f32x4(x: [f32; 4]) -> Self { Self::from_le_bytes([x[0].to_bits().to_le_bytes()[0], x[0].to_bits().to_le_bytes()[1], x[0].to_bits().to_le_bytes()[2], x[0].to_bits().to_le_bytes()[3], x[1].to_bits().to_le_bytes()[0], x[1].to_bits().to_le_bytes()[1], x[1].to_bits().to_le_bytes()[2], x[1].to_bits().to_le_bytes()[3], x[2].to_bits().to_le_bytes()[0], x[2].to_bits().to_le_bytes()[1], x[2].to_bits().to_le_bytes()[2], x[2].to_bits().to_le_bytes()[3], x[3].to_bits().to_le_bytes()[0], x[3].to_bits().to_le_bytes()[1], x[3].to_bits().to_le_bytes()[2], x[3].to_bits().to_le_bytes()[3]]) } - #[rustfmt::skip] #[inline] - pub const fn as_i64x2(self) -> [i64; 2] { + #[rustfmt::skip] + const fn as_i64x2(self) -> [i64; 2] { let b = self.to_le_bytes(); [i64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]), i64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]])] } - #[rustfmt::skip] #[inline] - pub const fn as_u64x2(self) -> [u64; 2] { + #[rustfmt::skip] + const fn as_u64x2(self) -> [u64; 2] { let b = self.to_le_bytes(); [u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]), u64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]])] } - #[rustfmt::skip] #[inline] - pub const fn as_f64x2(self) -> [f64; 2] { + #[rustfmt::skip] + const fn as_f64x2(self) -> [f64; 2] { let b = self.to_le_bytes(); [f64::from_bits(u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])), f64::from_bits(u64::from_le_bytes([b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]))] } - #[rustfmt::skip] #[inline] + #[rustfmt::skip] pub const fn from_i64x2(x: [i64; 2]) -> Self { Self::from_le_bytes([x[0].to_le_bytes()[0], x[0].to_le_bytes()[1], x[0].to_le_bytes()[2], x[0].to_le_bytes()[3], x[0].to_le_bytes()[4], x[0].to_le_bytes()[5], x[0].to_le_bytes()[6], x[0].to_le_bytes()[7], x[1].to_le_bytes()[0], x[1].to_le_bytes()[1], x[1].to_le_bytes()[2], x[1].to_le_bytes()[3], x[1].to_le_bytes()[4], x[1].to_le_bytes()[5], x[1].to_le_bytes()[6], x[1].to_le_bytes()[7]]) } - #[rustfmt::skip] #[inline] - pub const fn from_u64x2(x: [u64; 2]) -> Self { + #[rustfmt::skip] + const fn from_u64x2(x: [u64; 2]) -> Self { Self::from_le_bytes([x[0].to_le_bytes()[0], x[0].to_le_bytes()[1], x[0].to_le_bytes()[2], x[0].to_le_bytes()[3], x[0].to_le_bytes()[4], x[0].to_le_bytes()[5], x[0].to_le_bytes()[6], x[0].to_le_bytes()[7], x[1].to_le_bytes()[0], x[1].to_le_bytes()[1], x[1].to_le_bytes()[2], x[1].to_le_bytes()[3], x[1].to_le_bytes()[4], x[1].to_le_bytes()[5], x[1].to_le_bytes()[6], x[1].to_le_bytes()[7]]) } - #[rustfmt::skip] #[inline] - pub const fn from_f64x2(x: [f64; 2]) -> Self { + #[rustfmt::skip] + const fn from_f64x2(x: [f64; 2]) -> Self { Self::from_le_bytes([x[0].to_bits().to_le_bytes()[0], x[0].to_bits().to_le_bytes()[1], x[0].to_bits().to_le_bytes()[2], x[0].to_bits().to_le_bytes()[3], x[0].to_bits().to_le_bytes()[4], x[0].to_bits().to_le_bytes()[5], x[0].to_bits().to_le_bytes()[6], x[0].to_bits().to_le_bytes()[7], x[1].to_bits().to_le_bytes()[0], x[1].to_bits().to_le_bytes()[1], x[1].to_bits().to_le_bytes()[2], x[1].to_bits().to_le_bytes()[3], x[1].to_bits().to_le_bytes()[4], x[1].to_bits().to_le_bytes()[5], x[1].to_bits().to_le_bytes()[6], x[1].to_bits().to_le_bytes()[7]]) } #[inline] fn map_f32x4(self, mut op: impl FnMut(f32) -> f32) -> Self { let lanes = self.as_f32x4(); - Self::from_f32x4([op(lanes[0]), op(lanes[1]), op(lanes[2]), op(lanes[3])]) + Self::from_f32x4(core::array::from_fn(|i| op(lanes[i]))) } #[inline] fn zip_f32x4(self, rhs: Self, mut op: impl FnMut(f32, f32) -> f32) -> Self { let a = self.as_f32x4(); let b = rhs.as_f32x4(); - Self::from_f32x4([op(a[0], b[0]), op(a[1], b[1]), op(a[2], b[2]), op(a[3], b[3])]) + Self::from_f32x4(core::array::from_fn(|i| op(a[i], b[i]))) } #[inline] fn map_f64x2(self, mut op: impl FnMut(f64) -> f64) -> Self { let lanes = self.as_f64x2(); - Self::from_f64x2([op(lanes[0]), op(lanes[1])]) + Self::from_f64x2(core::array::from_fn(|i| op(lanes[i]))) } #[inline] fn zip_f64x2(self, rhs: Self, mut op: impl FnMut(f64, f64) -> f64) -> Self { let a = self.as_f64x2(); let b = rhs.as_f64x2(); - Self::from_f64x2([op(a[0], b[0]), op(a[1], b[1])]) + Self::from_f64x2(core::array::from_fn(|i| op(a[i], b[i]))) } #[inline] - pub const fn reduce_or(self) -> u8 { + const fn reduce_or(self) -> u8 { let mut result = 0u8; let bytes = self.to_le_bytes(); let mut i = 0; @@ -224,70 +481,52 @@ impl Value128 { #[doc(alias = "v128.any_true")] pub fn v128_any_true(self) -> bool { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return wasm::v128_any_true(self.to_wasm_v128()); - } + return wasm::v128_any_true(self.to_wasm_v128()); self.reduce_or() != 0 } #[doc(alias = "v128.not")] pub fn v128_not(self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::v128_not(self.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::v128_not(self.to_wasm_v128())); Self(!self.0) } #[doc(alias = "v128.and")] pub fn v128_and(self, rhs: Self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::v128_and(self.to_wasm_v128(), rhs.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::v128_and(self.to_wasm_v128(), rhs.to_wasm_v128())); Self(self.0 & rhs.0) } #[doc(alias = "v128.andnot")] pub fn v128_andnot(self, rhs: Self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::v128_andnot(self.to_wasm_v128(), rhs.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::v128_andnot(self.to_wasm_v128(), rhs.to_wasm_v128())); Self(self.0 & !rhs.0) } #[doc(alias = "v128.or")] pub fn v128_or(self, rhs: Self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::v128_or(self.to_wasm_v128(), rhs.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::v128_or(self.to_wasm_v128(), rhs.to_wasm_v128())); Self(self.0 | rhs.0) } #[doc(alias = "v128.xor")] pub fn v128_xor(self, rhs: Self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::v128_xor(self.to_wasm_v128(), rhs.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::v128_xor(self.to_wasm_v128(), rhs.to_wasm_v128())); Self(self.0 ^ rhs.0) } #[doc(alias = "v128.bitselect")] pub fn v128_bitselect(v1: Self, v2: Self, c: Self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::v128_bitselect(v1.to_wasm_v128(), v2.to_wasm_v128(), c.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::v128_bitselect(v1.to_wasm_v128(), v2.to_wasm_v128(), c.to_wasm_v128())); Self((v1.0 & c.0) | (v2.0 & !c.0)) } - pub fn swizzle(self, s: Self) -> Self { - self.i8x16_swizzle(s) - } - #[doc(alias = "v128.load8x8_s")] pub const fn v128_load8x8_s(src: [u8; 8]) -> Self { Self::from_i16x8([ @@ -355,9 +594,7 @@ impl Value128 { #[doc(alias = "i8x16.swizzle")] pub fn i8x16_swizzle(self, s: Self) -> Self { #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_swizzle(self.to_wasm_v128(), s.to_wasm_v128())); - } + return Self::from_wasm_v128(wasm::i8x16_swizzle(self.to_wasm_v128(), s.to_wasm_v128())); let a_bytes = self.to_le_bytes(); let s_bytes = s.to_le_bytes(); let mut result_bytes = [0u8; 16]; @@ -382,92 +619,6 @@ impl Value128 { Self::from_le_bytes(result_bytes) } - pub const fn extend_8_i8(src: i8) -> Self { - let mut result_bytes = [0u8; 16]; - let mut i = 0; - while i < 8 { - result_bytes[i * 2] = src as u8; - result_bytes[i * 2 + 1] = if src < 0 { 0xFF } else { 0x00 }; - i += 1; - } - Self::from_le_bytes(result_bytes) - } - - pub const fn extend_8_u8(src: u8) -> Self { - let mut result_bytes = [0u8; 16]; - let mut i = 0; - while i < 8 { - result_bytes[i * 2] = src; - result_bytes[i * 2 + 1] = 0x00; - i += 1; - } - Self::from_le_bytes(result_bytes) - } - - pub const fn extend_4_i16(src: i16) -> Self { - let mut result_bytes = [0u8; 16]; - let mut i = 0; - while i < 4 { - let bytes = src.to_le_bytes(); - result_bytes[i * 4] = bytes[0]; - result_bytes[i * 4 + 1] = bytes[1]; - result_bytes[i * 4 + 2] = if src < 0 { 0xFF } else { 0x00 }; - result_bytes[i * 4 + 3] = if src < 0 { 0xFF } else { 0x00 }; - i += 1; - } - Self::from_le_bytes(result_bytes) - } - - pub const fn extend_4_u16(src: u16) -> Self { - let mut result_bytes = [0u8; 16]; - let mut i = 0; - while i < 4 { - let bytes = src.to_le_bytes(); - result_bytes[i * 4] = bytes[0]; - result_bytes[i * 4 + 1] = bytes[1]; - result_bytes[i * 4 + 2] = 0x00; - result_bytes[i * 4 + 3] = 0x00; - i += 1; - } - Self::from_le_bytes(result_bytes) - } - - pub const fn extend_2_i32(src: i32) -> Self { - let mut result_bytes = [0u8; 16]; - let mut i = 0; - while i < 2 { - let bytes = src.to_le_bytes(); - result_bytes[i * 8] = bytes[0]; - result_bytes[i * 8 + 1] = bytes[1]; - result_bytes[i * 8 + 2] = bytes[2]; - result_bytes[i * 8 + 3] = bytes[3]; - result_bytes[i * 8 + 4] = if src < 0 { 0xFF } else { 0x00 }; - result_bytes[i * 8 + 5] = if src < 0 { 0xFF } else { 0x00 }; - result_bytes[i * 8 + 6] = if src < 0 { 0xFF } else { 0x00 }; - result_bytes[i * 8 + 7] = if src < 0 { 0xFF } else { 0x00 }; - i += 1; - } - Self::from_le_bytes(result_bytes) - } - - pub const fn extend_2_u32(src: u32) -> Self { - let mut result_bytes = [0u8; 16]; - let mut i = 0; - while i < 2 { - let bytes = src.to_le_bytes(); - result_bytes[i * 8] = bytes[0]; - result_bytes[i * 8 + 1] = bytes[1]; - result_bytes[i * 8 + 2] = bytes[2]; - result_bytes[i * 8 + 3] = bytes[3]; - result_bytes[i * 8 + 4] = 0x00; - result_bytes[i * 8 + 5] = 0x00; - result_bytes[i * 8 + 6] = 0x00; - result_bytes[i * 8 + 7] = 0x00; - i += 1; - } - Self::from_le_bytes(result_bytes) - } - pub const fn splat_i8(src: i8) -> Self { let mut result_bytes = [0u8; 16]; let byte = src as u8; @@ -509,1872 +660,301 @@ impl Value128 { self.replace_lane_bytes::<8>(lane, value.to_bits().to_le_bytes(), 2) } - #[doc(alias = "i8x16.all_true")] - pub const fn i8x16_all_true(self) -> bool { - let lanes = self.as_i8x16(); + simd_all_true!(i8x16_all_true, "i8x16.all_true", as_i8x16, 16); + simd_all_true!(i16x8_all_true, "i16x8.all_true", as_i16x8, 8); + simd_all_true!(i32x4_all_true, "i32x4.all_true", as_i32x4, 4); + simd_all_true!(i64x2_all_true, "i64x2.all_true", as_i64x2, 2); + + simd_bitmask!(i8x16_bitmask, "i8x16.bitmask", as_i8x16, 16); + simd_bitmask!(i16x8_bitmask, "i16x8.bitmask", as_i16x8, 8); + simd_bitmask!(i32x4_bitmask, "i32x4.bitmask", as_i32x4, 4); + simd_bitmask!(i64x2_bitmask, "i64x2.bitmask", as_i64x2, 2); + + #[doc(alias = "i8x16.popcnt")] + pub const fn i8x16_popcnt(self) -> Self { + let lanes = self.as_u8x16(); + let mut out = [0u8; 16]; let mut i = 0; while i < 16 { - if lanes[i] == 0 { - return false; - } + out[i] = lanes[i].count_ones() as u8; i += 1; } - true + Self::from_u8x16(out) } - #[doc(alias = "i16x8.all_true")] - pub const fn i16x8_all_true(self) -> bool { - let lanes = self.as_i16x8(); + simd_shift_left!(i8x16_shl, "i8x16.shl", i8, 16, as_i8x16, from_i8x16, 7); + simd_shift_left!(i16x8_shl, "i16x8.shl", i16, 8, as_i16x8, from_i16x8, 15); + simd_shift_left!(i32x4_shl, "i32x4.shl", i32, 4, as_i32x4, from_i32x4, 31); + simd_shift_left!(i64x2_shl, "i64x2.shl", i64, 2, as_i64x2, from_i64x2, 63); + + simd_shift_right!(i8x16_shr_s, "i8x16.shr_s", i8, 16, as_i8x16, from_i8x16, 7); + simd_shift_right!(i16x8_shr_s, "i16x8.shr_s", i16, 8, as_i16x8, from_i16x8, 15); + simd_shift_right!(i32x4_shr_s, "i32x4.shr_s", i32, 4, as_i32x4, from_i32x4, 31); + simd_shift_right!(i64x2_shr_s, "i64x2.shr_s", i64, 2, as_i64x2, from_i64x2, 63); + + simd_shift_right!(i8x16_shr_u, "i8x16.shr_u", u8, 16, as_u8x16, from_u8x16, 7); + simd_shift_right!(i16x8_shr_u, "i16x8.shr_u", u16, 8, as_u16x8, from_u16x8, 15); + simd_shift_right!(i32x4_shr_u, "i32x4.shr_u", u32, 4, as_u32x4, from_u32x4, 31); + simd_shift_right!(i64x2_shr_u, "i64x2.shr_u", u64, 2, as_u64x2, from_u64x2, 63); + + simd_wrapping_binop!(i8x16_add, "i8x16.add", i8x16_add, i8, 16, as_i8x16, from_i8x16, wrapping_add); + simd_wrapping_binop!(i16x8_add, "i16x8.add", i16x8_add, i16, 8, as_i16x8, from_i16x8, wrapping_add); + simd_wrapping_binop!(i32x4_add, "i32x4.add", i32x4_add, i32, 4, as_i32x4, from_i32x4, wrapping_add); + simd_wrapping_binop!(i64x2_add, "i64x2.add", i64x2_add, i64, 2, as_i64x2, from_i64x2, wrapping_add); + simd_wrapping_binop!(i8x16_sub, "i8x16.sub", i8x16_sub, i8, 16, as_i8x16, from_i8x16, wrapping_sub); + simd_wrapping_binop!(i16x8_sub, "i16x8.sub", i16x8_sub, i16, 8, as_i16x8, from_i16x8, wrapping_sub); + simd_wrapping_binop!(i32x4_sub, "i32x4.sub", i32x4_sub, i32, 4, as_i32x4, from_i32x4, wrapping_sub); + simd_wrapping_binop!(i64x2_sub, "i64x2.sub", i64x2_sub, i64, 2, as_i64x2, from_i64x2, wrapping_sub); + simd_wrapping_binop!(i16x8_mul, "i16x8.mul", i16x8_mul, i16, 8, as_i16x8, from_i16x8, wrapping_mul); + simd_wrapping_binop!(i32x4_mul, "i32x4.mul", i32x4_mul, i32, 4, as_i32x4, from_i32x4, wrapping_mul); + simd_wrapping_binop!(i64x2_mul, "i64x2.mul", i64x2_mul, i64, 2, as_i64x2, from_i64x2, wrapping_mul); + + simd_sat_binop!(i8x16_add_sat_s, "i8x16.add_sat_s", i8x16_add_sat, i8, 16, as_i8x16, from_i8x16, saturating_add); + simd_sat_binop!(i16x8_add_sat_s, "i16x8.add_sat_s", i16x8_add_sat, i16, 8, as_i16x8, from_i16x8, saturating_add); + simd_sat_binop!(i8x16_add_sat_u, "i8x16.add_sat_u", u8x16_add_sat, u8, 16, as_u8x16, from_u8x16, saturating_add); + simd_sat_binop!(i16x8_add_sat_u, "i16x8.add_sat_u", u16x8_add_sat, u16, 8, as_u16x8, from_u16x8, saturating_add); + simd_sat_binop!(i8x16_sub_sat_s, "i8x16.sub_sat_s", i8x16_sub_sat, i8, 16, as_i8x16, from_i8x16, saturating_sub); + simd_sat_binop!(i16x8_sub_sat_s, "i16x8.sub_sat_s", i16x8_sub_sat, i16, 8, as_i16x8, from_i16x8, saturating_sub); + simd_sat_binop!(i8x16_sub_sat_u, "i8x16.sub_sat_u", u8x16_sub_sat, u8, 16, as_u8x16, from_u8x16, saturating_sub); + simd_sat_binop!(i16x8_sub_sat_u, "i16x8.sub_sat_u", u16x8_sub_sat, u16, 8, as_u16x8, from_u16x8, saturating_sub); + + simd_avgr_u!(i8x16_avgr_u, "i8x16.avgr_u", u8x16_avgr, u8, u16, 16, as_u8x16, from_u8x16); + simd_avgr_u!(i16x8_avgr_u, "i16x8.avgr_u", u16x8_avgr, u16, u32, 8, as_u16x8, from_u16x8); + + #[doc(alias = "i8x16.narrow_i16x8_s")] + pub const fn i8x16_narrow_i16x8_s(a: Self, b: Self) -> Self { + let av = a.as_i16x8(); + let bv = b.as_i16x8(); + let mut out = [0i8; 16]; let mut i = 0; while i < 8 { - if lanes[i] == 0 { - return false; - } + out[i] = saturate_i16_to_i8(av[i]); + out[i + 8] = saturate_i16_to_i8(bv[i]); i += 1; } - true + Self::from_i8x16(out) } - #[doc(alias = "i32x4.all_true")] - pub const fn i32x4_all_true(self) -> bool { - let lanes = self.as_i32x4(); + #[doc(alias = "i8x16.narrow_i16x8_u")] + pub const fn i8x16_narrow_i16x8_u(a: Self, b: Self) -> Self { + let av = a.as_i16x8(); + let bv = b.as_i16x8(); + let mut out = [0u8; 16]; let mut i = 0; - while i < 4 { - if lanes[i] == 0 { - return false; - } + while i < 8 { + out[i] = saturate_i16_to_u8(av[i]); + out[i + 8] = saturate_i16_to_u8(bv[i]); i += 1; } - true + Self::from_u8x16(out) } - #[doc(alias = "i64x2.all_true")] - pub const fn i64x2_all_true(self) -> bool { - let lanes = self.as_i64x2(); + #[doc(alias = "i16x8.narrow_i32x4_s")] + pub const fn i16x8_narrow_i32x4_s(a: Self, b: Self) -> Self { + let av = a.as_i32x4(); + let bv = b.as_i32x4(); + let mut out = [0i16; 8]; let mut i = 0; - while i < 2 { - if lanes[i] == 0 { - return false; - } + while i < 4 { + out[i] = saturate_i32_to_i16(av[i]); + out[i + 4] = saturate_i32_to_i16(bv[i]); i += 1; } - true + Self::from_i16x8(out) } - #[doc(alias = "i8x16.bitmask")] - pub const fn i8x16_bitmask(self) -> u32 { - let lanes = self.as_i8x16(); - let mut mask = 0u32; + #[doc(alias = "i16x8.narrow_i32x4_u")] + pub const fn i16x8_narrow_i32x4_u(a: Self, b: Self) -> Self { + let av = a.as_i32x4(); + let bv = b.as_i32x4(); + let mut out = [0u16; 8]; let mut i = 0; - while i < 16 { - mask |= ((lanes[i] < 0) as u32) << i; + while i < 4 { + out[i] = saturate_i32_to_u16(av[i]); + out[i + 4] = saturate_i32_to_u16(bv[i]); i += 1; } - mask + Self::from_u16x8(out) } - #[doc(alias = "i16x8.bitmask")] - pub const fn i16x8_bitmask(self) -> u32 { - let lanes = self.as_i16x8(); - let mut mask = 0u32; + #[doc(alias = "i16x8.extadd_pairwise_i8x16_s")] + pub const fn i16x8_extadd_pairwise_i8x16_s(self) -> Self { + let lanes = self.as_i8x16(); + let mut out = [0i16; 8]; let mut i = 0; while i < 8 { - mask |= ((lanes[i] < 0) as u32) << i; + let j = i * 2; + out[i] = lanes[j] as i16 + lanes[j + 1] as i16; i += 1; } - mask + Self::from_i16x8(out) } - #[doc(alias = "i32x4.bitmask")] - pub const fn i32x4_bitmask(self) -> u32 { - let lanes = self.as_i32x4(); - let mut mask = 0u32; + #[doc(alias = "i16x8.extadd_pairwise_i8x16_u")] + pub const fn i16x8_extadd_pairwise_i8x16_u(self) -> Self { + let lanes = self.as_u8x16(); + let mut out = [0u16; 8]; let mut i = 0; - while i < 4 { - mask |= ((lanes[i] < 0) as u32) << i; + while i < 8 { + let j = i * 2; + out[i] = lanes[j] as u16 + lanes[j + 1] as u16; i += 1; } - mask + Self::from_u16x8(out) } - #[doc(alias = "i64x2.bitmask")] - pub const fn i64x2_bitmask(self) -> u32 { - let lanes = self.as_i64x2(); - let mut mask = 0u32; + #[doc(alias = "i32x4.extadd_pairwise_i16x8_s")] + pub const fn i32x4_extadd_pairwise_i16x8_s(self) -> Self { + let lanes = self.as_i16x8(); + let mut out = [0i32; 4]; let mut i = 0; - while i < 2 { - mask |= ((lanes[i] < 0) as u32) << i; + while i < 4 { + let j = i * 2; + out[i] = lanes[j] as i32 + lanes[j + 1] as i32; i += 1; } - mask + Self::from_i32x4(out) } - #[doc(alias = "i8x16.popcnt")] - pub const fn i8x16_popcnt(self) -> Self { - let lanes = self.as_u8x16(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = lanes[i].count_ones() as u8; - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i8x16.shl")] - pub const fn i8x16_shl(self, shift: u32) -> Self { - let lanes = self.as_i8x16(); - let s = shift & 7; - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = lanes[i].wrapping_shl(s); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.shl")] - pub const fn i16x8_shl(self, shift: u32) -> Self { - let lanes = self.as_i16x8(); - let s = shift & 15; - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = lanes[i].wrapping_shl(s); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.shl")] - pub const fn i32x4_shl(self, shift: u32) -> Self { - let lanes = self.as_i32x4(); - let s = shift & 31; - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = lanes[i].wrapping_shl(s); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.shl")] - pub const fn i64x2_shl(self, shift: u32) -> Self { - let lanes = self.as_i64x2(); - let s = shift & 63; - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = lanes[i].wrapping_shl(s); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.shr_s")] - pub const fn i8x16_shr_s(self, shift: u32) -> Self { - let lanes = self.as_i8x16(); - let s = shift & 7; - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.shr_s")] - pub const fn i16x8_shr_s(self, shift: u32) -> Self { - let lanes = self.as_i16x8(); - let s = shift & 15; - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.shr_s")] - pub const fn i32x4_shr_s(self, shift: u32) -> Self { - let lanes = self.as_i32x4(); - let s = shift & 31; - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.shr_s")] - pub const fn i64x2_shr_s(self, shift: u32) -> Self { - let lanes = self.as_i64x2(); - let s = shift & 63; - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.shr_u")] - pub const fn i8x16_shr_u(self, shift: u32) -> Self { - let lanes = self.as_u8x16(); - let s = shift & 7; - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.shr_u")] - pub const fn i16x8_shr_u(self, shift: u32) -> Self { - let lanes = self.as_u16x8(); - let s = shift & 15; - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i32x4.shr_u")] - pub const fn i32x4_shr_u(self, shift: u32) -> Self { - let lanes = self.as_u32x4(); - let s = shift & 31; - let mut out = [0u32; 4]; - let mut i = 0; - while i < 4 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_u32x4(out) - } - - #[doc(alias = "i64x2.shr_u")] - pub const fn i64x2_shr_u(self, shift: u32) -> Self { - let lanes = self.as_u64x2(); - let s = shift & 63; - let mut out = [0u64; 2]; - let mut i = 0; - while i < 2 { - out[i] = lanes[i] >> s; - i += 1; - } - Self::from_u64x2(out) - } - - #[doc(alias = "i8x16.add")] - pub fn i8x16_add(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_add(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].wrapping_add(b[i]); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.add")] - pub fn i16x8_add(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_add(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].wrapping_add(b[i]); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.add")] - pub fn i32x4_add(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_add(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = a[i].wrapping_add(b[i]); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.add")] - pub fn i64x2_add(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_add(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = a[i].wrapping_add(b[i]); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.sub")] - pub fn i8x16_sub(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_sub(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].wrapping_sub(b[i]); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.sub")] - pub fn i16x8_sub(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_sub(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].wrapping_sub(b[i]); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.sub")] - pub fn i32x4_sub(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_sub(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = a[i].wrapping_sub(b[i]); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.sub")] - pub fn i64x2_sub(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_sub(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = a[i].wrapping_sub(b[i]); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i16x8.mul")] - pub fn i16x8_mul(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_mul(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].wrapping_mul(b[i]); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.mul")] - pub fn i32x4_mul(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_mul(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = a[i].wrapping_mul(b[i]); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.mul")] - pub fn i64x2_mul(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_mul(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = a[i].wrapping_mul(b[i]); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.add_sat_s")] - pub fn i8x16_add_sat_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_add_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].saturating_add(b[i]); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.add_sat_s")] - pub fn i16x8_add_sat_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_add_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].saturating_add(b[i]); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i8x16.add_sat_u")] - pub fn i8x16_add_sat_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_add_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].saturating_add(b[i]); - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.add_sat_u")] - pub fn i16x8_add_sat_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_add_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].saturating_add(b[i]); - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i8x16.sub_sat_s")] - pub fn i8x16_sub_sat_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_sub_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].saturating_sub(b[i]); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.sub_sat_s")] - pub fn i16x8_sub_sat_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_sub_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].saturating_sub(b[i]); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i8x16.sub_sat_u")] - pub fn i8x16_sub_sat_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_sub_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].saturating_sub(b[i]); - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.sub_sat_u")] - pub fn i16x8_sub_sat_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_sub_sat(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].saturating_sub(b[i]); - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i8x16.avgr_u")] - pub fn i8x16_avgr_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_avgr(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = ((a[i] as u16 + b[i] as u16 + 1) >> 1) as u8; - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.avgr_u")] - pub fn i16x8_avgr_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_avgr(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = ((a[i] as u32 + b[i] as u32 + 1) >> 1) as u16; - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i8x16.narrow_i16x8_s")] - pub const fn i8x16_narrow_i16x8_s(a: Self, b: Self) -> Self { - let av = a.as_i16x8(); - let bv = b.as_i16x8(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 8 { - out[i] = saturate_i16_to_i8(av[i]); - out[i + 8] = saturate_i16_to_i8(bv[i]); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i8x16.narrow_i16x8_u")] - pub const fn i8x16_narrow_i16x8_u(a: Self, b: Self) -> Self { - let av = a.as_i16x8(); - let bv = b.as_i16x8(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 8 { - out[i] = saturate_i16_to_u8(av[i]); - out[i + 8] = saturate_i16_to_u8(bv[i]); - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.narrow_i32x4_s")] - pub const fn i16x8_narrow_i32x4_s(a: Self, b: Self) -> Self { - let av = a.as_i32x4(); - let bv = b.as_i32x4(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 4 { - out[i] = saturate_i32_to_i16(av[i]); - out[i + 4] = saturate_i32_to_i16(bv[i]); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i16x8.narrow_i32x4_u")] - pub const fn i16x8_narrow_i32x4_u(a: Self, b: Self) -> Self { - let av = a.as_i32x4(); - let bv = b.as_i32x4(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 4 { - out[i] = saturate_i32_to_u16(av[i]); - out[i + 4] = saturate_i32_to_u16(bv[i]); - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i16x8.extadd_pairwise_i8x16_s")] - pub const fn i16x8_extadd_pairwise_i8x16_s(self) -> Self { - let lanes = self.as_i8x16(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - let j = i * 2; - out[i] = lanes[j] as i16 + lanes[j + 1] as i16; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i16x8.extadd_pairwise_i8x16_u")] - pub const fn i16x8_extadd_pairwise_i8x16_u(self) -> Self { - let lanes = self.as_u8x16(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - let j = i * 2; - out[i] = lanes[j] as u16 + lanes[j + 1] as u16; - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i32x4.extadd_pairwise_i16x8_s")] - pub const fn i32x4_extadd_pairwise_i16x8_s(self) -> Self { - let lanes = self.as_i16x8(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - let j = i * 2; - out[i] = lanes[j] as i32 + lanes[j + 1] as i32; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i32x4.extadd_pairwise_i16x8_u")] - pub const fn i32x4_extadd_pairwise_i16x8_u(self) -> Self { - let lanes = self.as_u16x8(); - let mut out = [0u32; 4]; - let mut i = 0; - while i < 4 { - let j = i * 2; - out[i] = lanes[j] as u32 + lanes[j + 1] as u32; - i += 1; - } - Self::from_u32x4(out) - } - - #[doc(alias = "i16x8.extend_low_i8x16_s")] - pub const fn i16x8_extend_low_i8x16_s(self) -> Self { - let lanes = self.as_i8x16(); - Self::from_i16x8([ - lanes[0] as i16, - lanes[1] as i16, - lanes[2] as i16, - lanes[3] as i16, - lanes[4] as i16, - lanes[5] as i16, - lanes[6] as i16, - lanes[7] as i16, - ]) - } - - #[doc(alias = "i16x8.extend_low_i8x16_u")] - pub const fn i16x8_extend_low_i8x16_u(self) -> Self { - let lanes = self.as_u8x16(); - Self::from_u16x8([ - lanes[0] as u16, - lanes[1] as u16, - lanes[2] as u16, - lanes[3] as u16, - lanes[4] as u16, - lanes[5] as u16, - lanes[6] as u16, - lanes[7] as u16, - ]) - } - - #[doc(alias = "i16x8.extend_high_i8x16_s")] - pub const fn i16x8_extend_high_i8x16_s(self) -> Self { - let lanes = self.as_i8x16(); - Self::from_i16x8([ - lanes[8] as i16, - lanes[9] as i16, - lanes[10] as i16, - lanes[11] as i16, - lanes[12] as i16, - lanes[13] as i16, - lanes[14] as i16, - lanes[15] as i16, - ]) - } - - #[doc(alias = "i16x8.extend_high_i8x16_u")] - pub const fn i16x8_extend_high_i8x16_u(self) -> Self { - let lanes = self.as_u8x16(); - Self::from_u16x8([ - lanes[8] as u16, - lanes[9] as u16, - lanes[10] as u16, - lanes[11] as u16, - lanes[12] as u16, - lanes[13] as u16, - lanes[14] as u16, - lanes[15] as u16, - ]) - } - - #[doc(alias = "i32x4.extend_low_i16x8_s")] - pub const fn i32x4_extend_low_i16x8_s(self) -> Self { - let lanes = self.as_i16x8(); - Self::from_i32x4([lanes[0] as i32, lanes[1] as i32, lanes[2] as i32, lanes[3] as i32]) - } - - #[doc(alias = "i32x4.extend_low_i16x8_u")] - pub const fn i32x4_extend_low_i16x8_u(self) -> Self { - let lanes = self.as_u16x8(); - Self::from_u32x4([lanes[0] as u32, lanes[1] as u32, lanes[2] as u32, lanes[3] as u32]) - } - - #[doc(alias = "i32x4.extend_high_i16x8_s")] - pub const fn i32x4_extend_high_i16x8_s(self) -> Self { - let lanes = self.as_i16x8(); - Self::from_i32x4([lanes[4] as i32, lanes[5] as i32, lanes[6] as i32, lanes[7] as i32]) - } - - #[doc(alias = "i32x4.extend_high_i16x8_u")] - pub const fn i32x4_extend_high_i16x8_u(self) -> Self { - let lanes = self.as_u16x8(); - Self::from_u32x4([lanes[4] as u32, lanes[5] as u32, lanes[6] as u32, lanes[7] as u32]) - } - - #[doc(alias = "i64x2.extend_low_i32x4_s")] - pub const fn i64x2_extend_low_i32x4_s(self) -> Self { - let lanes = self.as_i32x4(); - Self::from_i64x2([lanes[0] as i64, lanes[1] as i64]) - } - - #[doc(alias = "i64x2.extend_low_i32x4_u")] - pub const fn i64x2_extend_low_i32x4_u(self) -> Self { - let lanes = self.as_u32x4(); - Self::from_u64x2([lanes[0] as u64, lanes[1] as u64]) - } - - #[doc(alias = "i64x2.extend_high_i32x4_s")] - pub const fn i64x2_extend_high_i32x4_s(self) -> Self { - let lanes = self.as_i32x4(); - Self::from_i64x2([lanes[2] as i64, lanes[3] as i64]) - } - - #[doc(alias = "i64x2.extend_high_i32x4_u")] - pub const fn i64x2_extend_high_i32x4_u(self) -> Self { - let lanes = self.as_u32x4(); - Self::from_u64x2([lanes[2] as u64, lanes[3] as u64]) - } - - #[doc(alias = "i16x8.extmul_low_i8x16_s")] - pub const fn i16x8_extmul_low_i8x16_s(self, rhs: Self) -> Self { - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = (a[i] as i16).wrapping_mul(b[i] as i16); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i16x8.extmul_low_i8x16_u")] - pub const fn i16x8_extmul_low_i8x16_u(self, rhs: Self) -> Self { - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = (a[i] as u16) * (b[i] as u16); - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i16x8.extmul_high_i8x16_s")] - pub const fn i16x8_extmul_high_i8x16_s(self, rhs: Self) -> Self { - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = (a[i + 8] as i16).wrapping_mul(b[i + 8] as i16); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i16x8.extmul_high_i8x16_u")] - pub const fn i16x8_extmul_high_i8x16_u(self, rhs: Self) -> Self { - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = (a[i + 8] as u16) * (b[i + 8] as u16); - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i32x4.extmul_low_i16x8_s")] - pub const fn i32x4_extmul_low_i16x8_s(self, rhs: Self) -> Self { - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = (a[i] as i32).wrapping_mul(b[i] as i32); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i32x4.extmul_low_i16x8_u")] - pub const fn i32x4_extmul_low_i16x8_u(self, rhs: Self) -> Self { - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u32; 4]; - let mut i = 0; - while i < 4 { - out[i] = (a[i] as u32) * (b[i] as u32); - i += 1; - } - Self::from_u32x4(out) - } - - #[doc(alias = "i32x4.extmul_high_i16x8_s")] - pub const fn i32x4_extmul_high_i16x8_s(self, rhs: Self) -> Self { - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = (a[i + 4] as i32).wrapping_mul(b[i + 4] as i32); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i32x4.extmul_high_i16x8_u")] - pub const fn i32x4_extmul_high_i16x8_u(self, rhs: Self) -> Self { - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u32; 4]; - let mut i = 0; - while i < 4 { - out[i] = (a[i + 4] as u32) * (b[i + 4] as u32); - i += 1; - } - Self::from_u32x4(out) - } - - #[doc(alias = "i64x2.extmul_low_i32x4_s")] - pub const fn i64x2_extmul_low_i32x4_s(self, rhs: Self) -> Self { - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = (a[i] as i64).wrapping_mul(b[i] as i64); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i64x2.extmul_low_i32x4_u")] - pub const fn i64x2_extmul_low_i32x4_u(self, rhs: Self) -> Self { - let a = self.as_u32x4(); - let b = rhs.as_u32x4(); - let mut out = [0u64; 2]; - let mut i = 0; - while i < 2 { - out[i] = (a[i] as u64) * (b[i] as u64); - i += 1; - } - Self::from_u64x2(out) - } - - #[doc(alias = "i64x2.extmul_high_i32x4_s")] - pub const fn i64x2_extmul_high_i32x4_s(self, rhs: Self) -> Self { - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = (a[i + 2] as i64).wrapping_mul(b[i + 2] as i64); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i64x2.extmul_high_i32x4_u")] - pub const fn i64x2_extmul_high_i32x4_u(self, rhs: Self) -> Self { - let a = self.as_u32x4(); - let b = rhs.as_u32x4(); - let mut out = [0u64; 2]; - let mut i = 0; - while i < 2 { - out[i] = (a[i + 2] as u64) * (b[i + 2] as u64); - i += 1; - } - Self::from_u64x2(out) - } - - #[doc(alias = "i16x8.q15mulr_sat_s")] - pub const fn i16x8_q15mulr_sat_s(self, rhs: Self) -> Self { - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - let r = ((a[i] as i32 * b[i] as i32) + (1 << 14)) >> 15; // 2^14: Q15 rounding - out[i] = if r > i16::MAX as i32 { - i16::MAX - } else if r < i16::MIN as i32 { - i16::MIN - } else { - r as i16 - }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.dot_i16x8_s")] - pub const fn i32x4_dot_i16x8_s(self, rhs: Self) -> Self { - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - Self::from_i32x4([ - (a[0] as i32).wrapping_mul(b[0] as i32).wrapping_add((a[1] as i32).wrapping_mul(b[1] as i32)), - (a[2] as i32).wrapping_mul(b[2] as i32).wrapping_add((a[3] as i32).wrapping_mul(b[3] as i32)), - (a[4] as i32).wrapping_mul(b[4] as i32).wrapping_add((a[5] as i32).wrapping_mul(b[5] as i32)), - (a[6] as i32).wrapping_mul(b[6] as i32).wrapping_add((a[7] as i32).wrapping_mul(b[7] as i32)), - ]) - } - - #[doc(alias = "i8x16.eq")] - pub fn i8x16_eq(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_eq(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] == b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.eq")] - pub fn i16x8_eq(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_eq(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] == b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.eq")] - pub fn i32x4_eq(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_eq(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] == b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.eq")] - pub fn i64x2_eq(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_eq(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] == b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.ne")] - pub fn i8x16_ne(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_ne(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] != b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.ne")] - pub fn i16x8_ne(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_ne(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] != b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.ne")] - pub fn i32x4_ne(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_ne(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] != b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.ne")] - pub fn i64x2_ne(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_ne(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] != b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.lt_s")] - pub fn i8x16_lt_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.lt_s")] - pub fn i16x8_lt_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.lt_s")] - pub fn i32x4_lt_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.lt_s")] - pub fn i64x2_lt_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.lt_u")] - pub fn i8x16_lt_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.lt_u")] - pub fn i16x8_lt_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.lt_u")] - pub fn i32x4_lt_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u32x4_lt(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u32x4(); - let b = rhs.as_u32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i8x16.gt_s")] - pub fn i8x16_gt_s(self, rhs: Self) -> Self { - rhs.i8x16_lt_s(self) - } - - #[doc(alias = "i16x8.gt_s")] - pub fn i16x8_gt_s(self, rhs: Self) -> Self { - rhs.i16x8_lt_s(self) - } - - #[doc(alias = "i32x4.gt_s")] - pub fn i32x4_gt_s(self, rhs: Self) -> Self { - rhs.i32x4_lt_s(self) - } - - #[doc(alias = "i64x2.gt_s")] - pub fn i64x2_gt_s(self, rhs: Self) -> Self { - rhs.i64x2_lt_s(self) - } - - #[doc(alias = "i8x16.gt_u")] - pub fn i8x16_gt_u(self, rhs: Self) -> Self { - rhs.i8x16_lt_u(self) - } - - #[doc(alias = "i16x8.gt_u")] - pub fn i16x8_gt_u(self, rhs: Self) -> Self { - rhs.i16x8_lt_u(self) - } - - #[doc(alias = "i32x4.gt_u")] - pub fn i32x4_gt_u(self, rhs: Self) -> Self { - rhs.i32x4_lt_u(self) - } - - #[doc(alias = "i8x16.le_s")] - pub fn i8x16_le_s(self, rhs: Self) -> Self { - rhs.i8x16_ge_s(self) - } - - #[doc(alias = "i16x8.le_s")] - pub fn i16x8_le_s(self, rhs: Self) -> Self { - rhs.i16x8_ge_s(self) - } - - #[doc(alias = "i32x4.le_s")] - pub fn i32x4_le_s(self, rhs: Self) -> Self { - rhs.i32x4_ge_s(self) - } - - #[doc(alias = "i64x2.le_s")] - pub fn i64x2_le_s(self, rhs: Self) -> Self { - rhs.i64x2_ge_s(self) - } - - #[doc(alias = "i8x16.le_u")] - pub fn i8x16_le_u(self, rhs: Self) -> Self { - rhs.i8x16_ge_u(self) - } - - #[doc(alias = "i16x8.le_u")] - pub fn i16x8_le_u(self, rhs: Self) -> Self { - rhs.i16x8_ge_u(self) - } - - #[doc(alias = "i32x4.le_u")] - pub fn i32x4_le_u(self, rhs: Self) -> Self { - rhs.i32x4_ge_u(self) - } - - #[doc(alias = "i8x16.ge_s")] - pub fn i8x16_ge_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.ge_s")] - pub fn i16x8_ge_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.ge_s")] - pub fn i32x4_ge_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.ge_s")] - pub fn i64x2_ge_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i64x2(); - let b = rhs.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.ge_u")] - pub fn i8x16_ge_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.ge_u")] - pub fn i16x8_ge_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.ge_u")] - pub fn i32x4_ge_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u32x4_ge(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u32x4(); - let b = rhs.as_u32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i8x16.abs")] - pub const fn i8x16_abs(self) -> Self { - let a = self.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].wrapping_abs(); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.abs")] - pub const fn i16x8_abs(self) -> Self { - let a = self.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].wrapping_abs(); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.abs")] - pub const fn i32x4_abs(self) -> Self { - let a = self.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = a[i].wrapping_abs(); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.abs")] - pub const fn i64x2_abs(self) -> Self { - let a = self.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = a[i].wrapping_abs(); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.neg")] - pub fn i8x16_neg(self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_neg(self.to_wasm_v128())); - } - let a = self.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = a[i].wrapping_neg(); - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.neg")] - pub fn i16x8_neg(self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_neg(self.to_wasm_v128())); - } - let a = self.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = a[i].wrapping_neg(); - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.neg")] - pub fn i32x4_neg(self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_neg(self.to_wasm_v128())); - } - let a = self.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = a[i].wrapping_neg(); - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i64x2.neg")] - pub fn i64x2_neg(self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i64x2_neg(self.to_wasm_v128())); - } - let a = self.as_i64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = a[i].wrapping_neg(); - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "i8x16.min_s")] - pub fn i8x16_min_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_min(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] < b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.min_s")] - pub fn i16x8_min_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_min(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] < b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.min_s")] - pub fn i32x4_min_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_min(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] < b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i8x16.min_u")] - pub fn i8x16_min_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_min(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] < b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.min_u")] - pub fn i16x8_min_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_min(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] < b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i32x4.min_u")] - pub fn i32x4_min_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u32x4_min(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u32x4(); - let b = rhs.as_u32x4(); - let mut out = [0u32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] < b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_u32x4(out) - } - - #[doc(alias = "i8x16.max_s")] - pub fn i8x16_max_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i8x16_max(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i8x16(); - let b = rhs.as_i8x16(); - let mut out = [0i8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] > b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_i8x16(out) - } - - #[doc(alias = "i16x8.max_s")] - pub fn i16x8_max_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i16x8_max(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i16x8(); - let b = rhs.as_i16x8(); - let mut out = [0i16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] > b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_i16x8(out) - } - - #[doc(alias = "i32x4.max_s")] - pub fn i32x4_max_s(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::i32x4_max(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_i32x4(); - let b = rhs.as_i32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] > b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "i8x16.max_u")] - pub fn i8x16_max_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u8x16_max(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u8x16(); - let b = rhs.as_u8x16(); - let mut out = [0u8; 16]; - let mut i = 0; - while i < 16 { - out[i] = if a[i] > b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_u8x16(out) - } - - #[doc(alias = "i16x8.max_u")] - pub fn i16x8_max_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u16x8_max(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u16x8(); - let b = rhs.as_u16x8(); - let mut out = [0u16; 8]; - let mut i = 0; - while i < 8 { - out[i] = if a[i] > b[i] { a[i] } else { b[i] }; - i += 1; - } - Self::from_u16x8(out) - } - - #[doc(alias = "i32x4.max_u")] - pub fn i32x4_max_u(self, rhs: Self) -> Self { - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] - { - return Self::from_wasm_v128(wasm::u32x4_max(self.to_wasm_v128(), rhs.to_wasm_v128())); - } - let a = self.as_u32x4(); - let b = rhs.as_u32x4(); + #[doc(alias = "i32x4.extadd_pairwise_i16x8_u")] + pub const fn i32x4_extadd_pairwise_i16x8_u(self) -> Self { + let lanes = self.as_u16x8(); let mut out = [0u32; 4]; let mut i = 0; while i < 4 { - out[i] = if a[i] > b[i] { a[i] } else { b[i] }; + let j = i * 2; + out[i] = lanes[j] as u32 + lanes[j + 1] as u32; i += 1; } Self::from_u32x4(out) } - #[doc(alias = "f32x4.eq")] - pub const fn f32x4_eq(self, rhs: Self) -> Self { - let a = self.as_f32x4(); - let b = rhs.as_f32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] == b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "f64x2.eq")] - pub const fn f64x2_eq(self, rhs: Self) -> Self { - let a = self.as_f64x2(); - let b = rhs.as_f64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] == b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "f32x4.ne")] - pub const fn f32x4_ne(self, rhs: Self) -> Self { - let a = self.as_f32x4(); - let b = rhs.as_f32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] != b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } + simd_extend_cast!(i16x8_extend_low_i8x16_s, "i16x8.extend_low_i8x16_s", as_i8x16, from_i16x8, i16, 8, 0); + simd_extend_cast!(i16x8_extend_low_i8x16_u, "i16x8.extend_low_i8x16_u", as_u8x16, from_u16x8, u16, 8, 0); + simd_extend_cast!(i16x8_extend_high_i8x16_s, "i16x8.extend_high_i8x16_s", as_i8x16, from_i16x8, i16, 8, 8); + simd_extend_cast!(i16x8_extend_high_i8x16_u, "i16x8.extend_high_i8x16_u", as_u8x16, from_u16x8, u16, 8, 8); + simd_extend_cast!(i32x4_extend_low_i16x8_s, "i32x4.extend_low_i16x8_s", as_i16x8, from_i32x4, i32, 4, 0); + simd_extend_cast!(i32x4_extend_low_i16x8_u, "i32x4.extend_low_i16x8_u", as_u16x8, from_u32x4, u32, 4, 0); + simd_extend_cast!(i32x4_extend_high_i16x8_s, "i32x4.extend_high_i16x8_s", as_i16x8, from_i32x4, i32, 4, 4); + simd_extend_cast!(i32x4_extend_high_i16x8_u, "i32x4.extend_high_i16x8_u", as_u16x8, from_u32x4, u32, 4, 4); + simd_extend_cast!(i64x2_extend_low_i32x4_s, "i64x2.extend_low_i32x4_s", as_i32x4, from_i64x2, i64, 2, 0); + simd_extend_cast!(i64x2_extend_low_i32x4_u, "i64x2.extend_low_i32x4_u", as_u32x4, from_u64x2, u64, 2, 0); + simd_extend_cast!(i64x2_extend_high_i32x4_s, "i64x2.extend_high_i32x4_s", as_i32x4, from_i64x2, i64, 2, 2); + simd_extend_cast!(i64x2_extend_high_i32x4_u, "i64x2.extend_high_i32x4_u", as_u32x4, from_u64x2, u64, 2, 2); + + simd_extmul_signed!(i16x8_extmul_low_i8x16_s, "i16x8.extmul_low_i8x16_s", as_i8x16, from_i16x8, i16, 8, 0); + simd_extmul_unsigned!(i16x8_extmul_low_i8x16_u, "i16x8.extmul_low_i8x16_u", as_u8x16, from_u16x8, u16, 8, 0); + simd_extmul_signed!(i16x8_extmul_high_i8x16_s, "i16x8.extmul_high_i8x16_s", as_i8x16, from_i16x8, i16, 8, 8); + simd_extmul_unsigned!(i16x8_extmul_high_i8x16_u, "i16x8.extmul_high_i8x16_u", as_u8x16, from_u16x8, u16, 8, 8); + simd_extmul_signed!(i32x4_extmul_low_i16x8_s, "i32x4.extmul_low_i16x8_s", as_i16x8, from_i32x4, i32, 4, 0); + simd_extmul_unsigned!(i32x4_extmul_low_i16x8_u, "i32x4.extmul_low_i16x8_u", as_u16x8, from_u32x4, u32, 4, 0); + simd_extmul_signed!(i32x4_extmul_high_i16x8_s, "i32x4.extmul_high_i16x8_s", as_i16x8, from_i32x4, i32, 4, 4); + simd_extmul_unsigned!(i32x4_extmul_high_i16x8_u, "i32x4.extmul_high_i16x8_u", as_u16x8, from_u32x4, u32, 4, 4); + simd_extmul_signed!(i64x2_extmul_low_i32x4_s, "i64x2.extmul_low_i32x4_s", as_i32x4, from_i64x2, i64, 2, 0); + simd_extmul_unsigned!(i64x2_extmul_low_i32x4_u, "i64x2.extmul_low_i32x4_u", as_u32x4, from_u64x2, u64, 2, 0); + simd_extmul_signed!(i64x2_extmul_high_i32x4_s, "i64x2.extmul_high_i32x4_s", as_i32x4, from_i64x2, i64, 2, 2); + simd_extmul_unsigned!(i64x2_extmul_high_i32x4_u, "i64x2.extmul_high_i32x4_u", as_u32x4, from_u64x2, u64, 2, 2); - #[doc(alias = "f64x2.ne")] - pub const fn f64x2_ne(self, rhs: Self) -> Self { - let a = self.as_f64x2(); - let b = rhs.as_f64x2(); - let mut out = [0i64; 2]; + #[doc(alias = "i16x8.q15mulr_sat_s")] + pub const fn i16x8_q15mulr_sat_s(self, rhs: Self) -> Self { + let a = self.as_i16x8(); + let b = rhs.as_i16x8(); + let mut out = [0i16; 8]; let mut i = 0; - while i < 2 { - out[i] = if a[i] != b[i] { -1 } else { 0 }; + while i < 8 { + let r = ((a[i] as i32 * b[i] as i32) + (1 << 14)) >> 15; // 2^14: Q15 rounding + out[i] = if r > i16::MAX as i32 { + i16::MAX + } else if r < i16::MIN as i32 { + i16::MIN + } else { + r as i16 + }; i += 1; } - Self::from_i64x2(out) + Self::from_i16x8(out) } - #[doc(alias = "f32x4.lt")] - pub const fn f32x4_lt(self, rhs: Self) -> Self { - let a = self.as_f32x4(); - let b = rhs.as_f32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) + #[doc(alias = "i32x4.dot_i16x8_s")] + pub const fn i32x4_dot_i16x8_s(self, rhs: Self) -> Self { + let a = self.as_i16x8(); + let b = rhs.as_i16x8(); + Self::from_i32x4([ + (a[0] as i32).wrapping_mul(b[0] as i32).wrapping_add((a[1] as i32).wrapping_mul(b[1] as i32)), + (a[2] as i32).wrapping_mul(b[2] as i32).wrapping_add((a[3] as i32).wrapping_mul(b[3] as i32)), + (a[4] as i32).wrapping_mul(b[4] as i32).wrapping_add((a[5] as i32).wrapping_mul(b[5] as i32)), + (a[6] as i32).wrapping_mul(b[6] as i32).wrapping_add((a[7] as i32).wrapping_mul(b[7] as i32)), + ]) } - #[doc(alias = "f64x2.lt")] - pub const fn f64x2_lt(self, rhs: Self) -> Self { - let a = self.as_f64x2(); - let b = rhs.as_f64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] < b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } + simd_cmp_mask!(i8x16_eq, "i8x16.eq", i8x16_eq, i8, 16, as_i8x16, from_i8x16, ==); + simd_cmp_mask!(i16x8_eq, "i16x8.eq", i16x8_eq, i16, 8, as_i16x8, from_i16x8, ==); + simd_cmp_mask!(i32x4_eq, "i32x4.eq", i32x4_eq, i32, 4, as_i32x4, from_i32x4, ==); + simd_cmp_mask!(i64x2_eq, "i64x2.eq", i64x2_eq, i64, 2, as_i64x2, from_i64x2, ==); + simd_cmp_mask!(i8x16_ne, "i8x16.ne", i8x16_ne, i8, 16, as_i8x16, from_i8x16, !=); + simd_cmp_mask!(i16x8_ne, "i16x8.ne", i16x8_ne, i16, 8, as_i16x8, from_i16x8, !=); + simd_cmp_mask!(i32x4_ne, "i32x4.ne", i32x4_ne, i32, 4, as_i32x4, from_i32x4, !=); + simd_cmp_mask!(i64x2_ne, "i64x2.ne", i64x2_ne, i64, 2, as_i64x2, from_i64x2, !=); + simd_cmp_mask!(i8x16_lt_s, "i8x16.lt_s", i8x16_lt, i8, 16, as_i8x16, from_i8x16, <); + simd_cmp_mask!(i16x8_lt_s, "i16x8.lt_s", i16x8_lt, i16, 8, as_i16x8, from_i16x8, <); + simd_cmp_mask!(i32x4_lt_s, "i32x4.lt_s", i32x4_lt, i32, 4, as_i32x4, from_i32x4, <); + simd_cmp_mask!(i64x2_lt_s, "i64x2.lt_s", i64x2_lt, i64, 2, as_i64x2, from_i64x2, <); + simd_cmp_mask!(i8x16_lt_u, "i8x16.lt_u", u8x16_lt, i8, 16, as_u8x16, from_i8x16, <); + simd_cmp_mask!(i16x8_lt_u, "i16x8.lt_u", u16x8_lt, i16, 8, as_u16x8, from_i16x8, <); + simd_cmp_mask!(i32x4_lt_u, "i32x4.lt_u", u32x4_lt, i32, 4, as_u32x4, from_i32x4, <); + + simd_cmp_delegate!(i8x16_gt_s, "i8x16.gt_s", i8x16_lt_s); + simd_cmp_delegate!(i16x8_gt_s, "i16x8.gt_s", i16x8_lt_s); + simd_cmp_delegate!(i32x4_gt_s, "i32x4.gt_s", i32x4_lt_s); + simd_cmp_delegate!(i64x2_gt_s, "i64x2.gt_s", i64x2_lt_s); + simd_cmp_delegate!(i8x16_gt_u, "i8x16.gt_u", i8x16_lt_u); + simd_cmp_delegate!(i16x8_gt_u, "i16x8.gt_u", i16x8_lt_u); + simd_cmp_delegate!(i32x4_gt_u, "i32x4.gt_u", i32x4_lt_u); + simd_cmp_delegate!(i8x16_le_s, "i8x16.le_s", i8x16_ge_s); + simd_cmp_delegate!(i16x8_le_s, "i16x8.le_s", i16x8_ge_s); + simd_cmp_delegate!(i32x4_le_s, "i32x4.le_s", i32x4_ge_s); + simd_cmp_delegate!(i64x2_le_s, "i64x2.le_s", i64x2_ge_s); + simd_cmp_delegate!(i8x16_le_u, "i8x16.le_u", i8x16_ge_u); + simd_cmp_delegate!(i16x8_le_u, "i16x8.le_u", i16x8_ge_u); + simd_cmp_delegate!(i32x4_le_u, "i32x4.le_u", i32x4_ge_u); + + simd_cmp_mask!(i8x16_ge_s, "i8x16.ge_s", i8x16_ge, i8, 16, as_i8x16, from_i8x16, >=); + simd_cmp_mask!(i16x8_ge_s, "i16x8.ge_s", i16x8_ge, i16, 8, as_i16x8, from_i16x8, >=); + simd_cmp_mask!(i32x4_ge_s, "i32x4.ge_s", i32x4_ge, i32, 4, as_i32x4, from_i32x4, >=); + simd_cmp_mask!(i64x2_ge_s, "i64x2.ge_s", i64x2_ge, i64, 2, as_i64x2, from_i64x2, >=); + simd_cmp_mask!(i8x16_ge_u, "i8x16.ge_u", u8x16_ge, i8, 16, as_u8x16, from_i8x16, >=); + simd_cmp_mask!(i16x8_ge_u, "i16x8.ge_u", u16x8_ge, i16, 8, as_u16x8, from_i16x8, >=); + simd_cmp_mask!(i32x4_ge_u, "i32x4.ge_u", u32x4_ge, i32, 4, as_u32x4, from_i32x4, >=); + + simd_abs_const!(i8x16_abs, "i8x16.abs", i8, 16, as_i8x16, from_i8x16); + simd_abs_const!(i16x8_abs, "i16x8.abs", i16, 8, as_i16x8, from_i16x8); + simd_abs_const!(i32x4_abs, "i32x4.abs", i32, 4, as_i32x4, from_i32x4); + simd_abs_const!(i64x2_abs, "i64x2.abs", i64, 2, as_i64x2, from_i64x2); + + simd_neg!(i8x16_neg, "i8x16.neg", i8x16_neg, i8, 16, as_i8x16, from_i8x16); + simd_neg!(i16x8_neg, "i16x8.neg", i16x8_neg, i16, 8, as_i16x8, from_i16x8); + simd_neg!(i32x4_neg, "i32x4.neg", i32x4_neg, i32, 4, as_i32x4, from_i32x4); + simd_neg!(i64x2_neg, "i64x2.neg", i64x2_neg, i64, 2, as_i64x2, from_i64x2); + + simd_minmax!(i8x16_min_s, "i8x16.min_s", i8x16_min, i8, 16, as_i8x16, from_i8x16, <); + simd_minmax!(i16x8_min_s, "i16x8.min_s", i16x8_min, i16, 8, as_i16x8, from_i16x8, <); + simd_minmax!(i32x4_min_s, "i32x4.min_s", i32x4_min, i32, 4, as_i32x4, from_i32x4, <); + simd_minmax!(i8x16_min_u, "i8x16.min_u", u8x16_min, u8, 16, as_u8x16, from_u8x16, <); + simd_minmax!(i16x8_min_u, "i16x8.min_u", u16x8_min, u16, 8, as_u16x8, from_u16x8, <); + simd_minmax!(i32x4_min_u, "i32x4.min_u", u32x4_min, u32, 4, as_u32x4, from_u32x4, <); + simd_minmax!(i8x16_max_s, "i8x16.max_s", i8x16_max, i8, 16, as_i8x16, from_i8x16, >); + simd_minmax!(i16x8_max_s, "i16x8.max_s", i16x8_max, i16, 8, as_i16x8, from_i16x8, >); + simd_minmax!(i32x4_max_s, "i32x4.max_s", i32x4_max, i32, 4, as_i32x4, from_i32x4, >); + simd_minmax!(i8x16_max_u, "i8x16.max_u", u8x16_max, u8, 16, as_u8x16, from_u8x16, >); + simd_minmax!(i16x8_max_u, "i16x8.max_u", u16x8_max, u16, 8, as_u16x8, from_u16x8, >); + simd_minmax!(i32x4_max_u, "i32x4.max_u", u32x4_max, u32, 4, as_u32x4, from_u32x4, >); + + simd_cmp_mask_const!(f32x4_eq, "f32x4.eq", i32, 4, as_f32x4, from_i32x4, ==); + simd_cmp_mask_const!(f64x2_eq, "f64x2.eq", i64, 2, as_f64x2, from_i64x2, ==); + simd_cmp_mask_const!(f32x4_ne, "f32x4.ne", i32, 4, as_f32x4, from_i32x4, !=); + simd_cmp_mask_const!(f64x2_ne, "f64x2.ne", i64, 2, as_f64x2, from_i64x2, !=); + simd_cmp_mask_const!(f32x4_lt, "f32x4.lt", i32, 4, as_f32x4, from_i32x4, <); + simd_cmp_mask_const!(f64x2_lt, "f64x2.lt", i64, 2, as_f64x2, from_i64x2, <); #[doc(alias = "f32x4.gt")] pub const fn f32x4_gt(self, rhs: Self) -> Self { @@ -2386,207 +966,46 @@ impl Value128 { rhs.f64x2_lt(self) } - #[doc(alias = "f32x4.le")] - pub const fn f32x4_le(self, rhs: Self) -> Self { - let a = self.as_f32x4(); - let b = rhs.as_f32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] <= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "f64x2.le")] - pub const fn f64x2_le(self, rhs: Self) -> Self { - let a = self.as_f64x2(); - let b = rhs.as_f64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] <= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "f32x4.ge")] - pub const fn f32x4_ge(self, rhs: Self) -> Self { - let a = self.as_f32x4(); - let b = rhs.as_f32x4(); - let mut out = [0i32; 4]; - let mut i = 0; - while i < 4 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i32x4(out) - } - - #[doc(alias = "f64x2.ge")] - pub const fn f64x2_ge(self, rhs: Self) -> Self { - let a = self.as_f64x2(); - let b = rhs.as_f64x2(); - let mut out = [0i64; 2]; - let mut i = 0; - while i < 2 { - out[i] = if a[i] >= b[i] { -1 } else { 0 }; - i += 1; - } - Self::from_i64x2(out) - } - - #[doc(alias = "f32x4.ceil")] - pub fn f32x4_ceil(self) -> Self { - self.map_f32x4(|x| canonicalize_simd_f32_nan(x.ceil())) - } - - #[doc(alias = "f64x2.ceil")] - pub fn f64x2_ceil(self) -> Self { - self.map_f64x2(|x| canonicalize_simd_f64_nan(x.ceil())) - } - - #[doc(alias = "f32x4.floor")] - pub fn f32x4_floor(self) -> Self { - self.map_f32x4(|x| canonicalize_simd_f32_nan(x.floor())) - } - - #[doc(alias = "f64x2.floor")] - pub fn f64x2_floor(self) -> Self { - self.map_f64x2(|x| canonicalize_simd_f64_nan(x.floor())) - } - - #[doc(alias = "f32x4.trunc")] - pub fn f32x4_trunc(self) -> Self { - self.map_f32x4(|x| canonicalize_simd_f32_nan(x.trunc())) - } - - #[doc(alias = "f64x2.trunc")] - pub fn f64x2_trunc(self) -> Self { - self.map_f64x2(|x| canonicalize_simd_f64_nan(x.trunc())) - } - - #[doc(alias = "f32x4.nearest")] - pub fn f32x4_nearest(self) -> Self { - self.map_f32x4(|x| canonicalize_simd_f32_nan(TinywasmFloatExt::tw_nearest(x))) - } - - #[doc(alias = "f64x2.nearest")] - pub fn f64x2_nearest(self) -> Self { - self.map_f64x2(|x| canonicalize_simd_f64_nan(TinywasmFloatExt::tw_nearest(x))) - } - - #[doc(alias = "f32x4.abs")] - pub fn f32x4_abs(self) -> Self { - self.map_f32x4(f32::abs) - } - - #[doc(alias = "f64x2.abs")] - pub fn f64x2_abs(self) -> Self { - self.map_f64x2(f64::abs) - } - - #[doc(alias = "f32x4.neg")] - pub fn f32x4_neg(self) -> Self { - self.map_f32x4(|x| -x) - } - - #[doc(alias = "f64x2.neg")] - pub fn f64x2_neg(self) -> Self { - self.map_f64x2(|x| -x) - } - - #[doc(alias = "f32x4.sqrt")] - pub fn f32x4_sqrt(self) -> Self { - self.map_f32x4(|x| canonicalize_simd_f32_nan(x.sqrt())) - } - - #[doc(alias = "f64x2.sqrt")] - pub fn f64x2_sqrt(self) -> Self { - self.map_f64x2(|x| canonicalize_simd_f64_nan(x.sqrt())) - } - - #[doc(alias = "f32x4.add")] - pub fn f32x4_add(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, |a, b| canonicalize_simd_f32_nan(a + b)) - } - - #[doc(alias = "f64x2.add")] - pub fn f64x2_add(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, |a, b| canonicalize_simd_f64_nan(a + b)) - } - - #[doc(alias = "f32x4.sub")] - pub fn f32x4_sub(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, |a, b| canonicalize_simd_f32_nan(a - b)) - } - - #[doc(alias = "f64x2.sub")] - pub fn f64x2_sub(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, |a, b| canonicalize_simd_f64_nan(a - b)) - } - - #[doc(alias = "f32x4.mul")] - pub fn f32x4_mul(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, |a, b| canonicalize_simd_f32_nan(a * b)) - } - - #[doc(alias = "f64x2.mul")] - pub fn f64x2_mul(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, |a, b| canonicalize_simd_f64_nan(a * b)) - } - - #[doc(alias = "f32x4.div")] - pub fn f32x4_div(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, |a, b| canonicalize_simd_f32_nan(a / b)) - } - - #[doc(alias = "f64x2.div")] - pub fn f64x2_div(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, |a, b| canonicalize_simd_f64_nan(a / b)) - } - - #[doc(alias = "f32x4.min")] - pub fn f32x4_min(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, TinywasmFloatExt::tw_minimum) - } - - #[doc(alias = "f64x2.min")] - pub fn f64x2_min(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, TinywasmFloatExt::tw_minimum) - } - - #[doc(alias = "f32x4.max")] - pub fn f32x4_max(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, TinywasmFloatExt::tw_maximum) - } - - #[doc(alias = "f64x2.max")] - pub fn f64x2_max(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, TinywasmFloatExt::tw_maximum) - } - - #[doc(alias = "f32x4.pmin")] - pub fn f32x4_pmin(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, |a, b| if b < a { b } else { a }) - } - - #[doc(alias = "f64x2.pmin")] - pub fn f64x2_pmin(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, |a, b| if b < a { b } else { a }) - } - - #[doc(alias = "f32x4.pmax")] - pub fn f32x4_pmax(self, rhs: Self) -> Self { - self.zip_f32x4(rhs, |a, b| if b > a { b } else { a }) - } - - #[doc(alias = "f64x2.pmax")] - pub fn f64x2_pmax(self, rhs: Self) -> Self { - self.zip_f64x2(rhs, |a, b| if b > a { b } else { a }) - } + simd_cmp_mask_const!(f32x4_le, "f32x4.le", i32, 4, as_f32x4, from_i32x4, <=); + simd_cmp_mask_const!(f64x2_le, "f64x2.le", i64, 2, as_f64x2, from_i64x2, <=); + simd_cmp_mask_const!(f32x4_ge, "f32x4.ge", i32, 4, as_f32x4, from_i32x4, >=); + simd_cmp_mask_const!(f64x2_ge, "f64x2.ge", i64, 2, as_f64x2, from_i64x2, >=); + + simd_float_unary!(f32x4_ceil, "f32x4.ceil", map_f32x4, |x| canonicalize_simd_f32_nan(x.ceil())); + simd_float_unary!(f64x2_ceil, "f64x2.ceil", map_f64x2, |x| canonicalize_simd_f64_nan(x.ceil())); + simd_float_unary!(f32x4_floor, "f32x4.floor", map_f32x4, |x| canonicalize_simd_f32_nan(x.floor())); + simd_float_unary!(f64x2_floor, "f64x2.floor", map_f64x2, |x| canonicalize_simd_f64_nan(x.floor())); + simd_float_unary!(f32x4_trunc, "f32x4.trunc", map_f32x4, |x| canonicalize_simd_f32_nan(x.trunc())); + simd_float_unary!(f64x2_trunc, "f64x2.trunc", map_f64x2, |x| canonicalize_simd_f64_nan(x.trunc())); + simd_float_unary!(f32x4_nearest, "f32x4.nearest", map_f32x4, |x| canonicalize_simd_f32_nan( + TinywasmFloatExt::tw_nearest(x) + )); + simd_float_unary!(f64x2_nearest, "f64x2.nearest", map_f64x2, |x| canonicalize_simd_f64_nan( + TinywasmFloatExt::tw_nearest(x) + )); + simd_float_unary!(f32x4_abs, "f32x4.abs", map_f32x4, f32::abs); + simd_float_unary!(f64x2_abs, "f64x2.abs", map_f64x2, f64::abs); + simd_float_unary!(f32x4_neg, "f32x4.neg", map_f32x4, |x| -x); + simd_float_unary!(f64x2_neg, "f64x2.neg", map_f64x2, |x| -x); + simd_float_unary!(f32x4_sqrt, "f32x4.sqrt", map_f32x4, |x| canonicalize_simd_f32_nan(x.sqrt())); + simd_float_unary!(f64x2_sqrt, "f64x2.sqrt", map_f64x2, |x| canonicalize_simd_f64_nan(x.sqrt())); + + simd_float_binary!(f32x4_add, "f32x4.add", zip_f32x4, |a, b| canonicalize_simd_f32_nan(a + b)); + simd_float_binary!(f64x2_add, "f64x2.add", zip_f64x2, |a, b| canonicalize_simd_f64_nan(a + b)); + simd_float_binary!(f32x4_sub, "f32x4.sub", zip_f32x4, |a, b| canonicalize_simd_f32_nan(a - b)); + simd_float_binary!(f64x2_sub, "f64x2.sub", zip_f64x2, |a, b| canonicalize_simd_f64_nan(a - b)); + simd_float_binary!(f32x4_mul, "f32x4.mul", zip_f32x4, |a, b| canonicalize_simd_f32_nan(a * b)); + simd_float_binary!(f64x2_mul, "f64x2.mul", zip_f64x2, |a, b| canonicalize_simd_f64_nan(a * b)); + simd_float_binary!(f32x4_div, "f32x4.div", zip_f32x4, |a, b| canonicalize_simd_f32_nan(a / b)); + simd_float_binary!(f64x2_div, "f64x2.div", zip_f64x2, |a, b| canonicalize_simd_f64_nan(a / b)); + simd_float_binary!(f32x4_min, "f32x4.min", zip_f32x4, TinywasmFloatExt::tw_minimum); + simd_float_binary!(f64x2_min, "f64x2.min", zip_f64x2, TinywasmFloatExt::tw_minimum); + simd_float_binary!(f32x4_max, "f32x4.max", zip_f32x4, TinywasmFloatExt::tw_maximum); + simd_float_binary!(f64x2_max, "f64x2.max", zip_f64x2, TinywasmFloatExt::tw_maximum); + simd_float_binary!(f32x4_pmin, "f32x4.pmin", zip_f32x4, |a, b| if b < a { b } else { a }); + simd_float_binary!(f64x2_pmin, "f64x2.pmin", zip_f64x2, |a, b| if b < a { b } else { a }); + simd_float_binary!(f32x4_pmax, "f32x4.pmax", zip_f32x4, |a, b| if b > a { b } else { a }); + simd_float_binary!(f64x2_pmax, "f64x2.pmax", zip_f64x2, |a, b| if b > a { b } else { a }); #[doc(alias = "i32x4.trunc_sat_f32x4_s")] pub fn i32x4_trunc_sat_f32x4_s(self) -> Self { @@ -2659,47 +1078,15 @@ impl Value128 { } pub const fn splat_i16(src: i16) -> Self { - let mut result_bytes = [0u8; 16]; - let bytes = src.to_le_bytes(); - let mut i = 0; - while i < 8 { - result_bytes[i * 2] = bytes[0]; - result_bytes[i * 2 + 1] = bytes[1]; - i += 1; - } - Self::from_le_bytes(result_bytes) + Self::from_i16x8([src; 8]) } pub const fn splat_i32(src: i32) -> Self { - let mut result_bytes = [0u8; 16]; - let bytes = src.to_le_bytes(); - let mut i = 0; - while i < 4 { - result_bytes[i * 4] = bytes[0]; - result_bytes[i * 4 + 1] = bytes[1]; - result_bytes[i * 4 + 2] = bytes[2]; - result_bytes[i * 4 + 3] = bytes[3]; - i += 1; - } - Self::from_le_bytes(result_bytes) + Self::from_i32x4([src; 4]) } pub const fn splat_i64(src: i64) -> Self { - let mut result_bytes = [0u8; 16]; - let bytes = src.to_le_bytes(); - let mut i = 0; - while i < 2 { - result_bytes[i * 8] = bytes[0]; - result_bytes[i * 8 + 1] = bytes[1]; - result_bytes[i * 8 + 2] = bytes[2]; - result_bytes[i * 8 + 3] = bytes[3]; - result_bytes[i * 8 + 4] = bytes[4]; - result_bytes[i * 8 + 5] = bytes[5]; - result_bytes[i * 8 + 6] = bytes[6]; - result_bytes[i * 8 + 7] = bytes[7]; - i += 1; - } - Self::from_le_bytes(result_bytes) + Self::from_i64x2([src; 2]) } pub const fn splat_f32(src: f32) -> Self { @@ -2725,44 +1112,19 @@ impl Value128 { } pub const fn extract_lane_i16(self, lane: u8) -> i16 { - debug_assert!(lane < 8); - let lane = lane as usize; - let bytes = self.to_le_bytes(); - let start = lane * 2; - i16::from_le_bytes([bytes[start], bytes[start + 1]]) + i16::from_le_bytes(self.extract_lane_bytes::<2>(lane, 8)) } pub const fn extract_lane_u16(self, lane: u8) -> u16 { - debug_assert!(lane < 8); - let lane = lane as usize; - let bytes = self.to_le_bytes(); - let start = lane * 2; - u16::from_le_bytes([bytes[start], bytes[start + 1]]) + u16::from_le_bytes(self.extract_lane_bytes::<2>(lane, 8)) } pub const fn extract_lane_i32(self, lane: u8) -> i32 { - debug_assert!(lane < 4); - let lane = lane as usize; - let bytes = self.to_le_bytes(); - let start = lane * 4; - i32::from_le_bytes([bytes[start], bytes[start + 1], bytes[start + 2], bytes[start + 3]]) + i32::from_le_bytes(self.extract_lane_bytes::<4>(lane, 4)) } pub const fn extract_lane_i64(self, lane: u8) -> i64 { - debug_assert!(lane < 2); - let lane = lane as usize; - let bytes = self.to_le_bytes(); - let start = lane * 8; - i64::from_le_bytes([ - bytes[start], - bytes[start + 1], - bytes[start + 2], - bytes[start + 3], - bytes[start + 4], - bytes[start + 5], - bytes[start + 6], - bytes[start + 7], - ]) + i64::from_le_bytes(self.extract_lane_bytes::<8>(lane, 2)) } pub const fn extract_lane_f32(self, lane: u8) -> f32 { @@ -2773,6 +1135,19 @@ impl Value128 { f64::from_bits(self.extract_lane_i64(lane) as u64) } + const fn extract_lane_bytes(self, lane: u8, lane_count: u8) -> [u8; LANE_BYTES] { + debug_assert!(lane < lane_count); + let bytes = self.to_le_bytes(); + let start = lane as usize * LANE_BYTES; + let mut out = [0u8; LANE_BYTES]; + let mut i = 0; + while i < LANE_BYTES { + out[i] = bytes[start + i]; + i += 1; + } + out + } + const fn replace_lane_bytes( self, lane: u8, @@ -2791,46 +1166,6 @@ impl Value128 { } } -impl From for i128 { - fn from(val: Value128) -> Self { - val.0 - } -} - -impl From for Value128 { - fn from(value: i128) -> Self { - Self(value) - } -} - -impl core::ops::Not for Value128 { - type Output = Self; - fn not(self) -> Self::Output { - Self(!self.0) - } -} - -impl core::ops::BitAnd for Value128 { - type Output = Self; - fn bitand(self, rhs: Self) -> Self::Output { - Self(self.0 & rhs.0) - } -} - -impl core::ops::BitOr for Value128 { - type Output = Self; - fn bitor(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) - } -} - -impl core::ops::BitXor for Value128 { - type Output = Self; - fn bitxor(self, rhs: Self) -> Self::Output { - Self(self.0 ^ rhs.0) - } -} - const fn canonicalize_simd_f32_nan(x: f32) -> f32 { #[cfg(feature = "canonicalize_nans")] if x.is_nan() { @@ -2854,85 +1189,67 @@ const fn canonicalize_simd_f64_nan(x: f64) -> f64 { } const fn saturate_i16_to_i8(x: i16) -> i8 { - if x > i8::MAX as i16 { - i8::MAX - } else if x < i8::MIN as i16 { - i8::MIN - } else { - x as i8 + match x { + v if v > i8::MAX as i16 => i8::MAX, + v if v < i8::MIN as i16 => i8::MIN, + v => v as i8, } } const fn saturate_i16_to_u8(x: i16) -> u8 { - if x <= 0 { - 0 - } else if x > u8::MAX as i16 { - u8::MAX - } else { - x as u8 + match x { + v if v <= 0 => 0, + v if v > u8::MAX as i16 => u8::MAX, + v => v as u8, } } const fn saturate_i32_to_i16(x: i32) -> i16 { - if x > i16::MAX as i32 { - i16::MAX - } else if x < i16::MIN as i32 { - i16::MIN - } else { - x as i16 + match x { + v if v > i16::MAX as i32 => i16::MAX, + v if v < i16::MIN as i32 => i16::MIN, + v => v as i16, } } const fn saturate_i32_to_u16(x: i32) -> u16 { - if x <= 0 { - 0 - } else if x > u16::MAX as i32 { - u16::MAX - } else { - x as u16 + match x { + v if v <= 0 => 0, + v if v > u16::MAX as i32 => u16::MAX, + v => v as u16, } } fn trunc_sat_f32_to_i32(v: f32) -> i32 { - if v.is_nan() { - 0 - } else if v <= i32::MIN as f32 - (1 << 8) as f32 { - i32::MIN - } else if v >= (i32::MAX as f32 + 1.0) { - i32::MAX - } else { - v.trunc() as i32 + match v { + x if x.is_nan() => 0, + x if x <= i32::MIN as f32 - (1 << 8) as f32 => i32::MIN, + x if x >= (i32::MAX as f32 + 1.0) => i32::MAX, + x => x.trunc() as i32, } } fn trunc_sat_f32_to_u32(v: f32) -> u32 { - if v.is_nan() || v <= -1.0_f32 { - 0 - } else if v >= (u32::MAX as f32 + 1.0) { - u32::MAX - } else { - v.trunc() as u32 + match v { + x if x.is_nan() || x <= -1.0_f32 => 0, + x if x >= (u32::MAX as f32 + 1.0) => u32::MAX, + x => x.trunc() as u32, } } fn trunc_sat_f64_to_i32(v: f64) -> i32 { - if v.is_nan() { - 0 - } else if v <= i32::MIN as f64 - 1.0_f64 { - i32::MIN - } else if v >= (i32::MAX as f64 + 1.0) { - i32::MAX - } else { - v.trunc() as i32 + match v { + x if x.is_nan() => 0, + x if x <= i32::MIN as f64 - 1.0_f64 => i32::MIN, + x if x >= (i32::MAX as f64 + 1.0) => i32::MAX, + x => x.trunc() as i32, } } fn trunc_sat_f64_to_u32(v: f64) -> u32 { - if v.is_nan() || v <= -1.0_f64 { - 0 - } else if v >= (u32::MAX as f64 + 1.0) { - u32::MAX - } else { - v.trunc() as u32 + match v { + x if x.is_nan() || x <= -1.0_f64 => 0, + x if x >= (u32::MAX as f64 + 1.0) => u32::MAX, + x => x.trunc() as u32, } } diff --git a/crates/tinywasm/src/interpreter/values.rs b/crates/tinywasm/src/interpreter/values.rs index b57008c..a127cf6 100644 --- a/crates/tinywasm/src/interpreter/values.rs +++ b/crates/tinywasm/src/interpreter/values.rs @@ -108,11 +108,11 @@ pub(crate) trait InternalValue: sealed::Sealed + Into + Copy + De fn stack_push(stack: &mut ValueStack, value: Self) -> Result<()>; fn local_get(stack: &ValueStack, frame: &CallFrame, index: LocalAddr) -> Self; fn local_set(stack: &mut ValueStack, frame: &CallFrame, index: LocalAddr, value: Self); - fn replace_top(stack: &mut ValueStack, func: impl FnOnce(Self) -> Result) -> Result<()>; - fn stack_calculate(stack: &mut ValueStack, func: impl FnOnce(Self, Self) -> Result) -> Result<()>; - fn stack_calculate3(stack: &mut ValueStack, func: impl FnOnce(Self, Self, Self) -> Result) -> Result<()>; fn stack_pop(stack: &mut ValueStack) -> Self; fn stack_peek(stack: &ValueStack) -> Self; + fn stack_apply1(stack: &mut ValueStack, func: impl FnOnce(Self) -> Result) -> Result<()>; + fn stack_apply2(stack: &mut ValueStack, func: impl FnOnce(Self, Self) -> Result) -> Result<()>; + fn stack_apply3(stack: &mut ValueStack, func: impl FnOnce(Self, Self, Self) -> Result) -> Result<()>; } macro_rules! impl_internalvalue { @@ -153,7 +153,14 @@ macro_rules! impl_internalvalue { } #[inline(always)] - fn stack_calculate(stack: &mut ValueStack, func: impl FnOnce(Self, Self) -> Result) -> Result<()> { + fn stack_apply1(stack: &mut ValueStack, func: impl FnOnce(Self) -> Result) -> Result<()> { + let top = stack.$stack.last_mut(); + *top = $to_internal(func($to_outer(*top))?); + Ok(()) + } + + #[inline(always)] + fn stack_apply2(stack: &mut ValueStack, func: impl FnOnce(Self, Self) -> Result) -> Result<()> { let v2 = stack.$stack.pop(); let v1 = stack.$stack.last_mut(); *v1 = $to_internal(func($to_outer(*v1), $to_outer(v2))?); @@ -161,20 +168,13 @@ macro_rules! impl_internalvalue { } #[inline(always)] - fn stack_calculate3(stack: &mut ValueStack, func: impl FnOnce(Self, Self, Self) -> Result) -> Result<()> { + fn stack_apply3(stack: &mut ValueStack, func: impl FnOnce(Self, Self, Self) -> Result) -> Result<()> { let v3 = stack.$stack.pop(); let v2 = stack.$stack.pop(); let v1 = stack.$stack.last_mut(); *v1 = $to_internal(func($to_outer(*v1), $to_outer(v2), $to_outer(v3))?); Ok(()) } - - #[inline(always)] - fn replace_top(stack: &mut ValueStack, func: impl FnOnce(Self) -> Result) -> Result<()> { - let v = stack.$stack.last_mut(); - *v = $to_internal(func($to_outer(*v))?); - Ok(()) - } } )* }; diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 5c08ab9..a0c5c69 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -154,7 +154,7 @@ impl MemoryInstance { } /// A trait for types that can be converted to and from static byte arrays -pub(crate) trait MemValue: Copy + Sized { +pub(crate) trait MemValue: Copy + Default { /// Store a value in memory fn to_mem_bytes(self) -> [u8; N]; @@ -168,12 +168,12 @@ macro_rules! impl_mem_traits { impl MemValue<$size> for $ty { #[inline(always)] fn from_mem_bytes(bytes: [u8; $size]) -> Self { - <$ty>::from_le_bytes(bytes.into()) + <$ty>::from_le_bytes(bytes) } #[inline(always)] fn to_mem_bytes(self) -> [u8; $size] { - self.to_le_bytes().into() + self.to_le_bytes() } } )* diff --git a/crates/tinywasm/tests/host_func_signature_check.rs b/crates/tinywasm/tests/host_func_signature_check.rs index 9389435..3b682b8 100644 --- a/crates/tinywasm/tests/host_func_signature_check.rs +++ b/crates/tinywasm/tests/host_func_signature_check.rs @@ -9,69 +9,62 @@ use tinywasm_types::ExternRef; const VAL_LISTS: &[&[WasmValue]] = &[ &[], &[WasmValue::I32(0)], - &[WasmValue::I32(0), WasmValue::I32(0)], // 2 of the same - &[WasmValue::I32(0), WasmValue::I32(0), WasmValue::F64(0.0)], // add another type - &[WasmValue::I32(0), WasmValue::F64(0.0), WasmValue::I32(0)], // reorder - &[WasmValue::RefExtern(ExternRef::null()), WasmValue::F64(0.0), WasmValue::I32(0)], // all different types + &[WasmValue::I32(0), WasmValue::I32(0)], + &[WasmValue::I32(0), WasmValue::I32(0), WasmValue::F64(0.0)], + &[WasmValue::I32(0), WasmValue::F64(0.0), WasmValue::I32(0)], + &[WasmValue::RefExtern(ExternRef::null()), WasmValue::F64(0.0), WasmValue::I32(0)], ]; -// (f64, i32, i32) and (f64) can be used to "match_none" -fn get_type_lists() -> impl Iterator + Clone> + Clone { - VAL_LISTS.iter().map(|l| l.iter().map(WasmValue::val_type)) +fn value_types(values: &[WasmValue]) -> Box<[ValType]> { + values.iter().map(WasmValue::val_type).collect() } -fn get_modules() -> Vec<(Module, FuncType, Vec)> { - let mut result = Vec::<(Module, FuncType, Vec)>::new(); - let val_and_tys = get_type_lists().zip(VAL_LISTS); - for res_types in get_type_lists() { - for (arg_types, arg_vals) in val_and_tys.clone() { - let ty = FuncType { results: res_types.clone().collect(), params: arg_types.collect() }; - result.push((proxy_module(&ty), ty, arg_vals.to_vec())); + +fn module_cases() -> Vec<(Module, FuncType, Vec)> { + let mut cases = Vec::<(Module, FuncType, Vec)>::new(); + for results in VAL_LISTS { + for params in VAL_LISTS { + let func_ty = FuncType { results: value_types(results), params: value_types(params) }; + cases.push((proxy_module(&func_ty), func_ty, params.to_vec())); } } - result + cases } #[test] fn test_return_invalid_type() -> Result<()> { - // try to return from host functions types that don't match their signatures - let mod_list = get_modules(); + let cases = module_cases(); - for (module, func_ty, test_args) in mod_list { - for result_to_try in VAL_LISTS { + for (module, func_ty, args) in cases { + for returned_values in VAL_LISTS { let mut store = Store::default(); let mut imports = Imports::new(); imports - .define("host", "hfn", Extern::func(&func_ty, |_: FuncContext<'_>, _| Ok(result_to_try.to_vec()))) + .define("host", "hfn", Extern::func(&func_ty, |_: FuncContext<'_>, _| Ok(returned_values.to_vec()))) .unwrap(); let instance = module.clone().instantiate(&mut store, Some(imports)).unwrap(); let caller = instance.exported_func_untyped(&store, "call_hfn").unwrap(); - let res_types_returned = result_to_try.iter().map(WasmValue::val_type); - dbg!(&res_types_returned, &func_ty); - let res_types_expected = &func_ty.results; - let should_succeed = res_types_returned.eq(res_types_expected.iter().cloned()); - // Extern::func that returns wrong type(s) can only be detected when it runs - let call_res = caller.call(&mut store, &test_args); - dbg!(&call_res); + // Return-type mismatch is only observable at call time. + let should_succeed = returned_values.iter().map(WasmValue::val_type).eq(func_ty.results.iter().copied()); + let call_res = caller.call(&mut store, &args); assert_eq!(call_res.is_ok(), should_succeed); - println!("this time ok"); } } + Ok(()) } #[test] fn test_linking_invalid_untyped_func() -> Result<()> { - // try to import host functions with function types no matching those expected by modules - let mod_list = get_modules(); - for (module, actual_func_ty, _) in &mod_list { - for (_, func_ty_to_try, _) in &mod_list { + let cases = module_cases(); + for (module, expected_func_ty, _) in &cases { + for (_, func_ty_to_try, _) in &cases { let tried_fn = Extern::func(func_ty_to_try, |_: FuncContext<'_>, _| panic!("not intended to be called")); let mut store = Store::default(); let mut imports = Imports::new(); imports.define("host", "hfn", tried_fn).unwrap(); - let should_succeed = func_ty_to_try == actual_func_ty; + let should_succeed = func_ty_to_try == expected_func_ty; let link_res = module.clone().instantiate(&mut store, Some(imports)); assert_eq!(link_res.is_ok(), should_succeed); @@ -83,28 +76,27 @@ fn test_linking_invalid_untyped_func() -> Result<()> { #[test] fn test_linking_invalid_typed_func() -> Result<()> { type Existing = (i32, i32, f64); - type NonMatchingOne = f64; - type NonMatchingMul = (f64, i32, i32); + type NonMatchingSingle = f64; + type NonMatchingTuple = (f64, i32, i32); const DONT_CALL: &str = "not meant to be called"; - // they don't match any signature from get_modules() - #[rustfmt::skip] // to make it table-like - let matching_none= &[ - Extern::typed_func(|_, _: NonMatchingMul| -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: NonMatchingMul| -> tinywasm::Result<()> { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: NonMatchingOne| -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: NonMatchingOne| -> tinywasm::Result<()> { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: Existing | -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: Existing | -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: () | -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: () | -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: NonMatchingOne| -> tinywasm::Result { panic!("{DONT_CALL}") } ), - Extern::typed_func(|_, _: NonMatchingOne| -> tinywasm::Result { panic!("{DONT_CALL}") } ), + // None of these typed host signatures are produced by module_cases(). + let matching_none = vec![ + Extern::typed_func(|_, _: NonMatchingTuple| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: NonMatchingTuple| -> tinywasm::Result<()> { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result<()> { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: Existing| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: Existing| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: ()| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: ()| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result { panic!("{DONT_CALL}") }), + Extern::typed_func(|_, _: NonMatchingSingle| -> tinywasm::Result { panic!("{DONT_CALL}") }), ]; - let mod_list = get_modules(); - for (module, _, _) in mod_list { - for typed_fn in matching_none.clone() { + let cases = module_cases(); + for (module, _, _) in cases { + for typed_fn in matching_none.iter().cloned() { let mut store = Store::default(); let mut imports = Imports::new(); imports.define("host", "hfn", typed_fn).unwrap(); @@ -113,7 +105,6 @@ fn test_linking_invalid_typed_func() -> Result<()> { } } - // the valid cases are well-checked in other tests Ok(()) } @@ -129,9 +120,6 @@ fn to_name(ty: &ValType) -> &str { } } -// make a module with imported function {module:"host", name:"hfn"} that takes specified results and returns specified params -// and 2 wasm functions: call_hfn takes params, passes them to hfn and returns it's results -// and 2 wasm functions: call_hfn_discard takes params, passes them to hfn and drops it's results fn proxy_module(func_ty: &FuncType) -> Module { let results = func_ty.results.as_ref(); let params = func_ty.params.as_ref(); @@ -139,7 +127,7 @@ fn proxy_module(func_ty: &FuncType) -> Module { if list.is_empty() { return "".to_string(); } - let step = list.iter().map(|ty| format!("{} ", to_name(ty)).to_string()).collect::(); + let step = list.iter().map(|ty| format!("{} ", to_name(ty))).collect::(); format!("({keyword} {step})") }; @@ -151,7 +139,7 @@ fn proxy_module(func_ty: &FuncType) -> Module { acc }); - let result_drops = "(drop)\n".repeat(results.len()).to_string(); + let result_drops = "(drop)\n".repeat(results.len()); let wasm_text = format!( r#"(module (import "host" "hfn" (func $host_fn {params_text} {results_text})) @@ -162,6 +150,7 @@ fn proxy_module(func_ty: &FuncType) -> Module { (func (export "call_hfn_discard") {params_text} {params_gets} (call $host_fn) + ;; Keep stack balanced for arbitrary result arity. {result_drops} ) ) diff --git a/examples/funcref_callbacks.rs b/examples/funcref_callbacks.rs index 01f833c..4ee7438 100644 --- a/examples/funcref_callbacks.rs +++ b/examples/funcref_callbacks.rs @@ -1,21 +1,17 @@ use eyre::Result; use tinywasm::{Extern, FuncContext, Imports, Module, Store, types::FuncRef}; +const LHS: i32 = 5; +const RHS: i32 = 3; + fn main() -> Result<()> { - by_func_ref_passed()?; - by_func_ref_returned()?; + run_passed_funcref_example()?; + run_returned_funcref_example()?; Ok(()) } -/// Example of passing Wasm functions (as `funcref`) to an imported host function -/// and the imported host function calling them. -fn by_func_ref_passed() -> Result<()> { - // A module with: - // - Imported function "host.call_this" that accepts a callback. - // - Exported Wasm function "tell_host_to_call" that calls "host.call_this" with Wasm functions $add and $sub. - // - Wasm functions $add and $sub and an imported function $mul used as callbacks - // (just to show that imported functions can be referenced too). - // - Exported Wasm function "call_binop_by_ref", a proxy used by the host to call func-references of type (i32, i32) -> i32. +fn run_passed_funcref_example() -> Result<()> { + // Host receives funcref and calls it via an exported proxy. const WASM: &str = r#" (module (import "host" "call_this" (func $host_callback_caller (param funcref))) @@ -30,7 +26,7 @@ fn by_func_ref_passed() -> Result<()> { (type $binop (func (param i32 i32) (result i32))) (table 3 funcref) - (elem (i32.const 0) $add $sub $host_mul) ;; Function can only be referenced if added to a table. + (elem (i32.const 0) $add $sub $host_mul) (func $add (param $x i32) (param $y i32) (result i32) local.get $x local.get $y @@ -50,51 +46,41 @@ fn by_func_ref_passed() -> Result<()> { ) "#; - let wasm = wat::parse_str(WASM).expect("Failed to parse WAT"); + let wasm = wat::parse_str(WASM).expect("failed to parse wat"); let module = Module::parse_bytes(&wasm)?; let mut store = Store::default(); let mut imports = Imports::new(); - // Import host function that takes callbacks and calls them. imports.define( "host", "call_this", - Extern::typed_func(|mut ctx: FuncContext<'_>, fn_ref: FuncRef| -> tinywasm::Result<()> { - let proxy_caller = + Extern::typed_func(|mut ctx: FuncContext<'_>, func_ref: FuncRef| -> tinywasm::Result<()> { + // Host cannot call a funcref directly, so it routes through Wasm. + let call_by_ref = ctx.module().exported_func::<(FuncRef, i32, i32), i32>(ctx.store(), "call_binop_by_ref")?; - // Call the callback we got as an argument using "call_binop_by_ref". - let res = proxy_caller.call(ctx.store_mut(), (fn_ref, 5, 3))?; - println!("(funcref {fn_ref:?})(5,3) results in {res}"); + let result = call_by_ref.call(ctx.store_mut(), (func_ref, LHS, RHS))?; + println!("(funcref {func_ref:?})({LHS},{RHS}) results in {result}"); Ok(()) }), )?; - // Import host.mul function (one of the functions whose references are taken). imports.define( "host", "mul", - Extern::typed_func(|_, args: (i32, i32)| -> tinywasm::Result { Ok(args.0 * args.1) }), + Extern::typed_func(|_, (lhs, rhs): (i32, i32)| -> tinywasm::Result { Ok(lhs * rhs) }), )?; let instance = module.instantiate(&mut store, Some(imports))?; let caller = instance.exported_func::<(), ()>(&store, "tell_host_to_call")?; - // Call "tell_host_to_call". caller.call(&mut store, ())?; - // An interesting detail is that neither $add, $sub, nor $mul were exported, - // but with a little help from the proxy "call_binop_by_ref", references to them are callable by the host. + Ok(()) } -/// Example of returning a Wasm function as a callback to a host function -/// and the host function calling it. -fn by_func_ref_returned() -> Result<()> { - // A module with: - // - An exported function "what_should_host_call" that returns 3 `funcref`s. - // - Wasm functions $add and $sub and an imported function $mul used as callbacks - // (just to show that imported functions can be referenced too). - // - Another exported Wasm function "call_binop_by_ref", a proxy used by the host to call func-references of type (i32, i32) -> i32 +fn run_returned_funcref_example() -> Result<()> { + // Wasm returns funcref values, host executes them through the same proxy. const WASM: &str = r#" (module (import "host" "mul" (func $host_mul (param $x i32) (param $y i32) (result i32))) @@ -125,33 +111,30 @@ fn by_func_ref_returned() -> Result<()> { ) "#; - let wasm = wat::parse_str(WASM).expect("Failed to parse WAT"); + let wasm = wat::parse_str(WASM).expect("failed to parse wat"); let module = Module::parse_bytes(&wasm)?; let mut store = Store::default(); let mut imports = Imports::new(); - // Import host.mul function (one of the possible operations). imports.define( "host", "mul", - Extern::typed_func(|_, args: (i32, i32)| -> tinywasm::Result { Ok(args.0 * args.1) }), + Extern::typed_func(|_, (lhs, rhs): (i32, i32)| -> tinywasm::Result { Ok(lhs * rhs) }), )?; let instance = module.instantiate(&mut store, Some(imports))?; - // Ask the module what to call. - let funcrefs = { - let address_getter = + let (add_ref, sub_ref, mul_ref) = { + let get_funcrefs = instance.exported_func::<(), (FuncRef, FuncRef, FuncRef)>(&store, "what_should_host_call")?; - address_getter.call(&mut store, ())? + get_funcrefs.call(&mut store, ())? }; - let proxy_caller = instance.exported_func::<(FuncRef, i32, i32), i32>(&store, "call_binop_by_ref")?; + let call_by_ref = instance.exported_func::<(FuncRef, i32, i32), i32>(&store, "call_binop_by_ref")?; - for (idx, func_ref) in [funcrefs.0, funcrefs.1, funcrefs.2].iter().enumerate() { - // Call those `funcref`s via "call_binop_by_ref". - let res = proxy_caller.call(&mut store, (*func_ref, 5, 3))?; - println!("At idx: {idx}, funcref {func_ref:?}(5,3) results in {res}"); + for (idx, func_ref) in [add_ref, sub_ref, mul_ref].iter().enumerate() { + let result = call_by_ref.call(&mut store, (*func_ref, LHS, RHS))?; + println!("At idx: {idx}, funcref {func_ref:?}({LHS},{RHS}) results in {result}"); } Ok(()) } -- cgit v1.3.1