Skip to content

Commit 103a6fe

Browse files
committed
LinearLifetimeChecker - make DeadEndBlocks optional
1 parent 2743b83 commit 103a6fe

File tree

9 files changed

+38
-25
lines changed

9 files changed

+38
-25
lines changed

include/swift/SIL/LinearLifetimeChecker.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,23 @@ class LinearLifetimeChecker {
5757
friend class SILOwnershipVerifier;
5858
friend class SILValueOwnershipChecker;
5959

60-
DeadEndBlocks &deadEndBlocks;
60+
// TODO: migrate away from using dead end blocks for OSSA values. end_borrow
61+
// or destroy_value should ideally exist on all paths. However, deadEndBlocks
62+
// may still be useful for checking memory lifetime for address uses.
63+
DeadEndBlocks *deadEndBlocks;
6164

6265
public:
63-
LinearLifetimeChecker(DeadEndBlocks &deadEndBlocks)
66+
/// \p deadEndBlocks should be provided for lifetimes that do not require
67+
/// consuming uses on dead-end paths, which end in an unreachable terminator.
68+
/// OSSA values require consumes on all paths, so \p deadEndBlocks are *not*
69+
/// required for OSSA lifetimes. Memory lifetimes and access scopes only
70+
/// require destroys on non-dead-end paths.
71+
///
72+
/// TODO: The verifier currently requires OSSA borrow scopes to end on all
73+
/// paths. Owned OSSA lifetimes may still be missing destroys on dead-end
74+
/// paths. Once owned values are fully enforced, the same invariant will hold
75+
/// for all OSSA values.
76+
LinearLifetimeChecker(DeadEndBlocks *deadEndBlocks = nullptr)
6477
: deadEndBlocks(deadEndBlocks) {}
6578

6679
/// Returns true that \p value forms a linear lifetime with consuming uses \p

lib/SIL/Verifier/GuaranteedPhiVerifierPrivate.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class DeadEndBlocks;
8585
class GuaranteedPhiVerifier {
8686
/// A cache of dead-end basic blocks that we use to determine if we can
8787
/// ignore "leaks".
88-
DeadEndBlocks &deadEndBlocks;
88+
DeadEndBlocks *deadEndBlocks = nullptr;
8989
/// The builder that the checker uses to emit error messages, crash if asked
9090
/// for, or supply back interesting info to the caller.
9191
LinearLifetimeChecker::ErrorBuilder errorBuilder;
@@ -96,7 +96,7 @@ class GuaranteedPhiVerifier {
9696
dependentPhiToBaseValueMap;
9797

9898
public:
99-
GuaranteedPhiVerifier(const SILFunction *func, DeadEndBlocks &deadEndBlocks,
99+
GuaranteedPhiVerifier(const SILFunction *func, DeadEndBlocks *deadEndBlocks,
100100
LinearLifetimeChecker::ErrorBuilder errorBuilder)
101101
: deadEndBlocks(deadEndBlocks), errorBuilder(errorBuilder) {}
102102

lib/SIL/Verifier/LinearLifetimeChecker.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,12 @@ struct State {
161161
/// Once we have setup all of our consuming/non-consuming blocks and have
162162
/// validated that all intra-block dataflow is safe, perform the inter-block
163163
/// dataflow.
164-
void performDataflow(DeadEndBlocks &deBlocks);
164+
void performDataflow(DeadEndBlocks *deBlocks);
165165

166166
/// After we have performed the dataflow, check the end state of our dataflow
167167
/// for validity. If this is a linear typed value, return true. Return false
168168
/// otherwise.
169-
void checkDataflowEndState(DeadEndBlocks &deBlocks);
169+
void checkDataflowEndState(DeadEndBlocks *deBlocks);
170170

171171
void dumpConsumingUsers() const {
172172
llvm::errs() << "Consuming Users:\n";
@@ -410,7 +410,7 @@ void State::checkPredsForDoubleConsume(SILBasicBlock *userBlock) {
410410
// Dataflow
411411
//===----------------------------------------------------------------------===//
412412

413-
void State::performDataflow(DeadEndBlocks &deBlocks) {
413+
void State::performDataflow(DeadEndBlocks *deBlocks) {
414414
LLVM_DEBUG(llvm::dbgs() << " Beginning to check dataflow constraints\n");
415415
// Until the worklist is empty...
416416
while (!worklist.empty()) {
@@ -446,7 +446,7 @@ void State::performDataflow(DeadEndBlocks &deBlocks) {
446446

447447
// Then check if the successor is a transitively unreachable block. In
448448
// such a case, we ignore it since we are going to leak along that path.
449-
if (deBlocks.isDeadEnd(succBlock))
449+
if (deBlocks && deBlocks->isDeadEnd(succBlock))
450450
continue;
451451

452452
// Otherwise, add the successor to our SuccessorBlocksThatMustBeVisited
@@ -483,7 +483,7 @@ void State::performDataflow(DeadEndBlocks &deBlocks) {
483483
}
484484
}
485485

486-
void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
486+
void State::checkDataflowEndState(DeadEndBlocks *deBlocks) {
487487
if (!successorBlocksThatMustBeVisited.empty()) {
488488
// If we are asked to store any leaking blocks, put them in the leaking
489489
// blocks array.
@@ -518,7 +518,7 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
518518
// be a use-before-def or a use-after-free.
519519
for (auto pair : blocksWithNonConsumingUses.getRange()) {
520520
auto *block = pair.first;
521-
if (deBlocks.isDeadEnd(block)) {
521+
if (deBlocks && deBlocks->isDeadEnd(block)) {
522522
continue;
523523
}
524524

@@ -601,7 +601,7 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl(
601601
for (auto *use : nonConsumingUses) {
602602
auto *useParent = use->getUser()->getParent();
603603
if (useParent == value->getParentBlock() ||
604-
deadEndBlocks.isDeadEnd(useParent)) {
604+
(deadEndBlocks && deadEndBlocks->isDeadEnd(useParent))) {
605605
continue;
606606
}
607607

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,11 +2551,11 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
25512551
bool success = useKind == AddressUseKind::NonEscaping;
25522552

25532553
require(!success || checkScopedAddressUses(
2554-
scopedAddress, &scopedAddressLiveness, &DEBlocks),
2554+
scopedAddress, &scopedAddressLiveness, DEBlocks.get()),
25552555
"Ill formed store_borrow scope");
25562556

25572557
require(!success || !hasOtherStoreBorrowsInLifetime(
2558-
SI, &scopedAddressLiveness, &DEBlocks),
2558+
SI, &scopedAddressLiveness, DEBlocks.get()),
25592559
"A store_borrow cannot be nested within another "
25602560
"store_borrow to its destination");
25612561

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ static bool fixupReferenceCounts(
130130
auto *stackLoc = builder.createAllocStack(loc, v->getType().getObjectType());
131131
builder.createCopyAddr(loc, v, stackLoc, IsNotTake, IsInitialization);
132132

133-
LinearLifetimeChecker checker(deadEndBlocks);
133+
LinearLifetimeChecker checker(&deadEndBlocks);
134134
bool consumedInLoop = checker.completeConsumingUseSet(
135135
pai, applySite.getCalleeOperand(),
136136
[&](SILBasicBlock::iterator insertPt) {
@@ -173,7 +173,7 @@ static bool fixupReferenceCounts(
173173
// just cares about the block the value is in. In a forthcoming commit, I
174174
// am going to change this to use a different API on the linear lifetime
175175
// checker that makes this clearer.
176-
LinearLifetimeChecker checker(deadEndBlocks);
176+
LinearLifetimeChecker checker(&deadEndBlocks);
177177
bool consumedInLoop = checker.completeConsumingUseSet(
178178
pai, applySite.getCalleeOperand(),
179179
[&](SILBasicBlock::iterator insertPt) {
@@ -220,7 +220,7 @@ static bool fixupReferenceCounts(
220220
// just cares about the block the value is in. In a forthcoming commit, I
221221
// am going to change this to use a different API on the linear lifetime
222222
// checker that makes this clearer.
223-
LinearLifetimeChecker checker(deadEndBlocks);
223+
LinearLifetimeChecker checker(&deadEndBlocks);
224224
checker.completeConsumingUseSet(
225225
pai, applySite.getCalleeOperand(),
226226
[&](SILBasicBlock::iterator insertPt) {
@@ -257,7 +257,7 @@ static bool fixupReferenceCounts(
257257
// just cares about the block the value is in. In a forthcoming commit, I
258258
// am going to change this to use a different API on the linear lifetime
259259
// checker that makes this clearer.
260-
LinearLifetimeChecker checker(deadEndBlocks);
260+
LinearLifetimeChecker checker(&deadEndBlocks);
261261
checker.completeConsumingUseSet(
262262
pai, applySite.getCalleeOperand(),
263263
[&](SILBasicBlock::iterator insertPt) {

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,7 +1197,7 @@ void AvailableValueAggregator::addHandOffCopyDestroysForPhis(
11971197
// Then perform the linear lifetime check. If we succeed, continue. We have
11981198
// no further work to do.
11991199
auto *loadOperand = &load->getAllOperands()[0];
1200-
LinearLifetimeChecker checker(deadEndBlocks);
1200+
LinearLifetimeChecker checker(&deadEndBlocks);
12011201
bool consumedInLoop = checker.completeConsumingUseSet(
12021202
phi, loadOperand, [&](SILBasicBlock::iterator iter) {
12031203
SILBuilderWithScope builder(iter);
@@ -1279,7 +1279,7 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues(
12791279
// Then perform the linear lifetime check. If we succeed, continue. We have
12801280
// no further work to do.
12811281
auto *loadOperand = &load->getAllOperands()[0];
1282-
LinearLifetimeChecker checker(deadEndBlocks);
1282+
LinearLifetimeChecker checker(&deadEndBlocks);
12831283
bool consumedInLoop = checker.completeConsumingUseSet(
12841284
cvi, loadOperand, [&](SILBasicBlock::iterator iter) {
12851285
SILBuilderWithScope builder(iter);

lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ bool SemanticARCOptVisitor::tryPerformOwnedCopyValueOptimization(
765765
// Ok, we have an owned value. If we do not have any non-destroying consuming
766766
// uses, see if all of our uses (ignoring destroying uses) are within our
767767
// parent owned value's lifetime.
768-
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
768+
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
769769
if (!checker.validateLifetime(originalValue, parentLifetimeEndingUses,
770770
allCopyUses))
771771
return false;

lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class StorageGuaranteesLoadVisitor
8888
SmallVector<Operand *, 8> endScopeUses;
8989
transform(access->getEndAccesses(), std::back_inserter(endScopeUses),
9090
[](EndAccessInst *eai) { return &eai->getAllOperands()[0]; });
91-
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
91+
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
9292
if (!checker.validateLifetime(access, endScopeUses,
9393
liveRange.getAllConsumingUses())) {
9494
// If we fail the linear lifetime check, then just recur:
@@ -138,7 +138,7 @@ class StorageGuaranteesLoadVisitor
138138
// Ok, we have some writes. See if any of them are within our live
139139
// range. If any are, we definitely can not promote to load_borrow.
140140
SmallVector<BeginAccessInst *, 16> foundBeginAccess;
141-
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
141+
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
142142
SILValue introducerValue = liveRange.getIntroducer().value;
143143
if (!checker.usesNotContainedWithinLifetime(introducerValue,
144144
liveRange.getDestroyingUses(),
@@ -244,7 +244,7 @@ class StorageGuaranteesLoadVisitor
244244
value.visitLocalScopeEndingUses(
245245
[&](Operand *use) { endScopeInsts.push_back(use); return true; });
246246

247-
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
247+
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
248248

249249
// Returns true on success. So we invert.
250250
bool foundError = !checker.validateLifetime(
@@ -291,7 +291,7 @@ class StorageGuaranteesLoadVisitor
291291

292292
// Then make sure that all of our load [copy] uses are within the
293293
// destroy_addr.
294-
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
294+
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
295295
// Returns true on success. So we invert.
296296
bool foundError = !checker.validateLifetime(
297297
stack, addrDestroyingOperands /*consuming users*/,

lib/SILOptimizer/SemanticARC/OwnershipConversionElimination.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ bool SemanticARCOptVisitor::visitUncheckedOwnershipConversionInst(
5656

5757
// Ok, now we need to perform our lifetime check.
5858
SmallVector<Operand *, 8> consumingUses(op->getConsumingUses());
59-
LinearLifetimeChecker checker(ctx.getDeadEndBlocks());
59+
LinearLifetimeChecker checker(&ctx.getDeadEndBlocks());
6060
if (!checker.validateLifetime(op, consumingUses, newUses))
6161
return false;
6262

0 commit comments

Comments
 (0)