Skip to content

Commit a2d01d3

Browse files
authored
Handle stack switching in Unsubtyping (#7608)
Add the rather complicated subtyping requirements to SubtypingDiscoverer and test that unsubtyping preserves the necessary subtype relationships.
1 parent 45b7110 commit a2d01d3

File tree

3 files changed

+599
-6
lines changed

3 files changed

+599
-6
lines changed

scripts/test/fuzzing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
'coalesce-locals-stack-switching.wast',
110110
'dce-stack-switching.wast',
111111
'precompute-stack-switching.wast',
112+
'unsubtyping-stack-switching.wast',
112113
'vacuum-stack-switching.wast',
113114
# TODO: fuzzer support for custom descriptors
114115
'custom-descriptors.wast',

src/ir/subtype-exprs.h

Lines changed: 118 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -412,15 +412,127 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
412412
void visitStringWTF16Get(StringWTF16Get* curr) {}
413413
void visitStringSliceWTF(StringSliceWTF* curr) {}
414414

415-
void visitContNew(ContNew* curr) { WASM_UNREACHABLE("not implemented"); }
416-
void visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
417-
void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("not implemented"); }
418-
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
415+
void visitContNew(ContNew* curr) {
416+
if (!curr->type.isContinuation()) {
417+
return;
418+
}
419+
// The type of the function reference must remain a subtype of the function
420+
// type expected by the continuation.
421+
self()->noteSubtype(curr->func->type.getHeapType(),
422+
curr->type.getHeapType().getContinuation().type);
423+
}
424+
void visitContBind(ContBind* curr) {
425+
if (!curr->cont->type.isContinuation()) {
426+
return;
427+
}
428+
// Each of the bound arguments must remain subtypes of their expected
429+
// parameters.
430+
auto params = curr->cont->type.getHeapType()
431+
.getContinuation()
432+
.type.getSignature()
433+
.params;
434+
assert(curr->operands.size() <= params.size());
435+
for (Index i = 0; i < curr->operands.size(); ++i) {
436+
self()->noteSubtype(curr->operands[i], params[i]);
437+
}
438+
}
439+
void visitSuspend(Suspend* curr) {
440+
// The operands must remain subtypes of the parameters given by the tag.
441+
auto params =
442+
self()->getModule()->getTag(curr->tag)->type.getSignature().params;
443+
assert(curr->operands.size() == params.size());
444+
for (Index i = 0; i < curr->operands.size(); ++i) {
445+
self()->noteSubtype(curr->operands[i], params[i]);
446+
}
447+
}
448+
void processResumeHandlers(Type contType,
449+
const ArenaVector<Name>& handlerTags,
450+
const ArenaVector<Name>& handlerBlocks) {
451+
auto contSig = contType.getHeapType().getContinuation().type.getSignature();
452+
assert(handlerTags.size() == handlerBlocks.size());
453+
auto& wasm = *self()->getModule();
454+
// Process each handler in turn.
455+
for (Index i = 0; i < handlerTags.size(); ++i) {
456+
if (!handlerBlocks[i]) {
457+
// Switch handlers do not constrain types in any way.
458+
continue;
459+
}
460+
auto tagSig = wasm.getTag(handlerTags[i])->type.getSignature();
461+
// The types sent on suspensions with this tag must remain subtypes of the
462+
// types expected at the target block.
463+
auto expected = self()->findBreakTarget(handlerBlocks[i])->type;
464+
assert(tagSig.params.size() + 1 == expected.size());
465+
for (Index j = 0; j < tagSig.params.size(); ++j) {
466+
self()->noteSubtype(tagSig.params[i], expected[i]);
467+
}
468+
auto nextSig = expected[expected.size() - 1]
469+
.getHeapType()
470+
.getContinuation()
471+
.type.getSignature();
472+
// The types we send to the next continuation must remain subtypes of the
473+
// types the continuation is expecting based on the tag results.
474+
self()->noteSubtype(nextSig.params, tagSig.results);
475+
// The types returned by the current continuation must remain subtypes of
476+
// the types returned by the next continuation.
477+
self()->noteSubtype(contSig.results, nextSig.results);
478+
}
479+
}
480+
void visitResume(Resume* curr) {
481+
if (!curr->cont->type.isContinuation()) {
482+
return;
483+
}
484+
processResumeHandlers(
485+
curr->cont->type, curr->handlerTags, curr->handlerBlocks);
486+
// The types we send to the resumed continuation must remain subtypes of the
487+
// types expected by the continuation.
488+
auto params = curr->cont->type.getHeapType()
489+
.getContinuation()
490+
.type.getSignature()
491+
.params;
492+
assert(curr->operands.size() == params.size());
493+
for (Index i = 0; i < curr->operands.size(); ++i) {
494+
self()->noteSubtype(curr->operands[i], params[i]);
495+
}
496+
}
419497
void visitResumeThrow(ResumeThrow* curr) {
420-
WASM_UNREACHABLE("not implemented");
498+
if (!curr->cont->type.isContinuation()) {
499+
return;
500+
}
501+
processResumeHandlers(
502+
curr->cont->type, curr->handlerTags, curr->handlerBlocks);
503+
// The types we use to create the exception package must remain subtypes of
504+
// the types expected by the exception tag.
505+
auto params =
506+
self()->getModule()->getTag(curr->tag)->type.getSignature().params;
507+
assert(curr->operands.size() == params.size());
508+
for (Index i = 0; i < curr->operands.size(); ++i) {
509+
self()->noteSubtype(curr->operands[i], params[i]);
510+
}
421511
}
422512
void visitStackSwitch(StackSwitch* curr) {
423-
WASM_UNREACHABLE("not implemented");
513+
if (!curr->cont->type.isContinuation()) {
514+
return;
515+
}
516+
// The types sent when switching must remain subtypes of the types expected
517+
// by the target continuation.
518+
auto contSig =
519+
curr->cont->type.getHeapType().getContinuation().type.getSignature();
520+
assert(curr->operands.size() + 1 == contSig.params.size());
521+
for (Index i = 0; i < curr->operands.size(); ++i) {
522+
self()->noteSubtype(curr->operands[i], contSig.params[i]);
523+
}
524+
// The type returned by the target continuation must remain a subtype of the
525+
// type the current continuation returns as indicated by the tag result.
526+
auto currResult =
527+
self()->getModule()->getTag(curr->tag)->type.getSignature().results;
528+
self()->noteSubtype(contSig.results, currResult);
529+
// The type returned by the current continuation must remain a subtype of
530+
// the type returned by the return continuation.
531+
auto retSig = contSig.params[contSig.params.size() - 1]
532+
.getHeapType()
533+
.getContinuation()
534+
.type.getSignature();
535+
self()->noteSubtype(currResult, retSig.results);
424536
}
425537
};
426538

0 commit comments

Comments
 (0)