Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
14af574
Stack switching proposal support
dhil Nov 4, 2024
edbc438
Address the 'easy' bits of Thomas' feedback
dhil Nov 5, 2024
c0d8648
Deduplicate the parsing logic for resume tables
dhil Nov 5, 2024
98f6314
Use inherited the 'type' member to track the continuation type in
dhil Nov 5, 2024
c407262
Format
dhil Nov 5, 2024
15fcbc6
Remove PrintSExpression::visit{Resume,ResumeThrow,StackSwitch}
dhil Nov 6, 2024
d1866bc
Don't (re)compute sentTypes for resume and resume_throw.
dhil Nov 6, 2024
2b06c76
[fuzz] Skip stack switching tests.
dhil Nov 6, 2024
3b339f9
Abstract shared logic for reading resume tables.
dhil Nov 6, 2024
0886451
Encode on-clauses using only two parallel vectors.
dhil Nov 6, 2024
d76f260
Return resumetable rather than taking it as a reference.
dhil Nov 7, 2024
1c2ed8a
Revert "Don't (re)compute sentTypes for resume and resume_throw."
dhil Nov 7, 2024
c8bf255
Propagate Type::unreachable in switch.
dhil Nov 7, 2024
9e16e47
Compute sentTypes for resumetables upon construction.
dhil Nov 8, 2024
157552a
WIP
dhil Nov 11, 2024
480843d
Check for unreachable continuation types.
dhil Nov 12, 2024
c8d345a
Compute resume table sent types during expression construction.
dhil Nov 13, 2024
0110dd5
Add switch lit test
dhil Nov 13, 2024
f418774
Fix unreachable type printing
dhil Nov 18, 2024
3bedcb5
Merge
dhil Nov 20, 2024
b1d65ce
Read sentTypes off label types.
dhil Nov 21, 2024
c549cd7
[WIP] Eliminate type immediate members.
dhil Nov 28, 2024
6a20680
Merge with upstream
dhil Nov 28, 2024
2a3fabe
Fix lint
dhil Dec 2, 2024
d19763c
Eliminate type immediates from stack switching instructions
dhil Dec 3, 2024
0e5caf2
Merge with upstream
dhil Jan 28, 2025
640aa92
Merge branch 'main' of github.com:WebAssembly/binaryen into stack-swi…
dhil Jan 28, 2025
4b6b93a
Run update_lit_tests
dhil Jan 28, 2025
9a8bb46
Rebuild and run update_lit_tests
dhil Jan 28, 2025
1d82c8b
Format
dhil Jan 28, 2025
60aed28
Update stack switching lit tests
dhil Jan 28, 2025
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
19 changes: 9 additions & 10 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2454,7 +2454,8 @@ makeSuspend(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) {

// resumetable ::= ('(' 'on' tagidx labelidx | 'on' tagidx switch ')')*
template<typename Ctx>
Result<> makeResumeTable(Ctx& ctx, typename Ctx::OnClauseListT& resumetable) {
Result<typename Ctx::OnClauseListT> makeResumeTable(Ctx& ctx) {
auto resumetable = ctx.makeOnClauseList();
while (ctx.in.takeSExprStart("on"sv)) {
auto tag = tagidx(ctx);
CHECK_ERR(tag);
Expand All @@ -2469,7 +2470,7 @@ Result<> makeResumeTable(Ctx& ctx, typename Ctx::OnClauseListT& resumetable) {
return ctx.in.err("expected ')' at end of handler clause");
}
}
return Ok{};
return resumetable;
}

// resume ::= 'resume' typeidx resumetable
Expand All @@ -2479,11 +2480,10 @@ makeResume(Ctx& ctx, Index pos, const std::vector<Annotation>& annotations) {
auto type = typeidx(ctx);
CHECK_ERR(type);

auto resumetable = ctx.makeOnClauseList();
auto ans = makeResumeTable(ctx, resumetable);
CHECK_ERR(ans);
auto resumetable = makeResumeTable(ctx);
CHECK_ERR(resumetable);

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

// resume_throw ::= 'resume_throw' typeidx tagidx ('(' 'on' tagidx labelidx |
Expand All @@ -2497,11 +2497,10 @@ Result<> makeResumeThrow(Ctx& ctx,
auto exnTag = tagidx(ctx);
CHECK_ERR(exnTag);

auto resumetable = ctx.makeOnClauseList();
auto ans = makeResumeTable(ctx, resumetable);
CHECK_ERR(ans);
auto resumetable = makeResumeTable(ctx);
CHECK_ERR(resumetable);

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

// switch ::= 'switch' typeidx tagidx
Expand Down
33 changes: 33 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,39 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
visitExpression(curr);
}
}
void visitContNew(ContNew* curr) {
if (!maybePrintUnreachableReplacement(curr, curr->type)) {
visitExpression(curr);
}
}
void visitContBind(ContBind* curr) {
if (!maybePrintUnreachableOrNullReplacement(
curr, Type(curr->sourceType, Nullable)) ||
!maybePrintUnreachableOrNullReplacement(curr, curr->type)) {
visitExpression(curr);
}
}
void visitResume(Resume* curr) {
if (!maybePrintUnreachableOrNullReplacement(
curr, Type(curr->contType, Nullable)) ||
!maybePrintUnreachableOrNullReplacement(curr, curr->type)) {
visitExpression(curr);
}
}
void visitResumeThrow(ResumeThrow* curr) {
if (!maybePrintUnreachableOrNullReplacement(
curr, Type(curr->contType, Nullable)) ||
!maybePrintUnreachableOrNullReplacement(curr, curr->type)) {
visitExpression(curr);
}
}
void visitStackSwitch(StackSwitch* curr) {
if (!maybePrintUnreachableOrNullReplacement(
curr, Type(curr->contType, Nullable)) ||
!maybePrintUnreachableOrNullReplacement(curr, curr->type)) {
visitExpression(curr);
}
}

// Module-level visitors
void handleSignature(HeapType curr, Name name = Name());
Expand Down
47 changes: 47 additions & 0 deletions src/wasm-builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,51 @@ class Builder {
ret->finalize(&wasm);
return ret;
}
template<typename ResumeOrResumeThrow>
std::vector<Type> computeResumeTableSentTypes(ResumeOrResumeThrow* curr) {
static_assert(std::is_base_of<Resume, ResumeOrResumeThrow>::value ||
std::is_base_of<ResumeThrow, ResumeOrResumeThrow>::value);
assert(curr->handlerBlocks.size() == curr->handlerTags.size());

// Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation
// type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction
// (resume $ct ... (tag $tag $block) ... ) causes $block to receive values
// of the following types when suspending to $tag: tgp* (ref $ct') where ct'
// = (cont $ft') and ft' = [tgr*] -> [ctr*].
//
std::vector<Type> sentTypes;
sentTypes.reserve(curr->handlerTags.size());
auto contSig = curr->contType.getContinuation().type.getSignature();
auto& ctrs = contSig.params;
for (Index i = 0; i < curr->handlerTags.size(); i++) {
if (curr->handlerBlocks[i].isNull()) {
sentTypes[i] = Type::none;
continue;
}

auto& tag = curr->handlerTags[i];
auto& tagSig = wasm.getTag(tag)->sig;

auto& tgps = tagSig.params;
auto& tgrs = tagSig.results;

HeapType ftPrime{Signature(tgrs, ctrs)};
HeapType ctPrime{Continuation(ftPrime)};
Type ctPrimeRef(ctPrime, Nullability::NonNullable);

if (tgps.size() > 0) {
TypeList sentValueTypes;
sentValueTypes.reserve(tgps.size() + 1);

sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end());
sentValueTypes.push_back(ctPrimeRef);
sentTypes[i] = Type(sentValueTypes);
} else {
sentTypes[i] = ctPrimeRef;
}
}
return sentTypes;
}
Resume* makeResume(HeapType contType,
const std::vector<Name>& handlerTags,
const std::vector<Name>& handlerBlocks,
Expand All @@ -1203,6 +1248,7 @@ class Builder {
ret->contType = contType;
ret->handlerTags.set(handlerTags);
ret->handlerBlocks.set(handlerBlocks);
ret->sentTypes.set(computeResumeTableSentTypes<Resume>(ret));
ret->operands.set(operands);
ret->cont = cont;
ret->finalize(&wasm);
Expand All @@ -1219,6 +1265,7 @@ class Builder {
ret->tag = tag;
ret->handlerTags.set(handlerTags);
ret->handlerBlocks.set(handlerBlocks);
ret->sentTypes.set(computeResumeTableSentTypes<ResumeThrow>(ret));
ret->operands.set(operands);
ret->cont = cont;
ret->finalize(&wasm);
Expand Down
6 changes: 5 additions & 1 deletion src/wasm/wasm-ir-builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1953,8 +1953,9 @@ IRBuilder::makeResume(HeapType ct,
return Err{"expected continuation type"};
}
Resume curr(wasm.allocator);
auto contSig = ct.getContinuation().type.getSignature();
curr.contType = ct;
curr.operands.resize(ct.getContinuation().type.getSignature().params.size());
curr.operands.resize(contSig.params.size());
CHECK_ERR(visitResume(&curr));

std::vector<Name> labelNames;
Expand All @@ -1979,6 +1980,9 @@ IRBuilder::makeResumeThrow(HeapType ct,
Name tag,
const std::vector<Name>& tags,
const std::vector<std::optional<Index>>& labels) {
if (tags.size() != labels.size()) {
return Err{"the sizes of tags and labels must be equal"};
}
if (!ct.isContinuation()) {
return Err{"expected continuation type"};
}
Expand Down
65 changes: 47 additions & 18 deletions src/wasm/wasm-validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3485,10 +3485,12 @@ void FunctionValidator::visitContNew(ContNew* curr) {
curr,
"cont.new requires stack-switching [--enable-stack-switching]");

shouldBeTrue((curr->type.isContinuation() &&
curr->type.getHeapType().getContinuation().type.isSignature()),
curr,
"invalid type in ContNew expression");
shouldBeTrue(
(curr->type.isContinuation() &&
curr->type.getHeapType().getContinuation().type.isSignature()) ||
curr->type == Type::unreachable,
curr,
"cont.new must be annotated with a continuation type");
}

void FunctionValidator::visitContBind(ContBind* curr) {
Expand All @@ -3497,15 +3499,19 @@ void FunctionValidator::visitContBind(ContBind* curr) {
curr,
"cont.bind requires stack-switching [--enable-stack-switching]");

shouldBeTrue((curr->sourceType.isContinuation() &&
curr->sourceType.getContinuation().type.isSignature()),
curr,
"invalid first type in ContBind expression");
shouldBeTrue(
(curr->sourceType.isContinuation() &&
curr->sourceType.getContinuation().type.isSignature()) ||
Type(curr->sourceType, Nullable) == Type::unreachable,
curr,
"the first type annotation on cont.bind must be a continuation type");

shouldBeTrue((curr->type.isContinuation() &&
curr->type.getHeapType().getContinuation().type.isSignature()),
curr,
"invalid second type in ContBind expression");
shouldBeTrue(
(curr->type.isContinuation() &&
curr->type.getHeapType().getContinuation().type.isSignature()) ||
curr->type == Type::unreachable,
curr,
"the second type annotation on cont.bind must be a continuation type");
}

void FunctionValidator::visitSuspend(Suspend* curr) {
Expand All @@ -3521,10 +3527,16 @@ void FunctionValidator::visitResume(Resume* curr) {
curr,
"resume requires stack-switching [--enable-stack-switching]");

shouldBeTrue(
curr->sentTypes.size() == curr->handlerBlocks.size(),
curr,
"sentTypes cache in resume instruction has not been initialized");

shouldBeTrue((curr->contType.isContinuation() &&
curr->contType.getContinuation().type.isSignature()),
curr->contType.getContinuation().type.isSignature()) ||
curr->type == Type::unreachable,
curr,
"invalid type in Resume expression");
"resume must be annotated with a continuation type");
}

void FunctionValidator::visitResumeThrow(ResumeThrow* curr) {
Expand All @@ -3536,10 +3548,21 @@ void FunctionValidator::visitResumeThrow(ResumeThrow* curr) {
"resume_throw requires exception handling [--enable-exception-handling] "
"and stack-switching [--enable-stack-switching]");

shouldBeTrue(
curr->sentTypes.size() == curr->handlerBlocks.size(),
curr,
"sentTypes cache in resume_throw instruction has not been initialized");

shouldBeTrue((curr->contType.isContinuation() &&
curr->contType.getContinuation().type.isSignature()),
curr->contType.getContinuation().type.isSignature()) ||
curr->type == Type::unreachable,
curr,
"invalid type in ResumeThrow expression");
"resume_throw must be annotated with a continuation type");

auto* tag = getModule()->getTagOrNull(curr->tag);
if (!shouldBeTrue(!!tag, curr, "resume_throw must be annotated with a tag")) {
return;
}
}

