Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed continuations: resume instructions #6083

Merged
merged 27 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f9ab9d4
[Typed continuations] resume instruction
frank-emrich Oct 3, 2023
2a932d3
Merge remote-tracking branch 'upstream/main' into wasmfx-resume
frank-emrich Nov 14, 2023
e96014a
dummy implementation for TypeGeneralizing pass
frank-emrich Nov 14, 2023
6b8787d
new implementation of operateOnScopeNameUsesAndSentTypes
frank-emrich Nov 14, 2023
a3cc291
OverriddenScopeNameUseVisitor
frank-emrich Nov 14, 2023
dfbb193
pass nullptr to func in operateOnScopeNameUsesAndSentValues
frank-emrich Nov 15, 2023
ef0a683
resume instructions have implicitTrap effect
frank-emrich Nov 15, 2023
53e40d4
move comment on own line
frank-emrich Nov 15, 2023
8f85434
manually remove (tag ...) lines erroneously added by updater script
frank-emrich Nov 15, 2023
b475268
experiment: move SentTypesVisitor outside of lambda
frank-emrich Nov 15, 2023
3c1906e
combine both experiments
frank-emrich Nov 15, 2023
6ab20ad
give up on OverriddenScopeNameUseVisitor
frank-emrich Nov 15, 2023
3556171
add typed_continuations_resume.wast to fuzzer ignore list
frank-emrich Nov 16, 2023
bc86090
remove visitTry, visitRethrow from SentTypesVisitor
frank-emrich Nov 21, 2023
97d54f7
Merge remote-tracking branch 'upstream/main' into wasmfx-resume
frank-emrich Nov 21, 2023
cf17036
dummy implementation of SubtypingDiscoverer::visitResume
frank-emrich Nov 21, 2023
db5b02a
Merge remote-tracking branch 'upstream/main' into wasmfx-resume
frank-emrich Dec 13, 2023
115d0e2
when printing resume instructions, keep tag clauses on same line
frank-emrich Dec 13, 2023
143bb2d
remove SentTypesVisitor in favor of simple loop
frank-emrich Dec 23, 2023
6749e08
printing: move as much into PrintExpressionContents as possible
frank-emrich Jan 7, 2024
17be964
Merge remote-tracking branch 'upstream/main' into wasmfx-resume
frank-emrich Jan 7, 2024
c262d3c
rephrase comment
frank-emrich Jan 7, 2024
fbcb7fb
Make Resume class more consistent with TryTable
frank-emrich Jan 7, 2024
06a26c1
include Resume's sentTypes field in field delegations
frank-emrich Jan 7, 2024
d41256d
address minor nits
frank-emrich Jan 8, 2024
ab6ff8b
move invariant checks to FunctionValidator::visitResume
frank-emrich Jan 8, 2024
179faa4
move & update typed_continuations_resume.wast
frank-emrich Jan 8, 2024
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
2 changes: 2 additions & 0 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@
# Typed function references instructions
("call_ref", "makeCallRef(s, /*isReturn=*/false)"),
("return_call_ref", "makeCallRef(s, /*isReturn=*/true)"),
# Typed continuations instructions
("resume", "makeResume(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 @@ -3004,6 +3004,9 @@ switch (buf[0]) {
default: goto parse_error;
}
}
case 's':
if (op == "resume"sv) { return makeResume(s); }
goto parse_error;
case 't': {
switch (buf[3]) {
case 'h':
Expand Down Expand Up @@ -8110,6 +8113,12 @@ switch (buf[0]) {
default: goto parse_error;
}
}
case 's':
if (op == "resume"sv) {
CHECK_ERR(makeResume(ctx, pos));
return Ok{};
}
goto parse_error;
case 't': {
switch (buf[3]) {
case 'h':
Expand Down
2 changes: 2 additions & 0 deletions src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ void ReFinalize::visitStringSliceIter(StringSliceIter* curr) {
curr->finalize();
}

void ReFinalize::visitResume(Resume* curr) { curr->finalize(); }

void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); }
void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); }
void ReFinalize::visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); }
Expand Down
22 changes: 22 additions & 0 deletions src/ir/branch-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,24 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
func(name, sw->value ? sw->value->type : Type::none);
} else if (auto* br = expr->dynCast<BrOn>()) {
func(name, br->getSentType());
} else if (auto* res = expr->dynCast<Resume>()) {
// FIXME(frank-emrich) For each of the (tag $t $b) entries for the resume
// instruction, we could determine the types of the values passed to block
// $b by looking up the signature of tag $t in the module, plus taking the
// type of the resumed continuation into account (the latter type is
// stored in the Resume node directly).
//
// However, we do not have access to that information at this point. We
// could get it from the Module (to look up the tag), but this would
// require additional plumbing at all the use sites of this function, such
// as users of BranchSeeker, which is in turn used by functions such as
// Block::finalize(). Plumbing the module through to that point would
// require chancing the re-finalisation code, for example.
//
// We could also store this information in the Resume node itself, but
// that would mean storing the signature of all the Tags that we have a
// handle clause for.
func(name, Type::none);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issue described here is what's causing the lit test suite to fail:
Enabling BINARYEN_PASS_DEBUG means that validateBinaryenIR eventually re-finalises each block by calling Block::finalize(). This can cause situations where the types collected by BranchSeeker fully determine the new type given to the block, ignoring the type annotation on it. This means that the code snippet here, where Type::none is passed as a placeholder for the real type causes problems, because Block::finalize() will obtain this from BranchSeeker and uses it as the overall type of the block. This then makes validateBinaryenIR fail.

This failure seems to be a relatively recent change, since the test suite was passing a few weeks ago when I did the majority of the work on this PR... But I guess it just shows that my "solution" of passing Type::none here was never quite right to begin with.

Copy link
Member

Choose a reason for hiding this comment

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

cc @aheejin, I guess the new EH design will also run into the same problem here: the types of values carried on the branches from the catch_table instruction will depend on the tags on that instruction, so we will need the module here to look up the types of those tags.

@frank-emrich, I don't think we have a choice but to plumb the module through to here sooner or later, and probably sooner, since I can't think of a good workaround 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've worked on this for a bit, which mostly involved changing all use sites of BranchSeeker, and have a question:

Currently, BranchSeeker is mostly used by calling its has and count functions. These don't care about the types sent to the branches types at all. In fact, there are few uses of BranchSeeker that do inspect the types field afterwards. And we only need access to the Module in order to correctly calculate the types sent to the branch. That means we would have to change Branchseeker::count and BranchSeeker::has to take a Module parameter, even though the types field is ignored.

This suggests that it may be useful to change the existing BranchSeeker such that it continues offering has and count with the existing signatures but remove the types field. This could be implemented entirely using operateOnScopeNameUses, no type (or module) information needed.

We then define a new BranchTypeSeeker that behaves more like the old BranchSeeker, which uses operateOnScopeNameUsesAndSentTypes, does accumulate the types sent to the branch, and needs access to the Module to do so to implement typed continuations and the new style EH.

This question came up when changing the signature of the various finalize methods of Block: The existing finalize() must definitely take a Module in the future, because it uses BranchSeeker to actually inspect the types sent to branches.
However, Block::finalize(Type type_) andBlock::finalize(Type type_, Breakability breakability) only use BranchUtils::BranchSeeker::has and don't care about the types sent to the block. Thus, if we go through with the splitting of BranchSeeker mentioned above, we could avoid also needing to add a Module parameter to these two.

I've mostly gone through with changing the signatures of all three Block::finalize functions now, so I don't care much which option we take here, but it felt a bit unfortunate to change all these use sites of Branchseeker::count and BranchSeeker::has, even though they don't actually care about the extra typing information.

Copy link
Member

Choose a reason for hiding this comment

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

This refactoring sounds good to me.

One option might be for the finalize methods that would need to take a Module to instead conservatively keep the type unchanged, then have special logic in the ReFinalize that takes the module into account to do better refinalization.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have created a separate PR with the Module parameter plumbing: #6096. I will rebase this PR once the other one has landed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After the discussion in #6096 I will now implement storing the sent types directly in Resume nodes, instead of using the Module here to determine them.

The most straightforward implementation is an ArenaVector<Type> whose i-th entry contains the type sent to the i-th block (i.e, curr->handlerBlocks[i]). Let's assume this vector is called sentTypes.

There is one different issue here that affects the overall implementation of operateOnScopeNameUsesAndSentTypes: Since it is implemented using operateOnScopeNameUses, a Resume instruction with n handler clauses results in n calls to the lambda defined in operateOnScopeNameUsesAndSentTypes, with the name of the corresponding handler block as the name argument each time. However, in the lambda we then have would run code like this:

  ...
  } else if (auto* res = expr->dynCast<Resume>()) {
    for (usize i = 0; < res->handlerBlocks.size(); i++) {
      auto handlerBlock = res->handlerBlocks[i];
      if (handlerBlock == name) {
        func(name, curr->sentTypes[i]);
      }
    }
  } else { ...

In total, this is quadratic in the number of handler clauses.

Which of the following solutions would you prefer:

  1. operateOnScopeNameUses takes a lambda whose parameter is a vector of names. We would effectively call the lambda "in bulk" for those node types that have more than one scope name use. But this would require creating singleton vector for all the other node types, so this doesn't seem efficient.
  2. We could pass some kind of index to the lambda, indicating that the currently passed Name is the i-th block name used by the particular node. But that doesn't seem great either, the index would be meaningless for all node types other than Resume.
  3. sentTypes could be a map from block names to types instead of just a vector of types. That seems relatively clean, but uses more space, and it would need to be integrated into the logic that updates tag names in case a naming section is encountered in a binary.
  4. We may implement operateOnScopeNameUsesAndSentValues without using operateOnScopeNameUses at all: Instead, it could just be a visitor on Expressions. In the case for Resume nodes, we would then process all the blocks at once and invoke func for each handler block.

I'm personally in favor of the last option: After all, the lambda passed to operateOnScopeNameUses in operateOnScopeNameUsesAndSentValues already disambiguates the different node types, operateOnScopeNameUses is basically just used to pre-filter the nodes and extract the name of the used block.

Note that this entire discussion equally applies to Try nodes once the updated EH proposal is implemented.

Copy link
Member

Choose a reason for hiding this comment

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

4 makes sense to me as well.

Copy link
Member

Choose a reason for hiding this comment

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

How many handler clauses do you expect to have with resume? With try_table, at least LLVM at the moment will generate instructions with only one clause (it is either for the C++ tag/label or the catch_all label. We don't generate both at the same time. But even if we do it's gonna be only two), so we are not really gonna be affected by the quadratic loop. If what you would have in average is less than 5, I'm not sure if this is something we should worry about?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've now re-implemented operateOnScopeNameUsesAndSentTypes using a Visitor.

@aheejin There are some compilation schemes where you may create resume instructions with tag clauses that handle more or less every single tag defined. However, even then I'd surprised to see more than a dozen or two. However, given that there are source languages with effect handlers that would like to target our instructions directly, it's basically up to what people do in those source languages. Having the quadratic behavior here is probably fine unless someone translates some crazy generated code using effect handlers into wasm.

But given that the solution using a visitor was relatively simple to implement, I'd say let's just err on the side of caution.

} else {
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
}
Expand All @@ -97,6 +115,10 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
func(name, sw->value);
} else if (auto* br = expr->dynCast<BrOn>()) {
func(name, br->ref);
} else if (auto* res = expr->dynCast<Resume>()) {
// FIXME(frank-emrich) We can't really determine the values being sent
// here because they come from suspend instructions elsewhere. Is that a
// problem?
frank-emrich marked this conversation as resolved.
Show resolved Hide resolved
} else {
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
}
Expand Down
5 changes: 5 additions & 0 deletions src/ir/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,11 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return 8 + visit(curr->ref) + visit(curr->num);
}

