Skip to content

Commit 38d3277

Browse files
authored
[StackSwitching] Add resume_throw_ref (#8140)
Implement it as a variation on the existing ResumeThrow class: if the tag exists, it is a resume_throw with that tag; if the tag is null, it is resume_throw_ref. Remove an assert in visitTryTable in the interpreter: the code actually works, and no TODO was necessary there: resuming "just works", like with CallRef etc. (visitTry does have a problem, resuming from a try's catch block - but try_table is fine.) This allows the test to pass.
1 parent bc55110 commit 38d3277

File tree

15 files changed

+492
-51
lines changed

15 files changed

+492
-51
lines changed

scripts/gen-s-parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@
609609
("suspend", "makeSuspend()"),
610610
("resume", "makeResume()"),
611611
("resume_throw", "makeResumeThrow()"),
612+
("resume_throw_ref", "makeResumeThrowRef()"),
612613
("switch", "makeStackSwitch()"),
613614
# GC
614615
("ref.i31", "makeRefI31(Unshared)"),

src/gen-s-parser.inc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4972,12 +4972,23 @@ switch (buf[0]) {
49724972
return Ok{};
49734973
}
49744974
goto parse_error;
4975-
case '_':
4976-
if (op == "resume_throw"sv) {
4977-
CHECK_ERR(makeResumeThrow(ctx, pos, annotations));
4978-
return Ok{};
4975+
case '_': {
4976+
switch (buf[12]) {
4977+
case '\0':
4978+
if (op == "resume_throw"sv) {
4979+
CHECK_ERR(makeResumeThrow(ctx, pos, annotations));
4980+
return Ok{};
4981+
}
4982+
goto parse_error;
4983+
case '_':
4984+
if (op == "resume_throw_ref"sv) {
4985+
CHECK_ERR(makeResumeThrowRef(ctx, pos, annotations));
4986+
return Ok{};
4987+
}
4988+
goto parse_error;
4989+
default: goto parse_error;
49794990
}
4980-
goto parse_error;
4991+
}
49814992
default: goto parse_error;
49824993
}
49834994
}

src/ir/child-typer.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,10 +1325,16 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
13251325
ct = curr->cont->type.getHeapType();
13261326
}
13271327
assert(ct->isContinuation());
1328-
auto params = wasm.getTag(curr->tag)->params();
1329-
assert(params.size() == curr->operands.size());
1330-
for (size_t i = 0; i < params.size(); ++i) {
1331-
note(&curr->operands[i], params[i]);
1328+
if (curr->tag) {
1329+
// resume_throw
1330+
auto params = wasm.getTag(curr->tag)->params();
1331+
assert(params.size() == curr->operands.size());
1332+
for (size_t i = 0; i < params.size(); ++i) {
1333+
note(&curr->operands[i], params[i]);
1334+
}
1335+
} else {
1336+
// resume_throw_ref
1337+
note(&curr->operands[0], Type(HeapType::exn, Nullable));
13321338
}
13331339
note(&curr->cont, Type(*ct, Nullable));
13341340
}

src/parser/contexts.h

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,13 @@ struct NullInstrParserCtx {
935935
return Ok{};
936936
}
937937
template<typename HeapTypeT>
938+
Result<> makeResumeThrowRef(Index,
939+
const std::vector<Annotation>&,
940+
HeapTypeT,
941+
const TagLabelListT&) {
942+
return Ok{};
943+
}
944+
template<typename HeapTypeT>
938945
Result<>
939946
makeStackSwitch(Index, const std::vector<Annotation>&, HeapTypeT, TagIdxT) {
940947
return Ok{};
@@ -2889,24 +2896,41 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
28892896
return withLoc(pos, irBuilder.makeResume(type, tags, labels));
28902897
}
28912898

2899+
struct ResumeThrowData {
2900+
std::vector<Name> tags;
2901+
std::vector<std::optional<Index>> labels;
2902+
2903+
ResumeThrowData(const std::vector<OnClauseInfo>& resumetable) {
2904+
tags.reserve(resumetable.size());
2905+
labels.reserve(resumetable.size());
2906+
for (const OnClauseInfo& info : resumetable) {
2907+
tags.push_back(info.tag);
2908+
if (info.isOnSwitch) {
2909+
labels.push_back(std::nullopt);
2910+
} else {
2911+
labels.push_back(std::optional<Index>(info.label));
2912+
}
2913+
}
2914+
}
2915+
};
2916+
28922917
Result<> makeResumeThrow(Index pos,
28932918
const std::vector<Annotation>& annotations,
28942919
HeapType type,
28952920
Name tag,
28962921
const std::vector<OnClauseInfo>& resumetable) {
2897-
std::vector<Name> tags;
2898-
std::vector<std::optional<Index>> labels;
2899-
tags.reserve(resumetable.size());
2900-
labels.reserve(resumetable.size());
2901-
for (const OnClauseInfo& info : resumetable) {
2902-
tags.push_back(info.tag);
2903-
if (info.isOnSwitch) {
2904-
labels.push_back(std::nullopt);
2905-
} else {
2906-
labels.push_back(std::optional<Index>(info.label));
2907-
}
2908-
}
2909-
return withLoc(pos, irBuilder.makeResumeThrow(type, tag, tags, labels));
2922+
ResumeThrowData data(resumetable);
2923+
return withLoc(
2924+
pos, irBuilder.makeResumeThrow(type, tag, data.tags, data.labels));
2925+
}
2926+
2927+
Result<> makeResumeThrowRef(Index pos,
2928+
const std::vector<Annotation>& annotations,
2929+
HeapType type,
2930+
const std::vector<OnClauseInfo>& resumetable) {
2931+
ResumeThrowData data(resumetable);
2932+
return withLoc(pos,
2933+
irBuilder.makeResumeThrowRef(type, data.tags, data.labels));
29102934
}
29112935

