Skip to content

Commit 73708c2

Browse files
authored
Implement SIMD replace_lane instruction translation and execution (#1424)
* fix definition of Instruction::F64x2ReplaceLaneImm32 * implement translation of SIMD replace lane instructions * implement SIMD replace lane instruction execution * un-ignore passing SIMD test
1 parent 3ea85a9 commit 73708c2

File tree

6 files changed

+291
-35
lines changed

6 files changed

+291
-35
lines changed

crates/ir/src/for_each_op.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6081,12 +6081,12 @@ macro_rules! for_each_op_grouped {
60816081
///
60826082
/// Followed by [`Instruction::F64Const32`] encoding the 32-bit immediate `value`.
60836083
#[snake_name(f64x2_replace_lane_imm32)]
6084-
F32x4ReplaceLaneImm32 {
6084+
F64x2ReplaceLaneImm32 {
60856085
@result: Reg,
60866086
/// The input [`V128`] that gets a value replaced.
60876087
input: Reg,
60886088
/// The lane of the replaced value.
6089-
lane: ImmLaneIdx4,
6089+
lane: ImmLaneIdx2,
60906090
},
60916091

60926092
/// Wasm `i8x16.shuffle` instruction.

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,74 +1379,74 @@ impl<'engine> Executor<'engine> {
13791379
result,
13801380
input,
13811381
lane,
1382-
} => todo!(),
1382+
} => self.execute_i8x16_replace_lane(result, input, lane),
13831383
#[cfg(feature = "simd")]
13841384
Instr::I8x16ReplaceLaneImm {
13851385
result,
13861386
input,
13871387
lane,
13881388
value,
1389-
} => todo!(),
1389+
} => self.execute_i8x16_replace_lane_imm(result, input, lane, value),
13901390
#[cfg(feature = "simd")]
13911391
Instr::I16x8ReplaceLane {
13921392
result,
13931393
input,
13941394
lane,
1395-
} => todo!(),
1395+
} => self.execute_i16x8_replace_lane(result, input, lane),
13961396
#[cfg(feature = "simd")]
13971397
Instr::I16x8ReplaceLaneImm {
13981398
result,
13991399
input,
14001400
lane,
1401-
} => todo!(),
1401+
} => self.execute_i16x8_replace_lane_imm(result, input, lane),
14021402
#[cfg(feature = "simd")]
14031403
Instr::I32x4ReplaceLane {
14041404
result,
14051405
input,
14061406
lane,
1407-
} => todo!(),
1407+
} => self.execute_i32x4_replace_lane(result, input, lane),
14081408
#[cfg(feature = "simd")]
14091409
Instr::I32x4ReplaceLaneImm {
14101410
result,
14111411
input,
14121412
lane,
1413-
} => todo!(),
1413+
} => self.execute_i32x4_replace_lane_imm(result, input, lane),
14141414
#[cfg(feature = "simd")]
14151415
Instr::I64x2ReplaceLane {
14161416
result,
14171417
input,
14181418
lane,
1419-
} => todo!(),
1419+
} => self.execute_i64x2_replace_lane(result, input, lane),
14201420
#[cfg(feature = "simd")]
14211421
Instr::I64x2ReplaceLaneImm32 {
14221422
result,
14231423
input,
14241424
lane,
1425-
} => todo!(),
1425+
} => self.execute_i64x2_replace_lane_imm32(result, input, lane),
14261426
#[cfg(feature = "simd")]
14271427
Instr::F32x4ReplaceLane {
14281428
result,
14291429
input,
14301430
lane,
1431-
} => todo!(),
1431+
} => self.execute_f32x4_replace_lane(result, input, lane),
14321432
#[cfg(feature = "simd")]
14331433
Instr::F32x4ReplaceLaneImm {
14341434
result,
14351435
input,
14361436
lane,
1437-
} => todo!(),
1437+
} => self.execute_f32x4_replace_lane_imm(result, input, lane),
14381438
#[cfg(feature = "simd")]
14391439
Instr::F64x2ReplaceLane {
14401440
result,
14411441
input,
14421442
lane,
1443-
} => todo!(),
1443+
} => self.execute_f64x2_replace_lane(result, input, lane),
14441444
#[cfg(feature = "simd")]
1445-
Instr::F32x4ReplaceLaneImm32 {
1445+
Instr::F64x2ReplaceLaneImm32 {
14461446
result,
14471447
input,
14481448
lane,
1449-
} => todo!(),
1449+
} => self.execute_f64x2_replace_lane_imm32(result, input, lane),
14501450
#[cfg(feature = "simd")]
14511451
Instr::I8x16Shuffle { result, lhs, rhs } => {
14521452
self.execute_i8x16_shuffle(result, lhs, rhs)

crates/wasmi/src/engine/executor/instrs/simd.rs

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
V128,
99
},
1010
engine::{executor::InstructionPtr, utils::unreachable_unchecked},
11-
ir::{Instruction, Reg, ShiftAmount},
11+
ir::{AnyConst32, Instruction, Reg, ShiftAmount},
1212
};
1313

