Skip to content

Commit 99d8138

Browse files
authored
Merge pull request #22232 from gottesmm/pr-cdad162a7c2bbb546bb8ca3115b35fa2f0798ca9
2 parents ebed597 + ac6f318 commit 99d8138

File tree

4 files changed

+223
-73
lines changed

4 files changed

+223
-73
lines changed

lib/SIL/LinearLifetimeChecker.cpp

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,19 @@ struct State {
9898
SILBasicBlock *userBlock);
9999

100100
/// Once we have marked all of our producing blocks.
101-
bool checkPredsForDoubleConsume(BranchPropagatedUser consumingUser,
101+
void checkPredsForDoubleConsume(BranchPropagatedUser consumingUser,
102102
SILBasicBlock *userBlock);
103-
bool checkPredsForDoubleConsume(SILBasicBlock *userBlock);
103+
void checkPredsForDoubleConsume(SILBasicBlock *userBlock);
104104

105105
/// Once we have setup all of our consuming/non-consuming blocks and have
106106
/// validated that all intra-block dataflow is safe, perform the inter-block
107107
/// dataflow.
108-
bool performDataflow(DeadEndBlocks &deBlocks);
108+
void performDataflow(DeadEndBlocks &deBlocks);
109109

110110
/// After we have performed the dataflow, check the end state of our dataflow
111111
/// for validity. If this is a linear typed value, return true. Return false
112112
/// otherwise.
113-
bool checkDataflowEndState(DeadEndBlocks &deBlocks);
113+
void checkDataflowEndState(DeadEndBlocks &deBlocks);
114114
};
115115