CostType visitResume(Resume* curr) {
// Inspired by indirect calls, but twice the cost.
return 12 + visit(curr->cont);
}

private:
CostType nullCheckCost(Expression* ref) {
// A nullable type requires a bounds check in most VMs.
Expand Down
14 changes: 14 additions & 0 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,20 @@ class EffectAnalyzer {
// traps when ref is null.
parent.implicitTrap = true;
}

void visitResume(Resume* curr) {
// This acts as a kitchen sink effect.
parent.calls = true;

// FIXME(frank-emrich) We should probably set parent.trap or
// parent.implicitTrap, because the resume instruction itself may trap:
// The continuation reference is nullable, and we trap if it is indeed
// null.
frank-emrich marked this conversation as resolved.
Show resolved Hide resolved

if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

Is this because we're modeling suspending as throwing? Probably worth a comment if so.

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 was just inspired by the following happening at the end of visitCall here :

// When EH is enabled, any call can throw.
if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
    parent.throws_ = true;
}

I'm not sure if it's really necessary for Resume. @aheejin would you mind having a look?

Copy link
Member

Choose a reason for hiding this comment

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

Can a resume instruction throw an exception? If not, we can remove this. (Calls can, which is why they have it.)

Copy link
Member

Choose a reason for hiding this comment

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

Yes, if the resumed continuation throws, it will bubble out from the resume instruction, just like an exception from a called function would.

parent.throws_ = true;
}
}
};

