Skip to content

Implement v128.{load,store}{8,16,32,64}_lane instructions #3278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@
("v128.xor", "makeBinary(s, BinaryOp::XorVec128)"),
("v128.andnot", "makeBinary(s, BinaryOp::AndNotVec128)"),
("v128.bitselect", "makeSIMDTernary(s, SIMDTernaryOp::Bitselect)"),
("v128.load8_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec8x16)"),
("v128.load16_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec16x8)"),
("v128.load32_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec32x4)"),
("v128.load64_lane", "makeSIMDLoadStoreLane(s, LoadLaneVec64x2)"),
("v128.store8_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec8x16)"),
("v128.store16_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec16x8)"),
("v128.store32_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec32x4)"),
("v128.store64_lane", "makeSIMDLoadStoreLane(s, StoreLaneVec64x2)"),
("i8x16.abs", "makeUnary(s, UnaryOp::AbsVecI8x16)"),
("i8x16.neg", "makeUnary(s, UnaryOp::NegVecI8x16)"),
("i8x16.any_true", "makeUnary(s, UnaryOp::AnyTrueVecI8x16)"),
Expand Down
1 change: 1 addition & 0 deletions src/binaryen-c.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ BINARYEN_API BinaryenExpressionId BinaryenSIMDShuffleId(void);
BINARYEN_API BinaryenExpressionId BinaryenSIMDTernaryId(void);
BINARYEN_API BinaryenExpressionId BinaryenSIMDShiftId(void);
BINARYEN_API BinaryenExpressionId BinaryenSIMDLoadId(void);
// TODO: Expose SIMDLoadStoreLane in C and JS APIs
BINARYEN_API BinaryenExpressionId BinaryenMemoryInitId(void);
BINARYEN_API BinaryenExpressionId BinaryenDataDropId(void);
BINARYEN_API BinaryenExpressionId BinaryenMemoryCopyId(void);
Expand Down
53 changes: 46 additions & 7 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2776,11 +2776,33 @@ switch (op[0]) {
case '\0':
if (strcmp(op, "v128.load") == 0) { return makeLoad(s, Type::v128, /*isAtomic=*/false); }
goto parse_error;
case '3':
if (strcmp(op, "v128.load32_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load32Zero); }
case '1':
if (strcmp(op, "v128.load16_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec16x8); }
goto parse_error;
case '6':
if (strcmp(op, "v128.load64_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load64Zero); }
case '3': {
switch (op[12]) {
case 'l':
if (strcmp(op, "v128.load32_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec32x4); }
goto parse_error;
case 'z':
if (strcmp(op, "v128.load32_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load32Zero); }
goto parse_error;
default: goto parse_error;
}
}
case '6': {
switch (op[12]) {
case 'l':
if (strcmp(op, "v128.load64_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec64x2); }
goto parse_error;
case 'z':
if (strcmp(op, "v128.load64_zero") == 0) { return makeSIMDLoad(s, SIMDLoadOp::Load64Zero); }
goto parse_error;
default: goto parse_error;
}
}
case '8':
if (strcmp(op, "v128.load8_lane") == 0) { return makeSIMDLoadStoreLane(s, LoadLaneVec8x16); }
goto parse_error;
default: goto parse_error;
}
Expand All @@ -2791,9 +2813,26 @@ switch (op[0]) {
case 'o':
if (strcmp(op, "v128.or") == 0) { return makeBinary(s, BinaryOp::OrVec128); }
goto parse_error;
case 's':
if (strcmp(op, "v128.store") == 0) { return makeStore(s, Type::v128, /*isAtomic=*/false); }
goto parse_error;
case 's': {
switch (op[10]) {
case '\0':
if (strcmp(op, "v128.store") == 0) { return makeStore(s, Type::v128, /*isAtomic=*/false); }
goto parse_error;
case '1':
if (strcmp(op, "v128.store16_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec16x8); }
goto parse_error;
case '3':
if (strcmp(op, "v128.store32_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec32x4); }
goto parse_error;
case '6':
if (strcmp(op, "v128.store64_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec64x2); }
goto parse_error;
case '8':
if (strcmp(op, "v128.store8_lane") == 0) { return makeSIMDLoadStoreLane(s, StoreLaneVec8x16); }
goto parse_error;
default: goto parse_error;
}
}
case 'x':
if (strcmp(op, "v128.xor") == 0) { return makeBinary(s, BinaryOp::XorVec128); }
goto parse_error;
Expand Down
6 changes: 6 additions & 0 deletions src/ir/ExpressionAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ template<typename T> void visitImmediates(Expression* curr, T& visitor) {
visitor.visitAddress(curr->offset);
visitor.visitAddress(curr->align);
}
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
visitor.visitInt(curr->op);
visitor.visitAddress(curr->offset);
visitor.visitAddress(curr->align);
visitor.visitInt(curr->index);
}
void visitMemoryInit(MemoryInit* curr) {
visitor.visitIndex(curr->segment);
}
Expand Down
8 changes: 8 additions & 0 deletions src/ir/ExpressionManipulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) {
return builder.makeSIMDLoad(
curr->op, curr->offset, curr->align, copy(curr->ptr));
}
Expression* visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
return builder.makeSIMDLoadStoreLane(curr->op,
curr->offset,
curr->align,
curr->index,
copy(curr->ptr),
copy(curr->vec));
}
Expression* visitConst(Const* curr) {
return builder.makeConst(curr->value);
}
Expand Down
3 changes: 3 additions & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ void ReFinalize::visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); }
void ReFinalize::visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); }
void ReFinalize::visitSIMDShift(SIMDShift* curr) { curr->finalize(); }
void ReFinalize::visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); }
void ReFinalize::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
curr->finalize();
}
void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); }
void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); }
void ReFinalize::visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); }
Expand Down
10 changes: 10 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,16 @@ struct EffectAnalyzer
implicitTrap = true;
}
}
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
if (curr->isLoad()) {
readsMemory = true;
} else {
writesMemory = true;
}
if (!ignoreImplicitTraps) {
implicitTrap = true;
}
}
void visitMemoryInit(MemoryInit* curr) {
writesMemory = true;
if (!ignoreImplicitTraps) {
Expand Down
2 changes: 2 additions & 0 deletions src/ir/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ struct ReFinalize
void visitSIMDTernary(SIMDTernary* curr);
void visitSIMDShift(SIMDShift* curr);
void visitSIMDLoad(SIMDLoad* curr);
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr);
void visitMemoryInit(MemoryInit* curr);
void visitDataDrop(DataDrop* curr);
void visitMemoryCopy(MemoryCopy* curr);
Expand Down Expand Up @@ -219,6 +220,7 @@ struct ReFinalizeNode : public OverriddenVisitor<ReFinalizeNode> {
void visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); }
void visitSIMDShift(SIMDShift* curr) { curr->finalize(); }
void visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); }
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) { curr->finalize(); }
void visitMemoryInit(MemoryInit* curr) { curr->finalize(); }
void visitDataDrop(DataDrop* curr) { curr->finalize(); }
void visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); }
Expand Down
2 changes: 2 additions & 0 deletions src/passes/DeadCodeElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@ struct DeadCodeElimination
DELEGATE(SIMDShift);
case Expression::Id::SIMDLoadId:
DELEGATE(SIMDLoad);
case Expression::Id::SIMDLoadStoreLaneId:
DELEGATE(SIMDLoadStoreLane);
case Expression::Id::MemoryInitId:
DELEGATE(MemoryInit);
case Expression::Id::DataDropId:
Expand Down
45 changes: 45 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,43 @@ struct PrintExpressionContents
o << " align=" << curr->align;
}
}
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
prepareColor(o);
switch (curr->op) {
case LoadLaneVec8x16:
o << "v128.load8_lane";
break;
case LoadLaneVec16x8:
o << "v128.load16_lane";
break;
case LoadLaneVec32x4:
o << "v128.load32_lane";
break;
case LoadLaneVec64x2:
o << "v128.load64_lane";
break;
case StoreLaneVec8x16:
o << "v128.store8_lane";
break;
case StoreLaneVec16x8:
o << "v128.store16_lane";
break;
case StoreLaneVec32x4:
o << "v128.store32_lane";
break;
case StoreLaneVec64x2:
o << "v128.store64_lane";
break;
}
restoreNormalColor(o);
if (curr->offset) {
o << " offset=" << curr->offset;
}
if (curr->align != curr->getMemBytes()) {
o << " align=" << curr->align;
}
o << " " << int(curr->index);
}
void visitMemoryInit(MemoryInit* curr) {
prepareColor(o);
o << "memory.init " << curr->segment;
Expand Down Expand Up @@ -1908,6 +1945,14 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
printFullLine(curr->ptr);
decIndent();
}
void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
incIndent();
printFullLine(curr->ptr);
printFullLine(curr->vec);
decIndent();
}
void visitMemoryInit(MemoryInit* curr) {
o << '(';
PrintExpressionContents(currFunction, o).visit(curr);
Expand Down
1 change: 1 addition & 0 deletions src/tools/fuzzing.h
Original file line number Diff line number Diff line change
Expand Up @@ -2465,6 +2465,7 @@ class TranslateToFuzzReader {
if (type != Type::v128) {
return makeSIMDExtract(type);
}
// TODO: Add SIMDLoadStoreLane once it is generally available
switch (upTo(7)) {
case 0:
return makeUnary(Type::v128);
Expand Down
10 changes: 10 additions & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,15 @@ enum ASTNodes {
V128Xor = 0x51,
V128Bitselect = 0x52,

V128Load8Lane = 0x58,
V128Load16Lane = 0x59,
V128Load32Lane = 0x5a,
V128Load64Lane = 0x5b,
V128Store8Lane = 0x5c,
V128Store16Lane = 0x5d,
V128Store32Lane = 0x5e,
V128Store64Lane = 0x5f,

I8x16Abs = 0x60,
I8x16Neg = 0x61,
I8x16AnyTrue = 0x62,
Expand Down Expand Up @@ -1486,6 +1495,7 @@ class WasmBinaryBuilder {
bool maybeVisitSIMDTernary(Expression*& out, uint32_t code);
bool maybeVisitSIMDShift(Expression*& out, uint32_t code);
bool maybeVisitSIMDLoad(Expression*& out, uint32_t code);
bool maybeVisitSIMDLoadStoreLane(Expression*& out, uint32_t code);
bool maybeVisitMemoryInit(Expression*& out, uint32_t code);
bool maybeVisitDataDrop(Expression*& out, uint32_t code);
bool maybeVisitMemoryCopy(Expression*& out, uint32_t code);
Expand Down
16 changes: 16 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,22 @@ class Builder {
ret->finalize();
return ret;
}
SIMDLoadStoreLane* makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op,
Address offset,
Address align,
uint8_t index,
Expression* ptr,
Expression* vec) {
auto* ret = wasm.allocator.alloc<SIMDLoadStoreLane>();
ret->op = op;
ret->offset = offset;
ret->align = align;
ret->index = index;
ret->ptr = ptr;
ret->vec = vec;
ret->finalize();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this does not assign vec?

(I wonder if an automatic code generation tool could help in the future here... 🤔)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! Yes, it certainly would.

return ret;
}
MemoryInit* makeMemoryInit(uint32_t segment,
Expression* dest,
Expression* offset,
Expand Down
79 changes: 78 additions & 1 deletion src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,9 @@ class ExpressionRunner : public OverriddenVisitor<SubType, Flow> {
Flow visitSIMDLoadSplat(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitSIMDLoadExtend(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitSIMDLoadZero(SIMDLoad* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
WASM_UNREACHABLE("unimp");
}
Flow visitPop(Pop* curr) { WASM_UNREACHABLE("unimp"); }
Flow visitRefNull(RefNull* curr) {
NOTE_ENTER("RefNull");
Expand Down Expand Up @@ -1627,6 +1630,10 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
NOTE_ENTER("SIMDLoadExtend");
return Flow(NONCONSTANT_FLOW);
}
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
NOTE_ENTER("SIMDLoadStoreLane");
return Flow(NONCONSTANT_FLOW);
}
Flow visitPop(Pop* curr) {
NOTE_ENTER("Pop");
return Flow(NONCONSTANT_FLOW);
Expand Down Expand Up @@ -2369,7 +2376,7 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
}
NOTE_EVAL1(flow);
Address src = instance.getFinalAddress(
curr, flow.getSingleValue(), curr->op == Load32Zero ? 32 : 64);
curr, flow.getSingleValue(), curr->getMemBytes());
auto zero =
Literal::makeZero(curr->op == Load32Zero ? Type::i32 : Type::i64);
if (curr->op == Load32Zero) {
Expand All @@ -2380,6 +2387,76 @@ template<typename GlobalManager, typename SubType> class ModuleInstanceBase {
return Literal(std::array<Literal, 2>{{val, zero}});
}
}
Flow visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
NOTE_ENTER("SIMDLoadStoreLane");
Flow flow = this->visit(curr->ptr);
if (flow.breaking()) {
return flow;
}
NOTE_EVAL1(flow);
Address addr = instance.getFinalAddress(
curr, flow.getSingleValue(), curr->getMemBytes());
flow = this->visit(curr->vec);
if (flow.breaking()) {
return flow;
}
Literal vec = flow.getSingleValue();
switch (curr->op) {
case LoadLaneVec8x16:
case StoreLaneVec8x16: {
std::array<Literal, 16> lanes = vec.getLanesUI8x16();
if (curr->isLoad()) {
lanes[curr->index] =
Literal(instance.externalInterface->load8u(addr));
return Literal(lanes);
} else {
instance.externalInterface->store8(addr,
lanes[curr->index].geti32());
return {};
}
}
case LoadLaneVec16x8:
case StoreLaneVec16x8: {
std::array<Literal, 8> lanes = vec.getLanesUI16x8();
if (curr->isLoad()) {
lanes[curr->index] =
Literal(instance.externalInterface->load16u(addr));
return Literal(lanes);
} else {
instance.externalInterface->store16(addr,
lanes[curr->index].geti32());
return {};
}
}
case LoadLaneVec32x4:
case StoreLaneVec32x4: {
std::array<Literal, 4> lanes = vec.getLanesI32x4();
if (curr->isLoad()) {
lanes[curr->index] =
Literal(instance.externalInterface->load32u(addr));
return Literal(lanes);
} else {
instance.externalInterface->store32(addr,
lanes[curr->index].geti32());
return {};
}
}
case StoreLaneVec64x2:
case LoadLaneVec64x2: {
std::array<Literal, 2> lanes = vec.getLanesI64x2();
if (curr->isLoad()) {
lanes[curr->index] =
Literal(instance.externalInterface->load64u(addr));
return Literal(lanes);
} else {
instance.externalInterface->store64(addr,
lanes[curr->index].geti64());
return {};
}
}
}
WASM_UNREACHABLE("unexpected op");
}
Flow visitMemorySize(MemorySize* curr) {
NOTE_ENTER("MemorySize");
return Literal::makeFromInt64(instance.memorySize,
Expand Down
1 change: 1 addition & 0 deletions src/wasm-s-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class SExpressionWasmBuilder {
Expression* makeSIMDTernary(Element& s, SIMDTernaryOp op);
Expression* makeSIMDShift(Element& s, SIMDShiftOp op);
Expression* makeSIMDLoad(Element& s, SIMDLoadOp op);
Expression* makeSIMDLoadStoreLane(Element& s, SIMDLoadStoreLaneOp op);
Expression* makeMemoryInit(Element& s);
Expression* makeDataDrop(Element& s);
Expression* makeMemoryCopy(Element& s);
Expand Down
Loading