summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.h1
-rw-r--r--src/gen-s-parser.inc53
-rw-r--r--src/ir/ExpressionAnalyzer.cpp6
-rw-r--r--src/ir/ExpressionManipulator.cpp8
-rw-r--r--src/ir/ReFinalize.cpp3
-rw-r--r--src/ir/effects.h10
-rw-r--r--src/ir/utils.h2
-rw-r--r--src/passes/DeadCodeElimination.cpp2
-rw-r--r--src/passes/Print.cpp45
-rw-r--r--src/tools/fuzzing.h1
-rw-r--r--src/wasm-binary.h10
-rw-r--r--src/wasm-builder.h16
-rw-r--r--src/wasm-interpreter.h79
-rw-r--r--src/wasm-s-parser.h1
-rw-r--r--src/wasm-stack.h1
-rw-r--r--src/wasm-traversal.h15
-rw-r--r--src/wasm.h33
-rw-r--r--src/wasm/wasm-binary.cpp54
-rw-r--r--src/wasm/wasm-s-parser.cpp43
-rw-r--r--src/wasm/wasm-stack.cpp33
-rw-r--r--src/wasm/wasm-validator.cpp54
-rw-r--r--src/wasm/wasm.cpp44
-rw-r--r--src/wasm2js.h4
23 files changed, 509 insertions, 9 deletions
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 8713d8f63..ecf0027f3 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -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);
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index 8a2c9163c..2bbcde499 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -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;
}
@@ -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;
diff --git a/src/ir/ExpressionAnalyzer.cpp b/src/ir/ExpressionAnalyzer.cpp
index 9b0bdd007..043fb259b 100644
--- a/src/ir/ExpressionAnalyzer.cpp
+++ b/src/ir/ExpressionAnalyzer.cpp
@@ -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);
}
diff --git a/src/ir/ExpressionManipulator.cpp b/src/ir/ExpressionManipulator.cpp
index 5fb98d86a..648a872fc 100644
--- a/src/ir/ExpressionManipulator.cpp
+++ b/src/ir/ExpressionManipulator.cpp
@@ -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);
}
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index c3516be0a..19fed54a7 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -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(); }
diff --git a/src/ir/effects.h b/src/ir/effects.h
index dc6c10645..1a713f944 100644
--- a/src/ir/effects.h
+++ b/src/ir/effects.h
@@ -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) {
diff --git a/src/ir/utils.h b/src/ir/utils.h
index c2d7645f7..cd50aa1cc 100644
--- a/src/ir/utils.h
+++ b/src/ir/utils.h
@@ -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);
@@ -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(); }
diff --git a/src/passes/DeadCodeElimination.cpp b/src/passes/DeadCodeElimination.cpp
index 067bc81e1..a17abfd90 100644
--- a/src/passes/DeadCodeElimination.cpp
+++ b/src/passes/DeadCodeElimination.cpp
@@ -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:
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index a8e5b0f9c..42dccb4bf 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -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;
@@ -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);
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index 36cf1fb42..d735e48c9 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -2465,6 +2465,7 @@ private:
if (type != Type::v128) {
return makeSIMDExtract(type);
}
+ // TODO: Add SIMDLoadStoreLane once it is generally available
switch (upTo(7)) {
case 0:
return makeUnary(Type::v128);
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 8c502d6b7..1faf30716 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -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,
@@ -1486,6 +1495,7 @@ public:
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);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 97877a0a1..77e2692f6 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -434,6 +434,22 @@ public:
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();
+ return ret;
+ }
MemoryInit* makeMemoryInit(uint32_t segment,
Expression* dest,
Expression* offset,
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 203786e72..5ca590208 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -1240,6 +1240,9 @@ public:
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");
@@ -1627,6 +1630,10 @@ public:
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);
@@ -2369,7 +2376,7 @@ private:
}
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) {
@@ -2380,6 +2387,76 @@ private:
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,
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index b6dcc058c..ac1b6d0e5 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -212,6 +212,7 @@ private:
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);
diff --git a/src/wasm-stack.h b/src/wasm-stack.h
index d4d04f850..951e06daa 100644
--- a/src/wasm-stack.h
+++ b/src/wasm-stack.h
@@ -123,6 +123,7 @@ public:
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);
diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h
index b32babf74..25988598a 100644
--- a/src/wasm-traversal.h
+++ b/src/wasm-traversal.h
@@ -61,6 +61,9 @@ template<typename SubType, typename ReturnType = void> struct Visitor {
ReturnType visitSIMDTernary(SIMDTernary* curr) { return ReturnType(); }
ReturnType visitSIMDShift(SIMDShift* curr) { return ReturnType(); }
ReturnType visitSIMDLoad(SIMDLoad* curr) { return ReturnType(); }
+ ReturnType visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
+ return ReturnType();
+ }
ReturnType visitMemoryInit(MemoryInit* curr) { return ReturnType(); }
ReturnType visitDataDrop(DataDrop* curr) { return ReturnType(); }
ReturnType visitMemoryCopy(MemoryCopy* curr) { return ReturnType(); }
@@ -290,6 +293,7 @@ struct OverriddenVisitor {
UNIMPLEMENTED(SIMDTernary);
UNIMPLEMENTED(SIMDShift);
UNIMPLEMENTED(SIMDLoad);
+ UNIMPLEMENTED(SIMDLoadStoreLane);
UNIMPLEMENTED(MemoryInit);
UNIMPLEMENTED(DataDrop);
UNIMPLEMENTED(MemoryCopy);
@@ -395,6 +399,8 @@ struct OverriddenVisitor {
DELEGATE(SIMDShift);
case Expression::Id::SIMDLoadId:
DELEGATE(SIMDLoad);
+ case Expression::Id::SIMDLoadStoreLaneId:
+ DELEGATE(SIMDLoadStoreLane);
case Expression::Id::MemoryInitId:
DELEGATE(MemoryInit);
case Expression::Id::DataDropId:
@@ -925,6 +931,9 @@ struct Walker : public VisitorType {
static void doVisitSIMDLoad(SubType* self, Expression** currp) {
self->visitSIMDLoad((*currp)->cast<SIMDLoad>());
}
+ static void doVisitSIMDLoadStoreLane(SubType* self, Expression** currp) {
+ self->visitSIMDLoadStoreLane((*currp)->cast<SIMDLoadStoreLane>());
+ }
static void doVisitMemoryInit(SubType* self, Expression** currp) {
self->visitMemoryInit((*currp)->cast<MemoryInit>());
}
@@ -1211,6 +1220,12 @@ struct PostWalker : public Walker<SubType, VisitorType> {
self->pushTask(SubType::scan, &curr->cast<SIMDLoad>()->ptr);
break;
}
+ case Expression::Id::SIMDLoadStoreLaneId: {
+ self->pushTask(SubType::doVisitSIMDLoadStoreLane, currp);
+ self->pushTask(SubType::scan, &curr->cast<SIMDLoadStoreLane>()->vec);
+ self->pushTask(SubType::scan, &curr->cast<SIMDLoadStoreLane>()->ptr);
+ break;
+ }
case Expression::Id::MemoryInitId: {
self->pushTask(SubType::doVisitMemoryInit, currp);
self->pushTask(SubType::scan, &curr->cast<MemoryInit>()->size);
diff --git a/src/wasm.h b/src/wasm.h
index 40859eefc..e2b97204b 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -481,7 +481,18 @@ enum SIMDLoadOp {
LoadExtSVec32x2ToVecI64x2,
LoadExtUVec32x2ToVecI64x2,
Load32Zero,
- Load64Zero
+ Load64Zero,
+};
+
+enum SIMDLoadStoreLaneOp {
+ LoadLaneVec8x16,
+ LoadLaneVec16x8,
+ LoadLaneVec32x4,
+ LoadLaneVec64x2,
+ StoreLaneVec8x16,
+ StoreLaneVec16x8,
+ StoreLaneVec32x4,
+ StoreLaneVec64x2,
};
enum SIMDTernaryOp { Bitselect, QFMAF32x4, QFMSF32x4, QFMAF64x2, QFMSF64x2 };
@@ -545,6 +556,7 @@ public:
SIMDTernaryId,
SIMDShiftId,
SIMDLoadId,
+ SIMDLoadStoreLaneId,
MemoryInitId,
DataDropId,
MemoryCopyId,
@@ -970,6 +982,25 @@ public:
void finalize();
};
+class SIMDLoadStoreLane
+ : public SpecificExpression<Expression::SIMDLoadStoreLaneId> {
+public:
+ SIMDLoadStoreLane() = default;
+ SIMDLoadStoreLane(MixedArena& allocator) {}
+
+ SIMDLoadStoreLaneOp op;
+ Address offset;
+ Address align;
+ uint8_t index;
+ Expression* ptr;
+ Expression* vec;
+
+ bool isStore();
+ bool isLoad() { return !isStore(); }
+ Index getMemBytes();
+ void finalize();
+};
+
class MemoryInit : public SpecificExpression<Expression::MemoryInitId> {
public:
MemoryInit() = default;
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index b6e926f6a..80f82dd5e 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -2711,6 +2711,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
if (maybeVisitSIMDLoad(curr, opcode)) {
break;
}
+ if (maybeVisitSIMDLoadStoreLane(curr, opcode)) {
+ break;
+ }
throwError("invalid code after SIMD prefix: " + std::to_string(opcode));
break;
}
@@ -4980,6 +4983,57 @@ bool WasmBinaryBuilder::maybeVisitSIMDLoad(Expression*& out, uint32_t code) {
return true;
}
+bool WasmBinaryBuilder::maybeVisitSIMDLoadStoreLane(Expression*& out,
+ uint32_t code) {
+ SIMDLoadStoreLaneOp op;
+ size_t lanes;
+ switch (code) {
+ case BinaryConsts::V128Load8Lane:
+ op = LoadLaneVec8x16;
+ lanes = 16;
+ break;
+ case BinaryConsts::V128Load16Lane:
+ op = LoadLaneVec16x8;
+ lanes = 8;
+ break;
+ case BinaryConsts::V128Load32Lane:
+ op = LoadLaneVec32x4;
+ lanes = 4;
+ break;
+ case BinaryConsts::V128Load64Lane:
+ op = LoadLaneVec64x2;
+ lanes = 2;
+ break;
+ case BinaryConsts::V128Store8Lane:
+ op = StoreLaneVec8x16;
+ lanes = 16;
+ break;
+ case BinaryConsts::V128Store16Lane:
+ op = StoreLaneVec16x8;
+ lanes = 8;
+ break;
+ case BinaryConsts::V128Store32Lane:
+ op = StoreLaneVec32x4;
+ lanes = 4;
+ break;
+ case BinaryConsts::V128Store64Lane:
+ op = StoreLaneVec64x2;
+ lanes = 2;
+ break;
+ default:
+ return false;
+ }
+ auto* curr = allocator.alloc<SIMDLoadStoreLane>();
+ curr->op = op;
+ readMemoryAccess(curr->align, curr->offset);
+ curr->index = getLaneIndex(lanes);
+ curr->vec = popNonVoidExpression();
+ curr->ptr = popNonVoidExpression();
+ curr->finalize();
+ out = curr;
+ return true;
+}
+
void WasmBinaryBuilder::visitSelect(Select* curr, uint8_t code) {
BYN_TRACE("zz node: Select, code " << int32_t(code) << std::endl);
if (code == BinaryConsts::SelectWithType) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index c4de76987..4e06a75bb 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -1300,8 +1300,12 @@ static size_t parseMemAttributes(Element& s,
size_t i = 1;
offset = 0;
align = fallbackAlign;
+ // Parse "align=X" and "offset=X" arguments, bailing out on anything else.
while (!s[i]->isList()) {
const char* str = s[i]->c_str();
+ if (strncmp(str, "align", 5) != 0 && strncmp(str, "offset", 6) != 0) {
+ return i;
+ }
const char* eq = strchr(str, '=');
if (!eq) {
throw ParseException(
@@ -1592,6 +1596,45 @@ Expression* SExpressionWasmBuilder::makeSIMDLoad(Element& s, SIMDLoadOp op) {
return ret;
}
+Expression*
+SExpressionWasmBuilder::makeSIMDLoadStoreLane(Element& s,
+ SIMDLoadStoreLaneOp op) {
+ auto* ret = allocator.alloc<SIMDLoadStoreLane>();
+ ret->op = op;
+ Address defaultAlign;
+ size_t lanes;
+ switch (op) {
+ case LoadLaneVec8x16:
+ case StoreLaneVec8x16:
+ defaultAlign = 1;
+ lanes = 16;
+ break;
+ case LoadLaneVec16x8:
+ case StoreLaneVec16x8:
+ defaultAlign = 2;
+ lanes = 8;
+ break;
+ case LoadLaneVec32x4:
+ case StoreLaneVec32x4:
+ defaultAlign = 4;
+ lanes = 4;
+ break;
+ case LoadLaneVec64x2:
+ case StoreLaneVec64x2:
+ defaultAlign = 8;
+ lanes = 2;
+ break;
+ default:
+ WASM_UNREACHABLE("Unexpected SIMDLoadStoreLane op");
+ }
+ size_t i = parseMemAttributes(s, ret->offset, ret->align, defaultAlign);
+ ret->index = parseLaneIndex(s[i++], lanes);
+ ret->ptr = parseExpression(s[i++]);
+ ret->vec = parseExpression(s[i]);
+ ret->finalize();
+ return ret;
+}
+
Expression* SExpressionWasmBuilder::makeMemoryInit(Element& s) {
auto ret = allocator.alloc<MemoryInit>();
ret->segment = atoi(s[1]->str().c_str());
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index fcf9e4b8a..4bc479d15 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -647,6 +647,39 @@ void BinaryInstWriter::visitSIMDLoad(SIMDLoad* curr) {
emitMemoryAccess(curr->align, /*(unused) bytes=*/0, curr->offset);
}
+void BinaryInstWriter::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
+ o << int8_t(BinaryConsts::SIMDPrefix);
+ switch (curr->op) {
+ case LoadLaneVec8x16:
+ o << U32LEB(BinaryConsts::V128Load8Lane);
+ break;
+ case LoadLaneVec16x8:
+ o << U32LEB(BinaryConsts::V128Load16Lane);
+ break;
+ case LoadLaneVec32x4:
+ o << U32LEB(BinaryConsts::V128Load32Lane);
+ break;
+ case LoadLaneVec64x2:
+ o << U32LEB(BinaryConsts::V128Load64Lane);
+ break;
+ case StoreLaneVec8x16:
+ o << U32LEB(BinaryConsts::V128Store8Lane);
+ break;
+ case StoreLaneVec16x8:
+ o << U32LEB(BinaryConsts::V128Store16Lane);
+ break;
+ case StoreLaneVec32x4:
+ o << U32LEB(BinaryConsts::V128Store32Lane);
+ break;
+ case StoreLaneVec64x2:
+ o << U32LEB(BinaryConsts::V128Store64Lane);
+ break;
+ }
+ assert(curr->align);
+ emitMemoryAccess(curr->align, /*(unused) bytes=*/0, curr->offset);
+ o << curr->index;
+}
+
void BinaryInstWriter::visitMemoryInit(MemoryInit* curr) {
o << int8_t(BinaryConsts::MiscPrefix);
o << U32LEB(BinaryConsts::MemoryInit);
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index ef6a29373..31b68a80c 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -317,6 +317,7 @@ public:
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);
@@ -1264,6 +1265,59 @@ void FunctionValidator::visitSIMDLoad(SIMDLoad* curr) {
validateAlignment(curr->align, memAlignType, bytes, /*isAtomic=*/false, curr);
}
+void FunctionValidator::visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
+ shouldBeTrue(
+ getModule()->memory.exists, curr, "Memory operations require a memory");
+ shouldBeTrue(
+ getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
+ if (curr->isLoad()) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type, Type(Type::v128), curr, "loadX_lane must have type v128");
+ } else {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->type, Type(Type::none), curr, "storeX_lane must have type none");
+ }
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->ptr->type,
+ indexType(),
+ curr,
+ "loadX_lane or storeX_lane address must match memory index type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->vec->type,
+ Type(Type::v128),
+ curr,
+ "loadX_lane or storeX_lane vector argument must have type v128");
+ size_t lanes;
+ Type memAlignType = Type::none;
+ switch (curr->op) {
+ case LoadLaneVec8x16:
+ case StoreLaneVec8x16:
+ lanes = 16;
+ memAlignType = Type::i32;
+ break;
+ case LoadLaneVec16x8:
+ case StoreLaneVec16x8:
+ lanes = 8;
+ memAlignType = Type::i32;
+ break;
+ case LoadLaneVec32x4:
+ case StoreLaneVec32x4:
+ lanes = 4;
+ memAlignType = Type::i32;
+ break;
+ case LoadLaneVec64x2:
+ case StoreLaneVec64x2:
+ lanes = 2;
+ memAlignType = Type::i64;
+ break;
+ default:
+ WASM_UNREACHABLE("Unexpected SIMDLoadStoreLane op");
+ }
+ Index bytes = curr->getMemBytes();
+ validateAlignment(curr->align, memAlignType, bytes, /*isAtomic=*/false, curr);
+ shouldBeTrue(curr->index < lanes, curr, "invalid lane index");
+}
+
void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
shouldBeTrue(getModule()->features.hasBulkMemory(),
curr,
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index 2052afa89..472902ad5 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -175,6 +175,8 @@ const char* getExpressionName(Expression* curr) {
return "simd_shift";
case Expression::Id::SIMDLoadId:
return "simd_load";
+ case Expression::Id::SIMDLoadStoreLaneId:
+ return "simd_load_store_lane";
case Expression::Id::MemoryInitId:
return "memory_init";
case Expression::Id::DataDropId:
@@ -674,6 +676,48 @@ Index SIMDLoad::getMemBytes() {
WASM_UNREACHABLE("unexpected op");
}
+void SIMDLoadStoreLane::finalize() {
+ assert(ptr && vec);
+ type = isLoad() ? Type::v128 : Type::none;
+ if (ptr->type == Type::unreachable || vec->type == Type::unreachable) {
+ type = Type::unreachable;
+ }
+}
+
+Index SIMDLoadStoreLane::getMemBytes() {
+ switch (op) {
+ case LoadLaneVec8x16:
+ case StoreLaneVec8x16:
+ return 1;
+ case LoadLaneVec16x8:
+ case StoreLaneVec16x8:
+ return 2;
+ case LoadLaneVec32x4:
+ case StoreLaneVec32x4:
+ return 4;
+ case LoadLaneVec64x2:
+ case StoreLaneVec64x2:
+ return 8;
+ }
+ WASM_UNREACHABLE("unexpected op");
+}
+
+bool SIMDLoadStoreLane::isStore() {
+ switch (op) {
+ case StoreLaneVec8x16:
+ case StoreLaneVec16x8:
+ case StoreLaneVec32x4:
+ case StoreLaneVec64x2:
+ return true;
+ case LoadLaneVec16x8:
+ case LoadLaneVec32x4:
+ case LoadLaneVec64x2:
+ case LoadLaneVec8x16:
+ return false;
+ }
+ WASM_UNREACHABLE("unexpected op");
+}
+
Const* Const::set(Literal value_) {
value = value_;
type = value.type;
diff --git a/src/wasm2js.h b/src/wasm2js.h
index c5297a12a..8d7e9d105 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -2070,6 +2070,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m,
unimplemented(curr);
WASM_UNREACHABLE("unimp");
}
+ Ref visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {
+ unimplemented(curr);
+ WASM_UNREACHABLE("unimp");
+ }
Ref visitMemoryInit(MemoryInit* curr) {
ABI::wasm2js::ensureHelpers(module, ABI::wasm2js::MEMORY_INIT);
return ValueBuilder::makeCall(ABI::wasm2js::MEMORY_INIT,