Skip to content

Commit b0df998

Browse files
committed
Fix ClosureLifetimeFixup to insert code after all paired endAsyncLetLifetimes
1 parent 7a6ee11 commit b0df998

File tree

1 file changed

+36
-39
lines changed

1 file changed

+36
-39
lines changed

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -421,27 +421,6 @@ static SILValue insertMarkDependenceForCapturedArguments(PartialApplyInst *pai,
421421
return curr;
422422
}
423423

424-
/// Returns the (single) "endAsyncLetLifetime" builtin if \p startAsyncLet is a
425-
/// "startAsyncLetWithLocalBuffer" builtin.
426-
static BuiltinInst *getEndAsyncLet(BuiltinInst *startAsyncLet) {
427-
if (startAsyncLet->getBuiltinKind() != BuiltinValueKind::StartAsyncLetWithLocalBuffer)
428-
return nullptr;
429-
430-
BuiltinInst *endAsyncLet = nullptr;
431-
for (Operand *op : startAsyncLet->getUses()) {
432-
auto *endBI = dyn_cast<BuiltinInst>(op->getUser());
433-
if (endBI && endBI->getBuiltinKind() == BuiltinValueKind::EndAsyncLetLifetime) {
434-
// At this stage of the pipeline, it's always the case that a
435-
// startAsyncLet has an endAsyncLet: that's how SILGen generates it.
436-
// Just to be on the safe side, do this check.
437-
if (endAsyncLet)
438-
return nullptr;
439-
endAsyncLet = endBI;
440-
}
441-
}
442-
return endAsyncLet;
443-
}
444-
445424
/// Call the \p insertFn with a builder at all insertion points after
446425
/// a closure is used by \p closureUser.
447426
static void insertAfterClosureUser(SILInstruction *closureUser,
@@ -467,14 +446,21 @@ static void insertAfterClosureUser(SILInstruction *closureUser,
467446
}
468447
}
469448

470-
if (auto *startAsyncLet = dyn_cast<BuiltinInst>(closureUser)) {
471-
BuiltinInst *endAsyncLet = getEndAsyncLet(startAsyncLet);
472-
if (!endAsyncLet)
473-
return;
474-
SILBuilderWithScope builder(std::next(endAsyncLet->getIterator()));
475-
insertFn(builder);
449+
// If the user is a startAsyncLet builtin, emit the code after all of the
450+
// endAsyncLetLifetime builtins.
451+
if (auto *startAsyncLet =
452+
isBuiltinInst(closureUser, BuiltinValueKind::StartAsyncLetWithLocalBuffer)) {
453+
for (Operand *op : startAsyncLet->getUses()) {
454+
auto endAsyncLet = isBuiltinInst(op->getUser(),
455+
BuiltinValueKind::EndAsyncLetLifetime);
456+
if (!endAsyncLet) continue;
457+
458+
SILBuilderWithScope builder(std::next(endAsyncLet->getIterator()));
459+
insertFn(builder);
460+
}
476461
return;
477462
}
463+
478464
FullApplySite fas = FullApplySite::isa(closureUser);
479465
assert(fas);
480466
fas.insertAfterApplication(insertFn);
@@ -1009,32 +995,43 @@ static bool tryExtendLifetimeToLastUse(
1009995
return false;
1010996

1011997
// Handle apply instructions and startAsyncLet.
1012-
BuiltinInst *endAsyncLet = nullptr;
998+
BuiltinInst *startAsyncLet = nullptr;
1013999
if (FullApplySite::isa(singleUser)) {
10141000
// TODO: Enable begin_apply/end_apply. It should work, but is not tested yet.
10151001
if (isa<BeginApplyInst>(singleUser))
10161002
return false;
1017-
} else if (auto *bi = dyn_cast<BuiltinInst>(singleUser)) {
1018-
endAsyncLet = getEndAsyncLet(bi);
1019-
if (!endAsyncLet)
1020-
return false;
1003+
} else if ((startAsyncLet = isBuiltinInst(singleUser,
1004+
BuiltinValueKind::StartAsyncLetWithLocalBuffer))) {
1005+
// continue
10211006
} else if (!isa<BeginBorrowInst>(singleUser)) {
10221007
return false;
10231008
}
10241009

10251010
if (SILValue closureOp = tryRewriteToPartialApplyStack(
10261011
cvt, singleUser, dominanceAnalysis, deleter, memoized,
10271012
reachableBlocks, /*const*/ modifiedCFG)) {
1028-
if (endAsyncLet) {
1013+
if (startAsyncLet) {
1014+
// Collect all of the endAsyncLet calls in one pass so that we can
1015+
// safely mutate the use-def chain in the second.
1016+
SmallVector<BuiltinInst*, 4> endAsyncLets;
1017+
for (auto use: startAsyncLet->getUses()) {
1018+
if (auto endAsyncLet =
1019+
isBuiltinInst(use->getUser(), BuiltinValueKind::EndAsyncLetLifetime)) {
1020+
endAsyncLets.push_back(endAsyncLet);
1021+
}
1022+
}
1023+
10291024
// Add the closure as a second operand to the endAsyncLet builtin.
10301025
// This ensures that the closure arguments are kept alive until the
10311026
// endAsyncLet builtin.
1032-
assert(endAsyncLet->getNumOperands() == 1);
1033-
SILBuilderWithScope builder(endAsyncLet);
1034-
builder.createBuiltin(endAsyncLet->getLoc(), endAsyncLet->getName(),
1035-
endAsyncLet->getType(), endAsyncLet->getSubstitutions(),
1036-
{endAsyncLet->getOperand(0), closureOp});
1037-
deleter.forceDelete(endAsyncLet);
1027+
for (auto endAsyncLet: endAsyncLets) {
1028+
assert(endAsyncLet->getNumOperands() == 1);
1029+
SILBuilderWithScope builder(endAsyncLet);
1030+
builder.createBuiltin(endAsyncLet->getLoc(), endAsyncLet->getName(),
1031+
endAsyncLet->getType(), endAsyncLet->getSubstitutions(),
1032+
{endAsyncLet->getOperand(0), closureOp});
1033+
deleter.forceDelete(endAsyncLet);
1034+
}
10381035
}
10391036
return true;
10401037
}

0 commit comments

Comments
 (0)