Skip to content
Merged
28 changes: 28 additions & 0 deletions crates/ir/src/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,34 @@
Err(self)
}

/// Creates a new [`Instruction::RegisterAndImm32`] from the given `reg` and `offset_hi`.
pub fn register_and_lane<LaneType>(reg: impl Into<Reg>, lane: LaneType) -> Self
where
LaneType: Into<u8>,
{
Self::register_and_imm32(reg, u32::from(lane.into()))
}

/// 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_lane<LaneType>(self) -> Result<(Reg, LaneType), Self>
where
LaneType: TryFrom<u8>,
{
if let Instruction::RegisterAndImm32 { reg, imm } = self {
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)

Check warning on line 267 in crates/ir/src/enum.rs

View check run for this annotation

Codecov / codecov/patch

crates/ir/src/enum.rs#L267

Added line #L267 was not covered by tests
};
return Ok((reg, lane));
}
Err(self)

Check warning on line 271 in crates/ir/src/enum.rs

View check run for this annotation

Codecov / codecov/patch

crates/ir/src/enum.rs#L271

Added line #L271 was not covered by tests
}

/// Creates a new [`Instruction::Imm16AndImm32`] from the given `value` and `offset_hi`.
pub fn imm16_and_offset_hi(value: impl Into<AnyConst16>, offset_hi: Offset64Hi) -> Self {
Self::imm16_and_imm32(value, offset_hi.0)
Expand Down
16 changes: 8 additions & 8 deletions crates/ir/src/for_each_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down Expand Up @@ -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
///
Expand Down Expand Up @@ -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
///
Expand Down Expand Up @@ -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
///
Expand Down
49 changes: 41 additions & 8 deletions crates/wasmi/src/engine/executor/instrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2242,21 +2242,37 @@
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:?}"),
}
}
Expand Down Expand Up @@ -2651,6 +2667,23 @@
},
}
}

/// Fetches the [`Reg`] and [`Offset64Hi`] parameters for a load or store [`Instruction`].
unsafe fn fetch_reg_and_lane<LaneType>(&self, delta: usize) -> (Reg, LaneType)
where
LaneType: TryFrom<u8>,
{
let mut addr: InstructionPtr = self.ip;
addr.add(delta);
match addr.get().filter_register_and_lane::<LaneType>() {
Ok(value) => value,
Err(instr) => unsafe {
unreachable_unchecked!(

Check warning on line 2681 in crates/wasmi/src/engine/executor/instrs.rs

View check run for this annotation

Codecov / codecov/patch

crates/wasmi/src/engine/executor/instrs.rs#L2681

Added line #L2681 was not covered by tests
"expected an `Instruction::RegisterAndImm32` but found: {instr:?}"
)
},
}
}
}

impl Executor<'_> {
Expand Down
38 changes: 0 additions & 38 deletions crates/wasmi/src/engine/executor/instrs/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
99 changes: 99 additions & 0 deletions crates/wasmi/src/engine/executor/instrs/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,105 @@ impl Executor<'_> {
}
}

type V128LoadLane<LaneType> =
fn(memory: &[u8], ptr: u64, offset: u64, x: V128, lane: LaneType) -> Result<V128, TrapCode>;

type V128LoadLaneAt<LaneType> =
fn(memory: &[u8], address: usize, x: V128, lane: LaneType) -> Result<V128, TrapCode>;

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<LaneType>(
&mut self,
store: &StoreInner,
result: Reg,
offset_lo: Offset64Lo,
load: V128LoadLane<LaneType>,
) -> Result<(), Error>
where
LaneType: TryFrom<u8> + Into<u8> + Copy,
{
let (ptr, offset_hi) = self.fetch_value_and_offset_hi();
let (v128, lane) = self.fetch_value_and_lane::<LaneType>(2);
let memory = self.fetch_optional_memory(3);
let offset = Offset64::combine(offset_hi, offset_lo);
let ptr = self.get_register_as::<u64>(ptr);
let v128 = self.get_register_as::<V128>(v128);
let memory = self.fetch_memory_bytes(memory, store);
let loaded = load(memory, ptr, u64::from(offset), v128, lane)?;
self.set_register_as::<V128>(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<LaneType>(
&mut self,
store: &StoreInner,
result: Reg,
address: Address32,
load_at: V128LoadLaneAt<LaneType>,
) -> Result<(), Error>
where
LaneType: TryFrom<u8> + Into<u8> + Copy,
{
let (v128, lane) = self.fetch_value_and_lane::<LaneType>(1);
let memory = self.fetch_optional_memory(2);
let v128 = self.get_register_as::<V128>(v128);
let memory = self.fetch_memory_bytes(memory, store);
let loaded = load_at(memory, usize::from(address), v128, lane)?;
self.set_register_as::<V128>(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) ),* $(,)?
Expand Down
47 changes: 47 additions & 0 deletions crates/wasmi/src/engine/executor/instrs/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<LaneType>(&self, delta: usize) -> (Reg, LaneType)
where
LaneType: TryFrom<u8>,
{
// Safety: Wasmi translation guarantees that `Instruction::RegisterAndImm32` exists.
unsafe { self.fetch_reg_and_lane::<LaneType>(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] {
Expand Down
6 changes: 6 additions & 0 deletions crates/wasmi/src/engine/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading