Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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/fuzz_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ def is_git_repo():
'typed_continuations_resume.wast',
'typed_continuations_contnew.wast',
'typed_continuations_contbind.wast',
'typed_continuations_suspend.wast',
# New EH implementation is in progress
'exception-handling.wast',
'translate-eh-old-to-new.wast',
Expand Down
1 change: 1 addition & 0 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@
("cont.new", "makeContNew(s)"),
("cont.bind", "makeContBind(s)"),
("resume", "makeResume(s)"),
("suspend", "makeSuspend(s)"),
# GC
("i31.new", "makeRefI31(s)"), # deprecated
("ref.i31", "makeRefI31(s)"),
Expand Down
9 changes: 9 additions & 0 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -3369,6 +3369,9 @@ switch (buf[0]) {
default: goto parse_error;
}
}
case 'u':
if (op == "suspend"sv) { return makeSuspend(s); }
goto parse_error;
default: goto parse_error;
}
}
Expand Down Expand Up @@ -8653,6 +8656,12 @@ switch (buf[0]) {
default: goto parse_error;
}
}
case 'u':
if (op == "suspend"sv) {
CHECK_ERR(makeSuspend(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
default: goto parse_error;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ void ReFinalize::visitStringSliceIter(StringSliceIter* curr) {
void ReFinalize::visitContNew(ContNew* curr) { curr->finalize(); }
void ReFinalize::visitContBind(ContBind* curr) { curr->finalize(); }
void ReFinalize::visitResume(Resume* curr) { curr->finalize(); }
void ReFinalize::visitSuspend(Suspend* curr) { curr->finalize(getModule()); }

void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); }
void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); }
Expand Down
8 changes: 8 additions & 0 deletions src/ir/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,14 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
// Inspired by indirect calls, but twice the cost.
return 12 + visit(curr->cont);
}
CostType visitSuspend(Suspend* curr) {
// Cheaper than resume, since payloads cannot be partially applied.
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain this more? What does it mean that resume can partially apply payloads?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is definitely my most dreaded file now 😅

What I meant in the original comment here is the following: If you call resume on a continuation, it may have been produced with cont.bind, meaning that we have partially applied some arguments to the continuation, and resume only needs to provide the remaining ones. That means that we need to perform some runtime check that determines how to handle payloads in resume.
In contrast, suspend always sees all the payloads statically: When you call suspend, you provide all the payloads at once.

But there are more considerations when talking about the performance of suspend and resume, so mentioning only partial applications in my comment was a bit misleading: resume may have to check if you are initiating execution for the first time vs continuing after a suspension, suspend may need to forward to an outer handler, etc.

Given that suspend works in tandem with resume (i.e., whenever you call suspend you continue execution at another resume) and they both do stack switching, I'm now feeling more inclined to just give suspend the same cost as resume and delete my comment on suspend. Does that sound okay?

Copy link
Member

Choose a reason for hiding this comment

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

Thanks, makes sense.

Yes, giving them the same cost without comment seems fine. Trying to be too precise in the reasoning here doesn't make sense given how imprecise all the numbers actually are :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, I've updated the function for suspend accordingly. I've also realized that I forgot to include the cost of the operands when calculating the cost of resume, so I fixed that in one go.

CostType ret = 8;
for (auto* arg : curr->operands) {
ret += visit(arg);
}
return ret;
}

private:
CostType nullCheckCost(Expression* ref) {
Expand Down
8 changes: 8 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,14 @@ class EffectAnalyzer {
parent.throws_ = true;
}
}
void visitSuspend(Suspend* curr) {
// Similar to resume/call: Suspending means that we execute arbitrary
// other code before we may resume here.
parent.calls = true;
if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
parent.throws_ = true;
}
}
};

public:
Expand Down
4 changes: 4 additions & 0 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,10 @@ struct InfoCollector
// TODO: optimize when possible
addRoot(curr);
}
void visitSuspend(Suspend* curr) {
// TODO: optimize when possible
addRoot(curr);
}

void visitFunction(Function* func) {
// Functions with a result can flow a value out from their body.
Expand Down
1 change: 1 addition & 0 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); }
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("not implemented"); }
};

} // namespace wasm
Expand Down
8 changes: 8 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,9 @@ struct NullInstrParserCtx {
const TagLabelListT&) {
return Ok{};
}
Result<> makeSuspend(Index, const std::vector<Annotation>&, TagIdxT) {
return Ok{};
}
};

struct NullCtx : NullTypeParserCtx, NullInstrParserCtx {
Expand Down Expand Up @@ -2594,6 +2597,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
}
return withLoc(pos, irBuilder.makeResume(type, tags, labels));
}

Result<>
makeSuspend(Index pos, const std::vector<Annotation>& annotations, Name tag) {
return withLoc(pos, irBuilder.makeSuspend(tag));
}
};

} // namespace wasm::WATParser
Expand Down
11 changes: 11 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ template<typename Ctx>
Result<> makeContNew(Ctx*, Index, const std::vector<Annotation>&);
template<typename Ctx>
Result<> makeResume(Ctx&, Index, const std::vector<Annotation>&);
template<typename Ctx>
Result<> makeSuspend(Ctx&, Index, const std::vector<Annotation>&);

// Modules
template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx);
Expand Down Expand Up @@ -2498,6 +2500,15 @@ makeResume(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) {
return ctx.makeResume(pos, annotations, *type, tagLabels);
}

template<typename Ctx>
Result<>
makeSuspend(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) {
auto tag = tagidx(ctx);
CHECK_ERR(tag);

return ctx.makeSuspend(pos, annotations, *tag);
}

// =======
// Modules
// =======
Expand Down
5 changes: 5 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2382,6 +2382,11 @@ struct PrintExpressionContents
o << ')';
}
}

void visitSuspend(Suspend* curr) {
printMedium(o, "suspend ");
curr->tag.print(o);
}
};

void PrintSExpression::setModule(Module* module) {
Expand Down
1 change: 1 addition & 0 deletions src/passes/TypeGeneralizing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,7 @@ struct TransferFn : OverriddenVisitor<TransferFn> {
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("TODO"); }
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("TODO"); }
void visitResume(Resume* curr) { WASM_UNREACHABLE("TODO"); }
void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("TODO"); }
};