1414
impl Executor<'_> {
@@ -29,6 +29,60 @@ impl Executor<'_> {
2929
}
3030
}
3131

32+
/// Fetches a [`Reg`] from an [`Instruction::Const32`] instruction parameter.
33+
fn fetch_const32_as<T>(&self) -> T
34+
where
35+
T: From<AnyConst32>,
36+
{
37+
let mut addr: InstructionPtr = self.ip;
38+
addr.add(1);
39+
match *addr.get() {
40+
Instruction::Const32 { value } => value.into(),
41+
unexpected => {
42+
// Safety: Wasmi translation guarantees that [`Instruction::Const32`] exists.
43+
unsafe {
44+
unreachable_unchecked!(
45+
"expected `Instruction::Const32` but found {unexpected:?}"
46+
)
47+
}
48+
}
49+
}
50+
}
51+
52+
/// Fetches a [`Reg`] from an [`Instruction::I64Const32`] instruction parameter.
53+
fn fetch_i64const32(&self) -> i64 {
54+
let mut addr: InstructionPtr = self.ip;
55+
addr.add(1);
56+
match *addr.get() {
57+
Instruction::I64Const32 { value } => value.into(),
58+
unexpected => {
59+
// Safety: Wasmi translation guarantees that [`Instruction::I64Const32`] exists.
60+
unsafe {
61+
unreachable_unchecked!(
62+
"expected `Instruction::I64Const32` but found {unexpected:?}"
63+
)
64+
}
65+
}
66+
}
67+
}
68+
69+
/// Fetches a [`Reg`] from an [`Instruction::F64Const32`] instruction parameter.
70+
fn fetch_f64const32(&self) -> f64 {
71+
let mut addr: InstructionPtr = self.ip;
72+
addr.add(1);
73+
match *addr.get() {
74+
Instruction::F64Const32 { value } => value.into(),
75+
unexpected => {
76+
// Safety: Wasmi translation guarantees that [`Instruction::F64Const32`] exists.
77+
unsafe {
78+
unreachable_unchecked!(
79+
"expected `Instruction::F64Const32` but found {unexpected:?}"
80+
)
81+
}
82+
}
83+
}
84+
}
85+
3286
/// Executes an [`Instruction::I8x16Shuffle`] instruction.
3387
pub fn execute_i8x16_shuffle(&mut self, result: Reg, lhs: Reg, rhs: Reg) {
3488
let selector = self.fetch_register();
@@ -60,6 +114,91 @@ impl Executor<'_> {
60114
self.set_register_as::<V128>(result, simd::v128_bitselect(lhs, rhs, selector));
61115
self.next_instr_at(2);
62116
}
117+
}
118+
119+
macro_rules! impl_replace_lane_ops {
120+
(
121+
$(
122+
($ty:ty, $lane_ty:ty, Instruction::$instr_name:ident, $exec_name:ident, $execute:expr)
123+
),* $(,)?
124+
) => {
125+
$(
126+
#[doc = concat!("Executes an [`Instruction::", stringify!($instr_name), "`].")]
127+
pub fn $exec_name(&mut self, result: Reg, input: Reg, lane: $lane_ty) {
128+
let value = self.fetch_register();
129+
let input = self.get_register_as::<V128>(input);
130+
let value = self.get_register_as::<$ty>(value);
131+
self.set_register_as::<V128>(result, $execute(input, lane, value));
132+
self.next_instr_at(2);
133+
}
134+
)*
135+
};
136+
}
137+
138+
impl Executor<'_> {
139+
impl_replace_lane_ops! {
140+
(i8, ImmLaneIdx16, Instruction::I8x16ReplaceLane, execute_i8x16_replace_lane, simd::i8x16_replace_lane),
141+
(i16, ImmLaneIdx8, Instruction::I16x8ReplaceLane, execute_i16x8_replace_lane, simd::i16x8_replace_lane),
142+
(i32, ImmLaneIdx4, Instruction::I32x4ReplaceLane, execute_i32x4_replace_lane, simd::i32x4_replace_lane),
143+
(i64, ImmLaneIdx2, Instruction::I64x2ReplaceLane, execute_i64x2_replace_lane, simd::i64x2_replace_lane),
144+
(f32, ImmLaneIdx4, Instruction::F32x4ReplaceLane, execute_f32x4_replace_lane, simd::f32x4_replace_lane),
145+
(f64, ImmLaneIdx2, Instruction::F64x2ReplaceLane, execute_f64x2_replace_lane, simd::f64x2_replace_lane),
146+
}
147+
148+
/// Executes an [`Instruction::I8x16ReplaceLaneImm`] instruction.
149+
pub fn execute_i8x16_replace_lane_imm(
150+
&mut self,
151+
result: Reg,
152+
input: Reg,
153+
lane: ImmLaneIdx16,
154+
value: i8,
155+
) {
156+
self.execute_replace_lane_impl(result, input, lane, value, simd::i8x16_replace_lane)
157+
}
158+
159+
/// Executes an [`Instruction::I16x8ReplaceLaneImm`] instruction.
160+
pub fn execute_i16x8_replace_lane_imm(&mut self, result: Reg, input: Reg, lane: ImmLaneIdx8) {
161+
let value = self.fetch_const32_as::<i32>() as i16;
162+
self.execute_replace_lane_impl(result, input, lane, value, simd::i16x8_replace_lane)
163+
}
164+
165+
/// Executes an [`Instruction::I32x4ReplaceLaneImm`] instruction.
166+
pub fn execute_i32x4_replace_lane_imm(&mut self, result: Reg, input: Reg, lane: ImmLaneIdx4) {
167+
let value = self.fetch_const32_as::<i32>();
168+
self.execute_replace_lane_impl(result, input, lane, value, simd::i32x4_replace_lane)
169+
}
170+
171+
/// Executes an [`Instruction::I64x2ReplaceLaneImm32`] instruction.
172+
pub fn execute_i64x2_replace_lane_imm32(&mut self, result: Reg, input: Reg, lane: ImmLaneIdx2) {
173+
let value = self.fetch_i64const32();
174+
self.execute_replace_lane_impl(result, input, lane, value, simd::i64x2_replace_lane)
175+
}
176+
177+
/// Executes an [`Instruction::F32x4ReplaceLaneImm`] instruction.
178+
pub fn execute_f32x4_replace_lane_imm(&mut self, result: Reg, input: Reg, lane: ImmLaneIdx4) {
179+
let value = self.fetch_const32_as::<f32>();
180+
self.execute_replace_lane_impl(result, input, lane, value, simd::f32x4_replace_lane)
181+
}
182+
183+
/// Executes an [`Instruction::F64x2ReplaceLaneImm32`] instruction.
184+
pub fn execute_f64x2_replace_lane_imm32(&mut self, result: Reg, input: Reg, lane: ImmLaneIdx2) {
185+
let value = self.fetch_f64const32();
186+
self.execute_replace_lane_impl(result, input, lane, value, simd::f64x2_replace_lane)
187+
}
188+
189+
/// Generically execute a SIMD replace lane instruction.
190+
fn execute_replace_lane_impl<T, LaneType>(
191+
&mut self,
192+
result: Reg,
193+
input: Reg,
194+
lane: LaneType,
195+
value: T,
196+
eval: fn(V128, LaneType, T) -> V128,
197+
) {
198+
let input = self.get_register_as::<V128>(input);
199+
self.set_register_as::<V128>(result, eval(input, lane, value));
200+
self.next_instr_at(2);
201+
}
63202

