diff options
-rw-r--r-- | src/ast/effects.h | 18 | ||||
-rw-r--r-- | src/passes/Print.cpp | 23 | ||||
-rw-r--r-- | src/wasm-binary.h | 8 | ||||
-rw-r--r-- | src/wasm-builder.h | 18 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 2 | ||||
-rw-r--r-- | src/wasm-traversal.h | 21 | ||||
-rw-r--r-- | src/wasm-validator.h | 2 | ||||
-rw-r--r-- | src/wasm.h | 26 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 55 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 24 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 14 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 12 | ||||
-rw-r--r-- | test/atomics.wast | 24 | ||||
-rw-r--r-- | test/atomics.wast.from-wast | 24 | ||||
-rw-r--r-- | test/atomics.wast.fromBinary | 26 | ||||
-rw-r--r-- | test/atomics.wast.fromBinary.noDebugInfo | 26 |
16 files changed, 321 insertions, 2 deletions
diff --git a/src/ast/effects.h b/src/ast/effects.h index 2db671b5e..1ae27d2a7 100644 --- a/src/ast/effects.h +++ b/src/ast/effects.h @@ -208,6 +208,24 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer> { isAtomic = true; if (!ignoreImplicitTraps) implicitTrap = true; } + void visitAtomicWait(AtomicWait* curr) { + readsMemory = true; + // AtomicWait doesn't strictly write memory, but it does modify the waiters + // list associated with the specified address, which we can think of as a + // write. + writesMemory = true; + isAtomic = true; + if (!ignoreImplicitTraps) implicitTrap = true; + } + void visitAtomicWake(AtomicWake* curr) { + // AtomicWake doesn't strictly write memory, but it does modify the waiters + // list associated with the specified address, which we can think of as a + // write. + readsMemory = true; + writesMemory = true; + isAtomic = true; + if (!ignoreImplicitTraps) implicitTrap = true; + }; void visitUnary(Unary *curr) { if (!ignoreImplicitTraps) { switch (curr->op) { diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 33e06d7a5..3c538a40e 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -368,6 +368,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } void visitAtomicRMW(AtomicRMW* curr) { o << '('; + prepareColor(o); printRMWSize(o, curr->type, curr->bytes); switch (curr->op) { case Add: o << "add"; break; @@ -388,6 +389,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { o << '('; + prepareColor(o); printRMWSize(o, curr->type, curr->bytes); o << "cmpxchg"; restoreNormalColor(o); @@ -400,6 +402,24 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printFullLine(curr->replacement); decIndent(); } + void visitAtomicWait(AtomicWait* curr) { + o << '(' ; + prepareColor(o); + o << printWasmType(curr->expectedType) << ".wait"; + restoreNormalColor(o); + incIndent(); + printFullLine(curr->ptr); + printFullLine(curr->expected); + printFullLine(curr->timeout); + decIndent(); + } + void visitAtomicWake(AtomicWake* curr) { + printOpening(o, "wake"); + incIndent(); + printFullLine(curr->ptr); + printFullLine(curr->wakeCount); + decIndent(); + } void visitConst(Const *curr) { o << curr->value; } @@ -562,8 +582,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { decIndent(); } void visitDrop(Drop *curr) { - o << '('; - prepareColor(o) << "drop"; + printOpening(o, "drop"); incIndent(); printFullLine(curr->value); decIndent(); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 9724bc93d..6df49b162 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -535,6 +535,10 @@ enum ASTNodes { }; enum AtomicOpcodes { + AtomicWake = 0x00, + I32AtomicWait = 0x01, + I64AtomicWait = 0x02, + I32AtomicLoad = 0x10, I64AtomicLoad = 0x11, I32AtomicLoad8U = 0x12, @@ -758,6 +762,8 @@ public: void visitStore(Store *curr); void visitAtomicRMW(AtomicRMW *curr); void visitAtomicCmpxchg(AtomicCmpxchg *curr); + void visitAtomicWait(AtomicWait *curr); + void visitAtomicWake(AtomicWake *curr); void visitConst(Const *curr); void visitUnary(Unary *curr); void visitBinary(Binary *curr); @@ -924,6 +930,8 @@ public: bool maybeVisitStore(Expression*& out, uint8_t code, bool isAtomic); bool maybeVisitAtomicRMW(Expression*& out, uint8_t code); bool maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code); + bool maybeVisitAtomicWait(Expression*& out, uint8_t code); + bool maybeVisitAtomicWake(Expression*& out, uint8_t code); bool maybeVisitConst(Expression*& out, uint8_t code); bool maybeVisitUnary(Expression*& out, uint8_t code); bool maybeVisitBinary(Expression*& out, uint8_t code); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index df9f1fcaa..4ef4b1e0b 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -198,6 +198,24 @@ public: load->isAtomic = true; return load; } + AtomicWait* makeAtomicWait(Expression* ptr, Expression* expected, Expression* timeout, WasmType type) { + auto* wait = allocator.alloc<AtomicWait>(); + wait->ptr = ptr; + wait->expected = expected; + wait->timeout = timeout; + wait->expectedType = type; + wait->type = i32; + wait->finalize(); + return wait; + } + AtomicWake* makeAtomicWake(Expression* ptr, Expression* wakeCount) { + auto* wake = allocator.alloc<AtomicWake>(); + wake->ptr = ptr; + wake->wakeCount = wakeCount; + wake->type = i32; + wake->finalize(); + return wake; + } Store* makeStore(unsigned bytes, uint32_t offset, unsigned align, Expression *ptr, Expression *value, WasmType type) { auto* ret = allocator.alloc<Store>(); ret->isAtomic = false; diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 6be17f561..31abb8fb9 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -184,6 +184,8 @@ private: Expression* makeAtomicRMWOrCmpxchg(Element& s, WasmType type); Expression* makeAtomicRMW(Element& s, WasmType type, uint8_t bytes, const char* extra); Expression* makeAtomicCmpxchg(Element& s, WasmType type, uint8_t bytes, const char* extra); + Expression* makeAtomicWait(Element& s, WasmType type); + Expression* makeAtomicWake(Element& s); Expression* makeIf(Element& s); Expression* makeMaybeBlock(Element& s, size_t i, WasmType type); Expression* makeLoop(Element& s); diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 49c7bfe06..6e46b8cb9 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -51,6 +51,8 @@ struct Visitor { ReturnType visitStore(Store* curr) { return ReturnType(); } ReturnType visitAtomicRMW(AtomicRMW* curr) { return ReturnType(); } ReturnType visitAtomicCmpxchg(AtomicCmpxchg* curr) { return ReturnType(); } + ReturnType visitAtomicWait(AtomicWait* curr) { return ReturnType(); } + ReturnType visitAtomicWake(AtomicWake* curr) { return ReturnType(); } ReturnType visitConst(Const* curr) { return ReturnType(); } ReturnType visitUnary(Unary* curr) { return ReturnType(); } ReturnType visitBinary(Binary* curr) { return ReturnType(); } @@ -94,6 +96,8 @@ struct Visitor { case Expression::Id::StoreId: DELEGATE(Store); case Expression::Id::AtomicRMWId: DELEGATE(AtomicRMW); case Expression::Id::AtomicCmpxchgId: DELEGATE(AtomicCmpxchg); + case Expression::Id::AtomicWaitId: DELEGATE(AtomicWait); + case Expression::Id::AtomicWakeId: DELEGATE(AtomicWake); case Expression::Id::ConstId: DELEGATE(Const); case Expression::Id::UnaryId: DELEGATE(Unary); case Expression::Id::BinaryId: DELEGATE(Binary); @@ -136,6 +140,8 @@ struct UnifiedExpressionVisitor : public Visitor<SubType> { ReturnType visitStore(Store* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitAtomicRMW(AtomicRMW* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitAtomicCmpxchg(AtomicCmpxchg* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitAtomicWait(AtomicWait* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitAtomicWake(AtomicWake* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitConst(Const* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitUnary(Unary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitBinary(Binary* curr) { return static_cast<SubType*>(this)->visitExpression(curr); } @@ -314,6 +320,8 @@ struct Walker : public VisitorType { static void doVisitStore(SubType* self, Expression** currp) { self->visitStore((*currp)->cast<Store>()); } static void doVisitAtomicRMW(SubType* self, Expression** currp) { self->visitAtomicRMW((*currp)->cast<AtomicRMW>()); } static void doVisitAtomicCmpxchg(SubType* self, Expression** currp){ self->visitAtomicCmpxchg((*currp)->cast<AtomicCmpxchg>()); } + static void doVisitAtomicWait(SubType* self, Expression** currp) { self->visitAtomicWait((*currp)->cast<AtomicWait>()); } + static void doVisitAtomicWake(SubType* self, Expression** currp) { self->visitAtomicWake((*currp)->cast<AtomicWake>()); } static void doVisitConst(SubType* self, Expression** currp) { self->visitConst((*currp)->cast<Const>()); } static void doVisitUnary(SubType* self, Expression** currp) { self->visitUnary((*currp)->cast<Unary>()); } static void doVisitBinary(SubType* self, Expression** currp) { self->visitBinary((*currp)->cast<Binary>()); } @@ -449,6 +457,19 @@ struct PostWalker : public Walker<SubType, VisitorType> { self->pushTask(SubType::scan, &curr->cast<AtomicCmpxchg>()->ptr); break; } + case Expression::Id::AtomicWaitId: { + self->pushTask(SubType::doVisitAtomicWait, currp); + self->pushTask(SubType::scan, &curr->cast<AtomicWait>()->timeout); + self->pushTask(SubType::scan, &curr->cast<AtomicWait>()->expected); + self->pushTask(SubType::scan, &curr->cast<AtomicWait>()->ptr); + break; + } + case Expression::Id::AtomicWakeId: { + self->pushTask(SubType::doVisitAtomicWake, currp); + self->pushTask(SubType::scan, &curr->cast<AtomicWake>()->wakeCount); + self->pushTask(SubType::scan, &curr->cast<AtomicWake>()->ptr); + break; + } case Expression::Id::ConstId: { self->pushTask(SubType::doVisitConst, currp); break; diff --git a/src/wasm-validator.h b/src/wasm-validator.h index a359102ea..d78b54a47 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -142,6 +142,8 @@ public: void visitStore(Store *curr); void visitAtomicRMW(AtomicRMW *curr); void visitAtomicCmpxchg(AtomicCmpxchg *curr); + void visitAtomicWait(AtomicWait *curr); + void visitAtomicWake(AtomicWake *curr); void visitBinary(Binary *curr); void visitUnary(Unary *curr); void visitSelect(Select* curr); diff --git a/src/wasm.h b/src/wasm.h index 974bf1730..e9782ab6e 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -183,6 +183,8 @@ public: UnreachableId, AtomicCmpxchgId, AtomicRMWId, + AtomicWaitId, + AtomicWakeId, NumExpressionIds }; Id _id; @@ -456,6 +458,30 @@ class AtomicCmpxchg : public SpecificExpression<Expression::AtomicCmpxchgId> { void finalize(); }; +class AtomicWait : public SpecificExpression<Expression::AtomicWaitId> { + public: + AtomicWait() = default; + AtomicWait(MixedArena& allocator) : AtomicWait() {} + + Expression* ptr; + Expression* expected; + Expression* timeout; + WasmType expectedType; + + void finalize(); +}; + +class AtomicWake : public SpecificExpression<Expression::AtomicWakeId> { + public: + AtomicWake() = default; + AtomicWake(MixedArena& allocator) : AtomicWake() {} + + Expression* ptr; + Expression* wakeCount; + + void finalize(); +}; + class Const : public SpecificExpression<Expression::ConstId> { public: Const() {} diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index b114b81fb..f0787600e 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -952,6 +952,27 @@ void WasmBinaryWriter::visitAtomicCmpxchg(AtomicCmpxchg *curr) { emitMemoryAccess(curr->bytes, curr->bytes, curr->offset); } +void WasmBinaryWriter::visitAtomicWait(AtomicWait *curr) { + if (debug) std::cerr << "zz node: AtomicWait" << std::endl; + recurse(curr->ptr); + recurse(curr->expected); + recurse(curr->timeout); + + o << int8_t(BinaryConsts::AtomicPrefix); + switch (curr->expectedType) { + case i32: o << int8_t(BinaryConsts::I32AtomicWait); break; + case i64: o << int8_t(BinaryConsts::I64AtomicWait); break; + default: WASM_UNREACHABLE(); + } +} + +void WasmBinaryWriter::visitAtomicWake(AtomicWake *curr) { + if (debug) std::cerr << "zz node: AtomicWake" << std::endl; + recurse(curr->ptr); + recurse(curr->wakeCount); + + o << int8_t(BinaryConsts::AtomicPrefix) << int8_t(BinaryConsts::AtomicWake); +} void WasmBinaryWriter::visitConst(Const *curr) { if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl; @@ -2123,6 +2144,8 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { if (maybeVisitStore(curr, code, /*isAtomic=*/true)) break; if (maybeVisitAtomicRMW(curr, code)) break; if (maybeVisitAtomicCmpxchg(curr, code)) break; + if (maybeVisitAtomicWait(curr, code)) break; + if (maybeVisitAtomicWake(curr, code)) break; throw ParseException("invalid code after atomic prefix: " + std::to_string(code)); } default: { @@ -2553,6 +2576,38 @@ bool WasmBinaryBuilder::maybeVisitAtomicCmpxchg(Expression*& out, uint8_t code) return true; } +bool WasmBinaryBuilder::maybeVisitAtomicWait(Expression*& out, uint8_t code) { + if (code < BinaryConsts::I32AtomicWait || code > BinaryConsts::I64AtomicWait) return false; + auto* curr = allocator.alloc<AtomicWait>(); + + switch (code) { + case BinaryConsts::I32AtomicWait: curr->expectedType = i32; break; + case BinaryConsts::I64AtomicWait: curr->expectedType = i64; break; + default: WASM_UNREACHABLE(); + } + curr->type = i32; + if (debug) std::cerr << "zz node: AtomicWait" << std::endl; + curr->timeout = popNonVoidExpression(); + curr->expected = popNonVoidExpression(); + curr->ptr = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + +bool WasmBinaryBuilder::maybeVisitAtomicWake(Expression*& out, uint8_t code) { + if (code != BinaryConsts::AtomicWake) return false; + auto* curr = allocator.alloc<AtomicWake>(); + if (debug) std::cerr << "zz node: AtomicWake" << std::endl; + + curr->type = i32; + curr->wakeCount = popNonVoidExpression(); + curr->ptr = popNonVoidExpression(); + curr->finalize(); + out = curr; + return true; +} + bool WasmBinaryBuilder::maybeVisitConst(Expression*& out, uint8_t code) { Const* curr; if (debug) std::cerr << "zz node: Const, code " << code << std::endl; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 044507e86..fd7c7e7ae 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -781,6 +781,7 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { abort_on(op); } case 'w': { + if (!strncmp(op, "wait", strlen("wait"))) return makeAtomicWait(s, type); if (op[1] == 'r') return makeUnary(s, UnaryOp::WrapInt64, type); abort_on(op); } @@ -866,6 +867,9 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { if (str[1] == 'n') return allocator.alloc<Unreachable>(); abort_on(str); } + case 'w': { + if (!strncmp(str, "wake", strlen("wake"))) return makeAtomicWake(s); + } default: abort_on(str); } } @@ -1241,6 +1245,26 @@ Expression* SExpressionWasmBuilder::makeAtomicCmpxchg(Element& s, WasmType type, return ret; } +Expression* SExpressionWasmBuilder::makeAtomicWait(Element& s, WasmType type) { + auto ret = allocator.alloc<AtomicWait>(); + ret->type = i32; + ret->expectedType = type; + ret->ptr = parseExpression(s[1]); + ret->expected = parseExpression(s[2]); + ret->timeout = parseExpression(s[3]); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeAtomicWake(Element& s) { + auto ret = allocator.alloc<AtomicWake>(); + ret->type = i32; + ret->ptr = parseExpression(s[1]); + ret->wakeCount = parseExpression(s[2]); + ret->finalize(); + return ret; +} + Expression* SExpressionWasmBuilder::makeIf(Element& s) { auto ret = allocator.alloc<If>(); Index i = 1; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index aaf5a7fd5..819602608 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -261,6 +261,20 @@ void WasmValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) { shouldBeEqualOrFirstIsUnreachable(curr->replacement->type, curr->type, curr, "Cmpxchg result type must match replacement"); shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types"); } +void WasmValidator::visitAtomicWait(AtomicWait* curr) { + if (!getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr); + shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWait must have type i32"); + shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWait pointer type must be i32"); + shouldBeIntOrUnreachable(curr->expected->type, curr, "AtomicWait expected type must be int"); + shouldBeEqualOrFirstIsUnreachable(curr->expected->type, curr->expectedType, curr, "AtomicWait expected type must match operand"); + shouldBeEqualOrFirstIsUnreachable(curr->timeout->type, i64, curr, "AtomicWait timeout type must be i64"); +} +void WasmValidator::visitAtomicWake(AtomicWake* curr) { + if (!getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr); + shouldBeEqualOrFirstIsUnreachable(curr->type, i32, curr, "AtomicWake must have type i32"); + shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicWake pointer type must be i32"); + shouldBeEqualOrFirstIsUnreachable(curr->wakeCount->type, i32, curr, "AtomicWake wakeCount type must be i32"); +} void WasmValidator::validateMemBytes(uint8_t bytes, WasmType type, Expression* curr) { switch (bytes) { case 1: diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 19ba5be21..f82a06e68 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -368,6 +368,18 @@ void AtomicCmpxchg::finalize() { } } +void AtomicWait::finalize() { + if (ptr->type == unreachable || expected->type == unreachable || timeout->type == unreachable) { + type = unreachable; + } +} + +void AtomicWake::finalize() { + if (ptr->type == unreachable || wakeCount->type == unreachable) { + type = unreachable; + } +} + Const* Const::set(Literal value_) { value = value_; type = value.type; diff --git a/test/atomics.wast b/test/atomics.wast index 006412025..7ed32f189 100644 --- a/test/atomics.wast +++ b/test/atomics.wast @@ -134,4 +134,28 @@ ) ) ) + (func $atomic-wait-wake (type $0) + (local $0 i32) + (local $1 i64) + (drop + (i32.wait + (get_local $0) + (get_local $0) + (get_local $1) + ) + ) + (drop + (wake + (get_local $0) + (get_local $0) + ) + ) + (drop + (i64.wait + (get_local $0) + (get_local $1) + (get_local $1) + ) + ) + ) ) diff --git a/test/atomics.wast.from-wast b/test/atomics.wast.from-wast index f44e232c4..48059dc45 100644 --- a/test/atomics.wast.from-wast +++ b/test/atomics.wast.from-wast @@ -134,4 +134,28 @@ ) ) ) + (func $atomic-wait-wake (type $0) + (local $0 i32) + (local $1 i64) + (drop + (i32.wait + (get_local $0) + (get_local $0) + (get_local $1) + ) + ) + (drop + (wake + (get_local $0) + (get_local $0) + ) + ) + (drop + (i64.wait + (get_local $0) + (get_local $1) + (get_local $1) + ) + ) + ) ) diff --git a/test/atomics.wast.fromBinary b/test/atomics.wast.fromBinary index 09f978122..31acc6635 100644 --- a/test/atomics.wast.fromBinary +++ b/test/atomics.wast.fromBinary @@ -140,5 +140,31 @@ ) ) ) + (func $atomic-wait-wake (type $0) + (local $var$0 i32) + (local $var$1 i64) + (block $label$0 + (drop + (i32.wait + (get_local $var$0) + (get_local $var$0) + (get_local $var$1) + ) + ) + (drop + (wake + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i64.wait + (get_local $var$0) + (get_local $var$1) + (get_local $var$1) + ) + ) + ) + ) ) diff --git a/test/atomics.wast.fromBinary.noDebugInfo b/test/atomics.wast.fromBinary.noDebugInfo index 3b3426b1d..c45ae1fac 100644 --- a/test/atomics.wast.fromBinary.noDebugInfo +++ b/test/atomics.wast.fromBinary.noDebugInfo @@ -140,5 +140,31 @@ ) ) ) + (func $3 (type $0) + (local $var$0 i32) + (local $var$1 i64) + (block $label$0 + (drop + (i32.wait + (get_local $var$0) + (get_local $var$0) + (get_local $var$1) + ) + ) + (drop + (wake + (get_local $var$0) + (get_local $var$0) + ) + ) + (drop + (i64.wait + (get_local $var$0) + (get_local $var$1) + (get_local $var$1) + ) + ) + ) + ) ) |