struct TypeGeneralizing : WalkerPass<PostWalker<TypeGeneralizing>> {
Expand Down
2 changes: 2 additions & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,7 @@ enum ASTNodes {
// typed continuation opcodes
ContNew = 0xe0,
ContBind = 0xe1,
Suspend = 0xe2,
Resume = 0xe3,

};
Expand Down Expand Up @@ -1810,6 +1811,7 @@ class WasmBinaryReader {
void visitContNew(ContNew* curr);
void visitContBind(ContBind* curr);
void visitResume(Resume* curr);
void visitSuspend(Suspend* curr);

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

Expand Down
7 changes: 7 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,13 @@ class Builder {
ret->finalize(&wasm);
return ret;
}
Suspend* makeSuspend(Name tag, const std::vector<Expression*>& args) {
auto* ret = wasm.allocator.alloc<Suspend>();
ret->tag = tag;
ret->operands.set(args);
ret->finalize(&wasm);
return ret;
}

// Additional helpers

Expand Down
7 changes: 7 additions & 0 deletions src/wasm-delegations-fields.def
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,13 @@ switch (DELEGATE_ID) {
DELEGATE_END(Resume);
break;
}
case Expression::Id::SuspendId: {
DELEGATE_START(Suspend);
DELEGATE_FIELD_CHILD_VECTOR(Suspend, operands);
DELEGATE_FIELD_NAME_KIND(Suspend, tag, ModuleItemKind::Tag);
DELEGATE_END(Suspend);
break;
}
}

#undef DELEGATE_ID
Expand Down
1 change: 1 addition & 0 deletions src/wasm-delegations.def
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,6 @@ DELEGATE(StringSliceIter);
DELEGATE(ContBind);
DELEGATE(ContNew);
DELEGATE(Resume);
DELEGATE(Suspend);

#undef DELEGATE
2 changes: 2 additions & 0 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2404,6 +2404,7 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
Flow visitContBind(ContBind* curr) { WASM_UNREACHABLE("unimplemented"); }
Flow visitContNew(ContNew* curr) { WASM_UNREACHABLE("unimplemented"); }
Flow visitResume(Resume* curr) { WASM_UNREACHABLE("unimplemented"); }
Flow visitSuspend(Suspend* curr) { WASM_UNREACHABLE("unimplemented"); }

void trap(const char* why) override { throw NonconstantException(); }

Expand Down Expand Up @@ -3980,6 +3981,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
Flow visitContBind(ContBind* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitContNew(ContNew* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); }
Flow visitSuspend(Suspend* curr) { return Flow(NONCONSTANT_FLOW); }

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

Expand Down
2 changes: 2 additions & 0 deletions src/wasm-ir-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
[[nodiscard]] Result<> makeResume(HeapType ct,
const std::vector<Name>& tags,
const std::vector<Index>& labels);
[[nodiscard]] Result<> makeSuspend(Name tag);

// Private functions that must be public for technical reasons.
[[nodiscard]] Result<> visitExpression(Expression*);
Expand Down Expand Up @@ -256,6 +257,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
[[nodiscard]] Result<> visitStringEncode(StringEncode*);
[[nodiscard]] Result<> visitContBind(ContBind*);
[[nodiscard]] Result<> visitResume(Resume*);
[[nodiscard]] Result<> visitSuspend(Suspend*);
[[nodiscard]] Result<> visitTupleMake(TupleMake*);
[[nodiscard]] Result<>
visitTupleExtract(TupleExtract*,
Expand Down
1 change: 1 addition & 0 deletions src/wasm-s-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ class SExpressionWasmBuilder {
Expression* makeContBind(Element& s);
Expression* makeContNew(Element& s);
Expression* makeResume(Element& s);
Expression* makeSuspend(Element& s);

// Helper functions
Type parseBlockType(Element& s, Index& i);
Expand Down
14 changes: 14 additions & 0 deletions src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ class Expression {
ContBindId,
ContNewId,
ResumeId,
SuspendId,
NumExpressionIds
};
Id _id;
Expand Down Expand Up @@ -2048,6 +2049,19 @@ class Resume : public SpecificExpression<Expression::ResumeId> {
ArenaVector<Type> sentTypes;
};

class Suspend : public SpecificExpression<Expression::SuspendId> {
public:
Suspend(MixedArena& allocator) : operands(allocator) {}

Name tag;
ExpressionList operands;

// We need access to the module to obtain the signature of the tag,
// which determines this node's type.
// If no module is given, then the type must have been set already.
void finalize(Module* wasm = nullptr);
};

// Globals

struct Named {
Expand Down
24 changes: 24 additions & 0 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4060,6 +4060,10 @@ BinaryConsts::ASTNodes WasmBinaryReader::readExpression(Expression*& curr) {
visitResume((curr = allocator.alloc<Resume>())->cast<Resume>());
break;
}
case BinaryConsts::Suspend: {
visitSuspend((curr = allocator.alloc<Suspend>())->cast<Suspend>());
break;
}
case BinaryConsts::AtomicPrefix: {
code = static_cast<uint8_t>(getU32LEB());
if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) {
Expand Down Expand Up @@ -7869,6 +7873,26 @@ void WasmBinaryReader::visitResume(Resume* curr) {
curr->finalize(&wasm);
}

void WasmBinaryReader::visitSuspend(Suspend* curr) {
BYN_TRACE("zz node: Suspend\n");

auto tagIndex = getU32LEB();
if (tagIndex >= wasm.tags.size()) {
throwError("bad tag index");
}
auto* tag = wasm.tags[tagIndex].get();
curr->tag = tag->name;
tagRefs[tagIndex].push_back(&curr->tag);

auto numArgs = tag->sig.params.size();
curr->operands.resize(numArgs);
for (size_t i = 0; i < numArgs; i++) {
curr->operands[numArgs - i - 1] = popNonVoidExpression();
}

curr->finalize(&wasm);
}

void WasmBinaryReader::throwError(std::string text) {
throw ParseException(text, 0, pos);
}
Expand Down
23 changes: 23 additions & 0 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,19 @@ Result<> IRBuilder::visitResume(Resume* curr) {
return Ok{};
}

Result<> IRBuilder::visitSuspend(Suspend* curr) {
auto tag = wasm.getTag(curr->tag);
auto sig = tag->sig;
auto size = sig.params.size();
curr->operands.resize(size);
for (size_t i = 0; i < size; ++i) {
auto val = pop();
CHECK_ERR(val);
curr->operands[size - i - 1] = *val;
}
return Ok{};
}

Result<> IRBuilder::visitTupleMake(TupleMake* curr) {
assert(curr->operands.size() >= 2);
for (size_t i = 0, size = curr->operands.size(); i < size; ++i) {
Expand Down Expand Up @@ -1930,4 +1943,14 @@ Result<> IRBuilder::makeResume(HeapType ct,
return Ok{};
}

Result<> IRBuilder::makeSuspend(Name tag) {
Suspend curr(wasm.allocator);
curr.tag = tag;
CHECK_ERR(visitSuspend(&curr));

std::vector<Expression*> operands(curr.operands.begin(), curr.operands.end());
push(builder.makeSuspend(tag, operands));
return Ok{};
}

} // namespace wasm
14 changes: 14 additions & 0 deletions src/wasm/wasm-s-parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3059,6 +3059,20 @@ Expression* SExpressionWasmBuilder::makeResume(Element& s) {
return ret;
}

Expression* SExpressionWasmBuilder::makeSuspend(Element& s) {
auto ret = allocator.alloc<Suspend>();

ret->tag = getTagName(*s[1]);

Index i = 2;
while (i < s.size()) {
ret->operands.push_back(parseExpression(s[i++]));
}

ret->finalize(&wasm);
return ret;
}

Expression* SExpressionWasmBuilder::makeRefI31(Element& s) {
auto ret = allocator.alloc<RefI31>();
ret->value = parseExpression(s[1]);
Expand Down
4 changes: 4 additions & 0 deletions src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2512,6 +2512,10 @@ void BinaryInstWriter::visitResume(Resume* curr) {
}
}

void BinaryInstWriter::visitSuspend(Suspend* curr) {
o << int8_t(BinaryConsts::Suspend) << U32LEB(parent.getTagIndex(curr->tag));
}

void BinaryInstWriter::emitScopeEnd(Expression* curr) {
assert(!breakStack.empty());
breakStack.pop_back();
Expand Down
Loading