22
22
23
23
#define DEBUG_TYPE " sil-linear-lifetime-checker"
24
24
#include " LinearLifetimeCheckerPrivate.h"
25
+ #include " swift/Basic/BlotMapVector.h"
26
+ #include " swift/Basic/Defer.h"
27
+ #include " swift/Basic/FrozenMultiMap.h"
25
28
#include " swift/SIL/BasicBlockUtils.h"
26
29
#include " swift/SIL/OwnershipUtils.h"
27
30
#include " swift/SIL/SILBasicBlock.h"
@@ -80,7 +83,13 @@ struct State {
80
83
81
84
// / The set of blocks with non-consuming uses and the associated
82
85
// / non-consuming use SILInstruction.
83
- llvm::SmallDenseMap<SILBasicBlock *, Operand *, 8 > blocksWithNonConsumingUses;
86
+ // /
87
+ // / NOTE: This is initialized in initializeAllNonConsumingUses after which it
88
+ // / is frozen. Before this is frozen, one can only add new (Key, Value) pairs
89
+ // / to the map. Once frozen, map operations and blot-erase operations can be
90
+ // / performed. Additionally it provides a getRange() operation that can be
91
+ // / used to iterate over all (Key, [Value]) pairs ignoring erased keys.
92
+ SmallFrozenMultiMap<SILBasicBlock *, Operand *, 8 > blocksWithNonConsumingUses;
84
93
85
94
// / The worklist that we use when performing our block dataflow.
86
95
SmallVector<SILBasicBlock *, 32 > worklist;
@@ -168,58 +177,39 @@ struct State {
168
177
169
178
void State::initializeAllNonConsumingUses (
170
179
ArrayRef<Operand *> nonConsumingUsers) {
180
+ SWIFT_DEFER {
181
+ // Once we have finished initializing blocksWithNonConsumingUses, we need to
182
+ // freeze it. By using a defer here, we can make sure we don't forget to do
183
+ // this below.
184
+ blocksWithNonConsumingUses.setFrozen ();
185
+ };
186
+
171
187
for (Operand *use : nonConsumingUsers) {
172
188
auto *userBlock = use->getUser ()->getParent ();
173
189
174
- // First check if this non consuming user is in our definition block. If so,
175
- // validate that it is strictly after our defining instruction. If so, just
176
- // emit an error now and exit.
177
- if (userBlock == getBeginBlock ()) {
178
- if (std::find_if (beginInst->getIterator (), userBlock->end (),
179
- [&use](const SILInstruction &inst) -> bool {
180
- return use->getUser () == &inst;
181
- }) == userBlock->end ()) {
182
-
183
- errorBuilder.handleUseAfterFree ([&] {
184
- llvm::errs () << " Found use before def?!\n "
185
- << " Value: " ;
186
- if (auto v = value) {
187
- llvm::errs () << *v;
188
- } else {
189
- llvm::errs () << " N/A. \n " ;
190
- }
191
- });
192
- return ;
193
- }
194
- }
195
-
196
- // First try to associate User with User->getParent().
197
- auto result =
198
- blocksWithNonConsumingUses.insert (std::make_pair (userBlock, use));
199
-
200
- // If the insertion succeeds, then we know that there is no more work to
201
- // be done, so process the next use.
202
- if (result.second )
203
- continue ;
204
-
205
- // If the insertion fails, then we have at least two non-consuming uses in
206
- // the same block. Since we are performing a liveness type of dataflow, we
207
- // only need the last non-consuming use to show that all consuming uses post
208
- // dominate both. Since we do not need to deal with cond_br that have
209
- // non-trivial values, we now can check if Use is after Result.first->second
210
- // in the use list. If Use is not later, then we wish to keep the already
211
- // mapped value, not use, so continue.
212
- if (std::find_if (result.first ->second ->getUser ()->getIterator (),
213
- userBlock->end (),
190
+ // Make sure that this non consuming user is in either not in our definition
191
+ // block or is strictly after our defining instruction. If so, stash the use
192
+ // and continue.
193
+ if (userBlock != getBeginBlock () ||
194
+ std::find_if (beginInst->getIterator (), userBlock->end (),
214
195
[&use](const SILInstruction &inst) -> bool {
215
196
return use->getUser () == &inst;
216
- }) == userBlock->end ()) {
197
+ }) != userBlock->end ()) {
198
+ blocksWithNonConsumingUses.insert (userBlock, use);
217
199
continue ;
218
200
}
219
201
220
- // At this point, we know that user is later in the Block than
221
- // result.first->second, so store user instead.
222
- result.first ->second = use;
202
+ // Otherwise, we emit an error since we found a use before our def. We do
203
+ // 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 "
206
+ << " Value: " ;
207
+ if (auto v = value) {
208
+ llvm::errs () << *v;
209
+ } else {
210
+ llvm::errs () << " N/A. \n " ;
211
+ }
212
+ });
223
213
}
224
214
}
225
215
@@ -284,20 +274,27 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse,
284
274
// If we do not have any consuming uses in the same block as our
285
275
// consuming user, then we can not have a same block use-after-free.
286
276
auto iter = blocksWithNonConsumingUses.find (userBlock);
287
- if (iter == blocksWithNonConsumingUses. end ())
277
+ if (! iter. hasValue ()) {
288
278
return ;
279
+ }
289
280
290
- Operand *nonConsumingUse = iter-> second ;
281
+ auto nonConsumingUsesInBlock = * iter;
291
282
292
- // Make sure that the non-consuming use is before the consuming
283
+ // Make sure that all of our non-consuming uses are before the consuming
293
284
// use. Otherwise, we have a use after free. Since we do not allow for cond_br
294
285
// anymore, we know that both of our users are non-cond branch users and thus
295
286
// must be instructions in the given block. Make sure that the non consuming
296
287
// user is strictly before the consuming user.
297
- if (std::find_if (consumingUse->getUser ()->getIterator (), userBlock->end (),
298
- [&nonConsumingUse](const SILInstruction &i) -> bool {
299
- return nonConsumingUse->getUser () == &i;
300
- }) != userBlock->end ()) {
288
+ for (auto *nonConsumingUse : nonConsumingUsesInBlock) {
289
+ if (std::find_if (consumingUse->getUser ()->getIterator (), userBlock->end (),
290
+ [&nonConsumingUse](const SILInstruction &i) -> bool {
291
+ return nonConsumingUse->getUser () == &i;
292
+ }) == userBlock->end ()) {
293
+ continue ;
294
+ }
295
+
296
+ // NOTE: We do not exit here since we want to catch /all/ errors that we can
297
+ // find.
301
298
errorBuilder.handleUseAfterFree ([&] {
302
299
llvm::errs () << " Found use after free?!\n "
303
300
<< " Value: " ;
@@ -307,15 +304,14 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse,
307
304
llvm::errs () << " N/A. \n " ;
308
305
}
309
306
llvm::errs () << " Consuming User: " << *consumingUse->getUser ()
310
- << " Non Consuming User: " << *iter-> second ->getUser ()
307
+ << " Non Consuming User: " << *nonConsumingUse ->getUser ()
311
308
<< " Block: bb" << userBlock->getDebugID () << " \n\n " ;
312
309
});
313
- return ;
314
310
}
315
311
316
- // Erase the use since we know that it is properly joint post-dominated.
317
- blocksWithNonConsumingUses. erase (iter);
318
- return ;
312
+ // Erase the use since we know that it is either properly joint post-dominated
313
+ // or it was not and we emitted use after free errors.
314
+ blocksWithNonConsumingUses. erase (userBlock) ;
319
315
}
320
316
321
317
void State::checkPredsForDoubleConsume (Operand *consumingUse,
@@ -485,35 +481,30 @@ void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
485
481
// wants us to tell it where to insert compensating destroys.
486
482
}
487
483
488
- // Make sure that we do not have any lifetime ending uses left to visit that
489
- // are not transitively unreachable blocks.... so return early.
490
- if (blocksWithNonConsumingUses.empty ()) {
491
- return ;
492
- }
493
-
494
484
// If we do have remaining blocks, then these non lifetime ending uses must be
495
485
// outside of our "alive" blocks implying a use-after free.
496
- for (auto &pair : blocksWithNonConsumingUses) {
497
- if (deBlocks.isDeadEnd (pair.first )) {
486
+ for (auto pair : blocksWithNonConsumingUses.getRange ()) {
487
+ auto *block = pair.first ;
488
+ if (deBlocks.isDeadEnd (block)) {
498
489
continue ;
499
490
}
500
491
501
492
errorBuilder.handleUseAfterFree ([&] {
502
- llvm::errs () << " Found use after free due to unvisited non lifetime "
503
- " ending uses?!\n "
493
+ llvm::errs () << " Found outside of lifetime uses!\n "
504
494
<< " Value: " ;
505
495
if (auto v = value) {
506
496
llvm::errs () << *v;
507
497
} else {
508
498
llvm::errs () << " N/A. \n " ;
509
499
}
510
500
511
- llvm::errs () << " Remaining Users:\n " ;
512
- for (auto &pair : blocksWithNonConsumingUses) {
513
- llvm::errs () << " User:" << *pair.second ->getUser () << " Block: bb"
514
- << pair.first ->getDebugID () << " \n " ;
501
+ auto uses = pair.second ;
502
+ llvm::errs () << " User List:\n " ;
503
+ for (auto *op : uses) {
504
+ llvm::errs () << " User:" << *op->getUser () << " Block: bb"
505
+ << block->getDebugID () << " \n " ;
506
+ llvm::errs () << " \n " ;
515
507
}
516
- llvm::errs () << " \n " ;
517
508
});
518
509
}
519
510
}
0 commit comments