Skip to content

Commit 4cbe1c0

Browse files
committed
Implement v128.{load,store}{8,16,32,64}_lane instructions
These instructions are proposed in WebAssembly/simd#350. This PR implements them throughout Binaryen except in the C/JS APIs and in the fuzzer, where it leaves TODOs instead. Right now these instructions are just being implemented for prototyping so adding them to the APIs isn't critical and they aren't generally available to be fuzzed in Wasm engines.
1 parent 3e1fdf2 commit 4cbe1c0

29 files changed

+1045
-145
lines changed

scripts/gen-s-parser.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@
350350
("v128.xor", "makeBinary(s, BinaryOp::XorVec128)"),
351351
("v128.andnot", "makeBinary(s, BinaryOp::AndNotVec128)"),
352352
("v128.bitselect", "makeSIMDTernary(s, SIMDTernaryOp::Bitselect)"),
353+
("v128.load8_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec8x16)"),
354+
("v128.load16_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec16x8)"),
355+
("v128.load32_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec32x4)"),
356+
("v128.load64_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec64x2)"),
357+
("v128.store8_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec8x16)"),
358+
("v128.store16_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec16x8)"),
359+
("v128.store32_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec32x4)"),
360+
("v128.store64_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec64x2)"),
353361
("i8x16.abs", "makeUnary(s, UnaryOp::AbsVecI8x16)"),
354362
("i8x16.neg", "makeUnary(s, UnaryOp::NegVecI8x16)"),
355363
("i8x16.any_true", "makeUnary(s, UnaryOp::AnyTrueVecI8x16)"),

src/binaryen-c.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ BINARYEN_API BinaryenExpressionId BinaryenSIMDShuffleId(void);
159159
BINARYEN_API BinaryenExpressionId BinaryenSIMDTernaryId(void);
160160
BINARYEN_API BinaryenExpressionId BinaryenSIMDShiftId(void);
161161
BINARYEN_API BinaryenExpressionId BinaryenSIMDLoadId(void);
162+
// TODO: Expose SIMDLoadStoreLane in C and JS APIs
162163
BINARYEN_API BinaryenExpressionId BinaryenMemoryInitId(void);
163164
BINARYEN_API BinaryenExpressionId BinaryenDataDropId(void);
164165
BINARYEN_API BinaryenExpressionId BinaryenMemoryCopyId(void);

