diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 29940383751..69fa247b5bb 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -353,12 +353,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 - 'typed_continuations.wast', - 'typed_continuations_resume.wast', - 'typed_continuations_contnew.wast', - 'typed_continuations_contbind.wast', - 'typed_continuations_suspend.wast', + # 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/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..1771f2e0e88 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.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.isNull() && dest == name) { 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 154da0e455c..6f4ef56ef6d 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1050,39 +1050,76 @@ template struct ChildTyper : OverriddenVisitor { note(&curr->end, Type::i32); } - 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(); + void visitContNew(ContNew* curr) { note(&curr->func, curr->type); } + + 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], paramsBefore[i]); + note(&curr->operands[i], sourceParams[i]); } - note(&curr->cont, Type(curr->contTypeBefore, Nullable)); + note(&curr->cont, Type(*src, 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) { - 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 visitSuspend(Suspend* 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(*ct, Nullable)); + } + + 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(*ct, 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..7fc203d4b91 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -1008,12 +1008,23 @@ 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. + 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; + } + + // A suspend may go unhandled and therefore trap. parent.implicitTrap = true; } void visitResume(Resume* curr) { @@ -1028,10 +1039,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/module-utils.cpp b/src/ir/module-utils.cpp index 5ebd6edefe5..6e410665c66 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -460,12 +460,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->cont->type); + 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->cont->type); + info.note(resume->type); + } else if (auto* resumeThrow = curr->dynCast()) { + info.note(resumeThrow->cont->type); + info.note(resumeThrow->type); + } else if (auto* switch_ = curr->dynCast()) { + 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/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 e6ee1816d3e..b8e868efb3d 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 807b6c0030e..fa0469c171f 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{}; } }; @@ -1387,6 +1404,9 @@ struct ParseDefsCtx : TypeParserCtx { using TagLabelListT = std::vector>; + struct OnClauseInfo; + using OnClauseListT = std::vector; + Lexer in; Module& wasm; @@ -1486,9 +1506,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}; } + + OnClauseListT makeOnClauseList() { return {}; } + void appendOnClause(std::vector& list, OnClauseInfo info) { + list.push_back(info); } Result getHeapTypeFromIdx(Index idx) { @@ -2593,37 +2624,68 @@ struct ParseDefsCtx : TypeParserCtx { return withLoc(pos, irBuilder.makeStringSliceWTF()); } - Result<> makeContBind(Index pos, - const std::vector& annotations, - HeapType contTypeBefore, - HeapType contTypeAfter) { - return withLoc(pos, irBuilder.makeContBind(contTypeBefore, contTypeAfter)); - } - 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 sourceType, + HeapType targetType) { + return withLoc(pos, irBuilder.makeContBind(sourceType, targetType)); + } + + 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> labels; + tags.reserve(resumetable.size()); + labels.reserve(resumetable.size()); + for (const OnClauseInfo& info : resumetable) { + tags.push_back(info.tag); + if (info.isOnSwitch) { + labels.push_back(std::nullopt); + } else { + labels.push_back(std::optional(info.label)); + } } return withLoc(pos, irBuilder.makeResume(type, tags, labels)); } - 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; + tags.reserve(resumetable.size()); + labels.reserve(resumetable.size()); + for (const OnClauseInfo& info : resumetable) { + tags.push_back(info.tag); + if (info.isOnSwitch) { + labels.push_back(std::nullopt); + } else { + labels.push_back(std::optional(info.label)); + } + } + return withLoc(pos, irBuilder.makeResumeThrow(type, tag, tags, labels)); + } + + 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 2d3321dcdd4..e9aa65036e0 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&) { @@ -2418,57 +2422,99 @@ 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<> 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 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 ')')* +// 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 tagLabels = ctx.makeTagLabelList(); +Result makeResumeTable(Ctx& ctx) { + 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); + if (ctx.in.takeKeyword("switch")) { + 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 resumetable; } +// resume ::= 'resume' typeidx resumetable template Result<> -makeSuspend(Ctx& ctx, Index pos, const std::vector& annotations) { +makeResume(Ctx& ctx, Index pos, const std::vector& annotations) { + auto type = typeidx(ctx); + CHECK_ERR(type); + + auto resumetable = makeResumeTable(ctx); + CHECK_ERR(resumetable); + + return ctx.makeResume(pos, annotations, *type, *resumetable); +} + +// resume_throw ::= 'resume_throw' typeidx tagidx ('(' 'on' tagidx labelidx | +// 'on' tagidx switch ')')* +template +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 = makeResumeTable(ctx); + CHECK_ERR(resumetable); + + 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 4ca40f35a30..76b7f4122dd 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -309,7 +309,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor { void visitLoop(Loop* curr); void visitTry(Try* curr); void visitTryTable(TryTable* curr); - void visitResume(Resume* curr); + bool maybePrintUnreachableReplacement(Expression* curr, Type type); bool maybePrintUnreachableOrNullReplacement(Expression* curr, Type type); void visitCallRef(CallRef* curr) { @@ -388,6 +388,35 @@ struct PrintSExpression : public UnifiedExpressionVisitor { visitExpression(curr); } } + void visitContNew(ContNew* curr) { + if (!maybePrintUnreachableReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitContBind(ContBind* curr) { + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitResume(Resume* curr) { + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitResumeThrow(ResumeThrow* curr) { + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } + void visitStackSwitch(StackSwitch* curr) { + if (!maybePrintUnreachableOrNullReplacement(curr, curr->cont->type) && + !maybePrintUnreachableOrNullReplacement(curr, curr->type)) { + visitExpression(curr); + } + } // Module-level visitors void handleSignature(Function* curr, bool printImplicitNames = false); @@ -2453,35 +2482,66 @@ struct PrintExpressionContents void visitStringSliceWTF(StringSliceWTF* curr) { 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->contTypeBefore); + printHeapType(curr->cont->type.getHeapType()); o << ' '; - printHeapType(curr->contTypeAfter); + printHeapType(curr->type.getHeapType()); } - 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"); - - 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 << ' '; - curr->handlerBlocks[i].print(o); + if (curr->handlerBlocks[i].isNull()) { + o << "switch"; + } else { + curr->handlerBlocks[i].print(o); + } o << ')'; } } + void visitResume(Resume* curr) { + assert(curr->cont->type.isContinuation()); + printMedium(o, "resume"); - void visitSuspend(Suspend* curr) { - printMedium(o, "suspend "); + o << ' '; + printHeapType(curr->cont->type.getHeapType()); + + handleResumeTable(o, curr); + } + void visitResumeThrow(ResumeThrow* curr) { + assert(curr->cont->type.isContinuation()); + printMedium(o, "resume_throw"); + + o << ' '; + 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->cont->type.getHeapType()); + o << ' '; curr->tag.print(o); } }; @@ -2775,7 +2835,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 // ... @@ -2849,23 +2909,6 @@ void PrintSExpression::visitTryTable(TryTable* curr) { controlFlowDepth--; } -void PrintSExpression::visitResume(Resume* curr) { - controlFlowDepth++; - o << '('; - printExpressionContents(curr); - - incIndent(); - - for (Index i = 0; i < curr->operands.size(); i++) { - printFullLine(curr->operands[i]); - } - - printFullLine(curr->cont); - - controlFlowDepth--; - decIndent(); -} - bool PrintSExpression::maybePrintUnreachableReplacement(Expression* curr, Type type) { // When we cannot print an instruction because the child from which it's 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 b9cbba136df..a6af70236ce 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -392,7 +392,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; @@ -1141,12 +1141,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 { diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 20485f14d10..74adec51390 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1157,44 +1157,67 @@ class Builder { ret->finalize(); return ret; } - ContBind* makeContBind(HeapType contTypeBefore, - HeapType contTypeAfter, - const std::vector& operands, + ContNew* makeContNew(HeapType type, Expression* func) { + auto* ret = wasm.allocator.alloc(); + ret->type = Type(type, NonNullable); + ret->func = func; + ret->finalize(); + return ret; + } + ContBind* makeContBind(HeapType targetType, + ExpressionList&& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->contTypeBefore = contTypeBefore; - ret->contTypeAfter = contTypeAfter; - ret->operands.set(operands); + ret->type = Type(targetType, NonNullable); + ret->operands = std::move(operands); ret->cont = cont; 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, + Resume* makeResume(const std::vector& handlerTags, const std::vector& handlerBlocks, - const std::vector& operands, + const std::vector& sentTypes, + ExpressionList&& operands, Expression* cont) { auto* ret = wasm.allocator.alloc(); - ret->contType = contType; ret->handlerTags.set(handlerTags); ret->handlerBlocks.set(handlerBlocks); - ret->operands.set(operands); + ret->sentTypes.set(sentTypes); + ret->operands = std::move(operands); ret->cont = cont; - ret->finalize(&wasm); + ret->finalize(); return ret; } - Suspend* makeSuspend(Name tag, const std::vector& args) { - auto* ret = wasm.allocator.alloc(); + ResumeThrow* makeResumeThrow(Name tag, + const std::vector& handlerTags, + const std::vector& handlerBlocks, + const std::vector& sentTypes, + ExpressionList&& operands, + Expression* cont) { + auto* ret = wasm.allocator.alloc(); ret->tag = tag; - ret->operands.set(args); - ret->finalize(&wasm); + ret->handlerTags.set(handlerTags); + ret->handlerBlocks.set(handlerBlocks); + ret->sentTypes.set(sentTypes); + ret->operands = std::move(operands); + ret->cont = cont; + ret->finalize(); + return ret; + } + StackSwitch* + makeStackSwitch(Name tag, ExpressionList&& operands, Expression* cont) { + auto* ret = wasm.allocator.alloc(); + ret->tag = tag; + 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 3be04022094..4937fdc6194 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -765,17 +765,19 @@ 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_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_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) @@ -783,13 +785,23 @@ 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(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_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_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_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 81531e27c13..22cfe98581f 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(); } @@ -4255,10 +4261,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 84ac2697930..603f404bf77 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -226,12 +226,17 @@ class IRBuilder : public UnifiedExpressionVisitor> { Result<> makeStringWTF16Get(); Result<> makeStringIterNext(); Result<> makeStringSliceWTF(); - Result<> makeContBind(HeapType contTypeBefore, HeapType contTypeAfter); Result<> makeContNew(HeapType ct); + Result<> makeContBind(HeapType sourceType, HeapType targetType); + Result<> makeSuspend(Name tag); Result<> makeResume(HeapType ct, const std::vector& tags, - const std::vector& labels); - Result<> makeSuspend(Name tag); + const std::vector>& labels); + Result<> makeResumeThrow(HeapType ct, + Name tag, + const std::vector& tags, + const std::vector>& labels); + Result<> makeStackSwitch(HeapType ct, Name tag); // Private functions that must be public for technical reasons. Result<> visitExpression(Expression*); 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 a5fc070e611..6a9f5720ec0 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -736,10 +736,13 @@ class Expression { StringEqId, StringWTF16GetId, StringSliceWTFId, - ContBindId, ContNewId, - ResumeId, + ContBindId, SuspendId, + ResumeId, + ResumeThrowId, + // Id for the stack switching `switch` + StackSwitchId, NumExpressionIds }; Id _id; @@ -1922,27 +1925,37 @@ class StringSliceWTF : public SpecificExpression { void finalize(); }; +class ContNew : public SpecificExpression { +public: + ContNew() = default; + ContNew(MixedArena& allocator) {} + + Expression* func; + + void finalize(); +}; + class ContBind : public SpecificExpression { public: ContBind(MixedArena& allocator) : operands(allocator) {} - HeapType contTypeBefore; - HeapType contTypeAfter; ExpressionList operands; Expression* cont; 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 { @@ -1951,17 +1964,21 @@ 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 + // switch)`. The first vector stores reifies the `$tag` bit of the + // aforementioned syntax... ArenaVector handlerTags; + // ... 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; 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 @@ -1971,17 +1988,41 @@ 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), operands(allocator), + sentTypes(allocator) {} Name tag; + // See the comment on `Resume` above. + ArenaVector handlerTags; + ArenaVector handlerBlocks; + 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. - 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 + // 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) {} + + Name tag; + + ExpressionList operands; + Expression* cont; + + // We need access to the module to obtain the signature of the tag. + void finalize(); }; // Globals diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 82ac422eaa4..def52e27433 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1348,8 +1348,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: @@ -3009,26 +3009,63 @@ Result<> WasmBinaryReader::readInst() { case BinaryConsts::RetCallRef: return builder.makeCallRef(getIndexedHeapType(), code == BinaryConsts::RetCallRef); + case BinaryConsts::ContNew: + return builder.makeContNew(getIndexedHeapType()); case BinaryConsts::ContBind: { auto before = getIndexedHeapType(); auto after = getIndexedHeapType(); return builder.makeContBind(before, after); } - case BinaryConsts::ContNew: - return builder.makeContNew(getIndexedHeapType()); + case BinaryConsts::Suspend: + return builder.makeSuspend(getTagName(getU32LEB())); case BinaryConsts::Resume: { auto type = getIndexedHeapType(); - std::vector tags; - std::vector labels; auto numHandlers = getU32LEB(); + std::vector tags; + std::vector> labels; + tags.reserve(numHandlers); + labels.reserve(numHandlers); for (Index i = 0; i < numHandlers; ++i) { - tags.push_back(getTagName(getU32LEB())); - labels.push_back(getU32LEB()); + uint8_t code = getInt8(); + if (code == BinaryConsts::OnLabel) { + tags.push_back(getTagName(getU32LEB())); + labels.push_back(std::optional{getU32LEB()}); + } else if (code == BinaryConsts::OnSwitch) { + tags.push_back(getTagName(getU32LEB())); + labels.push_back(std::nullopt); + } else { + return Err{"ON opcode expected"}; + } } return builder.makeResume(type, tags, labels); } - case BinaryConsts::Suspend: - return builder.makeSuspend(getTagName(getU32LEB())); + case BinaryConsts::ResumeThrow: { + auto type = getIndexedHeapType(); + auto tag = getTagName(getU32LEB()); + auto numHandlers = getU32LEB(); + std::vector tags; + std::vector> labels; + tags.reserve(numHandlers); + labels.reserve(numHandlers); + for (Index i = 0; i < numHandlers; ++i) { + uint8_t code = getInt8(); + if (code == BinaryConsts::OnLabel) { + tags.push_back(getTagName(getU32LEB())); + labels.push_back(std::optional{getU32LEB()}); + } else if (code == BinaryConsts::OnSwitch) { + tags.push_back(getTagName(getU32LEB())); + labels.push_back(std::nullopt); + } else { + return Err{"ON opcode expected"}; + } + } + return builder.makeResumeThrow(type, tag, tags, labels); + } + case BinaryConsts::Switch: { + auto type = getIndexedHeapType(); + auto tag = getTagName(getU32LEB()); + return builder.makeStackSwitch(type, tag); + } #define BINARY_INT(code) \ case BinaryConsts::I32##code: \ @@ -5064,9 +5101,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) { diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index fcb1fc48d9a..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) { @@ -1947,76 +1976,187 @@ Result<> IRBuilder::makeStringSliceWTF() { return Ok{}; } -Result<> IRBuilder::makeContBind(HeapType contTypeBefore, - HeapType contTypeAfter) { - if (!contTypeBefore.isContinuation() || !contTypeAfter.isContinuation()) { +Result<> IRBuilder::makeContNew(HeapType type) { + if (!type.isContinuation()) { + return Err{"expected continuation type"}; + } + ContNew curr; + curr.type = Type(type, NonNullable); + CHECK_ERR(visitContNew(&curr)); + + push(builder.makeContNew(type, curr.func)); + return Ok{}; +} + +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.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() + - " has fewer parameters than destination " + - contTypeAfter.toString()}; + sourceType.toString() + " has fewer parameters than target " + + targetType.toString()}; } - curr.operands.resize(paramsBefore - paramsAfter); - CHECK_ERR(visitContBind(&curr)); + curr.operands.resize(sourceParams - targetParams); + CHECK_ERR(ChildPopper{*this}.visitContBind(&curr, sourceType, targetType)); + CHECK_ERR(validateTypeAnnotation(sourceType, curr.cont)); + CHECK_ERR(validateTypeAnnotation(targetType, &curr)); + + push(builder.makeContBind(targetType, std::move(curr.operands), curr.cont)); + return Ok{}; +} + +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)); std::vector operands(curr.operands.begin(), curr.operands.end()); - push( - builder.makeContBind(contTypeBefore, contTypeAfter, operands, curr.cont)); + push(builder.makeSuspend(tag, operands)); return Ok{}; } -Result<> IRBuilder::makeContNew(HeapType ct) { +struct ResumeTable { + std::vector targets; + std::vector sentTypes; +}; + +static Result +makeResumeTable(const std::vector>& labels, + std::function(Index)> getLabelName, + std::function(Index)> getLabelType) { + std::vector targets; + targets.reserve(labels.size()); + + std::vector sentTypes; + sentTypes.reserve(sentTypes.size()); + + 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 { + return Err{"expected continuation type"}; + } + } else { + // (on $tag switch) clause + target = Name(); + sentType = Type::none; + } + targets.push_back(target); + sentTypes.push_back(sentType); + } + return ResumeTable{std::move(targets), std::move(sentTypes)}; +} + +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"}; } - ContNew curr; - curr.contType = ct; - CHECK_ERR(visitContNew(&curr)); - push(builder.makeContNew(ct, curr.func)); + Resume curr(wasm.allocator); + auto contSig = ct.getContinuation().type.getSignature(); + curr.operands.resize(contSig.params.size()); + + 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}.visitResume(&curr, ct)); + CHECK_ERR(validateTypeAnnotation(ct, curr.cont)); + + push(builder.makeResume(tags, + resumetable->targets, + resumetable->sentTypes, + std::move(curr.operands), + curr.cont)); + return Ok{}; } -Result<> IRBuilder::makeResume(HeapType ct, - const std::vector& tags, - const std::vector& labels) { +Result<> +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"}; } - Resume curr(wasm.allocator); - curr.contType = ct; - curr.operands.resize(ct.getContinuation().type.getSignature().params.size()); - 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); - } - std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeResume(ct, tags, labelNames, operands, curr.cont)); + ResumeThrow curr(wasm.allocator); + curr.tag = tag; + curr.operands.resize(wasm.getTag(tag)->sig.params.size()); + + 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)); + + push(builder.makeResumeThrow(tag, + tags, + resumetable->targets, + resumetable->sentTypes, + std::move(curr.operands), + curr.cont)); return Ok{}; } -Result<> IRBuilder::makeSuspend(Name tag) { - Suspend curr(wasm.allocator); +Result<> IRBuilder::makeStackSwitch(HeapType ct, Name tag) { + if (!ct.isContinuation()) { + return Err{"expected continuation type"}; + } + StackSwitch curr(wasm.allocator); curr.tag = tag; - curr.operands.resize(wasm.getTag(tag)->sig.params.size()); - CHECK_ERR(visitSuspend(&curr)); + auto nparams = ct.getContinuation().type.getSignature().params.size(); + if (nparams < 1) { + return Err{"arity mismatch: the continuation argument must have, at least, " + "unary arity"}; + } - std::vector operands(curr.operands.begin(), curr.operands.end()); - push(builder.makeSuspend(tag, operands)); + // The continuation argument of the continuation is synthetic, + // i.e. it is provided by the runtime. + curr.operands.resize(nparams - 1); + + 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-stack.cpp b/src/wasm/wasm-stack.cpp index f86fb58b948..e7d6805486a 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2608,31 +2608,70 @@ void BinaryInstWriter::visitStringSliceWTF(StringSliceWTF* curr) { << U32LEB(BinaryConsts::StringViewWTF16Slice); } -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); - parent.writeIndexedHeapType(curr->contType); + parent.writeIndexedHeapType(curr->type.getHeapType()); +} + +void BinaryInstWriter::visitSuspend(Suspend* curr) { + o << int8_t(BinaryConsts::Suspend) << U32LEB(parent.getTagIndex(curr->tag)); +} + +void BinaryInstWriter::visitContBind(ContBind* curr) { + assert(curr->cont->type.isContinuation() && curr->type.isContinuation()); + o << int8_t(BinaryConsts::ContBind); + 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); for (size_t i = 0; i < handlerNum; i++) { - o << U32LEB(parent.getTagIndex(curr->handlerTags[i])) - << U32LEB(getBreakIndex(curr->handlerBlocks[i])); + if (curr->handlerBlocks[i].isNull()) { + // 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::visitResumeThrow(ResumeThrow* curr) { + assert(curr->cont->type.isContinuation()); + o << int8_t(BinaryConsts::ResumeThrow); + parent.writeIndexedHeapType(curr->cont->type.getHeapType()); + o << U32LEB(parent.getTagIndex(curr->tag)); + + size_t handlerNum = curr->handlerTags.size(); + o << U32LEB(handlerNum); + for (size_t i = 0; i < handlerNum; i++) { + if (curr->handlerBlocks[i].isNull()) { + // 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::visitStackSwitch(StackSwitch* curr) { + assert(curr->cont->type.isContinuation()); + o << int8_t(BinaryConsts::Switch); + parent.writeIndexedHeapType(curr->cont->type.getHeapType()); + o << U32LEB(parent.getTagIndex(curr->tag)); } void BinaryInstWriter::emitScopeEnd(Expression* curr) { 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-type.cpp b/src/wasm/wasm-type.cpp index 9bb4f425907..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(); } @@ -1421,7 +1425,7 @@ FeatureSet HeapType::getFeatures() const { return; case HeapType::cont: case HeapType::nocont: - feats |= FeatureSet::TypedContinuations; + feats |= FeatureSet::StackSwitching; return; } } @@ -1447,7 +1451,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 e295d393179..d4cfd0051db 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); @@ -3475,61 +3477,111 @@ void FunctionValidator::visitStringSliceWTF(StringSliceWTF* curr) { "string operations require reference-types [--enable-strings]"); } -void FunctionValidator::visitContBind(ContBind* curr) { +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( - !getModule() || getModule()->features.hasTypedContinuations(), + (curr->type.isContinuation() && + curr->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, curr, - "cont.bind requires typed-continuatons [--enable-typed-continuations]"); + "cont.new must be annotated with a continuation type"); +} - shouldBeTrue((curr->contTypeBefore.isContinuation() && - curr->contTypeBefore.getContinuation().type.isSignature()), +void FunctionValidator::visitContBind(ContBind* curr) { + // TODO implement actual type-checking + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), curr, - "invalid first type in ContBind expression"); + "cont.bind requires stack-switching [--enable-stack-switching]"); - shouldBeTrue((curr->contTypeAfter.isContinuation() && - curr->contTypeAfter.getContinuation().type.isSignature()), + shouldBeTrue( + (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"); + + shouldBeTrue( + (curr->type.isContinuation() && + curr->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, + curr, + "the second type annotation on cont.bind must be a continuation type"); +} + +void FunctionValidator::visitSuspend(Suspend* curr) { + // TODO implement actual type-checking + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), curr, - "invalid second type in ContBind expression"); + "suspend requires stack-switching [--enable-stack-switching]"); } -void FunctionValidator::visitContNew(ContNew* curr) { +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"); + 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::visitResume(Resume* curr) { +void FunctionValidator::visitResumeThrow(ResumeThrow* curr) { // TODO implement actual type-checking shouldBeTrue( - !getModule() || getModule()->features.hasTypedContinuations(), + !getModule() || (getModule()->features.hasExceptionHandling() && + getModule()->features.hasStackSwitching()), curr, - "resume requires typed-continuatons [--enable-typed-continuations]"); + "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 Resume 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, - "invalid type in Resume expression"); + 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")) { + return; + } } -void FunctionValidator::visitSuspend(Suspend* curr) { +void FunctionValidator::visitStackSwitch(StackSwitch* curr) { // TODO implement actual type-checking + shouldBeTrue(!getModule() || getModule()->features.hasStackSwitching(), + curr, + "switch requires stack-switching [--enable-stack-switching]"); + shouldBeTrue( - !getModule() || getModule()->features.hasTypedContinuations(), + (curr->cont->type.isContinuation() && + curr->cont->type.getHeapType().getContinuation().type.isSignature()) || + curr->type == Type::unreachable, curr, - "suspend requires typed-continuations [--enable-typed-continuations]"); + "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) { @@ -4033,10 +4085,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 38f35411f52..f1ceb7dd5b4 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 @@ -1350,80 +1350,71 @@ void StringSliceWTF::finalize() { } } +void ContNew::finalize() { + if (func->type == Type::unreachable) { + type = Type::unreachable; + } +} + void ContBind::finalize() { if (cont->type == Type::unreachable) { type = Type::unreachable; - } else if (!handleUnreachableOperands(this)) { - type = Type(contTypeAfter, NonNullable); + return; + } + if (handleUnreachableOperands(this)) { + return; } } -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; } } -static void populateResumeSentTypes(Resume* curr, Module* wasm) { - if (!wasm) { +void Resume::finalize() { + if (cont->type == Type::unreachable) { + type = Type::unreachable; + return; + } + if (handleUnreachableOperands(this)) { return; } + assert(this->cont->type.isContinuation()); 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; - } - } + this->cont->type.getHeapType().getContinuation().type.getSignature(); + type = contSig.results; } -void Resume::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; } - populateResumeSentTypes(this, wasm); + assert(this->cont->type.isContinuation()); + const Signature& contSig = + this->cont->type.getHeapType().getContinuation().type.getSignature(); + type = contSig.results; } -void Suspend::finalize(Module* wasm) { - if (!handleUnreachableOperands(this) && wasm) { - auto tag = wasm->getTag(this->tag); - type = tag->sig.results; +void StackSwitch::finalize() { + if (cont->type == Type::unreachable) { + type = Type::unreachable; + return; + } + if (handleUnreachableOperands(this)) { + return; } + + assert(this->cont->type.isContinuation()); + type = + this->cont->type.getHeapType().getContinuation().type.getSignature().params; } size_t Function::getNumParams() { return getParams().size(); } diff --git a/src/wasm2js.h b/src/wasm2js.h index 7ed089448e2..4cfd11ab5ab 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2408,11 +2408,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"); } @@ -2420,7 +2424,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/stack_switching.wast b/test/lit/basic/stack_switching.wast new file mode 100644 index 00000000000..8fa87d11d89 --- /dev/null +++ b/test/lit/basic/stack_switching.wast @@ -0,0 +1,967 @@ +;; 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 $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 $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: (export "non-linear-1" (func $8)) + + ;; 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 $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: (export "unhandled-2" (func $6)) + + ;; 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 $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 $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 + (param $w contref) + (param $x nullcontref) + (param $y (ref cont)) + (param $z (ref nocont)) + (result contref) + (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: ) + ;; CHECK-BIN: (func $fglo (type $f1) + ;; 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 $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e2 $block) + ;; 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 $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block) + ;; 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 $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block) + ;; 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 $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block) + ;; 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: ) + ;; CHECK-BIN: (func $14 (type $f1) + ;; CHECK-BIN-NEXT: (resume_throw $k1 $exn + ;; CHECK-BIN-NEXT: (block $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block) + ;; 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: ) + (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 $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block) + ;; 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 $block (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block) + ;; 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 $block1 (result (ref $k1)) + ;; CHECK-BIN-NEXT: (resume $k1 (on $e1 $block1) + ;; 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-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: (export "uncaught-1" (func $10)) + +;; CHECK-BIN-NODEBUG: (export "uncaught-2" (func $11)) + +;; CHECK-BIN-NODEBUG: (export "uncaught-3" (func $14)) + +;; 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 $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: ) + +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$2 $block) +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block) +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block) +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block) +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block) +;; 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: ) + +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block) +;; 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 $block (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block) +;; 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 $block1 (result (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (resume $1 (on $tag$1 $block1) +;; 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/typed_continuations_contbind.wast b/test/lit/basic/stack_switching_contbind.wast similarity index 81% rename from test/lit/basic/typed_continuations_contbind.wast rename to test/lit/basic/stack_switching_contbind.wast index 065d4a7962f..f19cd270ec4 100644 --- a/test/lit/basic/typed_continuations_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,23 @@ (local.get $x) ) ) + + ;; CHECK-TEXT: (func $k (type $6) (result (ref $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: ) + ;; 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 +104,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 +119,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/typed_continuations_contnew.wast b/test/lit/basic/stack_switching_contnew.wast similarity index 56% rename from test/lit/basic/typed_continuations_contnew.wast rename to test/lit/basic/stack_switching_contnew.wast index c0539cb9a73..5404dfb6db5 100644 --- a/test/lit/basic/typed_continuations_contnew.wast +++ b/test/lit/basic/stack_switching_contnew.wast @@ -19,15 +19,33 @@ ;; 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 $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: (type $3 (func (result (ref cont)))) + ;; 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 +58,9 @@ ;; CHECK-BIN-NODEBUG: (type $2 (func (result (ref $1)))) - ;; CHECK-BIN-NODEBUG: (elem declare func $0) + ;; CHECK-BIN-NODEBUG: (type $3 (func (result (ref cont)))) + + ;; CHECK-BIN-NODEBUG: (elem declare func $1) (elem declare func $g) ;; CHECK-TEXT: (func $h (type $2) (result (ref $ct)) @@ -57,13 +77,38 @@ (cont.new $ct (ref.func $g)) ) + (func (result (ref cont)) + (cont.new $ct (ref.null nofunc))) + ) -;; CHECK-BIN-NODEBUG: (func $0 (type $0) (param $0 i32) (result i32) +;; 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: ) + +;; 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 $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.func $0) +;; CHECK-BIN-NODEBUG-NEXT: (ref.null nofunc) ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: ) 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..e0bd8bf485f --- /dev/null +++ b/test/lit/basic/stack_switching_resume_throw.wast @@ -0,0 +1,126 @@ +;; 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 $scratch (tuple i32 (ref $ct))) + ;; CHECK-BIN-NEXT: (local $scratch_2 i32) + ;; CHECK-BIN-NEXT: (local.set $scratch_2 + ;; CHECK-BIN-NEXT: (tuple.extract 2 0 + ;; CHECK-BIN-NEXT: (local.tee $scratch + ;; CHECK-BIN-NEXT: (block $block (type $2) (result i32 (ref $ct)) + ;; CHECK-BIN-NEXT: (return + ;; CHECK-BIN-NEXT: (resume_throw $ct $e (on $t $block) + ;; 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: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (drop + ;; CHECK-BIN-NEXT: (tuple.extract 2 1 + ;; CHECK-BIN-NEXT: (local.get $scratch) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.get $scratch_2) + ;; 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 $scratch (tuple i32 (ref $1))) +;; CHECK-BIN-NODEBUG-NEXT: (local $scratch_2 i32) +;; CHECK-BIN-NODEBUG-NEXT: (local.set $scratch_2 +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 0 +;; CHECK-BIN-NODEBUG-NEXT: (local.tee $scratch +;; CHECK-BIN-NODEBUG-NEXT: (block $block (type $2) (result i32 (ref $1)) +;; CHECK-BIN-NODEBUG-NEXT: (return +;; CHECK-BIN-NODEBUG-NEXT: (resume_throw $1 $tag$1 (on $tag$0 $block) +;; 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: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (drop +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 1 +;; CHECK-BIN-NODEBUG-NEXT: (local.get $scratch) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $scratch_2) +;; 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/basic/stack_switching_switch.wast b/test/lit/basic/stack_switching_switch.wast new file mode 100644 index 00000000000..0911d7f20d5 --- /dev/null +++ b/test/lit/basic/stack_switching_switch.wast @@ -0,0 +1,183 @@ +;; 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 $scratch_4 (tuple i32 (ref null $ct))) + ;; CHECK-BIN-NEXT: (local $scratch_5 i32) + ;; CHECK-BIN-NEXT: (local.set $scratch_2 + ;; CHECK-BIN-NEXT: (local.tee $scratch + ;; CHECK-BIN-NEXT: (block (result i32) + ;; CHECK-BIN-NEXT: (local.set $scratch_5 + ;; CHECK-BIN-NEXT: (tuple.extract 2 0 + ;; CHECK-BIN-NEXT: (local.tee $scratch_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: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.set $3 + ;; CHECK-BIN-NEXT: (tuple.extract 2 1 + ;; CHECK-BIN-NEXT: (local.get $scratch_4) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (local.get $scratch_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 $scratch (tuple i32 (ref null $1))) +;; CHECK-BIN-NODEBUG-NEXT: (local $scratch_5 i32) +;; 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 $scratch_5 +;; CHECK-BIN-NODEBUG-NEXT: (tuple.extract 2 0 +;; CHECK-BIN-NODEBUG-NEXT: (local.tee $scratch +;; 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: ) +;; 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 $scratch) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (local.get $scratch_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: ) diff --git a/test/lit/basic/typed_continuations.wast b/test/lit/basic/typed_continuations.wast deleted file mode 100644 index 1e024ca0b3e..00000000000 --- a/test/lit/basic/typed_continuations.wast +++ /dev/null @@ -1,68 +0,0 @@ -;; 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 (param (ref $ct)) (result (ref $ct)))) - - ;; CHECK-TEXT: (type $3 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) - - ;; CHECK-TEXT: (func $id (type $2) (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 $3 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) - - ;; CHECK-BIN: (func $id (type $2) (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-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-NEXT: (local.get $z) - ;; CHECK-BIN-NEXT: ) - (func $id2 - (param $w contref) - (param $x nullcontref) - (param $y (ref cont)) - (param $z (ref nocont)) - (result contref) - (local.get $z) - ) - -) -;; CHECK-BIN-NODEBUG: (type $0 (func (param i32) (result i32))) - -;; CHECK-BIN-NODEBUG: (type $1 (cont $0)) - -;; CHECK-BIN-NODEBUG: (type $2 (func (param (ref $1)) (result (ref $1)))) - -;; CHECK-BIN-NODEBUG: (type $3 (func (param contref nullcontref (ref cont) (ref nocont)) (result contref))) - -;; CHECK-BIN-NODEBUG: (func $0 (type $2) (param $0 (ref $1)) (result (ref $1)) -;; 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-NEXT: (local.get $3) -;; CHECK-BIN-NODEBUG-NEXT: ) 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 e44e51a167b..c414fd7e1cb 100644 --- a/test/lit/help/wasm-metadce.test +++ b/test/lit/help/wasm-metadce.test @@ -743,9 +743,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 5c978ee81e7..4f6c7344a73 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -752,9 +752,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 c1dd0401d9e..8cee8833e38 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -706,9 +706,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())