public:
Expand Down
2 changes: 2 additions & 0 deletions src/ir/module-utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ struct CodeScanner
counts.include(get->type);
} else if (auto* set = curr->dynCast<ArraySet>()) {
counts.note(set->ref->type);
} else if (auto* resume = curr->dynCast<Resume>()) {
counts.note(resume->contType);
} else if (Properties::isControlFlowStructure(curr)) {
counts.noteControlFlow(Signature(Type::none, curr->type));
}
Expand Down
5 changes: 5 additions & 0 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,11 @@ struct InfoCollector

void visitReturn(Return* curr) { addResult(curr->value); }

void visitResume(Resume* curr) {
// TODO: optimize when possible
addRoot(curr);
}

void visitFunction(Function* func) {
// Functions with a result can flow a value out from their body.
addResult(func->body);
Expand Down
5 changes: 5 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Result<> makeStringIterMove(Ctx&, Index, StringIterMoveOp op);
template<typename Ctx>
Result<> makeStringSliceWTF(Ctx&, Index, StringSliceWTFOp op);
template<typename Ctx> Result<> makeStringSliceIter(Ctx&, Index);
template<typename Ctx> Result<> makeResume(Ctx&, Index);

// Modules
template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx);
Expand Down Expand Up @@ -1477,6 +1478,10 @@ template<typename Ctx> Result<> makeStringSliceIter(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}

