Skip to content

Commit fdc4004

Browse files
Merge pull request #41377 from nate-chandler/lexical_lifetimes/destroy_hoisting/remove-from-copy_forwarding
[CopyForwarding] Removed destroy hoisting.
2 parents b571eb3 + 4e2667b commit fdc4004

File tree

5 files changed

+16
-563
lines changed

5 files changed

+16
-563
lines changed

lib/SILOptimizer/Transforms/CopyForwarding.cpp

Lines changed: 12 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ using namespace swift;
8686
// Temporary debugging flag until this pass is better tested.
8787
static llvm::cl::opt<bool> EnableCopyForwarding("enable-copyforwarding",
8888
llvm::cl::init(true));
89-
static llvm::cl::opt<bool> EnableDestroyHoisting("enable-destroyhoisting",
90-
llvm::cl::init(true));
9189

9290
/// \return true if the given copy source value can only be accessed via the
9391
/// given def (this def uniquely identifies the object).
@@ -513,7 +511,6 @@ class CopyForwarding {
513511
PostOrderAnalysis *PostOrder;
514512
DominanceAnalysis *DomAnalysis;
515513
RCIdentityAnalysis *RCIAnalysis;
516-
bool DoGlobalHoisting;
517514
bool HasChanged;
518515
bool HasChangedCFG;
519516

@@ -535,7 +532,6 @@ class CopyForwarding {
535532
SmallPtrSet<SILInstruction*, 16> SrcUserInsts;
536533
SmallPtrSet<SILInstruction*, 4> SrcDebugValueInsts;
537534
SmallVector<CopyAddrInst*, 4> TakePoints;
538-
SmallPtrSet<SILInstruction *, 16> StoredValueUserInsts;
539535
SmallVector<DestroyAddrInst*, 4> DestroyPoints;
540536
SmallPtrSet<SILBasicBlock*, 32> DeadInBlocks;
541537

@@ -579,7 +575,7 @@ class CopyForwarding {
579575
CopyForwarding(PostOrderAnalysis *PO, DominanceAnalysis *DA,
580576
RCIdentityAnalysis *RCIAnalysis)
581577
: PostOrder(PO), DomAnalysis(DA), RCIAnalysis(RCIAnalysis),
582-
DoGlobalHoisting(false), HasChanged(false), HasChangedCFG(false),
578+
HasChanged(false), HasChangedCFG(false),
583579
IsSrcLoadedFrom(false), HasUnknownStoredValue(false),
584580
HasForwardedToCopy(false), CurrentCopy(nullptr) {}
585581

@@ -590,7 +586,6 @@ class CopyForwarding {
590586
// some alloc_stack cases after global destroy hoisting. CopyForwarding will
591587
// be reapplied after the transparent function is inlined at which point
592588
// global hoisting will be done.
593-
DoGlobalHoisting = !F->isTransparent();
594589
if (HasChangedCFG) {
595590
// We are only invalidating the analysis that we use internally.
596591
// We'll invalidate the analysis that are used by other passes at the end.
@@ -605,7 +600,6 @@ class CopyForwarding {
605600
SrcUserInsts.clear();
606601
SrcDebugValueInsts.clear();
607602
TakePoints.clear();
608-
StoredValueUserInsts.clear();
609603
DestroyPoints.clear();
610604
DeadInBlocks.clear();
611605
CurrentCopy = nullptr;
@@ -621,15 +615,13 @@ class CopyForwarding {
621615
void forwardCopiesOf(SILValue Def, SILFunction *F);
622616

623617
protected:
624-
bool propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy);
618+
bool propagateCopy(CopyAddrInst *CopyInst);
625619
CopyAddrInst *findCopyIntoDeadTemp(
626620
CopyAddrInst *destCopy,
627621
SmallVectorImpl<SILInstruction *> &debugInstsToDelete);
628622
bool forwardDeadTempCopy(CopyAddrInst *destCopy);
629623
bool forwardPropagateCopy();
630624
bool backwardPropagateCopy();
631-
bool hoistDestroy(SILInstruction *DestroyPoint, SILLocation DestroyLoc,
632-
SmallVectorImpl<SILInstruction *> &debugInstsToDelete);
633625

634626
bool isSourceDeadAtCopy();
635627

@@ -671,7 +663,7 @@ class CopyDestUserVisitor : public AddressUserVisitor {
671663
/// If the forwarded copy is not an [init], then insert a destroy of the copy's
672664
/// dest.
673665
bool CopyForwarding::
674-
propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) {
666+
propagateCopy(CopyAddrInst *CopyInst) {
675667
if (!EnableCopyForwarding)
676668
return false;
677669

@@ -693,7 +685,7 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) {
693685

694686
// Handle copy-of-copy without analyzing uses.
695687
// Assumes that CurrentCopy->getSrc() is dead after CurrentCopy.
696-
assert(CurrentCopy->isTakeOfSrc() || hoistingDestroy);
688+
assert(CurrentCopy->isTakeOfSrc());
697689
if (forwardDeadTempCopy(CurrentCopy)) {
698690
HasChanged = true;
699691
++NumDeadTemp;
@@ -703,8 +695,7 @@ propagateCopy(CopyAddrInst *CopyInst, bool hoistingDestroy) {
703695
if (forwardPropagateCopy()) {
704696
LLVM_DEBUG(llvm::dbgs() << " Forwarding Copy:" << *CurrentCopy);
705697
if (!CurrentCopy->isInitializationOfDest()) {
706-
// Replace the original copy with a destroy. We may be able to hoist it
707-
// more in another pass but don't currently iterate.
698+
// Replace the original copy with a destroy.
708699
SILBuilderWithScope(CurrentCopy)
709700
.createDestroyAddr(CurrentCopy->getLoc(), CurrentCopy->getDest());
710701
}
@@ -832,16 +823,7 @@ forwardDeadTempCopy(CopyAddrInst *destCopy) {
832823
deadDebugUser->eraseFromParent();
833824
}
834825

835-
// Either `destCopy` is a take, or the caller is hoisting a destroy:
836-
// copy_addr %temp, %dest
837-
// ...
838-
// destroy %temp
839-
//
840-
// If the caller is hoisting a destroy, and we return `true` then it will
841-
// erase the destroy for us. Either way, it's safe to simply rewrite destCopy.
842-
// For now, don't bother finding the subsequent destroy, because this isn't
843-
// the common case.
844-
826+
// `destCopy` is a take. It's safe to simply rewrite destCopy.
845827
destCopy->setSrc(srcCopy->getSrc());
846828
destCopy->setIsTakeOfSrc(srcCopy->isTakeOfSrc());
847829
srcCopy->eraseFromParent();
@@ -878,8 +860,7 @@ bool CopyForwarding::doesCopyDominateDestUsers(
878860
return true;
879861
}
880862

881-
// Add all recognized users of storedValue to StoredValueUserInsts. Return true
882-
// if all users were recgonized.
863+
// Return true if all users were recgonized.
883864
//
884865
// To find all SSA users of storedValue, we first find the RC root, then search
885866
// past any instructions that may propagate the reference.
@@ -900,19 +881,16 @@ bool CopyForwarding::markStoredValueUsers(SILValue storedValue) {
900881
// Recognize any uses that have no results as normal uses. They cannot
901882
// transitively propagate a reference.
902883
if (user->getResults().empty()) {
903-
StoredValueUserInsts.insert(user);
904884
continue;
905885
}
906886
// Recognize full applies as normal uses. They may transitively retain, but
907887
// the caller cannot rely on that.
908888
if (FullApplySite::isa(user)) {
909-
StoredValueUserInsts.insert(user);
910889
continue;
911890
}
912891
// A single-valued use is nontransitive if its result is trivial.
913892
if (auto *SVI = dyn_cast<SingleValueInstruction>(user)) {
914893
if (SVI->getType().isTrivial(*F)) {
915-
StoredValueUserInsts.insert(user);
916894
continue;
917895
}
918896
}
@@ -957,8 +935,7 @@ static DeallocStackInst *getSingleDealloc(AllocStackInst *ASI) {
957935
/// If the last use (deinit) is a copy, replace it with a destroy+copy[init].
958936
///
959937
/// The caller has already guaranteed that the lifetime of the copy's source
960-
/// ends at this copy. Either the copy is a [take] or a destroy can be hoisted
961-
/// to the copy.
938+
/// ends at this copy. The copy is a [take].
962939
bool CopyForwarding::forwardPropagateCopy() {
963940

964941
SILValue CopyDest = CurrentCopy->getDest();
@@ -1221,98 +1198,6 @@ bool CopyForwarding::backwardPropagateCopy() {
12211198
return true;
12221199
}
12231200

1224-
/// Attempt to hoist a destroy point up to the last use. If the last use is a
1225-
/// copy, eliminate both the copy and the destroy.
1226-
///
1227-
/// The copy will be eliminated if the original is not accessed between the
1228-
/// point of copy and the original's destruction.
1229-
///
1230-
/// CurrentDef = <uniquely identified> // no aliases
1231-
/// ...
1232-
/// Copy = copy_addr [init] Def
1233-
/// ... // no access to CurrentDef
1234-
/// destroy_addr Def
1235-
///
1236-
/// Return true if a destroy was inserted, forwarded from a copy, or the
1237-
/// block was marked dead-in.
1238-
/// \p debugInstsToDelete will contain the debug_value users of copy src
1239-
/// that need to be deleted if the hoist is successful
1240-
bool CopyForwarding::hoistDestroy(
1241-
SILInstruction *DestroyPoint, SILLocation DestroyLoc,
1242-
SmallVectorImpl<SILInstruction *> &debugInstsToDelete) {
1243-
if (!EnableDestroyHoisting)
1244-
return false;
1245-
1246-
assert(!SrcUserInsts.count(DestroyPoint) && "caller should check terminator");
1247-
SILBasicBlock *BB = DestroyPoint->getParent();
1248-
1249-
// If DestroyPoint is a block terminator, we must hoist.
1250-
bool MustHoist = (DestroyPoint == BB->getTerminator());
1251-
// If we haven't seen anything significant, avoid useless hoisting.
1252-
bool ShouldHoist = MustHoist;
1253-
1254-
auto tryToInsertHoistedDestroyAfter = [&](SILInstruction *afterInst) {
1255-
if (!ShouldHoist)
1256-
return false;
1257-
LLVM_DEBUG(llvm::dbgs() << " Hoisting to Use:" << *afterInst);
1258-
SILBuilderWithScope(std::next(afterInst->getIterator()), afterInst)
1259-
.createDestroyAddr(DestroyLoc, CurrentDef);
1260-
HasChanged = true;
1261-
return true;
1262-
};
1263-
1264-
auto SI = DestroyPoint->getIterator(), SE = BB->begin();
1265-
while (SI != SE) {
1266-
--SI;
1267-
SILInstruction *Inst = &*SI;
1268-
if (!SrcUserInsts.count(Inst)) {
1269-
if (StoredValueUserInsts.count(Inst)) {
1270-
// The current definition may take ownership of a value stored into its
1271-
// address. Its lifetime cannot end before the last use of that stored
1272-
// value.
1273-
// CurrentDef = ...
1274-
// Copy = copy_addr CurrentDef to ...
1275-
// store StoredValue to CurrentDef
1276-
// ... // no access to CurrentDef
1277-
// retain StoredValue
1278-
// destroy_addr CurrentDef
1279-
LLVM_DEBUG(llvm::dbgs() << " Cannot hoist above stored value use:"
1280-
<< *Inst);
1281-
return tryToInsertHoistedDestroyAfter(Inst);
1282-
}
1283-
if (isa<DebugValueInst>(Inst)) {
1284-
// Collect debug_value uses of copy src. If the hoist is
1285-
// successful, these instructions will have consumed operand and need to
1286-
// be erased.
1287-
if (SrcDebugValueInsts.contains(Inst)) {
1288-
debugInstsToDelete.push_back(Inst);
1289-
}
1290-
}
1291-
if (!ShouldHoist && isa<ApplyInst>(Inst))
1292-
ShouldHoist = true;
1293-
continue;
1294-
}
1295-
if (auto *CopyInst = dyn_cast<CopyAddrInst>(Inst)) {
1296-
if (!CopyInst->isTakeOfSrc() && CopyInst->getSrc() == CurrentDef) {
1297-
// This use is a copy of CurrentDef. Attempt to forward CurrentDef to
1298-
// all uses of the copy's value.
1299-
if (propagateCopy(CopyInst, /*hoistingDestroy=*/true))
1300-
return true;
1301-
}
1302-
}
1303-
return tryToInsertHoistedDestroyAfter(Inst);
1304-
}
1305-
if (!DoGlobalHoisting) {
1306-
// If DoGlobalHoisting is set, then we should never mark a DeadInBlock, so
1307-
// MustHoist should be false.
1308-
assert(!MustHoist &&
1309-
"Cannot hoist above a terminator with global hoisting disabled.");
1310-
return false;
1311-
}
1312-
DeadInBlocks.insert(BB);
1313-
return true;
1314-
}
1315-
13161201
/// Perform CopyForwarding on the current Def.
13171202
void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) {
13181203
reset(F);
@@ -1322,97 +1207,9 @@ void CopyForwarding::forwardCopiesOf(SILValue Def, SILFunction *F) {
13221207
if (!visitAddressUsers(Def, nullptr, visitor))
13231208
return;
13241209

1325-
// First forward any copies that implicitly destroy CurrentDef. There is no
1326-
// need to hoist Destroy for these.
1210+
// Forward any copies that implicitly destroy CurrentDef.
13271211
for (auto *CopyInst : TakePoints) {
1328-
propagateCopy(CopyInst, /*hoistingDestroy=*/false);
1329-
}
1330-
// If the copied address is also loaded from, then destroy hoisting is unsafe.
1331-
//
1332-
// TODO: Record all loads during collectUsers. Implement findRetainPoints to
1333-
// peek though projections of the load, like unchecked_enum_data to find the
1334-
// true extent of the lifetime including transitively referenced objects.
1335-
if (IsSrcLoadedFrom || HasUnknownStoredValue)
1336-
return;
1337-
1338-
bool HoistedDestroyFound = false;
1339-
SILLocation HoistedDestroyLoc = F->getLocation();
1340-
const SILDebugScope *HoistedDebugScope = nullptr;
1341-
SmallVector<SILInstruction *, 2> debugInstsToDelete;
1342-
1343-
for (auto *Destroy : DestroyPoints) {
1344-
// If hoistDestroy returns false, it was not worth hoisting.
1345-
if (hoistDestroy(Destroy, Destroy->getLoc(), debugInstsToDelete)) {
1346-
// Propagate DestroyLoc for any destroy hoisted above a block.
1347-
if (DeadInBlocks.count(Destroy->getParent())) {
1348-
HoistedDestroyLoc = Destroy->getLoc();
1349-
HoistedDebugScope = Destroy->getDebugScope();
1350-
HoistedDestroyFound = true;
1351-
}
1352-
// We either just created a new destroy, forwarded a copy, or will
1353-
// continue propagating from this dead-in block. In any case, erase the
1354-
// original Destroy.
1355-
Destroy->eraseFromParent();
1356-
assert(HasChanged || !DeadInBlocks.empty() && "HasChanged should be set");
1357-
// Since the hoist was successful, delete all dead debug_value
1358-
for (auto *deadDebugUser : debugInstsToDelete) {
1359-
deadDebugUser->eraseFromParent();
1360-
}
1361-
}
1362-
debugInstsToDelete.clear();
1363-
}
1364-
// Any blocks containing a DestroyPoints where hoistDestroy did not find a use
1365-
// are now marked in DeadInBlocks.
1366-
if (DeadInBlocks.empty())
1367-
return;
1368-
1369-
assert(HoistedDestroyFound && "Hoisted destroy should have been found");
1370-
1371-
DestroyPoints.clear();
1372-
1373-
// Propagate dead-in blocks upward via PostOrder traversal.
1374-
// TODO: We could easily handle hoisting above loops if LoopInfo is available.
1375-
//
1376-
for (auto *BB : PostOrder->get(F)->getPostOrder()) {
1377-
SmallVector<unsigned, 4> DeadInSuccs;
1378-
ArrayRef<SILSuccessor> Succs = BB->getSuccessors();
1379-
if (Succs.empty())
1380-
continue;
1381-
1382-
for (unsigned EdgeIdx = 0, End = Succs.size(); EdgeIdx != End; ++EdgeIdx) {
1383-
if (DeadInBlocks.count(Succs[EdgeIdx].getBB()))
1384-
DeadInSuccs.push_back(EdgeIdx);
1385-
}
1386-
if (DeadInSuccs.size() == Succs.size() &&
1387-
!SrcUserInsts.count(BB->getTerminator())) {
1388-
// All successors are dead, so continue hoisting.
1389-
bool WasHoisted = hoistDestroy(BB->getTerminator(), HoistedDestroyLoc,
1390-
debugInstsToDelete);
1391-
(void)WasHoisted;
1392-
assert(WasHoisted && "should always hoist above a terminator");
1393-
for (auto *deadDebugUser : debugInstsToDelete) {
1394-
deadDebugUser->eraseFromParent();
1395-
}
1396-
debugInstsToDelete.clear();
1397-
continue;
1398-
}
1399-
// Emit a destroy on each CFG edge leading to a dead-in block. This requires
1400-
// splitting critical edges and will naturally handle redundant branch
1401-
// targets.
1402-
for (unsigned EdgeIdx : DeadInSuccs) {
1403-
SILBasicBlock *SuccBB = splitCriticalEdge(BB->getTerminator(), EdgeIdx);
1404-
if (SuccBB)
1405-
HasChangedCFG = true;
1406-
else
1407-
SuccBB = BB->getSuccessors()[EdgeIdx];
1408-
1409-
// We make no attempt to use the best DebugLoc, because in all known
1410-
// cases, we only have one.
1411-
SILBuilder B(SuccBB->begin());
1412-
B.setCurrentDebugScope(HoistedDebugScope);
1413-
B.createDestroyAddr(HoistedDestroyLoc, CurrentDef);
1414-
HasChanged = true;
1415-
}
1212+
propagateCopy(CopyInst);
14161213
}
14171214
}
14181215

@@ -1539,7 +1336,7 @@ static llvm::cl::opt<int> ForwardStop("copy-forward-stop",
15391336
class CopyForwardingPass : public SILFunctionTransform
15401337
{
15411338
void run() override {
1542-
if (!EnableCopyForwarding && !EnableDestroyHoisting)
1339+
if (!EnableCopyForwarding)
15431340
return;
15441341

15451342
// This pass assumes that the ownership lifetime of a value in a memory
@@ -1570,7 +1367,7 @@ class CopyForwardingPass : public SILFunctionTransform
15701367
for (auto &BB : *getFunction())
15711368
for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) {
15721369
if (auto *CopyInst = dyn_cast<CopyAddrInst>(&*II)) {
1573-
if (EnableDestroyHoisting && canNRVO(CopyInst)) {
1370+
if (canNRVO(CopyInst)) {
15741371
NRVOCopies.push_back(CopyInst);
15751372
continue;
15761373
}

test/SILOptimizer/copyforward.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding -enable-destroyhoisting | %FileCheck %s
1+
// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all %s -copy-forwarding -enable-copyforwarding | %FileCheck %s
22

33
// CopyForwarding currently only runs on OSSA. This file only contains
44
// tests that will break is CopyForwarding were to run on non-OSSA SIL.

0 commit comments

Comments
 (0)