Skip to content

Commit 3e2bcc0

Browse files
authored
Optimize resume of non-suspending continuations (#9071)
Add a `visitResume` in OptimizeInstructions that does the normal optimizations on null continuations and then tries to turn resumes into calls. We can do this when we are resuming a freshly allocated continuation created with a reference to a known function that GlobalEffects tells us will not suspend.
1 parent 5d93aba commit 3e2bcc0

2 files changed

Lines changed: 1196 additions & 0 deletions

File tree

src/passes/OptimizeInstructions.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,65 @@ struct OptimizeInstructions
14611461
}
14621462
}
14631463

1464+
void visitResume(Resume* curr) {
1465+
skipNonNullCast(curr->cont, curr);
1466+
if (trapOnNull(curr, curr->cont)) {
1467+
return;
1468+
}
1469+
if (curr->type == Type::unreachable) {
1470+
return;
1471+
}
1472+
1473+
// If this resume operates on a freshly-created continuation of an exact
1474+
// function that is known never to suspend, we can turn the resumption into
1475+
// a direct call, avoiding the continuation allocation and handler overhead.
1476+
1477+
// Continuations are single-shot, so resuming a continuation that has
1478+
// already been consumed will trap. If traps are assumed never to happen, we
1479+
// can assume this continuation will not be consumed on another path and
1480+
// look through tees and conditional branches. Otherwise, avoid looking
1481+
// through them to ensure the continuation cannot be consumed elsewhere.
1482+
auto behavior = getPassOptions().trapsNeverHappen
1483+
? Properties::FallthroughBehavior::AllowTeeBrIf
1484+
: Properties::FallthroughBehavior::NoTeeBrIf;
1485+
1486+
auto* contExpr = Properties::getFallthrough(
1487+
curr->cont, getPassOptions(), *getModule(), behavior);
1488+
auto* contNew = contExpr->dynCast<ContNew>();
1489+
if (!contNew) {
1490+
return;
1491+
}
1492+
if (contNew->func->type == Type::unreachable) {
1493+
return;
1494+
}
1495+
1496+
auto* funcExpr =
1497+
Properties::getFallthrough(contNew->func, getPassOptions(), *getModule());
1498+
auto* refFunc = funcExpr->dynCast<RefFunc>();
1499+
if (!refFunc) {
1500+
return;
1501+
}
1502+
1503+
auto* target = getModule()->getFunctionOrNull(refFunc->func);
1504+
if (!target || target->imported()) {
1505+
return;
1506+
}
1507+
1508+
if (!target->effects || target->effects->suspends) {
1509+
return;
1510+
}
1511+
1512+
auto* block =
1513+
ChildLocalizer(curr, getFunction(), *getModule(), getPassOptions())
1514+
.getChildrenReplacement();
1515+
Builder builder(*getModule());
1516+
Type results = target->getResults();
1517+
block->list.push_back(
1518+
builder.makeCall(target->name, curr->operands, results));
1519+
block->type = results;
1520+
replaceCurrent(block);
1521+
}
1522+
14641523
// Note on removing casts (which the following utilities, skipNonNullCast and
14651524
// skipCast do): removing a cast is potentially dangerous, as it removes
14661525
// information from the IR. For example:

0 commit comments

Comments
 (0)