void FunctionValidator::visitStackSwitch(StackSwitch* curr) {
Expand All @@ -3549,9 +3572,15 @@ void FunctionValidator::visitStackSwitch(StackSwitch* curr) {
"switch requires stack-switching [--enable-stack-switching]");

shouldBeTrue((curr->contType.isContinuation() &&
curr->contType.getContinuation().type.isSignature()),
curr->contType.getContinuation().type.isSignature()) ||
curr->type == Type::unreachable,
curr,
"invalid type in Switch expression");
"switch must be annotated with a continuation type");

auto* tag = getModule()->getTagOrNull(curr->tag);
if (!shouldBeTrue(!!tag, curr, "switch must be annotated with a tag")) {
return;
}
}

void FunctionValidator::visitFunction(Function* curr) {
Expand Down
86 changes: 86 additions & 0 deletions src/wasm/wasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,47 @@ void Suspend::finalize(Module* wasm) {
}
}

static void populateResumeSentTypes(Resume* curr, Module* wasm) {
if (!wasm) {
return;
}

const Signature& contSig =
curr->contType.getContinuation().type.getSignature();

// Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation
// type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume
// $ct ... (tag $tag $block) ... ) causes $block to receive values of the
// following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont
// $ft') and ft' = [tgr*] -> [ctr*].
//
auto& ctrs = contSig.results;
curr->sentTypes.clear();
curr->sentTypes.resize(curr->handlerTags.size());
for (Index i = 0; i < curr->handlerTags.size(); i++) {
auto& tag = curr->handlerTags[i];
auto& tagSig = wasm->getTag(tag)->sig;

auto& tgps = tagSig.params;
auto& tgrs = tagSig.results;

HeapType ftPrime{Signature(tgrs, ctrs)};
HeapType ctPrime{Continuation(ftPrime)};
Type ctPrimeRef(ctPrime, Nullability::NonNullable);

if (tgps.size() > 0) {
TypeList sentValueTypes;
sentValueTypes.reserve(tgps.size() + 1);

sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end());
sentValueTypes.push_back(ctPrimeRef);
curr->sentTypes[i] = Type(sentValueTypes);
} else {
curr->sentTypes[i] = ctPrimeRef;
}
}
}

