@@ -290,6 +290,22 @@ llvm::cl::opt<bool> DisableMoveOnlyAddressCheckerLifetimeExtension(
290
290
// MARK: Utilities
291
291
// ===----------------------------------------------------------------------===//
292
292
293
+ struct RAIILLVMDebug {
294
+ StringRef str;
295
+
296
+ RAIILLVMDebug (StringRef str) : str(str) {
297
+ LLVM_DEBUG (llvm::dbgs () << " ===>>> Starting " << str << ' \n ' );
298
+ }
299
+
300
+ RAIILLVMDebug (StringRef str, SILInstruction *u) : str(str) {
301
+ LLVM_DEBUG (llvm::dbgs () << " ===>>> Starting " << str << " :" << *u);
302
+ }
303
+
304
+ ~RAIILLVMDebug () {
305
+ LLVM_DEBUG (llvm::dbgs () << " ===<<< Completed " << str << ' \n ' );
306
+ }
307
+ };
308
+
293
309
static void insertDebugValueBefore (SILInstruction *insertPt,
294
310
DebugVarCarryingInst debugVar,
295
311
llvm::function_ref<SILValue ()> operand) {
@@ -385,17 +401,6 @@ static bool isInOutDefThatNeedsEndOfFunctionLiveness(MarkMustCheckInst *markedAd
385
401
}
386
402
}
387
403
388
- // See if we have an assignable_but_not_consumable from a formal access.
389
- // In this case, the value must be live at the end of the
390
- // access, similar to an inout parameter.
391
- if (markedAddr->getCheckKind () ==
392
- MarkMustCheckInst::CheckKind::AssignableButNotConsumable) {
393
- if (isa<BeginAccessInst>(operand)) {
394
- return true ;
395
- }
396
- }
397
-
398
-
399
404
return false ;
400
405
}
401
406
@@ -513,7 +518,7 @@ struct UseState {
513
518
// / terminator user and so that we can use the set to quickly identify later
514
519
// / while emitting diagnostics that a liveness use is a terminator user and
515
520
// / emit a specific diagnostic message.
516
- SmallSetVector<SILInstruction *, 2 > inoutTermUsers ;
521
+ SmallSetVector<SILInstruction *, 2 > implicitEndOfLifetimeLivenessUses ;
517
522
518
523
// / We add debug_values to liveness late after we diagnose, but before we
519
524
// / hoist destroys to ensure that we do not hoist destroys out of access
@@ -565,11 +570,17 @@ struct UseState {
565
570
setAffectedBits (inst, range, initInsts);
566
571
}
567
572
568
- // / Returns true if this is a terminator instruction that although it doesn't
569
- // / use our inout argument directly is used by the pass to ensure that we
570
- // / reinit said argument if we consumed it in the body of the function.
571
- bool isInOutTermUser (SILInstruction *inst) const {
572
- return inoutTermUsers.count (inst);
573
+ // / Returns true if this is an instruction that is used by the pass to ensure
574
+ // / that we reinit said argument if we consumed it in a region of code.
575
+ // /
576
+ // / Example:
577
+ // /
578
+ // / 1. In the case of an inout argument, this will contain the terminator
579
+ // / instruction.
580
+ // / 2. In the case of a ref_element_addr or a global, this will contain the
581
+ // / end_access.
582
+ bool isImplicitEndOfLifetimeLivenessUses (SILInstruction *inst) const {
583
+ return implicitEndOfLifetimeLivenessUses.count (inst);
573
584
}
574
585
575
586
// / Returns true if the given instruction is within the same block as a reinit
@@ -609,7 +620,7 @@ struct UseState {
609
620
initInsts.clear ();
610
621
reinitInsts.clear ();
611
622
dropDeinitInsts.clear ();
612
- inoutTermUsers .clear ();
623
+ implicitEndOfLifetimeLivenessUses .clear ();
613
624
debugValue = nullptr ;
614
625
}
615
626
@@ -647,8 +658,8 @@ struct UseState {
647
658
for (auto *inst : dropDeinitInsts) {
648
659
llvm::dbgs () << *inst;
649
660
}
650
- llvm::dbgs () << " InOut Term Users:\n " ;
651
- for (auto *inst : inoutTermUsers ) {
661
+ llvm::dbgs () << " Implicit End Of Lifetime Liveness Users:\n " ;
662
+ for (auto *inst : implicitEndOfLifetimeLivenessUses ) {
652
663
llvm::dbgs () << *inst;
653
664
}
654
665
llvm::dbgs () << " Debug Value User:\n " ;
@@ -680,16 +691,28 @@ struct UseState {
680
691
void
681
692
initializeLiveness (FieldSensitiveMultiDefPrunedLiveRange &prunedLiveness);
682
693
683
- void initializeInOutTermUsers () {
684
- if (!isInOutDefThatNeedsEndOfFunctionLiveness (address))
694
+ void initializeImplicitEndOfLifetimeLivenessUses () {
695
+ if (isInOutDefThatNeedsEndOfFunctionLiveness (address)) {
696
+ SmallVector<SILBasicBlock *, 8 > exitBlocks;
697
+ address->getFunction ()->findExitingBlocks (exitBlocks);
698
+ for (auto *block : exitBlocks) {
699
+ LLVM_DEBUG (llvm::dbgs () << " Adding term as liveness user: "
700
+ << *block->getTerminator ());
701
+ implicitEndOfLifetimeLivenessUses.insert (block->getTerminator ());
702
+ }
685
703
return ;
704
+ }
686
705
687
- SmallVector<SILBasicBlock *, 8 > exitBlocks;
688
- address->getFunction ()->findExitingBlocks (exitBlocks);
689
- for (auto *block : exitBlocks) {
690
- LLVM_DEBUG (llvm::dbgs () << " Adding term as liveness user: "
691
- << *block->getTerminator ());
692
- inoutTermUsers.insert (block->getTerminator ());
706
+ if (address->getCheckKind () ==
707
+ MarkMustCheckInst::CheckKind::AssignableButNotConsumable) {
708
+ if (auto *bai = dyn_cast<BeginAccessInst>(address->getOperand ())) {
709
+ for (auto *eai : bai->getEndAccesses ()) {
710
+ LLVM_DEBUG (llvm::dbgs () << " Adding end_access as implicit end of "
711
+ " lifetime liveness user: "
712
+ << *eai);
713
+ implicitEndOfLifetimeLivenessUses.insert (eai);
714
+ }
715
+ }
693
716
}
694
717
}
695
718
@@ -749,7 +772,7 @@ struct UseState {
749
772
// An "inout terminator use" is an implicit liveness use of the entire
750
773
// value. This is because we need to ensure that our inout value is
751
774
// reinitialized along exit paths.
752
- if (inoutTermUsers .count (inst))
775
+ if (implicitEndOfLifetimeLivenessUses .count (inst))
753
776
return true ;
754
777
755
778
return false ;
@@ -1064,13 +1087,11 @@ void UseState::initializeLiveness(
1064
1087
liveness.print (llvm::dbgs ()));
1065
1088
}
1066
1089
1067
- // Finally, if we have an inout argument, add a liveness use of the entire
1068
- // value on terminators in blocks that are exits from the function. This
1069
- // ensures that along all paths, if our inout is not reinitialized before we
1070
- // exit the function, we will get an error. We also stash these users into
1071
- // inoutTermUser so we can quickly recognize them later and emit a better
1072
- // error msg.
1073
- for (auto *inst : inoutTermUsers) {
1090
+ // Finally, if we have an inout argument or an access scope associated with a
1091
+ // ref_element_addr or global_addr, add a liveness use of the entire value on
1092
+ // the implicit end lifetime instruction. For inout this is terminators for
1093
+ // ref_element_addr, global_addr it is the end_access instruction.
1094
+ for (auto *inst : implicitEndOfLifetimeLivenessUses) {
1074
1095
liveness.updateForUse (inst, TypeTreeLeafTypeRange (address),
1075
1096
false /* lifetime ending*/ );
1076
1097
LLVM_DEBUG (llvm::dbgs () << " Added liveness for inoutTermUser: " << *inst;
@@ -2229,7 +2250,7 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
2229
2250
if (addressUseState.isLivenessUse (&*ii, errorSpan)) {
2230
2251
diagnosticEmitter.emitAddressDiagnostic (
2231
2252
addressUseState.address , &*ii, errorUser, false /* is consuming*/ ,
2232
- addressUseState.isInOutTermUser (&*ii));
2253
+ addressUseState.isImplicitEndOfLifetimeLivenessUses (&*ii));
2233
2254
foundSingleBlockError = true ;
2234
2255
emittedDiagnostic = true ;
2235
2256
break ;
@@ -2249,8 +2270,9 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
2249
2270
&*ii, FieldSensitivePrunedLiveness::NonLifetimeEndingUse,
2250
2271
errorSpan)) {
2251
2272
diagnosticEmitter.emitAddressDiagnostic (
2252
- addressUseState.address , &*ii, errorUser, false /* is consuming*/ ,
2253
- addressUseState.isInOutTermUser (&*ii));
2273
+ addressUseState.address , &*ii, errorUser,
2274
+ false /* is consuming*/ ,
2275
+ addressUseState.isImplicitEndOfLifetimeLivenessUses (&*ii));
2254
2276
foundSingleBlockError = true ;
2255
2277
emittedDiagnostic = true ;
2256
2278
break ;
@@ -2340,7 +2362,8 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
2340
2362
diagnosticEmitter.emitAddressDiagnostic (
2341
2363
addressUseState.address , &blockInst, errorUser,
2342
2364
false /* is consuming*/ ,
2343
- addressUseState.isInOutTermUser (&blockInst));
2365
+ addressUseState.isImplicitEndOfLifetimeLivenessUses (
2366
+ &blockInst));
2344
2367
foundSingleBlockError = true ;
2345
2368
emittedDiagnostic = true ;
2346
2369
break ;
@@ -2567,6 +2590,18 @@ void MoveOnlyAddressCheckerPImpl::insertDestroysOnBoundary(
2567
2590
continue ;
2568
2591
}
2569
2592
2593
+ // If we have an implicit end of lifetime use, we do not insert a
2594
+ // destroy_addr. Instead, we insert an undef debug value after the
2595
+ // use. This occurs if we have an end_access associated with a
2596
+ // global_addr or a ref_element_addr field access.
2597
+ if (addressUseState.isImplicitEndOfLifetimeLivenessUses (inst)) {
2598
+ LLVM_DEBUG (
2599
+ llvm::dbgs ()
2600
+ << " Use was an implicit end of lifetime liveness use!\n " );
2601
+ insertUndefDebugValue (inst->getNextInstruction ());
2602
+ continue ;
2603
+ }
2604
+
2570
2605
auto *insertPt = inst->getNextInstruction ();
2571
2606
insertDestroyBeforeInstruction (addressUseState, insertPt,
2572
2607
liveness.getRootValue (), bits, consumes);
@@ -3121,6 +3156,8 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
3121
3156
// [copy] + begin_borrow for further processing. This just eliminates a case
3122
3157
// that the checker doesn't need to know about.
3123
3158
{
3159
+ RAIILLVMDebug l (" CopiedLoadBorrowEliminationVisitor" );
3160
+
3124
3161
CopiedLoadBorrowEliminationState state (markedAddress->getFunction ());
3125
3162
CopiedLoadBorrowEliminationVisitor copiedLoadBorrowEliminator (state);
3126
3163
if (AddressUseKind::Unknown ==
@@ -3142,12 +3179,16 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
3142
3179
// SILGen will treat y as a separate rvalue from x and will create a temporary
3143
3180
// allocation. In contrast if we have a var, we treat x like an lvalue and
3144
3181
// just create GEPs appropriately.
3145
- if (eliminateTemporaryAllocationsFromLet (markedAddress)) {
3146
- LLVM_DEBUG (
3147
- llvm::dbgs ()
3148
- << " Succeeded in eliminating temporary allocations! Fn after:\n " ;
3149
- markedAddress->getFunction ()->dump ());
3150
- changed = true ;
3182
+ {
3183
+ RAIILLVMDebug l (" temporary allocations from rvalue accesses" );
3184
+
3185
+ if (eliminateTemporaryAllocationsFromLet (markedAddress)) {
3186
+ LLVM_DEBUG (
3187
+ llvm::dbgs ()
3188
+ << " Succeeded in eliminating temporary allocations! Fn after:\n " ;
3189
+ markedAddress->getFunction ()->dump ());
3190
+ changed = true ;
3191
+ }
3151
3192
}
3152
3193
3153
3194
// Then gather all uses of our address by walking from def->uses. We use this
@@ -3156,10 +3197,16 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
3156
3197
GatherUsesVisitor visitor (*this , addressUseState, markedAddress,
3157
3198
diagnosticEmitter);
3158
3199
SWIFT_DEFER { visitor.clear (); };
3159
- visitor.reset (markedAddress);
3160
- if (AddressUseKind::Unknown == std::move (visitor).walk (markedAddress)) {
3161
- LLVM_DEBUG (llvm::dbgs () << " Failed access path visit: " << *markedAddress);
3162
- return false ;
3200
+
3201
+ {
3202
+ RAIILLVMDebug l (" main use gathering visitor" );
3203
+
3204
+ visitor.reset (markedAddress);
3205
+ if (AddressUseKind::Unknown == std::move (visitor).walk (markedAddress)) {
3206
+ LLVM_DEBUG (llvm::dbgs ()
3207
+ << " Failed access path visit: " << *markedAddress);
3208
+ return false ;
3209
+ }
3163
3210
}
3164
3211
3165
3212
// If we found a load [copy] or copy_addr that requires multiple copies or an
@@ -3182,23 +3229,33 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
3182
3229
// DISCUSSION: For non address only types, this is not an issue since we
3183
3230
// eagerly load
3184
3231
3185
- addressUseState.initializeInOutTermUsers ();
3232
+ addressUseState.initializeImplicitEndOfLifetimeLivenessUses ();
3186
3233
3187
3234
// ===---
3188
3235
// Liveness Checking
3189
3236
//
3237
+
3190
3238
SmallVector<SILBasicBlock *, 32 > discoveredBlocks;
3191
3239
FieldSensitiveMultiDefPrunedLiveRange liveness (fn, markedAddress,
3192
3240
&discoveredBlocks);
3193
- addressUseState.initializeLiveness (liveness);
3194
3241
3195
- // Then compute the takes that are within the cumulative boundary of
3196
- // liveness that we have computed. If we find any, they are the errors ones.
3197
- GlobalLivenessChecker emitter (addressUseState, diagnosticEmitter, liveness);
3242
+ {
3243
+ RAIILLVMDebug logger (" liveness initialization" );
3198
3244
3199
- // If we had any errors, we do not want to modify the SIL... just bail.
3200
- if (emitter.compute ()) {
3201
- return true ;
3245
+ addressUseState.initializeLiveness (liveness);
3246
+ }
3247
+
3248
+ {
3249
+ RAIILLVMDebug l (" global liveness checking" );
3250
+
3251
+ // Then compute the takes that are within the cumulative boundary of
3252
+ // liveness that we have computed. If we find any, they are the errors ones.
3253
+ GlobalLivenessChecker emitter (addressUseState, diagnosticEmitter, liveness);
3254
+
3255
+ // If we had any errors, we do not want to modify the SIL... just bail.
3256
+ if (emitter.compute ()) {
3257
+ return true ;
3258
+ }
3202
3259
}
3203
3260
3204
3261
// ===
@@ -3242,15 +3299,40 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
3242
3299
// MARK: Top Level Entrypoint
3243
3300
// ===----------------------------------------------------------------------===//
3244
3301
3302
+ #ifndef NDEBUG
3303
+ static llvm::cl::opt<uint64_t > NumTopLevelToProcess (
3304
+ " sil-move-only-address-checker-num-top-level-to-process" ,
3305
+ llvm::cl::desc (" Allows for bisecting on move introducer that causes an "
3306
+ " error. Only meant for debugging!" ),
3307
+ llvm::cl::init(UINT64_MAX));
3308
+ #endif
3309
+
3310
+ static llvm::cl::opt<bool > DumpSILBeforeRemovingMarkMustCheck (
3311
+ " sil-move-only-address-checker-dump-before-removing-mark-must-check" ,
3312
+ llvm::cl::desc (" When bisecting it is useful to dump the SIL before the "
3313
+ " rest of the checker removes mark_must_check. This lets one "
3314
+ " grab the SIL of a bad variable after all of the rest have "
3315
+ " been processed to work with further in sil-opt." ),
3316
+ llvm::cl::init(false ));
3317
+
3245
3318
bool MoveOnlyAddressChecker::check (
3246
3319
SmallSetVector<MarkMustCheckInst *, 32 > &moveIntroducersToProcess) {
3247
3320
assert (moveIntroducersToProcess.size () &&
3248
3321
" Must have checks to process to call this function" );
3249
3322
MoveOnlyAddressCheckerPImpl pimpl (fn, diagnosticEmitter, domTree, poa,
3250
3323
allocator);
3251
3324
3325
+ #ifndef NDEBUG
3326
+ static uint64_t numProcessed = 0 ;
3327
+ #endif
3252
3328
for (auto *markedValue : moveIntroducersToProcess) {
3253
- LLVM_DEBUG (llvm::dbgs () << " Visiting: " << *markedValue);
3329
+ #ifndef NDEBUG
3330
+ ++numProcessed;
3331
+ if (NumTopLevelToProcess <= numProcessed)
3332
+ break ;
3333
+ #endif
3334
+ LLVM_DEBUG (llvm::dbgs ()
3335
+ << " ======>>> Visiting top level: " << *markedValue);
3254
3336
3255
3337
// Perform our address check.
3256
3338
unsigned diagnosticEmittedByEarlierPassCount =
@@ -3271,5 +3353,10 @@ bool MoveOnlyAddressChecker::check(
3271
3353
}
3272
3354
}
3273
3355
3356
+ if (DumpSILBeforeRemovingMarkMustCheck) {
3357
+ LLVM_DEBUG (llvm::dbgs ()
3358
+ << " Dumping SIL before removing mark must checks!\n " ;
3359
+ fn->dump ());
3360
+ }
3274
3361
return pimpl.changed ;
3275
3362
}
0 commit comments