diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.h | 1 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 53 | ||||
-rw-r--r-- | src/ir/ExpressionAnalyzer.cpp | 6 | ||||
-rw-r--r-- | src/ir/ExpressionManipulator.cpp | 8 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 3 | ||||
-rw-r--r-- | src/ir/effects.h | 10 | ||||
-rw-r--r-- | src/ir/utils.h | 2 | ||||
-rw-r--r-- | src/passes/DeadCodeElimination.cpp | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 45 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 1 | ||||
-rw-r--r-- | src/wasm-binary.h | 10 | ||||
-rw-r--r-- | src/wasm-builder.h | 16 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 79 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm-stack.h | 1 | ||||
-rw-r--r-- | src/wasm-traversal.h | 15 | ||||
-rw-r--r-- | src/wasm.h | 33 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 54 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 43 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 33 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 54 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 44 | ||||
-rw-r--r-- | src/wasm2js.h | 4 |
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, |