From 47b52a1633bce955381e07c01545eb9e13174fdd Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:06:39 +0100 Subject: [PATCH 01/12] add FuncTranslator::append_instr convenience method Also use it in SIMD translation code. --- crates/wasmi/src/engine/translator/mod.rs | 6 ++++++ crates/wasmi/src/engine/translator/simd/mod.rs | 10 ++++------ crates/wasmi/src/engine/translator/simd/visit.rs | 8 ++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/wasmi/src/engine/translator/mod.rs b/crates/wasmi/src/engine/translator/mod.rs index c3df4dbde8..fe82341b89 100644 --- a/crates/wasmi/src/engine/translator/mod.rs +++ b/crates/wasmi/src/engine/translator/mod.rs @@ -933,6 +933,12 @@ impl FuncTranslator { self.alloc.instr_encoder.push_instr(instr) } + /// Convenience method for appending an [`Instruction`] parameter. + fn append_instr(&mut self, instr: Instruction) -> Result<(), Error> { + self.alloc.instr_encoder.append_instr(instr)?; + Ok(()) + } + /// Utility function for pushing a new [`Instruction`] with basic fuel costs. /// /// # Note diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index fb753e3616..df8700aa55 100644 --- a/crates/wasmi/src/engine/translator/simd/mod.rs +++ b/crates/wasmi/src/engine/translator/simd/mod.rs @@ -225,7 +225,7 @@ impl FuncTranslator { }; self.push_fueled_instr(instr, FuelCosts::base)?; if let Some(param) = param { - self.alloc.instr_encoder.append_instr(param)?; + self.append_instr(param)?; } Ok(()) } @@ -297,9 +297,9 @@ impl FuncTranslator { 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)?; + self.append_instr(param)?; if !memory.is_default() { - self.alloc.instr_encoder.append_instr(memidx)?; + self.append_instr(memidx)?; } Ok(()) } @@ -339,9 +339,7 @@ impl FuncTranslator { make_instr_at: fn(value: Reg, address: Address32) -> Instruction, ) -> Result<(), Error> { self.push_fueled_instr(make_instr_at(value, address), FuelCosts::store)?; - self.alloc - .instr_encoder - .append_instr(Instruction::lane_and_memory_index(lane, memory))?; + self.append_instr(Instruction::lane_and_memory_index(lane, memory))?; Ok(()) } diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 4fd30fd4b5..73ea1cc9e6 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -300,9 +300,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { Instruction::i8x16_shuffle(result, lhs, rhs), FuelCosts::base, )?; - self.alloc - .instr_encoder - .append_instr(Instruction::register(selector))?; + self.append_instr(Instruction::register(selector))?; Ok(()) } @@ -727,9 +725,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { Instruction::v128_bitselect(result, lhs, rhs), FuelCosts::base, )?; - self.alloc - .instr_encoder - .append_instr(Instruction::register(selector))?; + self.append_instr(Instruction::register(selector))?; Ok(()) } From fb8901ba64a4ff655d645fcc60e9faff0e81e248 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:07:01 +0100 Subject: [PATCH 02/12] update docs for some v128.loadN_lane ops --- crates/ir/src/for_each_op.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/ir/src/for_each_op.rs b/crates/ir/src/for_each_op.rs index 82ccd4b7cd..cff35ca920 100644 --- a/crates/ir/src/for_each_op.rs +++ b/crates/ir/src/for_each_op.rs @@ -8480,8 +8480,8 @@ macro_rules! for_each_op_grouped { /// Followed by /// /// 1. [`Instruction::RegisterAndImm32`] encoding `ptr` and `offset_hi`. - /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane_index`. - /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory_index` used. + /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane` index. + /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory` index used. /// /// # Note /// @@ -8518,8 +8518,8 @@ macro_rules! for_each_op_grouped { /// Followed by /// /// 1. [`Instruction::RegisterAndImm32`] encoding `ptr` and `offset_hi`. - /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane_index`. - /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory_index` used. + /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane` index. + /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory` index used. /// /// # Note /// @@ -8556,8 +8556,8 @@ macro_rules! for_each_op_grouped { /// Followed by /// /// 1. [`Instruction::RegisterAndImm32`] encoding `ptr` and `offset_hi`. - /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane_index`. - /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory_index` used. + /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane` index. + /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory` index used. /// /// # Note /// @@ -8594,8 +8594,8 @@ macro_rules! for_each_op_grouped { /// Followed by /// /// 1. [`Instruction::RegisterAndImm32`] encoding `ptr` and `offset_hi`. - /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane_index`. - /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory_index` used. + /// 2. [`Instruction::RegisterAndImm32`] encoding `input` and `lane` index. + /// 3. Optional [`Instruction::MemoryIndex`] encoding the `memory` index used. /// /// # Note /// From ec95a01ac6da592623f120aebf4f31214dfa3f92 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:07:14 +0100 Subject: [PATCH 03/12] add Instruction::register_and_lane convenience constructor --- crates/ir/src/enum.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/ir/src/enum.rs b/crates/ir/src/enum.rs index 21319c8a4f..84b82755e2 100644 --- a/crates/ir/src/enum.rs +++ b/crates/ir/src/enum.rs @@ -230,6 +230,14 @@ impl Instruction { Self::register_and_imm32(reg, offset_hi.0) } + /// Creates a new [`Instruction::RegisterAndImm32`] from the given `reg` and `offset_hi`. + pub fn register_and_lane(reg: impl Into, lane: LaneType) -> Self + where + LaneType: Into, + { + Self::register_and_imm32(reg, u32::from(lane.into())) + } + /// Returns `Some` [`Reg`] and [`Offset64Hi`] if encoded properly. /// /// # Errors From 093e77f8872fe9f6a3760b6dafb3b5521668caa2 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:08:09 +0100 Subject: [PATCH 04/12] add translation for v128.load8_lane instruction --- .../wasmi/src/engine/translator/simd/mod.rs | 58 ++++++++++++++++++- .../wasmi/src/engine/translator/simd/visit.rs | 4 +- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index df8700aa55..a7e1b4a16e 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, TrapCode, TypedVal, V128}, + core::{simd, simd::ImmLaneIdx16, TrapCode, TypedVal, V128}, engine::{translator::Provider, FuelCosts}, ir::{ index, @@ -364,4 +364,60 @@ impl FuncTranslator { )?; Ok(Some(instr)) } + + fn translate_v128_load8_lane(&mut self, memarg: MemArg, lane: u8) -> Result<(), Error> { + bail_unreachable!(self); + let (memory, offset) = Self::decode_memarg(memarg); + let Ok(lane) = ImmLaneIdx16::try_from(lane) else { + panic!("encountered out of bounds lane: {lane}"); + }; + let (ptr, x) = self.alloc.stack.pop2(); + let x = self.alloc.stack.provider2reg(&x)?; + 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_load8_lane_at(memory, x, lane, address); + } + let zero_ptr = self.alloc.stack.alloc_const(0_u64)?; + (zero_ptr, u64::from(address)) + } + }; + let (offset_hi, offset_lo) = Offset64::split(offset); + let result = self.alloc.stack.push_dynamic()?; + self.push_fueled_instr( + Instruction::v128_load8_lane(result, offset_lo), + FuelCosts::store, + )?; + self.append_instr(Instruction::register_and_offset_hi(ptr, offset_hi))?; + self.append_instr(Instruction::register_and_lane(x, lane))?; + if !memory.is_default() { + self.append_instr(Instruction::memory_index(memory))?; + } + Ok(()) + } + + fn translate_v128_load8_lane_at( + &mut self, + memory: Memory, + x: Reg, + lane: LaneType, + address: Address32, + ) -> Result<(), Error> + where + LaneType: Into, + { + let result = self.alloc.stack.push_dynamic()?; + let instr = Instruction::v128_load8_lane_at(result, address); + let param = Instruction::register_and_lane(x, lane); + self.push_fueled_instr(instr, FuelCosts::base)?; + self.append_instr(param)?; + if !memory.is_default() { + self.append_instr(Instruction::memory_index(memory))?; + } + Ok(()) + } } diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 73ea1cc9e6..d444c263b4 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -163,8 +163,8 @@ impl VisitSimdOperator<'_> for FuncTranslator { ) } - fn visit_v128_load8_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_load8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_load8_lane(memarg, lane) } fn visit_v128_load16_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { From ff2c1aedcb78ddf7a66f500fe017b4ebfbb9d20b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:11:49 +0100 Subject: [PATCH 05/12] make v128.loadN_lane translation generic --- .../wasmi/src/engine/translator/simd/mod.rs | 28 +++++++++++++------ .../wasmi/src/engine/translator/simd/visit.rs | 7 ++++- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index a7e1b4a16e..5cd8512d09 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, @@ -365,10 +365,16 @@ impl FuncTranslator { Ok(Some(instr)) } - fn translate_v128_load8_lane(&mut self, memarg: MemArg, lane: u8) -> Result<(), Error> { + fn translate_v128_load8_lane( + &mut self, + memarg: MemArg, + lane: u8, + make_instr: fn(result: Reg, offset_lo: Offset64Lo) -> Instruction, + make_instr_at: fn(result: Reg, address: Address32) -> Instruction, + ) -> Result<(), Error> { bail_unreachable!(self); let (memory, offset) = Self::decode_memarg(memarg); - let Ok(lane) = ImmLaneIdx16::try_from(lane) else { + let Ok(lane) = T::LaneType::try_from(lane) else { panic!("encountered out of bounds lane: {lane}"); }; let (ptr, x) = self.alloc.stack.pop2(); @@ -380,7 +386,13 @@ impl FuncTranslator { return self.translate_trap(TrapCode::MemoryOutOfBounds); }; if let Ok(address) = Address32::try_from(address) { - return self.translate_v128_load8_lane_at(memory, x, lane, address); + return self.translate_v128_load8_lane_at( + memory, + x, + lane, + address, + make_instr_at, + ); } let zero_ptr = self.alloc.stack.alloc_const(0_u64)?; (zero_ptr, u64::from(address)) @@ -388,10 +400,7 @@ impl FuncTranslator { }; let (offset_hi, offset_lo) = Offset64::split(offset); let result = self.alloc.stack.push_dynamic()?; - self.push_fueled_instr( - Instruction::v128_load8_lane(result, offset_lo), - FuelCosts::store, - )?; + self.push_fueled_instr(make_instr(result, offset_lo), FuelCosts::store)?; self.append_instr(Instruction::register_and_offset_hi(ptr, offset_hi))?; self.append_instr(Instruction::register_and_lane(x, lane))?; if !memory.is_default() { @@ -406,12 +415,13 @@ impl FuncTranslator { x: Reg, lane: LaneType, address: Address32, + make_instr_at: fn(result: Reg, address: Address32) -> Instruction, ) -> Result<(), Error> where LaneType: Into, { let result = self.alloc.stack.push_dynamic()?; - let instr = Instruction::v128_load8_lane_at(result, address); + let instr = make_instr_at(result, address); let param = Instruction::register_and_lane(x, lane); self.push_fueled_instr(instr, FuelCosts::base)?; self.append_instr(param)?; diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index d444c263b4..b6cc3575f7 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -164,7 +164,12 @@ impl VisitSimdOperator<'_> for FuncTranslator { } fn visit_v128_load8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { - self.translate_v128_load8_lane(memarg, lane) + self.translate_v128_load8_lane::( + memarg, + lane, + Instruction::v128_load8_lane, + Instruction::v128_load8_lane_at, + ) } fn visit_v128_load16_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { From 8cc5a5cd32a18893c1c92a5ceb1e4581f2353a6b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:13:14 +0100 Subject: [PATCH 06/12] rename generic translation methods --- crates/wasmi/src/engine/translator/simd/mod.rs | 6 +++--- crates/wasmi/src/engine/translator/simd/visit.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/mod.rs b/crates/wasmi/src/engine/translator/simd/mod.rs index 5cd8512d09..c53118a521 100644 --- a/crates/wasmi/src/engine/translator/simd/mod.rs +++ b/crates/wasmi/src/engine/translator/simd/mod.rs @@ -365,7 +365,7 @@ impl FuncTranslator { Ok(Some(instr)) } - fn translate_v128_load8_lane( + fn translate_v128_load_lane( &mut self, memarg: MemArg, lane: u8, @@ -386,7 +386,7 @@ impl FuncTranslator { return self.translate_trap(TrapCode::MemoryOutOfBounds); }; if let Ok(address) = Address32::try_from(address) { - return self.translate_v128_load8_lane_at( + return self.translate_v128_load_lane_at( memory, x, lane, @@ -409,7 +409,7 @@ impl FuncTranslator { Ok(()) } - fn translate_v128_load8_lane_at( + fn translate_v128_load_lane_at( &mut self, memory: Memory, x: Reg, diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index b6cc3575f7..14a262aadd 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -164,7 +164,7 @@ impl VisitSimdOperator<'_> for FuncTranslator { } fn visit_v128_load8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { - self.translate_v128_load8_lane::( + self.translate_v128_load_lane::( memarg, lane, Instruction::v128_load8_lane, From 55a0d33067e2d4c402c92e3a72aac6b4831e5922 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 19:13:35 +0100 Subject: [PATCH 07/12] translate remaining v128.loadN_lane ops --- .../wasmi/src/engine/translator/simd/visit.rs | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/crates/wasmi/src/engine/translator/simd/visit.rs b/crates/wasmi/src/engine/translator/simd/visit.rs index 14a262aadd..9bbc5721b0 100644 --- a/crates/wasmi/src/engine/translator/simd/visit.rs +++ b/crates/wasmi/src/engine/translator/simd/visit.rs @@ -172,16 +172,31 @@ impl VisitSimdOperator<'_> for FuncTranslator { ) } - fn visit_v128_load16_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_load16_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_load_lane::( + memarg, + lane, + Instruction::v128_load16_lane, + Instruction::v128_load16_lane_at, + ) } - fn visit_v128_load32_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_load32_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_load_lane::( + memarg, + lane, + Instruction::v128_load32_lane, + Instruction::v128_load32_lane_at, + ) } - fn visit_v128_load64_lane(&mut self, _memarg: MemArg, _lane: u8) -> Self::Output { - todo!() + fn visit_v128_load64_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { + self.translate_v128_load_lane::( + memarg, + lane, + Instruction::v128_load64_lane, + Instruction::v128_load64_lane_at, + ) } fn visit_v128_store8_lane(&mut self, memarg: MemArg, lane: u8) -> Self::Output { From 0f87826d38aff6852f9cfc19f15962ea5f940dd0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 20:30:38 +0100 Subject: [PATCH 08/12] add convenience Instruction methods --- crates/ir/src/enum.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/ir/src/enum.rs b/crates/ir/src/enum.rs index 84b82755e2..5f33be151a 100644 --- a/crates/ir/src/enum.rs +++ b/crates/ir/src/enum.rs @@ -230,6 +230,19 @@ impl Instruction { Self::register_and_imm32(reg, offset_hi.0) } + /// Returns `Some` [`Reg`] and [`Offset64Hi`] 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_register_and_offset_hi(self) -> Result<(Reg, Offset64Hi), Self> { + if let Instruction::RegisterAndImm32 { reg, imm } = self { + return Ok((reg, Offset64Hi(u32::from(imm)))); + } + Err(self) + } + /// Creates a new [`Instruction::RegisterAndImm32`] from the given `reg` and `offset_hi`. pub fn register_and_lane(reg: impl Into, lane: LaneType) -> Self where @@ -238,15 +251,22 @@ impl Instruction { Self::register_and_imm32(reg, u32::from(lane.into())) } - /// Returns `Some` [`Reg`] and [`Offset64Hi`] if encoded properly. + /// Returns `Some` [`Reg`] and a `lane` index 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_register_and_offset_hi(self) -> Result<(Reg, Offset64Hi), Self> { + pub fn filter_register_and_lane(self) -> Result<(Reg, LaneType), Self> + where + LaneType: TryFrom, + { if let Instruction::RegisterAndImm32 { reg, imm } = self { - return Ok((reg, Offset64Hi(u32::from(imm)))); + let lane_index = u32::from(imm) as u8; + let Ok(lane) = LaneType::try_from(lane_index) else { + panic!("encountered out of bounds lane index: {}", lane_index) + }; + return Ok((reg, lane)); } Err(self) } From f4c80ee7790b756feb90d4941adabd8e52d061c9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 20:31:18 +0100 Subject: [PATCH 09/12] move some load utils to utils submodule --- .../wasmi/src/engine/executor/instrs/load.rs | 38 --------------- .../wasmi/src/engine/executor/instrs/utils.rs | 47 +++++++++++++++++++ 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/crates/wasmi/src/engine/executor/instrs/load.rs b/crates/wasmi/src/engine/executor/instrs/load.rs index 78c46691b4..4d24576c21 100644 --- a/crates/wasmi/src/engine/executor/instrs/load.rs +++ b/crates/wasmi/src/engine/executor/instrs/load.rs @@ -25,44 +25,6 @@ impl Executor<'_> { unsafe { self.fetch_reg_and_offset_hi() } } - /// Fetches the bytes of the default memory at index 0. - fn fetch_default_memory_bytes(&self) -> &[u8] { - // Safety: the `self.cache.memory` pointer is always synchronized - // conservatively whenever it could have been invalidated. - unsafe { self.cache.memory.data() } - } - - /// Fetches the bytes of the given `memory`. - fn fetch_memory_bytes<'exec, 'store, 'bytes>( - &'exec self, - memory: Memory, - store: &'store StoreInner, - ) -> &'bytes [u8] - where - 'exec: 'bytes, - 'store: 'bytes, - { - match memory.is_default() { - true => self.fetch_default_memory_bytes(), - false => self.fetch_non_default_memory_bytes(memory, store), - } - } - - /// Fetches the bytes of the given non-default `memory`. - #[cold] - fn fetch_non_default_memory_bytes<'exec, 'store, 'bytes>( - &'exec self, - memory: Memory, - store: &'store StoreInner, - ) -> &'bytes [u8] - where - 'exec: 'bytes, - 'store: 'bytes, - { - let memory = self.get_memory(memory); - store.resolve_memory(&memory).data() - } - /// Executes a generic Wasm `load[N_{s|u}]` operation. /// /// # Note diff --git a/crates/wasmi/src/engine/executor/instrs/utils.rs b/crates/wasmi/src/engine/executor/instrs/utils.rs index 2f08f4ff51..69966c2c2c 100644 --- a/crates/wasmi/src/engine/executor/instrs/utils.rs +++ b/crates/wasmi/src/engine/executor/instrs/utils.rs @@ -36,6 +36,53 @@ impl Executor<'_> { unsafe { self.fetch_reg_and_offset_hi() } } + /// Returns the register `value` and `lane` parameters for a `load` [`Instruction`]. + pub fn fetch_value_and_lane(&self, delta: usize) -> (Reg, LaneType) + where + LaneType: TryFrom, + { + // Safety: Wasmi translation guarantees that `Instruction::RegisterAndImm32` exists. + unsafe { self.fetch_reg_and_lane::(delta) } + } + + /// Fetches the bytes of the default memory at index 0. + pub fn fetch_default_memory_bytes(&self) -> &[u8] { + // Safety: the `self.cache.memory` pointer is always synchronized + // conservatively whenever it could have been invalidated. + unsafe { self.cache.memory.data() } + } + + /// Fetches the bytes of the given `memory`. + pub fn fetch_memory_bytes<'exec, 'store, 'bytes>( + &'exec self, + memory: Memory, + store: &'store StoreInner, + ) -> &'bytes [u8] + where + 'exec: 'bytes, + 'store: 'bytes, + { + match memory.is_default() { + true => self.fetch_default_memory_bytes(), + false => self.fetch_non_default_memory_bytes(memory, store), + } + } + + /// Fetches the bytes of the given non-default `memory`. + #[cold] + pub fn fetch_non_default_memory_bytes<'exec, 'store, 'bytes>( + &'exec self, + memory: Memory, + store: &'store StoreInner, + ) -> &'bytes [u8] + where + 'exec: 'bytes, + 'store: 'bytes, + { + let memory = self.get_memory(memory); + store.resolve_memory(&memory).data() + } + /// Fetches the bytes of the default memory at index 0. #[inline] pub fn fetch_default_memory_bytes_mut(&mut self) -> &mut [u8] { From 2b55134ac87044214d9caaaeed049b9310fae067 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 20:31:35 +0100 Subject: [PATCH 10/12] implement v128.loadN_lane execution --- crates/wasmi/src/engine/executor/instrs.rs | 49 +++++++-- .../wasmi/src/engine/executor/instrs/simd.rs | 99 +++++++++++++++++++ 2 files changed, 140 insertions(+), 8 deletions(-) diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index cf3b4b8714..cdc61200a8 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -2242,21 +2242,37 @@ impl<'engine> Executor<'engine> { offset, } => self.execute_v128_load32x2_u_offset16(result, ptr, offset)?, #[cfg(feature = "simd")] - Instr::V128Load8Lane { result, offset_lo } => todo!(), + Instr::V128Load8Lane { result, offset_lo } => { + self.execute_v128_load8_lane(&store.inner, result, offset_lo)? + } #[cfg(feature = "simd")] - Instr::V128Load8LaneAt { result, address } => todo!(), + Instr::V128Load8LaneAt { result, address } => { + self.execute_v128_load8_lane_at(&store.inner, result, address)? + } #[cfg(feature = "simd")] - Instr::V128Load16Lane { result, offset_lo } => todo!(), + Instr::V128Load16Lane { result, offset_lo } => { + self.execute_v128_load16_lane(&store.inner, result, offset_lo)? + } #[cfg(feature = "simd")] - Instr::V128Load16LaneAt { result, address } => todo!(), + Instr::V128Load16LaneAt { result, address } => { + self.execute_v128_load16_lane_at(&store.inner, result, address)? + } #[cfg(feature = "simd")] - Instr::V128Load32Lane { result, offset_lo } => todo!(), + Instr::V128Load32Lane { result, offset_lo } => { + self.execute_v128_load32_lane(&store.inner, result, offset_lo)? + } #[cfg(feature = "simd")] - Instr::V128Load32LaneAt { result, address } => todo!(), + Instr::V128Load32LaneAt { result, address } => { + self.execute_v128_load32_lane_at(&store.inner, result, address)? + } #[cfg(feature = "simd")] - Instr::V128Load64Lane { result, offset_lo } => todo!(), + Instr::V128Load64Lane { result, offset_lo } => { + self.execute_v128_load64_lane(&store.inner, result, offset_lo)? + } #[cfg(feature = "simd")] - Instr::V128Load64LaneAt { result, address } => todo!(), + Instr::V128Load64LaneAt { result, address } => { + self.execute_v128_load64_lane_at(&store.inner, result, address)? + } unsupported => panic!("encountered unsupported Wasmi instruction: {unsupported:?}"), } } @@ -2651,6 +2667,23 @@ impl Executor<'_> { }, } } + + /// Fetches the [`Reg`] and [`Offset64Hi`] parameters for a load or store [`Instruction`]. + unsafe fn fetch_reg_and_lane(&self, delta: usize) -> (Reg, LaneType) + where + LaneType: TryFrom, + { + let mut addr: InstructionPtr = self.ip; + addr.add(delta); + match addr.get().filter_register_and_lane::() { + Ok(value) => value, + Err(instr) => unsafe { + unreachable_unchecked!( + "expected an `Instruction::RegisterAndImm32` but found: {instr:?}" + ) + }, + } + } } impl Executor<'_> { diff --git a/crates/wasmi/src/engine/executor/instrs/simd.rs b/crates/wasmi/src/engine/executor/instrs/simd.rs index 081836ac66..e403800408 100644 --- a/crates/wasmi/src/engine/executor/instrs/simd.rs +++ b/crates/wasmi/src/engine/executor/instrs/simd.rs @@ -529,6 +529,105 @@ impl Executor<'_> { } } +type V128LoadLane = + fn(memory: &[u8], ptr: u64, offset: u64, x: V128, lane: LaneType) -> Result; + +type V128LoadLaneAt = + fn(memory: &[u8], address: usize, x: V128, lane: LaneType) -> Result; + +macro_rules! impl_execute_v128_load_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: &StoreInner, + result: Reg, + offset_lo: Offset64Lo, + ) -> Result<(), Error> { + self.execute_v128_load_lane_impl::<$lane_ty>(store, result, offset_lo, $eval) + } + )* + }; +} + +macro_rules! impl_execute_v128_load_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: &StoreInner, + result: Reg, + address: Address32, + ) -> Result<(), Error> { + self.execute_v128_load_lane_at_impl::<$lane_ty>(store, result, address, $eval) + } + )* + }; +} + +impl Executor<'_> { + fn execute_v128_load_lane_impl( + &mut self, + store: &StoreInner, + result: Reg, + offset_lo: Offset64Lo, + load: V128LoadLane, + ) -> Result<(), Error> + where + LaneType: TryFrom + Into + Copy, + { + let (ptr, offset_hi) = self.fetch_value_and_offset_hi(); + let (v128, lane) = self.fetch_value_and_lane::(2); + let memory = self.fetch_optional_memory(3); + let offset = Offset64::combine(offset_hi, offset_lo); + let ptr = self.get_register_as::(ptr); + let v128 = self.get_register_as::(v128); + let memory = self.fetch_memory_bytes(memory, store); + let loaded = load(memory, ptr, u64::from(offset), v128, lane)?; + self.set_register_as::(result, loaded); + self.try_next_instr_at(3) + } + + impl_execute_v128_load_lane! { + (Instruction::V128Load8Lane, ImmLaneIdx16, execute_v128_load8_lane, simd::v128_load8_lane), + (Instruction::V128Load16Lane, ImmLaneIdx8, execute_v128_load16_lane, simd::v128_load16_lane), + (Instruction::V128Load32Lane, ImmLaneIdx4, execute_v128_load32_lane, simd::v128_load32_lane), + (Instruction::V128Load64Lane, ImmLaneIdx2, execute_v128_load64_lane, simd::v128_load64_lane), + } + + fn execute_v128_load_lane_at_impl( + &mut self, + store: &StoreInner, + result: Reg, + address: Address32, + load_at: V128LoadLaneAt, + ) -> Result<(), Error> + where + LaneType: TryFrom + Into + Copy, + { + let (v128, lane) = self.fetch_value_and_lane::(1); + let memory = self.fetch_optional_memory(2); + let v128 = self.get_register_as::(v128); + let memory = self.fetch_memory_bytes(memory, store); + let loaded = load_at(memory, usize::from(address), v128, lane)?; + self.set_register_as::(result, loaded); + self.try_next_instr_at(2) + } + + impl_execute_v128_load_lane_at! { + (Instruction::V128Load8LaneAt, ImmLaneIdx16, execute_v128_load8_lane_at, simd::v128_load8_lane_at), + (Instruction::V128Load16LaneAt, ImmLaneIdx8, execute_v128_load16_lane_at, simd::v128_load16_lane_at), + (Instruction::V128Load32LaneAt, ImmLaneIdx4, execute_v128_load32_lane_at, simd::v128_load32_lane_at), + (Instruction::V128Load64LaneAt, ImmLaneIdx2, execute_v128_load64_lane_at, simd::v128_load64_lane_at), + } +} + macro_rules! impl_execute_v128_store_lane { ( $( (Instruction::$op:ident, $lane_ty:ty, $exec:ident, $eval:expr) ),* $(,)? From 6d4fb175945f1c09ee42317213bbde2352842cf0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 20:31:45 +0100 Subject: [PATCH 11/12] un-ignore passing SIMD tests --- crates/wast/tests/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/wast/tests/mod.rs b/crates/wast/tests/mod.rs index 02276aaefa..8ab5c36aec 100644 --- a/crates/wast/tests/mod.rs +++ b/crates/wast/tests/mod.rs @@ -241,10 +241,10 @@ macro_rules! expand_tests { fn wasm_simd_lane("simd_lane"); fn wasm_simd_linking("simd_linking"); fn wasm_simd_load("simd_load"); - #[ignore] fn wasm_simd_load16_lane("simd_load16_lane"); - #[ignore] fn wasm_simd_load32_lane("simd_load32_lane"); - #[ignore] fn wasm_simd_load64_lane("simd_load64_lane"); - #[ignore] fn wasm_simd_load8_lane("simd_load8_lane"); + fn wasm_simd_load16_lane("simd_load16_lane"); + fn wasm_simd_load32_lane("simd_load32_lane"); + fn wasm_simd_load64_lane("simd_load64_lane"); + fn wasm_simd_load8_lane("simd_load8_lane"); fn wasm_simd_load_splat("simd_load_splat"); fn wasm_simd_load_extend("simd_load_extend"); fn wasm_simd_load_zero("simd_load_zero"); From 0aa038ac730ee7db3e0f86804812080346888149 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 26 Mar 2025 20:34:02 +0100 Subject: [PATCH 12/12] un-ignore passing SIMD+multi-memory 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 8ab5c36aec..6d7e5bab90 100644 --- a/crates/wast/tests/mod.rs +++ b/crates/wast/tests/mod.rs @@ -329,7 +329,7 @@ macro_rules! expand_tests_mm { fn wasm_multi_memory_memory_trap1("proposals/multi-memory/memory_trap1"); fn wasm_multi_memory_memory_multi("proposals/multi-memory/memory-multi"); fn wasm_multi_memory_memory("proposals/multi-memory/memory"); - #[ignore] fn wasm_multi_memory_simd_memory("proposals/multi-memory/simd_memory-multi"); + fn wasm_multi_memory_simd_memory("proposals/multi-memory/simd_memory-multi"); fn wasm_multi_memory_start0("proposals/multi-memory/start0"); fn wasm_multi_memory_store("proposals/multi-memory/store"); fn wasm_multi_memory_store0("proposals/multi-memory/store0");