Skip to content

Commit 20e5577

Browse files
authored
Merge pull request #67089 from gottesmm/release-5.9-111659649
[5.9][move-only] When adding implicit liveness uses for class field/global accesses, do not use the terminator, use the end access.
2 parents b4d63bb + a53cdb0 commit 20e5577

7 files changed

+432
-59
lines changed

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,6 +2365,21 @@ void LifetimeChecker::updateInstructionForInitState(unsigned UseID) {
23652365
assert(!CA->isInitializationOfDest() &&
23662366
"should not modify copy_addr that already knows it is initialized");
23672367
CA->setIsInitializationOfDest(InitKind);
2368+
2369+
// If we had an initialization and had an assignable_but_not_consumable
2370+
// noncopyable type, convert it to be an initable_but_not_consumable so that
2371+
// we do not consume an uninitialized value.
2372+
if (InitKind == IsInitialization) {
2373+
if (auto *mmci =
2374+
dyn_cast<MarkMustCheckInst>(stripAccessMarkers(CA->getDest()))) {
2375+
if (mmci->getCheckKind() ==
2376+
MarkMustCheckInst::CheckKind::AssignableButNotConsumable) {
2377+
mmci->setCheckKind(
2378+
MarkMustCheckInst::CheckKind::InitableButNotConsumable);
2379+
}
2380+
}
2381+
}
2382+
23682383
return;
23692384
}
23702385

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 146 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
293309
static 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+
32453318
bool 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

Comments
 (0)