Skip to content

Commit fe74a7d

Browse files
Merge pull request #41113 from nate-chandler/lexical_lifetimes/destroy_folding/add
[CopyPropagation] Added lexical destroy folding.
2 parents 460b370 + 257e1d3 commit fe74a7d

14 files changed

+1702
-50
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ class PrunedLiveness {
338338
bool areUsesWithinBoundary(ArrayRef<Operand *> uses,
339339
DeadEndBlocks *deadEndBlocks) const;
340340

341+
/// \p deadEndBlocks is optional.
342+
bool areUsesOutsideBoundary(ArrayRef<Operand *> uses,
343+
DeadEndBlocks *deadEndBlocks) const;
344+
341345
/// Compute liveness for a single SSA definition.
342346
void computeSSALiveness(SILValue def);
343347
};

include/swift/SILOptimizer/Analysis/Reachability.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace swift {
4343
///
4444
/// // Mark the beginning of a block reachable. Only called once per block.
4545
/// // Typically a BasicBlockSet wrapper.
46-
/// boid markReachableBegin(SILBasicBlock *block);
46+
/// void markReachableBegin(SILBasicBlock *block);
4747
///
4848
/// // Mark the end of a block reachable. Only called once per block.
4949
/// // Typically a BasicBlockSet wrapper.

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,23 @@ class CanonicalizeOSSALifetime {
313313
CanonicalOSSAConsumeInfo consumes;
314314

315315
public:
316+
/// When rewriting destroys, is this an instruction which destroys should not
317+
/// be hoisted over to avoid churn and infinite looping.
318+
static bool ignoredByDestroyHoisting(SILInstructionKind kind) {
319+
switch (kind) {
320+
case SILInstructionKind::DestroyValueInst:
321+
case SILInstructionKind::CopyValueInst:
322+
case SILInstructionKind::BeginBorrowInst:
323+
case SILInstructionKind::EndBorrowInst:
324+
case SILInstructionKind::FunctionRefInst:
325+
case SILInstructionKind::EnumInst:
326+
case SILInstructionKind::StructInst:
327+
return true;
328+
default:
329+
return false;
330+
}
331+
}
332+
316333
void maybeNotifyMoveOnlyCopy(Operand *use) {
317334
if (!moveOnlyCopyValueNotification)
318335
return;

include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ bool shrinkBorrowScope(
151151
BeginBorrowInst *bbi, InstructionDeleter &deleter,
152152
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts);
153153

154+
bool foldDestroysOfCopiedLexicalBorrow(BeginBorrowInst *bbi,
155+
DominanceInfo &dominanceTree,
156+
InstructionDeleter &deleter);
157+
154158
} // namespace swift
155159

156160
#endif // SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEBORROWSCOPES_H

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,20 @@ bool PrunedLiveness::areUsesWithinBoundary(ArrayRef<Operand *> uses,
167167
return true;
168168
}
169169

170+
bool PrunedLiveness::areUsesOutsideBoundary(
171+
ArrayRef<Operand *> uses, DeadEndBlocks *deadEndBlocks) const {
172+
auto checkDeadEnd = [deadEndBlocks](SILInstruction *inst) {
173+
return deadEndBlocks && deadEndBlocks->isDeadEnd(inst->getParent());
174+
};
175+
176+
for (auto *use : uses) {
177+
auto *user = use->getUser();
178+
if (isWithinBoundary(user) || checkDeadEnd(user))
179+
return false;
180+
}
181+
return true;
182+
}
183+
170184
// An SSA def meets all the criteria for pruned liveness--def dominates all uses
171185
// with no holes in the liverange. The lifetime-ending uses are also
172186
// recorded--destroy_value or end_borrow. However destroy_values may not

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -434,21 +434,50 @@ void CopyPropagation::run() {
434434
}
435435
}
436436

437+
// canonicalizer performs all modifications through deleter's callbacks, so we
438+
// don't need to explicitly check for changes.
439+
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
440+
accessBlockAnalysis, domTree, deleter);
441+
437442
// NOTE: We assume that the function is in reverse post order so visiting the
438443
// blocks and pushing begin_borrows as we see them and then popping them
439444
// off the end will result in shrinking inner borrow scopes first.
440445
while (auto *bbi = beginBorrowsToShrink.pop()) {
441-
SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
442-
changed |= shrinkBorrowScope(bbi, deleter, modifiedCopyValueInsts);
443-
for (auto *cvi : modifiedCopyValueInsts)
444-
defWorklist.updateForCopy(cvi);
446+
bool firstRun = true;
447+
// Run the sequence of utilities:
448+
// - ShrinkBorrowScope
449+
// - CanonicalizeOSSALifetime
450+
// - LexicalDestroyFolder
451+
// at least once and then until each stops making changes.
452+
while (true) {
453+
SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
454+
auto shrunk = shrinkBorrowScope(bbi, deleter, modifiedCopyValueInsts);
455+
for (auto *cvi : modifiedCopyValueInsts)
456+
defWorklist.updateForCopy(cvi);
457+
changed |= shrunk;
458+
if (!shrunk && !firstRun)
459+
break;
460+
461+
// If borrowed value is not owned, neither CanonicalizeOSSALifetime nor
462+
// LexicalDestroyFolding will do anything with it. Just bail out now.
463+
auto borrowee = bbi->getOperand();
464+
if (borrowee.getOwnershipKind() != OwnershipKind::Owned)
465+
break;
466+
467+
auto canonicalized = canonicalizer.canonicalizeValueLifetime(borrowee);
468+
if (!canonicalized && !firstRun)
469+
break;
470+
471+
auto folded = foldDestroysOfCopiedLexicalBorrow(bbi, *domTree, deleter);
472+
if (!folded)
473+
break;
474+
// We don't add the produced move_value instruction to the worklist
475+
// because it can't handle propagation.
476+
firstRun = false;
477+
}
445478
}
446479
deleter.cleanupDeadInstructions();
447480

448-
// canonicalizer performs all modifications through deleter's callbacks, so we
449-
// don't need to explicitly check for changes.
450-
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
451-
accessBlockAnalysis, domTree, deleter);
452481
// For now, only modify forwarding instructions
453482
// At -Onone, we do nothing but rewrite copies of owned values.
454483
if (canonicalizeBorrows) {

lib/SILOptimizer/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ target_sources(swiftSILOptimizer PRIVATE
1818
InstructionDeleter.cpp
1919
InstOptUtils.cpp
2020
KeyPathProjector.cpp
21+
LexicalDestroyFolding.cpp
2122
LoadStoreOptUtils.cpp
2223
LoopUtils.cpp
2324
OptimizerStatsUtils.cpp

lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,10 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
492492
return;
493493
}
494494
// This is not a potential last user. Keep scanning.
495-
// Allow lifetimes to be artificially extended up to the next call. The goal
496-
// is to prevent repeated destroy rewriting without inhibiting optimization.
497-
if (ApplySite::isa(inst)) {
495+
// Allow lifetimes to be artificially extended up to the next non-ignored
496+
// instruction. The goal is to prevent repeated destroy rewriting without
497+
// inhibiting optimization.
498+
if (!ignoredByDestroyHoisting(inst->getKind())) {
498499
existingDestroy = nullptr;
499500
} else if (!existingDestroy) {
500501
if (auto *destroy = dyn_cast<DestroyValueInst>(inst)) {

0 commit comments

Comments
 (0)