Skip to content

Commit 1be4ea5

Browse files
committed
Stack switching proposal support
This patch implements text and binary encoding/decoding support for the stack switching proposal. It does so by adapting the previous typed-continunations implementation. Particular changes: * Support for new `resume` encoding. * Added support for `resume_throw` and `switch`. * Feature flag `typed-continuations` has been renamed to `stack-switching`. A small unfortunate implementation detail is that the internal name `Switch` was already taken by the `br_table` instruction, so I opted to give the `switch` instruction the internal name `StackSwitch`. A minor detail is that I have reordered the declarations/definitions of the stack switching instructions such that they appear in ascending order according to their opcode value (this is the same order that the stack-switching explainer document present them in).
1 parent 39bf87e commit 1be4ea5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1086
-245
lines changed

scripts/gen-s-parser.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,11 +596,13 @@
596596
# Typed function references instructions
597597
("call_ref", "makeCallRef(/*isReturn=*/false)"),
598598
("return_call_ref", "makeCallRef(/*isReturn=*/true)"),
599-
# Typed continuations instructions
599+
# Stack switching instructions
600600
("cont.new", "makeContNew()"),
601601
("cont.bind", "makeContBind()"),
602-
("resume", "makeResume()"),
603602
("suspend", "makeSuspend()"),
603+
("resume", "makeResume()"),
604+
("resume_throw", "makeResumeThrow()"),
605+
("switch", "makeStackSwitch()"),
604606
# GC
605607
("ref.i31", "makeRefI31(Unshared)"),
606608
("ref.i31_shared", "makeRefI31(Shared)"),

src/gen-s-parser.inc

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4807,12 +4807,23 @@ switch (buf[0]) {
48074807
default: goto parse_error;
48084808
}
48094809
}
4810-
case 's':
4811-
if (op == "resume"sv) {
4812-
CHECK_ERR(makeResume(ctx, pos, annotations));
4813-
return Ok{};
4810+
case 's': {
4811+
switch (buf[6]) {
4812+
case '\0':
4813+
if (op == "resume"sv) {
4814+
CHECK_ERR(makeResume(ctx, pos, annotations));
4815+
return Ok{};
4816+
}
4817+
goto parse_error;
4818+
case '_':
4819+
if (op == "resume_throw"sv) {
4820+
CHECK_ERR(makeResumeThrow(ctx, pos, annotations));
4821+
return Ok{};
4822+
}
4823+
goto parse_error;
4824+
default: goto parse_error;
48144825
}
4815-
goto parse_error;
4826+
}
48164827
case 't': {
48174828
switch (buf[3]) {
48184829
case 'h':
@@ -5076,6 +5087,12 @@ switch (buf[0]) {
50765087
return Ok{};
50775088
}
50785089
goto parse_error;
5090+
case 'w':
5091+
if (op == "switch"sv) {
5092+
CHECK_ERR(makeStackSwitch(ctx, pos, annotations));
5093+
return Ok{};
5094+
}
5095+
goto parse_error;
50795096
default: goto parse_error;
50805097
}
50815098
}

src/ir/ReFinalize.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ void ReFinalize::visitStringWTF16Get(StringWTF16Get* curr) { curr->finalize(); }
180180
void ReFinalize::visitStringSliceWTF(StringSliceWTF* curr) { curr->finalize(); }
181181
void ReFinalize::visitContNew(ContNew* curr) { curr->finalize(); }
182182
void ReFinalize::visitContBind(ContBind* curr) { curr->finalize(); }
183-
void ReFinalize::visitResume(Resume* curr) { curr->finalize(); }
184183
void ReFinalize::visitSuspend(Suspend* curr) { curr->finalize(getModule()); }
184+
void ReFinalize::visitResume(Resume* curr) { curr->finalize(); }
185+
void ReFinalize::visitResumeThrow(ResumeThrow* curr) { curr->finalize(); }
186+
void ReFinalize::visitStackSwitch(StackSwitch* curr) { curr->finalize(); }
185187

186188
void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); }
187189
void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); }

