Skip to content

Commit 840198e

Browse files
Merge pull request #73275 from nate-chandler/lifetime-completion/enable
[LifetimeCompletion] Enable.
2 parents b74303c + bff6c74 commit 840198e

File tree

6 files changed

+318
-31
lines changed

6 files changed

+318
-31
lines changed

include/swift/AST/SILOptions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ class SILOptions {
180180
/// If set to true, compile with the SIL Opaque Values enabled.
181181
bool EnableSILOpaqueValues = false;
182182

183-
/// Require linear OSSA lifetimes after SILGen
184-
bool OSSACompleteLifetimes = false;
183+
/// Introduce linear OSSA lifetimes after SILGen
184+
bool OSSACompleteLifetimes = true;
185185

186186
/// Verify linear OSSA lifetimes after SILGen
187187
bool OSSAVerifyComplete = false;

lib/DriverTool/sil_opt_main.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ struct SILOptOptions {
231231
EnableSILOpaqueValues = llvm::cl::opt<bool>("enable-sil-opaque-values",
232232
llvm::cl::desc("Compile the module with sil-opaque-values enabled."));
233233

234-
llvm::cl::opt<bool>
235-
EnableOSSACompleteLifetimes = llvm::cl::opt<bool>("enable-ossa-complete-lifetimes",
236-
llvm::cl::desc("Require linear OSSA lifetimes after SILGenCleanup."));
234+
llvm::cl::opt<bool> EnableOSSACompleteLifetimes = llvm::cl::opt<bool>(
235+
"enable-ossa-complete-lifetimes", llvm::cl::init(true),
236+
llvm::cl::desc("Require linear OSSA lifetimes after SILGenCleanup."));
237237
llvm::cl::opt<bool>
238238
EnableOSSAVerifyComplete = llvm::cl::opt<bool>("enable-ossa-verify-complete",
239239
llvm::cl::desc("Verify linear OSSA lifetimes after SILGenCleanup."));

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,22 @@
1414

1515
#include "swift/Basic/Assertions.h"
1616
#include "swift/Basic/Defer.h"
17+
#include "swift/SIL/BasicBlockDatastructures.h"
1718
#include "swift/SIL/DebugUtils.h"
1819
#include "swift/SIL/InstructionUtils.h"
1920
#include "swift/SIL/PrunedLiveness.h"
2021
#include "swift/SIL/SILArgument.h"
2122
#include "swift/SIL/SILBuilder.h"
2223
#include "swift/SIL/SILInstruction.h"
2324
#include "swift/SIL/SILValue.h"
24-
#include "swift/SIL/BasicBlockDatastructures.h"
2525
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
26+
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
2627
#include "swift/SILOptimizer/PassManager/Passes.h"
2728
#include "swift/SILOptimizer/PassManager/Transforms.h"
2829
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
2930
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
30-
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
3131
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
32+
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
3233
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
3334
#include "swift/SILOptimizer/Utils/StackNesting.h"
3435

@@ -445,14 +446,6 @@ static BuiltinInst *getEndAsyncLet(BuiltinInst *startAsyncLet) {
445446
/// a closure is used by \p closureUser.
446447
static void insertAfterClosureUser(SILInstruction *closureUser,
447448
function_ref<void(SILBuilder &)> insertFn) {
448-
// Don't insert any destroy or deallocation right before an unreachable.
449-
// It's not needed an will only add up to code size.
450-
auto insertAtNonUnreachable = [&](SILBuilder &builder) {
451-
if (isa<UnreachableInst>(builder.getInsertionPoint()))
452-
return;
453-
insertFn(builder);
454-
};
455-
456449
{
457450
SILInstruction *userForBorrow = closureUser;
458451
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(userForBorrow))
@@ -468,7 +461,7 @@ static void insertAfterClosureUser(SILInstruction *closureUser,
468461

469462
for (auto eb : endBorrows) {
470463
SILBuilderWithScope builder(std::next(eb->getIterator()));
471-
insertAtNonUnreachable(builder);
464+
insertFn(builder);
472465
}
473466
return;
474467
}
@@ -479,12 +472,12 @@ static void insertAfterClosureUser(SILInstruction *closureUser,
479472
if (!endAsyncLet)
480473
return;
481474
SILBuilderWithScope builder(std::next(endAsyncLet->getIterator()));
482-
insertAtNonUnreachable(builder);
475+
insertFn(builder);
483476
return;
484477
}
485478
FullApplySite fas = FullApplySite::isa(closureUser);
486479
assert(fas);
487-
fas.insertAfterApplication(insertAtNonUnreachable);
480+
fas.insertAfterApplication(insertFn);
488481
}
489482

490483
static SILValue skipConvert(SILValue v) {
@@ -1000,6 +993,7 @@ static SILValue tryRewriteToPartialApplyStack(
1000993

1001994
static bool tryExtendLifetimeToLastUse(
1002995
ConvertEscapeToNoEscapeInst *cvt, DominanceAnalysis *dominanceAnalysis,
996+
DeadEndBlocksAnalysis *deadEndBlocksAnalysis,
1003997
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
1004998
llvm::DenseSet<SILBasicBlock *> &unreachableBlocks,
1005999
InstructionDeleter &deleter, const bool &modifiedCFG) {
@@ -1048,10 +1042,22 @@ static bool tryExtendLifetimeToLastUse(
10481042
cvt->setLifetimeGuaranteed();
10491043
cvt->setOperand(closureCopy);
10501044

1051-
insertAfterClosureUser(singleUser, [closureCopy](SILBuilder &builder) {
1052-
auto loc = RegularLocation(builder.getInsertionPointLoc());
1053-
builder.createDestroyValue(loc, closureCopy);
1054-
});
1045+
auto *function = cvt->getFunction();
1046+
// The CFG may have been modified during this run, which would have made
1047+
// dead-end blocks analysis invalid. Mark it invalid it now if that
1048+
// happened. If the CFG hasn't been modified, this is a noop thanks to
1049+
// DeadEndBlocksAnalysis::shouldInvalidate.
1050+
deadEndBlocksAnalysis->invalidate(function,
1051+
analysisInvalidationKind(modifiedCFG));
1052+
auto *deadEndBlocks = deadEndBlocksAnalysis->get(function);
1053+
1054+
insertAfterClosureUser(
1055+
singleUser, [closureCopy, deadEndBlocks](SILBuilder &builder) {
1056+
auto loc = RegularLocation(builder.getInsertionPointLoc());
1057+
auto isDeadEnd = IsDeadEnd_t(
1058+
deadEndBlocks->isDeadEnd(builder.getInsertionPoint()->getParent()));
1059+
builder.createDestroyValue(loc, closureCopy, DontPoisonRefs, isDeadEnd);
1060+
});
10551061
/*
10561062
llvm::errs() << "after lifetime extension of\n";
10571063
escapingClosure->dump();
@@ -1440,6 +1446,7 @@ static void computeUnreachableBlocks(
14401446

14411447
static bool fixupClosureLifetimes(SILFunction &fn,
14421448
DominanceAnalysis *dominanceAnalysis,
1449+
DeadEndBlocksAnalysis *deadEndBlocksAnalysis,
14431450
bool &checkStackNesting, bool &modifiedCFG) {
14441451
bool changed = false;
14451452

@@ -1476,7 +1483,8 @@ static bool fixupClosureLifetimes(SILFunction &fn,
14761483
}
14771484
}
14781485

1479-
if (tryExtendLifetimeToLastUse(cvt, dominanceAnalysis, memoizedQueries,
1486+
if (tryExtendLifetimeToLastUse(cvt, dominanceAnalysis,
1487+
deadEndBlocksAnalysis, memoizedQueries,
14801488
unreachableBlocks, updater.getDeleter(),
14811489
/*const*/ modifiedCFG)) {
14821490
changed = true;
@@ -1516,9 +1524,11 @@ class ClosureLifetimeFixup : public SILFunctionTransform {
15161524
bool modifiedCFG = false;
15171525

15181526
auto *dominanceAnalysis = PM->getAnalysis<DominanceAnalysis>();
1527+
auto *deadEndBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
15191528

15201529
if (fixupClosureLifetimes(*getFunction(), dominanceAnalysis,
1521-
checkStackNesting, modifiedCFG)) {
1530+
deadEndBlocksAnalysis, checkStackNesting,
1531+
modifiedCFG)) {
15221532
updateBorrowedFrom(getPassManager(), getFunction());
15231533
if (checkStackNesting){
15241534
modifiedCFG |=

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ void CanonicalizeOSSALifetime::findExtendedBoundary(
943943
/// record it as a final consume.
944944
static void
945945
insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
946-
SILValue currentDef,
946+
SILValue currentDef, IsDeadEnd_t isDeadEnd,
947947
CanonicalOSSAConsumeInfo &consumes,
948948
SmallVectorImpl<DestroyValueInst *> &destroys,
949949
InstModCallbacks &callbacks) {
@@ -974,13 +974,63 @@ insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
974974
SILBuilderWithScope builder(nextInstruction);
975975
auto loc =
976976
RegularLocation::getAutoGeneratedLocation(nextInstruction->getLoc());
977-
auto *dvi = builder.createDestroyValue(loc, currentDef);
977+
auto *dvi =
978+
builder.createDestroyValue(loc, currentDef, DontPoisonRefs, isDeadEnd);
978979
callbacks.createdNewInst(dvi);
979980
consumes.recordFinalConsume(dvi);
980981
++NumDestroysGenerated;
981982
destroys.push_back(dvi);
982983
}
983984

985+
/// Whether a destroy created at \p inst should be marked [dead_end].
986+
///
987+
/// It should be if
988+
/// (1) \p inst is itself in a dead-end region
989+
/// (2) all destroys after \p inst are [dead_end]
990+
static IsDeadEnd_t
991+
isDeadEndDestroy(SILInstruction *inst,
992+
SmallPtrSetVector<SILInstruction *, 8> const &destroys,
993+
BasicBlockSet &semanticDestroysBlocks,
994+
DeadEndBlocks *deadEnds) {
995+
auto *parent = inst->getParent();
996+
if (!deadEnds->isDeadEnd(parent)) {
997+
// Only destroys in dead-ends can be non-meaningful (aka "dead end").
998+
return IsntDeadEnd;
999+
}
1000+
if (semanticDestroysBlocks.contains(parent)) {
1001+
// `parent` has a semantic destroy somewhere. Is it after `inst`?
1002+
for (auto *i = inst; i; i = i->getNextInstruction()) {
1003+
if (!destroys.contains(i)) {
1004+
continue;
1005+
}
1006+
auto *dvi = cast<DestroyValueInst>(i);
1007+
if (!dvi->isDeadEnd()) {
1008+
// Some subsequent destroy within `parent` was meaningful, so one
1009+
// created at `inst` must be too.
1010+
return IsntDeadEnd;
1011+
}
1012+
}
1013+
}
1014+
// Walk the portion of the dead-end region after `parent` to check that all
1015+
// destroys are non-meaningful.
1016+
BasicBlockWorklist worklist(inst->getFunction());
1017+
for (auto *successor : parent->getSuccessorBlocks()) {
1018+
worklist.push(successor);
1019+
}
1020+
while (auto *block = worklist.pop()) {
1021+
assert(deadEnds->isDeadEnd(block));
1022+
if (semanticDestroysBlocks.contains(block)) {
1023+
// Some subsequent destroy was meaningful, so one created at `inst`
1024+
// must be too.
1025+
return IsntDeadEnd;
1026+
}
1027+
for (auto *successor : block->getSuccessorBlocks()) {
1028+
worklist.pushIfNotVisited(successor);
1029+
}
1030+
}
1031+
return IsDeadEnd;
1032+
}
1033+
9841034
/// Inserts destroys along the boundary where needed and records all final
9851035
/// consuming uses.
9861036
///
@@ -992,6 +1042,18 @@ insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
9921042
void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
9931043
PrunedLivenessBoundary const &boundary,
9941044
SmallVectorImpl<DestroyValueInst *> &newDestroys) {
1045+
BasicBlockSet semanticDestroyBlocks(getCurrentDef()->getFunction());
1046+
for (auto *destroy : destroys) {
1047+
if (!cast<DestroyValueInst>(destroy)->isDeadEnd()) {
1048+
semanticDestroyBlocks.insert(destroy->getParent());
1049+
}
1050+
}
1051+
auto isDeadEnd = [&semanticDestroyBlocks,
1052+
this](SILInstruction *inst) -> IsDeadEnd_t {
1053+
return isDeadEndDestroy(
1054+
inst, destroys, semanticDestroyBlocks,
1055+
deadEndBlocksAnalysis->get(getCurrentDef()->getFunction()));
1056+
};
9951057
BasicBlockSet seenMergePoints(getCurrentDef()->getFunction());
9961058
for (auto *instruction : boundary.lastUsers) {
9971059
if (destroys.contains(instruction)) {
@@ -1013,7 +1075,8 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
10131075
}
10141076
auto *insertionPoint = &*successor->begin();
10151077
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1016-
consumes, newDestroys, getCallbacks());
1078+
isDeadEnd(insertionPoint), consumes,
1079+
newDestroys, getCallbacks());
10171080
LLVM_DEBUG(llvm::dbgs() << " Destroy after terminator "
10181081
<< *instruction << " at beginning of ";
10191082
successor->printID(llvm::dbgs(), false);
@@ -1022,7 +1085,8 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
10221085
continue;
10231086
}
10241087
auto *insertionPoint = instruction->getNextInstruction();
1025-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1088+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1089+
isDeadEnd(insertionPoint), consumes,
10261090
newDestroys, getCallbacks());
10271091
LLVM_DEBUG(llvm::dbgs()
10281092
<< " Destroy at last use " << insertionPoint << "\n");
@@ -1031,22 +1095,25 @@ void CanonicalizeOSSALifetime::insertDestroysOnBoundary(
10311095
}
10321096
for (auto *edgeDestination : boundary.boundaryEdges) {
10331097
auto *insertionPoint = &*edgeDestination->begin();
1034-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1098+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1099+
isDeadEnd(insertionPoint), consumes,
10351100
newDestroys, getCallbacks());
10361101
LLVM_DEBUG(llvm::dbgs() << " Destroy on edge " << edgeDestination << "\n");
10371102
}
10381103
for (auto *def : boundary.deadDefs) {
10391104
if (auto *arg = dyn_cast<SILArgument>(def)) {
10401105
auto *insertionPoint = &*arg->getParent()->begin();
1041-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1106+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1107+
isDeadEnd(insertionPoint), consumes,
10421108
newDestroys, getCallbacks());
10431109
LLVM_DEBUG(llvm::dbgs()
10441110
<< " Destroy after dead def arg " << arg << "\n");
10451111
} else {
10461112
auto *instruction = cast<SILInstruction>(def);
10471113
auto *insertionPoint = instruction->getNextInstruction();
10481114
assert(insertionPoint && "def instruction was a terminator?!");
1049-
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(), consumes,
1115+
insertDestroyBeforeInstruction(insertionPoint, getCurrentDef(),
1116+
isDeadEnd(insertionPoint), consumes,
10501117
newDestroys, getCallbacks());
10511118
LLVM_DEBUG(llvm::dbgs()
10521119
<< " Destroy after dead def inst " << instruction << "\n");

0 commit comments

Comments
 (0)