Skip to content

Commit fcdd100

Browse files
frank-emrichashleynh
authored andcommitted
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 fe41961 commit fcdd100

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;

src/parser/parsers.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ Result<> makeStringIterMove(Ctx&, Index, StringIterMoveOp op);
167167
template<typename Ctx>
168168
Result<> makeStringSliceWTF(Ctx&, Index, StringSliceWTFOp op);
169169
template<typename Ctx> Result<> makeStringSliceIter(Ctx&, Index);
170+
template<typename Ctx> Result<> makeContNew(Ctx&, Index);
170171
template<typename Ctx> Result<> makeResume(Ctx&, Index);
171172

172173
// Modules
@@ -1990,6 +1991,13 @@ template<typename Ctx> Result<> makeStringSliceIter(Ctx& ctx, Index pos) {
19901991
return ctx.makeStringSliceIter(pos);
19911992
}
19921993

1994+
template<typename Ctx> Result<> makeContNew(Ctx& ctx, Index pos) {
1995+
auto type = typeidx(ctx);
1996+
CHECK_ERR(type);
1997+
1998+
return ctx.makeContNew(pos, *type);
1999+
}
2000+
19932001
// resume ::= 'resume' typeidx ('(' 'tag' tagidx labelidx ')')*
19942002
template<typename Ctx> Result<> makeResume(Ctx& ctx, Index pos) {
19952003
auto type = typeidx(ctx);

src/passes/Print.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,6 +2379,11 @@ struct PrintExpressionContents
23792379
printMedium(o, "stringview_iter.slice");
23802380
}
23812381

2382+
void visitContNew(ContNew* curr) {
2383+
printMedium(o, "cont.new ");
2384+
printHeapType(curr->contType);
2385+
}
2386+
23822387
void visitResume(Resume* curr) {
23832388
printMedium(o, "resume");
23842389

src/passes/TypeGeneralizing.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,7 @@ struct TransferFn : OverriddenVisitor<TransferFn> {
875875
void visitStringSliceWTF(StringSliceWTF* curr) { WASM_UNREACHABLE("TODO"); }
876876
void visitStringSliceIter(StringSliceIter* curr) { WASM_UNREACHABLE("TODO"); }
877877

878+
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("TODO"); }
878879
void visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); }
879880
};
880881

src/wasm-binary.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ enum ASTNodes {
12981298
StringNewUTF8ArrayTry = 0xb8,
12991299

13001300
// typed continuation opcodes
1301+
ContNew = 0xe0,
13011302
Resume = 0xe3,
13021303

13031304
};
@@ -1926,6 +1927,7 @@ class WasmBinaryReader {
19261927
void visitCallRef(CallRef* curr);
19271928
void visitRefAsCast(RefCast* curr, uint32_t code);
19281929
void visitRefAs(RefAs* curr, uint8_t code);
1930+
void visitContNew(ContNew* curr);
19291931
void visitResume(Resume* curr);
19301932

19311933
[[noreturn]] void throwError(std::string text);

src/wasm-builder.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,13 @@ class Builder {
11981198
return ret;
11991199
}
12001200

1201+
ContNew* makeContNew(HeapType contType, Expression* func) {
1202+
auto* ret = wasm.allocator.alloc<ContNew>();
1203+
ret->contType = contType;
1204+
ret->func = func;
1205+
ret->finalize();
1206+
return ret;
1207+
}
12011208
Resume* makeResume(HeapType contType,
12021209
const std::vector<Name>& handlerTags,
12031210
const std::vector<Name>& handlerBlocks,

src/wasm-delegations-fields.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,13 @@ switch (DELEGATE_ID) {
946946
break;
947947
}
948948

949+
case Expression::Id::ContNewId: {
950+
DELEGATE_START(ContNew);
951+
DELEGATE_FIELD_CHILD(ContNew, func);
952+
DELEGATE_FIELD_HEAPTYPE(ContNew, contType);
953+
DELEGATE_END(ContNew);
954+
break;
955+
}
949956
case Expression::Id::ResumeId: {
950957
DELEGATE_START(Resume);
951958
DELEGATE_FIELD_TYPE_VECTOR(Resume, sentTypes);

src/wasm-delegations.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ DELEGATE(StringIterNext);
105105
DELEGATE(StringIterMove);
106106
DELEGATE(StringSliceWTF);
107107
DELEGATE(StringSliceIter);
108+
DELEGATE(ContNew);
108109
DELEGATE(Resume);
109110

110111
#undef DELEGATE

src/wasm-interpreter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,6 +2401,7 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
24012401
}
24022402
return ExpressionRunner<SubType>::visitRefAs(curr);
24032403
}
2404+
Flow visitContNew(ContNew* curr) { WASM_UNREACHABLE("unimplemented"); }
24042405
Flow visitResume(Resume* curr) { WASM_UNREACHABLE("unimplemented"); }
24052406

24062407
void trap(const char* why) override { throw NonconstantException(); }
@@ -3968,6 +3969,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
39683969
multiValues.pop_back();
39693970
return ret;
39703971
}
3972+
Flow visitContNew(ContNew* curr) { return Flow(NONCONSTANT_FLOW); }
39713973
Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); }
39723974