src/ir/branch-utils.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
8989
func(name, r->sentTypes[i]);
9090
}
9191
}
92+
} else if (auto* r = expr->dynCast<ResumeThrow>()) {
93+
for (Index i = 0; i < r->handlerTags.size(); i++) {
94+
auto dest = r->handlerTags[i];
95+
if (dest == name) {
96+
func(name, r->sentTypes[i]);
97+
}
98+
}
9299
} else {
93100
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
94101
}
@@ -118,6 +125,8 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
118125
// The values are supplied by suspend instructions executed while running
119126
// the continuation, so we are unable to know what they will be here.
120127
func(name, nullptr);
128+
} else if (expr->is<ResumeThrow>()) {
129+
func(name, nullptr);
121130
} else {
122131
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
123132
}

src/ir/child-typer.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,10 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
10501050
note(&curr->end, Type::i32);
10511051
}
10521052

1053+
void visitContNew(ContNew* curr) {
1054+
note(&curr->func, Type(curr->contType.getContinuation().type, Nullable));
1055+
}
1056+
10531057
void visitContBind(ContBind* curr) {
10541058
auto paramsBefore =
10551059
curr->contTypeBefore.getContinuation().type.getSignature().params;
@@ -1064,8 +1068,12 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
10641068
note(&curr->cont, Type(curr->contTypeBefore, Nullable));
10651069
}
10661070

1067-
void visitContNew(ContNew* curr) {
1068-
note(&curr->func, Type(curr->contType.getContinuation().type, Nullable));
1071+
void visitSuspend(Suspend* curr) {
1072+
auto params = wasm.getTag(curr->tag)->sig.params;
1073+
assert(params.size() == curr->operands.size());
1074+
for (size_t i = 0; i < params.size(); ++i) {
1075+
note(&curr->operands[i], params[i]);
1076+
}
10691077
}
10701078

10711079
void visitResume(Resume* curr) {
@@ -1077,12 +1085,23 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
10771085
note(&curr->cont, Type(curr->contType, Nullable));
10781086
}
10791087

1080-
void visitSuspend(Suspend* curr) {
1088+
void visitResumeThrow(ResumeThrow* curr) {
10811089
auto params = wasm.getTag(curr->tag)->sig.params;
10821090
assert(params.size() == curr->operands.size());
10831091
for (size_t i = 0; i < params.size(); ++i) {
10841092
note(&curr->operands[i], params[i]);
10851093
}
1094+
note(&curr->cont, Type(curr->contType, Nullable));
1095+
}
1096+
1097+
void visitStackSwitch(StackSwitch* curr) {
1098+
auto params = curr->contType.getContinuation().type.getSignature().params;
1099+
assert(params.size() >= 1 &&
1100+
((params.size() - 1) == curr->operands.size()));
1101+
for (size_t i = 0; i < params.size() - 1; ++i) {
1102+
note(&curr->operands[i], params[i]);
1103+
}
1104+
note(&curr->cont, Type(curr->contType, Nullable));
10861105
}
10871106
};
10881107

src/ir/cost.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
756756
return 8 + visit(curr->ref) + visit(curr->start) + visit(curr->end);
757757
}
758758

