30
30
#include " swift/SIL/SILBasicBlock.h"
31
31
#include " swift/SIL/SILFunction.h"
32
32
#include " llvm/ADT/DenseMap.h"
33
+ #include " llvm/ADT/MapVector.h"
33
34
#include " llvm/ADT/STLExtras.h"
34
35
#include " llvm/Support/Debug.h"
35
36
@@ -72,6 +73,11 @@ struct State {
72
73
// / put in missing destroys.
73
74
Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback;
74
75
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
+
75
81
// / The list of passed in consuming uses.
76
82
ArrayRef<Operand *> consumingUses;
77
83
@@ -101,20 +107,28 @@ struct State {
101
107
State (SILValue value, SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
102
108
LinearLifetimeChecker::ErrorBuilder &errorBuilder,
103
109
Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback,
110
+ Optional<function_ref<void (Operand *)>>
111
+ nonConsumingUseOutsideLifetimeCallback,
104
112
ArrayRef<Operand *> consumingUses, ArrayRef<Operand *> nonConsumingUses)
105
113
: value(value), beginInst(value->getDefiningInsertionPoint ()),
106
114
errorBuilder(errorBuilder), visitedBlocks(visitedBlocks),
107
115
leakingBlockCallback(leakingBlockCallback),
116
+ nonConsumingUseOutsideLifetimeCallback(
117
+ nonConsumingUseOutsideLifetimeCallback),
108
118
consumingUses(consumingUses), nonConsumingUses(nonConsumingUses) {}
109
119
110
120
State (SILBasicBlock *beginBlock,
111
121
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
112
122
LinearLifetimeChecker::ErrorBuilder &errorBuilder,
113
123
Optional<function_ref<void (SILBasicBlock *)>> leakingBlockCallback,
124
+ Optional<function_ref<void(Operand *)>>
125
+ nonConsumingUseOutsideLifetimeCallback,
114
126
ArrayRef<Operand *> consumingUses, ArrayRef<Operand *> nonConsumingUses)
115
127
: value(), beginInst(&*beginBlock->begin ()), errorBuilder(errorBuilder),
116
128
visitedBlocks(visitedBlocks),
117
129
leakingBlockCallback(leakingBlockCallback),
130
+ nonConsumingUseOutsideLifetimeCallback(
131
+ nonConsumingUseOutsideLifetimeCallback),
118
132
consumingUses(consumingUses), nonConsumingUses(nonConsumingUses) {}
119
133
120
134
SILBasicBlock *getBeginBlock () const { return beginInst->getParent (); }
@@ -199,16 +213,21 @@ void State::initializeAllNonConsumingUses(
199
213
continue ;
200
214
}
201
215
216
+ if (nonConsumingUseOutsideLifetimeCallback) {
217
+ (*nonConsumingUseOutsideLifetimeCallback)(use);
218
+ }
219
+
202
220
// Otherwise, we emit an error since we found a use before our def. We do
203
221
// 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 "
206
224
<< " Value: " ;
207
225
if (auto v = value) {
208
226
llvm::errs () << *v;
209
227
} else {
210
228
llvm::errs () << " N/A. \n " ;
211
229
}
230
+ llvm::errs () << " User: " << use->getUser ();
212
231
});
213
232
}
214
233
}
@@ -293,10 +312,14 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse,
293
312
continue ;
294
313
}
295
314
315
+ if (nonConsumingUseOutsideLifetimeCallback) {
316
+ (*nonConsumingUseOutsideLifetimeCallback)(nonConsumingUse);
317
+ }
318
+
296
319
// NOTE: We do not exit here since we want to catch /all/ errors that we can
297
320
// find.
298
- errorBuilder.handleUseAfterFree ([&] {
299
- llvm::errs () << " Found use after free ?!\n "
321
+ errorBuilder.handleUseOutsideOfLifetime ([&] {
322
+ llvm::errs () << " Found outside of lifetime use ?!\n "
300
323
<< " Value: " ;
301
324
if (auto v = value) {
302
325
llvm::errs () << *v;
@@ -482,30 +505,33 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
482
505
}
483
506
484
507
// 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.
486
510
for (auto pair : blocksWithNonConsumingUses.getRange ()) {
487
511
auto *block = pair.first ;
488
512
if (deBlocks.isDeadEnd (block)) {
489
513
continue ;
490
514
}
491
515
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);
499
520
}
500
521
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"
505
532
<< block->getDebugID () << " \n " ;
506
- llvm::errs () << " \n " ;
507
- }
508
- });
533
+ });
534
+ }
509
535
}
510
536
}
511
537
@@ -516,12 +542,15 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
516
542
LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl (
517
543
SILValue value, ArrayRef<Operand *> consumingUses,
518
544
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) {
520
548
assert ((!consumingUses.empty () || !deadEndBlocks.empty ()) &&
521
549
" Must have at least one consuming user?!" );
522
550
523
551
State state (value, visitedBlocks, errorBuilder, leakingBlockCallback,
524
- consumingUses, nonConsumingUses);
552
+ nonConsumingUseOutsideLifetimeCallback, consumingUses,
553
+ nonConsumingUses);
525
554
526
555
// First add our non-consuming uses and their blocks to the
527
556
// blocksWithNonConsumingUses map. While we do this, if we have multiple uses
@@ -551,19 +580,23 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValueImpl(
551
580
// Check if any of our non consuming uses are not in the parent block and
552
581
// are reachable. We flag those as additional use after frees. Any in the
553
582
// 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 ();
567
600
});
568
601
}
569
602
@@ -609,15 +642,15 @@ LinearLifetimeChecker::Error LinearLifetimeChecker::checkValue(
609
642
SILValue value, ArrayRef<Operand *> consumingUses,
610
643
ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder) {
611
644
return checkValueImpl (value, consumingUses, nonConsumingUses, errorBuilder,
612
- None);
645
+ None, None );
613
646
}
614
647
615
648
LinearLifetimeChecker::Error LinearLifetimeChecker::checkValue (
616
649
SILValue value, ArrayRef<Operand *> consumingUses,
617
650
ArrayRef<Operand *> nonConsumingUses, ErrorBuilder &errorBuilder,
618
651
function_ref<void (SILBasicBlock *)> leakingBlocksCallback) {
619
652
return checkValueImpl (value, consumingUses, nonConsumingUses, errorBuilder,
620
- leakingBlocksCallback);
653
+ leakingBlocksCallback, None );
621
654
}
622
655
623
656
bool LinearLifetimeChecker::completeConsumingUseSet (
@@ -645,3 +678,36 @@ bool LinearLifetimeChecker::validateLifetime(
645
678
return !checkValue (value, consumingUses, nonConsumingUses, errorBuilder)
646
679
.getFoundError ();
647
680
}
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