39733975
void trap(const char* why) override { externalInterface->trap(why); }

src/wasm-ir-builder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
206206
[[nodiscard]] Result<> makeStringIterMove(StringIterMoveOp op);
207207
[[nodiscard]] Result<> makeStringSliceWTF(StringSliceWTFOp op);
208208
[[nodiscard]] Result<> makeStringSliceIter();
209+
[[nodiscard]] Result<> makeContNew(HeapType ct);
209210
[[nodiscard]] Result<> makeResume(HeapType ct,
210211
const std::vector<Name>& tags,
211212
const std::vector<Index>& labels);

src/wasm-s-parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ class SExpressionWasmBuilder {
328328
Expression* makeStringIterMove(Element& s, StringIterMoveOp op);
329329
Expression* makeStringSliceWTF(Element& s, StringSliceWTFOp op);
330330
Expression* makeStringSliceIter(Element& s);
331+
Expression* makeContNew(Element& s);
331332
Expression* makeResume(Element& s);
332333

333334
// Helper functions

src/wasm.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ class Expression {
743743
StringIterMoveId,
744744
StringSliceWTFId,
745745
StringSliceIterId,
746+
ContNewId,
746747
ResumeId,
747748
NumExpressionIds
748749
};
@@ -1997,6 +1998,17 @@ class StringSliceIter
19971998
void finalize();
19981999
};
19992000

2001+
class ContNew : public SpecificExpression<Expression::ContNewId> {
2002+
public:
2003+
ContNew() = default;
2004+
ContNew(MixedArena& allocator) {}
2005+
2006+
HeapType contType;
2007+
Expression* func;
2008+
2009+
void finalize();
2010+
};
2011+
20002012
class Resume : public SpecificExpression<Expression::ResumeId> {
20012013
public:
20022014
Resume(MixedArena& allocator)

src/wasm/wasm-binary.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4046,6 +4046,12 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) {
40464046
visitCallRef(call);
40474047
break;
40484048
}
4049+
case BinaryConsts::ContNew: {
4050+
auto contNew = allocator.alloc<ContNew>();
4051+
curr = contNew;
4052+
visitContNew(contNew);
4053+
break;
4054+
}
40494055
case BinaryConsts::Resume: {
40504056
visitResume((curr = allocator.alloc<Resume>())->cast<Resume>());
40514057
break;
@@ -7762,6 +7768,20 @@ void WasmBinaryReader::visitRefAs(RefAs* curr, uint8_t code) {
77627768
curr->finalize();
77637769
}
77647770

7771+
void WasmBinaryReader::visitContNew(ContNew* curr) {
7772+
BYN_TRACE("zz node: ContNew\n");
7773+
7774+
auto contTypeIndex = getU32LEB();
7775+
curr->contType = getTypeByIndex(contTypeIndex);
7776+
if (!curr->contType.isContinuation()) {
7777+
throwError("non-continuation type in cont.new instruction " +
7778+
curr->contType.toString());
7779+
}
7780+
7781+
curr->func = popNonVoidExpression();
7782+
curr->finalize();
7783+
}
7784+
77657785
void WasmBinaryReader::visitResume(Resume* curr) {
77667786
BYN_TRACE("zz node: Resume\n");
77677787

src/wasm/wasm-ir-builder.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,6 +1783,17 @@ Result<> IRBuilder::makeStringSliceIter() {
17831783
return Ok{};
17841784
}
17851785

1786+
Result<> IRBuilder::makeContNew(HeapType ct) {
1787+
if (!ct.isContinuation()) {
1788+
return Err{"expected continuation type"};
1789+
}
1790+
ContNew curr;
1791+
CHECK_ERR(visitContNew(&curr));
1792+
1793+
push(builder.makeContNew(ct, curr.func));
1794+
return Ok{};
1795+
}
1796+
17861797
Result<> IRBuilder::makeResume(HeapType ct,
17871798
const std::vector<Name>& tags,
17881799
const std::vector<Index>& labels) {

0 commit comments

Comments
 (0)