src/gen-s-parser.inc

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2776,11 +2776,33 @@ switch (op[0]) {
27762776
case '\0':
27772777
if (strcmp(op, "v128.load") == 0) { return makeLoad(s, Type::v128, /*isAtomic=*/false); }
27782778
goto parse_error;
2779-
case '3':
2780-
if (strcmp(op, "v128.load32_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load32Zero); }
2779+
case '1':
2780+
if (strcmp(op, "v128.load16_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec16x8); }
27812781
goto parse_error;
2782-
case '6':
2783-
if (strcmp(op, "v128.load64_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load64Zero); }
2782+
case '3': {
2783+
switch (op[12]) {
2784+
case 'l':
2785+
if (strcmp(op, "v128.load32_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec32x4); }
2786+
goto parse_error;
2787+
case 'z':
2788+
if (strcmp(op, "v128.load32_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load32Zero); }
2789+
goto parse_error;
2790+
default: goto parse_error;
2791+
}
2792+
}
2793+
case '6': {
2794+
switch (op[12]) {
2795+
case 'l':
2796+
if (strcmp(op, "v128.load64_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec64x2); }
2797+
goto parse_error;
2798+
case 'z':
2799+
if (strcmp(op, "v128.load64_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load64Zero); }
2800+
goto parse_error;
2801+
default: goto parse_error;
2802+
}
2803+
}
2804+
case '8':
2805+
if (strcmp(op, "v128.load8_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec8x16); }
27842806
goto parse_error;
27852807
default: goto parse_error;
27862808
}
@@ -2791,9 +2813,26 @@ switch (op[0]) {
27912813
case 'o':
27922814
if (strcmp(op, "v128.or") == 0) { return makeBinary(s, BinaryOp::OrVec128); }
27932815
goto parse_error;
2794-
case 's':
2795-
if (strcmp(op, "v128.store") == 0) { return makeStore(s, Type::v128, /*isAtomic=*/false); }
2796-
goto parse_error;
2816+
case 's': {
2817+
switch (op[10]) {
2818+
case '\0':
2819+
if (strcmp(op, "v128.store") == 0) { return makeStore(s, Type::v128, /*isAtomic=*/false); }
2820+
goto parse_error;
2821+
case '1':
2822+
if (strcmp(op, "v128.store16_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec16x8); }
2823+
goto parse_error;
2824+
case '3':
2825+
if (strcmp(op, "v128.store32_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec32x4); }
2826+
goto parse_error;
2827+
case '6':
2828+
if (strcmp(op, "v128.store64_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec64x2); }
2829+
goto parse_error;
2830+
case '8':
2831+
if (strcmp(op, "v128.store8_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec8x16); }
2832+
goto parse_error;
2833+
default: goto parse_error;
2834+
}
2835+
}
27972836
case 'x':
27982837
if (strcmp(op, "v128.xor") == 0) { return makeBinary(s, BinaryOp::XorVec128); }
27992838
goto parse_error;

src/ir/ExpressionAnalyzer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ template<typename T> void visitImmediates(Expression* curr, T& visitor) {
203203
visitor.visitAddress(curr->offset);
204204
visitor.visitAddress(curr->align);
205205
}
206+
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
207+
visitor.visitInt(curr->op);
208+
visitor.visitAddress(curr->offset);
209+
visitor.visitAddress(curr->align);
210+
visitor.visitInt(curr->index);
211+
}
206212
void visitMemoryInit(MemoryInit* curr) {
207213
visitor.visitIndex(curr->segment);
208214
}

src/ir/ExpressionManipulator.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
182182
return builder.makeSIMDLoad(
183183
curr->op, curr->offset, curr->align, copy(curr->ptr));
184184
}
185+
Expression* visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
186+
return builder.makeSIMDLoadStoreLane(curr->op,
187+
curr->offset,
188+
curr->align,
189+
curr->index,
190+
copy(curr->ptr),
191+
copy(curr->vec));
192+
}
185193
Expression* visitConst(Const* curr) {
186194
return builder.makeConst(curr->value);
187195
}

src/ir/ReFinalize.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ void ReFinalize::visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); }
109109
void ReFinalize::visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); }
110110
void ReFinalize::visitSIMDShift(SIMDShift* curr) { curr->finalize(); }
111111
void ReFinalize::visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); }
112+
void ReFinalize::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
113+
curr->finalize();
114+
}
112115
void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); }
113116
void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); }
114117
void ReFinalize::visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); }

src/ir/effects.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,16 @@ struct EffectAnalyzer
385385
implicitTrap = true;
386386
}
387387
}
388+
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
389+
if (curr->isLoad()) {
390+
readsMemory = true;
391+
} else {
392+
writesMemory = true;
393+
}
394+
if (!ignoreImplicitTraps) {
395+
implicitTrap = true;
396+
}
397+
}
388398
void visitMemoryInit(MemoryInit* curr) {
389399
writesMemory = true;
390400
if (!ignoreImplicitTraps) {

src/ir/utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ struct ReFinalize
135135
void visitSIMDTernary(SIMDTernary* curr);
136136
void visitSIMDShift(SIMDShift* curr);
137137
void visitSIMDLoad(SIMDLoad* curr);
138+
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr);
138139
void visitMemoryInit(MemoryInit* curr);
139140
void visitDataDrop(DataDrop* curr);
140141
void visitMemoryCopy(MemoryCopy* curr);
@@ -219,6 +220,7 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> {
219220
void visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); }
220221
void visitSIMDShift(SIMDShift* curr) { curr->finalize(); }
221222
void visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); }
223+
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { curr->finalize(); }
222224
void visitMemoryInit(MemoryInit* curr) { curr->finalize(); }
223225
void visitDataDrop(DataDrop* curr) { curr->finalize(); }
224226
void visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); }

src/passes/DeadCodeElimination.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ struct DeadCodeElimination
345345
DELEGATE(SIMDShift);
346346
case Expression::Id::SIMDLoadId:
347347
DELEGATE(SIMDLoad);
348+
case Expression::Id::SIMDLoadStoreLaneId:
349+
DELEGATE(SIMDLoadStoreLane);
348350
case Expression::Id::MemoryInitId:
349351
DELEGATE(MemoryInit);
350352
case Expression::Id::DataDropId:

src/passes/Print.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,43 @@ struct PrintExpressionContents
499499
o << " align=" << curr->align;
500500
}
501501
}
502+
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
503+
prepareColor(o);
504+
switch (curr->op) {
505+
case LoadLaneVec8x16:
506+
o << "v128.load8_lane";
507+
break;
508+
case LoadLaneVec16x8:
509+
o << "v128.load16_lane";
510+
break;
511+
case LoadLaneVec32x4:
512+
o << "v128.load32_lane";
513+
break;
514+
case LoadLaneVec64x2:
515+
o << "v128.load64_lane";
516+
break;
517+
case StoreLaneVec8x16:
518+
o << "v128.store8_lane";
519+
break;
520+
case StoreLaneVec16x8:
521+
o << "v128.store16_lane";
522+
break;
523+
case StoreLaneVec32x4:
524+
o << "v128.store32_lane";
525+
break;
526+
case StoreLaneVec64x2:
527+
o << "v128.store64_lane";
528+
break;
529+
}
530+
restoreNormalColor(o);
531+
if (curr->offset) {
532+
o << " offset=" << curr->offset;
533+
}
534+
if (curr->align != curr->getMemBytes()) {
535+
o << " align=" << curr->align;
536+
}
537+
o << " " << curr->index;
538+
}
502539
void visitMemoryInit(MemoryInit* curr) {
503540
prepareColor(o);
504541
o << "memory.init " << curr->segment;
@@ -1908,6 +1945,14 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
19081945
printFullLine(curr->ptr);
19091946
decIndent();
19101947
}
1948+
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
1949+
o << '(';
1950+
PrintExpressionContents(currFunction, o).visit(curr);
1951+
incIndent();
1952+
printFullLine(curr->ptr);
1953+
printFullLine(curr->vec);
1954+
decIndent();
1955+
}
19111956
void visitMemoryInit(MemoryInit* curr) {
19121957
o << '(';
19131958
PrintExpressionContents(currFunction, o).visit(curr);

src/tools/fuzzing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,7 @@ class TranslateToFuzzReader {
24512451
if (type != Type::v128) {
24522452
return makeSIMDExtract(type);
24532453
}
2454+
// TODO: Add SIMDLoadStoreLane once it is generally available
24542455
switch (upTo(7)) {
24552456
case 0:
24562457
return makeUnary(Type::v128);

src/wasm-binary.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,15 @@ enum ASTNodes {
791791
V128Xor = 0x51,
792792
V128Bitselect = 0x52,
793793

794+
V128Load8Lane = 0x58,
795+
V128Load16Lane = 0x59,
796+
V128Load32Lane = 0x5a,
797+
V128Load64Lane = 0x5b,
798+
V128Store8Lane = 0x5c,
799+
V128Store16Lane = 0x5d,
800+
V128Store32Lane = 0x5e,
801+
V128Store64Lane = 0x5f,
802+
794803
I8x16Abs = 0x60,
795804
I8x16Neg = 0x61,
796805
I8x16AnyTrue = 0x62,
@@ -1486,6 +1495,7 @@ class WasmBinaryBuilder {
14861495
bool maybeVisitSIMDTernary(Expression*& out, uint32_t code);
14871496
bool maybeVisitSIMDShift(Expression*& out, uint32_t code);
14881497
bool maybeVisitSIMDLoad(Expression*& out, uint32_t code);
1498+
bool maybeVisitSIMDLoadStoreLane(Expression*& out, uint32_t code);
14891499
bool maybeVisitMemoryInit(Expression*& out, uint32_t code);
14901500
bool maybeVisitDataDrop(Expression*& out, uint32_t code);
14911501
bool maybeVisitMemoryCopy(Expression*& out, uint32_t code);

src/wasm-builder.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,21 @@ class Builder {
434434
ret->finalize();
435435
return ret;
436436
}
437+
SIMDLoadStoreLane* makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op,
438+
Address offset,
439+
Address align,
440+
uint8_t index,
441+
Expression* ptr,
442+
Expression* vec) {
443+
auto* ret = wasm.allocator.alloc<SIMDLoadStoreLane>();
444+
ret->op = op;
445+
ret->offset = offset;
446+
ret->align = align;
447+
ret->index = index;
448+
ret->ptr = ptr;
449+
ret->finalize();
450+
return ret;
451+
}
437452
MemoryInit* makeMemoryInit(uint32_t segment,
438453
Expression* dest,
439454
Expression* offset,

src/wasm-interpreter.h

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,9 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
12401240
Flow visitSIMDLoadSplat(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); }
12411241
Flow visitSIMDLoadExtend(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); }
12421242
Flow visitSIMDLoadZero(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); }
1243+
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
1244+
WASM_UNREACHABLE("unimp");
1245+
}
12431246
Flow visitPop(Pop* curr) { WASM_UNREACHABLE("unimp"); }
12441247
Flow visitRefNull(RefNull* curr) {
12451248
NOTE_ENTER("RefNull");
@@ -1627,6 +1630,10 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
16271630
NOTE_ENTER("SIMDLoadExtend");
16281631
return Flow(NONCONSTANT_FLOW);
16291632
}
1633+
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
1634+
NOTE_ENTER("SIMDLoadStoreLane");
1635+
return Flow(NONCONSTANT_FLOW);
1636+
}
16301637
Flow visitPop(Pop* curr) {
16311638
NOTE_ENTER("Pop");
16321639
return Flow(NONCONSTANT_FLOW);
@@ -2369,7 +2376,7 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
23692376
}
23702377
NOTE_EVAL1(flow);
23712378
Address src = instance.getFinalAddress(
2372-
curr, flow.getSingleValue(), curr->op == Load32Zero ? 32 : 64);
2379+
curr, flow.getSingleValue(), curr->getMemBytes());
23732380
auto zero =
23742381
Literal::makeZero(curr->op == Load32Zero ? Type::i32 : Type::i64);
23752382
if (curr->op == Load32Zero) {
@@ -2380,6 +2387,76 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
23802387
return Literal(std::array<Literal, 2>{{val, zero}});
23812388
}
23822389
}
2390+
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
2391+
NOTE_ENTER("SIMDLoadStoreLane");
2392+
Flow flow = this->visit(curr->ptr);
2393+
if (flow.breaking()) {
2394+
return flow;
2395+
}
2396+
NOTE_EVAL1(flow);
2397+
Address addr = instance.getFinalAddress(
2398+
curr, flow.getSingleValue(), curr->getMemBytes());
2399+
flow = this->visit(curr->vec);
2400+
if (flow.breaking()) {
2401+
return flow;
2402+
}
2403+
Literal vec = flow.getSingleValue();
2404+
switch (curr->op) {
2405+
case LoadLaneVec8x16:
2406+
case StoreLaneVec8x16: {
2407+
std::array<Literal, 16> lanes = vec.getLanesUI8x16();
2408+
if (curr->isLoad()) {
2409+
lanes[curr->index] =
2410+
Literal(instance.externalInterface->load8u(addr));
2411+
return Literal(lanes);
2412+
} else {
2413+
instance.externalInterface->store8(addr,
2414+
lanes[curr->index].geti32());
2415+
return {};
2416+
}
2417+
}
2418+
case LoadLaneVec16x8:
2419+
case StoreLaneVec16x8: {
2420+
std::array<Literal, 8> lanes = vec.getLanesUI16x8();
2421+
if (curr->isLoad()) {
2422+
lanes[curr->index] =
2423+
Literal(instance.externalInterface->load16u(addr));
2424+
return Literal(lanes);
2425+
} else {
2426+
instance.externalInterface->store16(addr,
2427+
lanes[curr->index].geti32());
2428+
return {};
2429+
}
2430+
}
2431+
case LoadLaneVec32x4:
2432+
case StoreLaneVec32x4: {
2433+
std::array<Literal, 4> lanes = vec.getLanesI32x4();
2434+
if (curr->isLoad()) {
2435+
lanes[curr->index] =
2436+
Literal(instance.externalInterface->load32u(addr));
2437+
return Literal(lanes);
2438+
} else {
2439+
instance.externalInterface->store32(addr,
2440+
lanes[curr->index].geti32());
2441+
return {};
2442+
}
2443+
}
2444+
case StoreLaneVec64x2:
2445+
case LoadLaneVec64x2: {
2446+
std::array<Literal, 2> lanes = vec.getLanesI64x2();
2447+
if (curr->isLoad()) {
2448+
lanes[curr->index] =
2449+
Literal(instance.externalInterface->load64u(addr));
2450+
return Literal(lanes);
2451+
} else {
2452+
instance.externalInterface->store64(addr,
2453+
lanes[curr->index].geti64());
2454+
return {};
2455+
}
2456+
}
2457+
}
2458+
WASM_UNREACHABLE("unexpected op");
2459+
}
23832460
Flow visitMemorySize(MemorySize* curr) {
23842461
NOTE_ENTER("MemorySize");
23852462
return Literal::makeFromInt64(instance.memorySize,

src/wasm-s-parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class SExpressionWasmBuilder {
212212
Expression* makeSIMDTernary(Element& s, SIMDTernaryOp op);
213213
Expression* makeSIMDShift(Element& s, SIMDShiftOp op);
214214
Expression* makeSIMDLoad(Element& s, SIMDLoadOp op);
215+
Expression* makeSIMDLoadStoreLane(Element& s, SIMDLoadStoreLaneOp op);
215216
Expression* makeMemoryInit(Element& s);
216217
Expression* makeDataDrop(Element& s);
217218
Expression* makeMemoryCopy(Element& s);

0 commit comments

Comments
 (0)