Skip to content

Handle stack switching in Unsubtyping #7608

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

Merged
merged 5 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from 3 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/test/fuzzing.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
'coalesce-locals-stack-switching.wast',
'dce-stack-switching.wast',
'precompute-stack-switching.wast',
'unsubtyping-stack-switching.wast',
'vacuum-stack-switching.wast',
# TODO: fuzzer support for custom descriptors
'custom-descriptors.wast',
Expand Down
124 changes: 118 additions & 6 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,127 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
void visitStringWTF16Get(StringWTF16Get* curr) {}
void visitStringSliceWTF(StringSliceWTF* curr) {}

void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); }
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("not implemented"); }
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
void visitContNew(ContNew* curr) {
if (!curr->type.isContinuation()) {
return;
}
// The type of the function reference must remain a subtype of the function
// type expected by the continuation.
self()->noteSubtype(curr->func->type.getHeapType(),
curr->type.getHeapType().getContinuation().type);
}
void visitContBind(ContBind* curr) {
if (!curr->cont->type.isContinuation()) {
return;
}
// Each of the bound arguments must remain subtypes of their expected
// parameters.
auto params = curr->cont->type.getHeapType()
.getContinuation()
.type.getSignature()
.params;
assert(curr->operands.size() <= params.size());
for (Index i = 0; i < curr->operands.size(); ++i) {
self()->noteSubtype(curr->operands[i], params[i]);
}
}
void visitSuspend(Suspend* curr) {
// The operands must remain subtypes of the parameters given by the tag.
auto params =
self()->getModule()->getTag(curr->tag)->type.getSignature().params;
assert(curr->operands.size() == params.size());
for (Index i = 0; i < curr->operands.size(); ++i) {
self()->noteSubtype(curr->operands[i], params[i]);
}
}
void processResumeHandlers(Type contType,
const ArenaVector<Name>& handlerTags,
const ArenaVector<Name>& handlerBlocks) {
auto contSig = contType.getHeapType().getContinuation().type.getSignature();
assert(handlerTags.size() == handlerBlocks.size());
auto& wasm = *self()->getModule();
// Process each handler in turn.
for (Index i = 0; i < handlerTags.size(); ++i) {
if (!handlerBlocks[i]) {
// Switch handlers do not constrain types in any way.
continue;
}
auto tagSig = wasm.getTag(handlerTags[i])->type.getSignature();
// The types sent on suspensions with this tag must remain subtypes of the
// types expected at the target block.
auto expected = self()->findBreakTarget(handlerBlocks[i])->type;
assert(tagSig.params.size() + 1 == expected.size());
for (Index j = 0; j < tagSig.params.size(); ++j) {
self()->noteSubtype(tagSig.params[i], expected[i]);
}
auto nextSig = expected[expected.size() - 1]
.getHeapType()
.getContinuation()
.type.getSignature();
// The types we send to the next continuation must remain subtypes of the
// types the continuation is expecting based on the tag results.
self()->noteSubtype(nextSig.params, tagSig.results);
// The types returned by the current continuation must remain subtypes of
// the types returned by the next continuation.
self()->noteSubtype(contSig.results, nextSig.results);
}
}
void visitResume(Resume* curr) {
if (!curr->cont->type.isContinuation()) {
return;
}
processResumeHandlers(
curr->cont->type, curr->handlerTags, curr->handlerBlocks);
// The types we send to the resumed continuation must remain subtypes of the
// types expected by the continuation.
auto params = curr->cont->type.getHeapType()
.getContinuation()
.type.getSignature()
.params;
assert(curr->operands.size() == params.size());
for (Index i = 0; i < curr->operands.size(); ++i) {
self()->noteSubtype(curr->operands[i], params[i]);
}
}
void visitResumeThrow(ResumeThrow* curr) {
WASM_UNREACHABLE("not implemented");
if (!curr->cont->type.isContinuation()) {
return;
}
processResumeHandlers(
curr->cont->type, curr->handlerTags, curr->handlerBlocks);
// The types we use to create the exception package must remain subtypes of
// the types expected by the exception tag.
auto params =
self()->getModule()->getTag(curr->tag)->type.getSignature().params;
assert(curr->operands.size() == params.size());
for (Index i = 0; i < curr->operands.size(); ++i) {
self()->noteSubtype(curr->operands[i], params[i]);
}
}
void visitStackSwitch(StackSwitch* curr) {
WASM_UNREACHABLE("not implemented");
if (!curr->cont->type.isContinuation()) {
return;
}
// The types sent when switching must remain subtypes of the types expected
// by the target continuation.
auto contSig =
curr->cont->type.getHeapType().getContinuation().type.getSignature();
assert(curr->operands.size() + 1 == contSig.params.size());
for (Index i = 0; i < curr->operands.size(); ++i) {
self()->noteSubtype(curr->operands[i], contSig.params[i]);
}
// The type returned by the target continuation must remain a subtype of the
// type the current continuation returns as indicated by the tag result.
auto currResult =
self()->getModule()->getTag(curr->tag)->type.getSignature().results;
self()->noteSubtype(contSig.results, currResult);
// The type returned by the current continuation must remain a subtype of
// the type returned by the return continuation.
auto retSig = contSig.params[contSig.params.size() - 1]
.getHeapType()
.getContinuation()
.type.getSignature();
self()->noteSubtype(currResult, retSig.results);
}
};

Expand Down
1 change: 1 addition & 0 deletions src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,7 @@ IRBuilder::makeResume(HeapType ct,
return Err{"the sizes of tags and labels must be equal"};
}
if (!ct.isContinuation()) {
std::cout << ct.toString() << "\n";
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
std::cout << ct.toString() << "\n";

return Err{"expected continuation type"};
}

Expand Down
Loading
Loading