29122936
Result<> makeStackSwitch(Index pos,

src/parser/parsers.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,21 @@ Result<> makeResumeThrow(Ctx& ctx,
27432743
return ctx.makeResumeThrow(pos, annotations, *type, *exnTag, *resumetable);
27442744
}
27452745

2746+
// resume_throw_ref ::= 'resume_throw' typeidx ('(' 'on' tagidx labelidx |
2747+
// 'on' tagidx switch ')')*
2748+
template<typename Ctx>
2749+
Result<> makeResumeThrowRef(Ctx& ctx,
2750+
Index pos,
2751+
const std::vector<Annotation>& annotations) {
2752+
auto type = typeidx(ctx);
2753+
CHECK_ERR(type);
2754+
2755+
auto resumetable = makeResumeTable(ctx);
2756+
CHECK_ERR(resumetable);
2757+
2758+
return ctx.makeResumeThrowRef(pos, annotations, *type, *resumetable);
2759+
}
2760+
27462761
// switch ::= 'switch' typeidx tagidx
27472762
template<typename Ctx>
27482763
Result<> makeStackSwitch(Ctx& ctx,

src/passes/Print.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2623,11 +2623,15 @@ struct PrintExpressionContents
26232623
void visitResumeThrow(ResumeThrow* curr) {
26242624
assert(curr->cont->type.isContinuation());
26252625
printMedium(o, "resume_throw");
2626-
2626+
if (!curr->tag) {
2627+
printMedium(o, "_ref");
2628+
}
26272629
o << ' ';
26282630
printHeapTypeName(curr->cont->type.getHeapType());
2629-
o << ' ';
2630-
curr->tag.print(o);
2631+
if (curr->tag) {
2632+
o << ' ';
2633+
curr->tag.print(o);
2634+
}
26312635

26322636
handleResumeTable(o, curr);
26332637
}

src/wasm-binary.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,8 @@ enum ASTNodes {
12461246
Suspend = 0xe2,
12471247
Resume = 0xe3,
12481248
ResumeThrow = 0xe4,
1249-
Switch = 0xe5, // NOTE(dhil): the internal class is known as
1249+
ResumeThrowRef = 0xe5,
1250+
Switch = 0xe6, // NOTE(dhil): the internal class is known as
12501251
// StackSwitch to avoid conflict with the existing
12511252
// 'switch table'.
12521253
OnLabel = 0x00, // (on $tag $label)

src/wasm-interpreter.h

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,14 @@ struct ContData {
273273
// suspend).
274274
Literals resumeArguments;
275275

276-
// If set, this is the exception to be thrown at the resume point.
276+
// If set, this is the tag for an exception to be thrown at the resume point
277+
// (from resume_throw).
277278
Tag* exceptionTag = nullptr;
278279

280+
// If set, this is the exception ref to be thrown at the resume point (from
281+
// resume_throw_ref).
282+
Literal exception;
283+
279284
// Whether we executed. Continuations are one-shot, so they may not be
280285
// executed a second time.
281286
bool executed = false;
@@ -4480,7 +4485,6 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
44804485
}
44814486
}
44824487
Flow visitTryTable(TryTable* curr) {
4483-
assert(!self()->isResuming()); // TODO
44844488
try {
44854489
return self()->visit(curr->body);
44864490
} catch (const WasmException& e) {
@@ -4557,6 +4561,22 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
45574561
old->executed = true;
45584562
return Literal(std::make_shared<ContData>(newData));
45594563
}
4564+
4565+
void maybeThrowAfterResuming(std::shared_ptr<ContData>& currContinuation) {
4566+
// We may throw by creating a tag, or an exnref.
4567+
auto* tag = currContinuation->exceptionTag;
4568+
auto exnref = currContinuation->exception.type != Type::none;
4569+
assert(!(tag && exnref));
4570+
if (tag) {
4571+
// resume_throw
4572+
throwException(WasmException{
4573+
self()->makeExnData(tag, currContinuation->resumeArguments)});
4574+
} else if (exnref) {
4575+
// resume_throw_ref
4576+
throwException(WasmException{currContinuation->exception});
4577+
}
4578+
}
4579+
45604580
Flow visitSuspend(Suspend* curr) {
45614581
// Process the arguments, whether or not we are resuming. If we are resuming
45624582
// then we don't need these values (we sent them as part of the suspension),
@@ -4579,11 +4599,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
45794599
// restoredValues map.
45804600
assert(currContinuation->resumeInfo.empty());
45814601
assert(self()->restoredValuesMap.empty());
4582-
// Throw, if we were resumed by resume_throw;
4583-
if (auto* tag = currContinuation->exceptionTag) {
4584-
throwException(WasmException{
4585-
self()->makeExnData(tag, currContinuation->resumeArguments)});
4586-
}
4602+
maybeThrowAfterResuming(currContinuation);
45874603
return currContinuation->resumeArguments;
45884604
}
45894605

@@ -4616,7 +4632,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
46164632
new_->resumeExpr = curr;
46174633
return Flow(SUSPEND_FLOW, tag, std::move(arguments));
46184634
}
4619-
template<typename T> Flow doResume(T* curr, Tag* exceptionTag = nullptr) {
4635+
template<typename T> Flow doResume(T* curr) {
46204636
Literals arguments;
46214637
VISIT_ARGUMENTS(flow, curr->operands, arguments)
46224638
VISIT_REUSE(flow, curr->cont);
@@ -4636,13 +4652,26 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
46364652
trap("continuation already executed");
46374653
}
46384654
contData->executed = true;
4655+
46394656
if (contData->resumeArguments.empty()) {
46404657
// The continuation has no bound arguments. For now, we just handle the
46414658
// simple case of binding all of them, so that means we can just use all
46424659
// the immediate ones here. TODO
46434660
contData->resumeArguments = arguments;
46444661
}
4645-
contData->exceptionTag = exceptionTag;
4662+
// Fill in the continuation data. How we do this depends on whether we
4663+
// are resume or resume_throw*.
4664+
if (auto* resumeThrow = curr->template dynCast<ResumeThrow>()) {
4665+
if (resumeThrow->tag) {
4666+
// resume_throw
4667+
contData->exceptionTag =
4668+
self()->getModule()->getTag(resumeThrow->tag);
4669+
} else {
4670+
// resume_throw_ref
4671+
contData->exception = arguments[0];
4672+
}
4673+
}
4674+
46464675
self()->pushCurrContinuation(contData);
46474676
self()->continuationStore->resuming = true;
46484677
#if WASM_INTERPRETER_DEBUG
@@ -4709,10 +4738,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
47094738
return ret;
47104739
}
47114740
Flow visitResume(Resume* curr) { return doResume(curr); }
4712-
Flow visitResumeThrow(ResumeThrow* curr) {
4713-
// TODO: should the Resume and ResumeThrow classes be merged?
4714-
return doResume(curr, self()->getModule()->getTag(curr->tag));
4715-
}
4741+
Flow visitResumeThrow(ResumeThrow* curr) { return doResume(curr); }
47164742
Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); }
47174743

