From 7db07e03b56577997706adda1358bb43155b2aeb Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 19 Mar 2026 21:45:31 +0100 Subject: feat: add all simd instructions, add stackop macro Signed-off-by: Henry --- crates/tinywasm/src/interpreter/executor.rs | 999 ++++---- crates/tinywasm/src/interpreter/num_helpers.rs | 2 +- .../tinywasm/src/interpreter/stack/value_stack.rs | 14 +- crates/tinywasm/src/interpreter/value128.rs | 2412 +++++++++++++++++++- crates/tinywasm/src/store/memory.rs | 2 +- crates/tinywasm/tests/generated/wasm-simd.csv | 2 +- 6 files changed, 2793 insertions(+), 638 deletions(-) (limited to 'crates') diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index c186c01..08089f4 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -44,6 +44,36 @@ impl<'store, 'stack> Executor<'store, 'stack> { fn exec_next(&mut self) -> ControlFlow> { use tinywasm_types::Instruction::*; + macro_rules! stack_op { + (simd_unary $method:ident) => { + self.stack.values.unary_same::(|v| Ok(v.$method())).to_cf()? + }; + (simd_binary $method:ident) => { + self.stack.values.binary_same::(|a, b| Ok(a.$method(b))).to_cf()? + }; + (unary $ty:ty, |$v:ident| $expr:expr) => { + self.stack.values.unary_same::<$ty>(|$v| Ok($expr)).to_cf()? + }; + (binary $ty:ty, |$a:ident, $b:ident| $expr:expr) => { + self.stack.values.binary_same::<$ty>(|$a, $b| Ok($expr)).to_cf()? + }; + (binary_try $ty:ty, |$a:ident, $b:ident| $expr:expr) => { + self.stack.values.binary_same::<$ty>(|$a, $b| $expr).to_cf()? + }; + (unary $from:ty => $to:ty, |$v:ident| $expr:expr) => { + self.stack.values.unary::<$from, $to>(|$v| Ok($expr)).to_cf()? + }; + (binary $from:ty => $to:ty, |$a:ident, $b:ident| $expr:expr) => { + self.stack.values.binary::<$from, $to>(|$a, $b| Ok($expr)).to_cf()? + }; + (binary $a:ty, $b:ty, |$lhs:ident, $rhs:ident| $expr:expr) => { + self.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.stack.values.binary_diff::<$a, $b, $res>(|$lhs, $rhs| Ok($expr)).to_cf()? + }; + } + #[rustfmt::skip] match self.cf.fetch_instr() { Nop | BrLabel(_) | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | F64ReinterpretI64 => {} @@ -106,6 +136,8 @@ impl<'store, 'stack> Executor<'store, 'stack> { I64Const(val) => self.exec_const(*val), F32Const(val) => self.exec_const(*val), F64Const(val) => self.exec_const(*val), + + // Reference types RefFunc(func_idx) => self.exec_const::(Some(*func_idx)), RefNull(_) => self.exec_const::(None), RefIsNull => self.exec_ref_is_null(), @@ -119,8 +151,17 @@ impl<'store, 'stack> Executor<'store, 'stack> { MemoryInit(data_idx, mem_idx) => self.exec_memory_init(*data_idx, *mem_idx).to_cf()?, DataDrop(data_index) => self.exec_data_drop(*data_index), ElemDrop(elem_index) => self.exec_elem_drop(*elem_index), + + // Table instructions + TableGet(table_idx) => self.exec_table_get(*table_idx).to_cf()?, + TableSet(table_idx) => self.exec_table_set(*table_idx).to_cf()?, + TableSize(table_idx) => self.exec_table_size(*table_idx).to_cf()?, + TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx).to_cf()?, + TableGrow(table_idx) => self.exec_table_grow(*table_idx).to_cf()?, + TableFill(table_idx) => self.exec_table_fill(*table_idx).to_cf()?, TableCopy { from, to } => self.exec_table_copy(*from, *to).to_cf()?, + // Core memory load/store operations I32Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, I64Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, F32Store(m) => self.exec_mem_store::(m.mem_addr(), m.offset(), |v| v)?, @@ -146,138 +187,140 @@ impl<'store, 'stack> Executor<'store, 'stack> { I64Load32S(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, I64Load32U(m) => self.exec_mem_load::(m.mem_addr(), m.offset(), i64::from)?, - I64Eqz => self.stack.values.replace_top::(|v| Ok(i32::from(v == 0))).to_cf()?, - I32Eqz => self.stack.values.replace_top_same::(|v| Ok(i32::from(v == 0))).to_cf()?, - I32Eq => self.stack.values.calculate_same::(|a, b| Ok(i32::from(a == b))).to_cf()?, - I64Eq => self.stack.values.calculate::(|a, b| Ok(i32::from(a == b))).to_cf()?, - F32Eq => self.stack.values.calculate::(|a, b| Ok(i32::from(a == b))).to_cf()?, - F64Eq => self.stack.values.calculate::(|a, b| Ok(i32::from(a == b))).to_cf()?, - - I32Ne => self.stack.values.calculate_same::(|a, b| Ok(i32::from(a != b))).to_cf()?, - I64Ne => self.stack.values.calculate::(|a, b| Ok(i32::from(a != b))).to_cf()?, - F32Ne => self.stack.values.calculate::(|a, b| Ok(i32::from(a != b))).to_cf()?, - F64Ne => self.stack.values.calculate::(|a, b| Ok(i32::from(a != b))).to_cf()?, - - I32LtS => self.stack.values.calculate_same::(|a, b| Ok(i32::from(a < b))).to_cf()?, - I64LtS => self.stack.values.calculate::(|a, b| Ok(i32::from(a < b))).to_cf()?, - I32LtU => self.stack.values.calculate::(|a, b| Ok(i32::from(a < b))).to_cf()?, - I64LtU => self.stack.values.calculate::(|a, b| Ok(i32::from(a < b))).to_cf()?, - F32Lt => self.stack.values.calculate::(|a, b| Ok(i32::from(a < b))).to_cf()?, - F64Lt => self.stack.values.calculate::(|a, b| Ok(i32::from(a < b))).to_cf()?, - - I32LeS => self.stack.values.calculate_same::(|a, b| Ok(i32::from(a <= b))).to_cf()?, - I64LeS => self.stack.values.calculate::(|a, b| Ok(i32::from(a <= b))).to_cf()?, - I32LeU => self.stack.values.calculate::(|a, b| Ok(i32::from(a <= b))).to_cf()?, - I64LeU => self.stack.values.calculate::(|a, b| Ok(i32::from(a <= b))).to_cf()?, - F32Le => self.stack.values.calculate::(|a, b| Ok(i32::from(a <= b))).to_cf()?, - F64Le => self.stack.values.calculate::(|a, b| Ok(i32::from(a <= b))).to_cf()?, - - I32GeS => self.stack.values.calculate_same::(|a, b| Ok(i32::from(a >= b))).to_cf()?, - I64GeS => self.stack.values.calculate::(|a, b| Ok(i32::from(a >= b))).to_cf()?, - I32GeU => self.stack.values.calculate::(|a, b| Ok(i32::from(a >= b))).to_cf()?, - I64GeU => self.stack.values.calculate::(|a, b| Ok(i32::from(a >= b))).to_cf()?, - F32Ge => self.stack.values.calculate::(|a, b| Ok(i32::from(a >= b))).to_cf()?, - F64Ge => self.stack.values.calculate::(|a, b| Ok(i32::from(a >= b))).to_cf()?, - - I32GtS => self.stack.values.calculate_same::(|a, b| Ok(i32::from(a > b))).to_cf()?, - I64GtS => self.stack.values.calculate::(|a, b| Ok(i32::from(a > b))).to_cf()?, - I32GtU => self.stack.values.calculate::(|a, b| Ok(i32::from(a > b))).to_cf()?, - I64GtU => self.stack.values.calculate::(|a, b| Ok(i32::from(a > b))).to_cf()?, - F32Gt => self.stack.values.calculate::(|a, b| Ok(i32::from(a > b))).to_cf()?, - F64Gt => self.stack.values.calculate::(|a, b| Ok(i32::from(a > b))).to_cf()?, - - I32Add => self.stack.values.calculate_same::(|a, b| Ok(a.wrapping_add(b))).to_cf()?, - I64Add => self.stack.values.calculate_same::(|a, b| Ok(a.wrapping_add(b))).to_cf()?, - F32Add => self.stack.values.calculate_same::(|a, b| Ok(a + b)).to_cf()?, - F64Add => self.stack.values.calculate_same::(|a, b| Ok(a + b)).to_cf()?, - - I32Sub => self.stack.values.calculate_same::(|a, b| Ok(a.wrapping_sub(b))).to_cf()?, - I64Sub => self.stack.values.calculate_same::(|a, b| Ok(a.wrapping_sub(b))).to_cf()?, - F32Sub => self.stack.values.calculate_same::(|a, b| Ok(a - b)).to_cf()?, - F64Sub => self.stack.values.calculate_same::(|a, b| Ok(a - b)).to_cf()?, - - F32Div => self.stack.values.calculate_same::(|a, b| Ok(a / b)).to_cf()?, - F64Div => self.stack.values.calculate_same::(|a, b| Ok(a / b)).to_cf()?, - - I32Mul => self.stack.values.calculate_same::(|a, b| Ok(a.wrapping_mul(b))).to_cf()?, - I64Mul => self.stack.values.calculate_same::(|a, b| Ok(a.wrapping_mul(b))).to_cf()?, - F32Mul => self.stack.values.calculate_same::(|a, b| Ok(a * b)).to_cf()?, - F64Mul => self.stack.values.calculate_same::(|a, b| Ok(a * b)).to_cf()?, - - I32DivS => self.stack.values.calculate_same::(|a, b| a.wasm_checked_div(b)).to_cf()?, - I64DivS => self.stack.values.calculate_same::(|a, b| a.wasm_checked_div(b)).to_cf()?, - I32DivU => self.stack.values.calculate_same::(|a, b| a.checked_div(b).ok_or_else(trap_0)).to_cf()?, - I64DivU => self.stack.values.calculate_same::(|a, b| a.checked_div(b).ok_or_else(trap_0)).to_cf()?, - I32RemS => self.stack.values.calculate_same::(|a, b| a.checked_wrapping_rem(b)).to_cf()?, - I64RemS => self.stack.values.calculate_same::(|a, b| a.checked_wrapping_rem(b)).to_cf()?, - I32RemU => self.stack.values.calculate_same::(|a, b| a.checked_wrapping_rem(b)).to_cf()?, - I64RemU => self.stack.values.calculate_same::(|a, b| a.checked_wrapping_rem(b)).to_cf()?, - - I32And => self.stack.values.calculate_same::(|a, b| Ok(a & b)).to_cf()?, - I64And => self.stack.values.calculate_same::(|a, b| Ok(a & b)).to_cf()?, - I32Or => self.stack.values.calculate_same::(|a, b| Ok(a | b)).to_cf()?, - I64Or => self.stack.values.calculate_same::(|a, b| Ok(a | b)).to_cf()?, - I32Xor => self.stack.values.calculate_same::(|a, b| Ok(a ^ b)).to_cf()?, - I64Xor => self.stack.values.calculate_same::(|a, b| Ok(a ^ b)).to_cf()?, - I32Shl => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_shl(b))).to_cf()?, - I64Shl => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_shl(b))).to_cf()?, - I32ShrS => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_shr(b))).to_cf()?, - I64ShrS => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_shr(b))).to_cf()?, - I32ShrU => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_shr(b))).to_cf()?, - I64ShrU => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_shr(b))).to_cf()?, - I32Rotl => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_rotl(b))).to_cf()?, - I64Rotl => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_rotl(b))).to_cf()?, - I32Rotr => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_rotr(b))).to_cf()?, - I64Rotr => self.stack.values.calculate_same::(|a, b| Ok(a.wasm_rotr(b))).to_cf()?, - - I32Clz => self.stack.values.replace_top_same::(|v| Ok(v.leading_zeros() as i32)).to_cf()?, - I64Clz => self.stack.values.replace_top_same::(|v| Ok(i64::from(v.leading_zeros()))).to_cf()?, - I32Ctz => self.stack.values.replace_top_same::(|v| Ok(v.trailing_zeros() as i32)).to_cf()?, - I64Ctz => self.stack.values.replace_top_same::(|v| Ok(i64::from(v.trailing_zeros()))).to_cf()?, - I32Popcnt => self.stack.values.replace_top_same::(|v| Ok(v.count_ones() as i32)).to_cf()?, - I64Popcnt => self.stack.values.replace_top_same::(|v| Ok(i64::from(v.count_ones()))).to_cf()?, - - F32ConvertI32S => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F32ConvertI64S => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F64ConvertI32S => self.stack.values.replace_top::(|v| Ok(f64::from(v))).to_cf()?, - F64ConvertI64S => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, - F32ConvertI32U => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F32ConvertI64U => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F64ConvertI32U => self.stack.values.replace_top::(|v| Ok(f64::from(v))).to_cf()?, - F64ConvertI64U => self.stack.values.replace_top::(|v| Ok(v as f64)).to_cf()?, - - I32Extend8S => self.stack.values.replace_top_same::(|v| Ok(i32::from(v as i8))).to_cf()?, - I32Extend16S => self.stack.values.replace_top_same::(|v| Ok(i32::from(v as i16))).to_cf()?, - I64Extend8S => self.stack.values.replace_top_same::(|v| Ok(i64::from(v as i8))).to_cf()?, - I64Extend16S => self.stack.values.replace_top_same::(|v| Ok(i64::from(v as i16))).to_cf()?, - I64Extend32S => self.stack.values.replace_top_same::(|v| Ok(i64::from(v as i32))).to_cf()?, - I64ExtendI32U => self.stack.values.replace_top::(|v| Ok(i64::from(v))).to_cf()?, - I64ExtendI32S => self.stack.values.replace_top::(|v| Ok(i64::from(v))).to_cf()?, - I32WrapI64 => self.stack.values.replace_top::(|v| Ok(v as i32)).to_cf()?, - - F32DemoteF64 => self.stack.values.replace_top::(|v| Ok(v as f32)).to_cf()?, - F64PromoteF32 => self.stack.values.replace_top::(|v| Ok(f64::from(v))).to_cf()?, - - F32Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, - F64Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, - F32Neg => self.stack.values.replace_top_same::(|v| Ok(-v)).to_cf()?, - F64Neg => self.stack.values.replace_top_same::(|v| Ok(-v)).to_cf()?, - F32Ceil => self.stack.values.replace_top_same::(|v| Ok(v.ceil())).to_cf()?, - F64Ceil => self.stack.values.replace_top_same::(|v| Ok(v.ceil())).to_cf()?, - F32Floor => self.stack.values.replace_top_same::(|v| Ok(v.floor())).to_cf()?, - F64Floor => self.stack.values.replace_top_same::(|v| Ok(v.floor())).to_cf()?, - F32Trunc => self.stack.values.replace_top_same::(|v| Ok(v.trunc())).to_cf()?, - F64Trunc => self.stack.values.replace_top_same::(|v| Ok(v.trunc())).to_cf()?, - F32Nearest => self.stack.values.replace_top_same::(|v| Ok(v.tw_nearest())).to_cf()?, - F64Nearest => self.stack.values.replace_top_same::(|v| Ok(v.tw_nearest())).to_cf()?, - F32Sqrt => self.stack.values.replace_top_same::(|v| Ok(v.sqrt())).to_cf()?, - F64Sqrt => self.stack.values.replace_top_same::(|v| Ok(v.sqrt())).to_cf()?, - F32Min => self.stack.values.calculate_same::(|a, b| Ok(a.tw_minimum(b))).to_cf()?, - F64Min => self.stack.values.calculate_same::(|a, b| Ok(a.tw_minimum(b))).to_cf()?, - F32Max => self.stack.values.calculate_same::(|a, b| Ok(a.tw_maximum(b))).to_cf()?, - F64Max => self.stack.values.calculate_same::(|a, b| Ok(a.tw_maximum(b))).to_cf()?, - F32Copysign => self.stack.values.calculate_same::(|a, b| Ok(a.copysign(b))).to_cf()?, - F64Copysign => self.stack.values.calculate_same::(|a, b| Ok(a.copysign(b))).to_cf()?, + I64Eqz => stack_op!(unary i64 => i32, |v| i32::from(v == 0)), + I32Eqz => stack_op!(unary i32, |v| i32::from(v == 0)), + I32Eq => stack_op!(binary i32, |a, b| i32::from(a == b)), + I64Eq => stack_op!(binary i64 => i32, |a, b| i32::from(a == b)), + F32Eq => stack_op!(binary f32 => i32, |a, b| i32::from(a == b)), + F64Eq => stack_op!(binary f64 => i32, |a, b| i32::from(a == b)), + + I32Ne => stack_op!(binary i32, |a, b| i32::from(a != b)), + I64Ne => stack_op!(binary i64 => i32, |a, b| i32::from(a != b)), + F32Ne => stack_op!(binary f32 => i32, |a, b| i32::from(a != b)), + F64Ne => stack_op!(binary f64 => i32, |a, b| i32::from(a != b)), + + I32LtS => stack_op!(binary i32, |a, b| i32::from(a < b)), + I64LtS => stack_op!(binary i64 => i32, |a, b| i32::from(a < b)), + I32LtU => stack_op!(binary u32 => i32, |a, b| i32::from(a < b)), + I64LtU => stack_op!(binary u64 => i32, |a, b| i32::from(a < b)), + F32Lt => stack_op!(binary f32 => i32, |a, b| i32::from(a < b)), + F64Lt => stack_op!(binary f64 => i32, |a, b| i32::from(a < b)), + + I32LeS => stack_op!(binary i32, |a, b| i32::from(a <= b)), + I64LeS => stack_op!(binary i64 => i32, |a, b| i32::from(a <= b)), + I32LeU => stack_op!(binary u32 => i32, |a, b| i32::from(a <= b)), + I64LeU => stack_op!(binary u64 => i32, |a, b| i32::from(a <= b)), + F32Le => stack_op!(binary f32 => i32, |a, b| i32::from(a <= b)), + F64Le => stack_op!(binary f64 => i32, |a, b| i32::from(a <= b)), + + I32GeS => stack_op!(binary i32, |a, b| i32::from(a >= b)), + I64GeS => stack_op!(binary i64 => i32, |a, b| i32::from(a >= b)), + I32GeU => stack_op!(binary u32 => i32, |a, b| i32::from(a >= b)), + I64GeU => stack_op!(binary u64 => i32, |a, b| i32::from(a >= b)), + F32Ge => stack_op!(binary f32 => i32, |a, b| i32::from(a >= b)), + F64Ge => stack_op!(binary f64 => i32, |a, b| i32::from(a >= b)), + + I32GtS => stack_op!(binary i32, |a, b| i32::from(a > b)), + I64GtS => stack_op!(binary i64 => i32, |a, b| i32::from(a > b)), + I32GtU => stack_op!(binary u32 => i32, |a, b| i32::from(a > b)), + I64GtU => stack_op!(binary u64 => i32, |a, b| i32::from(a > b)), + F32Gt => stack_op!(binary f32 => i32, |a, b| i32::from(a > b)), + F64Gt => stack_op!(binary f64 => i32, |a, b| i32::from(a > b)), + + I32Add => stack_op!(binary i32, |a, b| a.wrapping_add(b)), + I64Add => stack_op!(binary i64, |a, b| a.wrapping_add(b)), + F32Add => stack_op!(binary f32, |a, b| a + b), + F64Add => stack_op!(binary f64, |a, b| a + b), + + I32Sub => stack_op!(binary i32, |a, b| a.wrapping_sub(b)), + I64Sub => stack_op!(binary i64, |a, b| a.wrapping_sub(b)), + F32Sub => stack_op!(binary f32, |a, b| a - b), + F64Sub => stack_op!(binary f64, |a, b| a - b), + + F32Div => stack_op!(binary f32, |a, b| a / b), + F64Div => stack_op!(binary f64, |a, b| a / b), + + I32Mul => stack_op!(binary i32, |a, b| a.wrapping_mul(b)), + I64Mul => stack_op!(binary i64, |a, b| a.wrapping_mul(b)), + F32Mul => stack_op!(binary f32, |a, b| a * b), + F64Mul => stack_op!(binary f64, |a, b| a * b), + + I32DivS => stack_op!(binary_try i32, |a, b| a.wasm_checked_div(b)), + I64DivS => stack_op!(binary_try i64, |a, b| a.wasm_checked_div(b)), + I32DivU => stack_op!(binary_try u32, |a, b| a.checked_div(b).ok_or_else(trap_0)), + I64DivU => stack_op!(binary_try u64, |a, b| a.checked_div(b).ok_or_else(trap_0)), + I32RemS => stack_op!(binary_try i32, |a, b| a.checked_wrapping_rem(b)), + I64RemS => stack_op!(binary_try i64, |a, b| a.checked_wrapping_rem(b)), + I32RemU => stack_op!(binary_try u32, |a, b| a.checked_wrapping_rem(b)), + I64RemU => stack_op!(binary_try u64, |a, b| a.checked_wrapping_rem(b)), + + I32And => stack_op!(binary i32, |a, b| a & b), + I64And => stack_op!(binary i64, |a, b| a & b), + I32Or => stack_op!(binary i32, |a, b| a | b), + I64Or => stack_op!(binary i64, |a, b| a | b), + I32Xor => stack_op!(binary i32, |a, b| a ^ b), + I64Xor => stack_op!(binary i64, |a, b| a ^ b), + I32Shl => stack_op!(binary i32, |a, b| a.wasm_shl(b)), + I64Shl => stack_op!(binary i64, |a, b| a.wasm_shl(b)), + I32ShrS => stack_op!(binary i32, |a, b| a.wasm_shr(b)), + I64ShrS => stack_op!(binary i64, |a, b| a.wasm_shr(b)), + I32ShrU => stack_op!(binary u32, |a, b| a.wasm_shr(b)), + I64ShrU => stack_op!(binary u64, |a, b| a.wasm_shr(b)), + I32Rotl => stack_op!(binary i32, |a, b| a.wasm_rotl(b)), + I64Rotl => stack_op!(binary i64, |a, b| a.wasm_rotl(b)), + I32Rotr => stack_op!(binary i32, |a, b| a.wasm_rotr(b)), + I64Rotr => stack_op!(binary i64, |a, b| a.wasm_rotr(b)), + + I32Clz => stack_op!(unary i32, |v| v.leading_zeros() as i32), + I64Clz => stack_op!(unary i64, |v| i64::from(v.leading_zeros())), + I32Ctz => stack_op!(unary i32, |v| v.trailing_zeros() as i32), + I64Ctz => stack_op!(unary i64, |v| i64::from(v.trailing_zeros())), + I32Popcnt => stack_op!(unary i32, |v| v.count_ones() as i32), + I64Popcnt => stack_op!(unary i64, |v| i64::from(v.count_ones())), + + // Numeric conversion operations + F32ConvertI32S => stack_op!(unary i32 => f32, |v| v as f32), + F32ConvertI64S => stack_op!(unary i64 => f32, |v| v as f32), + F64ConvertI32S => stack_op!(unary i32 => f64, |v| f64::from(v)), + F64ConvertI64S => stack_op!(unary i64 => f64, |v| v as f64), + F32ConvertI32U => stack_op!(unary u32 => f32, |v| v as f32), + F32ConvertI64U => stack_op!(unary u64 => f32, |v| v as f32), + F64ConvertI32U => stack_op!(unary u32 => f64, |v| f64::from(v)), + F64ConvertI64U => stack_op!(unary u64 => f64, |v| v as f64), + + // Sign-extension operations + I32Extend8S => stack_op!(unary i32, |v| i32::from(v as i8)), + I32Extend16S => stack_op!(unary i32, |v| i32::from(v as i16)), + I64Extend8S => stack_op!(unary i64, |v| i64::from(v as i8)), + I64Extend16S => stack_op!(unary i64, |v| i64::from(v as i16)), + I64Extend32S => stack_op!(unary i64, |v| i64::from(v as i32)), + I64ExtendI32U => stack_op!(unary u32 => i64, |v| i64::from(v)), + I64ExtendI32S => stack_op!(unary i32 => i64, |v| i64::from(v)), + I32WrapI64 => stack_op!(unary i64 => i32, |v| v as i32), + + F32DemoteF64 => stack_op!(unary f64 => f32, |v| v as f32), + F64PromoteF32 => stack_op!(unary f32 => f64, |v| f64::from(v)), + + F32Abs => stack_op!(unary f32, |v| v.abs()), + F64Abs => stack_op!(unary f64, |v| v.abs()), + F32Neg => stack_op!(unary f32, |v| -v), + F64Neg => stack_op!(unary f64, |v| -v), + F32Ceil => stack_op!(unary f32, |v| v.ceil()), + F64Ceil => stack_op!(unary f64, |v| v.ceil()), + F32Floor => stack_op!(unary f32, |v| v.floor()), + F64Floor => stack_op!(unary f64, |v| v.floor()), + F32Trunc => stack_op!(unary f32, |v| v.trunc()), + F64Trunc => stack_op!(unary f64, |v| v.trunc()), + F32Nearest => stack_op!(unary f32, |v| v.tw_nearest()), + F64Nearest => stack_op!(unary f64, |v| v.tw_nearest()), + F32Sqrt => stack_op!(unary f32, |v| v.sqrt()), + F64Sqrt => stack_op!(unary f64, |v| v.sqrt()), + F32Min => stack_op!(binary f32, |a, b| a.tw_minimum(b)), + F64Min => stack_op!(binary f64, |a, b| a.tw_minimum(b)), + F32Max => stack_op!(binary f32, |a, b| a.tw_maximum(b)), + F64Max => stack_op!(binary f64, |a, b| a.tw_maximum(b)), + F32Copysign => stack_op!(binary f32, |a, b| a.copysign(b)), + F64Copysign => stack_op!(binary f64, |a, b| a.copysign(b)), I32TruncF32S => checked_conv_float!(f32, i32, self), I32TruncF64S => checked_conv_float!(f64, i32, self), @@ -288,408 +331,306 @@ impl<'store, 'stack> Executor<'store, 'stack> { I64TruncF32U => checked_conv_float!(f32, u64, i64, self), I64TruncF64U => checked_conv_float!(f64, u64, i64, self), - TableGet(table_idx) => self.exec_table_get(*table_idx).to_cf()?, - TableSet(table_idx) => self.exec_table_set(*table_idx).to_cf()?, - TableSize(table_idx) => self.exec_table_size(*table_idx).to_cf()?, - TableInit(elem_idx, table_idx) => self.exec_table_init(*elem_idx, *table_idx).to_cf()?, - TableGrow(table_idx) => self.exec_table_grow(*table_idx).to_cf()?, - TableFill(table_idx) => self.exec_table_fill(*table_idx).to_cf()?, - - I32TruncSatF32S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i32)).to_cf()?, - I32TruncSatF32U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u32)).to_cf()?, - I32TruncSatF64S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i32)).to_cf()?, - I32TruncSatF64U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u32)).to_cf()?, - I64TruncSatF32S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i64)).to_cf()?, - I64TruncSatF32U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u64)).to_cf()?, - I64TruncSatF64S => self.stack.values.replace_top::(|v| Ok(v.trunc() as i64)).to_cf()?, - I64TruncSatF64U => self.stack.values.replace_top::(|v| Ok(v.trunc() as u64)).to_cf()?, + // Non-trapping float-to-int conversions + I32TruncSatF32S => stack_op!(unary f32 => i32, |v| v.trunc() as i32), + I32TruncSatF32U => stack_op!(unary f32 => u32, |v| v.trunc() as u32), + I32TruncSatF64S => stack_op!(unary f64 => i32, |v| v.trunc() as i32), + I32TruncSatF64U => stack_op!(unary f64 => u32, |v| v.trunc() as u32), + I64TruncSatF32S => stack_op!(unary f32 => i64, |v| v.trunc() as i64), + I64TruncSatF32U => stack_op!(unary f32 => u64, |v| v.trunc() as u64), + I64TruncSatF64S => stack_op!(unary f64 => i64, |v| v.trunc() as i64), + I64TruncSatF64U => stack_op!(unary f64 => u64, |v| v.trunc() as u64), LocalCopy32(from, to) => self.exec_local_copy::(*from, *to), LocalCopy64(from, to) => self.exec_local_copy::(*from, *to), LocalCopy128(from, to) => self.exec_local_copy::(*from, *to), LocalCopyRef(from, to) => self.exec_local_copy::(*from, *to), - V128Not => self.stack.values.replace_top_same::(|v| Ok(!v)).to_cf()?, - V128And => self.stack.values.calculate_same::(|a, b| Ok(a & b)).to_cf()?, - V128AndNot => self.stack.values.calculate_same::(|a, b| Ok(a & (!b))).to_cf()?, - V128Or => self.stack.values.calculate_same::(|a, b| Ok(a | b)).to_cf()?, - V128Xor => self.stack.values.calculate_same::(|a, b| Ok(a ^ b)).to_cf()?, - V128Bitselect => self.stack.values.calculate_same_3::(|v1, v2, c| Ok((v1 & c) | (v2 & !c))).to_cf()?, - V128AnyTrue => self.stack.values.replace_top::(|v| Ok((v.reduce_or() != 0) as i32)).to_cf()?, - I8x16Swizzle => self.stack.values.calculate_same::(|a, s| Ok(a.swizzle(s))).to_cf()?, - - V128Load(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| v)?, - V128Load8x8S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), Value128::extend_8_i8)?, - V128Load8x8U(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::extend_8_u8)?, - V128Load16x4S(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::extend_4_i16)?, - V128Load16x4U(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::extend_4_u16)?, - V128Load32x2S(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::extend_2_i32)?, - V128Load32x2U(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::extend_2_u32)?, - V128Load8Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i8)?, - V128Load16Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i16)?, - V128Load32Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i32)?, - V128Load64Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i64)?, - - V128Store(arg) => self.exec_mem_store::(arg.mem_addr(), arg.offset(), |v| v)?, - - V128Store8Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Store16Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Store32Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Store64Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + // SIMD extension + V128Not => stack_op!(unary Value128, |v| v.v128_not()), + V128And => stack_op!(binary Value128, |a, b| a.v128_and(b)), + V128AndNot => stack_op!(binary Value128, |a, b| a.v128_andnot(b)), + V128Or => stack_op!(binary Value128, |a, b| a.v128_or(b)), + V128Xor => stack_op!(binary Value128, |a, b| a.v128_xor(b)), + V128Bitselect => self.stack.values.ternary_same::(|v1, v2, c| Ok(Value128::v128_bitselect(v1, v2, c))).to_cf()?, + 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)?, + V128Load8x8S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_s(v.to_le_bytes()))?, + V128Load8x8U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load8x8_u(v.to_le_bytes()))?, + V128Load16x4S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_s(v.to_le_bytes()))?, + V128Load16x4U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load16x4_u(v.to_le_bytes()))?, + V128Load32x2S(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_s(v.to_le_bytes()))?, + V128Load32x2U(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::v128_load32x2_u(v.to_le_bytes()))?, + V128Load8Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i8)?, + V128Load16Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i16)?, + V128Load32Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i32)?, + V128Load64Splat(_arg) => self.exec_mem_load::(_arg.mem_addr(), _arg.offset(), Value128::splat_i64)?, + + V128Store(arg) => self.exec_mem_store::(arg.mem_addr(), arg.offset(), |v| v)?, + + V128Store8Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Store16Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Store32Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Store64Lane(arg, lane) => self.exec_mem_store_lane::(arg.mem_addr(), arg.offset(), *lane)?, // Load a single 32-bit or 64-bit element into the lowest bits of a v128 vector, and initialize all other bits of the v128 vector to zero. - V128Load32Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?, - V128Load64Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?, - - V128Const(arg) => self.exec_const::( self.cf.data().v128_constants[*arg as usize].into()), - - I8x16ExtractLaneS(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_i8(*lane) as i32)).to_cf()?, - I8x16ExtractLaneU(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_u8(*lane) as i32)).to_cf()?, - I16x8ExtractLaneS(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_i16(*lane) as i32)).to_cf()?, - I16x8ExtractLaneU(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_u16(*lane) as i32)).to_cf()?, - I32x4ExtractLane(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_i32(*lane))).to_cf()?, - I64x2ExtractLane(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_i64(*lane))).to_cf()?, - F32x4ExtractLane(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_f32(*lane))).to_cf()?, - F64x2ExtractLane(lane) => self.stack.values.replace_top::(|v| Ok(v.extract_lane_f64(*lane))).to_cf()?, - - V128Load8Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load16Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load32Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, - V128Load64Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, - - // I8x16ReplaceLane(_lane) => unimplemented!(), - // I16x8ReplaceLane(_lane) => unimplemented!(), - // I32x4ReplaceLane(_lane) => unimplemented!(), - // I64x2ReplaceLane(_lane) => unimplemented!(), - // F32x4ReplaceLane(_lane) => unimplemented!(), - // F64x2ReplaceLane(_lane) => unimplemented!(), - - I8x16Splat => self.stack.values.replace_top::(|v| Ok(Value128::splat_i8(v as i8))).to_cf()?, - I16x8Splat => self.stack.values.replace_top::(|v| Ok(Value128::splat_i16(v as i16))).to_cf()?, - I32x4Splat => self.stack.values.replace_top::(|v| Ok(Value128::splat_i32(v))).to_cf()?, - I64x2Splat => self.stack.values.replace_top::(|v| Ok(Value128::splat_i64(v))).to_cf()?, - F32x4Splat => self.stack.values.replace_top::(|v| Ok(Value128::splat_f32(v))).to_cf()?, - F64x2Splat => self.stack.values.replace_top::(|v| Ok(Value128::splat_f64(v))).to_cf()?, - - // I8x16Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, - // I16x8Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, - // I32x4Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, - // I64x2Eq => self.stack.values.calculate_same::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, - // F32x4Eq => self.stack.values.calculate::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, - // F64x2Eq => self.stack.values.calculate::(|a, b| Ok(a.simd_eq(b).to_int())).to_cf()?, - - // I8x16Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, - // I16x8Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, - // I32x4Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, - // I64x2Ne => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, - // F32x4Ne => self.stack.values.calculate::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, - // F64x2Ne => self.stack.values.calculate::(|a, b| Ok(a.simd_ne(b).to_int())).to_cf()?, - - // I8x16LtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // I16x8LtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // I32x4LtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // I64x2LtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // I8x16LtU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // I16x8LtU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // I32x4LtU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // F32x4Lt => self.stack.values.calculate::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - // F64x2Lt => self.stack.values.calculate::(|a, b| Ok(a.simd_lt(b).to_int())).to_cf()?, - - // F32x4Gt => self.stack.values.calculate::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // F64x2Gt => self.stack.values.calculate::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - - // I8x16GtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // I16x8GtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // I32x4GtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // I64x2GtS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // I64x2LeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - // F32x4Le => self.stack.values.calculate::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - // F64x2Le => self.stack.values.calculate::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - - // I8x16GtU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // I16x8GtU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // I32x4GtU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_gt(b).to_int())).to_cf()?, - // F32x4Ge => self.stack.values.calculate::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - // F64x2Ge => self.stack.values.calculate::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - - // I8x16LeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - // I16x8LeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - // I32x4LeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - - // I8x16LeU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - // I16x8LeU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - // I32x4LeU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_le(b).to_int())).to_cf()?, - - // I8x16GeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - // I16x8GeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - // I32x4GeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - // I64x2GeS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - - // I8x16GeU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - // I16x8GeU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - // I32x4GeU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_ge(b).to_int())).to_cf()?, - - // I8x16Abs => self.stack.values.replace_top_same::(|a| Ok(a.abs())).to_cf()?, - // I16x8Abs => self.stack.values.replace_top_same::(|a| Ok(a.abs())).to_cf()?, - // I32x4Abs => self.stack.values.replace_top_same::(|a| Ok(a.abs())).to_cf()?, - // I64x2Abs => self.stack.values.replace_top_same::(|a| Ok(a.abs())).to_cf()?, - - // I8x16Neg => self.stack.values.replace_top_same::(|a| Ok(-a)).to_cf()?, - // I16x8Neg => self.stack.values.replace_top_same::(|a| Ok(-a)).to_cf()?, - // I32x4Neg => self.stack.values.replace_top_same::(|a| Ok(-a)).to_cf()?, - // I64x2Neg => self.stack.values.replace_top_same::(|a| Ok(-a)).to_cf()?, - - // I8x16AllTrue => self.stack.values.replace_top::(|v| Ok((v.simd_ne(Simd::splat(0)).all()) as i32)).to_cf()?, - // I16x8AllTrue => self.stack.values.replace_top::(|v| Ok((v.simd_ne(Simd::splat(0)).all()) as i32)).to_cf()?, - // I32x4AllTrue => self.stack.values.replace_top::(|v| Ok((v.simd_ne(Simd::splat(0)).all()) as i32)).to_cf()?, - // I64x2AllTrue => self.stack.values.replace_top::(|v| Ok((v.simd_ne(Simd::splat(0)).all()) as i32)).to_cf()?, - - // I8x16Bitmask => self.stack.values.replace_top::(|v| Ok(v.simd_lt(Simd::splat(0)).to_bitmask() as i32)).to_cf()?, - // I16x8Bitmask => self.stack.values.replace_top::(|v| Ok(v.simd_lt(Simd::splat(0)).to_bitmask() as i32)).to_cf()?, - // I32x4Bitmask => self.stack.values.replace_top::(|v| Ok(v.simd_lt(Simd::splat(0)).to_bitmask() as i32)).to_cf()?, - // I64x2Bitmask => self.stack.values.replace_top::(|v| Ok(v.simd_lt(Simd::splat(0)).to_bitmask() as i32)).to_cf()?, - - // I8x16Shl => self.stack.values.calculate_diff::(|a, b| Ok(b.shl(a as i8))).to_cf()?, - // I16x8Shl => self.stack.values.calculate_diff::(|a, b| Ok(b.shl(a as i16))).to_cf()?, - // I32x4Shl => self.stack.values.calculate_diff::(|a, b| Ok(b.shl(a))).to_cf()?, - // I64x2Shl => self.stack.values.calculate_diff::(|a, b| Ok(b.shl(a as i64))).to_cf()?, - - // I8x16ShrS => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as i8))).to_cf()?, - // I16x8ShrS => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as i16))).to_cf()?, - // I32x4ShrS => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a))).to_cf()?, - // I64x2ShrS => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as i64))).to_cf()?, - - // I8x16ShrU => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as u8))).to_cf()?, - // I16x8ShrU => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as u16))).to_cf()?, - // I32x4ShrU => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as u32))).to_cf()?, - // I64x2ShrU => self.stack.values.calculate_diff::(|a, b| Ok(b.shr(a as u64))).to_cf()?, - - // I8x16Add => self.stack.values.calculate_same::(|a, b| Ok(a + b)).to_cf()?, - // I16x8Add => self.stack.values.calculate_same::(|a, b| Ok(a + b)).to_cf()?, - // I32x4Add => self.stack.values.calculate_same::(|a, b| Ok(a + b)).to_cf()?, - // I64x2Add => self.stack.values.calculate_same::(|a, b| Ok(a + b)).to_cf()?, - - // I8x16Sub => self.stack.values.calculate_same::(|a, b| Ok(a - b)).to_cf()?, - // I16x8Sub => self.stack.values.calculate_same::(|a, b| Ok(a - b)).to_cf()?, - // I32x4Sub => self.stack.values.calculate_same::(|a, b| Ok(a - b)).to_cf()?, - // I64x2Sub => self.stack.values.calculate_same::(|a, b| Ok(a - b)).to_cf()?, - - // I8x16MinS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_min(b))).to_cf()?, - // I16x8MinS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_min(b))).to_cf()?, - // I32x4MinS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_min(b))).to_cf()?, - - // I8x16MinU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_min(b))).to_cf()?, - // I16x8MinU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_min(b))).to_cf()?, - // I32x4MinU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_min(b))).to_cf()?, - - // I8x16MaxS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_max(b))).to_cf()?, - // I16x8MaxS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_max(b))).to_cf()?, - // I32x4MaxS => self.stack.values.calculate_same::(|a, b| Ok(a.simd_max(b))).to_cf()?, - - // I8x16MaxU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_max(b))).to_cf()?, - // I16x8MaxU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_max(b))).to_cf()?, - // I32x4MaxU => self.stack.values.calculate_same::(|a, b| Ok(a.simd_max(b))).to_cf()?, - - // I64x2Mul => self.stack.values.calculate_same::(|a, b| Ok(a * b)).to_cf()?, - // I16x8Mul => self.stack.values.calculate_same::(|a, b| Ok(a * b)).to_cf()?, - // I32x4Mul => self.stack.values.calculate_same::(|a, b| Ok(a * b)).to_cf()?, - - // I8x16NarrowI16x8S => unimplemented!(), - // I8x16NarrowI16x8U => unimplemented!(), - // I16x8NarrowI32x4S => unimplemented!(), - // I16x8NarrowI32x4U => unimplemented!(), - - // I8x16AddSatS => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_add(b))).to_cf()?, - // I16x8AddSatS => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_add(b))).to_cf()?, - // I8x16AddSatU => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_add(b))).to_cf()?, - // I16x8AddSatU => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_add(b))).to_cf()?, - // I8x16SubSatS => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_sub(b))).to_cf()?, - // I16x8SubSatS => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_sub(b))).to_cf()?, - // I8x16SubSatU => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_sub(b))).to_cf()?, - // I16x8SubSatU => self.stack.values.calculate_same::(|a, b| Ok(a.saturating_sub(b))).to_cf()?, - - // I8x16AvgrU => unimplemented!(), - // I16x8AvgrU => unimplemented!(), - - // I16x8ExtAddPairwiseI8x16S => unimplemented!(), - // I16x8ExtAddPairwiseI8x16U => unimplemented!(), - // I32x4ExtAddPairwiseI16x8S => unimplemented!(), - // I32x4ExtAddPairwiseI16x8U => unimplemented!(), - - // I16x8ExtMulLowI8x16S => unimplemented!(), - // I16x8ExtMulLowI8x16U => unimplemented!(), - // I16x8ExtMulHighI8x16S => unimplemented!(), - // I16x8ExtMulHighI8x16U => unimplemented!(), - // I32x4ExtMulLowI16x8S => unimplemented!(), - // I32x4ExtMulLowI16x8U => unimplemented!(), - // I32x4ExtMulHighI16x8S => unimplemented!(), - // I32x4ExtMulHighI16x8U => unimplemented!(), - // I64x2ExtMulLowI32x4S => unimplemented!(), - // I64x2ExtMulLowI32x4U => unimplemented!(), - // I64x2ExtMulHighI32x4S => unimplemented!(), - // I64x2ExtMulHighI32x4U => unimplemented!(), - - // I16x8ExtendLowI8x16S => unimplemented!(), - // I16x8ExtendLowI8x16U => unimplemented!(), - // I16x8ExtendHighI8x16S => unimplemented!(), - // I16x8ExtendHighI8x16U => unimplemented!(), - // I32x4ExtendLowI16x8S => unimplemented!(), - // I32x4ExtendLowI16x8U => unimplemented!(), - // I32x4ExtendHighI16x8S => unimplemented!(), - // I32x4ExtendHighI16x8U => unimplemented!(), - // I64x2ExtendLowI32x4S => unimplemented!(), - // I64x2ExtendLowI32x4U => unimplemented!(), - // I64x2ExtendHighI32x4S => unimplemented!(), - // I64x2ExtendHighI32x4U => unimplemented!(), - - // I8x16Popcnt => self.stack.values.replace_top::(|v| Ok(v.count_ones())).to_cf()?, - // I8x16Shuffle(_idx) => unimplemented!(), - - I16x8Q15MulrSatS => self.stack.values.calculate_same::(|a, b| { - let subq15mulr = |a,b| { - let a = a as i32; - let b = b as i32; - let r = (a * b + 0x4000) >> 15; - if r > i16::MAX as i32 { - i16::MAX - } else if r < i16::MIN as i32 { - i16::MIN - } else { - r as i16 - } - }; - let a = a.as_i16x8(); - let b = b.as_i16x8(); - Ok(Value128::from_i16x8([ - subq15mulr(a[0], b[0]), - subq15mulr(a[1], b[1]), - subq15mulr(a[2], b[2]), - subq15mulr(a[3], b[3]), - subq15mulr(a[4], b[4]), - subq15mulr(a[5], b[5]), - subq15mulr(a[6], b[6]), - subq15mulr(a[7], b[7]), - ])) - }).to_cf()?, - - - I32x4DotI16x8S => self.stack.values.calculate::(|a, b| { - let a = a.as_i16x8(); - let b = b.as_i16x8(); - Ok(Value128::from_i32x4([ - i32::from(a[0] * b[0] + a[1] * b[1]), - i32::from(a[2] * b[2] + a[3] * b[3]), - i32::from(a[4] * b[4] + a[5] * b[5]), - i32::from(a[6] * b[6] + a[7] * b[7]), - ])) - }).to_cf()?, - - // F32x4Ceil => self.stack.values.replace_top_same::(|v| Ok(v.ceil())).to_cf()?, - // F64x2Ceil => self.stack.values.replace_top_same::(|v| Ok(v.ceil())).to_cf()?, - // F32x4Floor => self.stack.values.replace_top_same::(|v| Ok(v.floor())).to_cf()?, - // F64x2Floor => self.stack.values.replace_top_same::(|v| Ok(v.floor())).to_cf()?, - // F32x4Trunc => self.stack.values.replace_top_same::(|v| Ok(v.trunc())).to_cf()?, - // F64x2Trunc => self.stack.values.replace_top_same::(|v| Ok(v.trunc())).to_cf()?, - // F32x4Nearest => self.stack.values.replace_top_same::(|v| Ok(v.round())).to_cf()?, - // F64x2Nearest => self.stack.values.replace_top_same::(|v| Ok(v.round())).to_cf()?, - // F32x4Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, - // F64x2Abs => self.stack.values.replace_top_same::(|v| Ok(v.abs())).to_cf()?, - // F32x4Neg => self.stack.values.replace_top_same::(|v| Ok(-v)).to_cf()?, - // F64x2Neg => self.stack.values.replace_top_same::(|v| Ok(-v)).to_cf()?, - // F32x4Sqrt => self.stack.values.replace_top_same::(|v| Ok(canonicalize_f32x4(v.sqrt()))).to_cf()?, - // F64x2Sqrt => self.stack.values.replace_top_same::(|v| Ok(canonicalize_f64x2(v.sqrt()))).to_cf()?, - // F32x4Add => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f32x4(a + b))).to_cf()?, - // F64x2Add => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f64x2(a + b))).to_cf()?, - // F32x4Sub => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f32x4(a - b))).to_cf()?, - // F64x2Sub => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f64x2(a - b))).to_cf()?, - // F32x4Mul => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f32x4(a * b))).to_cf()?, - // F64x2Mul => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f64x2(a * b))).to_cf()?, - // F32x4Div => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f32x4(a / b))).to_cf()?, - // F64x2Div => self.stack.values.calculate_same::(|a, b| Ok(canonicalize_f64x2(a / b))).to_cf()?, - - // F32x4Min => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // b[0].tw_minimum(a[0]), - // b[1].tw_minimum(a[1]), - // b[2].tw_minimum(a[2]), - // b[3].tw_minimum(a[3]), - // ])) - // }).to_cf()?, - - - // F64x2Min => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // b[0].tw_minimum(a[0]), - // b[1].tw_minimum(a[1]), - // ])) - // }).to_cf()?, - - - // F32x4Max => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // b[0].tw_maximum(a[0]), - // b[1].tw_maximum(a[1]), - // b[2].tw_maximum(a[2]), - // b[3].tw_maximum(a[3]), - // ])) - // }).to_cf()?, - - - // F64x2Max => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // b[0].tw_maximum(a[0]), - // b[1].tw_maximum(a[1]), - // ])) - // }).to_cf()?, - - - // F32x4PMin => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // if b[0] < a[0] { b[0] } else { a[0]}, - // if b[1] < a[1] { b[1] } else { a[1]}, - // if b[2] < a[2] { b[2] } else { a[2]}, - // if b[3] < a[3] { b[3] } else { a[3]}, - // ])) - // }).to_cf()?, - - - // F32x4PMax => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // if b[0] > a[0] { b[0] } else { a[0]}, - // if b[1] > a[1] { b[1] } else { a[1]}, - // if b[2] > a[2] { b[2] } else { a[2]}, - // if b[3] > a[3] { b[3] } else { a[3]}, - // ])) - // }).to_cf()?, - - - // F64x2PMin => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // if b[0] < a[0] { b[0] } else { a[0]}, - // if b[1] < a[1] { b[1] } else { a[1]}, - // ])) - // }).to_cf()?, - - - // F64x2PMax => self.stack.values.calculate_same::(|a, b| { - // Ok(Simd::::from_array([ - // if b[0] > a[0] { b[0] } else { a[0]}, - // if b[1] > a[1] { b[1] } else { a[1]}, - // ])) - // }).to_cf()?, - - // // not correct - // I32x4TruncSatF32x4S => self.stack.values.replace_top::(|v| Ok(v.trunc())).to_cf()?, - // I32x4TruncSatF32x4U => self.stack.values.replace_top::(|v| Ok(v.trunc())).to_cf()?, - // F32x4ConvertI32x4S => unimplemented!(), - // F32x4ConvertI32x4U => unimplemented!(), - // F64x2ConvertLowI32x4S => unimplemented!(), - // F64x2ConvertLowI32x4U => unimplemented!(), - // F32x4DemoteF64x2Zero => unimplemented!(), - // F64x2PromoteLowF32x4 => unimplemented!(), - // I32x4TruncSatF64x2SZero => unimplemented!(), - // I32x4TruncSatF64x2UZero => unimplemented!(), - + V128Load32Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i32x4([v, 0, 0, 0]))?, + V128Load64Zero(arg) => self.exec_mem_load::(arg.mem_addr(), arg.offset(), |v| Value128::from_i64x2([v, 0]))?, + + V128Const(arg) => self.exec_const::(self.cf.data().v128_constants[*arg as usize].into()), + + I8x16ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i8(*lane) as i32), + I8x16ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u8(*lane) as i32), + I16x8ExtractLaneS(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i16(*lane) as i32), + I16x8ExtractLaneU(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_u16(*lane) as i32), + I32x4ExtractLane(lane) => stack_op!(unary Value128 => i32, |v| v.extract_lane_i32(*lane)), + I64x2ExtractLane(lane) => stack_op!(unary Value128 => i64, |v| v.extract_lane_i64(*lane)), + F32x4ExtractLane(lane) => stack_op!(unary Value128 => f32, |v| v.extract_lane_f32(*lane)), + F64x2ExtractLane(lane) => stack_op!(unary Value128 => f64, |v| v.extract_lane_f64(*lane)), + + V128Load8Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load16Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load32Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + V128Load64Lane(arg, lane) => self.exec_mem_load_lane::(arg.mem_addr(), arg.offset(), *lane)?, + + I8x16ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i8x16_replace_lane(*lane, value as i8)), + I16x8ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i16x8_replace_lane(*lane, value as i16)), + I32x4ReplaceLane(lane) => stack_op!(binary i32, Value128, |value, vec| vec.i32x4_replace_lane(*lane, value)), + I64x2ReplaceLane(lane) => stack_op!(binary i64, Value128, |value, vec| vec.i64x2_replace_lane(*lane, value)), + F32x4ReplaceLane(lane) => stack_op!(binary f32, Value128, |value, vec| vec.f32x4_replace_lane(*lane, value)), + F64x2ReplaceLane(lane) => stack_op!(binary f64, Value128, |value, vec| vec.f64x2_replace_lane(*lane, value)), + + I8x16Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i8(v as i8)), + I16x8Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i16(v as i16)), + I32x4Splat => stack_op!(unary i32 => Value128, |v| Value128::splat_i32(v)), + I64x2Splat => stack_op!(unary i64 => Value128, |v| Value128::splat_i64(v)), + F32x4Splat => stack_op!(unary f32 => Value128, |v| Value128::splat_f32(v)), + F64x2Splat => stack_op!(unary f64 => Value128, |v| Value128::splat_f64(v)), + + I8x16Eq => stack_op!(binary Value128, |a, b| a.i8x16_eq(b)), + I16x8Eq => stack_op!(binary Value128, |a, b| a.i16x8_eq(b)), + I32x4Eq => stack_op!(binary Value128, |a, b| a.i32x4_eq(b)), + I64x2Eq => stack_op!(binary Value128, |a, b| a.i64x2_eq(b)), + F32x4Eq => stack_op!(binary Value128, |a, b| a.f32x4_eq(b)), + F64x2Eq => stack_op!(binary Value128, |a, b| a.f64x2_eq(b)), + + I8x16Ne => stack_op!(binary Value128, |a, b| a.i8x16_ne(b)), + I16x8Ne => stack_op!(binary Value128, |a, b| a.i16x8_ne(b)), + I32x4Ne => stack_op!(binary Value128, |a, b| a.i32x4_ne(b)), + I64x2Ne => stack_op!(binary Value128, |a, b| a.i64x2_ne(b)), + F32x4Ne => stack_op!(binary Value128, |a, b| a.f32x4_ne(b)), + F64x2Ne => stack_op!(binary Value128, |a, b| a.f64x2_ne(b)), + + I8x16LtS => stack_op!(binary Value128, |a, b| a.i8x16_lt_s(b)), + I16x8LtS => stack_op!(binary Value128, |a, b| a.i16x8_lt_s(b)), + I32x4LtS => stack_op!(binary Value128, |a, b| a.i32x4_lt_s(b)), + I64x2LtS => stack_op!(binary Value128, |a, b| a.i64x2_lt_s(b)), + I8x16LtU => stack_op!(binary Value128, |a, b| a.i8x16_lt_u(b)), + I16x8LtU => stack_op!(binary Value128, |a, b| a.i16x8_lt_u(b)), + I32x4LtU => stack_op!(binary Value128, |a, b| a.i32x4_lt_u(b)), + F32x4Lt => stack_op!(binary Value128, |a, b| a.f32x4_lt(b)), + F64x2Lt => stack_op!(binary Value128, |a, b| a.f64x2_lt(b)), + + F32x4Gt => stack_op!(binary Value128, |a, b| a.f32x4_gt(b)), + F64x2Gt => stack_op!(binary Value128, |a, b| a.f64x2_gt(b)), + + I8x16GtS => stack_op!(binary Value128, |a, b| a.i8x16_gt_s(b)), + I16x8GtS => stack_op!(binary Value128, |a, b| a.i16x8_gt_s(b)), + I32x4GtS => stack_op!(binary Value128, |a, b| a.i32x4_gt_s(b)), + I64x2GtS => stack_op!(binary Value128, |a, b| a.i64x2_gt_s(b)), + I64x2LeS => stack_op!(binary Value128, |a, b| a.i64x2_le_s(b)), + F32x4Le => stack_op!(binary Value128, |a, b| a.f32x4_le(b)), + F64x2Le => stack_op!(binary Value128, |a, b| a.f64x2_le(b)), + + I8x16GtU => stack_op!(binary Value128, |a, b| a.i8x16_gt_u(b)), + I16x8GtU => stack_op!(binary Value128, |a, b| a.i16x8_gt_u(b)), + I32x4GtU => stack_op!(binary Value128, |a, b| a.i32x4_gt_u(b)), + F32x4Ge => stack_op!(binary Value128, |a, b| a.f32x4_ge(b)), + F64x2Ge => stack_op!(binary Value128, |a, b| a.f64x2_ge(b)), + + I8x16LeS => stack_op!(binary Value128, |a, b| a.i8x16_le_s(b)), + I16x8LeS => stack_op!(binary Value128, |a, b| a.i16x8_le_s(b)), + I32x4LeS => stack_op!(binary Value128, |a, b| a.i32x4_le_s(b)), + + I8x16LeU => stack_op!(binary Value128, |a, b| a.i8x16_le_u(b)), + I16x8LeU => stack_op!(binary Value128, |a, b| a.i16x8_le_u(b)), + I32x4LeU => stack_op!(binary Value128, |a, b| a.i32x4_le_u(b)), + + I8x16GeS => stack_op!(binary Value128, |a, b| a.i8x16_ge_s(b)), + I16x8GeS => stack_op!(binary Value128, |a, b| a.i16x8_ge_s(b)), + I32x4GeS => stack_op!(binary Value128, |a, b| a.i32x4_ge_s(b)), + I64x2GeS => stack_op!(binary Value128, |a, b| a.i64x2_ge_s(b)), + + I8x16GeU => stack_op!(binary Value128, |a, b| a.i8x16_ge_u(b)), + I16x8GeU => stack_op!(binary Value128, |a, b| a.i16x8_ge_u(b)), + I32x4GeU => stack_op!(binary Value128, |a, b| a.i32x4_ge_u(b)), + + I8x16Abs => stack_op!(unary Value128, |a| a.i8x16_abs()), + I16x8Abs => stack_op!(unary Value128, |a| a.i16x8_abs()), + I32x4Abs => stack_op!(unary Value128, |a| a.i32x4_abs()), + I64x2Abs => stack_op!(unary Value128, |a| a.i64x2_abs()), + + I8x16Neg => stack_op!(unary Value128, |a| a.i8x16_neg()), + I16x8Neg => stack_op!(unary Value128, |a| a.i16x8_neg()), + I32x4Neg => stack_op!(unary Value128, |a| a.i32x4_neg()), + I64x2Neg => stack_op!(unary Value128, |a| a.i64x2_neg()), + + I8x16AllTrue => stack_op!(unary Value128 => i32, |v| v.i8x16_all_true() as i32), + I16x8AllTrue => stack_op!(unary Value128 => i32, |v| v.i16x8_all_true() as i32), + I32x4AllTrue => stack_op!(unary Value128 => i32, |v| v.i32x4_all_true() as i32), + I64x2AllTrue => stack_op!(unary Value128 => i32, |v| v.i64x2_all_true() as i32), + + I8x16Bitmask => stack_op!(unary Value128 => i32, |v| v.i8x16_bitmask() as i32), + I16x8Bitmask => stack_op!(unary Value128 => i32, |v| v.i16x8_bitmask() as i32), + I32x4Bitmask => stack_op!(unary Value128 => i32, |v| v.i32x4_bitmask() as i32), + I64x2Bitmask => stack_op!(unary Value128 => i32, |v| v.i64x2_bitmask() as i32), + + I8x16Shl => stack_op!(binary i32, Value128, |a, b| b.i8x16_shl(a as u32)), + I16x8Shl => stack_op!(binary i32, Value128, |a, b| b.i16x8_shl(a as u32)), + I32x4Shl => stack_op!(binary i32, Value128, |a, b| b.i32x4_shl(a as u32)), + I64x2Shl => stack_op!(binary i32, Value128, |a, b| b.i64x2_shl(a as u32)), + + I8x16ShrS => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_s(a as u32)), + I16x8ShrS => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_s(a as u32)), + I32x4ShrS => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_s(a as u32)), + I64x2ShrS => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_s(a as u32)), + + I8x16ShrU => stack_op!(binary i32, Value128, |a, b| b.i8x16_shr_u(a as u32)), + I16x8ShrU => stack_op!(binary i32, Value128, |a, b| b.i16x8_shr_u(a as u32)), + I32x4ShrU => stack_op!(binary i32, Value128, |a, b| b.i32x4_shr_u(a as u32)), + I64x2ShrU => stack_op!(binary i32, Value128, |a, b| b.i64x2_shr_u(a as u32)), + + I8x16Add => stack_op!(binary Value128, |a, b| a.i8x16_add(b)), + I16x8Add => stack_op!(binary Value128, |a, b| a.i16x8_add(b)), + I32x4Add => stack_op!(binary Value128, |a, b| a.i32x4_add(b)), + I64x2Add => stack_op!(binary Value128, |a, b| a.i64x2_add(b)), + + I8x16Sub => stack_op!(binary Value128, |a, b| a.i8x16_sub(b)), + I16x8Sub => stack_op!(binary Value128, |a, b| a.i16x8_sub(b)), + I32x4Sub => stack_op!(binary Value128, |a, b| a.i32x4_sub(b)), + I64x2Sub => stack_op!(binary Value128, |a, b| a.i64x2_sub(b)), + + I8x16MinS => stack_op!(binary Value128, |a, b| a.i8x16_min_s(b)), + I16x8MinS => stack_op!(binary Value128, |a, b| a.i16x8_min_s(b)), + I32x4MinS => stack_op!(binary Value128, |a, b| a.i32x4_min_s(b)), + + I8x16MinU => stack_op!(binary Value128, |a, b| a.i8x16_min_u(b)), + I16x8MinU => stack_op!(binary Value128, |a, b| a.i16x8_min_u(b)), + I32x4MinU => stack_op!(binary Value128, |a, b| a.i32x4_min_u(b)), + + I8x16MaxS => stack_op!(binary Value128, |a, b| a.i8x16_max_s(b)), + I16x8MaxS => stack_op!(binary Value128, |a, b| a.i16x8_max_s(b)), + I32x4MaxS => stack_op!(binary Value128, |a, b| a.i32x4_max_s(b)), + + I8x16MaxU => stack_op!(binary Value128, |a, b| a.i8x16_max_u(b)), + I16x8MaxU => stack_op!(binary Value128, |a, b| a.i16x8_max_u(b)), + I32x4MaxU => stack_op!(binary Value128, |a, b| a.i32x4_max_u(b)), + + I64x2Mul => stack_op!(binary Value128, |a, b| a.i64x2_mul(b)), + I16x8Mul => stack_op!(binary Value128, |a, b| a.i16x8_mul(b)), + I32x4Mul => stack_op!(binary Value128, |a, b| a.i32x4_mul(b)), + + I8x16NarrowI16x8S => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_s(a, b)), + I8x16NarrowI16x8U => stack_op!(binary Value128, |a, b| Value128::i8x16_narrow_i16x8_u(a, b)), + I16x8NarrowI32x4S => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_s(a, b)), + I16x8NarrowI32x4U => stack_op!(binary Value128, |a, b| Value128::i16x8_narrow_i32x4_u(a, b)), + + I8x16AddSatS => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_s(b)), + I16x8AddSatS => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_s(b)), + I8x16AddSatU => stack_op!(binary Value128, |a, b| a.i8x16_add_sat_u(b)), + I16x8AddSatU => stack_op!(binary Value128, |a, b| a.i16x8_add_sat_u(b)), + I8x16SubSatS => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_s(b)), + I16x8SubSatS => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_s(b)), + I8x16SubSatU => stack_op!(binary Value128, |a, b| a.i8x16_sub_sat_u(b)), + I16x8SubSatU => stack_op!(binary Value128, |a, b| a.i16x8_sub_sat_u(b)), + + I8x16AvgrU => stack_op!(binary Value128, |a, b| a.i8x16_avgr_u(b)), + I16x8AvgrU => stack_op!(binary Value128, |a, b| a.i16x8_avgr_u(b)), + + I16x8ExtAddPairwiseI8x16S => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_s()), + I16x8ExtAddPairwiseI8x16U => stack_op!(unary Value128, |a| a.i16x8_extadd_pairwise_i8x16_u()), + I32x4ExtAddPairwiseI16x8S => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_s()), + I32x4ExtAddPairwiseI16x8U => stack_op!(unary Value128, |a| a.i32x4_extadd_pairwise_i16x8_u()), + + I16x8ExtMulLowI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_s(b)), + I16x8ExtMulLowI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_low_i8x16_u(b)), + I16x8ExtMulHighI8x16S => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_s(b)), + I16x8ExtMulHighI8x16U => stack_op!(binary Value128, |a, b| a.i16x8_extmul_high_i8x16_u(b)), + I32x4ExtMulLowI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_s(b)), + I32x4ExtMulLowI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_low_i16x8_u(b)), + I32x4ExtMulHighI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_s(b)), + I32x4ExtMulHighI16x8U => stack_op!(binary Value128, |a, b| a.i32x4_extmul_high_i16x8_u(b)), + I64x2ExtMulLowI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_s(b)), + I64x2ExtMulLowI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_low_i32x4_u(b)), + I64x2ExtMulHighI32x4S => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_s(b)), + I64x2ExtMulHighI32x4U => stack_op!(binary Value128, |a, b| a.i64x2_extmul_high_i32x4_u(b)), + + I16x8ExtendLowI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_s()), + I16x8ExtendLowI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_low_i8x16_u()), + I16x8ExtendHighI8x16S => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_s()), + I16x8ExtendHighI8x16U => stack_op!(unary Value128, |a| a.i16x8_extend_high_i8x16_u()), + I32x4ExtendLowI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_s()), + I32x4ExtendLowI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_low_i16x8_u()), + I32x4ExtendHighI16x8S => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_s()), + I32x4ExtendHighI16x8U => stack_op!(unary Value128, |a| a.i32x4_extend_high_i16x8_u()), + I64x2ExtendLowI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_s()), + I64x2ExtendLowI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_low_i32x4_u()), + I64x2ExtendHighI32x4S => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_s()), + I64x2ExtendHighI32x4U => stack_op!(unary Value128, |a| a.i64x2_extend_high_i32x4_u()), + + I8x16Popcnt => stack_op!(unary Value128, |v| v.i8x16_popcnt()), + I8x16Shuffle(idx) => { let idx = self.cf.data().v128_constants[*idx as usize].to_le_bytes(); stack_op!(binary Value128, |a, b| Value128::i8x16_shuffle(a, b, idx)) } + + I16x8Q15MulrSatS => stack_op!(binary Value128, |a, b| a.i16x8_q15mulr_sat_s(b)), + + I32x4DotI16x8S => stack_op!(binary Value128, |a, b| a.i32x4_dot_i16x8_s(b)), + + F32x4Ceil => stack_op!(simd_unary f32x4_ceil), + F64x2Ceil => stack_op!(simd_unary f64x2_ceil), + F32x4Floor => stack_op!(simd_unary f32x4_floor), + F64x2Floor => stack_op!(simd_unary f64x2_floor), + F32x4Trunc => stack_op!(simd_unary f32x4_trunc), + F64x2Trunc => stack_op!(simd_unary f64x2_trunc), + F32x4Nearest => stack_op!(simd_unary f32x4_nearest), + F64x2Nearest => stack_op!(simd_unary f64x2_nearest), + F32x4Abs => stack_op!(simd_unary f32x4_abs), + F64x2Abs => stack_op!(simd_unary f64x2_abs), + F32x4Neg => stack_op!(simd_unary f32x4_neg), + F64x2Neg => stack_op!(simd_unary f64x2_neg), + F32x4Sqrt => stack_op!(simd_unary f32x4_sqrt), + F64x2Sqrt => stack_op!(simd_unary f64x2_sqrt), + F32x4Add => stack_op!(simd_binary f32x4_add), + F64x2Add => stack_op!(simd_binary f64x2_add), + F32x4Sub => stack_op!(simd_binary f32x4_sub), + F64x2Sub => stack_op!(simd_binary f64x2_sub), + F32x4Mul => stack_op!(simd_binary f32x4_mul), + F64x2Mul => stack_op!(simd_binary f64x2_mul), + F32x4Div => stack_op!(simd_binary f32x4_div), + F64x2Div => stack_op!(simd_binary f64x2_div), + + F32x4Min => stack_op!(simd_binary f32x4_min), + F64x2Min => stack_op!(simd_binary f64x2_min), + F32x4Max => stack_op!(simd_binary f32x4_max), + F64x2Max => stack_op!(simd_binary f64x2_max), + F32x4PMin => stack_op!(simd_binary f32x4_pmin), + F32x4PMax => stack_op!(simd_binary f32x4_pmax), + F64x2PMin => stack_op!(simd_binary f64x2_pmin), + F64x2PMax => stack_op!(simd_binary f64x2_pmax), + + I32x4TruncSatF32x4S => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_s()), + I32x4TruncSatF32x4U => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f32x4_u()), + F32x4ConvertI32x4S => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_s()), + F32x4ConvertI32x4U => stack_op!(unary Value128, |v| v.f32x4_convert_i32x4_u()), + F64x2ConvertLowI32x4S => stack_op!(unary Value128, |v| v.f64x2_convert_low_i32x4_s()), + F64x2ConvertLowI32x4U => stack_op!(unary Value128, |v| v.f64x2_convert_low_i32x4_u()), + F32x4DemoteF64x2Zero => stack_op!(unary Value128, |v| v.f32x4_demote_f64x2_zero()), + F64x2PromoteLowF32x4 => stack_op!(unary Value128, |v| v.f64x2_promote_low_f32x4()), + I32x4TruncSatF64x2SZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_s_zero()), + I32x4TruncSatF64x2UZero => stack_op!(unary Value128, |v| v.i32x4_trunc_sat_f64x2_u_zero()), + + // Relaxed SIMD (not yet implemented) // I8x16RelaxedSwizzle => unimplemented!(), // I32x4RelaxedTruncF32x4S => unimplemented!(), // I32x4RelaxedTruncF32x4U => unimplemented!(), @@ -1094,8 +1035,10 @@ impl<'store, 'stack> Executor<'store, 'stack> { lane: u8, ) -> ControlFlow> { let mem = self.store.get_mem_mut(self.module.resolve_mem_addr(mem_addr)); - let val = self.stack.values.pop::().to_mem_bytes(); - let val = val[lane as usize].to_mem_bytes(); + let bytes = self.stack.values.pop::().to_mem_bytes(); + let lane_offset = lane as usize * N; + let mut val = [0u8; N]; + val.copy_from_slice(&bytes[lane_offset..lane_offset + N]); let addr = match mem.is_64bit() { true => self.stack.values.pop::() as u64, diff --git a/crates/tinywasm/src/interpreter/num_helpers.rs b/crates/tinywasm/src/interpreter/num_helpers.rs index 0356403..5b9e7b9 100644 --- a/crates/tinywasm/src/interpreter/num_helpers.rs +++ b/crates/tinywasm/src/interpreter/num_helpers.rs @@ -37,7 +37,7 @@ macro_rules! checked_conv_float { $self .stack .values - .replace_top::<$from, $to>(|v| { + .unary::<$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/value_stack.rs b/crates/tinywasm/src/interpreter/stack/value_stack.rs index 6e02b0f..2850e91 100644 --- a/crates/tinywasm/src/interpreter/stack/value_stack.rs +++ b/crates/tinywasm/src/interpreter/stack/value_stack.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use tinywasm_types::{ExternRef, FuncRef, ValType, ValueCounts, ValueCountsSmall, WasmValue}; -use crate::{Result, StackConfig, interpreter::*}; +use crate::{interpreter::*, Result, StackConfig}; use super::Locals; @@ -63,18 +63,18 @@ impl ValueStack { } #[inline] - pub(crate) fn calculate_same(&mut self, func: impl FnOnce(T, T) -> Result) -> Result<()> { + pub(crate) fn binary_same(&mut self, func: impl FnOnce(T, T) -> Result) -> Result<()> { T::stack_calculate(self, func) } #[inline] #[allow(dead_code)] - pub(crate) fn calculate_same_3(&mut self, func: impl FnOnce(T, T, T) -> Result) -> Result<()> { + pub(crate) fn ternary_same(&mut self, func: impl FnOnce(T, T, T) -> Result) -> Result<()> { T::stack_calculate3(self, func) } #[inline] - pub(crate) fn calculate( + pub(crate) fn binary( &mut self, func: impl FnOnce(T, T) -> Result, ) -> Result<()> { @@ -86,7 +86,7 @@ impl ValueStack { #[inline] #[allow(dead_code)] - pub(crate) fn calculate_diff( + pub(crate) fn binary_diff( &mut self, func: impl FnOnce(A, B) -> Result, ) -> Result<()> { @@ -97,7 +97,7 @@ impl ValueStack { } #[inline] - pub(crate) fn replace_top( + pub(crate) fn unary( &mut self, func: impl FnOnce(T) -> Result, ) -> Result<()> { @@ -107,7 +107,7 @@ impl ValueStack { } #[inline] - pub(crate) fn replace_top_same(&mut self, func: impl Fn(T) -> Result) -> Result<()> { + pub(crate) fn unary_same(&mut self, func: impl Fn(T) -> Result) -> Result<()> { T::replace_top(self, func) } diff --git a/crates/tinywasm/src/interpreter/value128.rs b/crates/tinywasm/src/interpreter/value128.rs index fcd208f..6b807c9 100644 --- a/crates/tinywasm/src/interpreter/value128.rs +++ b/crates/tinywasm/src/interpreter/value128.rs @@ -1,7 +1,84 @@ +use super::num_helpers::TinywasmFloatExt; + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] pub struct Value128(i128); impl Value128 { + #[inline] + fn canonicalize_simd_f32_nan(x: f32) -> f32 { + if x.is_nan() { + f32::from_bits(0x7fc0_0000) + } else { + x + } + } + + #[inline] + fn canonicalize_simd_f64_nan(x: f64) -> f64 { + if x.is_nan() { + f64::from_bits(0x7ff8_0000_0000_0000) + } else { + x + } + } + + 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 + } + } + + 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 + } + } + + 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 + } + } + + 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 + } + } + + const fn replace_lane_bytes( + self, + lane: u8, + value: [u8; LANE_BYTES], + lane_count: u8, + ) -> Self { + debug_assert!(lane < lane_count); + let mut bytes = self.to_le_bytes(); + let mut i = 0; + let start = lane as usize * LANE_BYTES; + while i < LANE_BYTES { + bytes[start + i] = value[i]; + i += 1; + } + Self::from_le_bytes(bytes) + } + pub const fn from_le_bytes(bytes: [u8; 16]) -> Self { Self(i128::from_le_bytes(bytes)) } @@ -119,6 +196,32 @@ impl Value128 { 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])]) + } + + #[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])]) + } + + #[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])]) + } + + #[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])]) + } + pub const fn reduce_or(self) -> u8 { let mut result = 0u8; let bytes = self.to_le_bytes(); @@ -130,7 +233,111 @@ impl Value128 { result } + #[doc(alias = "v128.any_true")] + pub const fn v128_any_true(self) -> bool { + self.reduce_or() != 0 + } + + #[doc(alias = "v128.not")] + pub const fn v128_not(self) -> Self { + Self(!self.0) + } + + #[doc(alias = "v128.and")] + pub const fn v128_and(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + + #[doc(alias = "v128.andnot")] + pub const fn v128_andnot(self, rhs: Self) -> Self { + Self(self.0 & !rhs.0) + } + + #[doc(alias = "v128.or")] + pub const fn v128_or(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + + #[doc(alias = "v128.xor")] + pub const fn v128_xor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } + + #[doc(alias = "v128.bitselect")] + pub const fn v128_bitselect(v1: Self, v2: Self, c: Self) -> Self { + Self((v1.0 & c.0) | (v2.0 & !c.0)) + } + pub const 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([ + src[0] as i8 as i16, + src[1] as i8 as i16, + src[2] as i8 as i16, + src[3] as i8 as i16, + src[4] as i8 as i16, + src[5] as i8 as i16, + src[6] as i8 as i16, + src[7] as i8 as i16, + ]) + } + + #[doc(alias = "v128.load8x8_u")] + pub const fn v128_load8x8_u(src: [u8; 8]) -> Self { + Self::from_u16x8([ + src[0] as u16, + src[1] as u16, + src[2] as u16, + src[3] as u16, + src[4] as u16, + src[5] as u16, + src[6] as u16, + src[7] as u16, + ]) + } + + #[doc(alias = "v128.load16x4_s")] + pub const fn v128_load16x4_s(src: [u8; 8]) -> Self { + Self::from_i32x4([ + i16::from_le_bytes([src[0], src[1]]) as i32, + i16::from_le_bytes([src[2], src[3]]) as i32, + i16::from_le_bytes([src[4], src[5]]) as i32, + i16::from_le_bytes([src[6], src[7]]) as i32, + ]) + } + + #[doc(alias = "v128.load16x4_u")] + pub const fn v128_load16x4_u(src: [u8; 8]) -> Self { + Self::from_u32x4([ + u16::from_le_bytes([src[0], src[1]]) as u32, + u16::from_le_bytes([src[2], src[3]]) as u32, + u16::from_le_bytes([src[4], src[5]]) as u32, + u16::from_le_bytes([src[6], src[7]]) as u32, + ]) + } + + #[doc(alias = "v128.load32x2_s")] + pub const fn v128_load32x2_s(src: [u8; 8]) -> Self { + Self::from_i64x2([ + i32::from_le_bytes([src[0], src[1], src[2], src[3]]) as i64, + i32::from_le_bytes([src[4], src[5], src[6], src[7]]) as i64, + ]) + } + + #[doc(alias = "v128.load32x2_u")] + pub const fn v128_load32x2_u(src: [u8; 8]) -> Self { + Self::from_u64x2([ + u32::from_le_bytes([src[0], src[1], src[2], src[3]]) as u64, + u32::from_le_bytes([src[4], src[5], src[6], src[7]]) as u64, + ]) + } + + #[doc(alias = "i8x16.swizzle")] + pub const fn i8x16_swizzle(self, s: Self) -> Self { let a_bytes = self.to_le_bytes(); let s_bytes = s.to_le_bytes(); let mut result_bytes = [0u8; 16]; @@ -143,6 +350,20 @@ impl Value128 { Self::from_le_bytes(result_bytes) } + #[doc(alias = "i8x16.shuffle")] + pub const fn i8x16_shuffle(a: Self, b: Self, idx: [u8; 16]) -> Self { + let a_bytes = a.to_le_bytes(); + let b_bytes = b.to_le_bytes(); + let mut result_bytes = [0u8; 16]; + let mut i = 0; + while i < 16 { + let index = idx[i] as usize; + result_bytes[i] = if index < 16 { a_bytes[index] } else { b_bytes[index - 16] }; + i += 1; + } + 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; @@ -240,152 +461,2095 @@ impl Value128 { Self::from_le_bytes(result_bytes) } - pub const fn splat_i16(src: i16) -> Self { - let mut result_bytes = [0u8; 16]; - let bytes = src.to_le_bytes(); + #[doc(alias = "i8x16.replace_lane")] + pub const fn i8x16_replace_lane(self, lane: u8, value: i8) -> Self { + self.replace_lane_bytes::<1>(lane, [value as u8], 16) + } + + #[doc(alias = "i16x8.replace_lane")] + pub const fn i16x8_replace_lane(self, lane: u8, value: i16) -> Self { + self.replace_lane_bytes::<2>(lane, value.to_le_bytes(), 8) + } + + #[doc(alias = "i32x4.replace_lane")] + pub const fn i32x4_replace_lane(self, lane: u8, value: i32) -> Self { + self.replace_lane_bytes::<4>(lane, value.to_le_bytes(), 4) + } + + #[doc(alias = "i64x2.replace_lane")] + pub const fn i64x2_replace_lane(self, lane: u8, value: i64) -> Self { + self.replace_lane_bytes::<8>(lane, value.to_le_bytes(), 2) + } + + #[doc(alias = "f32x4.replace_lane")] + pub const fn f32x4_replace_lane(self, lane: u8, value: f32) -> Self { + self.replace_lane_bytes::<4>(lane, value.to_bits().to_le_bytes(), 4) + } + + #[doc(alias = "f64x2.replace_lane")] + pub const fn f64x2_replace_lane(self, lane: u8, value: f64) -> Self { + 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(); + let mut i = 0; + while i < 16 { + if lanes[i] == 0 { + return false; + } + i += 1; + } + true + } + + #[doc(alias = "i16x8.all_true")] + pub const fn i16x8_all_true(self) -> bool { + let lanes = self.as_i16x8(); let mut i = 0; while i < 8 { - result_bytes[i * 2] = bytes[0]; - result_bytes[i * 2 + 1] = bytes[1]; + if lanes[i] == 0 { + return false; + } i += 1; } - Self::from_le_bytes(result_bytes) + true } - pub const fn splat_i32(src: i32) -> Self { - let mut result_bytes = [0u8; 16]; - let bytes = src.to_le_bytes(); + #[doc(alias = "i32x4.all_true")] + pub const fn i32x4_all_true(self) -> bool { + let lanes = self.as_i32x4(); 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]; + if lanes[i] == 0 { + return false; + } i += 1; } - Self::from_le_bytes(result_bytes) + true } - pub const fn splat_i64(src: i64) -> Self { - let mut result_bytes = [0u8; 16]; - let bytes = src.to_le_bytes(); + #[doc(alias = "i64x2.all_true")] + pub const fn i64x2_all_true(self) -> bool { + let lanes = self.as_i64x2(); 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]; + if lanes[i] == 0 { + return false; + } i += 1; } - Self::from_le_bytes(result_bytes) + true } - pub const fn splat_f32(src: f32) -> Self { - Self::splat_i32(src.to_bits() as i32) + #[doc(alias = "i8x16.bitmask")] + pub const fn i8x16_bitmask(self) -> u32 { + let lanes = self.as_i8x16(); + let mut mask = 0u32; + let mut i = 0; + while i < 16 { + mask |= ((lanes[i] < 0) as u32) << i; + i += 1; + } + mask } - pub const fn splat_f64(src: f64) -> Self { - Self::splat_i64(src.to_bits() as i64) + #[doc(alias = "i16x8.bitmask")] + pub const fn i16x8_bitmask(self) -> u32 { + let lanes = self.as_i16x8(); + let mut mask = 0u32; + let mut i = 0; + while i < 8 { + mask |= ((lanes[i] < 0) as u32) << i; + i += 1; + } + mask } - pub const fn extract_lane_i8(self, lane: u8) -> i8 { - debug_assert!(lane < 16); - let lane = lane as usize; - let bytes = self.to_le_bytes(); - bytes[lane] as i8 + #[doc(alias = "i32x4.bitmask")] + pub const fn i32x4_bitmask(self) -> u32 { + let lanes = self.as_i32x4(); + let mut mask = 0u32; + let mut i = 0; + while i < 4 { + mask |= ((lanes[i] < 0) as u32) << i; + i += 1; + } + mask } - pub const fn extract_lane_u8(self, lane: u8) -> u8 { - debug_assert!(lane < 16); - let lane = lane as usize; - let bytes = self.to_le_bytes(); - bytes[lane] + #[doc(alias = "i64x2.bitmask")] + pub const fn i64x2_bitmask(self) -> u32 { + let lanes = self.as_i64x2(); + let mut mask = 0u32; + let mut i = 0; + while i < 2 { + mask |= ((lanes[i] < 0) as u32) << i; + i += 1; + } + mask } - 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]]) + #[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) } - 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]]) + #[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) } - 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]]) + #[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) } - 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], - ]) + #[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) } - pub const fn extract_lane_f32(self, lane: u8) -> f32 { - f32::from_bits(self.extract_lane_i32(lane) as u32) + #[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) } - pub const fn extract_lane_f64(self, lane: u8) -> f64 { - f64::from_bits(self.extract_lane_i64(lane) as u64) + #[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) } -} -impl From for i128 { - fn from(val: Value128) -> Self { - val.0 + #[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) } -} -impl From for Value128 { - fn from(value: i128) -> Self { - Self(value) + #[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) } -} -impl core::ops::Not for Value128 { - type Output = Self; - fn not(self) -> Self::Output { - Self(!self.0) + #[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) } -} -impl core::ops::BitAnd for Value128 { - type Output = Self; - fn bitand(self, rhs: Self) -> Self::Output { - Self(self.0 & rhs.0) + #[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) } -} -impl core::ops::BitOr for Value128 { - type Output = Self; - fn bitor(self, rhs: Self) -> Self::Output { - Self(self.0 | rhs.0) + #[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 const fn i8x16_add(self, rhs: Self) -> Self { + 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 const fn i16x8_add(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 { + out[i] = a[i].wrapping_add(b[i]); + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.add")] + pub const fn i32x4_add(self, rhs: Self) -> Self { + 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 const fn i64x2_add(self, rhs: Self) -> Self { + 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 const fn i8x16_sub(self, rhs: Self) -> Self { + 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 const fn i16x8_sub(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 { + out[i] = a[i].wrapping_sub(b[i]); + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.sub")] + pub const fn i32x4_sub(self, rhs: Self) -> Self { + 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 const fn i64x2_sub(self, rhs: Self) -> Self { + 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 const fn i16x8_mul(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 { + out[i] = a[i].wrapping_mul(b[i]); + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.mul")] + pub const fn i32x4_mul(self, rhs: Self) -> Self { + 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 const fn i64x2_mul(self, rhs: Self) -> Self { + 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 const fn i8x16_add_sat_s(self, rhs: Self) -> Self { + 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 const fn i16x8_add_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 { + out[i] = a[i].saturating_add(b[i]); + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i8x16.add_sat_u")] + pub const fn i8x16_add_sat_u(self, rhs: Self) -> Self { + 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 const fn i16x8_add_sat_u(self, rhs: Self) -> Self { + 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 const fn i8x16_sub_sat_s(self, rhs: Self) -> Self { + 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 const fn i16x8_sub_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 { + out[i] = a[i].saturating_sub(b[i]); + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i8x16.sub_sat_u")] + pub const fn i8x16_sub_sat_u(self, rhs: Self) -> Self { + 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 const fn i16x8_sub_sat_u(self, rhs: Self) -> Self { + 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 const fn i8x16_avgr_u(self, rhs: Self) -> Self { + 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 const fn i16x8_avgr_u(self, rhs: Self) -> Self { + 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] = Self::saturate_i16_to_i8(av[i]); + out[i + 8] = Self::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] = Self::saturate_i16_to_u8(av[i]); + out[i + 8] = Self::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] = Self::saturate_i32_to_i16(av[i]); + out[i + 4] = Self::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] = Self::saturate_i32_to_u16(av[i]); + out[i + 4] = Self::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) + 0x4000) >> 15; + 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 const fn i8x16_eq(self, rhs: Self) -> Self { + 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 const fn i16x8_eq(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 { + out[i] = if a[i] == b[i] { -1 } else { 0 }; + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.eq")] + pub const fn i32x4_eq(self, rhs: Self) -> Self { + 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 const fn i64x2_eq(self, rhs: Self) -> Self { + 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 const fn i8x16_ne(self, rhs: Self) -> Self { + 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 const fn i16x8_ne(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 { + out[i] = if a[i] != b[i] { -1 } else { 0 }; + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.ne")] + pub const fn i32x4_ne(self, rhs: Self) -> Self { + 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 const fn i64x2_ne(self, rhs: Self) -> Self { + 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 const fn i8x16_lt_s(self, rhs: Self) -> Self { + 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 const fn i16x8_lt_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 { + out[i] = if a[i] < b[i] { -1 } else { 0 }; + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.lt_s")] + pub const fn i32x4_lt_s(self, rhs: Self) -> Self { + 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 const fn i64x2_lt_s(self, rhs: Self) -> Self { + 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 const fn i8x16_lt_u(self, rhs: Self) -> Self { + 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 const fn i16x8_lt_u(self, rhs: Self) -> Self { + 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 const fn i32x4_lt_u(self, rhs: Self) -> Self { + 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 const fn i8x16_gt_s(self, rhs: Self) -> Self { + rhs.i8x16_lt_s(self) + } + + #[doc(alias = "i16x8.gt_s")] + pub const fn i16x8_gt_s(self, rhs: Self) -> Self { + rhs.i16x8_lt_s(self) + } + + #[doc(alias = "i32x4.gt_s")] + pub const fn i32x4_gt_s(self, rhs: Self) -> Self { + rhs.i32x4_lt_s(self) + } + + #[doc(alias = "i64x2.gt_s")] + pub const fn i64x2_gt_s(self, rhs: Self) -> Self { + rhs.i64x2_lt_s(self) + } + + #[doc(alias = "i8x16.gt_u")] + pub const fn i8x16_gt_u(self, rhs: Self) -> Self { + rhs.i8x16_lt_u(self) + } + + #[doc(alias = "i16x8.gt_u")] + pub const fn i16x8_gt_u(self, rhs: Self) -> Self { + rhs.i16x8_lt_u(self) + } + + #[doc(alias = "i32x4.gt_u")] + pub const fn i32x4_gt_u(self, rhs: Self) -> Self { + rhs.i32x4_lt_u(self) + } + + #[doc(alias = "i8x16.le_s")] + pub const fn i8x16_le_s(self, rhs: Self) -> Self { + rhs.i8x16_ge_s(self) + } + + #[doc(alias = "i16x8.le_s")] + pub const fn i16x8_le_s(self, rhs: Self) -> Self { + rhs.i16x8_ge_s(self) + } + + #[doc(alias = "i32x4.le_s")] + pub const fn i32x4_le_s(self, rhs: Self) -> Self { + rhs.i32x4_ge_s(self) + } + + #[doc(alias = "i64x2.le_s")] + pub const fn i64x2_le_s(self, rhs: Self) -> Self { + rhs.i64x2_ge_s(self) + } + + #[doc(alias = "i8x16.le_u")] + pub const fn i8x16_le_u(self, rhs: Self) -> Self { + rhs.i8x16_ge_u(self) + } + + #[doc(alias = "i16x8.le_u")] + pub const fn i16x8_le_u(self, rhs: Self) -> Self { + rhs.i16x8_ge_u(self) + } + + #[doc(alias = "i32x4.le_u")] + pub const fn i32x4_le_u(self, rhs: Self) -> Self { + rhs.i32x4_ge_u(self) + } + + #[doc(alias = "i8x16.ge_s")] + pub const fn i8x16_ge_s(self, rhs: Self) -> Self { + 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 const fn i16x8_ge_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 { + out[i] = if a[i] >= b[i] { -1 } else { 0 }; + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.ge_s")] + pub const fn i32x4_ge_s(self, rhs: Self) -> Self { + 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 const fn i64x2_ge_s(self, rhs: Self) -> Self { + 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 const fn i8x16_ge_u(self, rhs: Self) -> Self { + 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 const fn i16x8_ge_u(self, rhs: Self) -> Self { + 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 const fn i32x4_ge_u(self, rhs: Self) -> Self { + 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 const fn i8x16_neg(self) -> Self { + 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 const fn i16x8_neg(self) -> Self { + 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 const fn i32x4_neg(self) -> Self { + 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 const fn i64x2_neg(self) -> Self { + 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 const fn i8x16_min_s(self, rhs: Self) -> Self { + 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 const fn i16x8_min_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 { + out[i] = if a[i] < b[i] { a[i] } else { b[i] }; + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.min_s")] + pub const fn i32x4_min_s(self, rhs: Self) -> Self { + 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 const fn i8x16_min_u(self, rhs: Self) -> Self { + 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 const fn i16x8_min_u(self, rhs: Self) -> Self { + 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 const fn i32x4_min_u(self, rhs: Self) -> Self { + 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 const fn i8x16_max_s(self, rhs: Self) -> Self { + 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 const fn i16x8_max_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 { + out[i] = if a[i] > b[i] { a[i] } else { b[i] }; + i += 1; + } + Self::from_i16x8(out) + } + + #[doc(alias = "i32x4.max_s")] + pub const fn i32x4_max_s(self, rhs: Self) -> Self { + 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 const fn i8x16_max_u(self, rhs: Self) -> Self { + 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 const fn i16x8_max_u(self, rhs: Self) -> Self { + 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 const fn i32x4_max_u(self, rhs: Self) -> Self { + 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 = "f32x4.eq")] + pub 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 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 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) + } + + #[doc(alias = "f64x2.ne")] + pub fn f64x2_ne(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.lt")] + pub 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 = "f64x2.lt")] + pub 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) + } + + #[doc(alias = "f32x4.gt")] + pub fn f32x4_gt(self, rhs: Self) -> Self { + rhs.f32x4_lt(self) + } + + #[doc(alias = "f64x2.gt")] + pub fn f64x2_gt(self, rhs: Self) -> Self { + rhs.f64x2_lt(self) + } + + #[doc(alias = "f32x4.le")] + pub 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 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 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 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(f32::ceil) + } + + #[doc(alias = "f64x2.ceil")] + pub fn f64x2_ceil(self) -> Self { + self.map_f64x2(f64::ceil) + } + + #[doc(alias = "f32x4.floor")] + pub fn f32x4_floor(self) -> Self { + self.map_f32x4(f32::floor) + } + + #[doc(alias = "f64x2.floor")] + pub fn f64x2_floor(self) -> Self { + self.map_f64x2(f64::floor) + } + + #[doc(alias = "f32x4.trunc")] + pub fn f32x4_trunc(self) -> Self { + self.map_f32x4(f32::trunc) + } + + #[doc(alias = "f64x2.trunc")] + pub fn f64x2_trunc(self) -> Self { + self.map_f64x2(f64::trunc) + } + + #[doc(alias = "f32x4.nearest")] + pub fn f32x4_nearest(self) -> Self { + self.map_f32x4(TinywasmFloatExt::tw_nearest) + } + + #[doc(alias = "f64x2.nearest")] + pub fn f64x2_nearest(self) -> Self { + self.map_f64x2(TinywasmFloatExt::tw_nearest) + } + + #[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| Self::canonicalize_simd_f32_nan(x.sqrt())) + } + + #[doc(alias = "f64x2.sqrt")] + pub fn f64x2_sqrt(self) -> Self { + self.map_f64x2(|x| Self::canonicalize_simd_f64_nan(x.sqrt())) + } + + #[doc(alias = "f32x4.add")] + pub fn f32x4_add(self, rhs: Self) -> Self { + self.zip_f32x4(rhs, |a, b| Self::canonicalize_simd_f32_nan(a + b)) + } + + #[doc(alias = "f64x2.add")] + pub fn f64x2_add(self, rhs: Self) -> Self { + self.zip_f64x2(rhs, |a, b| Self::canonicalize_simd_f64_nan(a + b)) + } + + #[doc(alias = "f32x4.sub")] + pub fn f32x4_sub(self, rhs: Self) -> Self { + self.zip_f32x4(rhs, |a, b| Self::canonicalize_simd_f32_nan(a - b)) + } + + #[doc(alias = "f64x2.sub")] + pub fn f64x2_sub(self, rhs: Self) -> Self { + self.zip_f64x2(rhs, |a, b| Self::canonicalize_simd_f64_nan(a - b)) + } + + #[doc(alias = "f32x4.mul")] + pub fn f32x4_mul(self, rhs: Self) -> Self { + self.zip_f32x4(rhs, |a, b| Self::canonicalize_simd_f32_nan(a * b)) + } + + #[doc(alias = "f64x2.mul")] + pub fn f64x2_mul(self, rhs: Self) -> Self { + self.zip_f64x2(rhs, |a, b| Self::canonicalize_simd_f64_nan(a * b)) + } + + #[doc(alias = "f32x4.div")] + pub fn f32x4_div(self, rhs: Self) -> Self { + self.zip_f32x4(rhs, |a, b| Self::canonicalize_simd_f32_nan(a / b)) + } + + #[doc(alias = "f64x2.div")] + pub fn f64x2_div(self, rhs: Self) -> Self { + self.zip_f64x2(rhs, |a, b| Self::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 }) + } + + #[doc(alias = "i32x4.trunc_sat_f32x4_s")] + pub fn i32x4_trunc_sat_f32x4_s(self) -> Self { + let v = self.as_f32x4(); + Self::from_i32x4([ + trunc_sat_f32_to_i32(v[0]), + trunc_sat_f32_to_i32(v[1]), + trunc_sat_f32_to_i32(v[2]), + trunc_sat_f32_to_i32(v[3]), + ]) + } + + #[doc(alias = "i32x4.trunc_sat_f32x4_u")] + pub fn i32x4_trunc_sat_f32x4_u(self) -> Self { + let v = self.as_f32x4(); + Self::from_u32x4([ + trunc_sat_f32_to_u32(v[0]), + trunc_sat_f32_to_u32(v[1]), + trunc_sat_f32_to_u32(v[2]), + trunc_sat_f32_to_u32(v[3]), + ]) + } + + #[doc(alias = "i32x4.trunc_sat_f64x2_s_zero")] + pub fn i32x4_trunc_sat_f64x2_s_zero(self) -> Self { + let v = self.as_f64x2(); + Self::from_i32x4([trunc_sat_f64_to_i32(v[0]), trunc_sat_f64_to_i32(v[1]), 0, 0]) + } + + #[doc(alias = "i32x4.trunc_sat_f64x2_u_zero")] + pub fn i32x4_trunc_sat_f64x2_u_zero(self) -> Self { + let v = self.as_f64x2(); + Self::from_u32x4([trunc_sat_f64_to_u32(v[0]), trunc_sat_f64_to_u32(v[1]), 0, 0]) + } + + #[doc(alias = "f32x4.convert_i32x4_s")] + pub fn f32x4_convert_i32x4_s(self) -> Self { + let v = self.as_i32x4(); + Self::from_f32x4([v[0] as f32, v[1] as f32, v[2] as f32, v[3] as f32]) + } + + #[doc(alias = "f32x4.convert_i32x4_u")] + pub fn f32x4_convert_i32x4_u(self) -> Self { + let v = self.as_u32x4(); + Self::from_f32x4([v[0] as f32, v[1] as f32, v[2] as f32, v[3] as f32]) + } + + #[doc(alias = "f64x2.convert_low_i32x4_s")] + pub fn f64x2_convert_low_i32x4_s(self) -> Self { + let v = self.as_i32x4(); + Self::from_f64x2([v[0] as f64, v[1] as f64]) + } + + #[doc(alias = "f64x2.convert_low_i32x4_u")] + pub fn f64x2_convert_low_i32x4_u(self) -> Self { + let v = self.as_u32x4(); + Self::from_f64x2([v[0] as f64, v[1] as f64]) + } + + #[doc(alias = "f32x4.demote_f64x2_zero")] + pub fn f32x4_demote_f64x2_zero(self) -> Self { + let v = self.as_f64x2(); + Self::from_f32x4([v[0] as f32, v[1] as f32, 0.0, 0.0]) + } + + #[doc(alias = "f64x2.promote_low_f32x4")] + pub fn f64x2_promote_low_f32x4(self) -> Self { + let v = self.as_f32x4(); + Self::from_f64x2([v[0] as f64, v[1] as f64]) + } + + 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) + } + + 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) + } + + 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) + } + + pub const fn splat_f32(src: f32) -> Self { + Self::splat_i32(src.to_bits() as i32) + } + + pub const fn splat_f64(src: f64) -> Self { + Self::splat_i64(src.to_bits() as i64) + } + + pub const fn extract_lane_i8(self, lane: u8) -> i8 { + debug_assert!(lane < 16); + let lane = lane as usize; + let bytes = self.to_le_bytes(); + bytes[lane] as i8 + } + + pub const fn extract_lane_u8(self, lane: u8) -> u8 { + debug_assert!(lane < 16); + let lane = lane as usize; + let bytes = self.to_le_bytes(); + bytes[lane] + } + + 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]]) + } + + 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]]) + } + + 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]]) + } + + 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], + ]) + } + + pub const fn extract_lane_f32(self, lane: u8) -> f32 { + f32::from_bits(self.extract_lane_i32(lane) as u32) + } + + pub const fn extract_lane_f64(self, lane: u8) -> f64 { + f64::from_bits(self.extract_lane_i64(lane) as u64) + } +} + +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) } } @@ -395,3 +2559,51 @@ impl core::ops::BitXor for Value128 { Self(self.0 ^ rhs.0) } } + +#[inline] +fn trunc_sat_f32_to_i32(v: f32) -> i32 { + if v.is_nan() { + 0 + } else if v <= -2147483904.0_f32 { + i32::MIN + } else if v >= 2147483648.0_f32 { + i32::MAX + } else { + v.trunc() as i32 + } +} + +#[inline] +fn trunc_sat_f32_to_u32(v: f32) -> u32 { + if v.is_nan() || v <= -1.0_f32 { + 0 + } else if v >= 4294967296.0_f32 { + u32::MAX + } else { + v.trunc() as u32 + } +} + +#[inline] +fn trunc_sat_f64_to_i32(v: f64) -> i32 { + if v.is_nan() { + 0 + } else if v <= -2147483649.0_f64 { + i32::MIN + } else if v >= 2147483648.0_f64 { + i32::MAX + } else { + v.trunc() as i32 + } +} + +#[inline] +fn trunc_sat_f64_to_u32(v: f64) -> u32 { + if v.is_nan() || v <= -1.0_f64 { + 0 + } else if v >= 4294967296.0_f64 { + u32::MAX + } else { + v.trunc() as u32 + } +} diff --git a/crates/tinywasm/src/store/memory.rs b/crates/tinywasm/src/store/memory.rs index 8898212..65c4361 100644 --- a/crates/tinywasm/src/store/memory.rs +++ b/crates/tinywasm/src/store/memory.rs @@ -2,7 +2,7 @@ use alloc::vec; use alloc::vec::Vec; use tinywasm_types::{MemoryArch, MemoryType, ModuleInstanceAddr}; -use crate::{Error, Result, cold, interpreter::Value128, log}; +use crate::{cold, interpreter::Value128, log, Error, Result}; /// A WebAssembly Memory Instance /// diff --git a/crates/tinywasm/tests/generated/wasm-simd.csv b/crates/tinywasm/tests/generated/wasm-simd.csv index 5603ed0..96d9796 100644 --- a/crates/tinywasm/tests/generated/wasm-simd.csv +++ b/crates/tinywasm/tests/generated/wasm-simd.csv @@ -1,2 +1,2 @@ 0.8.0,1300,24679,[{"name":"simd_address.wast","passed":4,"failed":45},{"name":"simd_align.wast","passed":46,"failed":54},{"name":"simd_bit_shift.wast","passed":39,"failed":213},{"name":"simd_bitwise.wast","passed":28,"failed":141},{"name":"simd_boolean.wast","passed":16,"failed":261},{"name":"simd_const.wast","passed":301,"failed":456},{"name":"simd_conversions.wast","passed":48,"failed":234},{"name":"simd_f32x4.wast","passed":16,"failed":774},{"name":"simd_f32x4_arith.wast","passed":16,"failed":1806},{"name":"simd_f32x4_cmp.wast","passed":24,"failed":2583},{"name":"simd_f32x4_pmin_pmax.wast","passed":14,"failed":3873},{"name":"simd_f32x4_rounding.wast","passed":24,"failed":177},{"name":"simd_f64x2.wast","passed":8,"failed":795},{"name":"simd_f64x2_arith.wast","passed":16,"failed":1809},{"name":"simd_f64x2_cmp.wast","passed":24,"failed":2661},{"name":"simd_f64x2_pmin_pmax.wast","passed":14,"failed":3873},{"name":"simd_f64x2_rounding.wast","passed":24,"failed":177},{"name":"simd_i16x8_arith.wast","passed":11,"failed":183},{"name":"simd_i16x8_arith2.wast","passed":19,"failed":153},{"name":"simd_i16x8_cmp.wast","passed":30,"failed":435},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":4,"failed":17},{"name":"simd_i16x8_extmul_i8x16.wast","passed":12,"failed":105},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":3,"failed":27},{"name":"simd_i16x8_sat_arith.wast","passed":16,"failed":206},{"name":"simd_i32x4_arith.wast","passed":11,"failed":183},{"name":"simd_i32x4_arith2.wast","passed":26,"failed":123},{"name":"simd_i32x4_cmp.wast","passed":40,"failed":435},{"name":"simd_i32x4_dot_i16x8.wast","passed":3,"failed":27},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":4,"failed":17},{"name":"simd_i32x4_extmul_i16x8.wast","passed":12,"failed":105},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":4,"failed":103},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":4,"failed":103},{"name":"simd_i64x2_arith.wast","passed":11,"failed":189},{"name":"simd_i64x2_arith2.wast","passed":2,"failed":23},{"name":"simd_i64x2_cmp.wast","passed":10,"failed":103},{"name":"simd_i64x2_extmul_i32x4.wast","passed":12,"failed":105},{"name":"simd_i8x16_arith.wast","passed":8,"failed":123},{"name":"simd_i8x16_arith2.wast","passed":25,"failed":186},{"name":"simd_i8x16_cmp.wast","passed":30,"failed":415},{"name":"simd_i8x16_sat_arith.wast","passed":24,"failed":190},{"name":"simd_int_to_int_extend.wast","passed":24,"failed":229},{"name":"simd_lane.wast","passed":189,"failed":286},{"name":"simd_linking.wast","passed":0,"failed":3},{"name":"simd_load.wast","passed":8,"failed":31},{"name":"simd_load16_lane.wast","passed":3,"failed":33},{"name":"simd_load32_lane.wast","passed":3,"failed":21},{"name":"simd_load64_lane.wast","passed":3,"failed":13},{"name":"simd_load8_lane.wast","passed":3,"failed":49},{"name":"simd_load_extend.wast","passed":18,"failed":86},{"name":"simd_load_splat.wast","passed":12,"failed":114},{"name":"simd_load_zero.wast","passed":10,"failed":29},{"name":"simd_splat.wast","passed":23,"failed":162},{"name":"simd_store.wast","passed":9,"failed":19},{"name":"simd_store16_lane.wast","passed":3,"failed":33},{"name":"simd_store32_lane.wast","passed":3,"failed":21},{"name":"simd_store64_lane.wast","passed":3,"failed":13},{"name":"simd_store8_lane.wast","passed":3,"failed":49}] -0.9.0-alpha.0,2867,23122,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":41,"failed":211},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":139,"failed":138},{"name":"simd_const.wast","passed":755,"failed":2},{"name":"simd_conversions.wast","passed":50,"failed":232},{"name":"simd_f32x4.wast","passed":18,"failed":772},{"name":"simd_f32x4_arith.wast","passed":19,"failed":1803},{"name":"simd_f32x4_cmp.wast","passed":26,"failed":2581},{"name":"simd_f32x4_pmin_pmax.wast","passed":15,"failed":3872},{"name":"simd_f32x4_rounding.wast","passed":25,"failed":176},{"name":"simd_f64x2.wast","passed":10,"failed":793},{"name":"simd_f64x2_arith.wast","passed":19,"failed":1806},{"name":"simd_f64x2_cmp.wast","passed":26,"failed":2659},{"name":"simd_f64x2_pmin_pmax.wast","passed":15,"failed":3872},{"name":"simd_f64x2_rounding.wast","passed":25,"failed":176},{"name":"simd_i16x8_arith.wast","passed":13,"failed":181},{"name":"simd_i16x8_arith2.wast","passed":21,"failed":151},{"name":"simd_i16x8_cmp.wast","passed":32,"failed":433},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":5,"failed":16},{"name":"simd_i16x8_extmul_i8x16.wast","passed":13,"failed":104},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":18,"failed":204},{"name":"simd_i32x4_arith.wast","passed":13,"failed":181},{"name":"simd_i32x4_arith2.wast","passed":28,"failed":121},{"name":"simd_i32x4_cmp.wast","passed":42,"failed":433},{"name":"simd_i32x4_dot_i16x8.wast","passed":14,"failed":18},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":5,"failed":16},{"name":"simd_i32x4_extmul_i16x8.wast","passed":13,"failed":104},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":5,"failed":102},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":5,"failed":102},{"name":"simd_i64x2_arith.wast","passed":13,"failed":187},{"name":"simd_i64x2_arith2.wast","passed":4,"failed":21},{"name":"simd_i64x2_cmp.wast","passed":11,"failed":102},{"name":"simd_i64x2_extmul_i32x4.wast","passed":13,"failed":104},{"name":"simd_i8x16_arith.wast","passed":10,"failed":121},{"name":"simd_i8x16_arith2.wast","passed":27,"failed":184},{"name":"simd_i8x16_cmp.wast","passed":32,"failed":413},{"name":"simd_i8x16_sat_arith.wast","passed":26,"failed":188},{"name":"simd_int_to_int_extend.wast","passed":25,"failed":228},{"name":"simd_lane.wast","passed":328,"failed":147},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":30,"failed":9},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":36,"failed":68},{"name":"simd_load_splat.wast","passed":126,"failed":0},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":158,"failed":27},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":4,"failed":32},{"name":"simd_store32_lane.wast","passed":4,"failed":20},{"name":"simd_store64_lane.wast","passed":4,"failed":12},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] +0.9.0-alpha.0,25964,25,[{"name":"simd_address.wast","passed":49,"failed":0},{"name":"simd_align.wast","passed":100,"failed":0},{"name":"simd_bit_shift.wast","passed":252,"failed":0},{"name":"simd_bitwise.wast","passed":169,"failed":0},{"name":"simd_boolean.wast","passed":277,"failed":0},{"name":"simd_const.wast","passed":757,"failed":0},{"name":"simd_conversions.wast","passed":276,"failed":6},{"name":"simd_f32x4.wast","passed":790,"failed":0},{"name":"simd_f32x4_arith.wast","passed":1822,"failed":0},{"name":"simd_f32x4_cmp.wast","passed":2607,"failed":0},{"name":"simd_f32x4_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f32x4_rounding.wast","passed":192,"failed":9},{"name":"simd_f64x2.wast","passed":803,"failed":0},{"name":"simd_f64x2_arith.wast","passed":1824,"failed":1},{"name":"simd_f64x2_cmp.wast","passed":2685,"failed":0},{"name":"simd_f64x2_pmin_pmax.wast","passed":3887,"failed":0},{"name":"simd_f64x2_rounding.wast","passed":192,"failed":9},{"name":"simd_i16x8_arith.wast","passed":194,"failed":0},{"name":"simd_i16x8_arith2.wast","passed":172,"failed":0},{"name":"simd_i16x8_cmp.wast","passed":465,"failed":0},{"name":"simd_i16x8_extadd_pairwise_i8x16.wast","passed":21,"failed":0},{"name":"simd_i16x8_extmul_i8x16.wast","passed":117,"failed":0},{"name":"simd_i16x8_q15mulr_sat_s.wast","passed":30,"failed":0},{"name":"simd_i16x8_sat_arith.wast","passed":222,"failed":0},{"name":"simd_i32x4_arith.wast","passed":194,"failed":0},{"name":"simd_i32x4_arith2.wast","passed":149,"failed":0},{"name":"simd_i32x4_cmp.wast","passed":475,"failed":0},{"name":"simd_i32x4_dot_i16x8.wast","passed":32,"failed":0},{"name":"simd_i32x4_extadd_pairwise_i16x8.wast","passed":21,"failed":0},{"name":"simd_i32x4_extmul_i16x8.wast","passed":117,"failed":0},{"name":"simd_i32x4_trunc_sat_f32x4.wast","passed":107,"failed":0},{"name":"simd_i32x4_trunc_sat_f64x2.wast","passed":107,"failed":0},{"name":"simd_i64x2_arith.wast","passed":200,"failed":0},{"name":"simd_i64x2_arith2.wast","passed":25,"failed":0},{"name":"simd_i64x2_cmp.wast","passed":113,"failed":0},{"name":"simd_i64x2_extmul_i32x4.wast","passed":117,"failed":0},{"name":"simd_i8x16_arith.wast","passed":131,"failed":0},{"name":"simd_i8x16_arith2.wast","passed":211,"failed":0},{"name":"simd_i8x16_cmp.wast","passed":445,"failed":0},{"name":"simd_i8x16_sat_arith.wast","passed":214,"failed":0},{"name":"simd_int_to_int_extend.wast","passed":253,"failed":0},{"name":"simd_lane.wast","passed":475,"failed":0},{"name":"simd_linking.wast","passed":3,"failed":0},{"name":"simd_load.wast","passed":39,"failed":0},{"name":"simd_load16_lane.wast","passed":36,"failed":0},{"name":"simd_load32_lane.wast","passed":24,"failed":0},{"name":"simd_load64_lane.wast","passed":16,"failed":0},{"name":"simd_load8_lane.wast","passed":52,"failed":0},{"name":"simd_load_extend.wast","passed":104,"failed":0},{"name":"simd_load_splat.wast","passed":126,"failed":0},{"name":"simd_load_zero.wast","passed":39,"failed":0},{"name":"simd_memory-multi.wast","passed":1,"failed":0},{"name":"simd_select.wast","passed":7,"failed":0},{"name":"simd_splat.wast","passed":185,"failed":0},{"name":"simd_store.wast","passed":28,"failed":0},{"name":"simd_store16_lane.wast","passed":36,"failed":0},{"name":"simd_store32_lane.wast","passed":24,"failed":0},{"name":"simd_store64_lane.wast","passed":16,"failed":0},{"name":"simd_store8_lane.wast","passed":52,"failed":0}] -- cgit v1.3.1