diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 56345480d57..0af02f92c60 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -206,6 +206,8 @@ ("i64.extend32_s", "makeUnary(UnaryOp::ExtendS32Int64)"), # atomic instructions ("memory.atomic.notify", "makeAtomicNotify()"), + ("waitqueue.wait", "makeWaitQueueWait()"), + ("waitqueue.notify", "makeWaitQueueNotify()"), ("memory.atomic.wait32", "makeAtomicWait(Type::i32)"), ("memory.atomic.wait64", "makeAtomicWait(Type::i64)"), ("atomic.fence", "makeAtomicFence()"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 0472d5a3dea..fde7fe6f4ae 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -5784,6 +5784,23 @@ switch (buf[0]) { default: goto parse_error; } } + case 'w': { + switch (buf[10]) { + case 'n': + if (op == "waitqueue.notify"sv) { + CHECK_ERR(makeWaitQueueNotify(ctx, pos, annotations)); + return Ok{}; + } + goto parse_error; + case 'w': + if (op == "waitqueue.wait"sv) { + CHECK_ERR(makeWaitQueueWait(ctx, pos, annotations)); + return Ok{}; + } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } parse_error: diff --git a/src/interpreter/interpreter.cpp b/src/interpreter/interpreter.cpp index 6c97c82f729..d8fb51bf47e 100644 --- a/src/interpreter/interpreter.cpp +++ b/src/interpreter/interpreter.cpp @@ -283,6 +283,8 @@ struct ExpressionInterpreter : OverriddenVisitor { Flow visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); } Flow visitResumeThrow(ResumeThrow* curr) { WASM_UNREACHABLE("TODO"); } Flow visitStackSwitch(StackSwitch* curr) { WASM_UNREACHABLE("TODO"); } + Flow visitWaitQueueWait(WaitQueueWait* curr) { WASM_UNREACHABLE("TODO"); } + Flow visitWaitQueueNotify(WaitQueueNotify* curr) { WASM_UNREACHABLE("TODO"); } }; } // anonymous namespace diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 06d6aab2c97..93fc9044dc9 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -202,6 +202,10 @@ void ReFinalize::visitResumeThrow(ResumeThrow* curr) { } } void ReFinalize::visitStackSwitch(StackSwitch* curr) { curr->finalize(); } +void ReFinalize::visitWaitQueueWait(WaitQueueWait* curr) { curr->finalize(); } +void ReFinalize::visitWaitQueueNotify(WaitQueueNotify* curr) { + curr->finalize(); +} void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index a6f87543814..9f445fe260d 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1357,6 +1357,23 @@ template struct ChildTyper : OverriddenVisitor { } note(&curr->cont, Type(*ct, Nullable)); } + + void visitWaitQueueWait(WaitQueueWait* curr) { + note(&curr->waitqueue, + Type(HeapType(Struct(std::vector{ + Field(Field::PackedType::WaitQueue, Mutability::Immutable)})), + NonNullable)); + note(&curr->value, Type(Type::BasicType::i32)); + note(&curr->timeout, Type(Type::BasicType::i64)); + } + + void visitWaitQueueNotify(WaitQueueNotify* curr) { + note(&curr->waitqueue, + Type(HeapType(Struct(std::vector{ + Field(Field::PackedType::WaitQueue, Mutability::Immutable)})), + NonNullable)); + note(&curr->count, Type(Type::BasicType::i32)); + } }; } // namespace wasm diff --git a/src/ir/cost.h b/src/ir/cost.h index 853040a3f19..58de2e1a762 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -118,6 +118,13 @@ struct CostAnalyzer : public OverriddenVisitor { return AtomicCost + visit(curr->ptr) + visit(curr->expected) + visit(curr->timeout); } + CostType visitWaitQueueWait(WaitQueueWait* curr) { + return AtomicCost + visit(curr->waitqueue) + visit(curr->value) + + visit(curr->timeout); + } + CostType visitWaitQueueNotify(WaitQueueNotify* curr) { + return AtomicCost + visit(curr->waitqueue) + visit(curr->count); + } CostType visitAtomicNotify(AtomicNotify* curr) { return AtomicCost + visit(curr->ptr) + visit(curr->notifyCount); } diff --git a/src/ir/effects.h b/src/ir/effects.h index 1a7f4af616b..b4f960d60f9 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -1159,6 +1159,31 @@ class EffectAnalyzer { parent.throws_ = true; } } + + void visitWaitQueueWait(WaitQueueWait* curr) { + parent.isAtomic = true; + parent.mayNotReturn = true; + + // field 0 must exist and be a packed waitqueue if this is valid Wasm. + if (curr->waitqueue->type.getHeapType() + .getStruct() + .fields.at(0) + .mutable_ == Mutable) { + parent.readsMutableStruct = true; + } + } + + void visitWaitQueueNotify(WaitQueueNotify* curr) { + parent.isAtomic = true; + + // field 0 must exist and be a packed waitqueue if this is valid Wasm. + if (curr->waitqueue->type.getHeapType() + .getStruct() + .fields.at(0) + .mutable_ == Mutable) { + parent.readsMutableStruct = true; + } + } }; public: diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index cc13101b717..9bebac568ac 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -446,6 +446,12 @@ struct CodeScanner : PostWalker { info.note(curr->cont->type); info.note(curr->type); } + void visitWaitQueueWait(WaitQueueWait* curr) { + info.note(curr->waitqueue->type); + } + void visitWaitQueueNotify(WaitQueueNotify* curr) { + info.note(curr->waitqueue->type); + } void visitBlock(Block* curr) { info.noteControlFlow(Signature(Type::none, curr->type)); } diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 06825343d1f..fccef6e4bfc 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1401,6 +1401,8 @@ struct InfoCollector // TODO: optimize when possible addRoot(curr); } + void visitWaitQueueWait(WaitQueueWait* curr) { addRoot(curr); } + void visitWaitQueueNotify(WaitQueueNotify* curr) { addRoot(curr); } void visitFunction(Function* func) { // Functions with a result can flow a value out from their body. diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index 5e8bac40a1f..99c6325ebc8 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -598,6 +598,18 @@ struct SubtypingDiscoverer : public OverriddenVisitor { .type.getSignature(); self()->noteSubtype(currResult, retSig.results); } + void visitWaitQueueWait(WaitQueueWait* curr) { + self()->noteSubtype(curr->waitqueue, + Type(HeapType(Struct(std::vector{Field( + Field::PackedType::WaitQueue, Immutable)})), + NonNullable)); + } + void visitWaitQueueNotify(WaitQueueNotify* curr) { + self()->noteSubtype(curr->waitqueue, + Type(HeapType(Struct(std::vector{Field( + Field::PackedType::WaitQueue, Immutable)})), + NonNullable)); + } }; } // namespace wasm diff --git a/src/parser/contexts.h b/src/parser/contexts.h index c16ac92268e..025715484ee 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -956,6 +956,13 @@ struct NullInstrParserCtx { makeStackSwitch(Index, const std::vector&, HeapTypeT, TagIdxT) { return Ok{}; } + + Result<> makeWaitQueueWait(Index, const std::vector&) { + return Ok{}; + } + Result<> makeWaitQueueNotify(Index, const std::vector&) { + return Ok{}; + } }; struct NullCtx : NullTypeParserCtx, NullInstrParserCtx { @@ -2947,6 +2954,16 @@ struct ParseDefsCtx : TypeParserCtx, AnnotationParserCtx { Name tag) { return withLoc(pos, irBuilder.makeStackSwitch(type, tag)); } + + Result<> makeWaitQueueWait(Index pos, + const std::vector& annotations) { + return withLoc(pos, irBuilder.makeWaitQueueWait()); + } + + Result<> makeWaitQueueNotify(Index pos, + const std::vector& annotations) { + return withLoc(pos, irBuilder.makeWaitQueueNotify()); + } }; } // namespace wasm::WATParser diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 2a3a5ec5a4e..4b01900ff84 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -2840,6 +2840,20 @@ Result<> makeStackSwitch(Ctx& ctx, return ctx.makeStackSwitch(pos, annotations, *type, *tag); } +template +Result<> makeWaitQueueWait(Ctx& ctx, + Index pos, + const std::vector& annotations) { + return ctx.makeWaitQueueWait(pos, annotations); +} + +template +Result<> makeWaitQueueNotify(Ctx& ctx, + Index pos, + const std::vector& annotations) { + return ctx.makeWaitQueueNotify(pos, annotations); +} + // ======= // Modules // ======= diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 6f2f558e1ec..609cbbc7d35 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -376,6 +376,16 @@ struct PrintSExpression : public UnifiedExpressionVisitor { visitExpression(curr); } } + void visitWaitQueueWait(WaitQueueWait* curr) { + if (!maybePrintUnreachableReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitWaitQueueNotify(WaitQueueNotify* curr) { + if (!maybePrintUnreachableReplacement(curr, curr->type)) { + visitExpression(curr); + } + } // Module-level visitors void handleSignature(Function* curr, bool printImplicitNames = false); @@ -2658,6 +2668,14 @@ struct PrintExpressionContents o << ' '; curr->tag.print(o); } + + void visitWaitQueueWait(WaitQueueWait* curr) { + printMedium(o, "waitqueue.wait"); + } + + void visitWaitQueueNotify(WaitQueueNotify* curr) { + printMedium(o, "waitqueue.notify"); + } }; void PrintSExpression::setModule(Module* module) { diff --git a/src/passes/TypeGeneralizing.cpp b/src/passes/TypeGeneralizing.cpp index c0607ef51be..ddabb445050 100644 --- a/src/passes/TypeGeneralizing.cpp +++ b/src/passes/TypeGeneralizing.cpp @@ -899,6 +899,8 @@ struct TransferFn : OverriddenVisitor { void visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); } void visitResumeThrow(ResumeThrow* curr) { WASM_UNREACHABLE("TODO"); } void visitStackSwitch(StackSwitch* curr) { WASM_UNREACHABLE("TODO"); } + void visitWaitQueueWait(WaitQueueWait* curr) { WASM_UNREACHABLE("TODO"); } + void visitWaitQueueNotify(WaitQueueNotify* curr) { WASM_UNREACHABLE("TODO"); } }; struct TypeGeneralizing : WalkerPass> { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 52a6ad9dfeb..f1b66067c25 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -702,6 +702,8 @@ enum ASTNodes { I64AtomicWait = 0x02, AtomicFence = 0x03, Pause = 0x04, + WaitQueueWait = 0x05, + WaitQueueNotify = 0x06, I32AtomicLoad = 0x10, I64AtomicLoad = 0x11, @@ -1252,9 +1254,10 @@ enum ASTNodes { Resume = 0xe3, ResumeThrow = 0xe4, ResumeThrowRef = 0xe5, - Switch = 0xe6, // NOTE(dhil): the internal class is known as - // StackSwitch to avoid conflict with the existing - // 'switch table'. + Switch = 0xe6, // NOTE(dhil): the internal class is known as + // StackSwitch to avoid conflict with the existing + // 'switch table'. + OnLabel = 0x00, // (on $tag $label) OnSwitch = 0x01 // (on $tag switch) }; diff --git a/src/wasm-builder.h b/src/wasm-builder.h index cd73babb6ca..0b70bfeaaf9 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1363,6 +1363,26 @@ class Builder { return ret; } + WaitQueueWait* makeWaitQueueWait(Expression* waitqueue, + Expression* value, + Expression* timeout) { + auto* ret = wasm.allocator.alloc(); + ret->waitqueue = waitqueue; + ret->value = value; + ret->timeout = timeout; + ret->finalize(); + return ret; + } + + WaitQueueNotify* makeWaitQueueNotify(Expression* waitqueue, + Expression* count) { + auto* ret = wasm.allocator.alloc(); + ret->waitqueue = waitqueue; + ret->count = count; + ret->finalize(); + return ret; + } + // Additional helpers Drop* makeDrop(Expression* value) { diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 895fa8b9276..f194283f6b8 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -887,6 +887,17 @@ DELEGATE_FIELD_CHILD_VECTOR(StackSwitch, operands) DELEGATE_FIELD_NAME_KIND(StackSwitch, tag, ModuleItemKind::Tag) DELEGATE_FIELD_CASE_END(StackSwitch) +DELEGATE_FIELD_CASE_START(WaitQueueWait) +DELEGATE_FIELD_CHILD(WaitQueueWait, timeout) +DELEGATE_FIELD_CHILD(WaitQueueWait, value) +DELEGATE_FIELD_CHILD(WaitQueueWait, waitqueue) +DELEGATE_FIELD_CASE_END(WaitQueueWait) + +DELEGATE_FIELD_CASE_START(WaitQueueNotify) +DELEGATE_FIELD_CHILD(WaitQueueNotify, count) +DELEGATE_FIELD_CHILD(WaitQueueNotify, waitqueue) +DELEGATE_FIELD_CASE_END(WaitQueueNotify) + DELEGATE_FIELD_MAIN_END diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index dcec0e6e938..d5a23b2c8d2 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -115,5 +115,7 @@ DELEGATE(Suspend); DELEGATE(Resume); DELEGATE(ResumeThrow); DELEGATE(StackSwitch); +DELEGATE(WaitQueueWait); +DELEGATE(WaitQueueNotify); #undef DELEGATE diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 5ae570437ed..cdafcc894b8 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2932,6 +2932,12 @@ class ConstantExpressionRunner : public ExpressionRunner { Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); } Flow visitResumeThrow(ResumeThrow* curr) { return Flow(NONCONSTANT_FLOW); } Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitWaitQueueWait(WaitQueueWait* curr) { + return Flow(NONCONSTANT_FLOW); + } + Flow visitWaitQueueNotify(WaitQueueNotify* curr) { + return Flow(NONCONSTANT_FLOW); + } void trap(std::string_view why) override { throw NonconstantException(); } @@ -4918,6 +4924,12 @@ class ModuleRunnerBase : public ExpressionRunner { Flow visitResume(Resume* curr) { return doResume(curr); } Flow visitResumeThrow(ResumeThrow* curr) { return doResume(curr); } Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitWaitQueueWait(WaitQueueWait* curr) { + WASM_UNREACHABLE("waitqueue not implemented"); + } + Flow visitWaitQueueNotify(WaitQueueNotify* curr) { + WASM_UNREACHABLE("waitqueue not implemented"); + } void trap(std::string_view why) override { // Traps break all current continuations - they will never be resumable. diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 82b7fc68450..9cd3a597383 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -285,6 +285,8 @@ class IRBuilder : public UnifiedExpressionVisitor> { return makeResumeThrow(ct, Name(), tags, labels); } Result<> makeStackSwitch(HeapType ct, Name tag); + Result<> makeWaitQueueWait(); + Result<> makeWaitQueueNotify(); // Private functions that must be public for technical reasons. Result<> visitExpression(Expression*); diff --git a/src/wasm.h b/src/wasm.h index c35b1ea2531..59cbf0dd198 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -760,6 +760,8 @@ class Expression { ResumeThrowId, // Id for the stack switching `switch` StackSwitchId, + WaitQueueWaitId, + WaitQueueNotifyId, NumExpressionIds }; Id _id; @@ -2155,6 +2157,30 @@ class StackSwitch : public SpecificExpression { void finalize(); }; +class WaitQueueWait : public SpecificExpression { +public: + WaitQueueWait() = default; + WaitQueueWait(MixedArena& allocator) : WaitQueueWait() {} + + Expression* waitqueue = nullptr; + Expression* value = nullptr; + Expression* timeout = nullptr; + + void finalize(); +}; + +class WaitQueueNotify + : public SpecificExpression { +public: + WaitQueueNotify() = default; + WaitQueueNotify(MixedArena& allocator) : WaitQueueNotify() {} + + Expression* waitqueue = nullptr; + Expression* count = nullptr; + + void finalize(); +}; + // Globals struct Named { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 6783b1eafbb..9d3427a093b 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3913,6 +3913,12 @@ Result<> WasmBinaryReader::readInst() { auto type = getIndexedHeapType(); return builder.makeArrayCmpxchg(type, order); } + case BinaryConsts::WaitQueueWait: { + return builder.makeWaitQueueWait(); + } + case BinaryConsts::WaitQueueNotify: { + return builder.makeWaitQueueNotify(); + } } return Err{"unknown atomic operation " + std::to_string(op)}; } diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 23ff8764971..97e1717e649 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -662,6 +662,18 @@ struct IRBuilder::ChildPopper ConstraintCollector{builder, children}.visitStackSwitch(curr, ct); return popConstrainedChildren(children); } + + Result<> visitWaitQueueWait(WaitQueueWait* curr) { + std::vector children; + ConstraintCollector{builder, children}.visitWaitQueueWait(curr); + return popConstrainedChildren(children); + } + + Result<> visitWaitQueueNotify(WaitQueueNotify* curr) { + std::vector children; + ConstraintCollector{builder, children}.visitWaitQueueNotify(curr); + return popConstrainedChildren(children); + } }; Result<> IRBuilder::visit(Expression* curr) { @@ -2655,6 +2667,20 @@ Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) { return Ok{}; } +Result<> IRBuilder::makeWaitQueueWait() { + WaitQueueWait curr(wasm.allocator); + CHECK_ERR(ChildPopper{*this}.visitWaitQueueWait(&curr)); + push(builder.makeWaitQueueWait(curr.waitqueue, curr.value, curr.timeout)); + return Ok{}; +} + +Result<> IRBuilder::makeWaitQueueNotify() { + WaitQueueNotify curr(wasm.allocator); + CHECK_ERR(ChildPopper{*this}.visitWaitQueueNotify(&curr)); + push(builder.makeWaitQueueNotify(curr.waitqueue, curr.count)); + return Ok{}; +} + void IRBuilder::applyAnnotations(Expression* expr, const CodeAnnotation& annotation) { if (annotation.branchLikely) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 0199e217c6a..7ca2e874463 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2911,6 +2911,16 @@ void BinaryInstWriter::visitStackSwitch(StackSwitch* curr) { o << U32LEB(parent.getTagIndex(curr->tag)); } +void BinaryInstWriter::visitWaitQueueWait(WaitQueueWait* curr) { + o << static_cast(BinaryConsts::AtomicPrefix) + << static_cast(BinaryConsts::WaitQueueWait); +} + +void BinaryInstWriter::visitWaitQueueNotify(WaitQueueNotify* curr) { + o << static_cast(BinaryConsts::AtomicPrefix) + << static_cast(BinaryConsts::WaitQueueNotify); +} + void BinaryInstWriter::emitScopeEnd(Expression* curr) { assert(!breakStack.empty()); breakStack.pop_back(); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 48156d008ba..8f017b84f77 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -569,6 +569,8 @@ struct FunctionValidator : public WalkerPass> { void visitResume(Resume* curr); void visitResumeThrow(ResumeThrow* curr); void visitStackSwitch(StackSwitch* curr); + void visitWaitQueueWait(WaitQueueWait* curr); + void visitWaitQueueNotify(WaitQueueNotify* curr); void visitFunction(Function* curr); @@ -4260,6 +4262,46 @@ void FunctionValidator::visitStackSwitch(StackSwitch* curr) { "switch must be annotated with a continuation type"); } +void FunctionValidator::visitWaitQueueWait(WaitQueueWait* curr) { + shouldBeTrue( + !getModule() || getModule()->features.hasSharedEverything(), + curr, + "waitqueue.wait requires shared-everything [--enable-shared-everything]"); + shouldBeSubType( + curr->waitqueue->type, + Type(HeapType( + Struct(std::vector{Field(Field::PackedType::WaitQueue, Immutable)})), + NonNullable), + curr, + "waitqueue.wait waitqueue must be a subtype of (struct (field waitqueue))"); + shouldBeEqual(curr->value->type, + Type(Type::BasicType::i32), + curr, + "waitqueue.wait value must be an i32"); + shouldBeEqual(curr->timeout->type, + Type(Type::BasicType::i64), + curr, + "waitqueue.wait timeout must be an i64"); +} + +void FunctionValidator::visitWaitQueueNotify(WaitQueueNotify* curr) { + shouldBeTrue( + !getModule() || getModule()->features.hasSharedEverything(), + curr, + "waitqueue.notify requires shared-everything [--enable-shared-everything]"); + shouldBeSubType(curr->waitqueue->type, + Type(HeapType(Struct(std::vector{ + Field(Field::PackedType::WaitQueue, Immutable)})), + NonNullable), + curr, + "waitqueue.notify waitqueue must be a subtype of (struct " + "(field waitqueue))"); + shouldBeEqual(curr->count->type, + Type(Type::BasicType::i32), + curr, + "waitqueue.notify count must be an i32"); +} + void FunctionValidator::visitFunction(Function* curr) { FeatureSet features; // Check for things like having a rec group with GC enabled. The type we're diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 959b6cd4bfe..4408a872a7f 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -2017,4 +2017,8 @@ void Module::clearDebugInfo() { debugInfoSymbolNames.clear(); } +void WaitQueueWait::finalize() { type = Type::i32; } + +void WaitQueueNotify::finalize() { type = Type::i32; } + } // namespace wasm diff --git a/src/wasm2js.h b/src/wasm2js.h index b92a7f851e5..a6573e9c757 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2464,6 +2464,14 @@ Ref Wasm2JSBuilder::processExpression(Expression* curr, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitWaitQueueWait(WaitQueueWait* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } + Ref visitWaitQueueNotify(WaitQueueNotify* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } private: Ref makePointer(Expression* ptr, Address offset) { diff --git a/test/lit/waitqueue.wast b/test/lit/waitqueue.wast index 1a3e9726674..d7ddbadc0dc 100644 --- a/test/lit/waitqueue.wast +++ b/test/lit/waitqueue.wast @@ -4,6 +4,28 @@ ;; RTRIP: (type $t (struct (field waitqueue))) (type $t (struct (field waitqueue))) - ;; use $t so roundtripping doesn't drop the definition - (global (ref null $t) (ref.null $t)) + ;; RTRIP: (global $g (ref $t) (struct.new $t + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: )) + (global $g (ref $t) (struct.new $t (i32.const 0))) + + ;; RTRIP: (func $waitqueue.wait (type $1) (result i32) + ;; RTRIP-NEXT: (waitqueue.wait + ;; RTRIP-NEXT: (global.get $g) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i64.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + (func $waitqueue.wait (result i32) + (waitqueue.wait (global.get $g) (i32.const 0) (i64.const 0)) + ) + ;; RTRIP: (func $waitqueue.notify (type $1) (result i32) + ;; RTRIP-NEXT: (waitqueue.notify + ;; RTRIP-NEXT: (global.get $g) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + (func $waitqueue.notify (result i32) + (waitqueue.notify (global.get $g) (i32.const 0)) + ) ) diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast new file mode 100644 index 00000000000..da871dd2229 --- /dev/null +++ b/test/spec/waitqueue.wast @@ -0,0 +1,59 @@ +(assert_invalid + (module + (func (param $expected i32) (param $timeout i64) (result i32) + (waitqueue.wait (i32.const 0) (local.get $expected) (local.get $timeout)) + ) + ) "waitqueue.wait waitqueue must be a subtype of (struct (field waitqueue))" +) + +(assert_invalid + (module + (type $t (struct (field waitqueue))) + (global $g (ref $t) (struct.new $t (i32.const 0))) + (func (param $expected i32) (param $timeout i64) (result i32) + (waitqueue.wait (global.get $g) (i64.const 0) (local.get $timeout)) + ) + ) "waitqueue.wait value must be an i32" +) + +(assert_invalid + (module + (type $t (struct (field waitqueue))) + (global $g (ref $t) (struct.new $t (i32.const 0))) + (func (param $expected i32) (param $timeout i64) (result i32) + (waitqueue.wait (global.get $g) (local.get $expected) (i32.const 0)) + ) + ) "waitqueue.wait timeout must be an i64" +) + +(assert_invalid + (module + (func (param $count i32) (result i32) + (waitqueue.notify (i32.const 0) (local.get $count)) + ) + ) "waitqueue.notify waitqueue must be a subtype of (struct (field waitqueue))" +) + +(assert_invalid + (module + (type $t (struct (field waitqueue))) + (global $g (ref $t) (struct.new $t (i32.const 0))) + (func (param $count i32) (result i32) + (waitqueue.notify (global.get $g) (i64.const 0)) + ) + ) "waitqueue.notify count must be an i32" +) + +(module + (type $t (struct (field waitqueue))) + + (global $g (ref $t) (struct.new $t (i32.const 0))) + + (func (export "waitqueue.wait") (param $expected i32) (param $timeout i64) (result i32) + (waitqueue.wait (global.get $g) (local.get $expected) (local.get $timeout)) + ) + + (func (export "waitqueue.notify") (param $count i32) (result i32) + (waitqueue.notify (global.get $g) (local.get $count)) + ) +)