Skip to content

Commit 224c8a5

Browse files
committed
[ownership] Use a richer error result than bool.
I am starting to use the linear lifetime checker in an optimizer role where it no longer asserts but instead tells the optimizer pass what is needed to cause the lifetime to be linear. To do so I need to be able to return richer information to the caller such as whether or not a leak, double consume, or use-after-free occurs.
1 parent d388837 commit 224c8a5

File tree

5 files changed

+82
-49
lines changed

5 files changed

+82
-49
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,44 @@ struct ErrorBehaviorKind {
6767

6868
} // end namespace ownership
6969

70+
class LinearLifetimeError {
71+
ownership::ErrorBehaviorKind errorBehavior;
72+
bool foundError = false;
73+
74+
public:
75+
LinearLifetimeError(ownership::ErrorBehaviorKind errorBehavior)
76+
: errorBehavior(errorBehavior) {}
77+
78+
bool getFoundError() const { return foundError; }
79+
80+
void handleLeakError(llvm::function_ref<void()> &&messagePrinterFunc) {
81+
foundError = true;
82+
83+
if (errorBehavior.shouldPrintMessage())
84+
messagePrinterFunc();
85+
86+
if (errorBehavior.shouldReturnFalseOnLeak())
87+
return;
88+
89+
// We already printed out our error if we needed to, so don't pass it along.
90+
handleError([]() {});
91+
}
92+
93+
void handleError(llvm::function_ref<void()> &&messagePrinterFunc) {
94+
foundError = true;
95+
96+
if (errorBehavior.shouldPrintMessage())
97+
messagePrinterFunc();
98+
99+
if (errorBehavior.shouldReturnFalse()) {
100+
return;
101+
}
102+
103+
assert(errorBehavior.shouldAssert() && "At this point, we should assert");
104+
llvm_unreachable("triggering standard assertion failure routine");
105+
}
106+
};
107+
70108
/// Returns true if:
71109
///
72110
/// 1. No consuming uses are reachable from any other consuming use, from any
@@ -82,7 +120,7 @@ struct ErrorBehaviorKind {
82120
/// error.
83121
/// \p leakingBlocks If non-null a list of blocks where the value was detected
84122
/// to leak. Can be used to insert missing destroys.
85-
bool valueHasLinearLifetime(
123+
LinearLifetimeError valueHasLinearLifetime(
86124
SILValue value, ArrayRef<BranchPropagatedUser> consumingUses,
87125
ArrayRef<BranchPropagatedUser> nonConsumingUses,
88126
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,

lib/SIL/LinearLifetimeChecker.cpp

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ struct State {
4545
/// The value that we are checking.
4646
SILValue value;
4747

48-
/// The behavior of the checker when we detect an error. Can either be
49-
/// returning false, returning false with a message emitted to stderr, or an
50-
/// assert.
51-
ErrorBehaviorKind errorBehavior;
48+
/// The result error object that use to signal either that no errors were
49+
/// found or if errors are found the specific type of error that was found.
50+
LinearLifetimeError error;
5251

5352
/// The blocks that we have already visited.
5453
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks;
@@ -76,8 +75,8 @@ struct State {
7675
State(SILValue value, SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
7776
ErrorBehaviorKind errorBehavior,
7877
SmallVectorImpl<SILBasicBlock *> *leakingBlocks)
79-
: value(value), errorBehavior(errorBehavior),
80-
visitedBlocks(visitedBlocks), leakingBlocks(leakingBlocks) {}
78+
: value(value), error(errorBehavior), visitedBlocks(visitedBlocks),
79+
leakingBlocks(leakingBlocks) {}
8180

8281
void initializeAllNonConsumingUses(
8382
ArrayRef<BranchPropagatedUser> nonConsumingUsers);
@@ -115,17 +114,9 @@ struct State {
115114

116115
/// Depending on our initialization, either return false or call Func and
117116
/// throw an error.
118-
bool handleError(llvm::function_ref<void()> &&messagePrinterFunc) const {
119-
if (errorBehavior.shouldPrintMessage()) {
120-
messagePrinterFunc();
121-
}
122-
123-
if (errorBehavior.shouldReturnFalse()) {
124-
return false;
125-
}
126-
127-
assert(errorBehavior.shouldAssert() && "At this point, we should assert");
128-
llvm_unreachable("triggering standard assertion failure routine");
117+
bool handleError(llvm::function_ref<void()> &&messagePrinterFunc) {
118+
error.handleError(std::move(messagePrinterFunc));
119+
return false;
129120
}
130121
};
131122

@@ -406,20 +397,17 @@ bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
406397
}
407398

408399
// If we are supposed to error on leaks, do so now.
409-
if (!errorBehavior.shouldReturnFalseOnLeak()) {
410-
return handleError([&] {
411-
llvm::errs() << "Function: '" << value->getFunction()->getName()
412-
<< "'\n"
413-
<< "Error! Found a leak due to a consuming post-dominance "
414-
"failure!\n"
415-
<< " Value: " << *value
416-
<< " Post Dominating Failure Blocks:\n";
417-
for (auto *succBlock : successorBlocksThatMustBeVisited) {
418-
llvm::errs() << " bb" << succBlock->getDebugID();
419-
}
420-
llvm::errs() << '\n';
421-
});
422-
}
400+
error.handleLeakError([&] {
401+
llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n"
402+
<< "Error! Found a leak due to a consuming post-dominance "
403+
"failure!\n"
404+
<< " Value: " << *value
405+
<< " Post Dominating Failure Blocks:\n";
406+
for (auto *succBlock : successorBlocksThatMustBeVisited) {
407+
llvm::errs() << " bb" << succBlock->getDebugID();
408+
}
409+
llvm::errs() << '\n';
410+
});
423411

424412
// Otherwise... see if we have any other failures. This signals the user
425413
// wants us to tell it where to insert compensating destroys.
@@ -461,7 +449,7 @@ bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
461449
// Top Level Entrypoints
462450
//===----------------------------------------------------------------------===//
463451

464-
bool swift::valueHasLinearLifetime(
452+
LinearLifetimeError swift::valueHasLinearLifetime(
465453
SILValue value, ArrayRef<BranchPropagatedUser> consumingUses,
466454
ArrayRef<BranchPropagatedUser> nonConsumingUses,
467455
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks, DeadEndBlocks &deBlocks,
@@ -496,7 +484,7 @@ bool swift::valueHasLinearLifetime(
496484
// have been detected by initializing our consuming uses. So we are done.
497485
if (consumingUses.size() == 1 &&
498486
consumingUses[0].getParent() == value->getParentBlock()) {
499-
return true;
487+
return state.error;
500488
}
501489

502490
// Ok, we may have multiple consuming uses. Add the user block of each of our
@@ -517,7 +505,7 @@ bool swift::valueHasLinearLifetime(
517505
// Make sure that the predecessor is not in our blocksWithConsumingUses
518506
// list.
519507
if (state.checkPredsForDoubleConsume(user, predBlock)) {
520-
return state.handleError([] {});
508+
return state.error;
521509
}
522510

523511
if (!state.visitedBlocks.insert(predBlock).second)
@@ -528,9 +516,10 @@ bool swift::valueHasLinearLifetime(
528516
// Now that our algorithm is completely prepared, run the
529517
// dataflow... If we find a failure, return false.
530518
if (!state.performDataflow(deBlocks))
531-
return false;
519+
return state.error;
532520

533521
// ...and then check that the end state shows that we have a valid linear
534522
// typed value.
535-
return state.checkDataflowEndState(deBlocks);
523+
state.checkDataflowEndState(deBlocks);
524+
return state.error;
536525
}

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,10 @@ class SILValueOwnershipChecker {
139139
SmallVector<BranchPropagatedUser, 32> allRegularUsers;
140140
copy(regularUsers, std::back_inserter(allRegularUsers));
141141
copy(implicitRegularUsers, std::back_inserter(allRegularUsers));
142-
result =
142+
auto linearLifetimeResult =
143143
valueHasLinearLifetime(value, lifetimeEndingUsers, allRegularUsers,
144144
visitedBlocks, deadEndBlocks, errorBehavior);
145+
result = !linearLifetimeResult.getFoundError();
145146

146147
return result.getValue();
147148
}

lib/SILOptimizer/Mandatory/MandatoryInlining.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,10 @@ static void fixupReferenceCounts(
136136

137137
visitedBlocks.clear();
138138
// If we need to insert compensating destroys, do so.
139-
if (!valueHasLinearLifetime(copy, {applySite}, {}, visitedBlocks,
140-
deadEndBlocks, errorBehavior,
141-
&leakingBlocks)) {
139+
auto error =
140+
valueHasLinearLifetime(copy, {applySite}, {}, visitedBlocks,
141+
deadEndBlocks, errorBehavior, &leakingBlocks);
142+
if (error.getFoundError()) {
142143
while (!leakingBlocks.empty()) {
143144
auto *leakingBlock = leakingBlocks.pop_back_val();
144145
auto loc = RegularLocation::getAutoGeneratedLocation();
@@ -163,9 +164,10 @@ static void fixupReferenceCounts(
163164

164165
visitedBlocks.clear();
165166
// If we need to insert compensating destroys, do so.
166-
if (!valueHasLinearLifetime(v, {applySite}, {}, visitedBlocks,
167-
deadEndBlocks, errorBehavior,
168-
&leakingBlocks)) {
167+
auto error =
168+
valueHasLinearLifetime(v, {applySite}, {}, visitedBlocks,
169+
deadEndBlocks, errorBehavior, &leakingBlocks);
170+
if (error.getFoundError()) {
169171
while (!leakingBlocks.empty()) {
170172
auto *leakingBlock = leakingBlocks.pop_back_val();
171173
auto loc = RegularLocation::getAutoGeneratedLocation();
@@ -190,9 +192,10 @@ static void fixupReferenceCounts(
190192

191193
visitedBlocks.clear();
192194
// If we need to insert compensating destroys, do so.
193-
if (!valueHasLinearLifetime(v, {applySite}, {}, visitedBlocks,
194-
deadEndBlocks, errorBehavior,
195-
&leakingBlocks)) {
195+
auto error =
196+
valueHasLinearLifetime(v, {applySite}, {}, visitedBlocks,
197+
deadEndBlocks, errorBehavior, &leakingBlocks);
198+
if (error.getFoundError()) {
196199
while (!leakingBlocks.empty()) {
197200
auto *leakingBlock = leakingBlocks.pop_back_val();
198201
auto loc = RegularLocation::getAutoGeneratedLocation();

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,10 @@ void AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li) {
777777
// no further work to do.
778778
auto errorKind =
779779
ownership::ErrorBehaviorKind::ReturnFalseOnLeakAssertOtherwise;
780-
if (valueHasLinearLifetime(cvi, consumingUses, {}, visitedBlocks,
781-
deadEndBlocks, errorKind, &leakingBlocks))
780+
auto error =
781+
valueHasLinearLifetime(cvi, consumingUses, {}, visitedBlocks,
782+
deadEndBlocks, errorKind, &leakingBlocks);
783+
if (!error.getFoundError())
782784
continue;
783785

784786
// Ok, we found some leaking blocks. Insert destroys at the

0 commit comments

Comments
 (0)