Skip to content

Commit 2d72275

Browse files
authored
Simplify simd load operators (#1922)
* add Wasmi specific v128.low{32,64}_zero methods * rename internal load_nxm -> widen_nxm * add Wasmi specific v128_widen_nxm methods * add new wasmi-specific simd ops * remove unnecessary leading pipes * fix broken intra doc links * add comment about new Wasmi ops * replace load.splat with load.scalar + simd.splat ops * add select_load_op utility method * translate load_lane ops as scalar load + replace_lane * fix bug in v128_low_zero64_sr exec handler * translate v128_loadN_zero as scalar load + mod * fix yet another broken intra doc link * rename translate_v128_load_low_zero -> translate_v128_load_modify * translate v128.load_extend to scalar load + widening * remove unnecessary SimdLoadOp utility trait * remove now unused simd.load Wasmi IR operators * update build script expected output file sizes
1 parent 5404dc2 commit 2d72275

15 files changed

Lines changed: 250 additions & 542 deletions

File tree

crates/core/src/simd.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,25 @@ pub fn v128_load_at(memory: &[u8], address: usize) -> Result<V128, TrapCode> {
14751475
memory::load_at::<u128>(memory, address).map(V128::from)
14761476
}
14771477

1478+
macro_rules! impl_v128_lowN_zero_for {
1479+
(
1480+
$( fn $name:ident(bits: $bits_ty:ty) -> V128; )*
1481+
) => {
1482+
$(
1483+
#[doc = concat!("Executes a Wasmi specific `", stringify!($name), "` instruction.")]
1484+
#[doc = ""]
1485+
#[doc = concat!("Returns a [`V128`] value with the first `", stringify!($bits_ty), "` lane set to `bits` and all others to zero.")]
1486+
pub fn $name(bits: $bits_ty) -> V128 {
1487+
V128::splat::<$bits_ty>(0).replace_lane::<$bits_ty>(<$bits_ty as IntoLaneIdx>::LaneIdx::zero(), bits)
1488+
}
1489+
)*
1490+
};
1491+
}
1492+
impl_v128_lowN_zero_for! {
1493+
fn v128_low32_zero(bits: u32) -> V128;
1494+
fn v128_low64_zero(bits: u64) -> V128;
1495+
}
1496+
14781497
macro_rules! impl_v128_loadN_zero_for {
14791498
(
14801499
$( fn $name:ident(memory: &[u8], ptr: u64, offset: u64) -> Result<V128, TrapCode> = $ty:ty; )*
@@ -1626,7 +1645,7 @@ impl_v128_loadN_lane_at_for! {
16261645
/// Usually `T` is an array of `U` where `U` fits multiple times into `Self`.
16271646
/// An example of this is that `u64` can be split into `[u32; 2]`.
16281647
///
1629-
/// This is a helper trait to implement [`V128::load_nxm`] generically.
1648+
/// This is a helper trait to implement [`V128::widen_nxm`] generically.
16301649
trait SplitInto<T> {
16311650
type Output;
16321651
fn split_into(self) -> Self::Output;
@@ -1661,7 +1680,7 @@ impl_split_into_for! {
16611680

16621681
/// Allows to extend all items in an array from `T` to `Ext`.
16631682
///
1664-
/// This is a helper trait to implement [`V128::load_nxm`] generically.
1683+
/// This is a helper trait to implement [`V128::widen_nxm`] generically.
16651684
trait ExtendArray<T> {
16661685
type Output;
16671686
fn extend_array(self) -> Self::Output;
@@ -1679,7 +1698,7 @@ where
16791698

16801699
impl V128 {
16811700
/// Interprets `bits` as array of `Narrow` and distribute the (sign) extended items as [`V128`].
1682-
fn load_nxm<Narrow, Wide>(bits: u64) -> V128
1701+
fn widen_nxm<Narrow, Wide>(bits: u64) -> V128
16831702
where
16841703
u64: SplitInto<Narrow, Output: ExtendArray<Wide, Output: Into<<Wide as IntoLanes>::Lanes>>>,
16851704
Wide: IntoLanes,
@@ -1688,6 +1707,33 @@ impl V128 {
16881707
}
16891708
}
16901709

1710+
macro_rules! impl_v128_widen_mxn {
1711+
(
1712+
$( fn $name:ident(bits: u64) -> V128 = ($n:ty => $w:ty); )*
1713+
) => {
1714+
$(
1715+
#[doc = concat!("Executes a specialized Wasmi `", stringify!($name), "` instruction.")]
1716+
#[doc = ""]
1717+
#[doc = concat!("Returns a [`V128`] where each lane is widened from `", stringify!($n), "` to `", stringify!($w), "`.")]
1718+
#[doc = ""]
1719+
#[doc = " # Note"]
1720+
#[doc = ""]
1721+
#[doc = concat!("The `bits` argument is reinterpreted as array of `", stringify!($n), "`.")]
1722+
pub fn $name(bits: u64) -> V128 {
1723+
V128::widen_nxm::<$n, $w>(bits)
1724+
}
1725+
)*
1726+
};
1727+
}
1728+
impl_v128_widen_mxn! {
1729+
fn v128_widen8x8_s(bits: u64) -> V128 = (i8 => i16);
1730+
fn v128_widen8x8_u(bits: u64) -> V128 = (u8 => u16);
1731+
fn v128_widen16x4_s(bits: u64) -> V128 = (i16 => i32);
1732+
fn v128_widen16x4_u(bits: u64) -> V128 = (u16 => u32);
1733+
fn v128_widen32x2_s(bits: u64) -> V128 = (i32 => i64);
1734+
fn v128_widen32x2_u(bits: u64) -> V128 = (u32 => u64);
1735+
}
1736+
16911737
macro_rules! impl_v128_load_mxn {
16921738
(
16931739
$( fn $name:ident(memory: &[u8], ptr: u64, offset: u64) -> Result<V128, TrapCode> = ($n:ty => $w:ty); )*
@@ -1700,7 +1746,7 @@ macro_rules! impl_v128_load_mxn {
17001746
/// - If `ptr + offset` overflows.
17011747
/// - If `ptr + offset` loads out of bounds from `memory`.
17021748
pub fn $name(memory: &[u8], ptr: u64, offset: u64) -> Result<V128, TrapCode> {
1703-
memory::load::<u64>(memory, ptr, offset).map(V128::load_nxm::<$n, $w>)
1749+
memory::load::<u64>(memory, ptr, offset).map(V128::widen_nxm::<$n, $w>)
17041750
}
17051751
)*
17061752
};
@@ -1725,7 +1771,7 @@ macro_rules! impl_v128_load_mxn_at {
17251771
///
17261772
/// If `address` loads out of bounds from `memory`.
17271773
pub fn $name(memory: &[u8], address: usize) -> Result<V128, TrapCode> {
1728-
memory::load_at::<u64>(memory, address).map(V128::load_nxm::<$n, $w>)
1774+
memory::load_at::<u64>(memory, address).map(V128::widen_nxm::<$n, $w>)
17291775
}
17301776
)*
17311777
};

crates/ir/build/display/decode.rs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use crate::build::{
1515
GenericOp,
1616
GlobalGetOp,
1717
GlobalSetOp,
18-
LoadKind,
1918
LoadOp,
2019
MemoryOperand,
2120
OffsetOperand,
@@ -166,17 +165,6 @@ impl Display for DisplayDecode<&'_ LoadOp> {
166165
OffsetOperand::Offset => "",
167166
OffsetOperand::Offset16 => "Offset16",
168167
};
169-
let (lane_suffix, lane_param) = match op.kind {
170-
LoadKind::Lane { width } => {
171-
let lane_param = DisplayConcat((',', FieldTy::from(width)));
172-
(
173-
Some(CamelCase(Ident::Lane)),
174-
Some(lane_param).display_maybe(),
175-
)
176-
}
177-
_ => (None, DisplayMaybe::None),
178-
};
179-
let lane_suffix = lane_suffix.display_maybe();
180168
let result_ty = op.result.field_ty(op.result_ty);
181169
let ptr_ty = match op.ptr {
182170
OperandKind::Reg => DisplayMaybe::Some(DisplayConcat((',', FieldTy::RegInt))),
@@ -187,10 +175,10 @@ impl Display for DisplayDecode<&'_ LoadOp> {
187175
OperandKind::Immediate => DisplayMaybe::None,
188176
OperandKind::Local(_index) => DisplayMaybe::None,
189177
};
190-
let generics = DisplayConcat(('<', result_ty, ptr_ty, lane_param, '>'));
178+
let generics = DisplayConcat(('<', result_ty, ptr_ty, '>'));
191179
writeln!(
192180
f,
193-
"pub type {camel_ident} = Load{lane_suffix}{at_suffix}Op{mem0_suffix}{offset16_suffix}{generics};"
181+
"pub type {camel_ident} = Load{at_suffix}Op{mem0_suffix}{offset16_suffix}{generics};"
194182
)
195183
}
196184
}

crates/ir/build/display/ident.rs

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -60,36 +60,39 @@ impl<T> DisplayIdent<T> {
6060
impl Display for CamelCase<Ty> {
6161
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6262
let s = match self.0 {
63-
| Ty::Bits8 => "8",
64-
| Ty::Bits16 => "16",
65-
| Ty::Bits32 => "32",
66-
| Ty::Bits64 => "64",
67-
| Ty::I32 => "I32",
68-
| Ty::I64 => "I64",
69-
| Ty::U8 => "U8",
70-
| Ty::U16 => "U16",
71-
| Ty::U32 => "U32",
72-
| Ty::U64 => "U64",
73-
| Ty::NonZeroI32 => "I32",
74-
| Ty::NonZeroI64 => "I64",
75-
| Ty::NonZeroU32 => "U32",
76-
| Ty::NonZeroU64 => "U64",
77-
| Ty::F32 => "F32",
78-
| Ty::F64 => "F64",
79-
| Ty::SignF32 => "F32",
80-
| Ty::SignF64 => "F64",
81-
| Ty::V128 => "V128",
82-
| Ty::I8x16 => "I8x16",
83-
| Ty::I16x8 => "I16x8",
84-
| Ty::I32x4 => "I32x4",
85-
| Ty::I64x2 => "I64x2",
86-
| Ty::U8x16 => "U8x16",
87-
| Ty::U16x8 => "U16x8",
88-
| Ty::U32x4 => "U32x4",
89-
| Ty::U64x2 => "U64x2",
90-
| Ty::F32x4 => "F32x4",
91-
| Ty::F64x2 => "F64x2",
92-
| Ty::ShiftAmount => "U8",
63+
Ty::Bits8 => "8",
64+
Ty::Bits16 => "16",
65+
Ty::Bits32 => "32",
66+
Ty::Bits64 => "64",
67+
Ty::Bits8x8 => "8x8",
68+
Ty::Bits16x4 => "16x4",
69+
Ty::Bits32x2 => "32x2",
70+
Ty::I32 => "I32",
71+
Ty::I64 => "I64",
72+
Ty::U8 => "U8",
73+
Ty::U16 => "U16",
74+
Ty::U32 => "U32",
75+
Ty::U64 => "U64",
76+
Ty::NonZeroI32 => "I32",
77+
Ty::NonZeroI64 => "I64",
78+
Ty::NonZeroU32 => "U32",
79+
Ty::NonZeroU64 => "U64",
80+
Ty::F32 => "F32",
81+
Ty::F64 => "F64",
82+
Ty::SignF32 => "F32",
83+
Ty::SignF64 => "F64",
84+
Ty::V128 => "V128",
85+
Ty::I8x16 => "I8x16",
86+
Ty::I16x8 => "I16x8",
87+
Ty::I32x4 => "I32x4",
88+
Ty::I64x2 => "I64x2",
89+
Ty::U8x16 => "U8x16",
90+
Ty::U16x8 => "U16x8",
91+
Ty::U32x4 => "U32x4",
92+
Ty::U64x2 => "U64x2",
93+
Ty::F32x4 => "F32x4",
94+
Ty::F64x2 => "F64x2",
95+
Ty::ShiftAmount => "U8",
9396
};
9497
f.write_str(s)
9598
}
@@ -102,6 +105,9 @@ impl Display for SnakeCase<Ty> {
102105
Ty::Bits16 => "16",
103106
Ty::Bits32 => "32",
104107
Ty::Bits64 => "64",
108+
Ty::Bits8x8 => "8x8",
109+
Ty::Bits16x4 => "16x4",
110+
Ty::Bits32x2 => "32x2",
105111
Ty::I32 => "i32",
106112
Ty::I64 => "i64",
107113
Ty::U8 => "u8",
@@ -214,7 +220,13 @@ impl Display for CamelCase<IdentSuffix<Ty>> {
214220
impl Display for SnakeCase<IdentSuffix<Ty>> {
215221
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216222
match self.0.0 {
217-
| Ty::Bits8 | Ty::Bits16 | Ty::Bits32 | Ty::Bits64 => {}
223+
| Ty::Bits8
224+
| Ty::Bits16
225+
| Ty::Bits32
226+
| Ty::Bits64
227+
| Ty::Bits8x8
228+
| Ty::Bits16x4
229+
| Ty::Bits32x2 => {}
218230
_ => SnakeCase(Sep).fmt(f)?,
219231
}
220232
SnakeCase(self.0.0).fmt(f)
@@ -415,12 +427,6 @@ impl Display for DisplayIdent<&'_ LoadOp> {
415427
let ident = case.wrap(Ident::Load);
416428
let result_suffix = case.wrap(Suffix(op.result));
417429
let ptr_suffix = SnakeCase(Suffix(op.ptr));
418-
let v128_suffix = op
419-
.v128_field()
420-
.map(|_| OperandKind::Slot)
421-
.map(Suffix)
422-
.map(SnakeCase)
423-
.display_maybe();
424430
let loaded_suffix = op.kind.loaded_layout().map(IdentSuffix).display_maybe();
425431
let ident_prefix = case.wrap(IdentPrefix(op.result_ty));
426432
let ident_suffix = op
@@ -433,7 +439,7 @@ impl Display for DisplayIdent<&'_ LoadOp> {
433439
let offset_suffix = self.map(op.offset);
434440
write!(
435441
f,
436-
"{ident_prefix}{ident}{ident_suffix}{loaded_suffix}{mem_suffix}{offset_suffix}_{result_suffix}{ptr_suffix}{v128_suffix}",
442+
"{ident_prefix}{ident}{ident_suffix}{loaded_suffix}{mem_suffix}{offset_suffix}_{result_suffix}{ptr_suffix}",
437443
)
438444
}
439445
}

crates/ir/build/ident.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ define_ident!(
238238
Splat: splat,
239239
Widen: widen,
240240
Extend: extend,
241-
Low: low,
241+
LowZero: low_zero,
242242
ExtractLane: extract_lane,
243243
ReplaceLane: replace_lane,
244244
Swizzle: swizzle,

crates/ir/build/isa.rs

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,52 +1193,46 @@ fn add_simd_unary_ops(isa: &mut Isa) {
11931193
OperandKind::Slot,
11941194
));
11951195
}
1196+
// Wasmi specific operators:
1197+
let wasmi_ops = [
1198+
(Ident::LowZero, Ty::V128, Ty::Bits32),
1199+
(Ident::LowZero, Ty::V128, Ty::Bits64),
1200+
(Ident::Widen, Ty::U16x8, Ty::Bits8x8),
1201+
(Ident::Widen, Ty::I16x8, Ty::Bits8x8),
1202+
(Ident::Widen, Ty::U32x4, Ty::Bits16x4),
1203+
(Ident::Widen, Ty::I32x4, Ty::Bits16x4),
1204+
(Ident::Widen, Ty::U64x2, Ty::Bits32x2),
1205+
(Ident::Widen, Ty::I64x2, Ty::Bits32x2),
1206+
];
1207+
for (ident, result_ty, value_ty) in wasmi_ops {
1208+
isa.push_op(UnaryOp::new(
1209+
ident,
1210+
result_ty,
1211+
value_ty,
1212+
OperandKind::Slot,
1213+
OperandKind::Reg,
1214+
));
1215+
}
11961216
}
11971217

11981218
fn add_simd_load_ops(isa: &mut Isa) {
1199-
#[rustfmt::skip]
1200-
let ops = [
1201-
(LoadKind::Value, Ty::V128),
1202-
// load-widen
1203-
(LoadKind::Widen { layout: Layout::Bits8x8 }, Ty::I16x8),
1204-
(LoadKind::Widen { layout: Layout::Bits8x8 }, Ty::U16x8),
1205-
(LoadKind::Widen { layout: Layout::Bits16x4 }, Ty::I32x4),
1206-
(LoadKind::Widen { layout: Layout::Bits16x4 }, Ty::U32x4),
1207-
(LoadKind::Widen { layout: Layout::Bits32x2 }, Ty::I64x2),
1208-
(LoadKind::Widen { layout: Layout::Bits32x2 }, Ty::U64x2),
1209-
// load-splat
1210-
(LoadKind::Splat { layout: Layout::Bits8 }, Ty::V128),
1211-
(LoadKind::Splat { layout: Layout::Bits16 }, Ty::V128),
1212-
(LoadKind::Splat { layout: Layout::Bits32 }, Ty::V128),
1213-
(LoadKind::Splat { layout: Layout::Bits64 }, Ty::V128),
1214-
// load-low
1215-
(LoadKind::Low { layout: Layout::Bits32 }, Ty::V128),
1216-
(LoadKind::Low { layout: Layout::Bits64 }, Ty::V128),
1217-
// load-lane
1218-
(LoadKind::Lane { width: LaneWidth::W8 }, Ty::V128),
1219-
(LoadKind::Lane { width: LaneWidth::W16 }, Ty::V128),
1220-
(LoadKind::Lane { width: LaneWidth::W32 }, Ty::V128),
1221-
(LoadKind::Lane { width: LaneWidth::W64 }, Ty::V128),
1222-
];
1223-
for (kind, result_ty) in ops {
1224-
for ptr in [OperandKind::Reg, OperandKind::Slot] {
1225-
isa.push_op(LoadOp::new(
1226-
kind,
1227-
result_ty,
1228-
OperandKind::Slot,
1229-
ptr,
1230-
MemoryOperand::Immediate,
1231-
OffsetOperand::Offset,
1232-
));
1233-
isa.push_op(LoadOp::new(
1234-
kind,
1235-
result_ty,
1236-
OperandKind::Slot,
1237-
ptr,
1238-
MemoryOperand::Mem0,
1239-
OffsetOperand::Offset16,
1240-
));
1241-
}
1219+
for ptr in [OperandKind::Reg, OperandKind::Slot] {
1220+
isa.push_op(LoadOp::new(
1221+
LoadKind::Value,
1222+
Ty::V128,
1223+
OperandKind::Slot,
1224+
ptr,
1225+
MemoryOperand::Immediate,
1226+
OffsetOperand::Offset,
1227+
));
1228+
isa.push_op(LoadOp::new(
1229+
LoadKind::Value,
1230+
Ty::V128,
1231+
OperandKind::Slot,
1232+
ptr,
1233+
MemoryOperand::Mem0,
1234+
OffsetOperand::Offset16,
1235+
));
12421236
}
12431237
}
12441238

crates/ir/build/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub fn generate_code(config: &Config) -> Result<(), Error> {
7777

7878
fn generate_op_rs(config: &Config, isa: &Isa, contents: &mut String) -> Result<(), Error> {
7979
let expected_size = match config.simd {
80-
true => 520_000,
80+
true => 480_000,
8181
false => 360_000,
8282
};
8383
write_to_buffer(contents, expected_size, |buffer| {
@@ -99,7 +99,7 @@ fn generate_op_rs(config: &Config, isa: &Isa, contents: &mut String) -> Result<(
9999

100100
fn generate_op_code_rs(config: &Config, isa: &Isa, contents: &mut String) -> Result<(), Error> {
101101
let expected_size = match config.simd {
102-
true => 310_000,
102+
true => 290_000,
103103
false => 230_000,
104104
};
105105
write_to_buffer(contents, expected_size, |buffer| {
@@ -111,7 +111,7 @@ fn generate_op_code_rs(config: &Config, isa: &Isa, contents: &mut String) -> Res
111111

112112
fn generate_encode_rs(config: &Config, isa: &Isa, contents: &mut String) -> Result<(), Error> {
113113
let expected_size = match config.simd {
114-
true => 220_000,
114+
true => 205_000,
115115
false => 170_000,
116116
};
117117
write_to_buffer(contents, expected_size, |buffer| {
@@ -123,7 +123,7 @@ fn generate_encode_rs(config: &Config, isa: &Isa, contents: &mut String) -> Resu
123123

124124
fn generate_decode_rs(config: &Config, isa: &Isa, contents: &mut String) -> Result<(), Error> {
125125
let expected_size = match config.simd {
126-
true => 90_000,
126+
true => 85_000,
127127
false => 70_000,
128128
};
129129
write_to_buffer(contents, expected_size, |buffer| {

0 commit comments

Comments
 (0)