Skip to content

[pmo] NFC. Split addMissingDestroysForCopiedValues into a version for load_borrow and one for load. #28180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 87 additions & 30 deletions lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,9 +427,11 @@ class AvailableValueAggregator {
/// If as a result of us copying values, we may have unconsumed destroys, find
/// the appropriate location and place the values there. Only used when
/// ownership is enabled.
SingleValueInstruction *
addMissingDestroysForCopiedValues(SingleValueInstruction *li,
SILValue newVal);
SingleValueInstruction *addMissingDestroysForCopiedValues(LoadBorrowInst *li,
SILValue newVal);

SingleValueInstruction *addMissingDestroysForCopiedValues(LoadInst *li,
SILValue newVal);

void print(llvm::raw_ostream &os) const;
void dump() const LLVM_ATTRIBUTE_USED;
Expand Down Expand Up @@ -751,14 +753,78 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy,
}

SingleValueInstruction *
AvailableValueAggregator::addMissingDestroysForCopiedValues(
SingleValueInstruction *svi, SILValue newVal) {
AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadInst *li,
SILValue newVal) {
assert(B.hasOwnership() &&
"We assume this is only called if we have ownership");

assert((isa<LoadBorrowInst>(svi) || isa<LoadInst>(svi)) &&
"Expected to have a /real/ load here since we assume that we have a "
"unary operand instruction");
SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
SmallVector<SILBasicBlock *, 8> leakingBlocks;
bool foundLoop = false;
auto loc = RegularLocation::getAutoGeneratedLocation();
while (!insertedInsts.empty()) {
auto *cvi = dyn_cast<CopyValueInst>(insertedInsts.pop_back_val());
if (!cvi)
continue;

// Clear our state.
visitedBlocks.clear();
leakingBlocks.clear();
// The linear lifetime checker doesn't care if the passed in load is
// actually a user of our copy_value. What we care about is that the load is
// guaranteed to be in the block where we have reformed the tuple in a
// consuming manner. This means if we add it as the consuming use of the
// copy, we can find the leaking places if any exist.
//
// Then perform the linear lifetime check. If we succeed, continue. We have
// no further work to do.
auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse;
LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks);
auto error = checker.checkValue(
cvi, {BranchPropagatedUser(&li->getAllOperands()[0])}, {}, errorKind,
&leakingBlocks);
if (!error.getFoundError())
continue;

// Ok, we found some leaking blocks. Since we are using the linear lifetime
// checker with memory, we do not have any guarantees that the store is out
// side of a loop and a load is in a loop. In such a case, we want to
// replace the load with a copy_value.
foundLoop |= error.getFoundOverConsume();

// Ok, we found some leaking blocks. Insert destroys at the
// beginning of these blocks for our copy_value.
for (auto *bb : leakingBlocks) {
SILBuilderWithScope b(bb->begin());
b.emitDestroyValueOperation(loc, cvi);
}
}

// If we didn't find a loop, we are done, just return svi to get RAUWed.
if (!foundLoop) {
return li;
}

// If we found a loop, then we know that our leaking blocks are the exiting
// blocks of the loop and the value has been lifetime extended over the loop.

// If we have a load, we need to put in a copy so that the destroys within
// the loop are properly balanced.
newVal = SILBuilderWithScope(li).emitCopyValueOperation(loc, newVal);

li->replaceAllUsesWith(newVal);
SILValue addr = li->getOperand();
li->eraseFromParent();
if (auto *addrI = addr->getDefiningInstruction())
recursivelyDeleteTriviallyDeadInstructions(addrI);
return nullptr;
}

SingleValueInstruction *
AvailableValueAggregator::addMissingDestroysForCopiedValues(LoadBorrowInst *lbi,
SILValue newVal) {
assert(B.hasOwnership() &&
"We assume this is only called if we have ownership");

SmallPtrSet<SILBasicBlock *, 8> visitedBlocks;
SmallVector<SILBasicBlock *, 8> leakingBlocks;
Expand All @@ -783,7 +849,7 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(
auto errorKind = ownership::ErrorBehaviorKind::ReturnFalse;
LinearLifetimeChecker checker(visitedBlocks, deadEndBlocks);
auto error = checker.checkValue(
cvi, {BranchPropagatedUser(&svi->getAllOperands()[0])}, {}, errorKind,
cvi, {BranchPropagatedUser(&lbi->getAllOperands()[0])}, {}, errorKind,
&leakingBlocks);
if (!error.getFoundError())
continue;
Expand All @@ -808,34 +874,25 @@ AvailableValueAggregator::addMissingDestroysForCopiedValues(
// to borrow at the load point. This means we need to handle the destroying
// of the value along paths reachable from the load_borrow. Luckily that
// will exactly be after the end_borrows of the load_borrow.
if (isa<LoadBorrowInst>(svi)) {
for (auto *use : svi->getUses()) {
if (auto *ebi = dyn_cast<EndBorrowInst>(use->getUser())) {
auto next = std::next(ebi->getIterator());
SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(),
newVal);
}
for (auto *use : lbi->getUses()) {
if (auto *ebi = dyn_cast<EndBorrowInst>(use->getUser())) {
auto next = std::next(ebi->getIterator());
SILBuilderWithScope(next).emitDestroyValueOperation(ebi->getLoc(),
newVal);
}
}
return svi;
return lbi;
}

// If we found a loop, then we know that our leaking blocks are the exiting
// blocks of the loop and the value has been lifetime extended over the loop.
if (isa<LoadInst>(svi)) {
// If we have a load, we need to put in a copy so that the destroys within
// the loop are properly balanced.
newVal = SILBuilderWithScope(svi).emitCopyValueOperation(loc, newVal);
} else {
// If we have a load_borrow, we create a begin_borrow for the end_borrows in
// the loop.
assert(isa<LoadBorrowInst>(svi));
newVal = SILBuilderWithScope(svi).createBeginBorrow(svi->getLoc(), newVal);
}
// If we have a load_borrow, we create a begin_borrow for the end_borrows in
// the loop.
newVal = SILBuilderWithScope(lbi).createBeginBorrow(lbi->getLoc(), newVal);

svi->replaceAllUsesWith(newVal);
SILValue addr = svi->getOperand(0);
svi->eraseFromParent();
lbi->replaceAllUsesWith(newVal);
SILValue addr = lbi->getOperand();
lbi->eraseFromParent();
if (auto *addrI = addr->getDefiningInstruction())
recursivelyDeleteTriviallyDeadInstructions(addrI);
return nullptr;
Expand Down