Skip to content

Commit feddd1d

Browse files
authored
Merge pull request #22038 from gottesmm/pr-48a80b46d0806aa50603d26425b66bdb807aecc2
2 parents 741dcc7 + f8f11fa commit feddd1d

File tree

2 files changed

+65
-24
lines changed

2 files changed

+65
-24
lines changed

include/swift/SIL/OwnershipUtils.h

+22-6
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ struct ErrorBehaviorKind {
3535
ReturnFalse = 1,
3636
PrintMessage = 2,
3737
Assert = 4,
38+
ReturnFalseOnLeak = 8,
3839
PrintMessageAndReturnFalse = PrintMessage | ReturnFalse,
3940
PrintMessageAndAssert = PrintMessage | Assert,
41+
ReturnFalseOnLeakAssertOtherwise = ReturnFalseOnLeak | Assert,
4042
} Value;
4143

4244
ErrorBehaviorKind() : Value(Invalid) {}
@@ -47,6 +49,11 @@ struct ErrorBehaviorKind {
4749
return Value & Assert;
4850
}
4951

52+
bool shouldReturnFalseOnLeak() const {
53+
assert(Value != Invalid);
54+
return Value & ReturnFalseOnLeak;
55+
}
56+
5057
bool shouldPrintMessage() const {
5158
assert(Value != Invalid);
5259
return Value & PrintMessage;
@@ -95,12 +102,21 @@ struct OwnershipChecker {
95102
/// non-consuming uses, or from the producer instruction.
96103
/// 2. The consuming use set jointly post dominates producers and all non
97104
/// consuming uses.
98-
bool valueHasLinearLifetime(SILValue value,
99-
ArrayRef<BranchPropagatedUser> consumingUses,
100-
ArrayRef<BranchPropagatedUser> nonConsumingUses,
101-
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
102-
DeadEndBlocks &deadEndBlocks,
103-
ownership::ErrorBehaviorKind errorBehavior);
105+
///
106+
/// \p value The value whose lifetime we are checking.
107+
/// \p consumingUses the array of users that destroy or consume a value.
108+
/// \p nonConsumingUses regular uses
109+
/// \p deadEndBlocks a cache for the dead end block computation
110+
/// \p errorBehavior If we detect an error, should we return false or hard
111+
/// error.
112+
/// \p leakingBlocks If non-null a list of blocks where the value was detected
113+
/// to leak. Can be used to insert missing destroys.
114+
bool valueHasLinearLifetime(
115+
SILValue value, ArrayRef<BranchPropagatedUser> consumingUses,
116+
ArrayRef<BranchPropagatedUser> nonConsumingUses,
117+
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
118+
DeadEndBlocks &deadEndBlocks, ownership::ErrorBehaviorKind errorBehavior,
119+
SmallVectorImpl<SILBasicBlock *> *leakingBlocks = nullptr);
104120

105121
/// Returns true if v is an address or trivial.
106122
bool isValueAddressOrTrivial(SILValue v, SILModule &m);

lib/SIL/LinearLifetimeChecker.cpp

+43-18
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ struct State {
5353
/// The blocks that we have already visited.
5454
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks;
5555

56+
/// If non-null a list that we should place any detected leaking blocks for
57+
/// our caller. The intention is that this can be used in a failing case to
58+
/// put in missing destroys.
59+
SmallVectorImpl<SILBasicBlock *> *leakingBlocks;
60+
5661
/// The set of blocks with consuming uses.
5762
SmallPtrSet<SILBasicBlock *, 8> blocksWithConsumingUses;
5863

@@ -66,12 +71,13 @@ struct State {
6671

6772
/// A list of successor blocks that we must visit by the time the algorithm
6873
/// terminates.
69-
SmallPtrSet<SILBasicBlock *, 8> successorBlocksThatMustBeVisited;
74+
SmallSetVector<SILBasicBlock *, 8> successorBlocksThatMustBeVisited;
7075

7176
State(SILValue value, SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
72-
ErrorBehaviorKind errorBehavior)
77+
ErrorBehaviorKind errorBehavior,
78+
SmallVectorImpl<SILBasicBlock *> *leakingBlocks)
7379
: value(value), errorBehavior(errorBehavior),
74-
visitedBlocks(visitedBlocks) {}
80+
visitedBlocks(visitedBlocks), leakingBlocks(leakingBlocks) {}
7581

7682
void initializeAllNonConsumingUses(
7783
ArrayRef<BranchPropagatedUser> nonConsumingUsers);
@@ -331,7 +337,7 @@ bool State::performDataflow(DeadEndBlocks &deBlocks) {
331337
// First remove BB from the SuccessorBlocksThatMustBeVisited list. This
332338
// ensures that when the algorithm terminates, we know that BB was not the
333339
// beginning of a non-covered path to the exit.
334-
successorBlocksThatMustBeVisited.erase(block);
340+
successorBlocksThatMustBeVisited.remove(block);
335341

336342
// Then remove BB from BlocksWithNonLifetimeEndingUses so we know that
337343
// this block was properly joint post-dominated by our lifetime ending
@@ -395,23 +401,41 @@ bool State::performDataflow(DeadEndBlocks &deBlocks) {
395401
bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
396402
// Make sure that we visited all successor blocks that we needed to visit to
397403
// make sure we didn't leak.
404+
bool doesntHaveAnyLeaks = true;
405+
398406
if (!successorBlocksThatMustBeVisited.empty()) {
399-
return handleError([&] {
400-
llvm::errs()
401-
<< "Function: '" << value->getFunction()->getName() << "'\n"
402-
<< "Error! Found a leak due to a consuming post-dominance failure!\n"
403-
<< " Value: " << *value << " Post Dominating Failure Blocks:\n";
404-
for (auto *succBlock : successorBlocksThatMustBeVisited) {
405-
llvm::errs() << " bb" << succBlock->getDebugID();
406-
}
407-
llvm::errs() << '\n';
408-
});
407+
// If we are asked to store any leaking blocks, put them in the leaking
408+
// blocks array.
409+
if (leakingBlocks) {
410+
copy(successorBlocksThatMustBeVisited,
411+
std::back_inserter(*leakingBlocks));
412+
}
413+
414+
// If we are supposed to error on leaks, do so now.
415+
if (!errorBehavior.shouldReturnFalseOnLeak()) {
416+
return handleError([&] {
417+
llvm::errs() << "Function: '" << value->getFunction()->getName()
418+
<< "'\n"
419+
<< "Error! Found a leak due to a consuming post-dominance "
420+
"failure!\n"
421+
<< " Value: " << *value
422+
<< " Post Dominating Failure Blocks:\n";
423+
for (auto *succBlock : successorBlocksThatMustBeVisited) {
424+
llvm::errs() << " bb" << succBlock->getDebugID();
425+
}
426+
llvm::errs() << '\n';
427+
});
428+
}
429+
430+
// Otherwise... see if we have any other failures. This signals the user
431+
// wants us to tell it where to insert compensating destroys.
432+
doesntHaveAnyLeaks = false;
409433
}
410434

411435
// Make sure that we do not have any lifetime ending uses left to visit that
412436
// are not transitively unreachable blocks.... so return early.
413437
if (blocksWithNonConsumingUses.empty()) {
414-
return true;
438+
return doesntHaveAnyLeaks;
415439
}
416440

417441
// If we do have remaining blocks, then these non lifetime ending uses must be
@@ -436,7 +460,7 @@ bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
436460

437461
// If all of our remaining blocks were dead uses, then return true. We are
438462
// good.
439-
return true;
463+
return doesntHaveAnyLeaks;
440464
}
441465

442466
//===----------------------------------------------------------------------===//
@@ -447,10 +471,11 @@ bool swift::valueHasLinearLifetime(
447471
SILValue value, ArrayRef<BranchPropagatedUser> consumingUses,
448472
ArrayRef<BranchPropagatedUser> nonConsumingUses,
449473
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks, DeadEndBlocks &deBlocks,
450-
ErrorBehaviorKind errorBehavior) {
474+
ErrorBehaviorKind errorBehavior,
475+
SmallVectorImpl<SILBasicBlock *> *leakingBlocks) {
451476
assert(!consumingUses.empty() && "Must have at least one consuming user?!");
452477

453-
State state(value, visitedBlocks, errorBehavior);
478+
State state(value, visitedBlocks, errorBehavior, leakingBlocks);
454479

455480
// First add our non-consuming uses and their blocks to the
456481
// blocksWithNonConsumingUses map. While we do this, if we have multiple uses

0 commit comments

Comments
 (0)