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

Stack switching proposal support #7041

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 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
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
6 changes: 4 additions & 2 deletions scripts/gen-s-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,11 +596,13 @@
# Typed function references instructions
("call_ref", "makeCallRef(/*isReturn=*/false)"),
("return_call_ref", "makeCallRef(/*isReturn=*/true)"),
# Typed continuations instructions
# Stack switching instructions
("cont.new", "makeContNew()"),
("cont.bind", "makeContBind()"),
("resume", "makeResume()"),
("suspend", "makeSuspend()"),
("resume", "makeResume()"),
("resume_throw", "makeResumeThrow()"),
("switch", "makeStackSwitch()"),
# GC
("ref.i31", "makeRefI31(Unshared)"),
("ref.i31_shared", "makeRefI31(Shared)"),
Expand Down
27 changes: 22 additions & 5 deletions src/gen-s-parser.inc
Original file line number Diff line number Diff line change
Expand Up @@ -4807,12 +4807,23 @@ switch (buf[0]) {
default: goto parse_error;
}
}
case 's':
if (op == "resume"sv) {
CHECK_ERR(makeResume(ctx, pos, annotations));
return Ok{};
case 's': {
switch (buf[6]) {
case '\0':
if (op == "resume"sv) {
CHECK_ERR(makeResume(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
case '_':
if (op == "resume_throw"sv) {
CHECK_ERR(makeResumeThrow(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
default: goto parse_error;
}
goto parse_error;
}
case 't': {
switch (buf[3]) {
case 'h':
Expand Down Expand Up @@ -5076,6 +5087,12 @@ switch (buf[0]) {
return Ok{};
}
goto parse_error;
case 'w':
if (op == "switch"sv) {
CHECK_ERR(makeStackSwitch(ctx, pos, annotations));
return Ok{};
}
goto parse_error;
default: goto parse_error;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/ir/ReFinalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@ void ReFinalize::visitStringWTF16Get(StringWTF16Get* curr) { curr->finalize(); }
void ReFinalize::visitStringSliceWTF(StringSliceWTF* curr) { curr->finalize(); }
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::visitResume(Resume* curr) { curr->finalize(); }
void ReFinalize::visitResumeThrow(ResumeThrow* curr) { curr->finalize(); }
void ReFinalize::visitStackSwitch(StackSwitch* curr) { curr->finalize(); }

void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); }
void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); }
Expand Down
11 changes: 10 additions & 1 deletion src/ir/branch-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,14 @@ void operateOnScopeNameUsesAndSentTypes(Expression* expr, T func) {
} else if (auto* r = expr->dynCast<Resume>()) {
for (Index i = 0; i < r->handlerTags.size(); i++) {
auto dest = r->handlerTags[i];
if (dest == name) {
if (dest == name && !r->onTags[i]) {
func(name, r->sentTypes[i]);
}
}
} else if (auto* r = expr->dynCast<ResumeThrow>()) {
for (Index i = 0; i < r->handlerTags.size(); i++) {
auto dest = r->handlerTags[i];
if (dest == name && !r->onTags[i]) {
func(name, r->sentTypes[i]);
}
}
Expand Down Expand Up @@ -118,6 +125,8 @@ void operateOnScopeNameUsesAndSentValues(Expression* expr, T func) {
// The values are supplied by suspend instructions executed while running
// the continuation, so we are unable to know what they will be here.
func(name, nullptr);
} else if (expr->is<ResumeThrow>()) {
func(name, nullptr);
} else {
assert(expr->is<Try>() || expr->is<Rethrow>()); // delegate or rethrow
}
Expand Down
25 changes: 22 additions & 3 deletions src/ir/child-typer.h
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,10 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
note(&curr->end, Type::i32);
}

void visitContNew(ContNew* curr) {
note(&curr->func, Type(curr->contType.getContinuation().type, Nullable));
}

void visitContBind(ContBind* curr) {
auto paramsBefore =
curr->contTypeBefore.getContinuation().type.getSignature().params;
Expand All @@ -1064,8 +1068,12 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
note(&curr->cont, Type(curr->contTypeBefore, Nullable));
}

void visitContNew(ContNew* curr) {
note(&curr->func, Type(curr->contType.getContinuation().type, Nullable));
void visitSuspend(Suspend* curr) {
auto params = wasm.getTag(curr->tag)->sig.params;
assert(params.size() == curr->operands.size());
for (size_t i = 0; i < params.size(); ++i) {
note(&curr->operands[i], params[i]);
}
}

void visitResume(Resume* curr) {
Expand All @@ -1077,12 +1085,23 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
note(&curr->cont, Type(curr->contType, Nullable));
}

void visitSuspend(Suspend* curr) {
void visitResumeThrow(ResumeThrow* curr) {
auto params = wasm.getTag(curr->tag)->sig.params;
assert(params.size() == curr->operands.size());
for (size_t i = 0; i < params.size(); ++i) {
note(&curr->operands[i], params[i]);
}
note(&curr->cont, Type(curr->contType, Nullable));
}

void visitStackSwitch(StackSwitch* curr) {
auto params = curr->contType.getContinuation().type.getSignature().params;
assert(params.size() >= 1 &&
((params.size() - 1) == curr->operands.size()));
for (size_t i = 0; i < params.size() - 1; ++i) {
note(&curr->operands[i], params[i]);
}
note(&curr->cont, Type(curr->contType, Nullable));
}
};

Expand Down
26 changes: 21 additions & 5 deletions src/ir/cost.h
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,10 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
return 8 + visit(curr->ref) + visit(curr->start) + visit(curr->end);
}

CostType visitContNew(ContNew* curr) {
// Some arbitrary "high" value, reflecting that this may allocate a stack
return 14 + visit(curr->func);
}
CostType visitContBind(ContBind* curr) {
// Inspired by struct.new: The only cost of cont.bind is that it may need to
// allocate a buffer to hold the arguments.
Expand All @@ -766,9 +770,12 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
}
return ret;
}
CostType visitContNew(ContNew* curr) {
// Some arbitrary "high" value, reflecting that this may allocate a stack
return 14 + visit(curr->func);
CostType visitSuspend(Suspend* curr) {
CostType ret = 12;
for (auto* arg : curr->operands) {
ret += visit(arg);
}
return ret;
}
CostType visitResume(Resume* curr) {
// Inspired by indirect calls, but twice the cost.
Expand All @@ -778,8 +785,17 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> {
}
return ret;
}
CostType visitSuspend(Suspend* curr) {
CostType ret = 12;
CostType visitResumeThrow(ResumeThrow* curr) {
// Inspired by indirect calls, but twice the cost.
CostType ret = 12 + visit(curr->cont);
for (auto* arg : curr->operands) {
ret += visit(arg);
}
return ret;
}
CostType visitStackSwitch(StackSwitch* curr) {
// Inspired by indirect calls, but twice the cost.
CostType ret = 12 + visit(curr->cont);
for (auto* arg : curr->operands) {
ret += visit(arg);
}
Expand Down
36 changes: 30 additions & 6 deletions src/ir/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -1008,13 +1008,21 @@ class EffectAnalyzer {
// traps when ref is null.
parent.implicitTrap = true;
}
void visitContNew(ContNew* curr) {
// traps when curr->func is null ref.
parent.implicitTrap = true;
}
void visitContBind(ContBind* curr) {
// traps when curr->cont is null ref.
parent.implicitTrap = true;
}
void visitContNew(ContNew* curr) {
// traps when curr->func is null ref.
parent.implicitTrap = 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;
}
}
void visitResume(Resume* curr) {
// This acts as a kitchen sink effect.
Expand All @@ -1028,10 +1036,26 @@ 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.
void visitResumeThrow(ResumeThrow* curr) {
// This acts as a kitchen sink effect.
parent.calls = true;

// resume_throw instructions accept nullable continuation
// references and trap on null.
parent.implicitTrap = true;
dhil marked this conversation as resolved.
Show resolved Hide resolved

if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
parent.throws_ = true;
}
}
void visitStackSwitch(StackSwitch* curr) {
// This acts as a kitchen sink effect.
parent.calls = true;

// switch instructions accept nullable continuation references
// and trap on null.
parent.implicitTrap = true;

if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) {
parent.throws_ = true;
}
Expand Down
12 changes: 10 additions & 2 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1225,19 +1225,27 @@ struct InfoCollector

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

void visitContNew(ContNew* curr) {
// TODO: optimize when possible
addRoot(curr);
}
void visitContBind(ContBind* curr) {
// TODO: optimize when possible
addRoot(curr);
}
void visitContNew(ContNew* curr) {
void visitSuspend(Suspend* curr) {
// TODO: optimize when possible
addRoot(curr);
}
void visitResume(Resume* curr) {
// TODO: optimize when possible
addRoot(curr);
}
void visitSuspend(Suspend* curr) {
void visitResumeThrow(ResumeThrow* curr) {
// TODO: optimize when possible
addRoot(curr);
}
void visitStackSwitch(StackSwitch* curr) {
// TODO: optimize when possible
addRoot(curr);
}
Expand Down
10 changes: 8 additions & 2 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,16 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
void visitStringWTF16Get(StringWTF16Get* curr) {}
void visitStringSliceWTF(StringSliceWTF* curr) {}

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 visitContBind(ContBind* curr) { WASM_UNREACHABLE("not implemented"); }
void visitSuspend(Suspend* curr) { WASM_UNREACHABLE("not implemented"); }
void visitResume(Resume* curr) { WASM_UNREACHABLE("not implemented"); }
void visitResumeThrow(ResumeThrow* curr) {
WASM_UNREACHABLE("not implemented");
}
void visitStackSwitch(StackSwitch* curr) {
WASM_UNREACHABLE("not implemented");
}
};

} // namespace wasm
Expand Down
Loading
Loading