Skip to content

Commit e2420f0

Browse files
authored
Typed continuations: cont.new instructions (#6308)
This PR is part of a series that adds basic support for the [typed continuations/wasmfx proposal](https://github.com/wasmfx/specfx). This particular PR adds support for the `cont.new` instruction for creating continuations, documented [here(https://github.com/wasmfx/specfx/blob/main/proposals/continuations/Overview.md#instructions). In short, these instructions are of the form `(cont.new $ct)` where `$ct` must be a continuation type. The instruction takes a single (nullable) function reference as its argument, which means that the folded representation of the instruction is of the form `(cont.new $ct (foo ...))`. Support for the instruction is implemented in both the old and the new wat parser. Note that this PR does not implement validation of the new instruction.
1 parent 2ceff4d commit e2420f0

30 files changed

+280
-38
lines changed

scripts/fuzz_opt.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def is_git_repo():
318318
# the fuzzer does not support typed continuations
319319
'typed_continuations.wast',
320320
'typed_continuations_resume.wast',
321+
'typed_continuations_contnew.wast',
321322
# New EH implementation is in progress
322323
'exception-handling.wast',
323324
'translate-eh-old-to-new.wast',

scripts/gen-s-parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@
567567
("call_ref", "makeCallRef(s, /*isReturn=*/false)"),
568568
("return_call_ref", "makeCallRef(s, /*isReturn=*/true)"),
569569
# Typed continuations instructions
570+
("cont.new", "makeContNew(s)"),
570571
("resume", "makeResume(s)"),
571572
# GC
572573
("i31.new", "makeRefI31(s)"), # deprecated

src/gen-s-parser.inc

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -149,21 +149,29 @@ switch (buf[0]) {
149149
}
150150
}
151151
case 'c': {
152-
switch (buf[4]) {
153-
case '\0':
154-
if (op == "call"sv) { return makeCall(s, /*isReturn=*/false); }
155-
goto parse_error;
156-
case '_': {
157-
switch (buf[5]) {
158-
case 'i':
159-
if (op == "call_indirect"sv) { return makeCallIndirect(s, /*isReturn=*/false); }
160-
goto parse_error;
161-
case 'r':
162-
if (op == "call_ref"sv) { return makeCallRef(s, /*isReturn=*/false); }
152+
switch (buf[1]) {
153+
case 'a': {
154+
switch (buf[4]) {
155+
case '\0':
156+
if (op == "call"sv) { return makeCall(s, /*isReturn=*/false); }
163157
goto parse_error;
158+
case '_': {
159+
switch (buf[5]) {
160+
case 'i':
161+
if (op == "call_indirect"sv) { return makeCallIndirect(s, /*isReturn=*/false); }
162+
goto parse_error;
163+
case 'r':
164+
if (op == "call_ref"sv) { return makeCallRef(s, /*isReturn=*/false); }
165+
goto parse_error;
166+
default: goto parse_error;
167+
}
168+
}
164169
default: goto parse_error;
165170
}
166171
}
172+
case 'o':
173+
if (op == "cont.new"sv) { return makeContNew(s); }
174+
goto parse_error;
167175
default: goto parse_error;
168176
}
169177
}
@@ -3816,30 +3824,41 @@ switch (buf[0]) {
38163824
}
38173825
}
38183826
case 'c': {
3819-
switch (buf[4]) {
3820-
case '\0':
3821-
if (op == "call"sv) {
3822-
CHECK_ERR(makeCall(ctx, pos, /*isReturn=*/false));
3823-
return Ok{};
3824-
}
3825-
goto parse_error;
3826-
case '_': {
3827-
switch (buf[5]) {
3828-
case 'i':
3829-
if (op == "call_indirect"sv) {
3830-
CHECK_ERR(makeCallIndirect(ctx, pos, /*isReturn=*/false));
3827+
switch (buf[1]) {
3828+
case 'a': {
3829+
switch (buf[4]) {
3830+
case '\0':
3831+
if (op == "call"sv) {
3832+
CHECK_ERR(makeCall(ctx, pos, /*isReturn=*/false));
38313833
return Ok{};
38323834
}
38333835
goto parse_error;
3834-
case 'r':
3835-
if (op == "call_ref"sv) {
3836-
CHECK_ERR(makeCallRef(ctx, pos, /*isReturn=*/false));
3837-
return Ok{};
3836+
case '_': {
3837+
switch (buf[5]) {
3838+
case 'i':
3839+
if (op == "call_indirect"sv) {
3840+
CHECK_ERR(makeCallIndirect(ctx, pos, /*isReturn=*/false));
3841+
return Ok{};
3842+
}
3843+
goto parse_error;
3844+
case 'r':
3845+
if (op == "call_ref"sv) {
3846+
CHECK_ERR(makeCallRef(ctx, pos, /*isReturn=*/false));
3847+
return Ok{};
3848+
}
3849+
goto parse_error;
3850+
default: goto parse_error;
38383851
}
3839-
goto parse_error;
3852+
}
38403853
default: goto parse_error;
38413854
}
38423855
}
3856+
case 'o':
3857+
if (op == "cont.new"sv) {
3858+
CHECK_ERR(makeContNew(ctx, pos));
3859+
return Ok{};
3860+
}
3861+
goto parse_error;
38433862
default: goto parse_error;
38443863
}
38453864
}

src/ir/ReFinalize.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ void ReFinalize::visitStringSliceIter(StringSliceIter* curr) {
182182
curr->finalize();
183183
}
184184

185+
void ReFinalize::visitContNew(ContNew* curr) { curr->finalize(); }
185186
void ReFinalize::visitResume(Resume* curr) { curr->finalize(); }
186187

187188
void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); }

src/ir/cost.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
726726
return 8 + visit(curr->ref) + visit(curr->num);
727727
}
728728

729+
CostType visitContNew(ContNew* curr) {
730+
// Some arbitrary "high" value, reflecting that this may allocate a stack
731+
return 14 + visit(curr->func);
732+
}
729733
CostType visitResume(Resume* curr) {
730734
// Inspired by indirect calls, but twice the cost.
731735
return 12 + visit(curr->cont);

src/ir/effects.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,10 @@ class EffectAnalyzer {
974974
parent.implicitTrap = true;
975975
}
976976

977+
void visitContNew(ContNew* curr) {
978+
// traps when curr->func is null ref.
979+
parent.implicitTrap = true;
980+
}
977981
void visitResume(Resume* curr) {
978982
// This acts as a kitchen sink effect.
979983
parent.calls = true;

src/ir/module-utils.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,8 @@ struct CodeScanner
341341
counts.include(get->type);
342342
} else if (auto* set = curr->dynCast<ArraySet>()) {
343343
counts.note(set->ref->type);
344+
} else if (auto* contNew = curr->dynCast<ContNew>()) {
345+
counts.note(contNew->contType);
344346
} else if (auto* resume = curr->dynCast<Resume>()) {
345347
counts.note(resume->contType);
346348
} else if (Properties::isControlFlowStructure(curr)) {

src/ir/possible-contents.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,10 @@ struct InfoCollector
12001200

12011201
void visitReturn(Return* curr) { addResult(curr->value); }
12021202

1203+
void visitContNew(ContNew* curr) {
1204+
// TODO: optimize when possible
1205+
addRoot(curr);
1206+
}
12031207
void visitResume(Resume* curr) {
12041208
// TODO: optimize when possible
12051209
addRoot(curr);

src/ir/subtype-exprs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
368368
void visitStringSliceWTF(StringSliceWTF* curr) {}
369369
void visitStringSliceIter(StringSliceIter* curr) {}
370370

371+
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); }
371372
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
372373
};
373374

src/parser/contexts.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,9 @@ struct NullInstrParserCtx {
570570
Result<> makeStringIterMove(Index, StringIterMoveOp) { return Ok{}; }
571571
Result<> makeStringSliceWTF(Index, StringSliceWTFOp) { return Ok{}; }
572572
Result<> makeStringSliceIter(Index) { return Ok{}; }
573+
template<typename HeapTypeT> Result<> makeContNew(Index, HeapTypeT) {
574+
return Ok{};
575+
}
573576
template<typename HeapTypeT>
574577
Result<> makeResume(Index, HeapTypeT, const TagLabelListT&) {
575578
return Ok{};
@@ -2010,6 +2013,10 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
20102013
return withLoc(pos, irBuilder.makeStringSliceIter());
20112014
}
20122015

2016+
Result<> makeContNew(Index pos, HeapType type) {
2017+
return withLoc(pos, irBuilder.makeContNew(type));
2018+
}
2019+
20132020
Result<>
20142021
makeResume(Index pos, HeapType type, const TagLabelListT& tagLabels) {
20152022
std::vector<Name> tags;

0 commit comments

Comments
 (0)