@@ -290,6 +290,22 @@ llvm::cl::opt<bool> DisableMoveOnlyAddressCheckerLifetimeExtension(
290290// MARK: Utilities
291291// ===----------------------------------------------------------------------===//
292292
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+
293309static void insertDebugValueBefore (SILInstruction *insertPt,
294310 DebugVarCarryingInst debugVar,
295311 llvm::function_ref<SILValue ()> operand) {
@@ -385,17 +401,6 @@ static bool isInOutDefThatNeedsEndOfFunctionLiveness(MarkMustCheckInst *markedAd
385401 }
386402 }
387403
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-
399404 return false ;
400405}
401406
@@ -513,7 +518,7 @@ struct UseState {
513518 // / terminator user and so that we can use the set to quickly identify later
514519 // / while emitting diagnostics that a liveness use is a terminator user and
515520 // / emit a specific diagnostic message.
516- SmallSetVector<SILInstruction *, 2 > inoutTermUsers ;
521+ SmallSetVector<SILInstruction *, 2 > implicitEndOfLifetimeLivenessUses ;
517522
518523 // / We add debug_values to liveness late after we diagnose, but before we
519524 // / hoist destroys to ensure that we do not hoist destroys out of access
@@ -565,11 +570,17 @@ struct UseState {
565570 setAffectedBits (inst, range, initInsts);
566571 }
567572
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);
573584 }
574585
575586 // / Returns true if the given instruction is within the same block as a reinit
@@ -609,7 +620,7 @@ struct UseState {
609620 initInsts.clear ();
610621 reinitInsts.clear ();
611622 dropDeinitInsts.clear ();
612- inoutTermUsers .clear ();
623+ implicitEndOfLifetimeLivenessUses .clear ();
613624 debugValue = nullptr ;
614625 }
615626
@@ -647,8 +658,8 @@ struct UseState {
647658 for (auto *inst : dropDeinitInsts) {
648659 llvm::dbgs () << *inst;
649660 }
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 ) {
652663 llvm::dbgs () << *inst;
653664 }
654665 llvm::dbgs () << " Debug Value User:\n " ;
@@ -680,16 +691,28 @@ struct UseState {
680691 void
681692 initializeLiveness (FieldSensitiveMultiDefPrunedLiveRange &prunedLiveness);
682693
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+ }
685703 return ;
704+ }
686705
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+ }
693716 }
694717 }
695718
@@ -749,7 +772,7 @@ struct UseState {
749772 // An "inout terminator use" is an implicit liveness use of the entire
750773 // value. This is because we need to ensure that our inout value is
751774 // reinitialized along exit paths.
752- if (inoutTermUsers .count (inst))
775+ if (implicitEndOfLifetimeLivenessUses .count (inst))
753776 return true ;
754777
755778 return false ;
@@ -1064,13 +1087,11 @@ void UseState::initializeLiveness(
10641087 liveness.print (llvm::dbgs ()));
10651088 }
10661089
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) {
10741095 liveness.updateForUse (inst, TypeTreeLeafTypeRange (address),
10751096 false /* lifetime ending*/ );
10761097 LLVM_DEBUG (llvm::dbgs () << " Added liveness for inoutTermUser: " << *inst;
@@ -2229,7 +2250,7 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
22292250 if (addressUseState.isLivenessUse (&*ii, errorSpan)) {
22302251 diagnosticEmitter.emitAddressDiagnostic (
22312252 addressUseState.address , &*ii, errorUser, false /* is consuming*/ ,
2232- addressUseState.isInOutTermUser (&*ii));
2253+ addressUseState.isImplicitEndOfLifetimeLivenessUses (&*ii));
22332254 foundSingleBlockError = true ;
22342255 emittedDiagnostic = true ;
22352256 break ;
@@ -2249,8 +2270,9 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
22492270 &*ii, FieldSensitivePrunedLiveness::NonLifetimeEndingUse,
22502271 errorSpan)) {
22512272 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));
22542276 foundSingleBlockError = true ;
22552277 emittedDiagnostic = true ;
22562278 break ;
@@ -2340,7 +2362,8 @@ bool GlobalLivenessChecker::testInstVectorLiveness(
23402362 diagnosticEmitter.emitAddressDiagnostic (
23412363 addressUseState.address , &blockInst, errorUser,
23422364 false /* is consuming*/ ,
2343- addressUseState.isInOutTermUser (&blockInst));
2365+ addressUseState.isImplicitEndOfLifetimeLivenessUses (
2366+ &blockInst));
23442367 foundSingleBlockError = true ;
23452368 emittedDiagnostic = true ;
23462369 break ;
@@ -2567,6 +2590,18 @@ void MoveOnlyAddressCheckerPImpl::insertDestroysOnBoundary(
25672590 continue ;
25682591 }
25692592
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+
25702605 auto *insertPt = inst->getNextInstruction ();
25712606 insertDestroyBeforeInstruction (addressUseState, insertPt,
25722607 liveness.getRootValue (), bits, consumes);
@@ -3121,6 +3156,8 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
31213156 // [copy] + begin_borrow for further processing. This just eliminates a case
31223157 // that the checker doesn't need to know about.
31233158 {
3159+ RAIILLVMDebug l (" CopiedLoadBorrowEliminationVisitor" );
3160+
31243161 CopiedLoadBorrowEliminationState state (markedAddress->getFunction ());
31253162 CopiedLoadBorrowEliminationVisitor copiedLoadBorrowEliminator (state);
31263163 if (AddressUseKind::Unknown ==
@@ -3142,12 +3179,16 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
31423179 // SILGen will treat y as a separate rvalue from x and will create a temporary
31433180 // allocation. In contrast if we have a var, we treat x like an lvalue and
31443181 // 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+ }
31513192 }
31523193
31533194 // Then gather all uses of our address by walking from def->uses. We use this
@@ -3156,10 +3197,16 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
31563197 GatherUsesVisitor visitor (*this , addressUseState, markedAddress,
31573198 diagnosticEmitter);
31583199 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+ }
31633210 }
31643211
31653212 // If we found a load [copy] or copy_addr that requires multiple copies or an
@@ -3182,23 +3229,33 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
31823229 // DISCUSSION: For non address only types, this is not an issue since we
31833230 // eagerly load
31843231
3185- addressUseState.initializeInOutTermUsers ();
3232+ addressUseState.initializeImplicitEndOfLifetimeLivenessUses ();
31863233
31873234 // ===---
31883235 // Liveness Checking
31893236 //
3237+
31903238 SmallVector<SILBasicBlock *, 32 > discoveredBlocks;
31913239 FieldSensitiveMultiDefPrunedLiveRange liveness (fn, markedAddress,
31923240 &discoveredBlocks);
3193- addressUseState.initializeLiveness (liveness);
31943241
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" );
31983244
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+ }
32023259 }
32033260
32043261 // ===
@@ -3242,15 +3299,40 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
32423299// MARK: Top Level Entrypoint
32433300// ===----------------------------------------------------------------------===//
32443301
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+
32453318bool MoveOnlyAddressChecker::check (
32463319 SmallSetVector<MarkMustCheckInst *, 32 > &moveIntroducersToProcess) {
32473320 assert (moveIntroducersToProcess.size () &&
32483321 " Must have checks to process to call this function" );
32493322 MoveOnlyAddressCheckerPImpl pimpl (fn, diagnosticEmitter, domTree, poa,
32503323 allocator);
32513324
3325+ #ifndef NDEBUG
3326+ static uint64_t numProcessed = 0 ;
3327+ #endif
32523328 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);
32543336
32553337 // Perform our address check.
32563338 unsigned diagnosticEmittedByEarlierPassCount =
@@ -3271,5 +3353,10 @@ bool MoveOnlyAddressChecker::check(
32713353 }
32723354 }
32733355
3356+ if (DumpSILBeforeRemovingMarkMustCheck) {
3357+ LLVM_DEBUG (llvm::dbgs ()
3358+ << " Dumping SIL before removing mark must checks!\n " ;
3359+ fn->dump ());
3360+ }
32743361 return pimpl.changed ;
32753362}
0 commit comments