Skip to content

Commit 88108ef

Browse files
authored
Typed continuations: cont.bind instructions (#6365)
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.bind` instruction for partially applying continuations, documented [here](https://github.com/wasmfx/specfx/blob/main/proposals/continuations/Overview.md#instructions). In short, these instructions are of the form `(cont.bind $ct_before $ct_after)` where `$ct_before` and `$ct_after` are related continuation types. They must only differ in the number of arguments, where `$ct_before` has _n_ additional parameters as compared to `$ct_after`, for some _n_ ≥ 0. The idea is that `(cont.bind $ct_before $ct_after)` then takes a reference to a continuation of type `$ct_before` as well as _n_ operands and returns a (reference to a) continuation of type `$ct_after`. Thus, the folded textual representation looks like `(cont.bind $ct_before $ct_after arg1 ... argn c)`. 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 2ca9638 commit 88108ef

30 files changed

+511
-132
lines changed

scripts/fuzz_opt.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def is_git_repo():
327327
'typed_continuations.wast',
328328
'typed_continuations_resume.wast',
329329
'typed_continuations_contnew.wast',
330+
'typed_continuations_contbind.wast',
330331
# New EH implementation is in progress
331332
'exception-handling.wast',
332333
'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
@@ -568,6 +568,7 @@
568568
("return_call_ref", "makeCallRef(s, /*isReturn=*/true)"),
569569
# Typed continuations instructions
570570
("cont.new", "makeContNew(s)"),
571+
("cont.bind", "makeContBind(s)"),
571572
("resume", "makeResume(s)"),
572573
# GC
573574
("i31.new", "makeRefI31(s)"), # deprecated

src/gen-s-parser.inc

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,17 @@ switch (buf[0]) {
169169
default: goto parse_error;
170170
}
171171
}
172-
case 'o':
173-
if (op == "cont.new"sv) { return makeContNew(s); }
174-
goto parse_error;
172+
case 'o': {
173+
switch (buf[5]) {
174+
case 'b':
175+
if (op == "cont.bind"sv) { return makeContBind(s); }
176+
goto parse_error;
177+
case 'n':
178+
if (op == "cont.new"sv) { return makeContNew(s); }
179+
goto parse_error;
180+
default: goto parse_error;
181+
}
182+
}
175183
default: goto parse_error;
176184
}
177185
}
@@ -3853,12 +3861,23 @@ switch (buf[0]) {
38533861
default: goto parse_error;
38543862
}
38553863
}
3856-
case 'o':
3857-
if (op == "cont.new"sv) {
3858-
CHECK_ERR(makeContNew(ctx, pos, annotations));
3859-
return Ok{};
3864+
case 'o': {
3865+
switch (buf[5]) {
3866+
case 'b':
3867+
if (op == "cont.bind"sv) {
3868+
CHECK_ERR(makeContBind(ctx, pos, annotations));
3869+
return Ok{};
3870+
}
3871+
goto parse_error;
3872+
case 'n':
3873+
if (op == "cont.new"sv) {
3874+
CHECK_ERR(makeContNew(ctx, pos, annotations));
3875+
return Ok{};
3876+
}
3877+
goto parse_error;
3878+
default: goto parse_error;
38603879
}
3861-
goto parse_error;
3880+
}
38623881
default: goto parse_error;
38633882
}
38643883
}

src/ir/ReFinalize.cpp

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

185185
void ReFinalize::visitContNew(ContNew* curr) { curr->finalize(); }
186+
void ReFinalize::visitContBind(ContBind* curr) { curr->finalize(); }
186187
void ReFinalize::visitResume(Resume* curr) { curr->finalize(); }
187188

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

src/ir/cost.h

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

729+
CostType visitContBind(ContBind* curr) {
730+
// Inspired by struct.new: The only cost of cont.bind is that it may need to
731+
// allocate a buffer to hold the arguments.
732+
CostType ret = 4;
733+
ret += visit(curr->cont);
734+
for (auto* arg : curr->operands) {
735+
ret += visit(arg);
736+
}
737+
return ret;
738+
}
729739
CostType visitContNew(ContNew* curr) {
730740
// Some arbitrary "high" value, reflecting that this may allocate a stack
731741
return 14 + visit(curr->func);

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 visitContBind(ContBind* curr) {
978+
// traps when curr->cont is null ref.
979+
parent.implicitTrap = true;
980+
}
977981
void visitContNew(ContNew* curr) {
978982
// traps when curr->func is null ref.
979983
parent.implicitTrap = true;

src/ir/module-utils.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ 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* contBind = curr->dynCast<ContBind>()) {
345+
counts.note(contBind->contTypeBefore);
346+
counts.note(contBind->contTypeAfter);
344347
} else if (auto* contNew = curr->dynCast<ContNew>()) {
345348
counts.note(contNew->contType);
346349
} else if (auto* resume = curr->dynCast<Resume>()) {

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 visitContBind(ContBind* curr) {
1204+
// TODO: optimize when possible
1205+
addRoot(curr);
1206+
}
12031207
void visitContNew(ContNew* 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
@@ -391,6 +391,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
391391
void visitStringSliceWTF(StringSliceWTF* curr) {}
392392
void visitStringSliceIter(StringSliceIter* curr) {}
393393

394+
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
394395
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); }
395396
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
396397
};

src/parser/contexts.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,11 @@ struct NullInstrParserCtx {
808808
return Ok{};
809809
}
810810
template<typename HeapTypeT>
811+
Result<>
812+
makeContBind(Index, const std::vector<Annotation>&, HeapTypeT, HeapTypeT) {
813+
return Ok{};
814+
}
815+
template<typename HeapTypeT>
811816
Result<> makeContNew(Index, const std::vector<Annotation>&, HeapTypeT) {
812817
return Ok{};
813818
}
@@ -2547,6 +2552,13 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
25472552
return withLoc(pos, irBuilder.makeStringSliceIter());
25482553
}
25492554

2555+
Result<> makeContBind(Index pos,
2556+
const std::vector<Annotation>& annotations,
2557+
HeapType contTypeBefore,
2558+
HeapType contTypeAfter) {
2559+
return withLoc(pos, irBuilder.makeContBind(contTypeBefore, contTypeAfter));
2560+
}
2561+
25502562
Result<> makeContNew(Index pos,
25512563
const std::vector<Annotation>& annotations,
25522564
HeapType type) {

0 commit comments

Comments
 (0)