From 14af574ffd8bc4d04580770009b0cbe2b1d5afce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Mon, 4 Nov 2024 17:20:23 +0100 Subject: [PATCH 01/23] Stack switching proposal support This patch implements text and binary encoding/decoding support for the stack switching proposal. It does so by adapting the previous typed-continunations implementation. Particular changes: * Support for new `resume` encoding. * Added support for `resume_throw` and `switch`. * Feature flag `typed-continuations` has been renamed to `stack-switching`. A small unfortunate implementation detail is that the internal name `Switch` was already taken by the `br_table` instruction, so I opted to give the `switch` instruction the internal name `StackSwitch`. A minor detail is that I have reordered the declarations/definitions of the stack switching instructions such that they appear in ascending order according to their opcode value (this is the same order that the stack-switching explainer document present them in). --- scripts/gen-s-parser.py | 6 +- src/gen-s-parser.inc | 27 +++- src/ir/ReFinalize.cpp | 4 +- src/ir/branch-utils.h | 11 +- src/ir/child-typer.h | 25 ++- src/ir/cost.h | 26 ++- src/ir/effects.h | 36 ++++- src/ir/possible-contents.cpp | 12 +- src/ir/subtype-exprs.h | 10 +- src/parser/contexts.h | 112 ++++++++++--- src/parser/parsers.h | 86 ++++++++-- src/passes/Print.cpp | 88 ++++++++-- src/passes/TypeGeneralizing.cpp | 6 +- src/tools/tool-options.h | 2 +- src/wasm-binary.h | 15 +- src/wasm-builder.h | 48 +++++- src/wasm-delegations-fields.def | 36 ++++- src/wasm-delegations.def | 6 +- src/wasm-features.h | 12 +- src/wasm-interpreter.h | 16 +- src/wasm-ir-builder.h | 13 +- src/wasm.h | 79 +++++++-- src/wasm/wasm-binary.cpp | 153 ++++++++++++++---- src/wasm/wasm-ir-builder.cpp | 100 +++++++++--- src/wasm/wasm-stack.cpp | 47 +++++- src/wasm/wasm-type.cpp | 4 +- src/wasm/wasm-validator.cpp | 74 ++++++--- src/wasm/wasm.cpp | 77 +++++++-- src/wasm2js.h | 12 +- ...ontinuations.wast => stack_switching.wast} | 0 ...ind.wast => stack_switching_contbind.wast} | 0 ...tnew.wast => stack_switching_contnew.wast} | 0 ...esume.wast => stack_switching_resume.wast} | 0 .../basic/stack_switching_resume_throw.wast | 132 +++++++++++++++ ...pend.wast => stack_switching_suspend.wast} | 0 test/lit/help/wasm-as.test | 4 +- test/lit/help/wasm-ctor-eval.test | 4 +- test/lit/help/wasm-dis.test | 4 +- test/lit/help/wasm-emscripten-finalize.test | 4 +- test/lit/help/wasm-merge.test | 4 +- test/lit/help/wasm-metadce.test | 4 +- test/lit/help/wasm-opt.test | 4 +- test/lit/help/wasm-reduce.test | 4 +- test/lit/help/wasm-split.test | 4 +- test/lit/help/wasm2js.test | 4 +- ..._roundtrip_print-features_all-features.txt | 2 +- test/unit/test_features.py | 16 +- 47 files changed, 1087 insertions(+), 246 deletions(-) rename test/lit/basic/{typed_continuations.wast => stack_switching.wast} (100%) rename test/lit/basic/{typed_continuations_contbind.wast => stack_switching_contbind.wast} (100%) rename test/lit/basic/{typed_continuations_contnew.wast => stack_switching_contnew.wast} (100%) rename test/lit/basic/{typed_continuations_resume.wast => stack_switching_resume.wast} (100%) create mode 100644 test/lit/basic/stack_switching_resume_throw.wast rename test/lit/basic/{typed_continuations_suspend.wast => stack_switching_suspend.wast} (100%) diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index d15c07e8eca..86e807d5474 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -596,11 +596,13 @@ # Typed function references instructions ("call_ref", "makeCallRef(/*isReturn=*/false)"), ("return_call_ref", "makeCallRef(/*isReturn=*/true)"), - # Typed continuations instructions + # Stack switching instructions ("cont.new", "makeContNew()"), ("cont.bind", "makeContBind()"), - ("resume", "makeResume()"), ("suspend", "makeSuspend()"), + ("resume", "makeResume()"), + ("resume_throw", "makeResumeThrow()"), + ("switch", "makeStackSwitch()"), # GC ("ref.i31", "makeRefI31(Unshared)"), ("ref.i31_shared", "makeRefI31(Shared)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 75fda4f7a6a..7e2f030eeb2 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -4807,12 +4807,23 @@ switch (buf[0]) { default: goto parse_error; } } - case 's': - if (op == "resume"sv) { - CHECK_ERR(makeResume(ctx, pos, annotations)); - return Ok{}; + case 's': { + switch (buf[6]) { + case '\0': + if (op == "resume"sv) { + CHECK_ERR(makeResume(ctx, pos, annotations)); + return Ok{}; + } + goto parse_error; + case '_': + if (op == "resume_throw"sv) { + CHECK_ERR(makeResumeThrow(ctx, pos, annotations)); + return Ok{}; + } + goto parse_error; + default: goto parse_error; } - goto parse_error; + } case 't': { switch (buf[3]) { case 'h': @@ -5076,6 +5087,12 @@ switch (buf[0]) { return Ok{}; } goto parse_error; + case 'w': + if (op == "switch"sv) { + CHECK_ERR(makeStackSwitch(ctx, pos, annotations)); + return Ok{}; + } + goto parse_error; default: goto parse_error; } } diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index c32d6efbf4c..60329295e09 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -180,8 +180,10 @@ void ReFinalize::visitStringWTF16Get(StringWTF16Get* curr) { curr->finalize(); } void ReFinalize::visitStringSliceWTF(StringSliceWTF* curr) { curr->finalize(); } void ReFinalize::visitContNew(ContNew* curr) { curr->finalize(); } void ReFinalize::visitContBind(ContBind* curr) { curr->finalize(); } -void ReFinalize::visitResume(Resume* curr) { curr->finalize(); } void ReFinalize::visitSuspend(Suspend* curr) { curr->finalize(getModule()); } +void ReFinalize::visitResume(Resume* curr) { curr->finalize(); } +void ReFinalize::visitResumeThrow(ResumeThrow* curr) { curr->finalize(); } +void ReFinalize::visitStackSwitch(StackSwitch* curr) { curr->finalize(); } void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index 369365e728c..5cfebc52d44 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -85,7 +85,14 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) { } else if (auto* r = expr->dynCast()) { for (Index i = 0; i < r->handlerTags.size(); i++) { auto dest = r->handlerTags[i]; - if (dest == name) { + if (dest == name && !r->onTags[i]) { + func(name, r->sentTypes[i]); + } + } + } else if (auto* r = expr->dynCast()) { + for (Index i = 0; i < r->handlerTags.size(); i++) { + auto dest = r->handlerTags[i]; + if (dest == name && !r->onTags[i]) { func(name, r->sentTypes[i]); } } @@ -118,6 +125,8 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) { // The values are supplied by suspend instructions executed while running // the continuation, so we are unable to know what they will be here. func(name, nullptr); + } else if (expr->is()) { + func(name, nullptr); } else { assert(expr->is() || expr->is()); // delegate or rethrow } diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 499a7e4ddfc..70388b2bc3c 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1050,6 +1050,10 @@ template struct ChildTyper : OverriddenVisitor { note(&curr->end, Type::i32); } + void visitContNew(ContNew* curr) { + note(&curr->func, Type(curr->contType.getContinuation().type, Nullable)); + } + void visitContBind(ContBind* curr) { auto paramsBefore = curr->contTypeBefore.getContinuation().type.getSignature().params; @@ -1064,8 +1068,12 @@ template struct ChildTyper : OverriddenVisitor { note(&curr->cont, Type(curr->contTypeBefore, Nullable)); } - void visitContNew(ContNew* curr) { - note(&curr->func, Type(curr->contType.getContinuation().type, Nullable)); + void visitSuspend(Suspend* curr) { + auto params = wasm.getTag(curr->tag)->sig.params; + assert(params.size() == curr->operands.size()); + for (size_t i = 0; i < params.size(); ++i) { + note(&curr->operands[i], params[i]); + } } void visitResume(Resume* curr) { @@ -1077,12 +1085,23 @@ template struct ChildTyper : OverriddenVisitor { note(&curr->cont, Type(curr->contType, Nullable)); } - void visitSuspend(Suspend* curr) { + void visitResumeThrow(ResumeThrow* curr) { auto params = wasm.getTag(curr->tag)->sig.params; assert(params.size() == curr->operands.size()); for (size_t i = 0; i < params.size(); ++i) { note(&curr->operands[i], params[i]); } + note(&curr->cont, Type(curr->contType, Nullable)); + } + + void visitStackSwitch(StackSwitch* curr) { + auto params = curr->contType.getContinuation().type.getSignature().params; + assert(params.size() >= 1 && + ((params.size() - 1) == curr->operands.size())); + for (size_t i = 0; i < params.size() - 1; ++i) { + note(&curr->operands[i], params[i]); + } + note(&curr->cont, Type(curr->contType, Nullable)); } }; diff --git a/src/ir/cost.h b/src/ir/cost.h index fcee6c18edb..3bdf45a2c20 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -756,6 +756,10 @@ struct CostAnalyzer : public OverriddenVisitor { return 8 + visit(curr->ref) + visit(curr->start) + visit(curr->end); } + CostType visitContNew(ContNew* curr) { + // Some arbitrary "high" value, reflecting that this may allocate a stack + return 14 + visit(curr->func); + } CostType visitContBind(ContBind* curr) { // Inspired by struct.new: The only cost of cont.bind is that it may need to // allocate a buffer to hold the arguments. @@ -766,9 +770,12 @@ struct CostAnalyzer : public OverriddenVisitor { } return ret; } - CostType visitContNew(ContNew* curr) { - // Some arbitrary "high" value, reflecting that this may allocate a stack - return 14 + visit(curr->func); + CostType visitSuspend(Suspend* curr) { + CostType ret = 12; + for (auto* arg : curr->operands) { + ret += visit(arg); + } + return ret; } CostType visitResume(Resume* curr) { // Inspired by indirect calls, but twice the cost. @@ -778,8 +785,17 @@ struct CostAnalyzer : public OverriddenVisitor { } return ret; } - CostType visitSuspend(Suspend* curr) { - CostType ret = 12; + CostType visitResumeThrow(ResumeThrow* curr) { + // Inspired by indirect calls, but twice the cost. + CostType ret = 12 + visit(curr->cont); + for (auto* arg : curr->operands) { + ret += visit(arg); + } + return ret; + } + CostType visitStackSwitch(StackSwitch* curr) { + // Inspired by indirect calls, but twice the cost. + CostType ret = 12 + visit(curr->cont); for (auto* arg : curr->operands) { ret += visit(arg); } diff --git a/src/ir/effects.h b/src/ir/effects.h index 716624d6455..1f527dcf4df 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -1008,13 +1008,21 @@ class EffectAnalyzer { // traps when ref is null. parent.implicitTrap = true; } + void visitContNew(ContNew* curr) { + // traps when curr->func is null ref. + parent.implicitTrap = true; + } void visitContBind(ContBind* curr) { // traps when curr->cont is null ref. parent.implicitTrap = true; } - void visitContNew(ContNew* curr) { - // traps when curr->func is null ref. - parent.implicitTrap = true; + void visitSuspend(Suspend* curr) { + // Similar to resume/call: Suspending means that we execute arbitrary + // other code before we may resume here. + parent.calls = true; + if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) { + parent.throws_ = true; + } } void visitResume(Resume* curr) { // This acts as a kitchen sink effect. @@ -1028,10 +1036,26 @@ class EffectAnalyzer { parent.throws_ = true; } } - void visitSuspend(Suspend* curr) { - // Similar to resume/call: Suspending means that we execute arbitrary - // other code before we may resume here. + void visitResumeThrow(ResumeThrow* curr) { + // This acts as a kitchen sink effect. parent.calls = true; + + // resume_throw instructions accept nullable continuation + // references and trap on null. + parent.implicitTrap = true; + + if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) { + parent.throws_ = true; + } + } + void visitStackSwitch(StackSwitch* curr) { + // This acts as a kitchen sink effect. + parent.calls = true; + + // switch instructions accept nullable continuation references + // and trap on null. + parent.implicitTrap = true; + if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) { parent.throws_ = true; } diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 17e40f1d805..c92c20e9c8e 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -1225,11 +1225,15 @@ struct InfoCollector void visitReturn(Return* curr) { addResult(curr->value); } + void visitContNew(ContNew* curr) { + // TODO: optimize when possible + addRoot(curr); + } void visitContBind(ContBind* curr) { // TODO: optimize when possible addRoot(curr); } - void visitContNew(ContNew* curr) { + void visitSuspend(Suspend* curr) { // TODO: optimize when possible addRoot(curr); } @@ -1237,7 +1241,11 @@ struct InfoCollector // TODO: optimize when possible addRoot(curr); } - void visitSuspend(Suspend* curr) { + void visitResumeThrow(ResumeThrow* curr) { + // TODO: optimize when possible + addRoot(curr); + } + void visitStackSwitch(StackSwitch* curr) { // TODO: optimize when possible addRoot(curr); } diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index 1895c856ae1..9d13dd5a1a3 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -397,10 +397,16 @@ struct SubtypingDiscoverer : public OverriddenVisitor { void visitStringWTF16Get(StringWTF16Get* curr) {} void visitStringSliceWTF(StringSliceWTF* curr) {} - void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); } void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); } - void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); } + void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); } void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("not implemented"); } + void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); } + void visitResumeThrow(ResumeThrow* curr) { + WASM_UNREACHABLE("not implemented"); + } + void visitStackSwitch(StackSwitch* curr) { + WASM_UNREACHABLE("not implemented"); + } }; } // namespace wasm diff --git a/src/parser/contexts.h b/src/parser/contexts.h index b0cd1458bb6..e7804908421 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -349,6 +349,8 @@ struct NullInstrParserCtx { using ExprT = Ok; using CatchT = Ok; using CatchListT = Ok; + using OnClauseT = Ok; + using OnClauseListT = Ok; using TagLabelListT = Ok; using FieldIdxT = Ok; @@ -442,8 +444,10 @@ struct NullInstrParserCtx { return Ok{}; } - TagLabelListT makeTagLabelList() { return Ok{}; } - void appendTagLabel(TagLabelListT&, TagIdxT, LabelIdxT) {} + OnClauseListT makeOnClauseList() { return Ok{}; } + void appendOnClause(OnClauseListT&, OnClauseT) {} + OnClauseT makeOnLabel(TagIdxT, LabelIdxT) { return Ok{}; } + OnClauseT makeOnSwitch(TagIdxT) { return Ok{}; } void setSrcLoc(const std::vector&) {} @@ -840,12 +844,15 @@ struct NullInstrParserCtx { return Ok{}; } template + Result<> makeContNew(Index, const std::vector&, HeapTypeT) { + return Ok{}; + } + template Result<> makeContBind(Index, const std::vector&, HeapTypeT, HeapTypeT) { return Ok{}; } - template - Result<> makeContNew(Index, const std::vector&, HeapTypeT) { + Result<> makeSuspend(Index, const std::vector&, TagIdxT) { return Ok{}; } template @@ -855,7 +862,17 @@ struct NullInstrParserCtx { const TagLabelListT&) { return Ok{}; } - Result<> makeSuspend(Index, const std::vector&, TagIdxT) { + template + Result<> makeResumeThrow(Index, + const std::vector&, + HeapTypeT, + TagIdxT, + const TagLabelListT&) { + return Ok{}; + } + template + Result<> + makeStackSwitch(Index, const std::vector&, HeapTypeT, TagIdxT) { return Ok{}; } }; @@ -1486,9 +1503,20 @@ struct ParseDefsCtx : TypeParserCtx { CatchInfo makeCatchAll(Index label) { return {{}, label, false}; } CatchInfo makeCatchAllRef(Index label) { return {{}, label, true}; } - TagLabelListT makeTagLabelList() { return {}; } - void appendTagLabel(TagLabelListT& tagLabels, Name tag, Index label) { - tagLabels.push_back({tag, label}); + struct OnClauseInfo { + Name tag; + Index label; // unset when isOnSwitch = true. + bool isOnSwitch; + }; + + OnClauseInfo makeOnLabel(Name tag, Index label) { + return {tag, label, false}; + } + OnClauseInfo makeOnSwitch(Name tag) { return {tag, {}, true}; } + + std::vector makeOnClauseList() { return {}; } + void appendOnClause(std::vector& list, OnClauseInfo info) { + list.push_back(info); } Result getHeapTypeFromIdx(Index idx) { @@ -2593,6 +2621,12 @@ struct ParseDefsCtx : TypeParserCtx { return withLoc(pos, irBuilder.makeStringSliceWTF()); } + Result<> makeContNew(Index pos, + const std::vector& annotations, + HeapType type) { + return withLoc(pos, irBuilder.makeContNew(type)); + } + Result<> makeContBind(Index pos, const std::vector& annotations, HeapType contTypeBefore, @@ -2600,30 +2634,64 @@ struct ParseDefsCtx : TypeParserCtx { return withLoc(pos, irBuilder.makeContBind(contTypeBefore, contTypeAfter)); } - Result<> makeContNew(Index pos, - const std::vector& annotations, - HeapType type) { - return withLoc(pos, irBuilder.makeContNew(type)); + Result<> + makeSuspend(Index pos, const std::vector& annotations, Name tag) { + return withLoc(pos, irBuilder.makeSuspend(tag)); } Result<> makeResume(Index pos, const std::vector& annotations, HeapType type, - const TagLabelListT& tagLabels) { + const std::vector& resumetable) { std::vector tags; std::vector labels; - tags.reserve(tagLabels.size()); - labels.reserve(tagLabels.size()); - for (auto& [tag, label] : tagLabels) { - tags.push_back(tag); - labels.push_back(label); + std::vector onTags; + tags.reserve(resumetable.size()); + labels.reserve(resumetable.size()); + onTags.reserve(resumetable.size()); + for (const OnClauseInfo& info : resumetable) { + tags.push_back(info.tag); + if (info.isOnSwitch) { + labels.push_back(Index()); + onTags.push_back(true); + } else { + labels.push_back(info.label); + onTags.push_back(false); + } } - return withLoc(pos, irBuilder.makeResume(type, tags, labels)); + return withLoc(pos, irBuilder.makeResume(type, tags, labels, onTags)); } - Result<> - makeSuspend(Index pos, const std::vector& annotations, Name tag) { - return withLoc(pos, irBuilder.makeSuspend(tag)); + Result<> makeResumeThrow(Index pos, + const std::vector& annotations, + HeapType type, + Name tag, + const std::vector& resumetable) { + std::vector tags; + std::vector labels; + std::vector onTags; + tags.reserve(resumetable.size()); + labels.reserve(resumetable.size()); + onTags.reserve(resumetable.size()); + for (auto& info : resumetable) { + tags.push_back(info.tag); + if (info.isOnSwitch) { + labels.push_back(Index()); + onTags.push_back(true); + } else { + labels.push_back(info.label); + onTags.push_back(false); + } + } + return withLoc(pos, + irBuilder.makeResumeThrow(type, tag, tags, labels, onTags)); + } + + Result<> makeStackSwitch(Index pos, + const std::vector& annotations, + HeapType type, + Name tag) { + return withLoc(pos, irBuilder.makeStackSwitch(type, tag)); } }; diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 02c2e9e4760..4230b2b4bcd 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -297,13 +297,17 @@ Result<> makeStringWTF16Get(Ctx&, Index, const std::vector&); template Result<> makeStringSliceWTF(Ctx&, Index, const std::vector&); template +Result<> makeContNew(Ctx*, Index, const std::vector&); +template Result<> makeContBind(Ctx&, Index, const std::vector&); template -Result<> makeContNew(Ctx*, Index, const std::vector&); +Result<> makeSuspend(Ctx&, Index, const std::vector&); template Result<> makeResume(Ctx&, Index, const std::vector&); template -Result<> makeSuspend(Ctx&, Index, const std::vector&); +Result<> makeResumeThrow(Ctx&, Index, const std::vector&); +template +Result<> makeStackSwitch(Ctx&, Index, const std::vector&); template Result<> ignore(Ctx&, Index, const std::vector&) { @@ -2417,6 +2421,15 @@ Result<> makeStringSliceWTF(Ctx& ctx, return ctx.makeStringSliceWTF(pos, annotations); } +template +Result<> +makeContNew(Ctx& ctx, Index pos, const std::vector& annotations) { + auto type = typeidx(ctx); + CHECK_ERR(type); + + return ctx.makeContNew(pos, annotations, *type); +} + // contbind ::= 'cont.bind' typeidx typeidx template Result<> @@ -2432,42 +2445,85 @@ makeContBind(Ctx& ctx, Index pos, const std::vector& annotations) { template Result<> -makeContNew(Ctx& ctx, Index pos, const std::vector& annotations) { - auto type = typeidx(ctx); - CHECK_ERR(type); +makeSuspend(Ctx& ctx, Index pos, const std::vector& annotations) { + auto tag = tagidx(ctx); + CHECK_ERR(tag); - return ctx.makeContNew(pos, annotations, *type); + return ctx.makeSuspend(pos, annotations, *tag); } -// resume ::= 'resume' typeidx ('(' 'on' tagidx labelidx ')')* +// resume ::= 'resume' typeidx ('(' 'on' tagidx labelidx | 'on' tagidx switch +// ')')* template Result<> makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { auto type = typeidx(ctx); CHECK_ERR(type); - auto tagLabels = ctx.makeTagLabelList(); + auto resumetable = ctx.makeOnClauseList(); while (ctx.in.takeSExprStart("on"sv)) { auto tag = tagidx(ctx); CHECK_ERR(tag); - auto label = labelidx(ctx); - CHECK_ERR(label); - ctx.appendTagLabel(tagLabels, *tag, *label); + auto keyword = ctx.in.peekKeyword(); + if (keyword == "switch") { + ctx.in.takeKeyword(); + ctx.appendOnClause(resumetable, ctx.makeOnSwitch(*tag)); + } else { + auto label = labelidx(ctx); + CHECK_ERR(label); + ctx.appendOnClause(resumetable, ctx.makeOnLabel(*tag, *label)); + } if (!ctx.in.takeRParen()) { return ctx.in.err("expected ')' at end of handler clause"); } } - return ctx.makeResume(pos, annotations, *type, tagLabels); + return ctx.makeResume(pos, annotations, *type, resumetable); } +// resume_throw ::= 'resume_throw' typeidx tagidx ('(' 'on' tagidx labelidx | +// 'on' tagidx switch ')')* template -Result<> -makeSuspend(Ctx& ctx, Index pos, const std::vector& annotations) { +Result<> makeResumeThrow(Ctx& ctx, + Index pos, + const std::vector& annotations) { + auto type = typeidx(ctx); + CHECK_ERR(type); + auto exnTag = tagidx(ctx); + CHECK_ERR(exnTag); + + auto resumetable = ctx.makeOnClauseList(); + while (ctx.in.takeSExprStart("on"sv)) { + auto tag = tagidx(ctx); + CHECK_ERR(tag); + auto keyword = ctx.in.peekKeyword(); + if (keyword == "switch") { + ctx.in.takeKeyword(); + ctx.appendOnClause(resumetable, ctx.makeOnSwitch(*tag)); + } else { + auto label = labelidx(ctx); + CHECK_ERR(label); + ctx.appendOnClause(resumetable, ctx.makeOnLabel(*tag, *label)); + } + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected ')' at end of handler clause"); + } + } + + return ctx.makeResumeThrow(pos, annotations, *type, *exnTag, resumetable); +} + +// switch ::= 'switch' typeidx tagidx +template +Result<> makeStackSwitch(Ctx& ctx, + Index pos, + const std::vector& annotations) { + auto type = typeidx(ctx); + CHECK_ERR(type); auto tag = tagidx(ctx); CHECK_ERR(tag); - return ctx.makeSuspend(pos, annotations, *tag); + return ctx.makeStackSwitch(pos, annotations, *type, *tag); } // ======= diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 854ea0b5b4d..c78763a82d0 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -302,6 +302,9 @@ struct PrintSExpression : public UnifiedExpressionVisitor { void visitTry(Try* curr); void visitTryTable(TryTable* curr); void visitResume(Resume* curr); + void visitResumeThrow(ResumeThrow* curr); + void visitStackSwitch(StackSwitch* curr); + bool maybePrintUnreachableReplacement(Expression* curr, Type type); bool maybePrintUnreachableOrNullReplacement(Expression* curr, Type type); void visitCallRef(CallRef* curr) { @@ -2427,17 +2430,20 @@ struct PrintExpressionContents void visitStringSliceWTF(StringSliceWTF* curr) { printMedium(o, "stringview_wtf16.slice"); } + void visitContNew(ContNew* curr) { + printMedium(o, "cont.new "); + printHeapType(curr->contType); + } void visitContBind(ContBind* curr) { printMedium(o, "cont.bind "); printHeapType(curr->contTypeBefore); o << ' '; printHeapType(curr->contTypeAfter); } - void visitContNew(ContNew* curr) { - printMedium(o, "cont.new "); - printHeapType(curr->contType); + void visitSuspend(Suspend* curr) { + printMedium(o, "suspend "); + curr->tag.print(o); } - void visitResume(Resume* curr) { printMedium(o, "resume"); @@ -2449,13 +2455,41 @@ struct PrintExpressionContents printMedium(o, "on "); curr->handlerTags[i].print(o); o << ' '; - curr->handlerBlocks[i].print(o); + if (curr->onTags[i]) { + o << "switch"; + } else { + curr->handlerBlocks[i].print(o); + } o << ')'; } } + void visitResumeThrow(ResumeThrow* curr) { + printMedium(o, "resume_throw"); - void visitSuspend(Suspend* curr) { - printMedium(o, "suspend "); + o << ' '; + printHeapType(curr->contType); + o << ' '; + curr->tag.print(o); + + for (Index i = 0; i < curr->handlerTags.size(); i++) { + o << " ("; + printMedium(o, "on "); + curr->handlerTags[i].print(o); + o << ' '; + if (curr->onTags[i]) { + o << "switch"; + } else { + curr->handlerBlocks[i].print(o); + } + o << ')'; + } + } + void visitStackSwitch(StackSwitch* curr) { + printMedium(o, "switch"); + + o << ' '; + printHeapType(curr->contType); + o << ' '; curr->tag.print(o); } }; @@ -2749,7 +2783,7 @@ void PrintSExpression::visitLoop(Loop* curr) { // The parenthesis wrapping do/catch/catch_all is just a syntax and does not // affect nested depths of instructions within. // -// try-delegate is written in the forded format as +// try-delegate is written in the folded format as // (try // (do // ... @@ -2824,7 +2858,7 @@ void PrintSExpression::visitTryTable(TryTable* curr) { } void PrintSExpression::visitResume(Resume* curr) { - controlFlowDepth++; + // controlFlowDepth++; o << '('; printExpressionContents(curr); @@ -2836,7 +2870,41 @@ void PrintSExpression::visitResume(Resume* curr) { printFullLine(curr->cont); - controlFlowDepth--; + // controlFlowDepth--; + decIndent(); +} + +void PrintSExpression::visitResumeThrow(ResumeThrow* curr) { + // controlFlowDepth++; + o << '('; + printExpressionContents(curr); + + incIndent(); + + for (Index i = 0; i < curr->operands.size(); i++) { + printFullLine(curr->operands[i]); + } + + printFullLine(curr->cont); + + // controlFlowDepth--; + decIndent(); +} + +void PrintSExpression::visitStackSwitch(StackSwitch* curr) { + // controlFlowDepth++; + o << '('; + printExpressionContents(curr); + + incIndent(); + + for (Index i = 0; i < curr->operands.size(); i++) { + printFullLine(curr->operands[i]); + } + + printFullLine(curr->cont); + + // controlFlowDepth--; decIndent(); } diff --git a/src/passes/TypeGeneralizing.cpp b/src/passes/TypeGeneralizing.cpp index c0359b897c3..1d88424ff38 100644 --- a/src/passes/TypeGeneralizing.cpp +++ b/src/passes/TypeGeneralizing.cpp @@ -871,10 +871,12 @@ struct TransferFn : OverriddenVisitor { void visitStringWTF16Get(StringWTF16Get* curr) { WASM_UNREACHABLE("TODO"); } void visitStringSliceWTF(StringSliceWTF* curr) { WASM_UNREACHABLE("TODO"); } - void visitContBind(ContBind* curr) { WASM_UNREACHABLE("TODO"); } void visitContNew(ContNew* curr) { WASM_UNREACHABLE("TODO"); } - void visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); } + void visitContBind(ContBind* curr) { WASM_UNREACHABLE("TODO"); } void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("TODO"); } + void visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); } + void visitResumeThrow(ResumeThrow* curr) { WASM_UNREACHABLE("TODO"); } + void visitStackSwitch(StackSwitch* curr) { WASM_UNREACHABLE("TODO"); } }; struct TypeGeneralizing : WalkerPass> { diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index f900d76ba48..a62707e0be5 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -94,7 +94,7 @@ struct ToolOptions : public Options { .addFeature(FeatureSet::ExtendedConst, "extended const expressions") .addFeature(FeatureSet::Strings, "strings") .addFeature(FeatureSet::MultiMemory, "multimemory") - .addFeature(FeatureSet::TypedContinuations, "typed continuations") + .addFeature(FeatureSet::StackSwitching, "stack switching") .addFeature(FeatureSet::SharedEverything, "shared-everything threads") .addFeature(FeatureSet::FP16, "float 16 operations") .add("--enable-typed-function-references", diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 3c59ed3aacb..745ec10d925 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -391,7 +391,7 @@ extern const char* RelaxedSIMDFeature; extern const char* ExtendedConstFeature; extern const char* StringsFeature; extern const char* MultiMemoryFeature; -extern const char* TypedContinuationsFeature; +extern const char* StackSwitchingFeature; extern const char* SharedEverythingFeature; extern const char* FP16Feature; @@ -1140,12 +1140,17 @@ enum ASTNodes { StringNewLossyUTF8Array = 0xb4, StringEncodeLossyUTF8Array = 0xb6, - // typed continuation opcodes + // stack switching opcodes ContNew = 0xe0, ContBind = 0xe1, Suspend = 0xe2, Resume = 0xe3, - + ResumeThrow = 0xe4, + Switch = 0xe5, // 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) }; enum MemoryAccess { @@ -1788,8 +1793,10 @@ class WasmBinaryReader { void visitRefAs(RefAs* curr, uint8_t code); void visitContNew(ContNew* curr); void visitContBind(ContBind* curr); - void visitResume(Resume* curr); void visitSuspend(Suspend* curr); + void visitResume(Resume* curr); + void visitResumeThrow(ResumeThrow* curr); + void visitStackSwitch(StackSwitch* curr); [[noreturn]] void throwError(std::string text); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 8f8895781bd..481da2de8fd 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1168,6 +1168,13 @@ class Builder { ret->finalize(); return ret; } + ContNew* makeContNew(HeapType contType, Expression* func) { + auto* ret = wasm.allocator.alloc(); + ret->contType = contType; + ret->func = func; + ret->finalize(); + return ret; + } ContBind* makeContBind(HeapType contTypeBefore, HeapType contTypeAfter, const std::vector& operands, @@ -1180,31 +1187,56 @@ class Builder { ret->finalize(); return ret; } - ContNew* makeContNew(HeapType contType, Expression* func) { - auto* ret = wasm.allocator.alloc(); - ret->contType = contType; - ret->func = func; - ret->finalize(); + Suspend* makeSuspend(Name tag, const std::vector& args) { + auto* ret = wasm.allocator.alloc(); + ret->tag = tag; + ret->operands.set(args); + ret->finalize(&wasm); return ret; } Resume* makeResume(HeapType contType, const std::vector& handlerTags, const std::vector& handlerBlocks, + const std::vector& onTags, const std::vector& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); ret->contType = contType; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); + ret->onTags.set(onTags); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); return ret; } - Suspend* makeSuspend(Name tag, const std::vector& args) { - auto* ret = wasm.allocator.alloc(); + ResumeThrow* makeResumeThrow(HeapType contType, + Name tag, + const std::vector& handlerTags, + const std::vector& handlerBlocks, + const std::vector& onTags, + const std::vector& operands, + Expression* cont) { + auto* ret = wasm.allocator.alloc(); + ret->contType = contType; ret->tag = tag; - ret->operands.set(args); + ret->handlerTags.set(handlerTags); + ret->handlerBlocks.set(handlerBlocks); + ret->onTags.set(onTags); + ret->operands.set(operands); + ret->cont = cont; + ret->finalize(&wasm); + return ret; + } + StackSwitch* makeStackSwitch(HeapType contType, + Name tag, + const std::vector& operands, + Expression* cont) { + auto* ret = wasm.allocator.alloc(); + ret->contType = contType; + ret->tag = tag; + ret->operands.set(operands); + ret->cont = cont; ret->finalize(&wasm); return ret; } diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 3be04022094..a0c4b5bab39 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -765,6 +765,11 @@ DELEGATE_FIELD_CHILD(StringSliceWTF, start) DELEGATE_FIELD_CHILD(StringSliceWTF, ref) DELEGATE_FIELD_CASE_END(StringSliceWTF) +DELEGATE_FIELD_CASE_START(ContNew) +DELEGATE_FIELD_CHILD(ContNew, func) +DELEGATE_FIELD_HEAPTYPE(ContNew, contType) +DELEGATE_FIELD_CASE_END(ContNew) + DELEGATE_FIELD_CASE_START(ContBind) DELEGATE_FIELD_CHILD(ContBind, cont) DELEGATE_FIELD_CHILD_VECTOR(ContBind, operands) @@ -772,13 +777,14 @@ DELEGATE_FIELD_HEAPTYPE(ContBind, contTypeAfter) DELEGATE_FIELD_HEAPTYPE(ContBind, contTypeBefore) DELEGATE_FIELD_CASE_END(ContBind) -DELEGATE_FIELD_CASE_START(ContNew) -DELEGATE_FIELD_CHILD(ContNew, func) -DELEGATE_FIELD_HEAPTYPE(ContNew, contType) -DELEGATE_FIELD_CASE_END(ContNew) +DELEGATE_FIELD_CASE_START(Suspend) +DELEGATE_FIELD_CHILD_VECTOR(Suspend, operands) +DELEGATE_FIELD_NAME_KIND(Suspend, tag, ModuleItemKind::Tag) +DELEGATE_FIELD_CASE_END(Suspend) DELEGATE_FIELD_CASE_START(Resume) DELEGATE_FIELD_TYPE_VECTOR(Resume, sentTypes) +DELEGATE_FIELD_INT_VECTOR(Resume, onTags) DELEGATE_FIELD_CHILD(Resume, cont) DELEGATE_FIELD_CHILD_VECTOR(Resume, operands) DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(Resume, handlerBlocks) @@ -786,10 +792,24 @@ DELEGATE_FIELD_NAME_KIND_VECTOR(Resume, handlerTags, ModuleItemKind::Tag) DELEGATE_FIELD_HEAPTYPE(Resume, contType) DELEGATE_FIELD_CASE_END(Resume) -DELEGATE_FIELD_CASE_START(Suspend) -DELEGATE_FIELD_CHILD_VECTOR(Suspend, operands) -DELEGATE_FIELD_NAME_KIND(Suspend, tag, ModuleItemKind::Tag) -DELEGATE_FIELD_CASE_END(Suspend) +DELEGATE_FIELD_CASE_START(ResumeThrow) +DELEGATE_FIELD_TYPE_VECTOR(ResumeThrow, sentTypes) +DELEGATE_FIELD_INT_VECTOR(ResumeThrow, onTags) +DELEGATE_FIELD_CHILD(ResumeThrow, cont) +DELEGATE_FIELD_CHILD_VECTOR(ResumeThrow, operands) +DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(ResumeThrow, handlerBlocks) +DELEGATE_FIELD_NAME_KIND_VECTOR(ResumeThrow, handlerTags, ModuleItemKind::Tag) +DELEGATE_FIELD_HEAPTYPE(ResumeThrow, contType) +DELEGATE_FIELD_NAME_KIND(ResumeThrow, tag, ModuleItemKind::Tag) +DELEGATE_FIELD_CASE_END(ResumeThrow) + +DELEGATE_FIELD_CASE_START(StackSwitch) +DELEGATE_FIELD_CHILD(Switch, cont) +DELEGATE_FIELD_CHILD_VECTOR(StackSwitch, operands) +DELEGATE_FIELD_HEAPTYPE(StackSwitch, contType) +DELEGATE_FIELD_NAME_KIND(StackSwitch, tag, ModuleItemKind::Tag) +DELEGATE_FIELD_CASE_END(StackSwitch) + DELEGATE_FIELD_MAIN_END diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index f4552a98b20..a2d97a8e65b 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -101,9 +101,11 @@ DELEGATE(StringConcat); DELEGATE(StringEq); DELEGATE(StringWTF16Get); DELEGATE(StringSliceWTF); -DELEGATE(ContBind); DELEGATE(ContNew); -DELEGATE(Resume); +DELEGATE(ContBind); DELEGATE(Suspend); +DELEGATE(Resume); +DELEGATE(ResumeThrow); +DELEGATE(StackSwitch); #undef DELEGATE diff --git a/src/wasm-features.h b/src/wasm-features.h index 92b07b5477f..0298d245d7a 100644 --- a/src/wasm-features.h +++ b/src/wasm-features.h @@ -44,7 +44,7 @@ struct FeatureSet { ExtendedConst = 1 << 13, Strings = 1 << 14, MultiMemory = 1 << 15, - TypedContinuations = 1 << 16, + StackSwitching = 1 << 16, SharedEverything = 1 << 17, FP16 = 1 << 18, MVP = None, @@ -88,8 +88,8 @@ struct FeatureSet { return "strings"; case MultiMemory: return "multimemory"; - case TypedContinuations: - return "typed-continuations"; + case StackSwitching: + return "stack-switching"; case SharedEverything: return "shared-everything"; case FP16: @@ -138,9 +138,7 @@ struct FeatureSet { bool hasExtendedConst() const { return (features & ExtendedConst) != 0; } bool hasStrings() const { return (features & Strings) != 0; } bool hasMultiMemory() const { return (features & MultiMemory) != 0; } - bool hasTypedContinuations() const { - return (features & TypedContinuations) != 0; - } + bool hasStackSwitching() const { return (features & StackSwitching) != 0; } bool hasSharedEverything() const { return (features & SharedEverything) != 0; } @@ -166,7 +164,7 @@ struct FeatureSet { void setExtendedConst(bool v = true) { set(ExtendedConst, v); } void setStrings(bool v = true) { set(Strings, v); } void setMultiMemory(bool v = true) { set(MultiMemory, v); } - void setTypedContinuations(bool v = true) { set(TypedContinuations, v); } + void setStackSwitching(bool v = true) { set(StackSwitching, v); } void setSharedEverything(bool v = true) { set(SharedEverything, v); } void setFP16(bool v = true) { set(FP16, v); } void setMVP() { features = MVP; } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 5f20819a1c8..97abf4b411b 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2551,10 +2551,16 @@ class ConstantExpressionRunner : public ExpressionRunner { } return ExpressionRunner::visitRefAs(curr); } - Flow visitContBind(ContBind* curr) { WASM_UNREACHABLE("unimplemented"); } Flow visitContNew(ContNew* curr) { WASM_UNREACHABLE("unimplemented"); } - Flow visitResume(Resume* curr) { WASM_UNREACHABLE("unimplemented"); } + Flow visitContBind(ContBind* curr) { WASM_UNREACHABLE("unimplemented"); } Flow visitSuspend(Suspend* curr) { WASM_UNREACHABLE("unimplemented"); } + Flow visitResume(Resume* curr) { WASM_UNREACHABLE("unimplemented"); } + Flow visitResumeThrow(ResumeThrow* curr) { + WASM_UNREACHABLE("unimplemented"); + } + Flow visitStackSwitch(StackSwitch* curr) { + WASM_UNREACHABLE("unimplemented"); + } void trap(const char* why) override { throw NonconstantException(); } @@ -4250,10 +4256,12 @@ class ModuleRunnerBase : public ExpressionRunner { multiValues.pop_back(); return ret; } - Flow visitContBind(ContBind* curr) { return Flow(NONCONSTANT_FLOW); } Flow visitContNew(ContNew* curr) { return Flow(NONCONSTANT_FLOW); } - Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitContBind(ContBind* curr) { return Flow(NONCONSTANT_FLOW); } Flow visitSuspend(Suspend* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitResumeThrow(ResumeThrow* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); } void trap(const char* why) override { externalInterface->trap(why); } diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index d7a1dde87b9..f0bae5ddcc7 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -217,13 +217,20 @@ class IRBuilder : public UnifiedExpressionVisitor> { [[nodiscard]] Result<> makeStringWTF16Get(); [[nodiscard]] Result<> makeStringIterNext(); [[nodiscard]] Result<> makeStringSliceWTF(); + [[nodiscard]] Result<> makeContNew(HeapType ct); [[nodiscard]] Result<> makeContBind(HeapType contTypeBefore, HeapType contTypeAfter); - [[nodiscard]] Result<> makeContNew(HeapType ct); + [[nodiscard]] Result<> makeSuspend(Name tag); [[nodiscard]] Result<> makeResume(HeapType ct, const std::vector& tags, - const std::vector& labels); - [[nodiscard]] Result<> makeSuspend(Name tag); + const std::vector& labels, + const std::vector& onTags); + [[nodiscard]] Result<> makeResumeThrow(HeapType ct, + Name tag, + const std::vector& tags, + const std::vector& labels, + const std::vector& onTags); + [[nodiscard]] Result<> makeStackSwitch(HeapType ct, Name tag); // Private functions that must be public for technical reasons. [[nodiscard]] Result<> visitExpression(Expression*); diff --git a/src/wasm.h b/src/wasm.h index 85473b1f957..422dcfb7eac 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -736,10 +736,12 @@ class Expression { StringEqId, StringWTF16GetId, StringSliceWTFId, - ContBindId, ContNewId, - ResumeId, + ContBindId, SuspendId, + ResumeId, + ResumeThrowId, + StackSwitchId, // Id for the stack switching `switch` NumExpressionIds }; Id _id; @@ -1923,6 +1925,17 @@ class StringSliceWTF : public SpecificExpression { void finalize(); }; +class ContNew : public SpecificExpression { +public: + ContNew() = default; + ContNew(MixedArena& allocator) {} + + HeapType contType; + Expression* func; + + void finalize(); +}; + class ContBind : public SpecificExpression { public: ContBind(MixedArena& allocator) : operands(allocator) {} @@ -1935,26 +1948,31 @@ class ContBind : public SpecificExpression { void finalize(); }; -class ContNew : public SpecificExpression { +class Suspend : public SpecificExpression { public: - ContNew() = default; - ContNew(MixedArena& allocator) {} + Suspend(MixedArena& allocator) : operands(allocator) {} - HeapType contType; - Expression* func; + Name tag; + ExpressionList operands; - void finalize(); + // We need access to the module to obtain the signature of the tag, + // which determines this node's type. + // If no module is given, then the type must have been set already. + void finalize(Module* wasm = nullptr); }; class Resume : public SpecificExpression { public: Resume(MixedArena& allocator) - : handlerTags(allocator), handlerBlocks(allocator), operands(allocator), - sentTypes(allocator) {} + : handlerTags(allocator), handlerBlocks(allocator), onTags(allocator), + operands(allocator), sentTypes(allocator) {} HeapType contType; ArenaVector handlerTags; + // Empty name (i.e. `Name()`) for switch tags. ArenaVector handlerBlocks; + // False if (on $tag $label) and true when (on $tag switch). + ArenaVector onTags; ExpressionList operands; Expression* cont; @@ -1972,16 +1990,47 @@ class Resume : public SpecificExpression { ArenaVector sentTypes; }; -class Suspend : public SpecificExpression { +class ResumeThrow : public SpecificExpression { public: - Suspend(MixedArena& allocator) : operands(allocator) {} + ResumeThrow(MixedArena& allocator) + : handlerTags(allocator), handlerBlocks(allocator), onTags(allocator), + operands(allocator), sentTypes(allocator) {} + HeapType contType; Name tag; + ArenaVector handlerTags; + // Empty name (i.e. `Name()`) for switch tags. + ArenaVector handlerBlocks; + // False if (on $tag $label) and true when (on $tag switch). + ArenaVector onTags; + ExpressionList operands; + Expression* cont; - // We need access to the module to obtain the signature of the tag, - // which determines this node's type. - // If no module is given, then the type must have been set already. + // When 'Module*' parameter is given, we populate the 'sentTypes' array, so + // that the types can be accessed in other analyses without accessing the + // module. + void finalize(Module* wasm = nullptr); + + // sentTypes[i] contains the type of the values that will be sent to the block + // handlerBlocks[i] if suspending with tag handlerTags[i]. Not part of the + // instruction's syntax, but stored here for subsequent use. + // This information is cached here in order not to query the module + // every time we query the sent types. + ArenaVector sentTypes; +}; + +class StackSwitch : public SpecificExpression { +public: + StackSwitch(MixedArena& allocator) : operands(allocator) {} + + HeapType contType; + Name tag; + + ExpressionList operands; + Expression* cont; + + // We need access to the module to obtain the signature of the tag. void finalize(Module* wasm = nullptr); }; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index bc7ec8ac898..a56c26d4444 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1347,8 +1347,8 @@ void WasmBinaryWriter::writeFeaturesSection() { return BinaryConsts::CustomSections::StringsFeature; case FeatureSet::MultiMemory: return BinaryConsts::CustomSections::MultiMemoryFeature; - case FeatureSet::TypedContinuations: - return BinaryConsts::CustomSections::TypedContinuationsFeature; + case FeatureSet::StackSwitching: + return BinaryConsts::CustomSections::StackSwitchingFeature; case FeatureSet::SharedEverything: return BinaryConsts::CustomSections::SharedEverythingFeature; case FeatureSet::FP16: @@ -3864,9 +3864,8 @@ void WasmBinaryReader::readFeatures(size_t payloadLen) { feature = FeatureSet::Strings; } else if (name == BinaryConsts::CustomSections::MultiMemoryFeature) { feature = FeatureSet::MultiMemory; - } else if (name == - BinaryConsts::CustomSections::TypedContinuationsFeature) { - feature = FeatureSet::TypedContinuations; + } else if (name == BinaryConsts::CustomSections::StackSwitchingFeature) { + feature = FeatureSet::StackSwitching; } else if (name == BinaryConsts::CustomSections::SharedEverythingFeature) { feature = FeatureSet::SharedEverything; } else if (name == BinaryConsts::CustomSections::FP16Feature) { @@ -4133,24 +4132,34 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) { visitCallRef(call); break; } - case BinaryConsts::ContBind: { - visitContBind((curr = allocator.alloc())->cast()); - break; - } case BinaryConsts::ContNew: { auto contNew = allocator.alloc(); curr = contNew; visitContNew(contNew); break; } - case BinaryConsts::Resume: { - visitResume((curr = allocator.alloc())->cast()); + case BinaryConsts::ContBind: { + visitContBind((curr = allocator.alloc())->cast()); break; } case BinaryConsts::Suspend: { visitSuspend((curr = allocator.alloc())->cast()); break; } + case BinaryConsts::Resume: { + visitResume((curr = allocator.alloc())->cast()); + break; + } + case BinaryConsts::ResumeThrow: { + visitResumeThrow( + (curr = allocator.alloc())->cast()); + break; + } + case BinaryConsts::Switch: { + visitStackSwitch( + (curr = allocator.alloc())->cast()); + break; + } case BinaryConsts::AtomicPrefix: { code = static_cast(getU32LEB()); if (maybeVisitLoad(curr, code, BinaryConsts::AtomicPrefix)) { @@ -7870,6 +7879,19 @@ void WasmBinaryReader::visitRefAs(RefAs* curr, uint8_t code) { curr->finalize(); } +void WasmBinaryReader::visitContNew(ContNew* curr) { + + auto contTypeIndex = getU32LEB(); + curr->contType = getTypeByIndex(contTypeIndex); + if (!curr->contType.isContinuation()) { + throwError("non-continuation type in cont.new instruction " + + curr->contType.toString()); + } + + curr->func = popNonVoidExpression(); + curr->finalize(); +} + void WasmBinaryReader::visitContBind(ContBind* curr) { auto contTypeBeforeIndex = getU32LEB(); @@ -7906,17 +7928,23 @@ void WasmBinaryReader::visitContBind(ContBind* curr) { curr->finalize(); } -void WasmBinaryReader::visitContNew(ContNew* curr) { +void WasmBinaryReader::visitSuspend(Suspend* curr) { - auto contTypeIndex = getU32LEB(); - curr->contType = getTypeByIndex(contTypeIndex); - if (!curr->contType.isContinuation()) { - throwError("non-continuation type in cont.new instruction " + - curr->contType.toString()); + auto tagIndex = getU32LEB(); + if (tagIndex >= wasm.tags.size()) { + throwError("bad tag index"); } + auto* tag = wasm.tags[tagIndex].get(); + curr->tag = tag->name; + tagRefs[tagIndex].push_back(&curr->tag); - curr->func = popNonVoidExpression(); - curr->finalize(); + auto numArgs = tag->sig.params.size(); + curr->operands.resize(numArgs); + for (size_t i = 0; i < numArgs; i++) { + curr->operands[numArgs - i - 1] = popNonVoidExpression(); + } + + curr->finalize(&wasm); } void WasmBinaryReader::visitResume(Resume* curr) { @@ -7935,16 +7963,25 @@ void WasmBinaryReader::visitResume(Resume* curr) { // valid until processNames ran. curr->handlerTags.resize(numHandlers); curr->handlerBlocks.resize(numHandlers); + curr->onTags.resize(numHandlers); for (size_t i = 0; i < numHandlers; i++) { + uint8_t code = getInt8(); auto tagIndex = getU32LEB(); auto tag = getTagName(tagIndex); - - auto handlerIndex = getU32LEB(); - auto handler = getBreakTarget(handlerIndex).name; + Name handler; + if (code == BinaryConsts::OnLabel) { // expect (on $tag $label) + auto handlerIndex = getU32LEB(); + handler = getBreakTarget(handlerIndex).name; + } else if (code == BinaryConsts::OnSwitch) { // expect (on $tag switch) + handler = Name(); + } else { // error + throwError("ON opcode expected"); + } curr->handlerTags[i] = tag; curr->handlerBlocks[i] = handler; + curr->onTags[i] = static_cast(code); // 0x00 is false, 0x01 is true // We don't know the final name yet tagRefs[tagIndex].push_back(&curr->handlerTags[i]); @@ -7962,17 +7999,79 @@ void WasmBinaryReader::visitResume(Resume* curr) { curr->finalize(&wasm); } -void WasmBinaryReader::visitSuspend(Suspend* curr) { +void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { + auto contTypeIndex = getU32LEB(); + curr->contType = getTypeByIndex(contTypeIndex); + if (!curr->contType.isContinuation()) { + throwError("non-continuation type in resume_throw instruction " + + curr->contType.toString()); + } + auto exnTagIndex = getU32LEB(); + auto* exnTag = wasm.tags[exnTagIndex].get(); + curr->tag = exnTag->name; + tagRefs[exnTagIndex].push_back(&curr->tag); - auto tagIndex = getU32LEB(); - if (tagIndex >= wasm.tags.size()) { - throwError("bad tag index"); + auto numHandlers = getU32LEB(); + + // We *must* bring the handlerTags vector to an appropriate size to ensure + // that we do not invalidate the pointers we add to tagRefs. They need to stay + // valid until processNames ran. + curr->handlerTags.resize(numHandlers); + curr->handlerBlocks.resize(numHandlers); + curr->onTags.resize(numHandlers); + + for (size_t i = 0; i < numHandlers; i++) { + uint8_t code = getInt8(); + auto tagIndex = getU32LEB(); + auto tag = getTagName(tagIndex); + Name handler; + if (code == BinaryConsts::OnLabel) { // expect (on $tag $label) + auto handlerIndex = getU32LEB(); + handler = getBreakTarget(handlerIndex).name; + } else if (code == BinaryConsts::OnSwitch) { // expect (on $tag switch) + handler = Name(); + } else { // error + throwError("ON opcode expected"); + } + + curr->handlerTags[i] = tag; + curr->handlerBlocks[i] = handler; + curr->onTags[i] = static_cast(code); // 0x00 is false, 0x01 is true + + // We don't know the final name yet + tagRefs[tagIndex].push_back(&curr->handlerTags[i]); + } + + curr->cont = popNonVoidExpression(); + + auto numArgs = exnTag->sig.params.size(); + curr->operands.resize(numArgs); + for (size_t i = 0; i < numArgs; i++) { + curr->operands[numArgs - i - 1] = popNonVoidExpression(); + } + + curr->finalize(&wasm); +} + +void WasmBinaryReader::visitStackSwitch(StackSwitch* curr) { + auto contTypeIndex = getU32LEB(); + curr->contType = getTypeByIndex(contTypeIndex); + if (!curr->contType.isContinuation()) { + throwError("non-continuation type in switch instruction " + + curr->contType.toString()); } + auto tagIndex = getU32LEB(); auto* tag = wasm.tags[tagIndex].get(); curr->tag = tag->name; tagRefs[tagIndex].push_back(&curr->tag); - auto numArgs = tag->sig.params.size(); + auto numArgs = + curr->contType.getContinuation().type.getSignature().params.size(); + if (numArgs < 1) { + throwError("switch requires a higher order continuation argument"); + } + numArgs = numArgs - 1; + curr->cont = popNonVoidExpression(); curr->operands.resize(numArgs); for (size_t i = 0; i < numArgs; i++) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index b238a926c42..72c8846fd1e 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1897,6 +1897,18 @@ Result<> IRBuilder::makeStringSliceWTF() { return Ok{}; } +Result<> IRBuilder::makeContNew(HeapType ct) { + if (!ct.isContinuation()) { + return Err{"expected continuation type"}; + } + ContNew curr; + curr.contType = ct; + CHECK_ERR(visitContNew(&curr)); + + push(builder.makeContNew(ct, curr.func)); + return Ok{}; +} + Result<> IRBuilder::makeContBind(HeapType contTypeBefore, HeapType contTypeAfter) { if (!contTypeBefore.isContinuation() || !contTypeAfter.isContinuation()) { @@ -1924,49 +1936,101 @@ Result<> IRBuilder::makeContBind(HeapType contTypeBefore, return Ok{}; } -Result<> IRBuilder::makeContNew(HeapType ct) { - if (!ct.isContinuation()) { - return Err{"expected continuation type"}; - } - ContNew curr; - curr.contType = ct; - CHECK_ERR(visitContNew(&curr)); +Result<> IRBuilder::makeSuspend(Name tag) { + Suspend curr(wasm.allocator); + curr.tag = tag; + curr.operands.resize(wasm.getTag(tag)->sig.params.size()); + CHECK_ERR(visitSuspend(&curr)); - push(builder.makeContNew(ct, curr.func)); + std::vector operands(curr.operands.begin(), curr.operands.end()); + push(builder.makeSuspend(tag, operands)); return Ok{}; } Result<> IRBuilder::makeResume(HeapType ct, const std::vector& tags, - const std::vector& labels) { + const std::vector& labels, + const std::vector& onTags) { if (!ct.isContinuation()) { return Err{"expected continuation type"}; } Resume curr(wasm.allocator); curr.contType = ct; curr.operands.resize(ct.getContinuation().type.getSignature().params.size()); + curr.handlerTags.set(tags); + curr.onTags.set(onTags); CHECK_ERR(visitResume(&curr)); std::vector labelNames; labelNames.reserve(labels.size()); - for (auto label : labels) { - auto name = getLabelName(label); - CHECK_ERR(name); - labelNames.push_back(*name); + for (size_t i = 0; i < labels.size(); i++) { + if (curr.onTags[i]) { + labelNames.push_back(Name()); + } else { + auto name = getLabelName(labels[i]); + CHECK_ERR(name); + labelNames.push_back(*name); + } } + curr.handlerBlocks.set(labelNames); std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResume(ct, tags, labelNames, operands, curr.cont)); + push(builder.makeResume(ct, tags, labelNames, onTags, operands, curr.cont)); return Ok{}; } -Result<> IRBuilder::makeSuspend(Name tag) { - Suspend curr(wasm.allocator); +Result<> IRBuilder::makeResumeThrow(HeapType ct, + Name tag, + const std::vector& tags, + const std::vector& labels, + const std::vector& onTags) { + if (!ct.isContinuation()) { + return Err{"expected continuation type"}; + } + ResumeThrow curr(wasm.allocator); + curr.contType = ct; curr.tag = tag; curr.operands.resize(wasm.getTag(tag)->sig.params.size()); - CHECK_ERR(visitSuspend(&curr)); + curr.handlerTags.set(tags); + curr.onTags.set(onTags); + CHECK_ERR(visitResumeThrow(&curr)); + std::vector labelNames; + labelNames.reserve(labels.size()); + for (size_t i = 0; i < labels.size(); i++) { + if (curr.onTags[i]) { + labelNames.push_back(Name()); + } else { + auto name = getLabelName(labels[i]); + CHECK_ERR(name); + labelNames.push_back(*name); + } + } + curr.handlerBlocks.set(labelNames); std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeSuspend(tag, operands)); + push(builder.makeResumeThrow( + ct, tag, tags, labelNames, onTags, operands, curr.cont)); + return Ok{}; +} + +Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) { + if (!ct.isContinuation()) { + return Err{"expected continuation type"}; + } + StackSwitch curr(wasm.allocator); + curr.contType = ct; + curr.tag = tag; + auto nparams = ct.getContinuation().type.getSignature().params.size(); + if (nparams < 1) { + return Err{"arity mismatch: the continuation argument must have, at least, " + "unary arity"}; + } + curr.operands.resize(nparams - 1); // the continuation argument of + // the continuation is synthetic, + // i.e. it is provided by the + // runtime. + CHECK_ERR(visitStackSwitch(&curr)); + std::vector operands(curr.operands.begin(), curr.operands.end()); + push(builder.makeStackSwitch(ct, tag, operands, curr.cont)); return Ok{}; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 7194229fea0..8e7dca97cca 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2608,31 +2608,62 @@ void BinaryInstWriter::visitStringSliceWTF(StringSliceWTF* curr) { << U32LEB(BinaryConsts::StringViewWTF16Slice); } +void BinaryInstWriter::visitContNew(ContNew* curr) { + o << int8_t(BinaryConsts::ContNew); + parent.writeIndexedHeapType(curr->contType); +} + +void BinaryInstWriter::visitSuspend(Suspend* curr) { + o << int8_t(BinaryConsts::Suspend) << U32LEB(parent.getTagIndex(curr->tag)); +} + void BinaryInstWriter::visitContBind(ContBind* curr) { o << int8_t(BinaryConsts::ContBind); parent.writeIndexedHeapType(curr->contTypeBefore); parent.writeIndexedHeapType(curr->contTypeAfter); } -void BinaryInstWriter::visitContNew(ContNew* curr) { - o << int8_t(BinaryConsts::ContNew); +void BinaryInstWriter::visitResume(Resume* curr) { + o << int8_t(BinaryConsts::Resume); parent.writeIndexedHeapType(curr->contType); + + size_t handlerNum = curr->handlerTags.size(); + o << U32LEB(handlerNum); + for (size_t i = 0; i < handlerNum; i++) { + if (curr->onTags[i]) { // on switch + o << int8_t(BinaryConsts::OnSwitch) + << U32LEB(parent.getTagIndex(curr->handlerTags[i])); + } else { // on label + o << int8_t(BinaryConsts::OnLabel) + << U32LEB(parent.getTagIndex(curr->handlerTags[i])) + << U32LEB(getBreakIndex(curr->handlerBlocks[i])); + } + } } -void BinaryInstWriter::visitResume(Resume* curr) { - o << int8_t(BinaryConsts::Resume); +void BinaryInstWriter::visitResumeThrow(ResumeThrow* curr) { + o << int8_t(BinaryConsts::ResumeThrow); parent.writeIndexedHeapType(curr->contType); + o << U32LEB(parent.getTagIndex(curr->tag)); size_t handlerNum = curr->handlerTags.size(); o << U32LEB(handlerNum); for (size_t i = 0; i < handlerNum; i++) { - o << U32LEB(parent.getTagIndex(curr->handlerTags[i])) - << U32LEB(getBreakIndex(curr->handlerBlocks[i])); + if (curr->onTags[i]) { // on switch + o << int8_t(BinaryConsts::OnSwitch) + << U32LEB(parent.getTagIndex(curr->handlerTags[i])); + } else { // on label + o << int8_t(BinaryConsts::OnLabel) + << U32LEB(parent.getTagIndex(curr->handlerTags[i])) + << U32LEB(getBreakIndex(curr->handlerBlocks[i])); + } } } -void BinaryInstWriter::visitSuspend(Suspend* curr) { - o << int8_t(BinaryConsts::Suspend) << U32LEB(parent.getTagIndex(curr->tag)); +void BinaryInstWriter::visitStackSwitch(StackSwitch* curr) { + o << int8_t(BinaryConsts::Switch); + parent.writeIndexedHeapType(curr->contType); + o << U32LEB(parent.getTagIndex(curr->tag)); } void BinaryInstWriter::emitScopeEnd(Expression* curr) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 9bb4f425907..2a981e7626a 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -1421,7 +1421,7 @@ FeatureSet HeapType::getFeatures() const { return; case HeapType::cont: case HeapType::nocont: - feats |= FeatureSet::TypedContinuations; + feats |= FeatureSet::StackSwitching; return; } } @@ -1447,7 +1447,7 @@ FeatureSet HeapType::getFeatures() const { feats |= FeatureSet::Multivalue; } } else if (heapType->isContinuation()) { - feats |= FeatureSet::TypedContinuations; + feats |= FeatureSet::StackSwitching; } // In addition, scan their non-ref children, to add dependencies on diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 5867c04a06a..dd1121a51f0 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -508,10 +508,12 @@ struct FunctionValidator : public WalkerPass> { void visitStringEq(StringEq* curr); void visitStringWTF16Get(StringWTF16Get* curr); void visitStringSliceWTF(StringSliceWTF* curr); - void visitContBind(ContBind* curr); void visitContNew(ContNew* curr); - void visitResume(Resume* curr); + void visitContBind(ContBind* curr); void visitSuspend(Suspend* curr); + void visitResume(Resume* curr); + void visitResumeThrow(ResumeThrow* curr); + void visitStackSwitch(StackSwitch* curr); void visitFunction(Function* curr); @@ -3477,12 +3479,23 @@ void FunctionValidator::visitStringSliceWTF(StringSliceWTF* curr) { "string operations require reference-types [--enable-strings]"); } +void FunctionValidator::visitContNew(ContNew* curr) { + // TODO implement actual type-checking + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), + curr, + "cont.new requires stack-switching [--enable-stack-switching]"); + + shouldBeTrue((curr->contType.isContinuation() && + curr->contType.getContinuation().type.isSignature()), + curr, + "invalid type in ContNew expression"); +} + void FunctionValidator::visitContBind(ContBind* curr) { // TODO implement actual type-checking - shouldBeTrue( - !getModule() || getModule()->features.hasTypedContinuations(), - curr, - "cont.bind requires typed-continuatons [--enable-typed-continuations]"); + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), + curr, + "cont.bind requires stack-switching [--enable-stack-switching]"); shouldBeTrue((curr->contTypeBefore.isContinuation() && curr->contTypeBefore.getContinuation().type.isSignature()), @@ -3495,43 +3508,58 @@ void FunctionValidator::visitContBind(ContBind* curr) { "invalid second type in ContBind expression"); } -void FunctionValidator::visitContNew(ContNew* curr) { +void FunctionValidator::visitSuspend(Suspend* curr) { // TODO implement actual type-checking + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), + curr, + "suspend requires stack-switching [--enable-stack-switching]"); +} + +void FunctionValidator::visitResume(Resume* curr) { + // TODO implement actual type-checking + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), + curr, + "resume requires stack-switching [--enable-stack-switching]"); + shouldBeTrue( - !getModule() || getModule()->features.hasTypedContinuations(), + curr->sentTypes.size() == curr->handlerBlocks.size(), curr, - "cont.new requires typed-continuatons [--enable-typed-continuations]"); + "sentTypes cache in Resume instruction has not been initialized"); shouldBeTrue((curr->contType.isContinuation() && curr->contType.getContinuation().type.isSignature()), curr, - "invalid type in ContNew expression"); + "invalid type in Resume expression"); } -void FunctionValidator::visitResume(Resume* curr) { +void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { // TODO implement actual type-checking shouldBeTrue( - !getModule() || getModule()->features.hasTypedContinuations(), + !getModule() || getModule()->features.hasStackSwitching(), curr, - "resume requires typed-continuatons [--enable-typed-continuations]"); + "resume_throw requires stack-switching [--enable-stack-switching]"); shouldBeTrue( curr->sentTypes.size() == curr->handlerBlocks.size(), curr, - "sentTypes cache in Resume instruction has not been initialized"); + "sentTypes cache in ResumeThrow instruction has not been initialized"); shouldBeTrue((curr->contType.isContinuation() && curr->contType.getContinuation().type.isSignature()), curr, - "invalid type in Resume expression"); + "invalid type in ResumeThrow expression"); } -void FunctionValidator::visitSuspend(Suspend* curr) { +void FunctionValidator::visitStackSwitch(StackSwitch* curr) { // TODO implement actual type-checking - shouldBeTrue( - !getModule() || getModule()->features.hasTypedContinuations(), - curr, - "suspend requires typed-continuations [--enable-typed-continuations]"); + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), + curr, + "switch requires stack-switching [--enable-stack-switching]"); + + shouldBeTrue((curr->contType.isContinuation() && + curr->contType.getContinuation().type.isSignature()), + curr, + "invalid type in Switch expression"); } void FunctionValidator::visitFunction(Function* curr) { @@ -4035,10 +4063,10 @@ static void validateTags(Module& module, ValidationInfo& info) { } for (auto& curr : module.tags) { if (curr->sig.results != Type(Type::none)) { - info.shouldBeTrue(module.features.hasTypedContinuations(), + info.shouldBeTrue(module.features.hasStackSwitching(), curr->name, - "Tags with result types require typed continuations " - "feature [--enable-typed-continuations]"); + "Tags with result types require stack switching " + "feature [--enable-stack-switching]"); } if (curr->sig.params.isTuple()) { info.shouldBeTrue( diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index a1ac076e08f..e7a5d8928f9 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -54,7 +54,7 @@ const char* RelaxedSIMDFeature = "relaxed-simd"; const char* ExtendedConstFeature = "extended-const"; const char* StringsFeature = "strings"; const char* MultiMemoryFeature = "multimemory"; -const char* TypedContinuationsFeature = "typed-continuations"; +const char* StackSwitchingFeature = "stack-switching"; const char* SharedEverythingFeature = "shared-everything"; const char* FP16Feature = "fp16"; } // namespace CustomSections @@ -1368,6 +1368,14 @@ void StringSliceWTF::finalize() { } } +void ContNew::finalize() { + if (func->type == Type::unreachable) { + type = Type::unreachable; + } else { + type = Type(contType, NonNullable); + } +} + void ContBind::finalize() { if (cont->type == Type::unreachable) { type = Type::unreachable; @@ -1376,11 +1384,10 @@ void ContBind::finalize() { } } -void ContNew::finalize() { - if (func->type == Type::unreachable) { - type = Type::unreachable; - } else { - type = Type(contType, NonNullable); +void Suspend::finalize(Module* wasm) { + if (!handleUnreachableOperands(this) && wasm) { + auto tag = wasm->getTag(this->tag); + type = tag->sig.results; } } @@ -1437,10 +1444,62 @@ void Resume::finalize(Module* wasm) { populateResumeSentTypes(this, wasm); } -void Suspend::finalize(Module* wasm) { +static void populateResumeThrowSentTypes(ResumeThrow* curr, Module* wasm) { + if (!wasm) { + return; + } + + const Signature& contSig = + curr->contType.getContinuation().type.getSignature(); + + // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation + // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume + // $ct ... (tag $tag $block) ... ) causes $block to receive values of the + // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont + // $ft') and ft' = [tgr*] -> [ctr*]. + // + auto& ctrs = contSig.results; + curr->sentTypes.clear(); + curr->sentTypes.resize(curr->handlerTags.size()); + for (Index i = 0; i < curr->handlerTags.size(); i++) { + auto& tag = curr->handlerTags[i]; + auto& tagSig = wasm->getTag(tag)->sig; + + auto& tgps = tagSig.params; + auto& tgrs = tagSig.results; + + HeapType ftPrime{Signature(tgrs, ctrs)}; + HeapType ctPrime{Continuation(ftPrime)}; + Type ctPrimeRef(ctPrime, Nullability::NonNullable); + + if (tgps.size() > 0) { + TypeList sentValueTypes; + sentValueTypes.reserve(tgps.size() + 1); + + sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); + sentValueTypes.push_back(ctPrimeRef); + curr->sentTypes[i] = Type(sentValueTypes); + } else { + curr->sentTypes[i] = ctPrimeRef; + } + } +} + +void ResumeThrow::finalize(Module* wasm) { + if (cont->type == Type::unreachable) { + type = Type::unreachable; + } else if (!handleUnreachableOperands(this)) { + const Signature& contSig = + this->contType.getContinuation().type.getSignature(); + type = contSig.results; + } + + populateResumeThrowSentTypes(this, wasm); +} + +void StackSwitch::finalize(Module* wasm) { if (!handleUnreachableOperands(this) && wasm) { - auto tag = wasm->getTag(this->tag); - type = tag->sig.results; + type = this->contType.getContinuation().type.getSignature().params; } } diff --git a/src/wasm2js.h b/src/wasm2js.h index 15ae019ea22..47d5abb2f91 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2401,11 +2401,15 @@ Ref Wasm2JSBuilder::processExpression(Expression* curr, ValueBuilder::makeCall(ABI::wasm2js::TRAP)); } + Ref visitContNew(ContNew* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitContBind(ContBind* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); } - Ref visitContNew(ContNew* curr) { + Ref visitSuspend(Suspend* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); } @@ -2413,7 +2417,11 @@ Ref Wasm2JSBuilder::processExpression(Expression* curr, unimplemented(curr); WASM_UNREACHABLE("unimp"); } - Ref visitSuspend(Suspend* curr) { + Ref visitResumeThrow(ResumeThrow* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } + Ref visitStackSwitch(StackSwitch* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); } diff --git a/test/lit/basic/typed_continuations.wast b/test/lit/basic/stack_switching.wast similarity index 100% rename from test/lit/basic/typed_continuations.wast rename to test/lit/basic/stack_switching.wast diff --git a/test/lit/basic/typed_continuations_contbind.wast b/test/lit/basic/stack_switching_contbind.wast similarity index 100% rename from test/lit/basic/typed_continuations_contbind.wast rename to test/lit/basic/stack_switching_contbind.wast diff --git a/test/lit/basic/typed_continuations_contnew.wast b/test/lit/basic/stack_switching_contnew.wast similarity index 100% rename from test/lit/basic/typed_continuations_contnew.wast rename to test/lit/basic/stack_switching_contnew.wast diff --git a/test/lit/basic/typed_continuations_resume.wast b/test/lit/basic/stack_switching_resume.wast similarity index 100% rename from test/lit/basic/typed_continuations_resume.wast rename to test/lit/basic/stack_switching_resume.wast diff --git a/test/lit/basic/stack_switching_resume_throw.wast b/test/lit/basic/stack_switching_resume_throw.wast new file mode 100644 index 00000000000..7d136ea3980 --- /dev/null +++ b/test/lit/basic/stack_switching_resume_throw.wast @@ -0,0 +1,132 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all -o %t.text.wast -g -S +;; RUN: wasm-as %s -all -g -o %t.wasm +;; RUN: wasm-dis %t.wasm -all -o %t.bin.wast +;; RUN: wasm-as %s -all -o %t.nodebug.wasm +;; RUN: wasm-dis %t.nodebug.wasm -all -o %t.bin.nodebug.wast +;; RUN: cat %t.text.wast | filecheck %s --check-prefix=CHECK-TEXT +;; RUN: cat %t.bin.wast | filecheck %s --check-prefix=CHECK-BIN +;; RUN: cat %t.bin.nodebug.wast | filecheck %s --check-prefix=CHECK-BIN-NODEBUG + +(module + ;; CHECK-TEXT: (type $ft (func (param i32) (result i32))) + ;; CHECK-BIN: (type $ft (func (param i32) (result i32))) + (type $ft (func (param i32) (result i32))) + ;; CHECK-TEXT: (type $ct (cont $ft)) + ;; CHECK-BIN: (type $ct (cont $ft)) + (type $ct (cont $ft)) + + ;; CHECK-TEXT: (type $2 (func (result i32 (ref $ct)))) + + ;; CHECK-TEXT: (type $3 (func (param i64))) + + ;; CHECK-TEXT: (type $4 (func (param (ref $ct)) (result i32))) + + ;; CHECK-TEXT: (tag $t (param i32) (result i32)) + ;; CHECK-BIN: (type $2 (func (result i32 (ref $ct)))) + + ;; CHECK-BIN: (type $3 (func (param i64))) + + ;; CHECK-BIN: (type $4 (func (param (ref $ct)) (result i32))) + + ;; CHECK-BIN: (tag $t (param i32) (result i32)) + (tag $t (param i32) (result i32)) + ;; CHECK-TEXT: (tag $e (param i64)) + ;; CHECK-BIN: (tag $e (param i64)) + (tag $e (param i64)) + + ;; CHECK-TEXT: (func $go (type $4) (param $x (ref $ct)) (result i32) + ;; CHECK-TEXT-NEXT: (tuple.extract 2 0 + ;; CHECK-TEXT-NEXT: (block $handler (type $2) (result i32 (ref $ct)) + ;; CHECK-TEXT-NEXT: (return + ;; CHECK-TEXT-NEXT: (resume_throw $ct $e (on $t $handler) + ;; CHECK-TEXT-NEXT: (i64.const 123) + ;; CHECK-TEXT-NEXT: (local.get $x) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $go (type $4) (param $x (ref $ct)) (result i32) + ;; CHECK-BIN-NEXT: (local $1 (tuple i32 (ref $ct))) + ;; CHECK-BIN-NEXT: (local $2 i32) + ;; CHECK-BIN-NEXT: (local.set $1 + ;; CHECK-BIN-NEXT: (block $label$1 (type $2) (result i32 (ref $ct)) + ;; CHECK-BIN-NEXT: (return + ;; CHECK-BIN-NEXT: (resume_throw $ct $e (on $t $label$1) + ;; CHECK-BIN-NEXT: (i64.const 123) + ;; CHECK-BIN-NEXT: (local.get $x) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (block (result i32) + ;; CHECK-BIN-NEXT: (local.set $2 + ;; CHECK-BIN-NEXT: (tuple.extract 2 0 + ;; CHECK-BIN-NEXT: (local.get $1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (tuple.extract 2 1 + ;; CHECK-BIN-NEXT: (local.get $1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.get $2) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $go (param $x (ref $ct)) (result i32) + (tuple.extract 2 0 + (block $handler (result i32 (ref $ct)) + (return + (resume_throw $ct $e + (on $t $handler) + (i64.const 123) + (local.get $x) + ) + ) + ) + ) + ) +) +;; CHECK-BIN-NODEBUG: (type $0 (func (param i32) (result i32))) + +;; CHECK-BIN-NODEBUG: (type $1 (cont $0)) + +;; CHECK-BIN-NODEBUG: (type $2 (func (result i32 (ref $1)))) + +;; CHECK-BIN-NODEBUG: (type $3 (func (param i64))) + +;; CHECK-BIN-NODEBUG: (type $4 (func (param (ref $1)) (result i32))) + +;; CHECK-BIN-NODEBUG: (tag $tag$0 (param i32) (result i32)) + +;; CHECK-BIN-NODEBUG: (tag $tag$1 (param i64)) + +;; CHECK-BIN-NODEBUG: (func $0 (type $4) (param $0 (ref $1)) (result i32) +;; CHECK-BIN-NODEBUG-NEXT: (local $1 (tuple i32 (ref $1))) +;; CHECK-BIN-NODEBUG-NEXT: (local $2 i32) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $1 +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (type $2) (result i32 (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (return +;; CHECK-BIN-NODEBUG-NEXT: (resume_throw $1 $tag$1 (on $tag$0 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (i64.const 123) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (block (result i32) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $2 +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 0 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $1) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $1) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $2) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) diff --git a/test/lit/basic/typed_continuations_suspend.wast b/test/lit/basic/stack_switching_suspend.wast similarity index 100% rename from test/lit/basic/typed_continuations_suspend.wast rename to test/lit/basic/stack_switching_suspend.wast diff --git a/test/lit/help/wasm-as.test b/test/lit/help/wasm-as.test index 5dd4b151573..46b03926ab6 100644 --- a/test/lit/help/wasm-as.test +++ b/test/lit/help/wasm-as.test @@ -104,9 +104,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-ctor-eval.test b/test/lit/help/wasm-ctor-eval.test index abb80b12eb6..6cb5fb6ad39 100644 --- a/test/lit/help/wasm-ctor-eval.test +++ b/test/lit/help/wasm-ctor-eval.test @@ -111,9 +111,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-dis.test b/test/lit/help/wasm-dis.test index 90bc12b9918..70801851a05 100644 --- a/test/lit/help/wasm-dis.test +++ b/test/lit/help/wasm-dis.test @@ -97,9 +97,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-emscripten-finalize.test b/test/lit/help/wasm-emscripten-finalize.test index cdd378d4082..a1b979940a9 100644 --- a/test/lit/help/wasm-emscripten-finalize.test +++ b/test/lit/help/wasm-emscripten-finalize.test @@ -139,9 +139,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-merge.test b/test/lit/help/wasm-merge.test index ee1fed13a92..e05b889c530 100644 --- a/test/lit/help/wasm-merge.test +++ b/test/lit/help/wasm-merge.test @@ -127,9 +127,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-metadce.test b/test/lit/help/wasm-metadce.test index 50295b1f02d..41569987288 100644 --- a/test/lit/help/wasm-metadce.test +++ b/test/lit/help/wasm-metadce.test @@ -731,9 +731,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index 0691d1c1839..86fe87a5ef3 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -740,9 +740,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-reduce.test b/test/lit/help/wasm-reduce.test index cc75aed2b11..ef27f04858d 100644 --- a/test/lit/help/wasm-reduce.test +++ b/test/lit/help/wasm-reduce.test @@ -133,9 +133,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-split.test b/test/lit/help/wasm-split.test index e5e73656205..142edca02bb 100644 --- a/test/lit/help/wasm-split.test +++ b/test/lit/help/wasm-split.test @@ -236,9 +236,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test index 89dcaa0280d..7f7aef782cf 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -694,9 +694,9 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-multimemory Disable multimemory ;; CHECK-NEXT: -;; CHECK-NEXT: --enable-typed-continuations Enable typed continuations +;; CHECK-NEXT: --enable-stack-switching Enable stack switching ;; CHECK-NEXT: -;; CHECK-NEXT: --disable-typed-continuations Disable typed continuations +;; CHECK-NEXT: --disable-stack-switching Disable stack switching ;; CHECK-NEXT: ;; CHECK-NEXT: --enable-shared-everything Enable shared-everything threads ;; CHECK-NEXT: diff --git a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt index 901f43bc504..d19756b8366 100644 --- a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt +++ b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt @@ -14,7 +14,7 @@ --enable-extended-const --enable-strings --enable-multimemory ---enable-typed-continuations +--enable-stack-switching --enable-shared-everything --enable-fp16 (module diff --git a/test/unit/test_features.py b/test/unit/test_features.py index f5c3fabc507..8c185ea700a 100644 --- a/test/unit/test_features.py +++ b/test/unit/test_features.py @@ -51,10 +51,10 @@ def check_gc(self, module, error): self.check_feature(module, error, '--enable-gc', ['--enable-reference-types']) - def check_typed_continuations(self, module, error): - # Typed continuations implies function references (which is provided by + def check_stack_switching(self, module, error): + # Stack switching implies function references (which is provided by # gc in binaryen, and implies reference types) and exceptions - self.check_feature(module, error, '--enable-typed-continuations', + self.check_feature(module, error, '--enable-stack-switching', ['--enable-gc', '--enable-reference-types', '--enable-exception-handling']) def test_v128_signature(self): @@ -290,9 +290,9 @@ def test_tag_results(self): (tag $foo (result i32)) ) ''' - self.check_typed_continuations(module, - 'Tags with result types require typed ' - 'continuations feature [--enable-typed-continuations]') + self.check_stack_switching(module, + 'Tags with result types require stack ' + 'switching feature [--enable-stack-switching]') def test_cont_type(self): module = ''' @@ -304,7 +304,7 @@ def test_cont_type(self): ) ) ''' - self.check_typed_continuations(module, 'all used types should be allowed') + self.check_stack_switching(module, 'all used types should be allowed') class TargetFeaturesSectionTest(utils.BinaryenTestCase): @@ -424,7 +424,7 @@ def test_emit_all_features(self): '--enable-extended-const', '--enable-strings', '--enable-multimemory', - '--enable-typed-continuations', + '--enable-stack-switching', '--enable-shared-everything', '--enable-fp16', ], p2.stdout.splitlines()) From edbc438e8465a48d5b03d9cbbb0a77edf09cdbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Tue, 5 Nov 2024 11:11:33 +0100 Subject: [PATCH 02/23] Address the 'easy' bits of Thomas' feedback --- src/ir/effects.h | 3 +++ src/parser/parsers.h | 4 +-- src/passes/Print.cpp | 6 ----- src/wasm-delegations-fields.def | 2 +- src/wasm/wasm-binary.cpp | 45 +++++++++++++++------------------ src/wasm/wasm-ir-builder.cpp | 21 +++++++-------- src/wasm/wasm-stack.cpp | 6 +++-- src/wasm/wasm-validator.cpp | 6 +++-- 8 files changed, 42 insertions(+), 51 deletions(-) diff --git a/src/ir/effects.h b/src/ir/effects.h index 1f527dcf4df..7fc203d4b91 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -1023,6 +1023,9 @@ class EffectAnalyzer { if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) { parent.throws_ = true; } + + // A suspend may go unhandled and therefore trap. + parent.implicitTrap = true; } void visitResume(Resume* curr) { // This acts as a kitchen sink effect. diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 4230b2b4bcd..6fda193548a 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -2464,9 +2464,7 @@ makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { while (ctx.in.takeSExprStart("on"sv)) { auto tag = tagidx(ctx); CHECK_ERR(tag); - auto keyword = ctx.in.peekKeyword(); - if (keyword == "switch") { - ctx.in.takeKeyword(); + if (ctx.in.takeKeyword("switch")) { ctx.appendOnClause(resumetable, ctx.makeOnSwitch(*tag)); } else { auto label = labelidx(ctx); diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index c78763a82d0..6f358da3917 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2858,7 +2858,6 @@ void PrintSExpression::visitTryTable(TryTable* curr) { } void PrintSExpression::visitResume(Resume* curr) { - // controlFlowDepth++; o << '('; printExpressionContents(curr); @@ -2870,12 +2869,10 @@ void PrintSExpression::visitResume(Resume* curr) { printFullLine(curr->cont); - // controlFlowDepth--; decIndent(); } void PrintSExpression::visitResumeThrow(ResumeThrow* curr) { - // controlFlowDepth++; o << '('; printExpressionContents(curr); @@ -2887,12 +2884,10 @@ void PrintSExpression::visitResumeThrow(ResumeThrow* curr) { printFullLine(curr->cont); - // controlFlowDepth--; decIndent(); } void PrintSExpression::visitStackSwitch(StackSwitch* curr) { - // controlFlowDepth++; o << '('; printExpressionContents(curr); @@ -2904,7 +2899,6 @@ void PrintSExpression::visitStackSwitch(StackSwitch* curr) { printFullLine(curr->cont); - // controlFlowDepth--; decIndent(); } diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index a0c4b5bab39..b9f1cd0f785 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -804,7 +804,7 @@ DELEGATE_FIELD_NAME_KIND(ResumeThrow, tag, ModuleItemKind::Tag) DELEGATE_FIELD_CASE_END(ResumeThrow) DELEGATE_FIELD_CASE_START(StackSwitch) -DELEGATE_FIELD_CHILD(Switch, cont) +DELEGATE_FIELD_CHILD(StackSwitch, cont) DELEGATE_FIELD_CHILD_VECTOR(StackSwitch, operands) DELEGATE_FIELD_HEAPTYPE(StackSwitch, contType) DELEGATE_FIELD_NAME_KIND(StackSwitch, tag, ModuleItemKind::Tag) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index a56c26d4444..473de5a07dc 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -7881,8 +7881,7 @@ void WasmBinaryReader::visitRefAs(RefAs* curr, uint8_t code) { void WasmBinaryReader::visitContNew(ContNew* curr) { - auto contTypeIndex = getU32LEB(); - curr->contType = getTypeByIndex(contTypeIndex); + curr->contType = getIndexedHeapType(); if (!curr->contType.isContinuation()) { throwError("non-continuation type in cont.new instruction " + curr->contType.toString()); @@ -7894,11 +7893,8 @@ void WasmBinaryReader::visitContNew(ContNew* curr) { void WasmBinaryReader::visitContBind(ContBind* curr) { - auto contTypeBeforeIndex = getU32LEB(); - curr->contTypeBefore = getTypeByIndex(contTypeBeforeIndex); - - auto contTypeAfterIndex = getU32LEB(); - curr->contTypeAfter = getTypeByIndex(contTypeAfterIndex); + curr->contTypeBefore = getIndexedHeapType(); + curr->contTypeAfter = getIndexedHeapType(); for (auto& ct : {curr->contTypeBefore, curr->contTypeAfter}) { if (!ct.isContinuation()) { @@ -7931,14 +7927,12 @@ void WasmBinaryReader::visitContBind(ContBind* curr) { void WasmBinaryReader::visitSuspend(Suspend* curr) { auto tagIndex = getU32LEB(); - if (tagIndex >= wasm.tags.size()) { - throwError("bad tag index"); - } - auto* tag = wasm.tags[tagIndex].get(); - curr->tag = tag->name; + curr->tag = getTagName(tagIndex); tagRefs[tagIndex].push_back(&curr->tag); - auto numArgs = tag->sig.params.size(); + // The access `tags[tagIndex]` must be in bounds as + // `getTagName(tagIndex)` succeeded above. + auto numArgs = wasm.tags[tagIndex].get()->sig.params.size(); curr->operands.resize(numArgs); for (size_t i = 0; i < numArgs; i++) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); @@ -7949,8 +7943,7 @@ void WasmBinaryReader::visitSuspend(Suspend* curr) { void WasmBinaryReader::visitResume(Resume* curr) { - auto contTypeIndex = getU32LEB(); - curr->contType = getTypeByIndex(contTypeIndex); + curr->contType = getIndexedHeapType(); if (!curr->contType.isContinuation()) { throwError("non-continuation type in resume instruction " + curr->contType.toString()); @@ -8000,15 +7993,13 @@ void WasmBinaryReader::visitResume(Resume* curr) { } void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { - auto contTypeIndex = getU32LEB(); - curr->contType = getTypeByIndex(contTypeIndex); + curr->contType = getIndexedHeapType(); if (!curr->contType.isContinuation()) { throwError("non-continuation type in resume_throw instruction " + curr->contType.toString()); } auto exnTagIndex = getU32LEB(); - auto* exnTag = wasm.tags[exnTagIndex].get(); - curr->tag = exnTag->name; + curr->tag = getTagName(exnTagIndex); tagRefs[exnTagIndex].push_back(&curr->tag); auto numHandlers = getU32LEB(); @@ -8025,12 +8016,15 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { auto tagIndex = getU32LEB(); auto tag = getTagName(tagIndex); Name handler; - if (code == BinaryConsts::OnLabel) { // expect (on $tag $label) + if (code == BinaryConsts::OnLabel) { + // expect (on $tag $label) auto handlerIndex = getU32LEB(); handler = getBreakTarget(handlerIndex).name; - } else if (code == BinaryConsts::OnSwitch) { // expect (on $tag switch) + } else if (code == BinaryConsts::OnSwitch) { + // expect (on $tag switch) handler = Name(); - } else { // error + } else { + // error throwError("ON opcode expected"); } @@ -8044,7 +8038,9 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { curr->cont = popNonVoidExpression(); - auto numArgs = exnTag->sig.params.size(); + // This `tags[exnTagIndex]` must be in bounds since + // `getTagName(exnTagIndex)` succeeded above. + auto numArgs = wasm.tags[exnTagIndex].get()->sig.params.size(); curr->operands.resize(numArgs); for (size_t i = 0; i < numArgs; i++) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); @@ -8054,8 +8050,7 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { } void WasmBinaryReader::visitStackSwitch(StackSwitch* curr) { - auto contTypeIndex = getU32LEB(); - curr->contType = getTypeByIndex(contTypeIndex); + curr->contType = getIndexedHeapType(); if (!curr->contType.isContinuation()) { throwError("non-continuation type in switch instruction " + curr->contType.toString()); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 72c8846fd1e..e9234127305 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1957,14 +1957,12 @@ Result<> IRBuilder::makeResume(HeapType ct, Resume curr(wasm.allocator); curr.contType = ct; curr.operands.resize(ct.getContinuation().type.getSignature().params.size()); - curr.handlerTags.set(tags); - curr.onTags.set(onTags); CHECK_ERR(visitResume(&curr)); std::vector labelNames; labelNames.reserve(labels.size()); for (size_t i = 0; i < labels.size(); i++) { - if (curr.onTags[i]) { + if (onTags[i]) { labelNames.push_back(Name()); } else { auto name = getLabelName(labels[i]); @@ -1972,7 +1970,7 @@ Result<> IRBuilder::makeResume(HeapType ct, labelNames.push_back(*name); } } - curr.handlerBlocks.set(labelNames); + std::vector operands(curr.operands.begin(), curr.operands.end()); push(builder.makeResume(ct, tags, labelNames, onTags, operands, curr.cont)); return Ok{}; @@ -1990,14 +1988,12 @@ Result<> IRBuilder::makeResumeThrow(HeapType ct, curr.contType = ct; curr.tag = tag; curr.operands.resize(wasm.getTag(tag)->sig.params.size()); - curr.handlerTags.set(tags); - curr.onTags.set(onTags); CHECK_ERR(visitResumeThrow(&curr)); std::vector labelNames; labelNames.reserve(labels.size()); for (size_t i = 0; i < labels.size(); i++) { - if (curr.onTags[i]) { + if (onTags[i]) { labelNames.push_back(Name()); } else { auto name = getLabelName(labels[i]); @@ -2005,7 +2001,7 @@ Result<> IRBuilder::makeResumeThrow(HeapType ct, labelNames.push_back(*name); } } - curr.handlerBlocks.set(labelNames); + std::vector operands(curr.operands.begin(), curr.operands.end()); push(builder.makeResumeThrow( ct, tag, tags, labelNames, onTags, operands, curr.cont)); @@ -2024,10 +2020,11 @@ Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) { return Err{"arity mismatch: the continuation argument must have, at least, " "unary arity"}; } - curr.operands.resize(nparams - 1); // the continuation argument of - // the continuation is synthetic, - // i.e. it is provided by the - // runtime. + + // The continuation argument of the continuation is synthetic, + // i.e. it is provided by the runtime. + curr.operands.resize(nparams - 1); + CHECK_ERR(visitStackSwitch(&curr)); std::vector operands(curr.operands.begin(), curr.operands.end()); push(builder.makeStackSwitch(ct, tag, operands, curr.cont)); diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 8e7dca97cca..00a40fd1831 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2630,10 +2630,12 @@ void BinaryInstWriter::visitResume(Resume* curr) { size_t handlerNum = curr->handlerTags.size(); o << U32LEB(handlerNum); for (size_t i = 0; i < handlerNum; i++) { - if (curr->onTags[i]) { // on switch + if (curr->onTags[i]) { + // on switch o << int8_t(BinaryConsts::OnSwitch) << U32LEB(parent.getTagIndex(curr->handlerTags[i])); - } else { // on label + } else { + // on label o << int8_t(BinaryConsts::OnLabel) << U32LEB(parent.getTagIndex(curr->handlerTags[i])) << U32LEB(getBreakIndex(curr->handlerBlocks[i])); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index dd1121a51f0..21118a02881 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3535,9 +3535,11 @@ void FunctionValidator::visitResume(Resume* curr) { void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { // TODO implement actual type-checking shouldBeTrue( - !getModule() || getModule()->features.hasStackSwitching(), + !getModule() || (getModule()->features.hasExceptionHandling() && + getModule()->features.hasStackSwitching()), curr, - "resume_throw requires stack-switching [--enable-stack-switching]"); + "resume_throw requires exception handling [--enable-exception-handling] " + "and stack-switching [--enable-stack-switching]"); shouldBeTrue( curr->sentTypes.size() == curr->handlerBlocks.size(), From c0d864823c70d0e4453d6372be3b0aaae4eaff0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Tue, 5 Nov 2024 11:50:01 +0100 Subject: [PATCH 03/23] Deduplicate the parsing logic for resume tables --- src/parser/contexts.h | 5 ++++- src/parser/parsers.h | 41 +++++++++++++++++------------------------ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/parser/contexts.h b/src/parser/contexts.h index e7804908421..5ea6125d09e 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1404,6 +1404,9 @@ struct ParseDefsCtx : TypeParserCtx { using TagLabelListT = std::vector>; + struct OnClauseInfo; + using OnClauseListT = std::vector; + Lexer in; Module& wasm; @@ -1514,7 +1517,7 @@ struct ParseDefsCtx : TypeParserCtx { } OnClauseInfo makeOnSwitch(Name tag) { return {tag, {}, true}; } - std::vector makeOnClauseList() { return {}; } + OnClauseListT makeOnClauseList() { return {}; } void appendOnClause(std::vector& list, OnClauseInfo info) { list.push_back(info); } diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 6fda193548a..cfefb63363f 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -2452,15 +2452,9 @@ makeSuspend(Ctx& ctx, Index pos, const std::vector& annotations) { return ctx.makeSuspend(pos, annotations, *tag); } -// resume ::= 'resume' typeidx ('(' 'on' tagidx labelidx | 'on' tagidx switch -// ')')* +// resumetable ::= ('(' 'on' tagidx labelidx | 'on' tagidx switch ')')* template -Result<> -makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { - auto type = typeidx(ctx); - CHECK_ERR(type); - - auto resumetable = ctx.makeOnClauseList(); +Result<> makeResumeTable(Ctx& ctx, typename Ctx::OnClauseListT& resumetable) { while (ctx.in.takeSExprStart("on"sv)) { auto tag = tagidx(ctx); CHECK_ERR(tag); @@ -2475,6 +2469,19 @@ makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { return ctx.in.err("expected ')' at end of handler clause"); } } + return Ok{}; +} + +// resume ::= 'resume' typeidx resumetable +template +Result<> +makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { + auto type = typeidx(ctx); + CHECK_ERR(type); + + auto resumetable = ctx.makeOnClauseList(); + auto ans = makeResumeTable(ctx, resumetable); + CHECK_ERR(ans); return ctx.makeResume(pos, annotations, *type, resumetable); } @@ -2491,22 +2498,8 @@ Result<> makeResumeThrow(Ctx& ctx, CHECK_ERR(exnTag); auto resumetable = ctx.makeOnClauseList(); - while (ctx.in.takeSExprStart("on"sv)) { - auto tag = tagidx(ctx); - CHECK_ERR(tag); - auto keyword = ctx.in.peekKeyword(); - if (keyword == "switch") { - ctx.in.takeKeyword(); - ctx.appendOnClause(resumetable, ctx.makeOnSwitch(*tag)); - } else { - auto label = labelidx(ctx); - CHECK_ERR(label); - ctx.appendOnClause(resumetable, ctx.makeOnLabel(*tag, *label)); - } - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected ')' at end of handler clause"); - } - } + auto ans = makeResumeTable(ctx, resumetable); + CHECK_ERR(ans); return ctx.makeResumeThrow(pos, annotations, *type, *exnTag, resumetable); } From 98f631404bd2b53a77e0604299015c0d2493fb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Tue, 5 Nov 2024 15:37:32 +0100 Subject: [PATCH 04/23] Use inherited the 'type' member to track the continuation type in 'ContNew' and 'ContBind' --- src/ir/child-typer.h | 20 +++++++++--------- src/ir/module-utils.cpp | 13 +++++++++--- src/parser/contexts.h | 6 +++--- src/parser/parsers.h | 10 ++++----- src/passes/Print.cpp | 6 +++--- src/wasm-builder.h | 12 +++++------ src/wasm-delegations-fields.def | 4 +--- src/wasm-ir-builder.h | 3 +-- src/wasm-type.h | 1 + src/wasm.h | 6 +++--- src/wasm/wasm-binary.cpp | 33 ++++++++++++++++-------------- src/wasm/wasm-ir-builder.cpp | 36 ++++++++++++++++----------------- src/wasm/wasm-stack.cpp | 6 +++--- src/wasm/wasm-type.cpp | 4 ++++ src/wasm/wasm-validator.cpp | 12 +++++------ src/wasm/wasm.cpp | 6 +----- 16 files changed, 91 insertions(+), 87 deletions(-) diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 70388b2bc3c..a74c76c6190 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1050,22 +1050,20 @@ template struct ChildTyper : OverriddenVisitor { note(&curr->end, Type::i32); } - void visitContNew(ContNew* curr) { - note(&curr->func, Type(curr->contType.getContinuation().type, Nullable)); - } + void visitContNew(ContNew* curr) { note(&curr->func, curr->type); } void visitContBind(ContBind* curr) { - auto paramsBefore = - curr->contTypeBefore.getContinuation().type.getSignature().params; - auto paramsAfter = - curr->contTypeAfter.getContinuation().type.getSignature().params; - assert(paramsBefore.size() >= paramsAfter.size()); - auto n = paramsBefore.size() - paramsAfter.size(); + auto sourceParams = + curr->sourceType.getContinuation().type.getSignature().params; + auto targetParams = + curr->type.getHeapType().getContinuation().type.getSignature().params; + assert(sourceParams.size() >= targetParams.size()); + auto n = sourceParams.size() - targetParams.size(); assert(curr->operands.size() == n); for (size_t i = 0; i < n; ++i) { - note(&curr->operands[i], paramsBefore[i]); + note(&curr->operands[i], sourceParams[i]); } - note(&curr->cont, Type(curr->contTypeBefore, Nullable)); + note(&curr->cont, Type(curr->sourceType, Nullable)); } void visitSuspend(Suspend* curr) { diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index 2f26cfa779c..4029a91554e 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -459,12 +459,19 @@ struct CodeScanner } else if (auto* set = curr->dynCast()) { info.note(set->ref->type); } else if (auto* contBind = curr->dynCast()) { - info.note(contBind->contTypeBefore); - info.note(contBind->contTypeAfter); + info.note(contBind->sourceType); + info.note(contBind->type); } else if (auto* contNew = curr->dynCast()) { - info.note(contNew->contType); + info.note(contNew->type); } else if (auto* resume = curr->dynCast()) { info.note(resume->contType); + info.note(resume->type); + } else if (auto* resumeThrow = curr->dynCast()) { + info.note(resumeThrow->contType); + info.note(resumeThrow->type); + } else if (auto* switch_ = curr->dynCast()) { + info.note(switch_->contType); + info.note(switch_->type); } else if (Properties::isControlFlowStructure(curr)) { info.noteControlFlow(Signature(Type::none, curr->type)); } diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 5ea6125d09e..b89545b27e5 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -2632,9 +2632,9 @@ struct ParseDefsCtx : TypeParserCtx { Result<> makeContBind(Index pos, const std::vector& annotations, - HeapType contTypeBefore, - HeapType contTypeAfter) { - return withLoc(pos, irBuilder.makeContBind(contTypeBefore, contTypeAfter)); + HeapType sourceType, + HeapType targetType) { + return withLoc(pos, irBuilder.makeContBind(sourceType, targetType)); } Result<> diff --git a/src/parser/parsers.h b/src/parser/parsers.h index cfefb63363f..e90a55d5eef 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -2434,13 +2434,13 @@ makeContNew(Ctx& ctx, Index pos, const std::vector& annotations) { template Result<> makeContBind(Ctx& ctx, Index pos, const std::vector& annotations) { - auto typeBefore = typeidx(ctx); - CHECK_ERR(typeBefore); + auto sourceType = typeidx(ctx); + CHECK_ERR(sourceType); - auto typeAfter = typeidx(ctx); - CHECK_ERR(typeAfter); + auto targetType = typeidx(ctx); + CHECK_ERR(targetType); - return ctx.makeContBind(pos, annotations, *typeBefore, *typeAfter); + return ctx.makeContBind(pos, annotations, *sourceType, *targetType); } template diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 6f358da3917..fde1d873c92 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2432,13 +2432,13 @@ struct PrintExpressionContents } void visitContNew(ContNew* curr) { printMedium(o, "cont.new "); - printHeapType(curr->contType); + printHeapType(curr->type.getHeapType()); } void visitContBind(ContBind* curr) { printMedium(o, "cont.bind "); - printHeapType(curr->contTypeBefore); + printHeapType(curr->sourceType); o << ' '; - printHeapType(curr->contTypeAfter); + printHeapType(curr->type.getHeapType()); } void visitSuspend(Suspend* curr) { printMedium(o, "suspend "); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 481da2de8fd..c29c5c6317f 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1168,20 +1168,20 @@ class Builder { ret->finalize(); return ret; } - ContNew* makeContNew(HeapType contType, Expression* func) { + ContNew* makeContNew(HeapType type, Expression* func) { auto* ret = wasm.allocator.alloc(); - ret->contType = contType; + ret->type = Type(type, NonNullable); ret->func = func; ret->finalize(); return ret; } - ContBind* makeContBind(HeapType contTypeBefore, - HeapType contTypeAfter, + ContBind* makeContBind(HeapType sourceType, + HeapType targetType, const std::vector& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->contTypeBefore = contTypeBefore; - ret->contTypeAfter = contTypeAfter; + ret->sourceType = sourceType; + ret->type = Type(targetType, NonNullable); ret->operands.set(operands); ret->cont = cont; ret->finalize(); diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index b9f1cd0f785..826633f9b87 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -767,14 +767,12 @@ DELEGATE_FIELD_CASE_END(StringSliceWTF) DELEGATE_FIELD_CASE_START(ContNew) DELEGATE_FIELD_CHILD(ContNew, func) -DELEGATE_FIELD_HEAPTYPE(ContNew, contType) DELEGATE_FIELD_CASE_END(ContNew) DELEGATE_FIELD_CASE_START(ContBind) DELEGATE_FIELD_CHILD(ContBind, cont) DELEGATE_FIELD_CHILD_VECTOR(ContBind, operands) -DELEGATE_FIELD_HEAPTYPE(ContBind, contTypeAfter) -DELEGATE_FIELD_HEAPTYPE(ContBind, contTypeBefore) +DELEGATE_FIELD_HEAPTYPE(ContBind, sourceType) DELEGATE_FIELD_CASE_END(ContBind) DELEGATE_FIELD_CASE_START(Suspend) diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index f0bae5ddcc7..715f6f0d349 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -218,8 +218,7 @@ class IRBuilder : public UnifiedExpressionVisitor> { [[nodiscard]] Result<> makeStringIterNext(); [[nodiscard]] Result<> makeStringSliceWTF(); [[nodiscard]] Result<> makeContNew(HeapType ct); - [[nodiscard]] Result<> makeContBind(HeapType contTypeBefore, - HeapType contTypeAfter); + [[nodiscard]] Result<> makeContBind(HeapType sourceType, HeapType targetType); [[nodiscard]] Result<> makeSuspend(Name tag); [[nodiscard]] Result<> makeResume(HeapType ct, const std::vector& tags, diff --git a/src/wasm-type.h b/src/wasm-type.h index d26ba324f04..dc7577c9e86 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -437,6 +437,7 @@ class Type { // Whether this type is only inhabited by null values. bool isNull() const; + bool isContinuation() const; bool isStruct() const; bool isArray() const; bool isExn() const; diff --git a/src/wasm.h b/src/wasm.h index 422dcfb7eac..9b9c659c015 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1930,7 +1930,6 @@ class ContNew : public SpecificExpression { ContNew() = default; ContNew(MixedArena& allocator) {} - HeapType contType; Expression* func; void finalize(); @@ -1940,8 +1939,9 @@ class ContBind : public SpecificExpression { public: ContBind(MixedArena& allocator) : operands(allocator) {} - HeapType contTypeBefore; - HeapType contTypeAfter; + // Syntax: cont.bind $src $dst + // We store $src here, and $dst in the inherited `type` member. + HeapType sourceType; ExpressionList operands; Expression* cont; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 473de5a07dc..a8442dc6e70 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -7881,41 +7881,44 @@ void WasmBinaryReader::visitRefAs(RefAs* curr, uint8_t code) { void WasmBinaryReader::visitContNew(ContNew* curr) { - curr->contType = getIndexedHeapType(); - if (!curr->contType.isContinuation()) { + auto heaptype = getIndexedHeapType(); + if (!heaptype.isContinuation()) { throwError("non-continuation type in cont.new instruction " + - curr->contType.toString()); + heaptype.toString()); } - + curr->type = Type(heaptype, NonNullable); curr->func = popNonVoidExpression(); curr->finalize(); } void WasmBinaryReader::visitContBind(ContBind* curr) { - curr->contTypeBefore = getIndexedHeapType(); - curr->contTypeAfter = getIndexedHeapType(); + auto sourceType = getIndexedHeapType(); + auto targetType = getIndexedHeapType(); - for (auto& ct : {curr->contTypeBefore, curr->contTypeAfter}) { + for (auto& ct : {sourceType, targetType}) { if (!ct.isContinuation()) { throwError("non-continuation type in cont.bind instruction " + ct.toString()); } } + curr->sourceType = sourceType; + curr->type = Type(targetType, NonNullable); + curr->cont = popNonVoidExpression(); - size_t paramsBefore = - curr->contTypeBefore.getContinuation().type.getSignature().params.size(); - size_t paramsAfter = - curr->contTypeAfter.getContinuation().type.getSignature().params.size(); - if (paramsBefore < paramsAfter) { + size_t sourceParams = + sourceType.getContinuation().type.getSignature().params.size(); + size_t targetParams = + targetType.getContinuation().type.getSignature().params.size(); + if (sourceParams < targetParams) { throwError("incompatible continuation types in cont.bind: source type " + - curr->contTypeBefore.toString() + + sourceType.toString() + " has fewer parameters than destination " + - curr->contTypeAfter.toString()); + targetType.toString()); } - size_t numArgs = paramsBefore - paramsAfter; + size_t numArgs = sourceParams - targetParams; curr->operands.resize(numArgs); for (size_t i = 0; i < numArgs; i++) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index e9234127305..23d523b580b 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1897,42 +1897,40 @@ Result<> IRBuilder::makeStringSliceWTF() { return Ok{}; } -Result<> IRBuilder::makeContNew(HeapType ct) { - if (!ct.isContinuation()) { +Result<> IRBuilder::makeContNew(HeapType type) { + if (!type.isContinuation()) { return Err{"expected continuation type"}; } ContNew curr; - curr.contType = ct; + curr.type = Type(type, NonNullable); CHECK_ERR(visitContNew(&curr)); - push(builder.makeContNew(ct, curr.func)); + push(builder.makeContNew(type, curr.func)); return Ok{}; } -Result<> IRBuilder::makeContBind(HeapType contTypeBefore, - HeapType contTypeAfter) { - if (!contTypeBefore.isContinuation() || !contTypeAfter.isContinuation()) { +Result<> IRBuilder::makeContBind(HeapType sourceType, HeapType targetType) { + if (!sourceType.isContinuation() || !targetType.isContinuation()) { return Err{"expected continuation types"}; } ContBind curr(wasm.allocator); - curr.contTypeBefore = contTypeBefore; - curr.contTypeAfter = contTypeAfter; - size_t paramsBefore = - contTypeBefore.getContinuation().type.getSignature().params.size(); - size_t paramsAfter = - contTypeAfter.getContinuation().type.getSignature().params.size(); - if (paramsBefore < paramsAfter) { + curr.sourceType = sourceType; + curr.type = Type(targetType, NonNullable); + size_t sourceParams = + sourceType.getContinuation().type.getSignature().params.size(); + size_t targetParams = + targetType.getContinuation().type.getSignature().params.size(); + if (sourceParams < targetParams) { return Err{"incompatible continuation types in cont.bind: source type " + - contTypeBefore.toString() + + sourceType.toString() + " has fewer parameters than destination " + - contTypeAfter.toString()}; + targetType.toString()}; } - curr.operands.resize(paramsBefore - paramsAfter); + curr.operands.resize(sourceParams - targetParams); CHECK_ERR(visitContBind(&curr)); std::vector operands(curr.operands.begin(), curr.operands.end()); - push( - builder.makeContBind(contTypeBefore, contTypeAfter, operands, curr.cont)); + push(builder.makeContBind(sourceType, targetType, operands, curr.cont)); return Ok{}; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 00a40fd1831..09c7788eb72 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2610,7 +2610,7 @@ void BinaryInstWriter::visitStringSliceWTF(StringSliceWTF* curr) { void BinaryInstWriter::visitContNew(ContNew* curr) { o << int8_t(BinaryConsts::ContNew); - parent.writeIndexedHeapType(curr->contType); + parent.writeIndexedHeapType(curr->type.getHeapType()); } void BinaryInstWriter::visitSuspend(Suspend* curr) { @@ -2619,8 +2619,8 @@ void BinaryInstWriter::visitSuspend(Suspend* curr) { void BinaryInstWriter::visitContBind(ContBind* curr) { o << int8_t(BinaryConsts::ContBind); - parent.writeIndexedHeapType(curr->contTypeBefore); - parent.writeIndexedHeapType(curr->contTypeAfter); + parent.writeIndexedHeapType(curr->sourceType); + parent.writeIndexedHeapType(curr->type.getHeapType()); } void BinaryInstWriter::visitResume(Resume* curr) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 2a981e7626a..179f5b3fadd 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -722,6 +722,10 @@ Type::Type(HeapType heapType, Nullability nullable) { new (this) Type(globalTypeStore.insert(TypeInfo(heapType, nullable))); } +bool Type::isContinuation() const { + return isRef() && getHeapType().isContinuation(); +} + bool Type::isStruct() const { return isRef() && getHeapType().isStruct(); } bool Type::isArray() const { return isRef() && getHeapType().isArray(); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 21118a02881..9df59113d39 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3485,8 +3485,8 @@ void FunctionValidator::visitContNew(ContNew* curr) { curr, "cont.new requires stack-switching [--enable-stack-switching]"); - shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()), + shouldBeTrue((curr->type.isContinuation() && + curr->type.getHeapType().getContinuation().type.isSignature()), curr, "invalid type in ContNew expression"); } @@ -3497,13 +3497,13 @@ void FunctionValidator::visitContBind(ContBind* curr) { curr, "cont.bind requires stack-switching [--enable-stack-switching]"); - shouldBeTrue((curr->contTypeBefore.isContinuation() && - curr->contTypeBefore.getContinuation().type.isSignature()), + shouldBeTrue((curr->sourceType.isContinuation() && + curr->sourceType.getContinuation().type.isSignature()), curr, "invalid first type in ContBind expression"); - shouldBeTrue((curr->contTypeAfter.isContinuation() && - curr->contTypeAfter.getContinuation().type.isSignature()), + shouldBeTrue((curr->type.isContinuation() && + curr->type.getHeapType().getContinuation().type.isSignature()), curr, "invalid second type in ContBind expression"); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index e7a5d8928f9..55e196297fb 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1371,16 +1371,12 @@ void StringSliceWTF::finalize() { void ContNew::finalize() { if (func->type == Type::unreachable) { type = Type::unreachable; - } else { - type = Type(contType, NonNullable); } } void ContBind::finalize() { - if (cont->type == Type::unreachable) { + if (handleUnreachableOperands(this)) { type = Type::unreachable; - } else if (!handleUnreachableOperands(this)) { - type = Type(contTypeAfter, NonNullable); } } From c4072621b555447f46a1d24506c1cfb2bae6fb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Tue, 5 Nov 2024 16:39:19 +0100 Subject: [PATCH 05/23] Format --- src/wasm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wasm.h b/src/wasm.h index 9b9c659c015..99c3383f8fe 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -741,7 +741,8 @@ class Expression { SuspendId, ResumeId, ResumeThrowId, - StackSwitchId, // Id for the stack switching `switch` + // Id for the stack switching `switch` + StackSwitchId, NumExpressionIds }; Id _id; From 15fcbc639977e14310910a1f1c1f63a3aa09d453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 6 Nov 2024 11:15:50 +0100 Subject: [PATCH 06/23] Remove PrintSExpression::visit{Resume,ResumeThrow,StackSwitch} --- src/passes/Print.cpp | 48 -------------------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index fde1d873c92..3d146213f27 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -301,9 +301,6 @@ struct PrintSExpression : public UnifiedExpressionVisitor { void visitLoop(Loop* curr); void visitTry(Try* curr); void visitTryTable(TryTable* curr); - void visitResume(Resume* curr); - void visitResumeThrow(ResumeThrow* curr); - void visitStackSwitch(StackSwitch* curr); bool maybePrintUnreachableReplacement(Expression* curr, Type type); bool maybePrintUnreachableOrNullReplacement(Expression* curr, Type type); @@ -2857,51 +2854,6 @@ void PrintSExpression::visitTryTable(TryTable* curr) { controlFlowDepth--; } -void PrintSExpression::visitResume(Resume* curr) { - o << '('; - printExpressionContents(curr); - - incIndent(); - - for (Index i = 0; i < curr->operands.size(); i++) { - printFullLine(curr->operands[i]); - } - - printFullLine(curr->cont); - - decIndent(); -} - -void PrintSExpression::visitResumeThrow(ResumeThrow* curr) { - o << '('; - printExpressionContents(curr); - - incIndent(); - - for (Index i = 0; i < curr->operands.size(); i++) { - printFullLine(curr->operands[i]); - } - - printFullLine(curr->cont); - - decIndent(); -} - -void PrintSExpression::visitStackSwitch(StackSwitch* curr) { - o << '('; - printExpressionContents(curr); - - incIndent(); - - for (Index i = 0; i < curr->operands.size(); i++) { - printFullLine(curr->operands[i]); - } - - printFullLine(curr->cont); - - decIndent(); -} - bool PrintSExpression::maybePrintUnreachableReplacement(Expression* curr, Type type) { // When we cannot print an instruction because the child from which it's From d1866bc587a36fc6c435d614c57f35ddc60828ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 6 Nov 2024 11:44:07 +0100 Subject: [PATCH 07/23] Don't (re)compute sentTypes for resume and resume_throw. --- src/wasm/wasm-validator.cpp | 10 ----- src/wasm/wasm.cpp | 90 ++----------------------------------- 2 files changed, 3 insertions(+), 97 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 9df59113d39..caa8f6dbe82 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3521,11 +3521,6 @@ void FunctionValidator::visitResume(Resume* curr) { curr, "resume requires stack-switching [--enable-stack-switching]"); - shouldBeTrue( - curr->sentTypes.size() == curr->handlerBlocks.size(), - curr, - "sentTypes cache in Resume instruction has not been initialized"); - shouldBeTrue((curr->contType.isContinuation() && curr->contType.getContinuation().type.isSignature()), curr, @@ -3541,11 +3536,6 @@ void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { "resume_throw requires exception handling [--enable-exception-handling] " "and stack-switching [--enable-stack-switching]"); - shouldBeTrue( - curr->sentTypes.size() == curr->handlerBlocks.size(), - curr, - "sentTypes cache in ResumeThrow instruction has not been initialized"); - shouldBeTrue((curr->contType.isContinuation() && curr->contType.getContinuation().type.isSignature()), curr, diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 55e196297fb..2a5050642c1 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1387,47 +1387,6 @@ void Suspend::finalize(Module* wasm) { } } -static void populateResumeSentTypes(Resume* curr, Module* wasm) { - if (!wasm) { - return; - } - - const Signature& contSig = - curr->contType.getContinuation().type.getSignature(); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume - // $ct ... (tag $tag $block) ... ) causes $block to receive values of the - // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont - // $ft') and ft' = [tgr*] -> [ctr*]. - // - auto& ctrs = contSig.results; - curr->sentTypes.clear(); - curr->sentTypes.resize(curr->handlerTags.size()); - for (Index i = 0; i < curr->handlerTags.size(); i++) { - auto& tag = curr->handlerTags[i]; - auto& tagSig = wasm->getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; - - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); - - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); - - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - curr->sentTypes[i] = Type(sentValueTypes); - } else { - curr->sentTypes[i] = ctPrimeRef; - } - } -} - void Resume::finalize(Module* wasm) { if (cont->type == Type::unreachable) { type = Type::unreachable; @@ -1436,49 +1395,6 @@ void Resume::finalize(Module* wasm) { this->contType.getContinuation().type.getSignature(); type = contSig.results; } - - populateResumeSentTypes(this, wasm); -} - -static void populateResumeThrowSentTypes(ResumeThrow* curr, Module* wasm) { - if (!wasm) { - return; - } - - const Signature& contSig = - curr->contType.getContinuation().type.getSignature(); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume - // $ct ... (tag $tag $block) ... ) causes $block to receive values of the - // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont - // $ft') and ft' = [tgr*] -> [ctr*]. - // - auto& ctrs = contSig.results; - curr->sentTypes.clear(); - curr->sentTypes.resize(curr->handlerTags.size()); - for (Index i = 0; i < curr->handlerTags.size(); i++) { - auto& tag = curr->handlerTags[i]; - auto& tagSig = wasm->getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; - - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); - - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); - - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - curr->sentTypes[i] = Type(sentValueTypes); - } else { - curr->sentTypes[i] = ctPrimeRef; - } - } } void ResumeThrow::finalize(Module* wasm) { @@ -1489,12 +1405,12 @@ void ResumeThrow::finalize(Module* wasm) { this->contType.getContinuation().type.getSignature(); type = contSig.results; } - - populateResumeThrowSentTypes(this, wasm); } void StackSwitch::finalize(Module* wasm) { - if (!handleUnreachableOperands(this) && wasm) { + if (cont->type == Type::unreachable) { + type = Type::unreachable; + } else if (!handleUnreachableOperands(this) && wasm) { type = this->contType.getContinuation().type.getSignature().params; } } From 2b06c766a7d67d6780be50b011aa7a3af793d04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 6 Nov 2024 12:44:16 +0100 Subject: [PATCH 08/23] [fuzz] Skip stack switching tests. --- scripts/fuzz_opt.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 3d466350120..63161548915 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -348,11 +348,12 @@ def is_git_repo(): 'multi-memory-lowering-import.wast', 'multi-memory-lowering-import-error.wast', # the fuzzer does not support typed continuations - 'typed_continuations.wast', - 'typed_continuations_resume.wast', - 'typed_continuations_contnew.wast', - 'typed_continuations_contbind.wast', - 'typed_continuations_suspend.wast', + 'stack_switching.wast', + 'stack_switching_contnew.wast', + 'stack_switching_contbind.wast', + 'stack_switching_suspend.wast', + 'stack_switching_resume.wast', + 'stack_switching_resume_throw.wast', ] From 3b339f9411c457f9b31e159b52b8dcbc15ee8865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 6 Nov 2024 13:38:38 +0100 Subject: [PATCH 09/23] Abstract shared logic for reading resume tables. --- src/wasm/wasm-binary.cpp | 72 ++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index a8442dc6e70..f9f9f9b567f 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -7944,15 +7944,12 @@ void WasmBinaryReader::visitSuspend(Suspend* curr) { curr->finalize(&wasm); } -void WasmBinaryReader::visitResume(Resume* curr) { - - curr->contType = getIndexedHeapType(); - if (!curr->contType.isContinuation()) { - throwError("non-continuation type in resume instruction " + - curr->contType.toString()); - } +template +static void readResumeTable(WasmBinaryReader* reader, ResumeType* curr) { + static_assert(std::is_base_of::value || + std::is_base_of::value); - auto numHandlers = getU32LEB(); + auto numHandlers = reader->getU32LEB(); // We *must* bring the handlerTags vector to an appropriate size to ensure // that we do not invalidate the pointers we add to tagRefs. They need to stay @@ -7962,17 +7959,17 @@ void WasmBinaryReader::visitResume(Resume* curr) { curr->onTags.resize(numHandlers); for (size_t i = 0; i < numHandlers; i++) { - uint8_t code = getInt8(); - auto tagIndex = getU32LEB(); - auto tag = getTagName(tagIndex); + uint8_t code = reader->getInt8(); + auto tagIndex = reader->getU32LEB(); + auto tag = reader->getTagName(tagIndex); Name handler; if (code == BinaryConsts::OnLabel) { // expect (on $tag $label) - auto handlerIndex = getU32LEB(); - handler = getBreakTarget(handlerIndex).name; + auto handlerIndex = reader->getU32LEB(); + handler = reader->getBreakTarget(handlerIndex).name; } else if (code == BinaryConsts::OnSwitch) { // expect (on $tag switch) handler = Name(); } else { // error - throwError("ON opcode expected"); + reader->throwError("ON opcode expected"); } curr->handlerTags[i] = tag; @@ -7980,9 +7977,20 @@ void WasmBinaryReader::visitResume(Resume* curr) { curr->onTags[i] = static_cast(code); // 0x00 is false, 0x01 is true // We don't know the final name yet - tagRefs[tagIndex].push_back(&curr->handlerTags[i]); + reader->tagRefs[tagIndex].push_back(&curr->handlerTags[i]); + } +} + +void WasmBinaryReader::visitResume(Resume* curr) { + + curr->contType = getIndexedHeapType(); + if (!curr->contType.isContinuation()) { + throwError("non-continuation type in resume instruction " + + curr->contType.toString()); } + readResumeTable(this, curr); + curr->cont = popNonVoidExpression(); auto numArgs = @@ -8005,39 +8013,7 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { curr->tag = getTagName(exnTagIndex); tagRefs[exnTagIndex].push_back(&curr->tag); - auto numHandlers = getU32LEB(); - - // We *must* bring the handlerTags vector to an appropriate size to ensure - // that we do not invalidate the pointers we add to tagRefs. They need to stay - // valid until processNames ran. - curr->handlerTags.resize(numHandlers); - curr->handlerBlocks.resize(numHandlers); - curr->onTags.resize(numHandlers); - - for (size_t i = 0; i < numHandlers; i++) { - uint8_t code = getInt8(); - auto tagIndex = getU32LEB(); - auto tag = getTagName(tagIndex); - Name handler; - if (code == BinaryConsts::OnLabel) { - // expect (on $tag $label) - auto handlerIndex = getU32LEB(); - handler = getBreakTarget(handlerIndex).name; - } else if (code == BinaryConsts::OnSwitch) { - // expect (on $tag switch) - handler = Name(); - } else { - // error - throwError("ON opcode expected"); - } - - curr->handlerTags[i] = tag; - curr->handlerBlocks[i] = handler; - curr->onTags[i] = static_cast(code); // 0x00 is false, 0x01 is true - - // We don't know the final name yet - tagRefs[tagIndex].push_back(&curr->handlerTags[i]); - } + readResumeTable(this, curr); curr->cont = popNonVoidExpression(); From 08864517e665ae9ec4ceeb7ef79b0a6b9dd9d4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 6 Nov 2024 18:26:39 +0100 Subject: [PATCH 10/23] Encode on-clauses using only two parallel vectors. --- src/ir/branch-utils.h | 4 ++-- src/parser/contexts.h | 27 ++++++++--------------- src/passes/Print.cpp | 33 ++++++++++++---------------- src/wasm-builder.h | 4 ---- src/wasm-delegations-fields.def | 2 -- src/wasm-ir-builder.h | 18 +++++++-------- src/wasm.h | 23 ++++++++++--------- src/wasm/wasm-binary.cpp | 8 +++---- src/wasm/wasm-ir-builder.cpp | 39 ++++++++++++++++----------------- src/wasm/wasm-stack.cpp | 8 ++++--- 10 files changed, 75 insertions(+), 91 deletions(-) diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index 5cfebc52d44..1771f2e0e88 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -85,14 +85,14 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) { } else if (auto* r = expr->dynCast()) { for (Index i = 0; i < r->handlerTags.size(); i++) { auto dest = r->handlerTags[i]; - if (dest == name && !r->onTags[i]) { + if (!dest.isNull() && dest == name) { func(name, r->sentTypes[i]); } } } else if (auto* r = expr->dynCast()) { for (Index i = 0; i < r->handlerTags.size(); i++) { auto dest = r->handlerTags[i]; - if (dest == name && !r->onTags[i]) { + if (!dest.isNull() && dest == name) { func(name, r->sentTypes[i]); } } diff --git a/src/parser/contexts.h b/src/parser/contexts.h index b89545b27e5..1b304470c2c 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -2647,22 +2647,18 @@ struct ParseDefsCtx : TypeParserCtx { HeapType type, const std::vector& resumetable) { std::vector tags; - std::vector labels; - std::vector onTags; + std::vector> labels; tags.reserve(resumetable.size()); labels.reserve(resumetable.size()); - onTags.reserve(resumetable.size()); for (const OnClauseInfo& info : resumetable) { tags.push_back(info.tag); if (info.isOnSwitch) { - labels.push_back(Index()); - onTags.push_back(true); + labels.push_back(std::nullopt); } else { - labels.push_back(info.label); - onTags.push_back(false); + labels.push_back(std::optional(info.label)); } } - return withLoc(pos, irBuilder.makeResume(type, tags, labels, onTags)); + return withLoc(pos, irBuilder.makeResume(type, tags, labels)); } Result<> makeResumeThrow(Index pos, @@ -2671,23 +2667,18 @@ struct ParseDefsCtx : TypeParserCtx { Name tag, const std::vector& resumetable) { std::vector tags; - std::vector labels; - std::vector onTags; + std::vector> labels; tags.reserve(resumetable.size()); labels.reserve(resumetable.size()); - onTags.reserve(resumetable.size()); - for (auto& info : resumetable) { + for (const OnClauseInfo& info : resumetable) { tags.push_back(info.tag); if (info.isOnSwitch) { - labels.push_back(Index()); - onTags.push_back(true); + labels.push_back(std::nullopt); } else { - labels.push_back(info.label); - onTags.push_back(false); + labels.push_back(std::optional(info.label)); } } - return withLoc(pos, - irBuilder.makeResumeThrow(type, tag, tags, labels, onTags)); + return withLoc(pos, irBuilder.makeResumeThrow(type, tag, tags, labels)); } Result<> makeStackSwitch(Index pos, diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 3d146213f27..2b1ae58867c 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2441,18 +2441,16 @@ struct PrintExpressionContents printMedium(o, "suspend "); curr->tag.print(o); } - void visitResume(Resume* curr) { - printMedium(o, "resume"); - - o << ' '; - printHeapType(curr->contType); - + template + static void handleResumeTable(std::ostream& o, ResumeType* curr) { + static_assert(std::is_base_of::value || + std::is_base_of::value); for (Index i = 0; i < curr->handlerTags.size(); i++) { o << " ("; printMedium(o, "on "); curr->handlerTags[i].print(o); o << ' '; - if (curr->onTags[i]) { + if (curr->handlerBlocks[i].isNull()) { o << "switch"; } else { curr->handlerBlocks[i].print(o); @@ -2460,6 +2458,14 @@ struct PrintExpressionContents o << ')'; } } + void visitResume(Resume* curr) { + printMedium(o, "resume"); + + o << ' '; + printHeapType(curr->contType); + + handleResumeTable(o, curr); + } void visitResumeThrow(ResumeThrow* curr) { printMedium(o, "resume_throw"); @@ -2468,18 +2474,7 @@ struct PrintExpressionContents o << ' '; curr->tag.print(o); - for (Index i = 0; i < curr->handlerTags.size(); i++) { - o << " ("; - printMedium(o, "on "); - curr->handlerTags[i].print(o); - o << ' '; - if (curr->onTags[i]) { - o << "switch"; - } else { - curr->handlerBlocks[i].print(o); - } - o << ')'; - } + handleResumeTable(o, curr); } void visitStackSwitch(StackSwitch* curr) { printMedium(o, "switch"); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index c29c5c6317f..bd0c6a40dba 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1197,14 +1197,12 @@ class Builder { Resume* makeResume(HeapType contType, const std::vector& handlerTags, const std::vector& handlerBlocks, - const std::vector& onTags, const std::vector& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); ret->contType = contType; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); - ret->onTags.set(onTags); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); @@ -1214,7 +1212,6 @@ class Builder { Name tag, const std::vector& handlerTags, const std::vector& handlerBlocks, - const std::vector& onTags, const std::vector& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); @@ -1222,7 +1219,6 @@ class Builder { ret->tag = tag; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); - ret->onTags.set(onTags); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 826633f9b87..258c3b58f43 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -782,7 +782,6 @@ DELEGATE_FIELD_CASE_END(Suspend) DELEGATE_FIELD_CASE_START(Resume) DELEGATE_FIELD_TYPE_VECTOR(Resume, sentTypes) -DELEGATE_FIELD_INT_VECTOR(Resume, onTags) DELEGATE_FIELD_CHILD(Resume, cont) DELEGATE_FIELD_CHILD_VECTOR(Resume, operands) DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(Resume, handlerBlocks) @@ -792,7 +791,6 @@ DELEGATE_FIELD_CASE_END(Resume) DELEGATE_FIELD_CASE_START(ResumeThrow) DELEGATE_FIELD_TYPE_VECTOR(ResumeThrow, sentTypes) -DELEGATE_FIELD_INT_VECTOR(ResumeThrow, onTags) DELEGATE_FIELD_CHILD(ResumeThrow, cont) DELEGATE_FIELD_CHILD_VECTOR(ResumeThrow, operands) DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(ResumeThrow, handlerBlocks) diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 715f6f0d349..861a897f777 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -220,15 +220,15 @@ class IRBuilder : public UnifiedExpressionVisitor> { [[nodiscard]] Result<> makeContNew(HeapType ct); [[nodiscard]] Result<> makeContBind(HeapType sourceType, HeapType targetType); [[nodiscard]] Result<> makeSuspend(Name tag); - [[nodiscard]] Result<> makeResume(HeapType ct, - const std::vector& tags, - const std::vector& labels, - const std::vector& onTags); - [[nodiscard]] Result<> makeResumeThrow(HeapType ct, - Name tag, - const std::vector& tags, - const std::vector& labels, - const std::vector& onTags); + [[nodiscard]] Result<> + makeResume(HeapType ct, + const std::vector& tags, + const std::vector>& labels); + [[nodiscard]] Result<> + makeResumeThrow(HeapType ct, + Name tag, + const std::vector& tags, + const std::vector>& labels); [[nodiscard]] Result<> makeStackSwitch(HeapType ct, Name tag); // Private functions that must be public for technical reasons. diff --git a/src/wasm.h b/src/wasm.h index 99c3383f8fe..217fcd46351 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1965,15 +1965,20 @@ class Suspend : public SpecificExpression { class Resume : public SpecificExpression { public: Resume(MixedArena& allocator) - : handlerTags(allocator), handlerBlocks(allocator), onTags(allocator), - operands(allocator), sentTypes(allocator) {} + : handlerTags(allocator), handlerBlocks(allocator), operands(allocator), + sentTypes(allocator) {} HeapType contType; + // The following two vectors are to be understood together + // pointwise. That is, the ith component of each vector together + // classifies an on-clause `(on $tag $label)` or `(on $tag + // switch)`. The first vector stores reifies the `$tag` bit of the + // aforementioned syntax... ArenaVector handlerTags; - // Empty name (i.e. `Name()`) for switch tags. + // ... whilst this vector reifies the `$label` bit of the + // syntax. For `switch` clauses the ith component will be the Empty + // name (i.e. `Name()`). ArenaVector handlerBlocks; - // False if (on $tag $label) and true when (on $tag switch). - ArenaVector onTags; ExpressionList operands; Expression* cont; @@ -1994,16 +1999,14 @@ class Resume : public SpecificExpression { class ResumeThrow : public SpecificExpression { public: ResumeThrow(MixedArena& allocator) - : handlerTags(allocator), handlerBlocks(allocator), onTags(allocator), - operands(allocator), sentTypes(allocator) {} + : handlerTags(allocator), handlerBlocks(allocator), operands(allocator), + sentTypes(allocator) {} HeapType contType; Name tag; + // See the comment on `Resume` above. ArenaVector handlerTags; - // Empty name (i.e. `Name()`) for switch tags. ArenaVector handlerBlocks; - // False if (on $tag $label) and true when (on $tag switch). - ArenaVector onTags; ExpressionList operands; Expression* cont; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index f9f9f9b567f..4b16b499783 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -7956,17 +7956,18 @@ static void readResumeTable(WasmBinaryReader* reader, ResumeType* curr) { // valid until processNames ran. curr->handlerTags.resize(numHandlers); curr->handlerBlocks.resize(numHandlers); - curr->onTags.resize(numHandlers); for (size_t i = 0; i < numHandlers; i++) { uint8_t code = reader->getInt8(); auto tagIndex = reader->getU32LEB(); auto tag = reader->getTagName(tagIndex); Name handler; - if (code == BinaryConsts::OnLabel) { // expect (on $tag $label) + if (code == BinaryConsts::OnLabel) { + // expect (on $tag $label) auto handlerIndex = reader->getU32LEB(); handler = reader->getBreakTarget(handlerIndex).name; - } else if (code == BinaryConsts::OnSwitch) { // expect (on $tag switch) + } else if (code == BinaryConsts::OnSwitch) { + // expect (on $tag switch) handler = Name(); } else { // error reader->throwError("ON opcode expected"); @@ -7974,7 +7975,6 @@ static void readResumeTable(WasmBinaryReader* reader, ResumeType* curr) { curr->handlerTags[i] = tag; curr->handlerBlocks[i] = handler; - curr->onTags[i] = static_cast(code); // 0x00 is false, 0x01 is true // We don't know the final name yet reader->tagRefs[tagIndex].push_back(&curr->handlerTags[i]); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 23d523b580b..46756cca7b9 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1945,10 +1945,10 @@ Result<> IRBuilder::makeSuspend(Name tag) { return Ok{}; } -Result<> IRBuilder::makeResume(HeapType ct, - const std::vector& tags, - const std::vector& labels, - const std::vector& onTags) { +Result<> +IRBuilder::makeResume(HeapType ct, + const std::vector& tags, + const std::vector>& labels) { if (!ct.isContinuation()) { return Err{"expected continuation type"}; } @@ -1960,25 +1960,25 @@ Result<> IRBuilder::makeResume(HeapType ct, std::vector labelNames; labelNames.reserve(labels.size()); for (size_t i = 0; i < labels.size(); i++) { - if (onTags[i]) { - labelNames.push_back(Name()); - } else { - auto name = getLabelName(labels[i]); + if (labels[i].has_value()) { + auto name = getLabelName(labels[i].value()); CHECK_ERR(name); labelNames.push_back(*name); + } else { + labelNames.push_back(Name()); } } std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResume(ct, tags, labelNames, onTags, operands, curr.cont)); + push(builder.makeResume(ct, tags, labelNames, operands, curr.cont)); return Ok{}; } -Result<> IRBuilder::makeResumeThrow(HeapType ct, - Name tag, - const std::vector& tags, - const std::vector& labels, - const std::vector& onTags) { +Result<> +IRBuilder::makeResumeThrow(HeapType ct, + Name tag, + const std::vector& tags, + const std::vector>& labels) { if (!ct.isContinuation()) { return Err{"expected continuation type"}; } @@ -1991,18 +1991,17 @@ Result<> IRBuilder::makeResumeThrow(HeapType ct, std::vector labelNames; labelNames.reserve(labels.size()); for (size_t i = 0; i < labels.size(); i++) { - if (onTags[i]) { - labelNames.push_back(Name()); - } else { - auto name = getLabelName(labels[i]); + if (labels[i].has_value()) { + auto name = getLabelName(labels[i].value()); CHECK_ERR(name); labelNames.push_back(*name); + } else { + labelNames.push_back(Name()); } } std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResumeThrow( - ct, tag, tags, labelNames, onTags, operands, curr.cont)); + push(builder.makeResumeThrow(ct, tag, tags, labelNames, operands, curr.cont)); return Ok{}; } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 09c7788eb72..3ceb721d0a3 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2630,7 +2630,7 @@ void BinaryInstWriter::visitResume(Resume* curr) { size_t handlerNum = curr->handlerTags.size(); o << U32LEB(handlerNum); for (size_t i = 0; i < handlerNum; i++) { - if (curr->onTags[i]) { + if (curr->handlerBlocks[i].isNull()) { // on switch o << int8_t(BinaryConsts::OnSwitch) << U32LEB(parent.getTagIndex(curr->handlerTags[i])); @@ -2651,10 +2651,12 @@ void BinaryInstWriter::visitResumeThrow(ResumeThrow* curr) { size_t handlerNum = curr->handlerTags.size(); o << U32LEB(handlerNum); for (size_t i = 0; i < handlerNum; i++) { - if (curr->onTags[i]) { // on switch + if (curr->handlerBlocks[i].isNull()) { + // on switch o << int8_t(BinaryConsts::OnSwitch) << U32LEB(parent.getTagIndex(curr->handlerTags[i])); - } else { // on label + } else { + // on label o << int8_t(BinaryConsts::OnLabel) << U32LEB(parent.getTagIndex(curr->handlerTags[i])) << U32LEB(getBreakIndex(curr->handlerBlocks[i])); From d76f260153b4dee2ea52c79bb7bd6bcb7f3bcdfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Thu, 7 Nov 2024 10:42:17 +0100 Subject: [PATCH 11/23] Return resumetable rather than taking it as a reference. --- src/parser/parsers.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/parser/parsers.h b/src/parser/parsers.h index e90a55d5eef..ccd343423cd 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -2454,7 +2454,8 @@ makeSuspend(Ctx& ctx, Index pos, const std::vector& annotations) { // resumetable ::= ('(' 'on' tagidx labelidx | 'on' tagidx switch ')')* template -Result<> makeResumeTable(Ctx& ctx, typename Ctx::OnClauseListT& resumetable) { +Result makeResumeTable(Ctx& ctx) { + auto resumetable = ctx.makeOnClauseList(); while (ctx.in.takeSExprStart("on"sv)) { auto tag = tagidx(ctx); CHECK_ERR(tag); @@ -2469,7 +2470,7 @@ Result<> makeResumeTable(Ctx& ctx, typename Ctx::OnClauseListT& resumetable) { return ctx.in.err("expected ')' at end of handler clause"); } } - return Ok{}; + return resumetable; } // resume ::= 'resume' typeidx resumetable @@ -2479,11 +2480,10 @@ makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { auto type = typeidx(ctx); CHECK_ERR(type); - auto resumetable = ctx.makeOnClauseList(); - auto ans = makeResumeTable(ctx, resumetable); - CHECK_ERR(ans); + auto resumetable = makeResumeTable(ctx); + CHECK_ERR(resumetable); - return ctx.makeResume(pos, annotations, *type, resumetable); + return ctx.makeResume(pos, annotations, *type, *resumetable); } // resume_throw ::= 'resume_throw' typeidx tagidx ('(' 'on' tagidx labelidx | @@ -2497,11 +2497,10 @@ Result<> makeResumeThrow(Ctx& ctx, auto exnTag = tagidx(ctx); CHECK_ERR(exnTag); - auto resumetable = ctx.makeOnClauseList(); - auto ans = makeResumeTable(ctx, resumetable); - CHECK_ERR(ans); + auto resumetable = makeResumeTable(ctx); + CHECK_ERR(resumetable); - return ctx.makeResumeThrow(pos, annotations, *type, *exnTag, resumetable); + return ctx.makeResumeThrow(pos, annotations, *type, *exnTag, *resumetable); } // switch ::= 'switch' typeidx tagidx From 1c2ed8a69f32edca77c4c21760b487c4e4e5c8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Thu, 7 Nov 2024 13:36:39 +0100 Subject: [PATCH 12/23] Revert "Don't (re)compute sentTypes for resume and resume_throw." This reverts commit d1866bc587a36fc6c435d614c57f35ddc60828ce. --- src/wasm/wasm-validator.cpp | 10 +++++ src/wasm/wasm.cpp | 90 +++++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index caa8f6dbe82..9df59113d39 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3521,6 +3521,11 @@ void FunctionValidator::visitResume(Resume* curr) { curr, "resume requires stack-switching [--enable-stack-switching]"); + shouldBeTrue( + curr->sentTypes.size() == curr->handlerBlocks.size(), + curr, + "sentTypes cache in Resume instruction has not been initialized"); + shouldBeTrue((curr->contType.isContinuation() && curr->contType.getContinuation().type.isSignature()), curr, @@ -3536,6 +3541,11 @@ void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { "resume_throw requires exception handling [--enable-exception-handling] " "and stack-switching [--enable-stack-switching]"); + shouldBeTrue( + curr->sentTypes.size() == curr->handlerBlocks.size(), + curr, + "sentTypes cache in ResumeThrow instruction has not been initialized"); + shouldBeTrue((curr->contType.isContinuation() && curr->contType.getContinuation().type.isSignature()), curr, diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 2a5050642c1..55e196297fb 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1387,6 +1387,47 @@ void Suspend::finalize(Module* wasm) { } } +static void populateResumeSentTypes(Resume* curr, Module* wasm) { + if (!wasm) { + return; + } + + const Signature& contSig = + curr->contType.getContinuation().type.getSignature(); + + // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation + // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume + // $ct ... (tag $tag $block) ... ) causes $block to receive values of the + // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont + // $ft') and ft' = [tgr*] -> [ctr*]. + // + auto& ctrs = contSig.results; + curr->sentTypes.clear(); + curr->sentTypes.resize(curr->handlerTags.size()); + for (Index i = 0; i < curr->handlerTags.size(); i++) { + auto& tag = curr->handlerTags[i]; + auto& tagSig = wasm->getTag(tag)->sig; + + auto& tgps = tagSig.params; + auto& tgrs = tagSig.results; + + HeapType ftPrime{Signature(tgrs, ctrs)}; + HeapType ctPrime{Continuation(ftPrime)}; + Type ctPrimeRef(ctPrime, Nullability::NonNullable); + + if (tgps.size() > 0) { + TypeList sentValueTypes; + sentValueTypes.reserve(tgps.size() + 1); + + sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); + sentValueTypes.push_back(ctPrimeRef); + curr->sentTypes[i] = Type(sentValueTypes); + } else { + curr->sentTypes[i] = ctPrimeRef; + } + } +} + void Resume::finalize(Module* wasm) { if (cont->type == Type::unreachable) { type = Type::unreachable; @@ -1395,6 +1436,49 @@ void Resume::finalize(Module* wasm) { this->contType.getContinuation().type.getSignature(); type = contSig.results; } + + populateResumeSentTypes(this, wasm); +} + +static void populateResumeThrowSentTypes(ResumeThrow* curr, Module* wasm) { + if (!wasm) { + return; + } + + const Signature& contSig = + curr->contType.getContinuation().type.getSignature(); + + // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation + // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume + // $ct ... (tag $tag $block) ... ) causes $block to receive values of the + // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont + // $ft') and ft' = [tgr*] -> [ctr*]. + // + auto& ctrs = contSig.results; + curr->sentTypes.clear(); + curr->sentTypes.resize(curr->handlerTags.size()); + for (Index i = 0; i < curr->handlerTags.size(); i++) { + auto& tag = curr->handlerTags[i]; + auto& tagSig = wasm->getTag(tag)->sig; + + auto& tgps = tagSig.params; + auto& tgrs = tagSig.results; + + HeapType ftPrime{Signature(tgrs, ctrs)}; + HeapType ctPrime{Continuation(ftPrime)}; + Type ctPrimeRef(ctPrime, Nullability::NonNullable); + + if (tgps.size() > 0) { + TypeList sentValueTypes; + sentValueTypes.reserve(tgps.size() + 1); + + sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); + sentValueTypes.push_back(ctPrimeRef); + curr->sentTypes[i] = Type(sentValueTypes); + } else { + curr->sentTypes[i] = ctPrimeRef; + } + } } void ResumeThrow::finalize(Module* wasm) { @@ -1405,12 +1489,12 @@ void ResumeThrow::finalize(Module* wasm) { this->contType.getContinuation().type.getSignature(); type = contSig.results; } + + populateResumeThrowSentTypes(this, wasm); } void StackSwitch::finalize(Module* wasm) { - if (cont->type == Type::unreachable) { - type = Type::unreachable; - } else if (!handleUnreachableOperands(this) && wasm) { + if (!handleUnreachableOperands(this) && wasm) { type = this->contType.getContinuation().type.getSignature().params; } } From c8bf255ba8888d231c2272f6963d455631de4124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Thu, 7 Nov 2024 13:42:14 +0100 Subject: [PATCH 13/23] Propagate Type::unreachable in switch. --- src/wasm/wasm.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 55e196297fb..841737d47ea 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1494,7 +1494,9 @@ void ResumeThrow::finalize(Module* wasm) { } void StackSwitch::finalize(Module* wasm) { - if (!handleUnreachableOperands(this) && wasm) { + if (cont->type == Type::unreachable) { + type = Type::unreachable; + } else if (!handleUnreachableOperands(this) && wasm) { type = this->contType.getContinuation().type.getSignature().params; } } From 9e16e477341e95206f61d6d9cfc31b5d922206ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Fri, 8 Nov 2024 16:22:23 +0100 Subject: [PATCH 14/23] Compute sentTypes for resumetables upon construction. --- src/wasm-builder.h | 47 ++++++++++++++++++++++++++++++++++++ src/wasm/wasm-ir-builder.cpp | 6 ++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/wasm-builder.h b/src/wasm-builder.h index bd0c6a40dba..91a974af984 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1194,6 +1194,51 @@ class Builder { ret->finalize(&wasm); return ret; } + template + std::vector computeResumeTableSentTypes(ResumeOrResumeThrow* curr) { + static_assert(std::is_base_of::value || + std::is_base_of::value); + assert(curr->handlerBlocks.size() == curr->handlerTags.size()); + + // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation + // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction + // (resume $ct ... (tag $tag $block) ... ) causes $block to receive values + // of the following types when suspending to $tag: tgp* (ref $ct') where ct' + // = (cont $ft') and ft' = [tgr*] -> [ctr*]. + // + std::vector sentTypes; + sentTypes.reserve(curr->handlerTags.size()); + auto contSig = curr->contType.getContinuation().type.getSignature(); + auto& ctrs = contSig.params; + for (Index i = 0; i < curr->handlerTags.size(); i++) { + if (curr->handlerBlocks[i].isNull()) { + sentTypes[i] = Type::none; + continue; + } + + auto& tag = curr->handlerTags[i]; + auto& tagSig = wasm.getTag(tag)->sig; + + auto& tgps = tagSig.params; + auto& tgrs = tagSig.results; + + HeapType ftPrime{Signature(tgrs, ctrs)}; + HeapType ctPrime{Continuation(ftPrime)}; + Type ctPrimeRef(ctPrime, Nullability::NonNullable); + + if (tgps.size() > 0) { + TypeList sentValueTypes; + sentValueTypes.reserve(tgps.size() + 1); + + sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); + sentValueTypes.push_back(ctPrimeRef); + sentTypes[i] = Type(sentValueTypes); + } else { + sentTypes[i] = ctPrimeRef; + } + } + return sentTypes; + } Resume* makeResume(HeapType contType, const std::vector& handlerTags, const std::vector& handlerBlocks, @@ -1203,6 +1248,7 @@ class Builder { ret->contType = contType; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); + ret->sentTypes.set(computeResumeTableSentTypes(ret)); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); @@ -1219,6 +1265,7 @@ class Builder { ret->tag = tag; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); + ret->sentTypes.set(computeResumeTableSentTypes(ret)); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 46756cca7b9..761b4e6381f 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1953,8 +1953,9 @@ IRBuilder::makeResume(HeapType ct, return Err{"expected continuation type"}; } Resume curr(wasm.allocator); + auto contSig = ct.getContinuation().type.getSignature(); curr.contType = ct; - curr.operands.resize(ct.getContinuation().type.getSignature().params.size()); + curr.operands.resize(contSig.params.size()); CHECK_ERR(visitResume(&curr)); std::vector labelNames; @@ -1979,6 +1980,9 @@ IRBuilder::makeResumeThrow(HeapType ct, Name tag, const std::vector& tags, const std::vector>& labels) { + if (tags.size() != labels.size()) { + return Err{"the sizes of tags and labels must be equal"}; + } if (!ct.isContinuation()) { return Err{"expected continuation type"}; } From 157552ad4bcd77d7718fdfa944992b3505c416c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Mon, 11 Nov 2024 23:28:49 +0100 Subject: [PATCH 15/23] WIP --- src/passes/Print.cpp | 33 ++++++++++++++++++++ src/wasm/wasm-validator.cpp | 23 ++++++++------ test/lit/basic/stack_switching_contbind.wast | 24 ++++++++++++++ test/lit/basic/stack_switching_contnew.wast | 30 ++++++++++++++---- 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 2b1ae58867c..18313bd64d1 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -380,6 +380,39 @@ struct PrintSExpression : public UnifiedExpressionVisitor { visitExpression(curr); } } + void visitContNew(ContNew* curr) { + if (!maybePrintUnreachableReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitContBind(ContBind* curr) { + if (!maybePrintUnreachableOrNullReplacement( + curr, Type(curr->sourceType, Nullable)) || + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitResume(Resume* curr) { + if (!maybePrintUnreachableOrNullReplacement( + curr, Type(curr->contType, Nullable)) || + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitResumeThrow(ResumeThrow* curr) { + if (!maybePrintUnreachableOrNullReplacement( + curr, Type(curr->contType, Nullable)) || + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitStackSwitch(StackSwitch* curr) { + if (!maybePrintUnreachableOrNullReplacement( + curr, Type(curr->contType, Nullable)) || + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } // Module-level visitors void handleSignature(HeapType curr, Name name = Name()); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 9df59113d39..8122198f50d 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3485,10 +3485,12 @@ void FunctionValidator::visitContNew(ContNew* curr) { curr, "cont.new requires stack-switching [--enable-stack-switching]"); - shouldBeTrue((curr->type.isContinuation() && - curr->type.getHeapType().getContinuation().type.isSignature()), - curr, - "invalid type in ContNew expression"); + shouldBeTrue( + (curr->type.isContinuation() && + curr->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, + curr, + "invalid type in ContNew expression"); } void FunctionValidator::visitContBind(ContBind* curr) { @@ -3498,14 +3500,17 @@ void FunctionValidator::visitContBind(ContBind* curr) { "cont.bind requires stack-switching [--enable-stack-switching]"); shouldBeTrue((curr->sourceType.isContinuation() && - curr->sourceType.getContinuation().type.isSignature()), + curr->sourceType.getContinuation().type.isSignature()) || + Type(curr->sourceType, Nullable) == Type::unreachable, curr, "invalid first type in ContBind expression"); - shouldBeTrue((curr->type.isContinuation() && - curr->type.getHeapType().getContinuation().type.isSignature()), - curr, - "invalid second type in ContBind expression"); + shouldBeTrue( + (curr->type.isContinuation() && + curr->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, + curr, + "invalid second type in ContBind expression"); } void FunctionValidator::visitSuspend(Suspend* curr) { diff --git a/test/lit/basic/stack_switching_contbind.wast b/test/lit/basic/stack_switching_contbind.wast index 065d4a7962f..d559ad86359 100644 --- a/test/lit/basic/stack_switching_contbind.wast +++ b/test/lit/basic/stack_switching_contbind.wast @@ -29,6 +29,8 @@ ;; CHECK-TEXT: (type $5 (func (param (ref $ct1)) (result (ref $ct1)))) + ;; CHECK-TEXT: (type $6 (func (result (ref $ct1)))) + ;; CHECK-TEXT: (func $f (type $4) (param $x (ref $ct1)) (result (ref $ct2)) ;; CHECK-TEXT-NEXT: (cont.bind $ct1 $ct2 ;; CHECK-TEXT-NEXT: (i32.const 123) @@ -40,6 +42,8 @@ ;; CHECK-BIN: (type $5 (func (param (ref $ct1)) (result (ref $ct1)))) + ;; CHECK-BIN: (type $6 (func (result (ref $ct1)))) + ;; CHECK-BIN: (func $f (type $4) (param $x (ref $ct1)) (result (ref $ct2)) ;; CHECK-BIN-NEXT: (cont.bind $ct1 $ct2 ;; CHECK-BIN-NEXT: (i32.const 123) @@ -70,6 +74,20 @@ (local.get $x) ) ) + + ;; CHECK-TEXT: (func $k (type $6) (result (ref $ct1)) + ;; CHECK-TEXT-NEXT: (cont.bind $ct1 $ct1 + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $k (type $6) (result (ref $ct1)) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + (func $k (result (ref $ct1)) + (cont.bind $ct1 $ct1 + (unreachable) + ) + ) ) ;; CHECK-BIN-NODEBUG: (type $0 (func (param i32 i64 i32) (result i32))) @@ -83,6 +101,8 @@ ;; CHECK-BIN-NODEBUG: (type $5 (func (param (ref $1)) (result (ref $1)))) +;; CHECK-BIN-NODEBUG: (type $6 (func (result (ref $1)))) + ;; CHECK-BIN-NODEBUG: (func $0 (type $4) (param $0 (ref $1)) (result (ref $3)) ;; CHECK-BIN-NODEBUG-NEXT: (cont.bind $1 $3 ;; CHECK-BIN-NODEBUG-NEXT: (i32.const 123) @@ -96,3 +116,7 @@ ;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $2 (type $6) (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) diff --git a/test/lit/basic/stack_switching_contnew.wast b/test/lit/basic/stack_switching_contnew.wast index c0539cb9a73..19f27b28147 100644 --- a/test/lit/basic/stack_switching_contnew.wast +++ b/test/lit/basic/stack_switching_contnew.wast @@ -21,13 +21,27 @@ ;; CHECK-TEXT: (elem declare func $g) - ;; CHECK-TEXT: (func $g (type $ft) (param $0 i32) (result i32) - ;; CHECK-TEXT-NEXT: (i32.const 123) + ;; CHECK-TEXT: (func $f (type $2) (result (ref $ct)) + ;; CHECK-TEXT-NEXT: (block ;; (replaces unreachable ContNew we can't emit) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) ;; CHECK-TEXT-NEXT: ) ;; CHECK-BIN: (type $2 (func (result (ref $ct)))) ;; CHECK-BIN: (elem declare func $g) + ;; CHECK-BIN: (func $f (type $2) (result (ref $ct)) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + (func $f (result (ref $ct)) + (cont.new $ct (unreachable))) + + ;; CHECK-TEXT: (func $g (type $ft) (param $0 i32) (result i32) + ;; CHECK-TEXT-NEXT: (i32.const 123) + ;; CHECK-TEXT-NEXT: ) ;; CHECK-BIN: (func $g (type $ft) (param $0 i32) (result i32) ;; CHECK-BIN-NEXT: (i32.const 123) ;; CHECK-BIN-NEXT: ) @@ -40,7 +54,7 @@ ;; CHECK-BIN-NODEBUG: (type $2 (func (result (ref $1)))) - ;; CHECK-BIN-NODEBUG: (elem declare func $0) + ;; CHECK-BIN-NODEBUG: (elem declare func $1) (elem declare func $g) ;; CHECK-TEXT: (func $h (type $2) (result (ref $ct)) @@ -58,12 +72,16 @@ ) ) -;; CHECK-BIN-NODEBUG: (func $0 (type $0) (param $0 i32) (result i32) +;; CHECK-BIN-NODEBUG: (func $0 (type $2) (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $1 (type $0) (param $0 i32) (result i32) ;; CHECK-BIN-NODEBUG-NEXT: (i32.const 123) ;; CHECK-BIN-NODEBUG-NEXT: ) -;; CHECK-BIN-NODEBUG: (func $1 (type $2) (result (ref $1)) +;; CHECK-BIN-NODEBUG: (func $2 (type $2) (result (ref $1)) ;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 -;; CHECK-BIN-NODEBUG-NEXT: (ref.func $0) +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $1) ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: ) From 480843d3b572e04b9b528b232af7dfc10971510e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Tue, 12 Nov 2024 15:26:09 +0100 Subject: [PATCH 16/23] Check for unreachable continuation types. --- src/wasm/wasm-validator.cpp | 44 +- test/lit/basic/stack_switching.wast | 933 +++++++++++++++++++- test/lit/basic/stack_switching_contnew.wast | 27 + 3 files changed, 975 insertions(+), 29 deletions(-) diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 8122198f50d..6b57f78dfa1 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3490,7 +3490,7 @@ void FunctionValidator::visitContNew(ContNew* curr) { curr->type.getHeapType().getContinuation().type.isSignature()) || curr->type == Type::unreachable, curr, - "invalid type in ContNew expression"); + "cont.new must be annotated with a continuation type"); } void FunctionValidator::visitContBind(ContBind* curr) { @@ -3499,18 +3499,19 @@ void FunctionValidator::visitContBind(ContBind* curr) { curr, "cont.bind requires stack-switching [--enable-stack-switching]"); - shouldBeTrue((curr->sourceType.isContinuation() && - curr->sourceType.getContinuation().type.isSignature()) || - Type(curr->sourceType, Nullable) == Type::unreachable, - curr, - "invalid first type in ContBind expression"); + shouldBeTrue( + (curr->sourceType.isContinuation() && + curr->sourceType.getContinuation().type.isSignature()) || + Type(curr->sourceType, Nullable) == Type::unreachable, + curr, + "the first type annotation on cont.bind must be a continuation type"); shouldBeTrue( (curr->type.isContinuation() && curr->type.getHeapType().getContinuation().type.isSignature()) || curr->type == Type::unreachable, curr, - "invalid second type in ContBind expression"); + "the second type annotation on cont.bind must be a continuation type"); } void FunctionValidator::visitSuspend(Suspend* curr) { @@ -3529,12 +3530,13 @@ void FunctionValidator::visitResume(Resume* curr) { shouldBeTrue( curr->sentTypes.size() == curr->handlerBlocks.size(), curr, - "sentTypes cache in Resume instruction has not been initialized"); + "sentTypes cache in resume instruction has not been initialized"); shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()), + curr->contType.getContinuation().type.isSignature()) || + curr->type == Type::unreachable, curr, - "invalid type in Resume expression"); + "resume must be annotated with a continuation type"); } void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { @@ -3549,12 +3551,18 @@ void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { shouldBeTrue( curr->sentTypes.size() == curr->handlerBlocks.size(), curr, - "sentTypes cache in ResumeThrow instruction has not been initialized"); + "sentTypes cache in resume_throw instruction has not been initialized"); shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()), + curr->contType.getContinuation().type.isSignature()) || + curr->type == Type::unreachable, curr, - "invalid type in ResumeThrow expression"); + "resume_throw must be annotated with a continuation type"); + + auto* tag = getModule()->getTagOrNull(curr->tag); + if (!shouldBeTrue(!!tag, curr, "resume_throw must be annotated with a tag")) { + return; + } } void FunctionValidator::visitStackSwitch(StackSwitch* curr) { @@ -3564,9 +3572,15 @@ void FunctionValidator::visitStackSwitch(StackSwitch* curr) { "switch requires stack-switching [--enable-stack-switching]"); shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()), + curr->contType.getContinuation().type.isSignature()) || + curr->type == Type::unreachable, curr, - "invalid type in Switch expression"); + "switch must be annotated with a continuation type"); + + auto* tag = getModule()->getTagOrNull(curr->tag); + if (!shouldBeTrue(!!tag, curr, "switch must be annotated with a tag")) { + return; + } } void FunctionValidator::visitFunction(Function* curr) { diff --git a/test/lit/basic/stack_switching.wast b/test/lit/basic/stack_switching.wast index 1e024ca0b3e..c62222c8988 100644 --- a/test/lit/basic/stack_switching.wast +++ b/test/lit/basic/stack_switching.wast @@ -10,35 +10,131 @@ ;; RUN: cat %t.bin.nodebug.wast | filecheck %s --check-prefix=CHECK-BIN-NODEBUG (module + ;; CHECK-TEXT: (type $f1 (func)) + + ;; CHECK-TEXT: (type $k1 (cont $f1)) + + ;; CHECK-TEXT: (type $2 (func (param (ref $k1)))) + + ;; CHECK-TEXT: (type $ft1 (func (param i32))) + + ;; CHECK-TEXT: (type $ct1 (sub (cont $ft1))) + ;; CHECK-TEXT: (type $ft (func (param i32) (result i32))) + ;; CHECK-BIN: (type $f1 (func)) + + ;; CHECK-BIN: (type $k1 (cont $f1)) + + ;; CHECK-BIN: (type $2 (func (param (ref $k1)))) + + ;; CHECK-BIN: (type $ft1 (func (param i32))) + + ;; CHECK-BIN: (type $ct1 (sub (cont $ft1))) + ;; CHECK-BIN: (type $ft (func (param i32) (result i32))) (type $ft (func (param i32) (result i32))) ;; CHECK-TEXT: (type $ct (cont $ft)) ;; CHECK-BIN: (type $ct (cont $ft)) (type $ct (cont $ft)) - ;; CHECK-TEXT: (type $2 (func (param (ref $ct)) (result (ref $ct)))) + ;; CHECK-TEXT: (type $7 (func (param (ref $ct)) (result (ref $ct)))) + + ;; CHECK-TEXT: (type $8 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) + + ;; CHECK-TEXT: (type $9 (func (param (ref $ct1)))) + + ;; CHECK-TEXT: (type $ct0 (sub (cont $f1))) + + ;; CHECK-TEXT: (global $kglo (mut (ref null $k1)) (ref.null nocont)) + + ;; CHECK-TEXT: (global $gglo (ref null $k1) (ref.null nocont)) + + ;; CHECK-TEXT: (elem declare func $f1 $f2 $f3 $fglo $r0 $r1) + + ;; CHECK-TEXT: (tag $exn) + + ;; CHECK-TEXT: (tag $e1) + + ;; CHECK-TEXT: (tag $e2) + + ;; CHECK-TEXT: (export "unhandled-1" (func $f1)) + + ;; CHECK-TEXT: (export "unhandled-2" (func $2)) + + ;; CHECK-TEXT: (export "unhandled-3" (func $3)) + + ;; CHECK-TEXT: (export "handled" (func $4)) + + ;; CHECK-TEXT: (export "uncaught-1" (func $5)) + + ;; CHECK-TEXT: (export "uncaught-2" (func $6)) + + ;; CHECK-TEXT: (export "uncaught-3" (func $7)) - ;; CHECK-TEXT: (type $3 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) + ;; CHECK-TEXT: (export "non-linear-1" (func $8)) - ;; CHECK-TEXT: (func $id (type $2) (param $x (ref $ct)) (result (ref $ct)) + ;; CHECK-TEXT: (export "non-linear-2" (func $9)) + + ;; CHECK-TEXT: (export "non-linear-3" (func $10)) + + ;; CHECK-TEXT: (export "non-linear-4" (func $11)) + + ;; CHECK-TEXT: (func $id (type $7) (param $x (ref $ct)) (result (ref $ct)) ;; CHECK-TEXT-NEXT: (local.get $x) ;; CHECK-TEXT-NEXT: ) - ;; CHECK-BIN: (type $2 (func (param (ref $ct)) (result (ref $ct)))) + ;; CHECK-BIN: (type $7 (func (param (ref $ct)) (result (ref $ct)))) + + ;; CHECK-BIN: (type $8 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) + + ;; CHECK-BIN: (type $9 (func (param (ref $ct1)))) + + ;; CHECK-BIN: (type $ct0 (sub (cont $f1))) + + ;; CHECK-BIN: (global $kglo (mut (ref null $k1)) (ref.null nocont)) + + ;; CHECK-BIN: (global $gglo (ref null $k1) (ref.null nocont)) + + ;; CHECK-BIN: (elem declare func $f1 $f2 $f3 $fglo $r0 $r1) + + ;; CHECK-BIN: (tag $exn) + + ;; CHECK-BIN: (tag $e1) + + ;; CHECK-BIN: (tag $e2) + + ;; CHECK-BIN: (export "unhandled-1" (func $f1)) - ;; CHECK-BIN: (type $3 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) + ;; CHECK-BIN: (export "unhandled-2" (func $6)) - ;; CHECK-BIN: (func $id (type $2) (param $x (ref $ct)) (result (ref $ct)) + ;; CHECK-BIN: (export "unhandled-3" (func $7)) + + ;; CHECK-BIN: (export "handled" (func $8)) + + ;; CHECK-BIN: (export "uncaught-1" (func $10)) + + ;; CHECK-BIN: (export "uncaught-2" (func $11)) + + ;; CHECK-BIN: (export "uncaught-3" (func $14)) + + ;; CHECK-BIN: (export "non-linear-1" (func $21)) + + ;; CHECK-BIN: (export "non-linear-2" (func $22)) + + ;; CHECK-BIN: (export "non-linear-3" (func $23)) + + ;; CHECK-BIN: (export "non-linear-4" (func $24)) + + ;; CHECK-BIN: (func $id (type $7) (param $x (ref $ct)) (result (ref $ct)) ;; CHECK-BIN-NEXT: (local.get $x) ;; CHECK-BIN-NEXT: ) (func $id (param $x (ref $ct)) (result (ref $ct)) (local.get $x) ) - ;; CHECK-TEXT: (func $id2 (type $3) (param $w contref) (param $x nullcontref) (param $y (ref cont)) (param $z (ref nocont)) (result contref) + ;; CHECK-TEXT: (func $id2 (type $8) (param $w contref) (param $x nullcontref) (param $y (ref cont)) (param $z (ref nocont)) (result contref) ;; CHECK-TEXT-NEXT: (local.get $z) ;; CHECK-TEXT-NEXT: ) - ;; CHECK-BIN: (func $id2 (type $3) (param $w contref) (param $x nullcontref) (param $y (ref cont)) (param $z (ref nocont)) (result contref) + ;; CHECK-BIN: (func $id2 (type $8) (param $w contref) (param $x nullcontref) (param $y (ref cont)) (param $z (ref nocont)) (result contref) ;; CHECK-BIN-NEXT: (local.get $z) ;; CHECK-BIN-NEXT: ) (func $id2 @@ -50,19 +146,828 @@ (local.get $z) ) + (tag $exn) + (tag $e1) + (tag $e2) + + (type $f1 (func)) + (type $k1 (cont $f1)) + + (rec + (type $f2 (func (param (ref $f3)))) + (type $f3 (func (param (ref $f2)))) + ) + (type $k2 (cont $f2)) + (type $k3 (cont $f3)) + + (type $ft1 (func (param i32))) + (type $ct1 (sub (cont $ft1))) + + (type $ft0 (func)) + (type $ct0 (sub (cont $ft0))) + + (type $f4 (sub (func (result anyref)))) + (type $f5 (sub $f4 (func (result eqref)))) + (type $c4 (sub (cont $f4))) + (type $c5 (sub $c4 (cont $f5))) + + (type $ft2 (func)) + (type $ct2 (cont $ft2)) + + (global $kglo (mut (ref null $ct2)) (ref.null $ct2)) + (global $gglo (ref null $ct2) (ref.null $ct2)) + + ;; CHECK-TEXT: (func $fglo (type $f1) + ;; CHECK-TEXT-NEXT: (nop) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $fglo (type $f1) + ;; CHECK-BIN-NEXT: (nop) + ;; CHECK-BIN-NEXT: ) + (func $fglo) + ;; CHECK-BIN-NODEBUG: (type $0 (func)) + + ;; CHECK-BIN-NODEBUG: (type $1 (cont $0)) + + ;; CHECK-BIN-NODEBUG: (type $2 (func (param (ref $1)))) + + ;; CHECK-BIN-NODEBUG: (type $3 (func (param i32))) + + ;; CHECK-BIN-NODEBUG: (type $4 (sub (cont $3))) + + ;; CHECK-BIN-NODEBUG: (type $5 (func (param i32) (result i32))) + + ;; CHECK-BIN-NODEBUG: (type $6 (cont $5)) + + ;; CHECK-BIN-NODEBUG: (type $7 (func (param (ref $6)) (result (ref $6)))) + + ;; CHECK-BIN-NODEBUG: (type $8 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) + + ;; CHECK-BIN-NODEBUG: (type $9 (func (param (ref $4)))) + + ;; CHECK-BIN-NODEBUG: (type $10 (sub (cont $0))) + + ;; CHECK-BIN-NODEBUG: (global $global$0 (mut (ref null $1)) (ref.null nocont)) + + ;; CHECK-BIN-NODEBUG: (global $global$1 (ref null $1) (ref.null nocont)) + + ;; CHECK-BIN-NODEBUG: (elem declare func $12 $15 $16 $2 $5 $9) + (elem declare func $fglo) + + (func + (global.set $kglo (cont.new $ct2 (ref.func $fglo)))) + + (func (param $x (ref $ct1)) + (i32.const 123) + (local.get $x) + (cont.bind $ct1 $ct0) + (drop) + ) + + ;; CHECK-TEXT: (func $0 (type $f1) + ;; CHECK-TEXT-NEXT: (global.set $kglo + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $fglo) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $1 (type $9) (param $x (ref $ct1)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (cont.bind $ct1 $ct0 + ;; CHECK-TEXT-NEXT: (i32.const 123) + ;; CHECK-TEXT-NEXT: (local.get $x) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $f1 (type $f1) + ;; CHECK-TEXT-NEXT: (suspend $e1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $3 (type $f1) + ;; CHECK-BIN-NEXT: (global.set $kglo + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $fglo) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $4 (type $9) (param $x (ref $ct1)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (cont.bind $ct1 $ct0 + ;; CHECK-BIN-NEXT: (i32.const 123) + ;; CHECK-BIN-NEXT: (local.get $x) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $f1 (type $f1) + ;; CHECK-BIN-NEXT: (suspend $e1) + ;; CHECK-BIN-NEXT: ) + (func $f1 (export "unhandled-1") + (suspend $e1) + ) + + (func (export "unhandled-2") + (resume $k1 (cont.new $k1 (ref.func $f1))) + ) + + (func (export "unhandled-3") + (block $h (result (ref $k1)) + (resume $k1 (on $e2 $h) (cont.new $k1 (ref.func $f1))) + (unreachable) + ) + (drop) + ) + + (func (export "handled") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f1))) + (unreachable) + ) + (drop) + ) + + (elem declare func $f2) + ;; CHECK-TEXT: (func $2 (type $f1) + ;; CHECK-TEXT-NEXT: (resume $k1 + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $f1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $3 (type $f1) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block $h (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e2 $h) + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $f1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $4 (type $f1) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block $h (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h) + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $f1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $f2 (type $f1) + ;; CHECK-TEXT-NEXT: (throw $exn) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $6 (type $f1) + ;; CHECK-BIN-NEXT: (resume $k1 + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $f1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $7 (type $f1) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e2 $label$1) + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $f1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $8 (type $f1) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$1) + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $f1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $f2 (type $f1) + ;; CHECK-BIN-NEXT: (throw $exn) + ;; CHECK-BIN-NEXT: ) + (func $f2 + (throw $exn) + ) + + (func (export "uncaught-1") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f2))) + (unreachable) + ) + (drop) + ) + + (func (export "uncaught-2") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f1))) + (unreachable) + ) + (resume_throw $k1 $exn) + ) + + (elem declare func $f3) + ;; CHECK-TEXT: (func $5 (type $f1) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block $h (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h) + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $f2) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $6 (type $f1) + ;; CHECK-TEXT-NEXT: (resume_throw $k1 $exn + ;; CHECK-TEXT-NEXT: (block $h (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h) + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $f1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $f3 (type $f1) + ;; CHECK-TEXT-NEXT: (call $f4) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $10 (type $f1) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$1) + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $f2) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $11 (type $f1) + ;; CHECK-BIN-NEXT: (resume_throw $k1 $exn + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$1) + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $f1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $f3 (type $f1) + ;; CHECK-BIN-NEXT: (call $f4) + ;; CHECK-BIN-NEXT: ) + (func $f3 + (call $f4) + ) + ;; CHECK-TEXT: (func $f4 (type $f1) + ;; CHECK-TEXT-NEXT: (suspend $e1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $f4 (type $f1) + ;; CHECK-BIN-NEXT: (suspend $e1) + ;; CHECK-BIN-NEXT: ) + (func $f4 + (suspend $e1) + ) + + (func (export "uncaught-3") + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (cont.new $k1 (ref.func $f3))) + (unreachable) + ) + (resume_throw $k1 $exn) + ) + + (elem declare func $r0 $r1) + ;; CHECK-TEXT: (func $7 (type $f1) + ;; CHECK-TEXT-NEXT: (resume_throw $k1 $exn + ;; CHECK-TEXT-NEXT: (block $h (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h) + ;; CHECK-TEXT-NEXT: (cont.new $k1 + ;; CHECK-TEXT-NEXT: (ref.func $f3) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + + ;; CHECK-TEXT: (func $r0 (type $f1) + ;; CHECK-TEXT-NEXT: (nop) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $14 (type $f1) + ;; CHECK-BIN-NEXT: (resume_throw $k1 $exn + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$1) + ;; CHECK-BIN-NEXT: (cont.new $k1 + ;; CHECK-BIN-NEXT: (ref.func $f3) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + + ;; CHECK-BIN: (func $r0 (type $f1) + ;; CHECK-BIN-NEXT: (nop) + ;; CHECK-BIN-NEXT: ) + (func $r0) + + ;; CHECK-TEXT: (func $r1 (type $f1) + ;; CHECK-TEXT-NEXT: (suspend $e1) + ;; CHECK-TEXT-NEXT: (suspend $e1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $r1 (type $f1) + ;; CHECK-BIN-NEXT: (suspend $e1) + ;; CHECK-BIN-NEXT: (suspend $e1) + ;; CHECK-BIN-NEXT: ) + (func $r1 (suspend $e1) (suspend $e1)) + + ;; CHECK-TEXT: (func $nl1 (type $2) (param $k (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (resume $k1 + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $nl1 (type $2) (param $k (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (resume $k1 + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $nl1 (param $k (ref $k1)) + (resume $k1 (local.get $k)) + (resume $k1 (local.get $k)) + ) + ;; CHECK-TEXT: (func $nl2 (type $2) (param $k (ref $k1)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block $h (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h) + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (resume $k1 + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $nl2 (type $2) (param $k (ref $k1)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$1) + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (resume $k1 + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + (func $nl2 (param $k (ref $k1)) + (block $h (result (ref $k1)) + (resume $k1 (on $e1 $h) (local.get $k)) + (unreachable) + ) + (resume $k1 (local.get $k)) + (unreachable) + ) + ;; CHECK-TEXT: (func $nl3 (type $2) (param $k (ref $k1)) + ;; CHECK-TEXT-NEXT: (local $k' (ref null $k1)) + ;; CHECK-TEXT-NEXT: (local.set $k' + ;; CHECK-TEXT-NEXT: (block $h1 (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h1) + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (block $h2 (result (ref $k1)) + ;; CHECK-TEXT-NEXT: (resume $k1 (on $e1 $h2) + ;; CHECK-TEXT-NEXT: (local.get $k') + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (resume $k1 + ;; CHECK-TEXT-NEXT: (local.get $k') + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $nl3 (type $2) (param $k (ref $k1)) + ;; CHECK-BIN-NEXT: (local $k' (ref null $k1)) + ;; CHECK-BIN-NEXT: (local.set $k' + ;; CHECK-BIN-NEXT: (block $label$1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$1) + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (block $label$2 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $label$2) + ;; CHECK-BIN-NEXT: (local.get $k') + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (resume $k1 + ;; CHECK-BIN-NEXT: (local.get $k') + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (unreachable) + ;; CHECK-BIN-NEXT: ) + (func $nl3 (param $k (ref $k1)) + (local $k' (ref null $k1)) + (block $h1 (result (ref $k1)) + (resume $k1 (on $e1 $h1) (local.get $k)) + (unreachable) + ) + (local.set $k') + (block $h2 (result (ref $k1)) + (resume $k1 (on $e1 $h2) (local.get $k')) + (unreachable) + ) + (resume $k1 (local.get $k')) + (unreachable) + ) + ;; CHECK-TEXT: (func $nl4 (type $2) (param $k (ref $k1)) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (cont.bind $k1 $k1 + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (resume $k1 + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $nl4 (type $2) (param $k (ref $k1)) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (cont.bind $k1 $k1 + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (resume $k1 + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $nl4 (param $k (ref $k1)) + (drop (cont.bind $k1 $k1 (local.get $k))) + (resume $k1 (local.get $k)) + ) + + (func (export "non-linear-1") + (call $nl1 (cont.new $k1 (ref.func $r0))) + ) + (func (export "non-linear-2") + (call $nl2 (cont.new $k1 (ref.func $r1))) + ) + (func (export "non-linear-3") + (call $nl3 (cont.new $k1 (ref.func $r1))) + ) + (func (export "non-linear-4") + (call $nl4 (cont.new $k1 (ref.func $r1))) + ) + ) -;; CHECK-BIN-NODEBUG: (type $0 (func (param i32) (result i32))) +;; CHECK-TEXT: (func $8 (type $f1) +;; CHECK-TEXT-NEXT: (call $nl1 +;; CHECK-TEXT-NEXT: (cont.new $k1 +;; CHECK-TEXT-NEXT: (ref.func $r0) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) + +;; CHECK-TEXT: (func $9 (type $f1) +;; CHECK-TEXT-NEXT: (call $nl2 +;; CHECK-TEXT-NEXT: (cont.new $k1 +;; CHECK-TEXT-NEXT: (ref.func $r1) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) + +;; CHECK-TEXT: (func $10 (type $f1) +;; CHECK-TEXT-NEXT: (call $nl3 +;; CHECK-TEXT-NEXT: (cont.new $k1 +;; CHECK-TEXT-NEXT: (ref.func $r1) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) + +;; CHECK-TEXT: (func $11 (type $f1) +;; CHECK-TEXT-NEXT: (call $nl4 +;; CHECK-TEXT-NEXT: (cont.new $k1 +;; CHECK-TEXT-NEXT: (ref.func $r1) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) + +;; CHECK-BIN: (func $21 (type $f1) +;; CHECK-BIN-NEXT: (call $nl1 +;; CHECK-BIN-NEXT: (cont.new $k1 +;; CHECK-BIN-NEXT: (ref.func $r0) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) + +;; CHECK-BIN: (func $22 (type $f1) +;; CHECK-BIN-NEXT: (call $nl2 +;; CHECK-BIN-NEXT: (cont.new $k1 +;; CHECK-BIN-NEXT: (ref.func $r1) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) + +;; CHECK-BIN: (func $23 (type $f1) +;; CHECK-BIN-NEXT: (call $nl3 +;; CHECK-BIN-NEXT: (cont.new $k1 +;; CHECK-BIN-NEXT: (ref.func $r1) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) + +;; CHECK-BIN: (func $24 (type $f1) +;; CHECK-BIN-NEXT: (call $nl4 +;; CHECK-BIN-NEXT: (cont.new $k1 +;; CHECK-BIN-NEXT: (ref.func $r1) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) + +;; CHECK-BIN-NODEBUG: (tag $tag$0) + +;; CHECK-BIN-NODEBUG: (tag $tag$1) + +;; CHECK-BIN-NODEBUG: (tag $tag$2) + +;; CHECK-BIN-NODEBUG: (export "unhandled-1" (func $5)) + +;; CHECK-BIN-NODEBUG: (export "unhandled-2" (func $6)) + +;; CHECK-BIN-NODEBUG: (export "unhandled-3" (func $7)) + +;; CHECK-BIN-NODEBUG: (export "handled" (func $8)) -;; CHECK-BIN-NODEBUG: (type $1 (cont $0)) +;; CHECK-BIN-NODEBUG: (export "uncaught-1" (func $10)) -;; CHECK-BIN-NODEBUG: (type $2 (func (param (ref $1)) (result (ref $1)))) +;; CHECK-BIN-NODEBUG: (export "uncaught-2" (func $11)) -;; CHECK-BIN-NODEBUG: (type $3 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) +;; CHECK-BIN-NODEBUG: (export "uncaught-3" (func $14)) -;; CHECK-BIN-NODEBUG: (func $0 (type $2) (param $0 (ref $1)) (result (ref $1)) +;; CHECK-BIN-NODEBUG: (export "non-linear-1" (func $21)) + +;; CHECK-BIN-NODEBUG: (export "non-linear-2" (func $22)) + +;; CHECK-BIN-NODEBUG: (export "non-linear-3" (func $23)) + +;; CHECK-BIN-NODEBUG: (export "non-linear-4" (func $24)) + +;; CHECK-BIN-NODEBUG: (func $0 (type $7) (param $0 (ref $6)) (result (ref $6)) ;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) ;; CHECK-BIN-NODEBUG-NEXT: ) -;; CHECK-BIN-NODEBUG: (func $1 (type $3) (param $0 contref) (param $1 nullcontref) (param $2 (ref cont)) (param $3 (ref nocont)) (result contref) +;; CHECK-BIN-NODEBUG: (func $1 (type $8) (param $0 contref) (param $1 nullcontref) (param $2 (ref cont)) (param $3 (ref nocont)) (result contref) ;; CHECK-BIN-NODEBUG-NEXT: (local.get $3) ;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $2 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (nop) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $3 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (global.set $global$0 +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $2) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $4 (type $9) (param $0 (ref $4)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (cont.bind $4 $10 +;; CHECK-BIN-NODEBUG-NEXT: (i32.const 123) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $5 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (suspend $tag$1) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $6 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $5) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $7 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$2 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $5) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $8 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $5) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $9 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (throw $tag$0) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $10 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $9) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $11 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (resume_throw $1 $tag$0 +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $5) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $12 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (call $13) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $13 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (suspend $tag$1) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $14 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (resume_throw $1 $tag$0 +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $12) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $15 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (nop) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $16 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (suspend $tag$1) +;; CHECK-BIN-NODEBUG-NEXT: (suspend $tag$1) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $17 (type $2) (param $0 (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $18 (type $2) (param $0 (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $19 (type $2) (param $0 (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (local $1 (ref null $1)) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $1 +;; CHECK-BIN-NODEBUG-NEXT: (block $label$1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$1) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (block $label$2 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $label$2) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $1) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $1) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (unreachable) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $20 (type $2) (param $0 (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (cont.bind $1 $1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $21 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (call $17 +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $15) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $22 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (call $18 +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $16) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $23 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (call $19 +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $16) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $24 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (call $20 +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.func $16) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) diff --git a/test/lit/basic/stack_switching_contnew.wast b/test/lit/basic/stack_switching_contnew.wast index 19f27b28147..5404dfb6db5 100644 --- a/test/lit/basic/stack_switching_contnew.wast +++ b/test/lit/basic/stack_switching_contnew.wast @@ -19,6 +19,8 @@ ;; CHECK-TEXT: (type $2 (func (result (ref $ct)))) + ;; CHECK-TEXT: (type $3 (func (result (ref cont)))) + ;; CHECK-TEXT: (elem declare func $g) ;; CHECK-TEXT: (func $f (type $2) (result (ref $ct)) @@ -31,6 +33,8 @@ ;; CHECK-TEXT-NEXT: ) ;; CHECK-BIN: (type $2 (func (result (ref $ct)))) + ;; CHECK-BIN: (type $3 (func (result (ref cont)))) + ;; CHECK-BIN: (elem declare func $g) ;; CHECK-BIN: (func $f (type $2) (result (ref $ct)) @@ -54,6 +58,8 @@ ;; CHECK-BIN-NODEBUG: (type $2 (func (result (ref $1)))) + ;; CHECK-BIN-NODEBUG: (type $3 (func (result (ref cont)))) + ;; CHECK-BIN-NODEBUG: (elem declare func $1) (elem declare func $g) @@ -71,7 +77,22 @@ (cont.new $ct (ref.func $g)) ) + (func (result (ref cont)) + (cont.new $ct (ref.null nofunc))) + ) +;; CHECK-TEXT: (func $0 (type $3) (result (ref cont)) +;; CHECK-TEXT-NEXT: (cont.new $ct +;; CHECK-TEXT-NEXT: (ref.null nofunc) +;; CHECK-TEXT-NEXT: ) +;; CHECK-TEXT-NEXT: ) + +;; CHECK-BIN: (func $3 (type $3) (result (ref cont)) +;; CHECK-BIN-NEXT: (cont.new $ct +;; CHECK-BIN-NEXT: (ref.null nofunc) +;; CHECK-BIN-NEXT: ) +;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NODEBUG: (func $0 (type $2) (result (ref $1)) ;; CHECK-BIN-NODEBUG-NEXT: (unreachable) ;; CHECK-BIN-NODEBUG-NEXT: ) @@ -85,3 +106,9 @@ ;; CHECK-BIN-NODEBUG-NEXT: (ref.func $1) ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $3 (type $3) (result (ref cont)) +;; CHECK-BIN-NODEBUG-NEXT: (cont.new $1 +;; CHECK-BIN-NODEBUG-NEXT: (ref.null nofunc) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) From c8d345a900151fffe69df37d99a27b7076de3c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 13 Nov 2024 15:14:36 +0100 Subject: [PATCH 17/23] Compute resume table sent types during expression construction. --- src/wasm-builder.h | 51 ++------------------- src/wasm/wasm-binary.cpp | 48 ++++++++++++++++++++ src/wasm/wasm-ir-builder.cpp | 63 ++++++++++++++++++++++++-- src/wasm/wasm.cpp | 86 ------------------------------------ 4 files changed, 112 insertions(+), 136 deletions(-) diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 91a974af984..7954297bdb7 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1194,61 +1194,17 @@ class Builder { ret->finalize(&wasm); return ret; } - template - std::vector computeResumeTableSentTypes(ResumeOrResumeThrow* curr) { - static_assert(std::is_base_of::value || - std::is_base_of::value); - assert(curr->handlerBlocks.size() == curr->handlerTags.size()); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction - // (resume $ct ... (tag $tag $block) ... ) causes $block to receive values - // of the following types when suspending to $tag: tgp* (ref $ct') where ct' - // = (cont $ft') and ft' = [tgr*] -> [ctr*]. - // - std::vector sentTypes; - sentTypes.reserve(curr->handlerTags.size()); - auto contSig = curr->contType.getContinuation().type.getSignature(); - auto& ctrs = contSig.params; - for (Index i = 0; i < curr->handlerTags.size(); i++) { - if (curr->handlerBlocks[i].isNull()) { - sentTypes[i] = Type::none; - continue; - } - - auto& tag = curr->handlerTags[i]; - auto& tagSig = wasm.getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; - - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); - - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); - - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - sentTypes[i] = Type(sentValueTypes); - } else { - sentTypes[i] = ctPrimeRef; - } - } - return sentTypes; - } Resume* makeResume(HeapType contType, const std::vector& handlerTags, const std::vector& handlerBlocks, + const std::vector& sentTypes, const std::vector& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); ret->contType = contType; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); - ret->sentTypes.set(computeResumeTableSentTypes(ret)); + ret->sentTypes.set(sentTypes); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); @@ -1258,6 +1214,7 @@ class Builder { Name tag, const std::vector& handlerTags, const std::vector& handlerBlocks, + const std::vector& sentTypes, const std::vector& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); @@ -1265,7 +1222,7 @@ class Builder { ret->tag = tag; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); - ret->sentTypes.set(computeResumeTableSentTypes(ret)); + ret->sentTypes.set(sentTypes); ret->operands.set(operands); ret->cont = cont; ret->finalize(&wasm); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 4b16b499783..9f04477d5b1 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -7981,6 +7981,52 @@ static void readResumeTable(WasmBinaryReader* reader, ResumeType* curr) { } } +template +static void computeResumeTableSentTypes(Module& wasm, + ResumeOrResumeThrow* curr) { + static_assert(std::is_base_of::value || + std::is_base_of::value); + assert(curr->handlerBlocks.size() == curr->handlerTags.size()); + + // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation + // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction + // (resume $ct ... (tag $tag $block) ... ) causes $block to receive values + // of the following types when suspending to $tag: tgp* (ref $ct') where ct' + // = (cont $ft') and ft' = [tgr*] -> [ctr*]. + // + curr->sentTypes.reserve(curr->handlerTags.size()); + auto contSig = curr->contType.getContinuation().type.getSignature(); + auto& ctrs = contSig.params; + for (Index i = 0; i < curr->handlerTags.size(); i++) { + Type sentType; + if (curr->handlerBlocks[i].isNull()) { + sentType = Type::none; + } else { + auto& tag = curr->handlerTags[i]; + auto& tagSig = wasm.getTag(tag)->sig; + + auto& tgps = tagSig.params; + auto& tgrs = tagSig.results; + + HeapType ftPrime{Signature(tgrs, ctrs)}; + HeapType ctPrime{Continuation(ftPrime)}; + Type ctPrimeRef(ctPrime, Nullability::NonNullable); + + if (tgps.size() > 0) { + TypeList sentValueTypes; + sentValueTypes.reserve(tgps.size() + 1); + + sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); + sentValueTypes.push_back(ctPrimeRef); + sentType = Type(sentValueTypes); + } else { + sentType = ctPrimeRef; + } + } + curr->sentTypes.push_back(sentType); + } +} + void WasmBinaryReader::visitResume(Resume* curr) { curr->contType = getIndexedHeapType(); @@ -7990,6 +8036,7 @@ void WasmBinaryReader::visitResume(Resume* curr) { } readResumeTable(this, curr); + computeResumeTableSentTypes(wasm, curr); curr->cont = popNonVoidExpression(); @@ -8014,6 +8061,7 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { tagRefs[exnTagIndex].push_back(&curr->tag); readResumeTable(this, curr); + computeResumeTableSentTypes(wasm, curr); curr->cont = popNonVoidExpression(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 761b4e6381f..6c17f919aaa 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1945,6 +1945,55 @@ Result<> IRBuilder::makeSuspend(Name tag) { return Ok{}; } +static std::vector +computeResumeTableSentTypes(Module& wasm, + const Signature& contSig, + const std::vector& handlerTags, + const std::vector handlerBlocks) { + assert(handlerBlocks.size() == handlerTags.size()); + + // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation + // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction + // (resume $ct ... (tag $tag $block) ... ) causes $block to receive values + // of the following types when suspending to $tag: tgp* (ref $ct') where ct' + // = (cont $ft') and ft' = [tgr*] -> [ctr*]. + // + std::vector sentTypes; + sentTypes.reserve(handlerTags.size()); + auto& ctrs = contSig.params; + for (Index i = 0; i < handlerTags.size(); i++) { + Type sentType; + if (handlerBlocks[i].isNull()) { + sentType = Type::none; + } else { + auto& tag = handlerTags[i]; + auto& tagSig = wasm.getTag(tag)->sig; + + auto& tgps = tagSig.params; + auto& tgrs = tagSig.results; + + HeapType ftPrime{Signature(tgrs, ctrs)}; + HeapType ctPrime{Continuation(ftPrime)}; + Type ctPrimeRef(ctPrime, Nullability::NonNullable); + + if (tgps.size() > 0) { + TypeList sentValueTypes; + sentValueTypes.reserve(tgps.size() + 1); + + sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); + sentValueTypes.push_back(ctPrimeRef); + sentType = Type(sentValueTypes); + } else { + sentType = ctPrimeRef; + } + } + sentTypes.push_back(sentType); + } + assert(sentTypes.size() == handlerTags.size() && + sentTypes.size() == handlerBlocks.size()); + return sentTypes; +} + Result<> IRBuilder::makeResume(HeapType ct, const std::vector& tags, @@ -1969,9 +2018,13 @@ IRBuilder::makeResume(HeapType ct, labelNames.push_back(Name()); } } - + std::vector sentTypes = + computeResumeTableSentTypes(wasm, contSig, tags, labelNames); + curr.sentTypes.resize(sentTypes.size()); + assert(sentTypes.size() == labelNames.size()); std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResume(ct, tags, labelNames, operands, curr.cont)); + push( + builder.makeResume(ct, tags, labelNames, sentTypes, operands, curr.cont)); return Ok{}; } @@ -1987,6 +2040,7 @@ IRBuilder::makeResumeThrow(HeapType ct, return Err{"expected continuation type"}; } ResumeThrow curr(wasm.allocator); + auto contSig = ct.getContinuation().type.getSignature(); curr.contType = ct; curr.tag = tag; curr.operands.resize(wasm.getTag(tag)->sig.params.size()); @@ -2003,9 +2057,12 @@ IRBuilder::makeResumeThrow(HeapType ct, labelNames.push_back(Name()); } } + std::vector sentTypes = + computeResumeTableSentTypes(wasm, contSig, tags, labelNames); std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResumeThrow(ct, tag, tags, labelNames, operands, curr.cont)); + push(builder.makeResumeThrow( + ct, tag, tags, labelNames, sentTypes, operands, curr.cont)); return Ok{}; } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 841737d47ea..2a5050642c1 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1387,47 +1387,6 @@ void Suspend::finalize(Module* wasm) { } } -static void populateResumeSentTypes(Resume* curr, Module* wasm) { - if (!wasm) { - return; - } - - const Signature& contSig = - curr->contType.getContinuation().type.getSignature(); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume - // $ct ... (tag $tag $block) ... ) causes $block to receive values of the - // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont - // $ft') and ft' = [tgr*] -> [ctr*]. - // - auto& ctrs = contSig.results; - curr->sentTypes.clear(); - curr->sentTypes.resize(curr->handlerTags.size()); - for (Index i = 0; i < curr->handlerTags.size(); i++) { - auto& tag = curr->handlerTags[i]; - auto& tagSig = wasm->getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; - - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); - - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); - - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - curr->sentTypes[i] = Type(sentValueTypes); - } else { - curr->sentTypes[i] = ctPrimeRef; - } - } -} - void Resume::finalize(Module* wasm) { if (cont->type == Type::unreachable) { type = Type::unreachable; @@ -1436,49 +1395,6 @@ void Resume::finalize(Module* wasm) { this->contType.getContinuation().type.getSignature(); type = contSig.results; } - - populateResumeSentTypes(this, wasm); -} - -static void populateResumeThrowSentTypes(ResumeThrow* curr, Module* wasm) { - if (!wasm) { - return; - } - - const Signature& contSig = - curr->contType.getContinuation().type.getSignature(); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume - // $ct ... (tag $tag $block) ... ) causes $block to receive values of the - // following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont - // $ft') and ft' = [tgr*] -> [ctr*]. - // - auto& ctrs = contSig.results; - curr->sentTypes.clear(); - curr->sentTypes.resize(curr->handlerTags.size()); - for (Index i = 0; i < curr->handlerTags.size(); i++) { - auto& tag = curr->handlerTags[i]; - auto& tagSig = wasm->getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; - - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); - - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); - - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - curr->sentTypes[i] = Type(sentValueTypes); - } else { - curr->sentTypes[i] = ctPrimeRef; - } - } } void ResumeThrow::finalize(Module* wasm) { @@ -1489,8 +1405,6 @@ void ResumeThrow::finalize(Module* wasm) { this->contType.getContinuation().type.getSignature(); type = contSig.results; } - - populateResumeThrowSentTypes(this, wasm); } void StackSwitch::finalize(Module* wasm) { From 0110dd584dc6d3d52599a239f4c2fa8f7c30bead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Wed, 13 Nov 2024 15:35:48 +0100 Subject: [PATCH 18/23] Add switch lit test --- scripts/fuzz_opt.py | 3 +- test/lit/basic/stack_switching_switch.wast | 185 +++++++++++++++++++++ 2 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 test/lit/basic/stack_switching_switch.wast diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 63161548915..c4358fe1244 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -347,13 +347,14 @@ def is_git_repo(): # the fuzzer does not support imported memories 'multi-memory-lowering-import.wast', 'multi-memory-lowering-import-error.wast', - # the fuzzer does not support typed continuations + # the fuzzer does not support stack switching 'stack_switching.wast', 'stack_switching_contnew.wast', 'stack_switching_contbind.wast', 'stack_switching_suspend.wast', 'stack_switching_resume.wast', 'stack_switching_resume_throw.wast', + 'stack_switching_switch.wast' ] diff --git a/test/lit/basic/stack_switching_switch.wast b/test/lit/basic/stack_switching_switch.wast new file mode 100644 index 00000000000..eaefacbcd99 --- /dev/null +++ b/test/lit/basic/stack_switching_switch.wast @@ -0,0 +1,185 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all -o %t.text.wast -g -S +;; RUN: wasm-as %s -all -g -o %t.wasm +;; RUN: wasm-dis %t.wasm -all -o %t.bin.wast +;; RUN: wasm-as %s -all -o %t.nodebug.wasm +;; RUN: wasm-dis %t.nodebug.wasm -all -o %t.bin.nodebug.wast +;; RUN: cat %t.text.wast | filecheck %s --check-prefix=CHECK-TEXT +;; RUN: cat %t.bin.wast | filecheck %s --check-prefix=CHECK-BIN +;; RUN: cat %t.bin.nodebug.wast | filecheck %s --check-prefix=CHECK-BIN-NODEBUG + +(module + (rec + ;; CHECK-TEXT: (rec + ;; CHECK-TEXT-NEXT: (type $ft (func (param i32 (ref null $ct)) (result i32))) + ;; CHECK-BIN: (rec + ;; CHECK-BIN-NEXT: (type $ft (func (param i32 (ref null $ct)) (result i32))) + (type $ft (func (param i32) (param (ref null $ct)) (result i32))) + ;; CHECK-TEXT: (type $ct (cont $ft)) + ;; CHECK-BIN: (type $ct (cont $ft)) + (type $ct (cont $ft))) + + ;; CHECK-TEXT: (type $2 (func (result i32))) + + ;; CHECK-TEXT: (type $3 (func (param (ref null $ct)) (result i32))) + + ;; CHECK-TEXT: (type $4 (func (param (ref $ct)) (result i32))) + + ;; CHECK-TEXT: (tag $t (result i32)) + ;; CHECK-BIN: (type $2 (func (result i32))) + + ;; CHECK-BIN: (type $3 (func (param (ref null $ct)) (result i32))) + + ;; CHECK-BIN: (type $4 (func (param (ref $ct)) (result i32))) + + ;; CHECK-BIN: (tag $t (result i32)) + (tag $t (result i32)) + + ;; CHECK-TEXT: (func $swap (type $3) (param $k (ref null $ct)) (result i32) + ;; CHECK-TEXT-NEXT: (local $scratch (tuple i32 (ref null $ct))) + ;; CHECK-TEXT-NEXT: (local $scratch_2 i32) + ;; CHECK-TEXT-NEXT: (return + ;; CHECK-TEXT-NEXT: (block (result i32) + ;; CHECK-TEXT-NEXT: (local.set $scratch_2 + ;; CHECK-TEXT-NEXT: (tuple.extract 2 0 + ;; CHECK-TEXT-NEXT: (local.tee $scratch + ;; CHECK-TEXT-NEXT: (switch $ct $t + ;; CHECK-TEXT-NEXT: (i32.const 42) + ;; CHECK-TEXT-NEXT: (local.get $k) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (local.set $k + ;; CHECK-TEXT-NEXT: (tuple.extract 2 1 + ;; CHECK-TEXT-NEXT: (local.get $scratch) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (local.get $scratch_2) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $swap (type $3) (param $k (ref null $ct)) (result i32) + ;; CHECK-BIN-NEXT: (local $scratch i32) + ;; CHECK-BIN-NEXT: (local $scratch_2 i32) + ;; CHECK-BIN-NEXT: (local $3 (ref null $ct)) + ;; CHECK-BIN-NEXT: (local $4 (tuple i32 (ref null $ct))) + ;; CHECK-BIN-NEXT: (local $5 i32) + ;; CHECK-BIN-NEXT: (local.set $4 + ;; CHECK-BIN-NEXT: (switch $ct $t + ;; CHECK-BIN-NEXT: (i32.const 42) + ;; CHECK-BIN-NEXT: (local.get $k) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.set $scratch_2 + ;; CHECK-BIN-NEXT: (local.tee $scratch + ;; CHECK-BIN-NEXT: (block (result i32) + ;; CHECK-BIN-NEXT: (local.set $5 + ;; CHECK-BIN-NEXT: (tuple.extract 2 0 + ;; CHECK-BIN-NEXT: (local.get $4) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.set $3 + ;; CHECK-BIN-NEXT: (tuple.extract 2 1 + ;; CHECK-BIN-NEXT: (local.get $4) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.get $5) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.set $k + ;; CHECK-BIN-NEXT: (local.get $3) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (return + ;; CHECK-BIN-NEXT: (local.get $scratch_2) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $swap (param $k (ref null $ct)) (result i32) + (switch $ct $t + (i32.const 42) + (local.get $k)) + (local.set $k) + (return)) + + ;; CHECK-TEXT: (func $go (type $4) (param $x (ref $ct)) (result i32) + ;; CHECK-TEXT-NEXT: (resume $ct (on $t switch) + ;; CHECK-TEXT-NEXT: (i32.const 123) + ;; CHECK-TEXT-NEXT: (ref.null nocont) + ;; CHECK-TEXT-NEXT: (local.get $x) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $go (type $4) (param $x (ref $ct)) (result i32) + ;; CHECK-BIN-NEXT: (resume $ct (on $t switch) + ;; CHECK-BIN-NEXT: (i32.const 123) + ;; CHECK-BIN-NEXT: (ref.null nocont) + ;; CHECK-BIN-NEXT: (local.get $x) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $go (param $x (ref $ct)) (result i32) + (resume $ct + (on $t switch) + (i32.const 123) + (ref.null $ct) + (local.get $x) + ) + ) +) +;; CHECK-BIN-NODEBUG: (rec +;; CHECK-BIN-NODEBUG-NEXT: (type $0 (func (param i32 (ref null $1)) (result i32))) + +;; CHECK-BIN-NODEBUG: (type $1 (cont $0)) + +;; CHECK-BIN-NODEBUG: (type $2 (func (result i32))) + +;; CHECK-BIN-NODEBUG: (type $3 (func (param (ref null $1)) (result i32))) + +;; CHECK-BIN-NODEBUG: (type $4 (func (param (ref $1)) (result i32))) + +;; CHECK-BIN-NODEBUG: (tag $tag$0 (result i32)) + +;; CHECK-BIN-NODEBUG: (func $0 (type $3) (param $0 (ref null $1)) (result i32) +;; CHECK-BIN-NODEBUG-NEXT: (local $1 i32) +;; CHECK-BIN-NODEBUG-NEXT: (local $2 i32) +;; CHECK-BIN-NODEBUG-NEXT: (local $3 (ref null $1)) +;; CHECK-BIN-NODEBUG-NEXT: (local $4 (tuple i32 (ref null $1))) +;; CHECK-BIN-NODEBUG-NEXT: (local $5 i32) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $4 +;; CHECK-BIN-NODEBUG-NEXT: (switch $1 $tag$0 +;; CHECK-BIN-NODEBUG-NEXT: (i32.const 42) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $2 +;; CHECK-BIN-NODEBUG-NEXT: (local.tee $1 +;; CHECK-BIN-NODEBUG-NEXT: (block (result i32) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $5 +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 0 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $4) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $3 +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $4) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $5) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $0 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $3) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (return +;; CHECK-BIN-NODEBUG-NEXT: (local.get $2) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $1 (type $4) (param $0 (ref $1)) (result i32) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$0 switch) +;; CHECK-BIN-NODEBUG-NEXT: (i32.const 123) +;; CHECK-BIN-NODEBUG-NEXT: (ref.null nocont) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) From f418774ee03428aff8873d12d1db1a13e2675159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Mon, 18 Nov 2024 14:42:29 +0100 Subject: [PATCH 19/23] Fix unreachable type printing --- src/passes/Print.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 18313bd64d1..85f4263409f 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -387,28 +387,28 @@ struct PrintSExpression : public UnifiedExpressionVisitor { } void visitContBind(ContBind* curr) { if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->sourceType, Nullable)) || + curr, Type(curr->sourceType, Nullable)) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } } void visitResume(Resume* curr) { if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->contType, Nullable)) || + curr, Type(curr->contType, Nullable)) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } } void visitResumeThrow(ResumeThrow* curr) { if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->contType, Nullable)) || + curr, Type(curr->contType, Nullable)) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } } void visitStackSwitch(StackSwitch* curr) { if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->contType, Nullable)) || + curr, Type(curr->contType, Nullable)) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } From b1d65ce727f1864d75462e842dd1edb52af2b46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Thu, 21 Nov 2024 15:44:18 +0100 Subject: [PATCH 20/23] Read sentTypes off label types. --- src/wasm/wasm-binary.cpp | 74 ++++++-------------- src/wasm/wasm-ir-builder.cpp | 131 ++++++++++++++++------------------- 2 files changed, 81 insertions(+), 124 deletions(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index c6faed0d15a..9f93a8e628d 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -7974,77 +7974,47 @@ static void readResumeTable(WasmBinaryReader* reader, ResumeType* curr) { std::is_base_of::value); auto numHandlers = reader->getU32LEB(); - - // We *must* bring the handlerTags vector to an appropriate size to ensure - // that we do not invalidate the pointers we add to tagRefs. They need to stay - // valid until processNames ran. curr->handlerTags.resize(numHandlers); curr->handlerBlocks.resize(numHandlers); + curr->sentTypes.resize(numHandlers); for (size_t i = 0; i < numHandlers; i++) { uint8_t code = reader->getInt8(); auto tagIndex = reader->getU32LEB(); auto tag = reader->getTagName(tagIndex); Name handler; + Type sentType; if (code == BinaryConsts::OnLabel) { // expect (on $tag $label) auto handlerIndex = reader->getU32LEB(); - handler = reader->getBreakTarget(handlerIndex).name; + auto target = reader->getBreakTarget(handlerIndex); + handler = target.name; + if (target.type.isContinuation()) { + sentType = target.type; + } else if (target.type.isTuple() && + target.type.getTuple().back().isContinuation()) { + // The continuation type is expected to be the last element of + // a multi-valued block. + sentType = target.type; + } else { + if constexpr (std::is_base_of::value) { + reader->throwError("non-continuation type on resume branch target"); + } else { + reader->throwError( + "non-continuation type on resume_throw branch target"); + } + } } else if (code == BinaryConsts::OnSwitch) { // expect (on $tag switch) handler = Name(); + sentType = Type::none; } else { // error reader->throwError("ON opcode expected"); } curr->handlerTags[i] = tag; curr->handlerBlocks[i] = handler; - } -} - -template -static void computeResumeTableSentTypes(Module& wasm, - ResumeOrResumeThrow* curr) { - static_assert(std::is_base_of::value || - std::is_base_of::value); - assert(curr->handlerBlocks.size() == curr->handlerTags.size()); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction - // (resume $ct ... (tag $tag $block) ... ) causes $block to receive values - // of the following types when suspending to $tag: tgp* (ref $ct') where ct' - // = (cont $ft') and ft' = [tgr*] -> [ctr*]. - // - curr->sentTypes.reserve(curr->handlerTags.size()); - auto contSig = curr->contType.getContinuation().type.getSignature(); - auto& ctrs = contSig.params; - for (Index i = 0; i < curr->handlerTags.size(); i++) { - Type sentType; - if (curr->handlerBlocks[i].isNull()) { - sentType = Type::none; - } else { - auto& tag = curr->handlerTags[i]; - auto& tagSig = wasm.getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; - - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); - - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); - - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - sentType = Type(sentValueTypes); - } else { - sentType = ctPrimeRef; - } - } - curr->sentTypes.push_back(sentType); + curr->sentTypes[i] = sentType; } } @@ -8057,7 +8027,6 @@ void WasmBinaryReader::visitResume(Resume* curr) { } readResumeTable(this, curr); - computeResumeTableSentTypes(wasm, curr); curr->cont = popNonVoidExpression(); @@ -8081,7 +8050,6 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { curr->tag = getTagName(exnTagIndex); readResumeTable(this, curr); - computeResumeTableSentTypes(wasm, curr); curr->cont = popNonVoidExpression(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 4e4c1c3f209..c5bbac0422e 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -1963,53 +1963,52 @@ Result<> IRBuilder::makeSuspend(Name tag) { return Ok{}; } -static std::vector -computeResumeTableSentTypes(Module& wasm, - const Signature& contSig, - const std::vector& handlerTags, - const std::vector handlerBlocks) { - assert(handlerBlocks.size() == handlerTags.size()); - - // Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation - // type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction - // (resume $ct ... (tag $tag $block) ... ) causes $block to receive values - // of the following types when suspending to $tag: tgp* (ref $ct') where ct' - // = (cont $ft') and ft' = [tgr*] -> [ctr*]. - // +struct ResumeTable { + std::vector targets; std::vector sentTypes; - sentTypes.reserve(handlerTags.size()); - auto& ctrs = contSig.params; - for (Index i = 0; i < handlerTags.size(); i++) { - Type sentType; - if (handlerBlocks[i].isNull()) { - sentType = Type::none; - } else { - auto& tag = handlerTags[i]; - auto& tagSig = wasm.getTag(tag)->sig; - - auto& tgps = tagSig.params; - auto& tgrs = tagSig.results; +}; - HeapType ftPrime{Signature(tgrs, ctrs)}; - HeapType ctPrime{Continuation(ftPrime)}; - Type ctPrimeRef(ctPrime, Nullability::NonNullable); +static Result +makeResumeTable(const std::vector>& labels, + std::function(Index)> getLabelName, + std::function(Index)> getLabelType) { + std::vector targets; + targets.reserve(labels.size()); - if (tgps.size() > 0) { - TypeList sentValueTypes; - sentValueTypes.reserve(tgps.size() + 1); + std::vector sentTypes; + sentTypes.reserve(sentTypes.size()); - sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end()); - sentValueTypes.push_back(ctPrimeRef); - sentType = Type(sentValueTypes); + for (Index i = 0; i < labels.size(); i++) { + Name target; + Type sentType; + if (labels[i].has_value()) { + // (on $tag $label) clause + Index labelIndex = labels[i].value(); + Result name = getLabelName(labelIndex); + CHECK_ERR(name); + target = *name; + + Result targetType = getLabelType(labelIndex); + CHECK_ERR(targetType); + if (targetType->isContinuation()) { + sentType = *targetType; + } else if (targetType->isTuple() && + targetType->getTuple().back().isContinuation()) { + // The continuation type is expected to be the last element of + // a multi-valued block. + sentType = *targetType; } else { - sentType = ctPrimeRef; + return Err{"expected continuation type"}; } + } else { + // (on $tag switch) clause + target = Name(); + sentType = Type::none; } + targets.push_back(target); sentTypes.push_back(sentType); } - assert(sentTypes.size() == handlerTags.size() && - sentTypes.size() == handlerBlocks.size()); - return sentTypes; + return ResumeTable{std::move(targets), std::move(sentTypes)}; } Result<> @@ -2025,24 +2024,18 @@ IRBuilder::makeResume(HeapType ct, curr.operands.resize(contSig.params.size()); CHECK_ERR(visitResume(&curr)); - std::vector labelNames; - labelNames.reserve(labels.size()); - for (size_t i = 0; i < labels.size(); i++) { - if (labels[i].has_value()) { - auto name = getLabelName(labels[i].value()); - CHECK_ERR(name); - labelNames.push_back(*name); - } else { - labelNames.push_back(Name()); - } - } - std::vector sentTypes = - computeResumeTableSentTypes(wasm, contSig, tags, labelNames); - curr.sentTypes.resize(sentTypes.size()); - assert(sentTypes.size() == labelNames.size()); + Result resumetable = std::move(makeResumeTable( + labels, + [this](Index i) { return this->getLabelName(i); }, + [this](Index i) { return this->getLabelType(i); })); + CHECK_ERR(resumetable); std::vector operands(curr.operands.begin(), curr.operands.end()); - push( - builder.makeResume(ct, tags, labelNames, sentTypes, operands, curr.cont)); + push(builder.makeResume(ct, + tags, + resumetable->targets, + resumetable->sentTypes, + operands, + curr.cont)); return Ok{}; } @@ -2058,29 +2051,25 @@ IRBuilder::makeResumeThrow(HeapType ct, return Err{"expected continuation type"}; } ResumeThrow curr(wasm.allocator); - auto contSig = ct.getContinuation().type.getSignature(); curr.contType = ct; curr.tag = tag; curr.operands.resize(wasm.getTag(tag)->sig.params.size()); CHECK_ERR(visitResumeThrow(&curr)); - std::vector labelNames; - labelNames.reserve(labels.size()); - for (size_t i = 0; i < labels.size(); i++) { - if (labels[i].has_value()) { - auto name = getLabelName(labels[i].value()); - CHECK_ERR(name); - labelNames.push_back(*name); - } else { - labelNames.push_back(Name()); - } - } - std::vector sentTypes = - computeResumeTableSentTypes(wasm, contSig, tags, labelNames); + Result resumetable = std::move(makeResumeTable( + labels, + [this](Index i) { return this->getLabelName(i); }, + [this](Index i) { return this->getLabelType(i); })); + CHECK_ERR(resumetable); std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResumeThrow( - ct, tag, tags, labelNames, sentTypes, operands, curr.cont)); + push(builder.makeResumeThrow(ct, + tag, + tags, + resumetable->targets, + resumetable->sentTypes, + operands, + curr.cont)); return Ok{}; } From c549cd758bdf11b8ec46947b335cb9092018678b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Thu, 28 Nov 2024 12:21:11 +0100 Subject: [PATCH 21/23] [WIP] Eliminate type immediate members. This commit is a partial solution to removing the type immediate members from `cont.new,cont.bind,resume,resume_throw,switch`. However, when I fully remove the type immediates then I observe a crash in `child-ir.h` on `visitContBind,visitResume,visitResumeThrow,visitStackSwitch`. It seems that `curr->cont->type` is sometimes not a continuation type... --- src/ir/module-utils.cpp | 6 +-- src/passes/Print.cpp | 25 +++++------ src/wasm-builder.h | 6 +-- src/wasm.h | 12 ++---- src/wasm/wasm-binary.cpp | 7 ++- src/wasm/wasm-stack.cpp | 12 ++++-- src/wasm/wasm-validator.cpp | 39 +++++++++-------- src/wasm/wasm.cpp | 45 ++++++++++++++------ test/lit/basic/stack_switching_contbind.wast | 5 ++- 9 files changed, 89 insertions(+), 68 deletions(-) diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index c8425aa4be6..265fc465afd 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -465,13 +465,13 @@ struct CodeScanner } else if (auto* contNew = curr->dynCast()) { info.note(contNew->type); } else if (auto* resume = curr->dynCast()) { - info.note(resume->contType); + info.note(resume->cont->type); info.note(resume->type); } else if (auto* resumeThrow = curr->dynCast()) { - info.note(resumeThrow->contType); + info.note(resumeThrow->cont->type); info.note(resumeThrow->type); } else if (auto* switch_ = curr->dynCast()) { - info.note(switch_->contType); + info.note(switch_->cont->type); info.note(switch_->type); } else if (Properties::isControlFlowStructure(curr)) { info.noteControlFlow(Signature(Type::none, curr->type)); diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index c5983e733bc..7dc81afdc7d 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -386,29 +386,25 @@ struct PrintSExpression : public UnifiedExpressionVisitor { } } void visitContBind(ContBind* curr) { - if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->sourceType, Nullable)) && + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } } void visitResume(Resume* curr) { - if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->contType, Nullable)) && + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } } void visitResumeThrow(ResumeThrow* curr) { - if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->contType, Nullable)) && + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } } void visitStackSwitch(StackSwitch* curr) { - if (!maybePrintUnreachableOrNullReplacement( - curr, Type(curr->contType, Nullable)) && + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { visitExpression(curr); } @@ -2461,12 +2457,14 @@ struct PrintExpressionContents printMedium(o, "stringview_wtf16.slice"); } void visitContNew(ContNew* curr) { + assert(curr->type.isContinuation()); printMedium(o, "cont.new "); printHeapType(curr->type.getHeapType()); } void visitContBind(ContBind* curr) { + assert(curr->cont->type.isContinuation() && curr->type.isContinuation()); printMedium(o, "cont.bind "); - printHeapType(curr->sourceType); + printHeapType(curr->cont->type.getHeapType()); o << ' '; printHeapType(curr->type.getHeapType()); } @@ -2492,28 +2490,31 @@ struct PrintExpressionContents } } void visitResume(Resume* curr) { + assert(curr->cont->type.isContinuation()); printMedium(o, "resume"); o << ' '; - printHeapType(curr->contType); + printHeapType(curr->cont->type.getHeapType()); handleResumeTable(o, curr); } void visitResumeThrow(ResumeThrow* curr) { + assert(curr->cont->type.isContinuation()); printMedium(o, "resume_throw"); o << ' '; - printHeapType(curr->contType); + printHeapType(curr->cont->type.getHeapType()); o << ' '; curr->tag.print(o); handleResumeTable(o, curr); } void visitStackSwitch(StackSwitch* curr) { + assert(curr->cont->type.isContinuation()); printMedium(o, "switch"); o << ' '; - printHeapType(curr->contType); + printHeapType(curr->cont->type.getHeapType()); o << ' '; curr->tag.print(o); } diff --git a/src/wasm-builder.h b/src/wasm-builder.h index d06ba1c6edd..08193381253 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1207,7 +1207,7 @@ class Builder { ret->sentTypes.set(sentTypes); ret->operands.set(operands); ret->cont = cont; - ret->finalize(&wasm); + ret->finalize(); return ret; } ResumeThrow* makeResumeThrow(HeapType contType, @@ -1225,7 +1225,7 @@ class Builder { ret->sentTypes.set(sentTypes); ret->operands.set(operands); ret->cont = cont; - ret->finalize(&wasm); + ret->finalize(); return ret; } StackSwitch* makeStackSwitch(HeapType contType, @@ -1237,7 +1237,7 @@ class Builder { ret->tag = tag; ret->operands.set(operands); ret->cont = cont; - ret->finalize(&wasm); + ret->finalize(); return ret; } diff --git a/src/wasm.h b/src/wasm.h index 8afa5c39d13..d5e4f3a2e5f 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1983,10 +1983,7 @@ class Resume : public SpecificExpression { ExpressionList operands; Expression* cont; - // When 'Module*' parameter is given, we populate the 'sentTypes' array, so - // that the types can be accessed in other analyses without accessing the - // module. - void finalize(Module* wasm = nullptr); + void finalize(); // sentTypes[i] contains the type of the values that will be sent to the block // handlerBlocks[i] if suspending with tag handlerTags[i]. Not part of the @@ -2011,10 +2008,7 @@ class ResumeThrow : public SpecificExpression { ExpressionList operands; Expression* cont; - // When 'Module*' parameter is given, we populate the 'sentTypes' array, so - // that the types can be accessed in other analyses without accessing the - // module. - void finalize(Module* wasm = nullptr); + void finalize(); // sentTypes[i] contains the type of the values that will be sent to the block // handlerBlocks[i] if suspending with tag handlerTags[i]. Not part of the @@ -2035,7 +2029,7 @@ class StackSwitch : public SpecificExpression { Expression* cont; // We need access to the module to obtain the signature of the tag. - void finalize(Module* wasm = nullptr); + void finalize(); }; // Globals diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 9f93a8e628d..5f15394244d 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -8019,7 +8019,6 @@ static void readResumeTable(WasmBinaryReader* reader, ResumeType* curr) { } void WasmBinaryReader::visitResume(Resume* curr) { - curr->contType = getIndexedHeapType(); if (!curr->contType.isContinuation()) { throwError("non-continuation type in resume instruction " + @@ -8037,7 +8036,7 @@ void WasmBinaryReader::visitResume(Resume* curr) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); } - curr->finalize(&wasm); + curr->finalize(); } void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { @@ -8061,7 +8060,7 @@ void WasmBinaryReader::visitResumeThrow(ResumeThrow* curr) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); } - curr->finalize(&wasm); + curr->finalize(); } void WasmBinaryReader::visitStackSwitch(StackSwitch* curr) { @@ -8086,7 +8085,7 @@ void WasmBinaryReader::visitStackSwitch(StackSwitch* curr) { curr->operands[numArgs - i - 1] = popNonVoidExpression(); } - curr->finalize(&wasm); + curr->finalize(); } void WasmBinaryReader::throwError(std::string text) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 3ceb721d0a3..ca611975328 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2618,14 +2618,16 @@ void BinaryInstWriter::visitSuspend(Suspend* curr) { } void BinaryInstWriter::visitContBind(ContBind* curr) { + assert(curr->cont->type.isContinuation() && curr->type.isContinuation()); o << int8_t(BinaryConsts::ContBind); - parent.writeIndexedHeapType(curr->sourceType); + parent.writeIndexedHeapType(curr->cont->type.getHeapType()); parent.writeIndexedHeapType(curr->type.getHeapType()); } void BinaryInstWriter::visitResume(Resume* curr) { + assert(curr->cont->type.isContinuation()); o << int8_t(BinaryConsts::Resume); - parent.writeIndexedHeapType(curr->contType); + parent.writeIndexedHeapType(curr->cont->type.getHeapType()); size_t handlerNum = curr->handlerTags.size(); o << U32LEB(handlerNum); @@ -2644,8 +2646,9 @@ void BinaryInstWriter::visitResume(Resume* curr) { } void BinaryInstWriter::visitResumeThrow(ResumeThrow* curr) { + assert(curr->cont->type.isContinuation()); o << int8_t(BinaryConsts::ResumeThrow); - parent.writeIndexedHeapType(curr->contType); + parent.writeIndexedHeapType(curr->cont->type.getHeapType()); o << U32LEB(parent.getTagIndex(curr->tag)); size_t handlerNum = curr->handlerTags.size(); @@ -2665,8 +2668,9 @@ void BinaryInstWriter::visitResumeThrow(ResumeThrow* curr) { } void BinaryInstWriter::visitStackSwitch(StackSwitch* curr) { + assert(curr->cont->type.isContinuation()); o << int8_t(BinaryConsts::Switch); - parent.writeIndexedHeapType(curr->contType); + parent.writeIndexedHeapType(curr->cont->type.getHeapType()); o << U32LEB(parent.getTagIndex(curr->tag)); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index b34b84af3a8..dc3a9fce6d8 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3502,9 +3502,9 @@ void FunctionValidator::visitContBind(ContBind* curr) { "cont.bind requires stack-switching [--enable-stack-switching]"); shouldBeTrue( - (curr->sourceType.isContinuation() && - curr->sourceType.getContinuation().type.isSignature()) || - Type(curr->sourceType, Nullable) == Type::unreachable, + (curr->cont->type.isContinuation() && + curr->cont->type.getHeapType().getContinuation().type.isSignature()) || + curr->cont->type == Type::unreachable, curr, "the first type annotation on cont.bind must be a continuation type"); @@ -3534,11 +3534,12 @@ void FunctionValidator::visitResume(Resume* curr) { curr, "sentTypes cache in resume instruction has not been initialized"); - shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()) || - curr->type == Type::unreachable, - curr, - "resume must be annotated with a continuation type"); + shouldBeTrue( + (curr->cont->type.isContinuation() && + curr->cont->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, + curr, + "resume must be annotated with a continuation type"); } void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { @@ -3555,11 +3556,12 @@ void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { curr, "sentTypes cache in resume_throw instruction has not been initialized"); - shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()) || - curr->type == Type::unreachable, - curr, - "resume_throw must be annotated with a continuation type"); + shouldBeTrue( + (curr->cont->type.isContinuation() && + curr->cont->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, + curr, + "resume_throw must be annotated with a continuation type"); auto* tag = getModule()->getTagOrNull(curr->tag); if (!shouldBeTrue(!!tag, curr, "resume_throw must be annotated with a tag")) { @@ -3573,11 +3575,12 @@ void FunctionValidator::visitStackSwitch(StackSwitch* curr) { curr, "switch requires stack-switching [--enable-stack-switching]"); - shouldBeTrue((curr->contType.isContinuation() && - curr->contType.getContinuation().type.isSignature()) || - curr->type == Type::unreachable, - curr, - "switch must be annotated with a continuation type"); + shouldBeTrue( + (curr->cont->type.isContinuation() && + curr->cont->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, + curr, + "switch must be annotated with a continuation type"); auto* tag = getModule()->getTagOrNull(curr->tag); if (!shouldBeTrue(!!tag, curr, "switch must be annotated with a tag")) { diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 2a5050642c1..cbbfc2f369e 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1375,8 +1375,12 @@ void ContNew::finalize() { } void ContBind::finalize() { - if (handleUnreachableOperands(this)) { + if (cont->type == Type::unreachable) { type = Type::unreachable; + return; + } + if (handleUnreachableOperands(this)) { + return; } } @@ -1387,32 +1391,45 @@ void Suspend::finalize(Module* wasm) { } } -void Resume::finalize(Module* wasm) { +void Resume::finalize() { if (cont->type == Type::unreachable) { type = Type::unreachable; - } else if (!handleUnreachableOperands(this)) { - const Signature& contSig = - this->contType.getContinuation().type.getSignature(); - type = contSig.results; + return; } + if (handleUnreachableOperands(this)) { + return; + } + + const Signature& contSig = + this->cont->type.getHeapType().getContinuation().type.getSignature(); + type = contSig.results; } -void ResumeThrow::finalize(Module* wasm) { +void ResumeThrow::finalize() { if (cont->type == Type::unreachable) { type = Type::unreachable; - } else if (!handleUnreachableOperands(this)) { - const Signature& contSig = - this->contType.getContinuation().type.getSignature(); - type = contSig.results; + return; } + if (handleUnreachableOperands(this)) { + return; + } + + const Signature& contSig = + this->cont->type.getHeapType().getContinuation().type.getSignature(); + type = contSig.results; } -void StackSwitch::finalize(Module* wasm) { +void StackSwitch::finalize() { if (cont->type == Type::unreachable) { type = Type::unreachable; - } else if (!handleUnreachableOperands(this) && wasm) { - type = this->contType.getContinuation().type.getSignature().params; + return; } + if (handleUnreachableOperands(this)) { + return; + } + + type = + this->cont->type.getHeapType().getContinuation().type.getSignature().params; } size_t Function::getNumParams() { return getParams().size(); } diff --git a/test/lit/basic/stack_switching_contbind.wast b/test/lit/basic/stack_switching_contbind.wast index d559ad86359..f19cd270ec4 100644 --- a/test/lit/basic/stack_switching_contbind.wast +++ b/test/lit/basic/stack_switching_contbind.wast @@ -76,7 +76,10 @@ ) ;; CHECK-TEXT: (func $k (type $6) (result (ref $ct1)) - ;; CHECK-TEXT-NEXT: (cont.bind $ct1 $ct1 + ;; CHECK-TEXT-NEXT: (block ;; (replaces unreachable ContBind we can't emit) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (unreachable) + ;; CHECK-TEXT-NEXT: ) ;; CHECK-TEXT-NEXT: (unreachable) ;; CHECK-TEXT-NEXT: ) ;; CHECK-TEXT-NEXT: ) From 2a3fabe0987d68a22ddb82560e3147a67429c705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Mon, 2 Dec 2024 10:50:43 +0100 Subject: [PATCH 22/23] Fix lint --- src/wasm/wasm-ir-builder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index cb36be453b8..ef7cecb95ed 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2056,10 +2056,10 @@ IRBuilder::makeResume(HeapType ct, curr.operands.resize(contSig.params.size()); CHECK_ERR(visitResume(&curr)); - Result resumetable = std::move(makeResumeTable( + Result resumetable = makeResumeTable( labels, [this](Index i) { return this->getLabelName(i); }, - [this](Index i) { return this->getLabelType(i); })); + [this](Index i) { return this->getLabelType(i); }); CHECK_ERR(resumetable); std::vector operands(curr.operands.begin(), curr.operands.end()); push(builder.makeResume(ct, @@ -2088,10 +2088,10 @@ IRBuilder::makeResumeThrow(HeapType ct, curr.operands.resize(wasm.getTag(tag)->sig.params.size()); CHECK_ERR(visitResumeThrow(&curr)); - Result resumetable = std::move(makeResumeTable( + Result resumetable = makeResumeTable( labels, [this](Index i) { return this->getLabelName(i); }, - [this](Index i) { return this->getLabelType(i); })); + [this](Index i) { return this->getLabelType(i); }); CHECK_ERR(resumetable); std::vector operands(curr.operands.begin(), curr.operands.end()); From d19763c0574518d3e2523cc38c9033f38aa632c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Hillerstr=C3=B6m?= Date: Tue, 3 Dec 2024 11:26:50 +0100 Subject: [PATCH 23/23] Eliminate type immediates from stack switching instructions --- src/ir/child-typer.h | 48 +++++++++++++++------ src/ir/module-utils.cpp | 2 +- src/wasm-builder.h | 33 ++++++-------- src/wasm-delegations-fields.def | 4 -- src/wasm.h | 6 --- src/wasm/wasm-ir-builder.cpp | 76 +++++++++++++++++++++++---------- src/wasm/wasm-type-shape.cpp | 2 + src/wasm/wasm.cpp | 3 ++ 8 files changed, 106 insertions(+), 68 deletions(-) diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 4c43deaebd1..6f4ef56ef6d 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1052,18 +1052,24 @@ template struct ChildTyper : OverriddenVisitor { void visitContNew(ContNew* curr) { note(&curr->func, curr->type); } - void visitContBind(ContBind* curr) { - auto sourceParams = - curr->sourceType.getContinuation().type.getSignature().params; - auto targetParams = - curr->type.getHeapType().getContinuation().type.getSignature().params; + void visitContBind(ContBind* curr, + std::optional src = std::nullopt, + std::optional dest = std::nullopt) { + if (!src.has_value()) { + src = curr->cont->type.getHeapType(); + } + if (!dest.has_value()) { + dest = curr->type.getHeapType(); + } + auto sourceParams = src->getContinuation().type.getSignature().params; + auto targetParams = dest->getContinuation().type.getSignature().params; assert(sourceParams.size() >= targetParams.size()); auto n = sourceParams.size() - targetParams.size(); assert(curr->operands.size() == n); for (size_t i = 0; i < n; ++i) { note(&curr->operands[i], sourceParams[i]); } - note(&curr->cont, Type(curr->sourceType, Nullable)); + note(&curr->cont, Type(*src, Nullable)); } void visitSuspend(Suspend* curr) { @@ -1074,32 +1080,46 @@ template struct ChildTyper : OverriddenVisitor { } } - void visitResume(Resume* curr) { - auto params = curr->contType.getContinuation().type.getSignature().params; + void visitResume(Resume* curr, std::optional ct = std::nullopt) { + if (!ct.has_value()) { + ct = curr->cont->type.getHeapType(); + } + assert(ct->isContinuation()); + auto params = ct->getContinuation().type.getSignature().params; assert(params.size() == curr->operands.size()); for (size_t i = 0; i < params.size(); ++i) { note(&curr->operands[i], params[i]); } - note(&curr->cont, Type(curr->contType, Nullable)); + note(&curr->cont, Type(*ct, Nullable)); } - void visitResumeThrow(ResumeThrow* curr) { + void visitResumeThrow(ResumeThrow* curr, + std::optional ct = std::nullopt) { + if (!ct.has_value()) { + ct = curr->cont->type.getHeapType(); + } + assert(ct->isContinuation()); auto params = wasm.getTag(curr->tag)->sig.params; assert(params.size() == curr->operands.size()); for (size_t i = 0; i < params.size(); ++i) { note(&curr->operands[i], params[i]); } - note(&curr->cont, Type(curr->contType, Nullable)); + note(&curr->cont, Type(*ct, Nullable)); } - void visitStackSwitch(StackSwitch* curr) { - auto params = curr->contType.getContinuation().type.getSignature().params; + void visitStackSwitch(StackSwitch* curr, + std::optional ct = std::nullopt) { + if (!ct.has_value()) { + ct = curr->cont->type.getHeapType(); + } + assert(ct->isContinuation()); + auto params = ct->getContinuation().type.getSignature().params; assert(params.size() >= 1 && ((params.size() - 1) == curr->operands.size())); for (size_t i = 0; i < params.size() - 1; ++i) { note(&curr->operands[i], params[i]); } - note(&curr->cont, Type(curr->contType, Nullable)); + note(&curr->cont, Type(*ct, Nullable)); } }; diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index 31ef853a682..6e410665c66 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -460,7 +460,7 @@ struct CodeScanner } else if (auto* set = curr->dynCast()) { info.note(set->ref->type); } else if (auto* contBind = curr->dynCast()) { - info.note(contBind->sourceType); + info.note(contBind->cont->type); info.note(contBind->type); } else if (auto* contNew = curr->dynCast()) { info.note(contNew->type); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 07b04fc6292..74adec51390 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1164,14 +1164,12 @@ class Builder { ret->finalize(); return ret; } - ContBind* makeContBind(HeapType sourceType, - HeapType targetType, - const std::vector& operands, + ContBind* makeContBind(HeapType targetType, + ExpressionList&& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->sourceType = sourceType; ret->type = Type(targetType, NonNullable); - ret->operands.set(operands); + ret->operands = std::move(operands); ret->cont = cont; ret->finalize(); return ret; @@ -1183,48 +1181,41 @@ class Builder { ret->finalize(&wasm); return ret; } - Resume* makeResume(HeapType contType, - const std::vector& handlerTags, + Resume* makeResume(const std::vector& handlerTags, const std::vector& handlerBlocks, const std::vector& sentTypes, - const std::vector& operands, + ExpressionList&& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->contType = contType; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); ret->sentTypes.set(sentTypes); - ret->operands.set(operands); + ret->operands = std::move(operands); ret->cont = cont; ret->finalize(); return ret; } - ResumeThrow* makeResumeThrow(HeapType contType, - Name tag, + ResumeThrow* makeResumeThrow(Name tag, const std::vector& handlerTags, const std::vector& handlerBlocks, const std::vector& sentTypes, - const std::vector& operands, + ExpressionList&& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->contType = contType; ret->tag = tag; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); ret->sentTypes.set(sentTypes); - ret->operands.set(operands); + ret->operands = std::move(operands); ret->cont = cont; ret->finalize(); return ret; } - StackSwitch* makeStackSwitch(HeapType contType, - Name tag, - const std::vector& operands, - Expression* cont) { + StackSwitch* + makeStackSwitch(Name tag, ExpressionList&& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->contType = contType; ret->tag = tag; - ret->operands.set(operands); + ret->operands = std::move(operands); ret->cont = cont; ret->finalize(); return ret; diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index 258c3b58f43..4937fdc6194 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -772,7 +772,6 @@ DELEGATE_FIELD_CASE_END(ContNew) DELEGATE_FIELD_CASE_START(ContBind) DELEGATE_FIELD_CHILD(ContBind, cont) DELEGATE_FIELD_CHILD_VECTOR(ContBind, operands) -DELEGATE_FIELD_HEAPTYPE(ContBind, sourceType) DELEGATE_FIELD_CASE_END(ContBind) DELEGATE_FIELD_CASE_START(Suspend) @@ -786,7 +785,6 @@ DELEGATE_FIELD_CHILD(Resume, cont) DELEGATE_FIELD_CHILD_VECTOR(Resume, operands) DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(Resume, handlerBlocks) DELEGATE_FIELD_NAME_KIND_VECTOR(Resume, handlerTags, ModuleItemKind::Tag) -DELEGATE_FIELD_HEAPTYPE(Resume, contType) DELEGATE_FIELD_CASE_END(Resume) DELEGATE_FIELD_CASE_START(ResumeThrow) @@ -795,14 +793,12 @@ DELEGATE_FIELD_CHILD(ResumeThrow, cont) DELEGATE_FIELD_CHILD_VECTOR(ResumeThrow, operands) DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(ResumeThrow, handlerBlocks) DELEGATE_FIELD_NAME_KIND_VECTOR(ResumeThrow, handlerTags, ModuleItemKind::Tag) -DELEGATE_FIELD_HEAPTYPE(ResumeThrow, contType) DELEGATE_FIELD_NAME_KIND(ResumeThrow, tag, ModuleItemKind::Tag) DELEGATE_FIELD_CASE_END(ResumeThrow) DELEGATE_FIELD_CASE_START(StackSwitch) DELEGATE_FIELD_CHILD(StackSwitch, cont) DELEGATE_FIELD_CHILD_VECTOR(StackSwitch, operands) -DELEGATE_FIELD_HEAPTYPE(StackSwitch, contType) DELEGATE_FIELD_NAME_KIND(StackSwitch, tag, ModuleItemKind::Tag) DELEGATE_FIELD_CASE_END(StackSwitch) diff --git a/src/wasm.h b/src/wasm.h index adf8d0a779d..6a9f5720ec0 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1939,9 +1939,6 @@ class ContBind : public SpecificExpression { public: ContBind(MixedArena& allocator) : operands(allocator) {} - // Syntax: cont.bind $src $dst - // We store $src here, and $dst in the inherited `type` member. - HeapType sourceType; ExpressionList operands; Expression* cont; @@ -1967,7 +1964,6 @@ class Resume : public SpecificExpression { : handlerTags(allocator), handlerBlocks(allocator), operands(allocator), sentTypes(allocator) {} - HeapType contType; // The following two vectors are to be understood together // pointwise. That is, the ith component of each vector together // classifies an on-clause `(on $tag $label)` or `(on $tag @@ -1998,7 +1994,6 @@ class ResumeThrow : public SpecificExpression { : handlerTags(allocator), handlerBlocks(allocator), operands(allocator), sentTypes(allocator) {} - HeapType contType; Name tag; // See the comment on `Resume` above. ArenaVector handlerTags; @@ -2021,7 +2016,6 @@ class StackSwitch : public SpecificExpression { public: StackSwitch(MixedArena& allocator) : operands(allocator) {} - HeapType contType; Name tag; ExpressionList operands; diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index ef7cecb95ed..cc353bf0054 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -648,6 +648,35 @@ struct IRBuilder::ChildPopper ConstraintCollector{builder, children}.visitTupleExtract(curr, arity); return popConstrainedChildren(children); } + + Result<> visitContBind(ContBind* curr, + std::optional src = std::nullopt, + std::optional dest = std::nullopt) { + std::vector children; + ConstraintCollector{builder, children}.visitContBind(curr, src, dest); + return popConstrainedChildren(children); + } + + Result<> visitResume(Resume* curr, + std::optional ct = std::nullopt) { + std::vector children; + ConstraintCollector{builder, children}.visitResume(curr, ct); + return popConstrainedChildren(children); + } + + Result<> visitResumeThrow(ResumeThrow* curr, + std::optional ct = std::nullopt) { + std::vector children; + ConstraintCollector{builder, children}.visitResumeThrow(curr, ct); + return popConstrainedChildren(children); + } + + Result<> visitStackSwitch(StackSwitch* curr, + std::optional ct = std::nullopt) { + std::vector children; + ConstraintCollector{builder, children}.visitStackSwitch(curr, ct); + return popConstrainedChildren(children); + } }; Result<> IRBuilder::visit(Expression* curr) { @@ -1964,7 +1993,7 @@ Result<> IRBuilder::makeContBind(HeapType sourceType, HeapType targetType) { return Err{"expected continuation types"}; } ContBind curr(wasm.allocator); - curr.sourceType = sourceType; + curr.type = Type(targetType, NonNullable); size_t sourceParams = sourceType.getContinuation().type.getSignature().params.size(); @@ -1972,15 +2001,15 @@ Result<> IRBuilder::makeContBind(HeapType sourceType, HeapType targetType) { targetType.getContinuation().type.getSignature().params.size(); if (sourceParams < targetParams) { return Err{"incompatible continuation types in cont.bind: source type " + - sourceType.toString() + - " has fewer parameters than destination " + + sourceType.toString() + " has fewer parameters than target " + targetType.toString()}; } curr.operands.resize(sourceParams - targetParams); - CHECK_ERR(visitContBind(&curr)); + CHECK_ERR(ChildPopper{*this}.visitContBind(&curr, sourceType, targetType)); + CHECK_ERR(validateTypeAnnotation(sourceType, curr.cont)); + CHECK_ERR(validateTypeAnnotation(targetType, &curr)); - std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeContBind(sourceType, targetType, operands, curr.cont)); + push(builder.makeContBind(targetType, std::move(curr.operands), curr.cont)); return Ok{}; } @@ -2047,27 +2076,31 @@ Result<> IRBuilder::makeResume(HeapType ct, const std::vector& tags, const std::vector>& labels) { + if (tags.size() != labels.size()) { + return Err{"the sizes of tags and labels must be equal"}; + } if (!ct.isContinuation()) { return Err{"expected continuation type"}; } + Resume curr(wasm.allocator); auto contSig = ct.getContinuation().type.getSignature(); - curr.contType = ct; curr.operands.resize(contSig.params.size()); - CHECK_ERR(visitResume(&curr)); Result resumetable = makeResumeTable( labels, [this](Index i) { return this->getLabelName(i); }, [this](Index i) { return this->getLabelType(i); }); CHECK_ERR(resumetable); - std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResume(ct, - tags, + CHECK_ERR(ChildPopper{*this}.visitResume(&curr, ct)); + CHECK_ERR(validateTypeAnnotation(ct, curr.cont)); + + push(builder.makeResume(tags, resumetable->targets, resumetable->sentTypes, - operands, + std::move(curr.operands), curr.cont)); + return Ok{}; } @@ -2082,25 +2115,24 @@ IRBuilder::makeResumeThrow(HeapType ct, if (!ct.isContinuation()) { return Err{"expected continuation type"}; } + ResumeThrow curr(wasm.allocator); - curr.contType = ct; curr.tag = tag; curr.operands.resize(wasm.getTag(tag)->sig.params.size()); - CHECK_ERR(visitResumeThrow(&curr)); Result resumetable = makeResumeTable( labels, [this](Index i) { return this->getLabelName(i); }, [this](Index i) { return this->getLabelType(i); }); CHECK_ERR(resumetable); + CHECK_ERR(ChildPopper{*this}.visitResumeThrow(&curr, ct)); + CHECK_ERR(validateTypeAnnotation(ct, curr.cont)); - std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResumeThrow(ct, - tag, + push(builder.makeResumeThrow(tag, tags, resumetable->targets, resumetable->sentTypes, - operands, + std::move(curr.operands), curr.cont)); return Ok{}; } @@ -2110,7 +2142,6 @@ Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) { return Err{"expected continuation type"}; } StackSwitch curr(wasm.allocator); - curr.contType = ct; curr.tag = tag; auto nparams = ct.getContinuation().type.getSignature().params.size(); if (nparams < 1) { @@ -2122,9 +2153,10 @@ Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) { // i.e. it is provided by the runtime. curr.operands.resize(nparams - 1); - CHECK_ERR(visitStackSwitch(&curr)); - std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeStackSwitch(ct, tag, operands, curr.cont)); + CHECK_ERR(ChildPopper{*this}.visitStackSwitch(&curr, ct)); + CHECK_ERR(validateTypeAnnotation(ct, curr.cont)); + + push(builder.makeStackSwitch(tag, std::move(curr.operands), curr.cont)); return Ok{}; } diff --git a/src/wasm/wasm-type-shape.cpp b/src/wasm/wasm-type-shape.cpp index b1f74d49177..734c5e4b903 100644 --- a/src/wasm/wasm-type-shape.cpp +++ b/src/wasm/wasm-type-shape.cpp @@ -84,6 +84,7 @@ template struct RecGroupComparator { case HeapTypeKind::Array: return compare(a.getArray(), b.getArray()); case HeapTypeKind::Cont: + assert(a.isContinuation() && b.isContinuation()); return compare(a.getContinuation(), b.getContinuation()); case HeapTypeKind::Basic: break; @@ -237,6 +238,7 @@ struct RecGroupHasher { hash_combine(digest, hash(type.getArray())); return digest; case HeapTypeKind::Cont: + assert(type.isContinuation()); wasm::rehash(digest, 2381496927); hash_combine(digest, hash(type.getContinuation())); return digest; diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index b97baae2490..f1ceb7dd5b4 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1382,6 +1382,7 @@ void Resume::finalize() { return; } + assert(this->cont->type.isContinuation()); const Signature& contSig = this->cont->type.getHeapType().getContinuation().type.getSignature(); type = contSig.results; @@ -1396,6 +1397,7 @@ void ResumeThrow::finalize() { return; } + assert(this->cont->type.isContinuation()); const Signature& contSig = this->cont->type.getHeapType().getContinuation().type.getSignature(); type = contSig.results; @@ -1410,6 +1412,7 @@ void StackSwitch::finalize() { return; } + assert(this->cont->type.isContinuation()); type = this->cont->type.getHeapType().getContinuation().type.getSignature().params; }