Skip to content

Commit 835581f

Browse files
authored
vNxM.load_splat instructions (#2350)
Introduces a new instruction class, `SIMDLoad`. Implements encoding, decoding, parsing, printing, and interpretation of the load and splat instructions, including in the C and JS APIs. `v128.load` remains in the `Load` instruction class for now because the interpreter code expects a `Load` to be able to load any memory value type.
1 parent fb217c8 commit 835581f

37 files changed

Lines changed: 1041 additions & 398 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ full changeset diff at the end of each section.
1515
Current Trunk
1616
-------------
1717

18+
- Added load_splat SIMD instructions
1819
- Binaryen.js instruction API changes:
1920
- `notify` -> `atomic.notify`
2021
- `i32.wait` / `i64.wait` -> `i32.atomic.wait` / `i64.atomic.wait`

Contributing.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,25 @@ Interested in participating? Please follow
66
[the same contributing guidelines as the design repository]: https://github.com/WebAssembly/design/blob/master/Contributing.md
77

88
Also, please be sure to read [the README.md](README.md) for this repository.
9+
10+
## Adding support for new instructions
11+
12+
Use this handy checklist to make sure your new instructions are fully supported:
13+
14+
- [ ] Instruction class or opcode added to src/wasm.h
15+
- [ ] Instruction class added to src/wasm-builder.h
16+
- [ ] Instruction class added to src/wasm-traversal.h
17+
- [ ] Validation added to src/wasm/wasm-validator.cpp
18+
- [ ] Interpretation added to src/wasm-interpreter.h
19+
- [ ] Effects handled in src/ir/effects.h
20+
- [ ] Hashing and comparing in src/ir/ExpressionAnalyzer.cpp
21+
- [ ] Parsing added in scripts/gen-s-parser.py and src/wasm/wasm-s-parser.cpp
22+
- [ ] Printing added in src/passes/Print.cpp
23+
- [ ] Decoding added in src/wasm/wasm-binary.cpp
24+
- [ ] Binary writing added in src/wasm-stack.h and src/wasm/wasm-stack.cpp
25+
- [ ] Support added to src/tools/fuzzing.h
26+
- [ ] C API support added in src/binaryen-c.h and src/binaryen-c.cpp
27+
- [ ] JS API support added in build-js.sh and src/js/binaryen.js-post.js
28+
- [ ] C API tested in test/example/c-api-kitchen-sink.c
29+
- [ ] JS API tested in test/binaryen.js/kitchen-sink.js
30+
- [ ] Tests added in test/spec

build-js.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ export_function "_BinaryenSIMDReplaceId"
225225
export_function "_BinaryenSIMDShuffleId"
226226
export_function "_BinaryenSIMDTernaryId"
227227
export_function "_BinaryenSIMDShiftId"
228+
export_function "_BinaryenSIMDLoadId"
228229
export_function "_BinaryenMemoryInitId"
229230
export_function "_BinaryenDataDropId"
230231
export_function "_BinaryenMemoryCopyId"
@@ -550,6 +551,10 @@ export_function "_BinaryenConvertSVecI32x4ToVecF32x4"
550551
export_function "_BinaryenConvertUVecI32x4ToVecF32x4"
551552
export_function "_BinaryenConvertSVecI64x2ToVecF64x2"
552553
export_function "_BinaryenConvertUVecI64x2ToVecF64x2"
554+
export_function "_BinaryenLoadSplatVec8x16"
555+
export_function "_BinaryenLoadSplatVec16x8"
556+
export_function "_BinaryenLoadSplatVec32x4"
557+
export_function "_BinaryenLoadSplatVec64x2"
553558
export_function "_BinaryenNarrowSVecI16x8ToVecI8x16"
554559
export_function "_BinaryenNarrowUVecI16x8ToVecI8x16"
555560
export_function "_BinaryenNarrowSVecI32x4ToVecI16x8"
@@ -601,6 +606,7 @@ export_function "_BinaryenSIMDReplace"
601606
export_function "_BinaryenSIMDShuffle"
602607
export_function "_BinaryenSIMDTernary"
603608
export_function "_BinaryenSIMDShift"
609+
export_function "_BinaryenSIMDLoad"
604610
export_function "_BinaryenMemoryInit"
605611
export_function "_BinaryenDataDrop"
606612
export_function "_BinaryenMemoryCopy"
@@ -772,6 +778,12 @@ export_function "_BinaryenSIMDShiftGetOp"
772778
export_function "_BinaryenSIMDShiftGetVec"
773779
export_function "_BinaryenSIMDShiftGetShift"
774780

781+
# 'SIMDLoad' expression operations
782+
export_function "_BinaryenSIMDLoadGetOp"
783+
export_function "_BinaryenSIMDLoadGetOffset"
784+
export_function "_BinaryenSIMDLoadGetAlign"
785+
export_function "_BinaryenSIMDLoadGetPtr"
786+
775787
# 'MemoryInit' expression operations
776788
export_function "_BinaryenMemoryInitGetSegment"
777789
export_function "_BinaryenMemoryInitGetDest"

scripts/gen-s-parser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@
429429
("f32x4.convert_i32x4_u", "makeUnary(s, UnaryOp::ConvertUVecI32x4ToVecF32x4)"),
430430
("f64x2.convert_i64x2_s", "makeUnary(s, UnaryOp::ConvertSVecI64x2ToVecF64x2)"),
431431
("f64x2.convert_i64x2_u", "makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2)"),
432+
("v8x16.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec8x16)"),
433+
("v16x8.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec16x8)"),
434+
("v32x4.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec32x4)"),
435+
("v64x2.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec64x2)"),
432436
("i8x16.narrow_i16x8_s", "makeBinary(s, BinaryOp::NarrowSVecI16x8ToVecI8x16)"),
433437
("i8x16.narrow_i16x8_u", "makeBinary(s, BinaryOp::NarrowUVecI16x8ToVecI8x16)"),
434438
("i16x8.narrow_i32x4_s", "makeBinary(s, BinaryOp::NarrowSVecI32x4ToVecI16x8)"),

src/binaryen-c.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,9 @@ BinaryenExpressionId BinaryenSIMDTernaryId(void) {
349349
BinaryenExpressionId BinaryenSIMDShiftId(void) {
350350
return Expression::Id::SIMDShiftId;
351351
}
352+
BinaryenExpressionId BinaryenSIMDLoadId(void) {
353+
return Expression::Id::SIMDLoadId;
354+
}
352355
BinaryenExpressionId BinaryenMemoryInitId(void) {
353356
return Expression::Id::MemoryInitId;
354357
}
@@ -880,6 +883,10 @@ BinaryenOp BinaryenConvertSVecI64x2ToVecF64x2(void) {
880883
BinaryenOp BinaryenConvertUVecI64x2ToVecF64x2(void) {
881884
return ConvertUVecI64x2ToVecF64x2;
882885
}
886+
BinaryenOp BinaryenLoadSplatVec8x16(void) { return LoadSplatVec8x16; }
887+
BinaryenOp BinaryenLoadSplatVec16x8(void) { return LoadSplatVec16x8; }
888+
BinaryenOp BinaryenLoadSplatVec32x4(void) { return LoadSplatVec32x4; }
889+
BinaryenOp BinaryenLoadSplatVec64x2(void) { return LoadSplatVec64x2; }
883890
BinaryenOp BinaryenNarrowSVecI16x8ToVecI8x16(void) {
884891
return NarrowSVecI16x8ToVecI8x16;
885892
}
@@ -1599,6 +1606,20 @@ BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module,
15991606
}
16001607
return static_cast<Expression*>(ret);
16011608
}
1609+
BinaryenExpressionRef BinaryenSIMDLoad(BinaryenModuleRef module,
1610+
BinaryenOp op,
1611+
uint32_t offset,
1612+
uint32_t align,
1613+
BinaryenExpressionRef ptr) {
1614+
auto* ret =
1615+
Builder(*(Module*)module)
1616+
.makeSIMDLoad(
1617+
SIMDLoadOp(op), Address(offset), Address(align), (Expression*)ptr);
1618+
if (tracing) {
1619+
traceExpression(ret, "BinaryenSIMDLoad", op, offset, align, ptr);
1620+
}
1621+
return static_cast<Expression*>(ret);
1622+
}
16021623
BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module,
16031624
uint32_t segment,
16041625
BinaryenExpressionRef dest,
@@ -2767,6 +2788,47 @@ BinaryenExpressionRef BinaryenSIMDShiftGetShift(BinaryenExpressionRef expr) {
27672788
assert(expression->is<SIMDShift>());
27682789
return static_cast<SIMDShift*>(expression)->shift;
27692790
}
2791+
// SIMDLoad
2792+
BinaryenOp BinaryenSIMDLoadGetOp(BinaryenExpressionRef expr) {
2793+
if (tracing) {
2794+
std::cout << " BinaryenSIMDLoadGetOp(expressions[" << expressions[expr]
2795+
<< "])\n";
2796+
}
2797+
2798+
auto* expression = (Expression*)expr;
2799+
assert(expression->is<SIMDLoad>());
2800+
return static_cast<SIMDLoad*>(expression)->op;
2801+
}
2802+
uint32_t BinaryenSIMDLoadGetOffset(BinaryenExpressionRef expr) {
2803+
if (tracing) {
2804+
std::cout << " BinaryenSIMDLoadGetOffset(expressions[" << expressions[expr]
2805+
<< "])\n";
2806+
}
2807+
2808+
auto* expression = (Expression*)expr;
2809+
assert(expression->is<SIMDLoad>());
2810+
return static_cast<SIMDLoad*>(expression)->offset;
2811+
}
2812+
uint32_t BinaryenSIMDLoadGetAlign(BinaryenExpressionRef expr) {
2813+
if (tracing) {
2814+
std::cout << " BinaryenSIMDLoadGetAlign(expressions[" << expressions[expr]
2815+
<< "])\n";
2816+
}
2817+
2818+
auto* expression = (Expression*)expr;
2819+
assert(expression->is<SIMDLoad>());
2820+
return static_cast<SIMDLoad*>(expression)->align;
2821+
}
2822+
BinaryenExpressionRef BinaryenSIMDLoadGetPtr(BinaryenExpressionRef expr) {
2823+
if (tracing) {
2824+
std::cout << " BinaryenSIMDLoadGetPtr(expressions[" << expressions[expr]
2825+
<< "])\n";
2826+
}
2827+
2828+
auto* expression = (Expression*)expr;
2829+
assert(expression->is<SIMDLoad>());
2830+
return static_cast<SIMDLoad*>(expression)->ptr;
2831+
}
27702832
// MemoryInit
27712833
uint32_t BinaryenMemoryInitGetSegment(BinaryenExpressionRef expr) {
27722834
if (tracing) {

src/binaryen-c.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ BINARYEN_API BinaryenExpressionId BinaryenSIMDReplaceId(void);
131131
BINARYEN_API BinaryenExpressionId BinaryenSIMDShuffleId(void);
132132
BINARYEN_API BinaryenExpressionId BinaryenSIMDTernaryId(void);
133133
BINARYEN_API BinaryenExpressionId BinaryenSIMDShiftId(void);
134+
BINARYEN_API BinaryenExpressionId BinaryenSIMDLoadId(void);
134135
BINARYEN_API BinaryenExpressionId BinaryenMemoryInitId(void);
135136
BINARYEN_API BinaryenExpressionId BinaryenDataDropId(void);
136137
BINARYEN_API BinaryenExpressionId BinaryenMemoryCopyId(void);
@@ -522,6 +523,10 @@ BINARYEN_API BinaryenOp BinaryenConvertSVecI32x4ToVecF32x4(void);
522523
BINARYEN_API BinaryenOp BinaryenConvertUVecI32x4ToVecF32x4(void);
523524
BINARYEN_API BinaryenOp BinaryenConvertSVecI64x2ToVecF64x2(void);
524525
BINARYEN_API BinaryenOp BinaryenConvertUVecI64x2ToVecF64x2(void);
526+
BINARYEN_API BinaryenOp BinaryenLoadSplatVec8x16(void);
527+
BINARYEN_API BinaryenOp BinaryenLoadSplatVec16x8(void);
528+
BINARYEN_API BinaryenOp BinaryenLoadSplatVec32x4(void);
529+
BINARYEN_API BinaryenOp BinaryenLoadSplatVec64x2(void);
525530
BINARYEN_API BinaryenOp BinaryenNarrowSVecI16x8ToVecI8x16(void);
526531
BINARYEN_API BinaryenOp BinaryenNarrowUVecI16x8ToVecI8x16(void);
527532
BINARYEN_API BinaryenOp BinaryenNarrowSVecI32x4ToVecI16x8(void);
@@ -736,6 +741,11 @@ BinaryenSIMDShift(BinaryenModuleRef module,
736741
BinaryenOp op,
737742
BinaryenExpressionRef vec,
738743
BinaryenExpressionRef shift);
744+
BINARYEN_API BinaryenExpressionRef BinaryenSIMDLoad(BinaryenModuleRef module,
745+
BinaryenOp op,
746+
uint32_t offset,
747+
uint32_t align,
748+
BinaryenExpressionRef ptr);
739749
BINARYEN_API BinaryenExpressionRef
740750
BinaryenMemoryInit(BinaryenModuleRef module,
741751
uint32_t segment,
@@ -962,6 +972,12 @@ BinaryenSIMDShiftGetVec(BinaryenExpressionRef expr);
962972
BINARYEN_API BinaryenExpressionRef
963973
BinaryenSIMDShiftGetShift(BinaryenExpressionRef expr);
964974

975+
BINARYEN_API BinaryenOp BinaryenSIMDLoadGetOp(BinaryenExpressionRef expr);
976+
BINARYEN_API uint32_t BinaryenSIMDLoadGetOffset(BinaryenExpressionRef expr);
977+
BINARYEN_API uint32_t BinaryenSIMDLoadGetAlign(BinaryenExpressionRef expr);
978+
BINARYEN_API BinaryenExpressionRef
979+
BinaryenSIMDLoadGetPtr(BinaryenExpressionRef expr);
980+
965981
BINARYEN_API uint32_t BinaryenMemoryInitGetSegment(BinaryenExpressionRef expr);
966982
BINARYEN_API BinaryenExpressionRef
967983
BinaryenMemoryInitGetDest(BinaryenExpressionRef expr);

src/gen-s-parser.inc

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2418,40 +2418,62 @@ switch (op[0]) {
24182418
case 'v': {
24192419
switch (op[1]) {
24202420
case '1': {
2421-
switch (op[5]) {
2422-
case 'a':
2423-
if (strcmp(op, "v128.and") == 0) { return makeBinary(s, BinaryOp::AndVec128); }
2424-
goto parse_error;
2425-
case 'b':
2426-
if (strcmp(op, "v128.bitselect") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::Bitselect); }
2427-
goto parse_error;
2428-
case 'c':
2429-
if (strcmp(op, "v128.const") == 0) { return makeConst(s, v128); }
2421+
switch (op[2]) {
2422+
case '2': {
2423+
switch (op[5]) {
2424+
case 'a':
2425+
if (strcmp(op, "v128.and") == 0) { return makeBinary(s, BinaryOp::AndVec128); }
2426+
goto parse_error;
2427+
case 'b':
2428+
if (strcmp(op, "v128.bitselect") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::Bitselect); }
2429+
goto parse_error;
2430+
case 'c':
2431+
if (strcmp(op, "v128.const") == 0) { return makeConst(s, v128); }
2432+
goto parse_error;
2433+
case 'l':
2434+
if (strcmp(op, "v128.load") == 0) { return makeLoad(s, v128, /*isAtomic=*/false); }
2435+
goto parse_error;
2436+
case 'n':
2437+
if (strcmp(op, "v128.not") == 0) { return makeUnary(s, UnaryOp::NotVec128); }
2438+
goto parse_error;
2439+
case 'o':
2440+
if (strcmp(op, "v128.or") == 0) { return makeBinary(s, BinaryOp::OrVec128); }
2441+
goto parse_error;
2442+
case 'p':
2443+
if (strcmp(op, "v128.pop") == 0) { return makePop(v128); }
2444+
goto parse_error;
2445+
case 's':
2446+
if (strcmp(op, "v128.store") == 0) { return makeStore(s, v128, /*isAtomic=*/false); }
2447+
goto parse_error;
2448+
case 'x':
2449+
if (strcmp(op, "v128.xor") == 0) { return makeBinary(s, BinaryOp::XorVec128); }
2450+
goto parse_error;
2451+
default: goto parse_error;
2452+
}
2453+
}
2454+
case '6':
2455+
if (strcmp(op, "v16x8.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec16x8); }
24302456
goto parse_error;
2457+
default: goto parse_error;
2458+
}
2459+
}
2460+
case '3':
2461+
if (strcmp(op, "v32x4.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec32x4); }
2462+
goto parse_error;
2463+
case '6':
2464+
if (strcmp(op, "v64x2.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec64x2); }
2465+
goto parse_error;
2466+
case '8': {
2467+
switch (op[6]) {
24312468
case 'l':
2432-
if (strcmp(op, "v128.load") == 0) { return makeLoad(s, v128, /*isAtomic=*/false); }
2433-
goto parse_error;
2434-
case 'n':
2435-
if (strcmp(op, "v128.not") == 0) { return makeUnary(s, UnaryOp::NotVec128); }
2436-
goto parse_error;
2437-
case 'o':
2438-
if (strcmp(op, "v128.or") == 0) { return makeBinary(s, BinaryOp::OrVec128); }
2439-
goto parse_error;
2440-
case 'p':
2441-
if (strcmp(op, "v128.pop") == 0) { return makePop(v128); }
2469+
if (strcmp(op, "v8x16.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec8x16); }
24422470
goto parse_error;
24432471
case 's':
2444-
if (strcmp(op, "v128.store") == 0) { return makeStore(s, v128, /*isAtomic=*/false); }
2445-
goto parse_error;
2446-
case 'x':
2447-
if (strcmp(op, "v128.xor") == 0) { return makeBinary(s, BinaryOp::XorVec128); }
2472+
if (strcmp(op, "v8x16.shuffle") == 0) { return makeSIMDShuffle(s); }
24482473
goto parse_error;
24492474
default: goto parse_error;
24502475
}
24512476
}
2452-
case '8':
2453-
if (strcmp(op, "v8x16.shuffle") == 0) { return makeSIMDShuffle(s); }
2454-
goto parse_error;
24552477
default: goto parse_error;
24562478
}
24572479
}

src/ir/ExpressionAnalyzer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ template<typename T> void visitImmediates(Expression* curr, T& visitor) {
196196
}
197197
void visitSIMDTernary(SIMDTernary* curr) { visitor.visitInt(curr->op); }
198198
void visitSIMDShift(SIMDShift* curr) { visitor.visitInt(curr->op); }
199+
void visitSIMDLoad(SIMDLoad* curr) {
200+
visitor.visitInt(curr->op);
201+
visitor.visitAddress(curr->offset);
202+
visitor.visitAddress(curr->align);
203+
}
199204
void visitMemoryInit(MemoryInit* curr) {
200205
visitor.visitIndex(curr->segment);
201206
}

src/ir/ExpressionManipulator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
179179
return builder.makeSIMDShift(
180180
curr->op, copy(curr->vec), copy(curr->shift));
181181
}
182+
Expression* visitSIMDLoad(SIMDLoad* curr) {
183+
return builder.makeSIMDLoad(
184+
curr->op, curr->offset, curr->align, copy(curr->ptr));
185+
}
182186
Expression* visitConst(Const* curr) {
183187
return builder.makeConst(curr->value);
184188
}

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ void ReFinalize::visitSIMDReplace(SIMDReplace* curr) { curr->finalize(); }
147147
void ReFinalize::visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); }
148148
void ReFinalize::visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); }
149149
void ReFinalize::visitSIMDShift(SIMDShift* curr) { curr->finalize(); }
150+
void ReFinalize::visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); }
150151
void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); }
151152
void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); }
152153
void ReFinalize::visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); }

0 commit comments

Comments
 (0)