void Resume::finalize(Module* wasm) {
if (cont->type == Type::unreachable) {
type = Type::unreachable;
Expand All @@ -1395,6 +1436,49 @@ void Resume::finalize(Module* wasm) {
this->contType.getContinuation().type.getSignature();
type = contSig.results;
}

populateResumeSentTypes(this, wasm);
}

static void populateResumeThrowSentTypes(ResumeThrow* curr, Module* wasm) {
if (!wasm) {
return;
}

const Signature& contSig =
curr->contType.getContinuation().type.getSignature();

// Let $tag be a tag with type [tgp*] -> [tgr*]. Let $ct be a continuation
// type (cont $ft), where $ft is [ctp*] -> [ctr*]. Then an instruction (resume
// $ct ... (tag $tag $block) ... ) causes $block to receive values of the
// following types when suspending to $tag: tgp* (ref $ct') where ct' = (cont
// $ft') and ft' = [tgr*] -> [ctr*].
//
auto& ctrs = contSig.results;
curr->sentTypes.clear();
curr->sentTypes.resize(curr->handlerTags.size());
for (Index i = 0; i < curr->handlerTags.size(); i++) {
auto& tag = curr->handlerTags[i];
auto& tagSig = wasm->getTag(tag)->sig;

auto& tgps = tagSig.params;
auto& tgrs = tagSig.results;

HeapType ftPrime{Signature(tgrs, ctrs)};
HeapType ctPrime{Continuation(ftPrime)};
Type ctPrimeRef(ctPrime, Nullability::NonNullable);

if (tgps.size() > 0) {
TypeList sentValueTypes;
sentValueTypes.reserve(tgps.size() + 1);

sentValueTypes.insert(sentValueTypes.begin(), tgps.begin(), tgps.end());
sentValueTypes.push_back(ctPrimeRef);
curr->sentTypes[i] = Type(sentValueTypes);
} else {
curr->sentTypes[i] = ctPrimeRef;
}
}
}

void ResumeThrow::finalize(Module* wasm) {
Expand All @@ -1405,6 +1489,8 @@ void ResumeThrow::finalize(Module* wasm) {
this->contType.getContinuation().type.getSignature();
type = contSig.results;
}

populateResumeThrowSentTypes(this, wasm);
}

void StackSwitch::finalize(Module* wasm) {
Expand Down
Loading
Loading