template<typename Ctx> Result<> makeResume(Ctx& ctx, Index pos) {
return ctx.in.err("unimplemented instruction");
}

// =======
// Modules
// =======
Expand Down
31 changes: 31 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
void visitIf(If* curr);
void visitLoop(Loop* curr);
void visitTry(Try* curr);
void visitResume(Resume* curr);
void maybePrintUnreachableReplacement(Expression* curr, Type type);
void maybePrintUnreachableOrNullReplacement(Expression* curr, Type type);
void visitCallRef(CallRef* curr) {
Expand Down Expand Up @@ -2384,6 +2385,7 @@ struct PrintExpressionContents
void visitStringSliceIter(StringSliceIter* curr) {
printMedium(o, "stringview_iter.slice");
}
void visitResume(Resume* curr) { printMedium(o, "resume"); }
frank-emrich marked this conversation as resolved.
Show resolved Hide resolved
};

void PrintSExpression::setModule(Module* module) {
Expand Down Expand Up @@ -2711,6 +2713,35 @@ void PrintSExpression::visitTry(Try* curr) {
}
}

void PrintSExpression::visitResume(Resume* curr) {
controlFlowDepth++;
o << '(';
printExpressionContents(curr);
o << ' ';
printHeapType(curr->contType);
incIndent();

for (size_t i = 0; i < curr->handlerTags.size(); i++) {
doIndent(o, indent);

o << '(';
printMedium(o, "tag ");
printName(curr->handlerTags[i], o);
o << " ";
printName(curr->handlerBlocks[i], o);
o << ")\n";
}

for (size_t i = 0; i < curr->operands.size(); i++) {
printFullLine(curr->operands[i]);
}

printFullLine(curr->cont);

controlFlowDepth--;
decIndent();
}

void PrintSExpression::maybePrintUnreachableReplacement(Expression* curr,
Type type) {
// See the parallel function
Expand Down
6 changes: 6 additions & 0 deletions src/passes/Unsubtyping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,12 @@ struct Unsubtyping
void visitStringIterMove(StringIterMove* curr) {}
void visitStringSliceWTF(StringSliceWTF* curr) {}
void visitStringSliceIter(StringSliceIter* curr) {}

void visitResume(Resume* curr) {
// TODO Implement this by exposing the subtyping relations that the
// validator expects to hold, once validation of resume is implemented
WASM_UNREACHABLE("not implemented");
}
};

} // anonymous namespace
Expand Down
5 changes: 5 additions & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,10 @@ enum ASTNodes {
StringEncodeLossyUTF8Array = 0xb6,
StringEncodeWTF8Array = 0xb7,
StringNewUTF8ArrayTry = 0xb8,

// typed continuation opcodes
Resume = 0xe3,

};