47184744
void trap(const char* why) override {
@@ -4784,10 +4810,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
47844810
// to do is just start calling this function (with the arguments we've
47854811
// set), so resuming is done. (And throw, if resume_throw.)
47864812
self()->continuationStore->resuming = false;
4787-
if (auto* tag = currContinuation->exceptionTag) {
4788-
throwException(WasmException{
4789-
self()->makeExnData(tag, currContinuation->resumeArguments)});
4790-
}
4813+
maybeThrowAfterResuming(currContinuation);
47914814
}
47924815
}
47934816

src/wasm-ir-builder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
273273
Name tag,
274274
const std::vector<Name>& tags,
275275
const std::vector<std::optional<Index>>& labels);
276+
Result<> makeResumeThrowRef(HeapType ct,
277+
const std::vector<Name>& tags,
278+
const std::vector<std::optional<Index>>& labels) {
279+
// resume_throw_ref has an empty tag.
280+
return makeResumeThrow(ct, Name(), tags, labels);
281+
}
276282
Result<> makeStackSwitch(HeapType ct, Name tag);
277283

278284
// Private functions that must be public for technical reasons.

src/wasm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,9 @@ class ResumeThrow : public SpecificExpression<Expression::ResumeThrowId> {
21142114
: handlerTags(allocator), handlerBlocks(allocator), operands(allocator),
21152115
sentTypes(allocator) {}
21162116

2117+
// If tag is set to a non-null Name, this is a resume_throw and |operands|
2118+
// contains the values to be set in an exception of that tag. If tag is null,
2119+
// this is resume_throw_ref and |operands| contains a single item, the exnref.
21172120
Name tag;
21182121
// See the comment on `Resume` above.
21192122
ArenaVector<Name> handlerTags;

0 commit comments

Comments
 (0)