64203
impl_unary_executors! {
65204
(Instruction::V128AnyTrue, execute_v128_any_true, simd::v128_any_true),

crates/wasmi/src/engine/translator/simd/mod.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
};
1010

1111
trait IntoLane {
12-
type LaneType;
12+
type LaneType: TryFrom<u8>;
1313
}
1414

1515
macro_rules! impl_into_lane_for {
@@ -68,7 +68,6 @@ impl FuncTranslator {
6868
const_eval: fn(input: V128, lane: T::LaneType) -> R,
6969
) -> Result<(), Error>
7070
where
71-
T::LaneType: TryFrom<u8>,
7271
R: Into<TypedVal>,
7372
{
7473
bail_unreachable!(self);
@@ -135,7 +134,7 @@ impl FuncTranslator {
135134
Ok(())
136135
}
137136

138-
/// Generically translate a Wasm shift instruction.
137+
/// Generically translate a Wasm SIMD shift instruction.
139138
fn translate_simd_shift<T>(
140139
&mut self,
141140
make_instr: fn(result: Reg, lhs: Reg, rhs: Reg) -> Instruction,
@@ -173,4 +172,48 @@ impl FuncTranslator {
173172
self.push_fueled_instr(instr, FuelCosts::base)?;
174173
Ok(())
175174
}
175+
176+
/// Generically translate a Wasm SIMD replace lane instruction.
177+
#[allow(clippy::type_complexity)]
178+
fn translate_replace_lane<T>(
179+
&mut self,
180+
lane: u8,
181+
const_eval: fn(input: V128, lane: T::LaneType, value: T) -> V128,
182+
make_instr: fn(result: Reg, input: Reg, lane: T::LaneType) -> Instruction,
183+
make_instr_imm: fn(
184+
this: &mut Self,
185+
result: Reg,
186+
input: Reg,
187+
lane: T::LaneType,
188+
value: T,
189+
) -> Result<(Instruction, Option<Instruction>), Error>,
190+
) -> Result<(), Error>
191+
where
192+
T: IntoLane + From<TypedVal>,
193+
{
194+
bail_unreachable!(self);
195+
let Ok(lane) = <T::LaneType>::try_from(lane) else {
196+
panic!("encountered out of bounds lane index: {lane}");
197+
};
198+
let (input, value) = self.alloc.stack.pop2();
199+
if let (Provider::Const(x), Provider::Const(item)) = (input, value) {
200+
let result = const_eval(x.into(), lane, item.into());
201+
self.alloc.stack.push_const(result);
202+
return Ok(());
203+
}
204+
let input = self.alloc.stack.provider2reg(&input)?;
205+
let result = self.alloc.stack.push_dynamic()?;
206+
let (instr, param) = match value {
207+
Provider::Register(value) => (
208+
make_instr(result, input, lane),
209+
Some(Instruction::register(value)),
210+
),
211+
Provider::Const(value) => make_instr_imm(self, result, input, lane, value.into())?,
212+
};
213+
self.push_fueled_instr(instr, FuelCosts::base)?;
214+
if let Some(param) = param {
215+
self.alloc.instr_encoder.append_instr(param)?;
216+
}
217+
Ok(())
218+
}
176219
}

0 commit comments

Comments
 (0)