enum MemoryAccess {
Expand Down Expand Up @@ -1893,6 +1897,7 @@ class WasmBinaryReader {
void visitCallRef(CallRef* curr);
void visitRefAsCast(RefCast* curr, uint32_t code);
void visitRefAs(RefAs* curr, uint8_t code);
void visitResume(Resume* curr);

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

Expand Down
15 changes: 15 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,21 @@ class Builder {
return ret;
}

Resume* makeResume(HeapType contType,
const std::vector<Name>& handlerTags,
const std::vector<Name>& handlerBlocks,
const std::vector<Expression*>& operands,
Expression* cont) {
auto* ret = wasm.allocator.alloc<Resume>();
ret->contType = contType;
ret->handlerTags.set(handlerTags);
ret->handlerBlocks.set(handlerBlocks);
ret->operands.set(operands);
ret->cont = cont;
ret->finalize();
return ret;
}

// Additional helpers

Drop* makeDrop(Expression* value) {
Expand Down
11 changes: 11 additions & 0 deletions src/wasm-delegations-fields.def
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,17 @@ switch (DELEGATE_ID) {
DELEGATE_END(StringSliceIter);
break;
}

case Expression::Id::ResumeId: {
DELEGATE_START(Resume);
Copy link
Member

Choose a reason for hiding this comment

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

It seems we need to add sentTypes here as well. See #6181 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

DELEGATE_FIELD_CHILD(Resume, cont);
DELEGATE_FIELD_CHILD_VECTOR(Resume, operands);
DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(Resume, handlerBlocks);
DELEGATE_FIELD_NAME_KIND_VECTOR(Resume, handlerTags, ModuleItemKind::Tag);
DELEGATE_FIELD_HEAPTYPE(Resume, contType);
DELEGATE_END(Resume);
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 @@ -102,5 +102,6 @@ DELEGATE(StringIterNext);
DELEGATE(StringIterMove);
DELEGATE(StringSliceWTF);
DELEGATE(StringSliceIter);
DELEGATE(Resume);

#undef DELEGATE
2 changes: 2 additions & 0 deletions src/wasm-interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2342,6 +2342,7 @@ class ConstantExpressionRunner : public ExpressionRunner<SubType> {
}
return ExpressionRunner<SubType>::visitRefAs(curr);
}
Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); }

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

Expand Down Expand Up @@ -3857,6 +3858,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
multiValues.pop_back();
return ret;
}
Flow visitResume(Resume* curr) { return Flow(NONCONSTANT_FLOW); }
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be WASM_UNREACHABLE("unimplemented"). NONCONSTANT_FLOW is only used in the ExpressionRunner up above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


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

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 @@ -325,6 +325,7 @@ class SExpressionWasmBuilder {
Expression* makeStringIterMove(Element& s, StringIterMoveOp op);
Expression* makeStringSliceWTF(Element& s, StringSliceWTFOp op);
Expression* makeStringSliceIter(Element& s);
Expression* makeResume(Element& s);

// Helper functions
Type parseBlockType(Element& s, Index& i);
Expand Down
16 changes: 16 additions & 0 deletions src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ class Expression {
StringIterMoveId,
StringSliceWTFId,
StringSliceIterId,
ResumeId,
NumExpressionIds
};
Id _id;
Expand Down Expand Up @@ -1920,6 +1921,21 @@ class StringSliceIter
void finalize();
};

class Resume : public SpecificExpression<Expression::ResumeId> {
public:
Resume(MixedArena& allocator)
: handlerTags(allocator), handlerBlocks(allocator), operands(allocator) {}

HeapType contType;
ArenaVector<Name> handlerTags;
ArenaVector<Name> handlerBlocks;

ExpressionList operands;
Expression* cont;

void finalize();
};

// Globals

struct Named {
Expand Down
Loading
Loading