From 389b855487cf5c34b6ef6959ba22227126bed047 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 13:28:53 +0100 Subject: [PATCH 01/12] implement v128_store8_lane translation --- crates/core/src/simd.rs | 6 + crates/ir/src/enum.rs | 5 + crates/wasmi/src/engine/translator/mod.rs | 33 +++- .../wasmi/src/engine/translator/simd/mod.rs | 155 +++++++++++++++++- .../wasmi/src/engine/translator/simd/visit.rs | 4 +- 5 files changed, 196 insertions(+), 7 deletions(-) diff --git a/crates/core/src/simd.rs b/crates/core/src/simd.rs index ad5743eb34..518da30bae 100644 --- a/crates/core/src/simd.rs +++ b/crates/core/src/simd.rs @@ -95,6 +95,12 @@ macro_rules! impl_imm_lane_id { } } + impl From<$name> for u8 { + fn from(lane: $name) -> u8 { + lane.get() + } + } + impl TryFrom for $name { type Error = OutOfBoundsLaneIdx; diff --git a/crates/ir/src/enum.rs b/crates/ir/src/enum.rs index f0b3e4e43e..736f12e94e 100644 --- a/crates/ir/src/enum.rs +++ b/crates/ir/src/enum.rs @@ -263,6 +263,11 @@ impl Instruction { } Err(self) } + + /// Creates a new [`Instruction::Imm16AndImm32`] from the given `lane` and `memory` index. + pub fn lane_and_memory_index(value: impl Into, memory: Memory) -> Self { + Self::imm16_and_imm32(u16::from(value.into()), u32::from(memory)) + } } #[test] diff --git a/crates/wasmi/src/engine/translator/mod.rs b/crates/wasmi/src/engine/translator/mod.rs index 5913f7e751..c3df4dbde8 100644 --- a/crates/wasmi/src/engine/translator/mod.rs +++ b/crates/wasmi/src/engine/translator/mod.rs @@ -2149,8 +2149,39 @@ impl FuncTranslator { Field: TryFrom + Into, { bail_unreachable!(self); - let (memory, offset) = Self::decode_memarg(memarg); let (ptr, value) = self.alloc.stack.pop2(); + self.translate_istore_wrap_impl::( + memarg, + ptr, + value, + make_instr, + make_instr_imm, + make_instr_offset16, + make_instr_offset16_imm, + make_instr_at, + make_instr_at_imm, + ) + } + + /// Implementation of [`Self::translate_istore_wrap`] without emulation stack popping. + #[allow(clippy::too_many_arguments)] + fn translate_istore_wrap_impl( + &mut self, + memarg: MemArg, + ptr: TypedProvider, + value: TypedProvider, + make_instr: fn(ptr: Reg, offset_lo: Offset64Lo) -> Instruction, + make_instr_imm: fn(ptr: Reg, offset_lo: Offset64Lo) -> Instruction, + make_instr_offset16: fn(ptr: Reg, offset: Offset16, value: Reg) -> Instruction, + make_instr_offset16_imm: fn(ptr: Reg, offset: Offset16, value: Field) -> Instruction, + make_instr_at: fn(value: Reg, address: Address32) -> Instruction, + make_instr_at_imm: fn(value: Field, address: Address32) -> Instruction, + ) -> Result<(), Error> + where + Src: Copy + Wrap + From, + Field: TryFrom + Into, + { + let (memory, offset) = Self::decode_memarg(memarg); let (ptr, offset) = match ptr { Provider::Register(ptr) => (ptr, offset), Provider::Const(ptr) => { diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index f3ea984b2e..79e2493934 100644 --- a/crates/wasmi/src/engine/translator/simd/mod.rs +++ b/crates/wasmi/src/engine/translator/simd/mod.rs @@ -1,12 +1,25 @@ mod visit; -use super::{utils::Wrap, FuncTranslator}; +use super::{utils::Wrap, FuncTranslator, Instr, TypedProvider}; use crate::{ - core::{simd, TypedVal, V128}, - engine::{translator::provider::Provider, FuelCosts}, - ir::{Instruction, IntoShiftAmount, Reg}, + core::{simd, simd::ImmLaneIdx16, TrapCode, TypedVal, V128}, + engine::{translator::Provider, FuelCosts}, + ir::{ + index, + index::Memory, + Address32, + AnyConst16, + Instruction, + IntoShiftAmount, + Offset16, + Offset64, + Offset64Lo, + Offset8, + Reg, + }, Error, }; +use wasmparser::MemArg; trait IntoLane { type LaneType: TryFrom; @@ -216,4 +229,138 @@ impl FuncTranslator { } Ok(()) } + + fn translate_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Result<(), Error> { + bail_unreachable!(self); + let Ok(lane) = ImmLaneIdx16::try_from(lane) else { + panic!("encountered out of bounds lane index: {lane}"); + }; + let (ptr, v128) = self.alloc.stack.pop2(); + let v128 = match v128 { + Provider::Register(v128) => v128, + Provider::Const(v128) => { + // Case: with `v128` being an immediate value we can extract its + // lane value and translate as a more efficient non-SIMD operation. + let v128 = V128::from(v128); + let value = simd::i8x16_extract_lane_s(v128, lane); + return self.translate_v128_store8_lane_imm::( + memarg, + ptr, + value, + Instruction::i32_store8_imm, + Instruction::i32_store8_offset16_imm, + Instruction::i32_store8_at_imm, + ); + } + }; + let (memory, offset) = Self::decode_memarg(memarg); + let (ptr, offset) = match ptr { + Provider::Register(ptr) => (ptr, offset), + Provider::Const(ptr) => { + let Some(address) = self.effective_address(memory, ptr, offset) else { + return self.translate_trap(TrapCode::MemoryOutOfBounds); + }; + if let Ok(address) = Address32::try_from(address) { + return self.translate_v128_store8_lane_at( + memory, + address, + v128, + lane, + Instruction::v128_store8_lane_at, + ); + } + // Case: we cannot use specialized encoding and thus have to fall back + // to the general case where `ptr` is zero and `offset` stores the + // `ptr+offset` address value. + let zero_ptr = self.alloc.stack.alloc_const(0_u64)?; + (zero_ptr, u64::from(address)) + } + }; + if let Ok(Some(_)) = self.translate_v128_store_lane_mem0( + memory, + ptr, + offset, + v128, + lane, + Instruction::v128_store8_lane_offset8, + ) { + return Ok(()); + } + let (offset_hi, offset_lo) = Offset64::split(offset); + let instr = Instruction::v128_store8_lane(ptr, offset_lo); + let param = Instruction::register_and_offset_hi(v128, offset_hi); + let memidx = Instruction::memory_index(memory); + self.push_fueled_instr(instr, FuelCosts::store)?; + self.alloc.instr_encoder.append_instr(param)?; + if !memory.is_default() { + self.alloc.instr_encoder.append_instr(memidx)?; + } + Ok(()) + } + + fn translate_v128_store8_lane_imm( + &mut self, + memarg: MemArg, + ptr: TypedProvider, + src: Src, + make_instr_imm: fn(ptr: Reg, offset_lo: Offset64Lo) -> Instruction, + make_instr_offset16_imm: fn(ptr: Reg, offset: Offset16, value: Field) -> Instruction, + make_instr_at_imm: fn(value: Field, address: Address32) -> Instruction, + ) -> Result<(), Error> + where + Src: Copy + Wrap + From + Into, + Field: TryFrom + Into, + { + self.translate_istore_wrap_impl::( + memarg, + ptr, + Provider::Const(src.into()), + |_, _| unreachable!(), + make_instr_imm, + |_, _, _| unreachable!(), + make_instr_offset16_imm, + |_, _| unreachable!(), + make_instr_at_imm, + ) + } + + fn translate_v128_store8_lane_at( + &mut self, + memory: index::Memory, + address: Address32, + value: Reg, + lane: LaneType, + make_instr_at: fn(value: Reg, address: Address32) -> Instruction, + ) -> Result<(), Error> + where + LaneType: Into, + { + self.push_fueled_instr(make_instr_at(value, address), FuelCosts::store)?; + self.alloc + .instr_encoder + .append_instr(Instruction::lane_and_memory_index(lane, memory))?; + Ok(()) + } + + fn translate_v128_store_lane_mem0( + &mut self, + memory: Memory, + ptr: Reg, + offset: u64, + value: Reg, + lane: LaneType, + make_instr_offset8: fn(Reg, Reg, Offset8, LaneType) -> Instruction, + ) -> Result, Error> { + if !memory.is_default() { + return Ok(None); + } + let Ok(offset8) = Offset8::try_from(offset) else { + return Ok(None); + }; + let instr = self.push_fueled_instr( + make_instr_offset8(ptr, value, offset8, lane), + FuelCosts::store, + )?; + Ok(Some(instr)) + } } diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 87ecd3019f..59eac6793f 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -119,8 +119,8 @@ impl VisitSimdOperator<'_> for FuncTranslator { todo!() } - fn visit_v128_store8_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_store8_lane(memarg, lane) } fn visit_v128_store16_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { From 05fc9f287e89dd6547e89f96791fdacc7723b147 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 13:29:19 +0100 Subject: [PATCH 02/12] implement v128_store8_lane execution --- crates/ir/src/enum.rs | 19 ++++ crates/wasmi/src/engine/executor/instrs.rs | 16 +++- .../wasmi/src/engine/executor/instrs/simd.rs | 87 ++++++++++++++++++- .../wasmi/src/engine/executor/instrs/store.rs | 49 +---------- .../wasmi/src/engine/executor/instrs/utils.rs | 55 ++++++++++++ 5 files changed, 172 insertions(+), 54 deletions(-) diff --git a/crates/ir/src/enum.rs b/crates/ir/src/enum.rs index 736f12e94e..21319c8a4f 100644 --- a/crates/ir/src/enum.rs +++ b/crates/ir/src/enum.rs @@ -268,6 +268,25 @@ impl Instruction { pub fn lane_and_memory_index(value: impl Into, memory: Memory) -> Self { Self::imm16_and_imm32(u16::from(value.into()), u32::from(memory)) } + + /// Returns `Some` lane and [`index::Memory`] if encoded properly. + /// + /// # Errors + /// + /// Returns back `self` if it was an incorrect [`Instruction`]. + /// This allows for a better error message to inform the user. + pub fn filter_lane_and_memory(self) -> Result<(LaneType, index::Memory), Self> + where + LaneType: TryFrom, + { + if let Instruction::Imm16AndImm32 { imm16, imm32 } = self { + let Ok(lane) = LaneType::try_from(i16::from(imm16) as u16 as u8) else { + return Err(self); + }; + return Ok((lane, index::Memory::from(u32::from(imm32)))); + } + Err(self) + } } #[test] diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index ffbe6395fa..b9b422e84b 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -1976,16 +1976,26 @@ impl<'engine> Executor<'engine> { self.execute_v128_store_at(&mut store.inner, address, value)? } #[cfg(feature = "simd")] - Instr::V128Store8Lane { ptr, offset_lo } => todo!(), + Instr::V128Store8Lane { ptr, offset_lo } => { + self.execute_v128_store8_lane(&mut store.inner, ptr, offset_lo)? + } #[cfg(feature = "simd")] Instr::V128Store8LaneOffset8 { ptr, value, offset, lane, - } => todo!(), + } => self.execute_v128_store8_lane_offset8( + &mut store.inner, + ptr, + value, + offset, + lane, + )?, #[cfg(feature = "simd")] - Instr::V128Store8LaneAt { value, address } => todo!(), + Instr::V128Store8LaneAt { value, address } => { + self.execute_v128_store8_lane_at(&mut store.inner, value, address)? + } #[cfg(feature = "simd")] Instr::V128Store16Lane { ptr, offset_lo } => todo!(), #[cfg(feature = "simd")] diff --git a/crates/wasmi/src/engine/executor/instrs/simd.rs b/crates/wasmi/src/engine/executor/instrs/simd.rs index 879317fa6b..07fcf5d047 100644 --- a/crates/wasmi/src/engine/executor/instrs/simd.rs +++ b/crates/wasmi/src/engine/executor/instrs/simd.rs @@ -1,14 +1,25 @@ use super::Executor; use crate::{ core::{ - simd, - simd::{ImmLaneIdx16, ImmLaneIdx2, ImmLaneIdx32, ImmLaneIdx4, ImmLaneIdx8}, + simd::{self, ImmLaneIdx16, ImmLaneIdx2, ImmLaneIdx32, ImmLaneIdx4, ImmLaneIdx8}, UntypedVal, WriteAs, V128, }, engine::{executor::InstructionPtr, utils::unreachable_unchecked}, - ir::{AnyConst32, Instruction, Reg, ShiftAmount}, + ir::{ + index, + Address32, + AnyConst32, + Instruction, + Offset64, + Offset64Lo, + Offset8, + Reg, + ShiftAmount, + }, + store::StoreInner, + Error, }; impl Executor<'_> { @@ -491,3 +502,73 @@ impl Executor<'_> { (Instruction::I64x2ShrUBy, execute_i64x2_shr_u_by, simd::i64x2_shr_u), } } + +impl Executor<'_> { + /// Returns the optional `memory` parameter for a `load_at` [`Instruction`]. + /// + /// # Note + /// + /// - Returns the default [`index::Memory`] if the parameter is missing. + /// - Bumps `self.ip` if a [`Instruction::MemoryIndex`] parameter was found. + #[inline(always)] + fn fetch_lane_and_memory(&mut self, delta: usize) -> (LaneType, index::Memory) + where + LaneType: TryFrom, + { + let mut addr: InstructionPtr = self.ip; + addr.add(delta); + match addr.get().filter_lane_and_memory() { + Ok(value) => value, + Err(instr) => unsafe { + unreachable_unchecked!( + "expected an `Instruction::Imm16AndImm32` but found: {instr:?}" + ) + }, + } + } + + pub fn execute_v128_store8_lane( + &mut self, + store: &mut StoreInner, + ptr: Reg, + offset_lo: Offset64Lo, + ) -> Result<(), Error> { + let (value, offset_hi) = self.fetch_value_and_offset_hi(); + let (lane, memory) = self.fetch_lane_and_memory(2); + let offset = Offset64::combine(offset_hi, offset_lo); + let ptr = self.get_register_as::(ptr); + let v128 = self.get_register_as::(value); + let memory = self.fetch_memory_bytes_mut(memory, store); + simd::v128_store8_lane(memory, ptr, u64::from(offset), v128, lane)?; + self.try_next_instr_at(3) + } + + pub fn execute_v128_store8_lane_offset8( + &mut self, + store: &mut StoreInner, + ptr: Reg, + value: Reg, + offset: Offset8, + lane: ImmLaneIdx16, + ) -> Result<(), Error> { + let ptr = self.get_register_as::(ptr); + let offset = u64::from(Offset64::from(offset)); + let v128 = self.get_register_as::(value); + let memory = self.fetch_default_memory_bytes_mut(); + simd::v128_store8_lane(memory, ptr, offset, v128, lane)?; + self.try_next_instr() + } + + pub fn execute_v128_store8_lane_at( + &mut self, + store: &mut StoreInner, + value: Reg, + address: Address32, + ) -> Result<(), Error> { + let (lane, memory) = self.fetch_lane_and_memory(1); + let v128 = self.get_register_as::(value); + let memory = self.fetch_memory_bytes_mut(memory, store); + simd::v128_store8_lane_at(memory, usize::from(address), v128, lane)?; + self.try_next_instr_at(2) + } +} diff --git a/crates/wasmi/src/engine/executor/instrs/store.rs b/crates/wasmi/src/engine/executor/instrs/store.rs index 5ce9976b32..6026c6dbff 100644 --- a/crates/wasmi/src/engine/executor/instrs/store.rs +++ b/crates/wasmi/src/engine/executor/instrs/store.rs @@ -31,12 +31,6 @@ type WasmStoreOp = type WasmStoreAtOp = fn(memory: &mut [u8], address: usize, value: T) -> Result<(), TrapCode>; impl Executor<'_> { - /// Returns the register `value` and `offset` parameters for a `load` [`Instruction`]. - fn fetch_value_and_offset_hi(&self) -> (Reg, Offset64Hi) { - // Safety: Wasmi translation guarantees that `Instruction::RegisterAndImm32` exists. - unsafe { self.fetch_reg_and_offset_hi() } - } - /// Returns the immediate `value` and `offset_hi` parameters for a `load` [`Instruction`]. fn fetch_value_and_offset_imm(&self) -> (T, Offset64Hi) where @@ -54,47 +48,6 @@ impl Executor<'_> { } } - /// Fetches the bytes of the default memory at index 0. - #[inline] - fn fetch_default_memory_bytes_mut(&mut self) -> &mut [u8] { - // Safety: the `self.cache.memory` pointer is always synchronized - // conservatively whenever it could have been invalidated. - unsafe { self.cache.memory.data_mut() } - } - - /// Fetches the bytes of the given `memory`. - #[inline] - fn fetch_memory_bytes_mut<'exec, 'store, 'bytes>( - &'exec mut self, - memory: Memory, - store: &'store mut StoreInner, - ) -> &'bytes mut [u8] - where - 'exec: 'bytes, - 'store: 'bytes, - { - match memory.is_default() { - true => self.fetch_default_memory_bytes_mut(), - false => self.fetch_non_default_memory_bytes_mut(memory, store), - } - } - - /// Fetches the bytes of the given non-default `memory`. - #[cold] - #[inline] - fn fetch_non_default_memory_bytes_mut<'exec, 'store, 'bytes>( - &'exec mut self, - memory: Memory, - store: &'store mut StoreInner, - ) -> &'bytes mut [u8] - where - 'exec: 'bytes, - 'store: 'bytes, - { - let memory = self.get_memory(memory); - store.resolve_memory_mut(&memory).data_mut() - } - /// Executes a generic Wasm `store[N]` operation. /// /// # Note @@ -105,7 +58,7 @@ impl Executor<'_> { /// - `{i32, i64}.store8` /// - `{i32, i64}.store16` /// - `i64.store32` - fn execute_store_wrap( + pub(super) fn execute_store_wrap( &mut self, store: &mut StoreInner, memory: Memory, diff --git a/crates/wasmi/src/engine/executor/instrs/utils.rs b/crates/wasmi/src/engine/executor/instrs/utils.rs index 0d3fb88ffb..6621768b42 100644 --- a/crates/wasmi/src/engine/executor/instrs/utils.rs +++ b/crates/wasmi/src/engine/executor/instrs/utils.rs @@ -1,3 +1,9 @@ +use super::Executor; +use crate::{ + ir::{index::Memory, Offset64Hi, Reg}, + store::StoreInner, +}; + macro_rules! impl_unary_executors { ( $( (Instruction::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => { $( @@ -19,3 +25,52 @@ macro_rules! impl_binary_executors { )* }; } + +impl Executor<'_> { + /// Returns the register `value` and `offset` parameters for a `load` [`Instruction`]. + pub fn fetch_value_and_offset_hi(&self) -> (Reg, Offset64Hi) { + // Safety: Wasmi translation guarantees that `Instruction::RegisterAndImm32` exists. + unsafe { self.fetch_reg_and_offset_hi() } + } + + /// Fetches the bytes of the default memory at index 0. + #[inline] + pub fn fetch_default_memory_bytes_mut(&mut self) -> &mut [u8] { + // Safety: the `self.cache.memory` pointer is always synchronized + // conservatively whenever it could have been invalidated. + unsafe { self.cache.memory.data_mut() } + } + + /// Fetches the bytes of the given `memory`. + #[inline] + pub fn fetch_memory_bytes_mut<'exec, 'store, 'bytes>( + &'exec mut self, + memory: Memory, + store: &'store mut StoreInner, + ) -> &'bytes mut [u8] + where + 'exec: 'bytes, + 'store: 'bytes, + { + match memory.is_default() { + true => self.fetch_default_memory_bytes_mut(), + false => self.fetch_non_default_memory_bytes_mut(memory, store), + } + } + + /// Fetches the bytes of the given non-default `memory`. + #[cold] + #[inline] + pub fn fetch_non_default_memory_bytes_mut<'exec, 'store, 'bytes>( + &'exec mut self, + memory: Memory, + store: &'store mut StoreInner, + ) -> &'bytes mut [u8] + where + 'exec: 'bytes, + 'store: 'bytes, + { + let memory = self.get_memory(memory); + store.resolve_memory_mut(&memory).data_mut() + } +} From 9ab295bcfc811373c9ec23afae116a4891b45b14 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 13:29:25 +0100 Subject: [PATCH 03/12] un-ignore passing SIMD tests --- crates/wast/tests/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wast/tests/mod.rs b/crates/wast/tests/mod.rs index 31300baba2..fa3faf3c71 100644 --- a/crates/wast/tests/mod.rs +++ b/crates/wast/tests/mod.rs @@ -253,7 +253,7 @@ macro_rules! expand_tests { #[ignore] fn wasm_simd_store16_lane("simd_store16_lane"); #[ignore] fn wasm_simd_store32_lane("simd_store32_lane"); #[ignore] fn wasm_simd_store64_lane("simd_store64_lane"); - #[ignore] fn wasm_simd_store8_lane("simd_store8_lane"); + fn wasm_simd_store8_lane("simd_store8_lane"); } }; } From 088aba0aae8ad2c2133199443fe5ce5096791a7d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 13:33:16 +0100 Subject: [PATCH 04/12] fix intra doc link --- crates/wasmi/src/engine/executor/instrs/utils.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/wasmi/src/engine/executor/instrs/utils.rs b/crates/wasmi/src/engine/executor/instrs/utils.rs index 6621768b42..2f08f4ff51 100644 --- a/crates/wasmi/src/engine/executor/instrs/utils.rs +++ b/crates/wasmi/src/engine/executor/instrs/utils.rs @@ -4,6 +4,9 @@ use crate::{ store::StoreInner, }; +#[cfg(doc)] +use crate::ir::Instruction; + macro_rules! impl_unary_executors { ( $( (Instruction::$var_name:ident, $fn_name:ident, $op:expr) ),* $(,)? ) => { $( From 9d6fb9f969f57db230d484a8127cf14e93c0c166 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 13:59:58 +0100 Subject: [PATCH 05/12] make translate_v128_store8_lane more generic --- .../wasmi/src/engine/translator/simd/mod.rs | 65 ++++++++++--------- .../wasmi/src/engine/translator/simd/visit.rs | 19 +++++- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index 79e2493934..44effb2b1d 100644 --- a/crates/wasmi/src/engine/translator/simd/mod.rs +++ b/crates/wasmi/src/engine/translator/simd/mod.rs @@ -2,7 +2,7 @@ mod visit; use super::{utils::Wrap, FuncTranslator, Instr, TypedProvider}; use crate::{ - core::{simd, simd::ImmLaneIdx16, TrapCode, TypedVal, V128}, + core::{simd, TrapCode, TypedVal, V128}, engine::{translator::Provider, FuelCosts}, ir::{ index, @@ -22,7 +22,7 @@ use crate::{ use wasmparser::MemArg; trait IntoLane { - type LaneType: TryFrom; + type LaneType: TryFrom + Into; } macro_rules! impl_into_lane_for { @@ -230,9 +230,29 @@ impl FuncTranslator { Ok(()) } - fn translate_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Result<(), Error> { + #[allow(clippy::type_complexity)] + fn translate_v128_store8_lane( + &mut self, + memarg: MemArg, + lane: u8, + make_instr: fn(ptr: Reg, offset_lo: Offset64Lo) -> Instruction, + make_instr_offset8: fn( + ptr: Reg, + value: Reg, + offset: Offset8, + lane: T::LaneType, + ) -> Instruction, + make_instr_at: fn(value: Reg, address: Address32) -> Instruction, + translate_imm: fn( + &mut Self, + memarg: MemArg, + ptr: TypedProvider, + lane: T::LaneType, + value: V128, + ) -> Result<(), Error>, + ) -> Result<(), Error> { bail_unreachable!(self); - let Ok(lane) = ImmLaneIdx16::try_from(lane) else { + let Ok(lane) = ::try_from(lane) else { panic!("encountered out of bounds lane index: {lane}"); }; let (ptr, v128) = self.alloc.stack.pop2(); @@ -241,16 +261,7 @@ impl FuncTranslator { Provider::Const(v128) => { // Case: with `v128` being an immediate value we can extract its // lane value and translate as a more efficient non-SIMD operation. - let v128 = V128::from(v128); - let value = simd::i8x16_extract_lane_s(v128, lane); - return self.translate_v128_store8_lane_imm::( - memarg, - ptr, - value, - Instruction::i32_store8_imm, - Instruction::i32_store8_offset16_imm, - Instruction::i32_store8_at_imm, - ); + return translate_imm(self, memarg, ptr, lane, V128::from(v128)); } }; let (memory, offset) = Self::decode_memarg(memarg); @@ -261,12 +272,12 @@ impl FuncTranslator { return self.translate_trap(TrapCode::MemoryOutOfBounds); }; if let Ok(address) = Address32::try_from(address) { - return self.translate_v128_store8_lane_at( + return self.translate_v128_store8_lane_at::( memory, address, v128, lane, - Instruction::v128_store8_lane_at, + make_instr_at, ); } // Case: we cannot use specialized encoding and thus have to fall back @@ -276,18 +287,13 @@ impl FuncTranslator { (zero_ptr, u64::from(address)) } }; - if let Ok(Some(_)) = self.translate_v128_store_lane_mem0( - memory, - ptr, - offset, - v128, - lane, - Instruction::v128_store8_lane_offset8, - ) { + if let Ok(Some(_)) = + self.translate_v128_store_lane_mem0(memory, ptr, offset, v128, lane, make_instr_offset8) + { return Ok(()); } let (offset_hi, offset_lo) = Offset64::split(offset); - let instr = Instruction::v128_store8_lane(ptr, offset_lo); + let instr = make_instr(ptr, offset_lo); let param = Instruction::register_and_offset_hi(v128, offset_hi); let memidx = Instruction::memory_index(memory); self.push_fueled_instr(instr, FuelCosts::store)?; @@ -324,17 +330,14 @@ impl FuncTranslator { ) } - fn translate_v128_store8_lane_at( + fn translate_v128_store8_lane_at( &mut self, memory: index::Memory, address: Address32, value: Reg, - lane: LaneType, + lane: T::LaneType, make_instr_at: fn(value: Reg, address: Address32) -> Instruction, - ) -> Result<(), Error> - where - LaneType: Into, - { + ) -> Result<(), Error> { self.push_fueled_instr(make_instr_at(value, address), FuelCosts::store)?; self.alloc .instr_encoder diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 59eac6793f..a6a8afc20e 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -120,7 +120,24 @@ impl VisitSimdOperator<'_> for FuncTranslator { } fn visit_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { - self.translate_v128_store8_lane(memarg, lane) + self.translate_v128_store8_lane::( + memarg, + lane, + Instruction::v128_store8_lane, + Instruction::v128_store8_lane_offset8, + Instruction::v128_store8_lane_at, + |this, memarg, ptr, lane, v128| { + let value = simd::i8x16_extract_lane_s(v128, lane); + this.translate_v128_store8_lane_imm::( + memarg, + ptr, + value, + Instruction::i32_store8_imm, + Instruction::i32_store8_offset16_imm, + Instruction::i32_store8_at_imm, + ) + }, + ) } fn visit_v128_store16_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { From 5c3946e6fb9c79bdfc30c0fee8bde41e0201199d Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 14:00:14 +0100 Subject: [PATCH 06/12] add translation for v128_store16_lane --- .../wasmi/src/engine/translator/simd/visit.rs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index a6a8afc20e..10addf2b80 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -140,8 +140,25 @@ impl VisitSimdOperator<'_> for FuncTranslator { ) } - fn visit_v128_store16_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_store16_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_store8_lane::( + memarg, + lane, + Instruction::v128_store16_lane, + Instruction::v128_store16_lane_offset8, + Instruction::v128_store16_lane_at, + |this, memarg, ptr, lane, v128| { + let value = simd::i16x8_extract_lane_s(v128, lane); + this.translate_v128_store8_lane_imm::( + memarg, + ptr, + value, + Instruction::i32_store16_imm, + Instruction::i32_store16_offset16_imm, + Instruction::i32_store16_at_imm, + ) + }, + ) } fn visit_v128_store32_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { From 6baa7f1e0f6c393cce7e3a510f14d1a295f073e3 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 14:01:24 +0100 Subject: [PATCH 07/12] rename generic translation methods --- crates/wasmi/src/engine/translator/simd/mod.rs | 8 ++++---- crates/wasmi/src/engine/translator/simd/visit.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index 44effb2b1d..fb753e3616 100644 --- a/crates/wasmi/src/engine/translator/simd/mod.rs +++ b/crates/wasmi/src/engine/translator/simd/mod.rs @@ -231,7 +231,7 @@ impl FuncTranslator { } #[allow(clippy::type_complexity)] - fn translate_v128_store8_lane( + fn translate_v128_store_lane( &mut self, memarg: MemArg, lane: u8, @@ -272,7 +272,7 @@ impl FuncTranslator { return self.translate_trap(TrapCode::MemoryOutOfBounds); }; if let Ok(address) = Address32::try_from(address) { - return self.translate_v128_store8_lane_at::( + return self.translate_v128_store_lane_at::( memory, address, v128, @@ -304,7 +304,7 @@ impl FuncTranslator { Ok(()) } - fn translate_v128_store8_lane_imm( + fn translate_v128_store_lane_imm( &mut self, memarg: MemArg, ptr: TypedProvider, @@ -330,7 +330,7 @@ impl FuncTranslator { ) } - fn translate_v128_store8_lane_at( + fn translate_v128_store_lane_at( &mut self, memory: index::Memory, address: Address32, diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 10addf2b80..a8fda8169a 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -120,7 +120,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { } fn visit_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { - self.translate_v128_store8_lane::( + self.translate_v128_store_lane::( memarg, lane, Instruction::v128_store8_lane, @@ -128,7 +128,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { Instruction::v128_store8_lane_at, |this, memarg, ptr, lane, v128| { let value = simd::i8x16_extract_lane_s(v128, lane); - this.translate_v128_store8_lane_imm::( + this.translate_v128_store_lane_imm::( memarg, ptr, value, @@ -141,7 +141,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { } fn visit_v128_store16_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { - self.translate_v128_store8_lane::( + self.translate_v128_store_lane::( memarg, lane, Instruction::v128_store16_lane, @@ -149,7 +149,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { Instruction::v128_store16_lane_at, |this, memarg, ptr, lane, v128| { let value = simd::i16x8_extract_lane_s(v128, lane); - this.translate_v128_store8_lane_imm::( + this.translate_v128_store_lane_imm::( memarg, ptr, value, From 17807bf90354f92514bd328185d1ea495c20f24b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 14:37:20 +0100 Subject: [PATCH 08/12] implement v128.storeN_lane execution --- crates/wasmi/src/engine/executor/instrs.rs | 48 ++++++-- .../wasmi/src/engine/executor/instrs/simd.rs | 113 ++++++++++++++++-- 2 files changed, 144 insertions(+), 17 deletions(-) diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index b9b422e84b..02f18ff0d0 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -1997,38 +1997,68 @@ impl<'engine> Executor<'engine> { self.execute_v128_store8_lane_at(&mut store.inner, value, address)? } #[cfg(feature = "simd")] - Instr::V128Store16Lane { ptr, offset_lo } => todo!(), + Instr::V128Store16Lane { ptr, offset_lo } => { + self.execute_v128_store16_lane(&mut store.inner, ptr, offset_lo)? + } #[cfg(feature = "simd")] Instr::V128Store16LaneOffset8 { ptr, value, offset, lane, - } => todo!(), + } => self.execute_v128_store16_lane_offset8( + &mut store.inner, + ptr, + value, + offset, + lane, + )?, #[cfg(feature = "simd")] - Instr::V128Store16LaneAt { value, address } => todo!(), + Instr::V128Store16LaneAt { value, address } => { + self.execute_v128_store16_lane_at(&mut store.inner, value, address)? + } #[cfg(feature = "simd")] - Instr::V128Store32Lane { ptr, offset_lo } => todo!(), + Instr::V128Store32Lane { ptr, offset_lo } => { + self.execute_v128_store32_lane(&mut store.inner, ptr, offset_lo)? + } #[cfg(feature = "simd")] Instr::V128Store32LaneOffset8 { ptr, value, offset, lane, - } => todo!(), + } => self.execute_v128_store32_lane_offset8( + &mut store.inner, + ptr, + value, + offset, + lane, + )?, #[cfg(feature = "simd")] - Instr::V128Store32LaneAt { value, address } => todo!(), + Instr::V128Store32LaneAt { value, address } => { + self.execute_v128_store32_lane_at(&mut store.inner, value, address)? + } #[cfg(feature = "simd")] - Instr::V128Store64Lane { ptr, offset_lo } => todo!(), + Instr::V128Store64Lane { ptr, offset_lo } => { + self.execute_v128_store64_lane(&mut store.inner, ptr, offset_lo)? + } #[cfg(feature = "simd")] Instr::V128Store64LaneOffset8 { ptr, value, offset, lane, - } => todo!(), + } => self.execute_v128_store64_lane_offset8( + &mut store.inner, + ptr, + value, + offset, + lane, + )?, #[cfg(feature = "simd")] - Instr::V128Store64LaneAt { value, address } => todo!(), + Instr::V128Store64LaneAt { value, address } => { + self.execute_v128_store64_lane_at(&mut store.inner, value, address)? + } #[cfg(feature = "simd")] Instr::V128Load { result, offset_lo } => { self.execute_v128_load(&store.inner, result, offset_lo)? diff --git a/crates/wasmi/src/engine/executor/instrs/simd.rs b/crates/wasmi/src/engine/executor/instrs/simd.rs index 07fcf5d047..081836ac66 100644 --- a/crates/wasmi/src/engine/executor/instrs/simd.rs +++ b/crates/wasmi/src/engine/executor/instrs/simd.rs @@ -2,6 +2,7 @@ use super::Executor; use crate::{ core::{ simd::{self, ImmLaneIdx16, ImmLaneIdx2, ImmLaneIdx32, ImmLaneIdx4, ImmLaneIdx8}, + TrapCode, UntypedVal, WriteAs, V128, @@ -526,12 +527,82 @@ impl Executor<'_> { }, } } +} + +macro_rules! impl_execute_v128_store_lane { + ( + $( (Instruction::$op:ident, $lane_ty:ty, $exec:ident, $eval:expr) ),* $(,)? + ) => { + $( + #[doc = concat!("Executes an [`Instruction::", stringify!($op), "`] instruction.")] + pub fn $exec( + &mut self, + store: &mut StoreInner, + ptr: Reg, + offset_lo: Offset64Lo, + ) -> Result<(), Error> { + self.execute_v128_store_lane::<$lane_ty>(store, ptr, offset_lo, $eval) + } + )* + }; +} + +macro_rules! impl_execute_v128_store_lane_offset16 { + ( + $( (Instruction::$op:ident, $lane_ty:ty, $exec:ident, $eval:expr) ),* $(,)? + ) => { + $( + #[doc = concat!("Executes an [`Instruction::", stringify!($op), "`] instruction.")] + pub fn $exec( + &mut self, + store: &mut StoreInner, + ptr: Reg, + value: Reg, + offset: Offset8, + lane: $lane_ty, + ) -> Result<(), Error> { + self.execute_v128_store_lane_offset8::<$lane_ty>(store, ptr, value, offset, lane, $eval) + } + )* + }; +} - pub fn execute_v128_store8_lane( +macro_rules! impl_execute_v128_store_lane_at { + ( + $( (Instruction::$op:ident, $lane_ty:ty, $exec:ident, $eval:expr) ),* $(,)? + ) => { + $( + #[doc = concat!("Executes an [`Instruction::", stringify!($op), "`] instruction.")] + pub fn $exec( + &mut self, + store: &mut StoreInner, + value: Reg, + address: Address32, + ) -> Result<(), Error> { + self.execute_v128_store_lane_at::<$lane_ty>(store, value, address, $eval) + } + )* + }; +} + +type V128StoreLane = fn( + memory: &mut [u8], + ptr: u64, + offset: u64, + value: V128, + lane: LaneType, +) -> Result<(), TrapCode>; + +type V128StoreLaneAt = + fn(memory: &mut [u8], address: usize, value: V128, lane: LaneType) -> Result<(), TrapCode>; + +impl Executor<'_> { + fn execute_v128_store_lane( &mut self, store: &mut StoreInner, ptr: Reg, offset_lo: Offset64Lo, + eval: V128StoreLane, ) -> Result<(), Error> { let (value, offset_hi) = self.fetch_value_and_offset_hi(); let (lane, memory) = self.fetch_lane_and_memory(2); @@ -543,32 +614,58 @@ impl Executor<'_> { self.try_next_instr_at(3) } - pub fn execute_v128_store8_lane_offset8( + impl_execute_v128_store_lane! { + (Instruction::V128Store8Lane, ImmLaneIdx16, execute_v128_store8_lane, simd::v128_store8_lane), + (Instruction::V128Store16Lane, ImmLaneIdx8, execute_v128_store16_lane, simd::v128_store16_lane), + (Instruction::V128Store32Lane, ImmLaneIdx4, execute_v128_store32_lane, simd::v128_store32_lane), + (Instruction::V128Store64Lane, ImmLaneIdx2, execute_v128_store64_lane, simd::v128_store64_lane), + } + + fn execute_v128_store_lane_offset8( &mut self, store: &mut StoreInner, ptr: Reg, value: Reg, offset: Offset8, - lane: ImmLaneIdx16, + lane: LaneType, + eval: V128StoreLane, ) -> Result<(), Error> { let ptr = self.get_register_as::(ptr); let offset = u64::from(Offset64::from(offset)); let v128 = self.get_register_as::(value); let memory = self.fetch_default_memory_bytes_mut(); - simd::v128_store8_lane(memory, ptr, offset, v128, lane)?; + eval(memory, ptr, offset, v128, lane)?; self.try_next_instr() } - pub fn execute_v128_store8_lane_at( + impl_execute_v128_store_lane_offset16! { + (Instruction::V128Store8LaneOffset8, ImmLaneIdx16, execute_v128_store8_lane_offset8, simd::v128_store8_lane), + (Instruction::V128Store16LaneOffset8, ImmLaneIdx8, execute_v128_store16_lane_offset8, simd::v128_store16_lane), + (Instruction::V128Store32LaneOffset8, ImmLaneIdx4, execute_v128_store32_lane_offset8, simd::v128_store32_lane), + (Instruction::V128Store64LaneOffset8, ImmLaneIdx2, execute_v128_store64_lane_offset8, simd::v128_store64_lane), + } + + fn execute_v128_store_lane_at( &mut self, store: &mut StoreInner, value: Reg, address: Address32, - ) -> Result<(), Error> { - let (lane, memory) = self.fetch_lane_and_memory(1); + eval: V128StoreLaneAt, + ) -> Result<(), Error> + where + LaneType: TryFrom + Into, + { + let (lane, memory) = self.fetch_lane_and_memory::(1); let v128 = self.get_register_as::(value); let memory = self.fetch_memory_bytes_mut(memory, store); - simd::v128_store8_lane_at(memory, usize::from(address), v128, lane)?; + eval(memory, usize::from(address), v128, lane)?; self.try_next_instr_at(2) } + + impl_execute_v128_store_lane_at! { + (Instruction::V128Store8LaneAt, ImmLaneIdx16, execute_v128_store8_lane_at, simd::v128_store8_lane_at), + (Instruction::V128Store16LaneAt, ImmLaneIdx8, execute_v128_store16_lane_at, simd::v128_store16_lane_at), + (Instruction::V128Store32LaneAt, ImmLaneIdx4, execute_v128_store32_lane_at, simd::v128_store32_lane_at), + (Instruction::V128Store64LaneAt, ImmLaneIdx2, execute_v128_store64_lane_at, simd::v128_store64_lane_at), + } } From a46a1dd48cf61580dd2c736e8fa7a089f772e278 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 14:37:30 +0100 Subject: [PATCH 09/12] un-ignore passing SIMD test --- crates/wast/tests/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/wast/tests/mod.rs b/crates/wast/tests/mod.rs index fa3faf3c71..7475610504 100644 --- a/crates/wast/tests/mod.rs +++ b/crates/wast/tests/mod.rs @@ -250,7 +250,7 @@ macro_rules! expand_tests { #[ignore] fn wasm_simd_load_zero("simd_load_zero"); fn wasm_simd_splat("simd_splat"); fn wasm_simd_store("simd_store"); - #[ignore] fn wasm_simd_store16_lane("simd_store16_lane"); + fn wasm_simd_store16_lane("simd_store16_lane"); #[ignore] fn wasm_simd_store32_lane("simd_store32_lane"); #[ignore] fn wasm_simd_store64_lane("simd_store64_lane"); fn wasm_simd_store8_lane("simd_store8_lane"); From 1e7c6353671d391d04b991542d8c6794a9c3f906 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 14:48:53 +0100 Subject: [PATCH 10/12] implement v128.store{32,64}_lane translation partially --- .../wasmi/src/engine/translator/simd/visit.rs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index a8fda8169a..1451a2b3e6 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -161,12 +161,30 @@ impl VisitSimdOperator<'_> for FuncTranslator { ) } - fn visit_v128_store32_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_store32_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_store_lane::( + memarg, + lane, + Instruction::v128_store32_lane, + Instruction::v128_store32_lane_offset8, + Instruction::v128_store32_lane_at, + |this, memarg, ptr, lane, v128| { + todo!() + }, + ) } - fn visit_v128_store64_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_store64_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_store_lane::( + memarg, + lane, + Instruction::v128_store64_lane, + Instruction::v128_store64_lane_offset8, + Instruction::v128_store64_lane_at, + |this, memarg, ptr, lane, v128| { + todo!() + }, + ) } fn visit_v128_const(&mut self, value: wasmparser::V128) -> Self::Output { From f0a6ac6f24100fe7ed0d20afac2ebda7d94722c7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 14:49:00 +0100 Subject: [PATCH 11/12] un-ignore passing SIMD tests --- crates/wast/tests/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wast/tests/mod.rs b/crates/wast/tests/mod.rs index 7475610504..ddd25aa7b5 100644 --- a/crates/wast/tests/mod.rs +++ b/crates/wast/tests/mod.rs @@ -251,8 +251,8 @@ macro_rules! expand_tests { fn wasm_simd_splat("simd_splat"); fn wasm_simd_store("simd_store"); fn wasm_simd_store16_lane("simd_store16_lane"); - #[ignore] fn wasm_simd_store32_lane("simd_store32_lane"); - #[ignore] fn wasm_simd_store64_lane("simd_store64_lane"); + fn wasm_simd_store32_lane("simd_store32_lane"); + fn wasm_simd_store64_lane("simd_store64_lane"); fn wasm_simd_store8_lane("simd_store8_lane"); } }; From 5677a43c91c37716153d9ec7fdf20f9eefa9e4c6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 15:01:28 +0100 Subject: [PATCH 12/12] fully implement v128.storeN_lane translation --- .../wasmi/src/engine/translator/simd/visit.rs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 1451a2b3e6..fdbb2cae26 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -169,7 +169,18 @@ impl VisitSimdOperator<'_> for FuncTranslator { Instruction::v128_store32_lane_offset8, Instruction::v128_store32_lane_at, |this, memarg, ptr, lane, v128| { - todo!() + let value = simd::i32x4_extract_lane(v128, lane); + this.translate_istore_wrap_impl::( + memarg, + ptr, + Provider::Const(value.into()), + Instruction::store32, + Instruction::i32_store_imm16, + Instruction::store32_offset16, + Instruction::i32_store_offset16_imm16, + Instruction::store32_at, + Instruction::i32_store_at_imm16, + ) }, ) } @@ -182,7 +193,18 @@ impl VisitSimdOperator<'_> for FuncTranslator { Instruction::v128_store64_lane_offset8, Instruction::v128_store64_lane_at, |this, memarg, ptr, lane, v128| { - todo!() + let value = simd::i64x2_extract_lane(v128, lane); + this.translate_istore_wrap_impl::( + memarg, + ptr, + Provider::Const(value.into()), + Instruction::store64, + Instruction::i64_store_imm16, + Instruction::store64_offset16, + Instruction::i64_store_offset16_imm16, + Instruction::store64_at, + Instruction::i64_store_at_imm16, + ) }, ) }