Skip to content

[CopyPropagation] Added lexical destroy folding. #41113

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
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions include/swift/SIL/PrunedLiveness.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ class PrunedLiveness {
bool areUsesWithinBoundary(ArrayRef<Operand *> uses,
DeadEndBlocks *deadEndBlocks) const;

/// \p deadEndBlocks is optional.
bool areUsesOutsideBoundary(ArrayRef<Operand *> uses,
DeadEndBlocks *deadEndBlocks) const;

/// Compute liveness for a single SSA definition.
void computeSSALiveness(SILValue def);
};
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SILOptimizer/Analysis/Reachability.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace swift {
///
/// // Mark the beginning of a block reachable. Only called once per block.
/// // Typically a BasicBlockSet wrapper.
/// boid markReachableBegin(SILBasicBlock *block);
/// void markReachableBegin(SILBasicBlock *block);
///
/// // Mark the end of a block reachable. Only called once per block.
/// // Typically a BasicBlockSet wrapper.
Expand Down
17 changes: 17 additions & 0 deletions include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,23 @@ class CanonicalizeOSSALifetime {
CanonicalOSSAConsumeInfo consumes;

public:
/// When rewriting destroys, is this an instruction which destroys should not
/// be hoisted over to avoid churn and infinite looping.
static bool ignoredByDestroyHoisting(SILInstructionKind kind) {
switch (kind) {
case SILInstructionKind::DestroyValueInst:
case SILInstructionKind::CopyValueInst:
case SILInstructionKind::BeginBorrowInst:
case SILInstructionKind::EndBorrowInst:
case SILInstructionKind::FunctionRefInst:
case SILInstructionKind::EnumInst:
case SILInstructionKind::StructInst:
return true;
default:
return false;
}
}

void maybeNotifyMoveOnlyCopy(Operand *use) {
if (!moveOnlyCopyValueNotification)
return;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ bool shrinkBorrowScope(
BeginBorrowInst *bbi, InstructionDeleter &deleter,
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts);

bool foldDestroysOfCopiedLexicalBorrow(BeginBorrowInst *bbi,
DominanceInfo &dominanceTree,
InstructionDeleter &deleter);

} // namespace swift

#endif // SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEBORROWSCOPES_H
14 changes: 14 additions & 0 deletions lib/SIL/Utils/PrunedLiveness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@ bool PrunedLiveness::areUsesWithinBoundary(ArrayRef<Operand *> uses,
return true;
}

bool PrunedLiveness::areUsesOutsideBoundary(
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
};

for (auto *use : uses) {
auto *user = use->getUser();
if (isWithinBoundary(user) || checkDeadEnd(user))
return false;
}
return true;
}

// An SSA def meets all the criteria for pruned liveness--def dominates all uses
// with no holes in the liverange. The lifetime-ending uses are also
// recorded--destroy_value or end_borrow. However destroy_values may not
Expand Down
45 changes: 37 additions & 8 deletions lib/SILOptimizer/Transforms/CopyPropagation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,21 +434,50 @@ void CopyPropagation::run() {
}
}

// canonicalizer performs all modifications through deleter's callbacks, so we
// don't need to explicitly check for changes.
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
accessBlockAnalysis, domTree, deleter);

// NOTE: We assume that the function is in reverse post order so visiting the
// blocks and pushing begin_borrows as we see them and then popping them
// off the end will result in shrinking inner borrow scopes first.
while (auto *bbi = beginBorrowsToShrink.pop()) {
SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
changed |= shrinkBorrowScope(bbi, deleter, modifiedCopyValueInsts);
for (auto *cvi : modifiedCopyValueInsts)
defWorklist.updateForCopy(cvi);
bool firstRun = true;
// Run the sequence of utilities:
// - ShrinkBorrowScope
// - CanonicalizeOSSALifetime
// - LexicalDestroyFolder
// at least once and then until each stops making changes.
while (true) {
SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
auto shrunk = shrinkBorrowScope(bbi, deleter, modifiedCopyValueInsts);
for (auto *cvi : modifiedCopyValueInsts)
defWorklist.updateForCopy(cvi);
changed |= shrunk;
if (!shrunk && !firstRun)
break;

// If borrowed value is not owned, neither CanonicalizeOSSALifetime nor
// LexicalDestroyFolding will do anything with it. Just bail out now.
auto borrowee = bbi->getOperand();
if (borrowee.getOwnershipKind() != OwnershipKind::Owned)
break;

auto canonicalized = canonicalizer.canonicalizeValueLifetime(borrowee);
if (!canonicalized && !firstRun)
break;

auto folded = foldDestroysOfCopiedLexicalBorrow(bbi, *domTree, deleter);
if (!folded)
break;
// We don't add the produced move_value instruction to the worklist
// because it can't handle propagation.
firstRun = false;
}
}
deleter.cleanupDeadInstructions();

// canonicalizer performs all modifications through deleter's callbacks, so we
// don't need to explicitly check for changes.
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
accessBlockAnalysis, domTree, deleter);
// For now, only modify forwarding instructions
// At -Onone, we do nothing but rewrite copies of owned values.
if (canonicalizeBorrows) {
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target_sources(swiftSILOptimizer PRIVATE
InstructionDeleter.cpp
InstOptUtils.cpp
KeyPathProjector.cpp
LexicalDestroyFolding.cpp
LoadStoreOptUtils.cpp
LoopUtils.cpp
OptimizerStatsUtils.cpp
Expand Down
7 changes: 4 additions & 3 deletions lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,10 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
return;
}
// This is not a potential last user. Keep scanning.
// Allow lifetimes to be artificially extended up to the next call. The goal
// is to prevent repeated destroy rewriting without inhibiting optimization.
if (ApplySite::isa(inst)) {
// Allow lifetimes to be artificially extended up to the next non-ignored
// instruction. The goal is to prevent repeated destroy rewriting without
// inhibiting optimization.
if (!ignoredByDestroyHoisting(inst->getKind())) {
existingDestroy = nullptr;
} else if (!existingDestroy) {
if (auto *destroy = dyn_cast<DestroyValueInst>(inst)) {
Expand Down
Loading