116116
} // end anonymous namespace
@@ -166,8 +166,7 @@ void State::initializeAllNonConsumingUses(
166166

167167
void State::initializeAllConsumingUses(
168168
ArrayRef<BranchPropagatedUser> consumingUses,
169-
SmallVectorImpl<std::pair<BranchPropagatedUser, SILBasicBlock *>>
170-
&predsToAddToWorklist) {
169+
SmallVectorImpl<BrPropUserAndBlockPair> &predsToAddToWorklist) {
171170
for (BranchPropagatedUser user : consumingUses) {
172171
SILBasicBlock *userBlock = user.getParent();
173172

@@ -271,41 +270,58 @@ void State::checkForSameBlockUseAfterFree(BranchPropagatedUser consumingUser,
271270
return;
272271
}
273272

274-
bool State::checkPredsForDoubleConsume(BranchPropagatedUser consumingUser,
273+
void State::checkPredsForDoubleConsume(BranchPropagatedUser consumingUser,
275274
SILBasicBlock *userBlock) {
276275
if (!blocksWithConsumingUses.count(userBlock))
277-
return false;
276+
return;
277+
278+
// Check if this is a block that we have already visited. This means that we
279+
// had a back edge of some sort. Double check that we haven't missed any
280+
// successors.
281+
if (visitedBlocks.count(userBlock)) {
282+
for (auto *succ : userBlock->getSuccessorBlocks()) {
283+
if (!visitedBlocks.count(succ)) {
284+
successorBlocksThatMustBeVisited.insert(succ);
285+
}
286+
}
287+
}
278288

279289
error.handleOverConsume([&] {
280290
llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n"
281291
<< "Found over consume?!\n"
282292
<< "Value: " << *value << "User: " << *consumingUser
283293
<< "Block: bb" << userBlock->getDebugID() << "\n\n";
284294
});
285-
286-
// If we reached this point, then we did not assert, but we did flag an
287-
// error. Return true so we continue the walk.
288-
return true;
289295
}
290296

291-
bool State::checkPredsForDoubleConsume(SILBasicBlock *userBlock) {
297+
void State::checkPredsForDoubleConsume(SILBasicBlock *userBlock) {
292298
if (!blocksWithConsumingUses.count(userBlock))
293-
return false;
299+
return;
300+
301+
// Check if this is a block that we have already visited. This means that we
302+
// had a back edge of some sort. Double check that we haven't missed any
303+
// successors.
304+
if (visitedBlocks.count(userBlock)) {
305+
for (auto *succ : userBlock->getSuccessorBlocks()) {
306+
if (!visitedBlocks.count(succ)) {
307+
successorBlocksThatMustBeVisited.insert(succ);
308+
}
309+
}
310+
}
294311

295312
error.handleOverConsume([&] {
296313
llvm::errs() << "Function: '" << value->getFunction()->getName() << "'\n"
297314
<< "Found over consume?!\n"
298315
<< "Value: " << *value << "Block: bb"
299316
<< userBlock->getDebugID() << "\n\n";
300317
});
301-
return true;
302318
}
303319

304320
//===----------------------------------------------------------------------===//
305321
// Dataflow
306322
//===----------------------------------------------------------------------===//
307323

308-
bool State::performDataflow(DeadEndBlocks &deBlocks) {
324+
void State::performDataflow(DeadEndBlocks &deBlocks) {
309325
LLVM_DEBUG(llvm::dbgs() << " Beginning to check dataflow constraints\n");
310326
// Until the worklist is empty...
311327
while (!worklist.empty()) {
@@ -366,11 +382,7 @@ bool State::performDataflow(DeadEndBlocks &deBlocks) {
366382
// 2. We add the predecessor to the worklist if we have not visited it yet.
367383
for (auto *predBlock : block->getPredecessorBlocks()) {
368384
// Check if we have an over consume.
369-
if (checkPredsForDoubleConsume(predBlock)) {
370-
// If we were to assert, it is handled inside check preds for double
371-
// consume. So just return false so that we bail.
372-
return false;
373-
}
385+
checkPredsForDoubleConsume(predBlock);
374386

375387
if (visitedBlocks.count(predBlock)) {
376388
continue;
@@ -380,15 +392,9 @@ bool State::performDataflow(DeadEndBlocks &deBlocks) {
380392
worklist.push_back(predBlock);
381393
}
382394
}
383-
384-
return true;
385395
}
386396

387-
bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
388-
// Make sure that we visited all successor blocks that we needed to visit to
389-
// make sure we didn't leak.
390-
bool doesntHaveAnyLeaks = true;
391-
397+
void State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
392398
if (!successorBlocksThatMustBeVisited.empty()) {
393399
// If we are asked to store any leaking blocks, put them in the leaking
394400
// blocks array.
@@ -412,13 +418,12 @@ bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
412418

413419
// Otherwise... see if we have any other failures. This signals the user
414420
// wants us to tell it where to insert compensating destroys.
415-
doesntHaveAnyLeaks = false;
416421
}
417422

418423
// Make sure that we do not have any lifetime ending uses left to visit that
419424
// are not transitively unreachable blocks.... so return early.
420425
if (blocksWithNonConsumingUses.empty()) {
421-
return doesntHaveAnyLeaks;
426+
return;
422427
}
423428

424429
// If we do have remaining blocks, then these non lifetime ending uses must be
@@ -439,12 +444,7 @@ bool State::checkDataflowEndState(DeadEndBlocks &deBlocks) {
439444
}
440445
llvm::errs() << "\n";
441446
});
442-
return false;
443447
}
444-
445-
// If all of our remaining blocks were dead uses, then return true. We are
446-
// good.
447-
return doesntHaveAnyLeaks;
448448
}
449449

450450
//===----------------------------------------------------------------------===//
@@ -506,19 +506,17 @@ LinearLifetimeError swift::valueHasLinearLifetime(
506506

507507
// Make sure that the predecessor is not in our blocksWithConsumingUses
508508
// list.
509-
if (state.checkPredsForDoubleConsume(user, predBlock)) {
510-
return state.error;
511-
}
509+
state.checkPredsForDoubleConsume(user, predBlock);
512510

513511
if (!state.visitedBlocks.insert(predBlock).second)
514512
continue;
513+
515514
state.worklist.push_back(predBlock);
516515
}
517516

518517
// Now that our algorithm is completely prepared, run the
519518
// dataflow... If we find a failure, return false.
520-
if (!state.performDataflow(deBlocks))
521-
return state.error;
519+
state.performDataflow(deBlocks);
522520

523521
// ...and then check that the end state shows that we have a valid linear
524522
// typed value.

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ class AvailableValueAggregator {
425425
/// If as a result of us copying values, we may have unconsumed destroys, find
426426
/// the appropriate location and place the values there. Only used when
427427
/// ownership is enabled.
428-
void addMissingDestroysForCopiedValues(LoadInst *li);
428+
LoadInst *addMissingDestroysForCopiedValues(LoadInst *li, SILValue newVal);
429429

430430
void print(llvm::raw_ostream &os) const;
431431
void dump() const LLVM_ATTRIBUTE_USED;
@@ -746,51 +746,69 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy,
746746
return eltVal;
747747
}
748748

749-
void AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li) {
749+
LoadInst *
750+
AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li,
751+
SILValue newVal) {
750752
// If ownership is not enabled... bail. We do not need to do this since we do
751753
// not need to insert an extra copy unless we have ownership since without
752754
// ownership stores do not consume.
753755
if (!B.hasOwnership())
754-
return;
756+
return li;
755757

756-
SmallVector<BranchPropagatedUser, 1> consumingUses;
757758
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
758759
SmallVector<SILBasicBlock *, 8> leakingBlocks;
760+
bool foundLoop = false;
761+
auto loc = RegularLocation::getAutoGeneratedLocation();
759762
while (!insertedInsts.empty()) {
760763
auto *cvi = dyn_cast<CopyValueInst>(insertedInsts.pop_back_val());
761764
if (!cvi)
762765
continue;
763766

764-
// Clear our worklist.
765-
consumingUses.clear();
767+
// Clear our state.
766768
visitedBlocks.clear();
767769
leakingBlocks.clear();
768-
769770
// The linear lifetime checker doesn't care if the passed in load is
770771
// actually a user of our copy_value. What we care about is that the load is
771772
// guaranteed to be in the block where we have reformed the tuple in a
772773
// consuming manner. This means if we add it as the consuming use of the
773774
// copy, we can find the leaking places if any exist.
774-
consumingUses.push_back(li);
775-
775+
//
776776
// Then perform the linear lifetime check. If we succeed, continue. We have
777777
// no further work to do.
778-
auto errorKind =
779-
ownership::ErrorBehaviorKind::ReturnFalseOnLeakAssertOtherwise;
780-
auto error =
781-
valueHasLinearLifetime(cvi, consumingUses, {}, visitedBlocks,
782-
deadEndBlocks, errorKind, &leakingBlocks);
778+
auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse;
779+
auto error = valueHasLinearLifetime(
780+
cvi, {li}, {}, visitedBlocks, deadEndBlocks, errorKind, &leakingBlocks);
783781
if (!error.getFoundError())
784782
continue;
785783

784+
// Ok, we found some leaking blocks. Since we are using the linear lifetime
785+
// checker with memory, we do not have any guarantees that the store is out
786+
// side of a loop and a load is in a loop. In such a case, we want to
787+
// replace the load with a copy_value.
788+
foundLoop |= error.getFoundOverConsume();
789+
786790
// Ok, we found some leaking blocks. Insert destroys at the
787791
// beginning of these blocks for our copy_value.
788-
auto loc = RegularLocation::getAutoGeneratedLocation();
789792
for (auto *bb : leakingBlocks) {
790793
SILBuilderWithScope b(bb->begin());
791794
b.emitDestroyValueOperation(loc, cvi);
792795
}
793796
}
797+
798+
// If we didn't find a loop, we are done, just return li to get RAUWed.
799+
if (!foundLoop)
800+
return li;
801+
802+
// If we found a loop, then we know that our leaking blocks are the exiting
803+
// blocks of the loop. Thus we need to change the load inst to a copy_value
804+
// instead of deleting it.
805+
newVal = SILBuilderWithScope(li).emitCopyValueOperation(loc, newVal);
806+
li->replaceAllUsesWith(newVal);
807+
SILValue addr = li->getOperand();
808+
li->eraseFromParent();
809+
if (auto *addrI = addr->getDefiningInstruction())
810+
recursivelyDeleteTriviallyDeadInstructions(addrI);
811+
return nullptr;
794812
}
795813

796814
//===----------------------------------------------------------------------===//
@@ -1374,27 +1392,31 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
13741392
auto *Load = cast<LoadInst>(Inst);
13751393
AvailableValueAggregator Agg(Load, AvailableValues, Uses, deadEndBlocks,
13761394
false /*isTake*/);
1377-
SILValue NewVal = Agg.aggregateValues(LoadTy, Load->getOperand(), FirstElt);
1395+
SILValue newVal = Agg.aggregateValues(LoadTy, Load->getOperand(), FirstElt);
1396+
1397+
LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *Load << "\n");
1398+
LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n");
13781399

13791400
// If we inserted any copies, we created the copies at our stores. We know
13801401
// that in our load block, we will reform the aggregate as appropriate at the
13811402
// load implying that the value /must/ be fully consumed. Thus any leaking
13821403
// blocks that we may have can be found by performing a linear lifetime check
13831404
// over all copies that we found using the load as the "consuming uses" (just
13841405
// for the purposes of identifying the consuming block).
1385-
Agg.addMissingDestroysForCopiedValues(Load);
1406+
auto *oldLoad = Agg.addMissingDestroysForCopiedValues(Load, newVal);
13861407

13871408
++NumLoadPromoted;
1388-
1389-
// Simply replace the load.
1390-
LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *Load << "\n");
1391-
LLVM_DEBUG(llvm::dbgs() << " To value: " << *NewVal << "\n");
1392-
1393-
Load->replaceAllUsesWith(NewVal);
1394-
SILValue Addr = Load->getOperand();
1395-
Load->eraseFromParent();
1396-
if (auto *AddrI = Addr->getDefiningInstruction())
1397-
recursivelyDeleteTriviallyDeadInstructions(AddrI);
1409+
1410+
// If we are returned the load, eliminate it. Otherwise, it was already
1411+
// handled for us... so return true.
1412+
if (!oldLoad)
1413+
return true;
1414+
1415+
oldLoad->replaceAllUsesWith(newVal);
1416+
SILValue addr = oldLoad->getOperand();
1417+
oldLoad->eraseFromParent();
1418+
if (auto *addrI = addr->getDefiningInstruction())
1419+
recursivelyDeleteTriviallyDeadInstructions(addrI);
13981420
return true;
13991421
}
14001422

0 commit comments

Comments
 (0)