759+
CostType visitContNew(ContNew* curr) {
760+
// Some arbitrary "high" value, reflecting that this may allocate a stack
761+
return 14 + visit(curr->func);
762+
}
759763
CostType visitContBind(ContBind* curr) {
760764
// Inspired by struct.new: The only cost of cont.bind is that it may need to
761765
// allocate a buffer to hold the arguments.
@@ -766,9 +770,12 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
766770
}
767771
return ret;
768772
}
769-
CostType visitContNew(ContNew* curr) {
770-
// Some arbitrary "high" value, reflecting that this may allocate a stack
771-
return 14 + visit(curr->func);
773+
CostType visitSuspend(Suspend* curr) {
774+
CostType ret = 12;
775+
for (auto* arg : curr->operands) {
776+
ret += visit(arg);
777+
}
778+
return ret;
772779
}
773780
CostType visitResume(Resume* curr) {
774781
// Inspired by indirect calls, but twice the cost.
@@ -778,8 +785,17 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
778785
}
779786
return ret;
780787
}
781-
CostType visitSuspend(Suspend* curr) {
782-
CostType ret = 12;
788+
CostType visitResumeThrow(ResumeThrow* curr) {
789+
// Inspired by indirect calls, but twice the cost.
790+
CostType ret = 12 + visit(curr->cont);
791+
for (auto* arg : curr->operands) {
792+
ret += visit(arg);
793+
}
794+
return ret;
795+
}
796+
CostType visitStackSwitch(StackSwitch* curr) {
797+
// Inspired by indirect calls, but twice the cost.
798+
CostType ret = 12 + visit(curr->cont);
783799
for (auto* arg : curr->operands) {
784800
ret += visit(arg);
785801
}

src/ir/effects.h

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,13 +1008,21 @@ class EffectAnalyzer {
10081008
// traps when ref is null.
10091009
parent.implicitTrap = true;
10101010
}
1011+
void visitContNew(ContNew* curr) {
1012+
// traps when curr->func is null ref.
1013+
parent.implicitTrap = true;
1014+
}
10111015
void visitContBind(ContBind* curr) {
10121016
// traps when curr->cont is null ref.
10131017
parent.implicitTrap = true;
10141018
}
1015-
void visitContNew(ContNew* curr) {
1016-
// traps when curr->func is null ref.
1017-
parent.implicitTrap = true;
1019+
void visitSuspend(Suspend* curr) {
1020+
// Similar to resume/call: Suspending means that we execute arbitrary
1021+
// other code before we may resume here.
1022+
parent.calls = true;
1023+
if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
1024+
parent.throws_ = true;
1025+
}
10181026
}
10191027
void visitResume(Resume* curr) {
10201028
// This acts as a kitchen sink effect.
@@ -1028,10 +1036,26 @@ class EffectAnalyzer {
10281036
parent.throws_ = true;
10291037
}
10301038
}
1031-
void visitSuspend(Suspend* curr) {
1032-
// Similar to resume/call: Suspending means that we execute arbitrary
1033-
// other code before we may resume here.
1039+
void visitResumeThrow(ResumeThrow* curr) {
1040+
// This acts as a kitchen sink effect.
10341041
parent.calls = true;
1042+
1043+
// resume_throw instructions accept nullable continuation
1044+
// references and trap on null.
1045+
parent.implicitTrap = true;
1046+
1047+
if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
1048+
parent.throws_ = true;
1049+
}
1050+
}
1051+
void visitStackSwitch(StackSwitch* curr) {
1052+
// This acts as a kitchen sink effect.
1053+
parent.calls = true;
1054+
1055+
// switch instructions accept nullable continuation references
1056+
// and trap on null.
1057+
parent.implicitTrap = true;
1058+
10351059
if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
10361060
parent.throws_ = true;
10371061
}

src/ir/possible-contents.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,19 +1225,27 @@ struct InfoCollector
12251225

12261226
void visitReturn(Return* curr) { addResult(curr->value); }
12271227

1228+
void visitContNew(ContNew* curr) {
1229+
// TODO: optimize when possible
1230+
addRoot(curr);
1231+
}
12281232
void visitContBind(ContBind* curr) {
12291233
// TODO: optimize when possible
12301234
addRoot(curr);
12311235
}
1232-
void visitContNew(ContNew* curr) {
1236+
void visitSuspend(Suspend* curr) {
12331237
// TODO: optimize when possible
12341238
addRoot(curr);
12351239
}
12361240
void visitResume(Resume* curr) {
12371241
// TODO: optimize when possible
12381242
addRoot(curr);
12391243
}
1240-
void visitSuspend(Suspend* curr) {
1244+
void visitResumeThrow(ResumeThrow* curr) {
1245+
// TODO: optimize when possible
1246+
addRoot(curr);
1247+
}
1248+
void visitStackSwitch(StackSwitch* curr) {
12411249
// TODO: optimize when possible
12421250
addRoot(curr);
12431251
}

src/ir/subtype-exprs.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,10 +397,16 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
397397
void visitStringWTF16Get(StringWTF16Get* curr) {}
398398
void visitStringSliceWTF(StringSliceWTF* curr) {}
399399

400-
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
401400
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); }
402-
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
401+
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
403402
void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("not implemented"); }
403+
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
404+
void visitResumeThrow(ResumeThrow* curr) {
405+
WASM_UNREACHABLE("not implemented");
406+
}
407+
void visitStackSwitch(StackSwitch* curr) {
408+
WASM_UNREACHABLE("not implemented");
409+
}
404410
};
405411

406412
} // namespace wasm

0 commit comments

Comments
 (0)