Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@
("suspend", "makeSuspend()"),
("resume", "makeResume()"),
("resume_throw", "makeResumeThrow()"),
("resume_throw_ref", "makeResumeThrowRef()"),
("switch", "makeStackSwitch()"),
# GC
("ref.i31", "makeRefI31(Unshared)"),
Expand Down
21 changes: 16 additions & 5 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -4972,12 +4972,23 @@ switch (buf[0]) {
return Ok{};
}
goto parse_error;
case '_':
if (op == "resume_throw"sv) {
CHECK_ERR(makeResumeThrow(ctx, pos, annotations));
return Ok{};
case '_': {
switch (buf[12]) {
case '\0':
if (op == "resume_throw"sv) {
CHECK_ERR(makeResumeThrow(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
case '_':
if (op == "resume_throw_ref"sv) {
CHECK_ERR(makeResumeThrowRef(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
default: goto parse_error;
}
goto parse_error;
}
default: goto parse_error;
}
}
Expand Down
14 changes: 10 additions & 4 deletions src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1325,10 +1325,16 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
ct = curr->cont->type.getHeapType();
}
assert(ct->isContinuation());
auto params = wasm.getTag(curr->tag)->params();
assert(params.size() == curr->operands.size());
for (size_t i = 0; i < params.size(); ++i) {
note(&curr->operands[i], params[i]);
if (curr->tag) {
// resume_throw
auto params = wasm.getTag(curr->tag)->params();
assert(params.size() == curr->operands.size());
for (size_t i = 0; i < params.size(); ++i) {
note(&curr->operands[i], params[i]);
}
} else {
// resume_throw_ref
note(&curr->operands[0], Type(HeapType::exn, Nullable));
}
note(&curr->cont, Type(*ct, Nullable));
}
Expand Down
50 changes: 37 additions & 13 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,13 @@ struct NullInstrParserCtx {
return Ok{};
}
template<typename HeapTypeT>
Result<> makeResumeThrowRef(Index,
const std::vector<Annotation>&,
HeapTypeT,
const TagLabelListT&) {
return Ok{};
}
template<typename HeapTypeT>
Result<>
makeStackSwitch(Index, const std::vector<Annotation>&, HeapTypeT, TagIdxT) {
return Ok{};
Expand Down Expand Up @@ -2889,24 +2896,41 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx>, AnnotationParserCtx {
return withLoc(pos, irBuilder.makeResume(type, tags, labels));
}

struct ResumeThrowData {
std::vector<Name> tags;
std::vector<std::optional<Index>> labels;

ResumeThrowData(const std::vector<OnClauseInfo>& resumetable) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might feel more lightweight to just return a pair of vectors or modify the vectors via outparams.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, outparams would require declaring them twice, and returning a pair seems less clear than named fields in a struct... I guess I'll keep it as is, but we can refactor later if we decide to.

tags.reserve(resumetable.size());
labels.reserve(resumetable.size());
for (const OnClauseInfo& info : resumetable) {
tags.push_back(info.tag);
if (info.isOnSwitch) {
labels.push_back(std::nullopt);
} else {
labels.push_back(std::optional<Index>(info.label));
}
}
}
};

Result<> makeResumeThrow(Index pos,
const std::vector<Annotation>& annotations,
HeapType type,
Name tag,
const std::vector<OnClauseInfo>& resumetable) {
std::vector<Name> tags;
std::vector<std::optional<Index>> labels;
tags.reserve(resumetable.size());
labels.reserve(resumetable.size());
for (const OnClauseInfo& info : resumetable) {
tags.push_back(info.tag);
if (info.isOnSwitch) {
labels.push_back(std::nullopt);
} else {
labels.push_back(std::optional<Index>(info.label));
}
}
return withLoc(pos, irBuilder.makeResumeThrow(type, tag, tags, labels));
ResumeThrowData data(resumetable);
return withLoc(
pos, irBuilder.makeResumeThrow(type, tag, data.tags, data.labels));
}

Result<> makeResumeThrowRef(Index pos,
const std::vector<Annotation>& annotations,
HeapType type,
const std::vector<OnClauseInfo>& resumetable) {
ResumeThrowData data(resumetable);
return withLoc(pos,
irBuilder.makeResumeThrowRef(type, data.tags, data.labels));
}

Result<> makeStackSwitch(Index pos,
Expand Down
15 changes: 15 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,21 @@ Result<> makeResumeThrow(Ctx& ctx,
return ctx.makeResumeThrow(pos, annotations, *type, *exnTag, *resumetable);
}

// resume_throw_ref ::= 'resume_throw' typeidx ('(' 'on' tagidx labelidx |
// 'on' tagidx switch ')')*
template<typename Ctx>
Result<> makeResumeThrowRef(Ctx& ctx,
Index pos,
const std::vector<Annotation>& annotations) {
auto type = typeidx(ctx);
CHECK_ERR(type);

auto resumetable = makeResumeTable(ctx);
CHECK_ERR(resumetable);

return ctx.makeResumeThrowRef(pos, annotations, *type, *resumetable);
}

// switch ::= 'switch' typeidx tagidx
template<typename Ctx>
Result<> makeStackSwitch(Ctx& ctx,
Expand Down
10 changes: 7 additions & 3 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2623,11 +2623,15 @@ struct PrintExpressionContents
void visitResumeThrow(ResumeThrow* curr) {
assert(curr->cont->type.isContinuation());
printMedium(o, "resume_throw");

if (!curr->tag) {
printMedium(o, "_ref");
}
o << ' ';
printHeapTypeName(curr->cont->type.getHeapType());
o << ' ';
curr->tag.print(o);
if (curr->tag) {
o << ' ';
curr->tag.print(o);
}

handleResumeTable(o, curr);
}
Expand Down
3 changes: 2 additions & 1 deletion src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,8 @@ enum ASTNodes {
Suspend = 0xe2,
Resume = 0xe3,
ResumeThrow = 0xe4,
Switch = 0xe5, // NOTE(dhil): the internal class is known as
ResumeThrowRef = 0xe5,
Switch = 0xe6, // NOTE(dhil): the internal class is known as
// StackSwitch to avoid conflict with the existing
// 'switch table'.
OnLabel = 0x00, // (on $tag $label)
Expand Down
57 changes: 40 additions & 17 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,14 @@ struct ContData {
// suspend).
Literals resumeArguments;

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

// If set, this is the exception ref to be thrown at the resume point (from
// resume_throw_ref).
Literal exception;

// Whether we executed. Continuations are one-shot, so they may not be
// executed a second time.
bool executed = false;
Expand Down Expand Up @@ -4480,7 +4485,6 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
}
}
Flow visitTryTable(TryTable* curr) {
assert(!self()->isResuming()); // TODO
try {
return self()->visit(curr->body);
} catch (const WasmException& e) {
Expand Down Expand Up @@ -4557,6 +4561,22 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
old->executed = true;
return Literal(std::make_shared<ContData>(newData));
}

void maybeThrowAfterResuming(std::shared_ptr<ContData>& currContinuation) {
// We may throw by creating a tag, or an exnref.
auto* tag = currContinuation->exceptionTag;
auto exnref = currContinuation->exception.type != Type::none;
assert(!(tag && exnref));
if (tag) {
// resume_throw
throwException(WasmException{
self()->makeExnData(tag, currContinuation->resumeArguments)});
} else if (exnref) {
// resume_throw_ref
throwException(WasmException{currContinuation->exception});
}
}

Flow visitSuspend(Suspend* curr) {
// Process the arguments, whether or not we are resuming. If we are resuming
// then we don't need these values (we sent them as part of the suspension),
Expand All @@ -4579,11 +4599,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
// restoredValues map.
assert(currContinuation->resumeInfo.empty());
assert(self()->restoredValuesMap.empty());
// Throw, if we were resumed by resume_throw;
if (auto* tag = currContinuation->exceptionTag) {
throwException(WasmException{
self()->makeExnData(tag, currContinuation->resumeArguments)});
}
maybeThrowAfterResuming(currContinuation);
return currContinuation->resumeArguments;
}

Expand Down Expand Up @@ -4616,7 +4632,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
new_->resumeExpr = curr;
return Flow(SUSPEND_FLOW, tag, std::move(arguments));
}
template<typename T> Flow doResume(T* curr, Tag* exceptionTag = nullptr) {
template<typename T> Flow doResume(T* curr) {
Literals arguments;
VISIT_ARGUMENTS(flow, curr->operands, arguments)
VISIT_REUSE(flow, curr->cont);
Expand All @@ -4636,13 +4652,26 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
trap("continuation already executed");
}
contData->executed = true;

if (contData->resumeArguments.empty()) {
// The continuation has no bound arguments. For now, we just handle the
// simple case of binding all of them, so that means we can just use all
// the immediate ones here. TODO
contData->resumeArguments = arguments;
}
contData->exceptionTag = exceptionTag;
// Fill in the continuation data. How we do this depends on whether we
// are resume or resume_throw*.
if (auto* resumeThrow = curr->template dynCast<ResumeThrow>()) {
if (resumeThrow->tag) {
// resume_throw
contData->exceptionTag =
self()->getModule()->getTag(resumeThrow->tag);
} else {
// resume_throw_ref
contData->exception = arguments[0];
}
}

self()->pushCurrContinuation(contData);
self()->continuationStore->resuming = true;
#if WASM_INTERPRETER_DEBUG
Expand Down Expand Up @@ -4709,10 +4738,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
return ret;
}
Flow visitResume(Resume* curr) { return doResume(curr); }
Flow visitResumeThrow(ResumeThrow* curr) {
// TODO: should the Resume and ResumeThrow classes be merged?
return doResume(curr, self()->getModule()->getTag(curr->tag));
}
Flow visitResumeThrow(ResumeThrow* curr) { return doResume(curr); }
Flow visitStackSwitch(StackSwitch* curr) { return Flow(NONCONSTANT_FLOW); }

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

Expand Down
6 changes: 6 additions & 0 deletions src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
Name tag,
const std::vector<Name>& tags,
const std::vector<std::optional<Index>>& labels);
Result<> makeResumeThrowRef(HeapType ct,
const std::vector<Name>& tags,
const std::vector<std::optional<Index>>& labels) {
// resume_throw_ref has an empty tag.
return makeResumeThrow(ct, Name(), tags, labels);
}
Result<> makeStackSwitch(HeapType ct, Name tag);

// Private functions that must be public for technical reasons.
Expand Down
3 changes: 3 additions & 0 deletions src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2114,6 +2114,9 @@ class ResumeThrow : public SpecificExpression<Expression::ResumeThrowId> {
: handlerTags(allocator), handlerBlocks(allocator), operands(allocator),
sentTypes(allocator) {}

// If tag is set to a non-null Name, this is a resume_throw and |operands|
// contains the values to be set in an exception of that tag. If tag is null,
// this is resume_throw_ref and |operands| contains a single item, the exnref.
Name tag;
// See the comment on `Resume` above.
ArenaVector<Name> handlerTags;
Expand Down
8 changes: 6 additions & 2 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3374,9 +3374,13 @@ Result<> WasmBinaryReader::readInst() {
}
return builder.makeResume(type, tags, labels);
}
case BinaryConsts::ResumeThrow: {
case BinaryConsts::ResumeThrow:
case BinaryConsts::ResumeThrowRef: {
auto type = getIndexedHeapType();
auto tag = getTagName(getU32LEB());
Name tag;
if (code == BinaryConsts::ResumeThrow) {
tag = getTagName(getU32LEB());
}
auto numHandlers = getU32LEB();
std::vector<Name> tags;
std::vector<std::optional<Index>> labels;
Expand Down
8 changes: 7 additions & 1 deletion src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2586,7 +2586,13 @@ IRBuilder::makeResumeThrow(HeapType ct,

ResumeThrow curr(wasm.allocator);
curr.tag = tag;
curr.operands.resize(wasm.getTag(tag)->params().size());
if (tag) {
// This is a normal resume_throw.
curr.operands.resize(wasm.getTag(tag)->params().size());
} else {
// This is a resume_throw_ref.
curr.operands.resize(1);
}

Result<ResumeTable> resumetable = makeResumeTable(
labels,
Expand Down
7 changes: 5 additions & 2 deletions src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2852,9 +2852,12 @@ void BinaryInstWriter::visitResumeThrow(ResumeThrow* curr) {
emitUnreachable();
return;
}
o << int8_t(BinaryConsts::ResumeThrow);
o << int8_t(curr->tag ? BinaryConsts::ResumeThrow
: BinaryConsts::ResumeThrowRef);
parent.writeIndexedHeapType(curr->cont->type.getHeapType());
o << U32LEB(parent.getTagIndex(curr->tag));
if (curr->tag) {
o << U32LEB(parent.getTagIndex(curr->tag));
}

size_t handlerNum = curr->handlerTags.size();
o << U32LEB(handlerNum);
Expand Down
Loading
Loading