3030#include " swift/SIL/SILBasicBlock.h"
3131#include " swift/SIL/SILFunction.h"
3232#include " llvm/ADT/DenseMap.h"
33+ #include " llvm/ADT/MapVector.h"
3334#include " llvm/ADT/STLExtras.h"
3435#include " llvm/Support/Debug.h"
3536
@@ -72,6 +73,11 @@ struct State {
7273 // / put in missing destroys.
7374 Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback;
7475
76+ // / If non-null a callback that we should pass all uses that we detect are not
77+ // / within the linear lifetime we are checking.
78+ Optional<function_ref<void (Operand *)>>
79+ nonConsumingUseOutsideLifetimeCallback;
80+
7581 // / The list of passed in consuming uses.
7682 ArrayRef<Operand *> consumingUses;
7783
@@ -101,20 +107,28 @@ struct State {
101107 State (SILValue value, SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
102108 LinearLifetimeChecker::ErrorBuilder &errorBuilder,
103109 Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback,
110+ Optional<function_ref<void (Operand *)>>
111+ nonConsumingUseOutsideLifetimeCallback,
104112 ArrayRef<Operand *> consumingUses, ArrayRef<Operand *> nonConsumingUses)
105113 : value(value), beginInst(value->getDefiningInsertionPoint ()),
106114 errorBuilder(errorBuilder), visitedBlocks(visitedBlocks),
107115 leakingBlockCallback(leakingBlockCallback),
116+ nonConsumingUseOutsideLifetimeCallback(
117+ nonConsumingUseOutsideLifetimeCallback),
108118 consumingUses(consumingUses), nonConsumingUses(nonConsumingUses) {}
109119
110120 State (SILBasicBlock *beginBlock,
111121 SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
112122 LinearLifetimeChecker::ErrorBuilder &errorBuilder,
113123 Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback,
124+ Optional<function_ref<void(Operand *)>>
125+ nonConsumingUseOutsideLifetimeCallback,
114126 ArrayRef<Operand *> consumingUses, ArrayRef<Operand *> nonConsumingUses)
115127 : value(), beginInst(&*beginBlock->begin ()), errorBuilder(errorBuilder),
116128 visitedBlocks(visitedBlocks),
117129 leakingBlockCallback(leakingBlockCallback),
130+ nonConsumingUseOutsideLifetimeCallback(
131+ nonConsumingUseOutsideLifetimeCallback),
118132 consumingUses(consumingUses), nonConsumingUses(nonConsumingUses) {}
119133
120134 SILBasicBlock *getBeginBlock () const { return beginInst->getParent (); }
@@ -199,16 +213,21 @@ void State::initializeAllNonConsumingUses(
199213 continue ;
200214 }
201215
216+ if (nonConsumingUseOutsideLifetimeCallback) {
217+ (*nonConsumingUseOutsideLifetimeCallback)(use);
218+ }
219+
202220 // Otherwise, we emit an error since we found a use before our def. We do
203221 // not bail early here since we want to gather up /all/ that we find.
204- errorBuilder.handleUseAfterFree ([&] {
205- llvm::errs () << " Found use before def ?!\n "
222+ errorBuilder.handleUseOutsideOfLifetime ([&] {
223+ llvm::errs () << " Found use outside of lifetime ?!\n "
206224 << " Value: " ;
207225 if (auto v = value) {
208226 llvm::errs () << *v;
209227 } else {
210228 llvm::errs () << " N/A. \n " ;
211229 }
230+ llvm::errs () << " User: " << use->getUser ();
212231 });
213232 }
214233}
@@ -293,10 +312,14 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse,
293312 continue ;
294313 }
295314
315+ if (nonConsumingUseOutsideLifetimeCallback) {
316+ (*nonConsumingUseOutsideLifetimeCallback)(nonConsumingUse);
317+ }
318+
296319 // NOTE: We do not exit here since we want to catch /all/ errors that we can
297320 // find.
298- errorBuilder.handleUseAfterFree ([&] {
299- llvm::errs () << " Found use after free ?!\n "
321+ errorBuilder.handleUseOutsideOfLifetime ([&] {
322+ llvm::errs () << " Found outside of lifetime use ?!\n "
300323 << " Value: " ;
301324 if (auto v = value) {
302325 llvm::errs () << *v;
@@ -482,30 +505,33 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
482505 }
483506
484507 // If we do have remaining blocks, then these non lifetime ending uses must be
485- // outside of our "alive" blocks implying a use-after free.
508+ // outside of our "alive" blocks implying an outside of lifetime use. It could
509+ // be a use-before-def or a use-after-free.
486510 for (auto pair : blocksWithNonConsumingUses.getRange ()) {
487511 auto *block = pair.first ;
488512 if (deBlocks.isDeadEnd (block)) {
489513 continue ;
490514 }
491515
492- errorBuilder.handleUseAfterFree ([&] {
493- llvm::errs () << " Found outside of lifetime uses!\n "
494- << " Value: " ;
495- if (auto v = value) {
496- llvm::errs () << *v;
497- } else {
498- llvm::errs () << " N/A. \n " ;
516+ auto useList = pair.second ;
517+ for (auto *use : useList) {
518+ if (nonConsumingUseOutsideLifetimeCallback) {
519+ (*nonConsumingUseOutsideLifetimeCallback)(use);
499520 }
500521
501- auto uses = pair.second ;
502- llvm::errs () << " User List:\n " ;
503- for (auto *op : uses) {
504- llvm::errs () << " User:" << *op->getUser () << " Block: bb"
522+ errorBuilder.handleUseOutsideOfLifetime ([&] {
523+ llvm::errs () << " Found outside of lifetime use!\n "
524+ << " Value: " ;
525+ if (auto v = value) {
526+ llvm::errs () << *v;
527+ } else {
528+ llvm::errs () << " N/A. \n " ;
529+ }
530+
531+ llvm::errs () << " User:" << *use->getUser () << " Block: bb"
505532 << block->getDebugID () << " \n " ;
506- llvm::errs () << " \n " ;
507- }
508- });
533+ });
534+ }
509535 }
510536}
511537
@@ -516,12 +542,15 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
516542LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl (
517543 SILValue value, ArrayRef<Operand *> consumingUses,
518544 ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder,
519- Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback) {
545+ Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback,
546+ Optional<function_ref<void(Operand *)>>
547+ nonConsumingUseOutsideLifetimeCallback) {
520548 assert ((!consumingUses.empty () || !deadEndBlocks.empty ()) &&
521549 " Must have at least one consuming user?!" );
522550
523551 State state (value, visitedBlocks, errorBuilder, leakingBlockCallback,
524- consumingUses, nonConsumingUses);
552+ nonConsumingUseOutsideLifetimeCallback, consumingUses,
553+ nonConsumingUses);
525554
526555 // First add our non-consuming uses and their blocks to the
527556 // blocksWithNonConsumingUses map. While we do this, if we have multiple uses
@@ -551,19 +580,23 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl(
551580 // Check if any of our non consuming uses are not in the parent block and
552581 // are reachable. We flag those as additional use after frees. Any in the
553582 // same block, we would have flagged.
554- if (llvm::any_of (nonConsumingUses, [&](Operand *use) {
555- auto *useParent = use->getUser ()->getParent ();
556- return useParent != value->getParentBlock () &&
557- !deadEndBlocks.isDeadEnd (useParent);
558- })) {
559- state.errorBuilder .handleUseAfterFree ([&] {
560- llvm::errs () << " Found use after free due to unvisited non lifetime "
561- " ending uses?!\n "
562- << " Value: " << *value << " Remaining Users:\n " ;
563- for (const auto &use : nonConsumingUses) {
564- llvm::errs () << " User: " << *use->getUser ();
565- }
566- llvm::errs () << " \n " ;
583+ for (auto *use : nonConsumingUses) {
584+ auto *useParent = use->getUser ()->getParent ();
585+ if (useParent == value->getParentBlock () ||
586+ deadEndBlocks.isDeadEnd (useParent)) {
587+ continue ;
588+ }
589+
590+ if (nonConsumingUseOutsideLifetimeCallback) {
591+ (*nonConsumingUseOutsideLifetimeCallback)(use);
592+ }
593+
594+ state.errorBuilder .handleUseOutsideOfLifetime ([&] {
595+ llvm::errs () << " Function: '" << value->getFunction ()->getName ()
596+ << " '\n "
597+ << " Found non consuming use outside of the lifetime being "
598+ " verified.\n "
599+ << " Value: " << *value << " User: " << *use->getUser ();
567600 });
568601 }
569602
@@ -609,15 +642,15 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValue(
609642 SILValue value, ArrayRef<Operand *> consumingUses,
610643 ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder) {
611644 return checkValueImpl (value, consumingUses, nonConsumingUses, errorBuilder,
612- None);
645+ None, None );
613646}
614647
615648LinearLifetimeChecker::Error LinearLifetimeChecker::checkValue (
616649 SILValue value, ArrayRef<Operand *> consumingUses,
617650 ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder,
618651 function_ref<void (SILBasicBlock *)> leakingBlocksCallback) {
619652 return checkValueImpl (value, consumingUses, nonConsumingUses, errorBuilder,
620- leakingBlocksCallback);
653+ leakingBlocksCallback, None );
621654}
622655
623656bool LinearLifetimeChecker::completeConsumingUseSet (
@@ -645,3 +678,36 @@ bool LinearLifetimeChecker::validateLifetime(
645678 return !checkValue (value, consumingUses, nonConsumingUses, errorBuilder)
646679 .getFoundError ();
647680}
681+
682+ bool LinearLifetimeChecker::usesNotContainedWithinLifetime (
683+ SILValue value, ArrayRef<Operand *> consumingUses,
684+ ArrayRef<Operand *> usesToTest) {
685+
686+ auto errorBehavior = ErrorBehaviorKind (
687+ ErrorBehaviorKind::ReturnFalse |
688+ ErrorBehaviorKind::StoreNonConsumingUsesOutsideLifetime);
689+ ErrorBuilder errorBuilder (*value->getFunction (), errorBehavior);
690+
691+ using OptType = Optional<function_ref<void (Operand *)>>;
692+ #ifndef NDEBUG
693+ SmallVector<Operand *, 32 > uniqueUsers;
694+ #endif
695+ unsigned numFoundUses = 0 ;
696+ auto error = checkValueImpl (value, consumingUses, usesToTest, errorBuilder,
697+ None, OptType ([&](Operand *use) {
698+ #ifndef NDEBUG
699+ uniqueUsers.push_back (use);
700+ #endif
701+ ++numFoundUses;
702+ }));
703+
704+ #ifndef NDEBUG
705+ // Make sure in assert builds that we did not double count any operands.
706+ sortUnique (uniqueUsers);
707+ assert (numFoundUses == uniqueUsers.size ());
708+ #endif
709+
710+ // Return true if we /did/ found an error and when emitting that error, we
711+ // found /all/ uses we were looking for.
712+ return error.getFoundError () && numFoundUses == usesToTest.size ();
713+ }
0 commit comments