Skip to content

Commit c3c2b84

Browse files
committed
[ownership] Change CanonicalOSSALifetime to use an InstModCallback.
This enables passes to use this as a utility that properly composes with how the pass maintains its state. If an InstModCallback isn't passed in, we use the default InstModCallback which should be cheap (always succeeding check for nullptr + call inline default callback).
1 parent ed8b1b2 commit c3c2b84

File tree

3 files changed

+76
-44
lines changed

3 files changed

+76
-44
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
#include "swift/SIL/SILInstruction.h"
9898
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
9999
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
100+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
100101
#include "swift/SILOptimizer/Utils/PrunedLiveness.h"
101102
#include "llvm/ADT/DenseMap.h"
102103
#include "llvm/ADT/SetVector.h"
@@ -107,13 +108,19 @@ namespace swift {
107108
/// result or invalid SILValue. The caller must delete the extract and its
108109
/// now-dead copy use.
109110
///
110-
// If a copied-def is a struct-extract, attempt a destructure conversion
111-
// %extract = struct_extract %... : $TypeWithSingleOwnershipValue
112-
// %copy = copy_value %extract : $OwnershipValue
113-
// To:
114-
// %copy = copy_value %extract : $TypeWithSingleOwnershipValue
115-
// (%extracted,...) = destructure %copy : $TypeWithSingleOwnershipValue
116-
SILValue convertExtractToDestructure(StructExtractInst *extract);
111+
/// If a copied-def is a struct-extract, attempt a destructure conversion
112+
/// %extract = struct_extract %... : $TypeWithSingleOwnershipValue
113+
/// %copy = copy_value %extract : $OwnershipValue
114+
/// To:
115+
/// %copy = copy_value %extract : $TypeWithSingleOwnershipValue
116+
/// (%extracted,...) = destructure %copy : $TypeWithSingleOwnershipValue
117+
///
118+
/// \p instModCallbacks If non-null, this routine uses
119+
/// InstModCallbacks::{setUseValue,RAUW}() internally to modify code. Otherwise,
120+
/// just performs standard operations.
121+
SILValue
122+
convertExtractToDestructure(StructExtractInst *extract,
123+
InstModCallbacks *instModCallbacks = nullptr);
117124

118125
/// Information about consumes on the extended-lifetime boundary. Consuming uses
119126
/// within the lifetime are not included--they will consume a copy after
@@ -333,16 +340,21 @@ class CanonicalizeOSSALifetime {
333340
/// lifetime.
334341
CanonicalOSSAConsumeInfo consumes;
335342

343+
/// The callbacks to use when deleting/rauwing instructions.
344+
InstModCallbacks instModCallbacks;
345+
336346
public:
337-
CanonicalizeOSSALifetime(bool pruneDebugMode, bool canonicalizeBorrowMode,
338-
bool poisonRefsMode,
339-
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
340-
DominanceAnalysis *dominanceAnalysis)
341-
: pruneDebugMode(pruneDebugMode),
342-
canonicalizeBorrowMode(canonicalizeBorrowMode),
343-
poisonRefsMode(poisonRefsMode),
344-
accessBlockAnalysis(accessBlockAnalysis),
345-
dominanceAnalysis(dominanceAnalysis) {}
347+
CanonicalizeOSSALifetime(
348+
bool pruneDebugMode, bool canonicalizeBorrowMode, bool poisonRefsMode,
349+
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
350+
DominanceAnalysis *dominanceAnalysis,
351+
InstModCallbacks instModCallbacks = InstModCallbacks())
352+
: pruneDebugMode(pruneDebugMode),
353+
canonicalizeBorrowMode(canonicalizeBorrowMode),
354+
poisonRefsMode(poisonRefsMode),
355+
accessBlockAnalysis(accessBlockAnalysis),
356+
dominanceAnalysis(dominanceAnalysis),
357+
instModCallbacks(instModCallbacks) {}
346358

347359
SILValue getCurrentDef() const { return currentDef; }
348360

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ class InstModCallbacks {
361361
/// A function sets the value in \p use to be \p newValue.
362362
///
363363
/// Default implementation just calls use->set(newValue).
364+
///
365+
/// NOTE: It is assumed that this operation will never invalidate instruction
366+
/// iterators.
364367
std::function<void(Operand *use, SILValue newValue)> setUseValueFunc;
365368

366369
/// A boolean that tracks if any of our callbacks were ever called.

lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,22 @@ SILValue CanonicalizeOSSALifetime::getCanonicalCopiedDef(SILValue v) {
9494
/// The lifetime extends beyond given consuming use. Copy the value.
9595
///
9696
/// This can set the operand value, but cannot invalidate the use iterator.
97-
static void copyLiveUse(Operand *use) {
97+
static void copyLiveUse(Operand *use, InstModCallbacks &instModCallbacks) {
9898
SILInstruction *user = use->getUser();
99-
SILBuilderWithScope B(user->getIterator());
99+
SILBuilderWithScope builder(user->getIterator());
100100

101101
auto loc = RegularLocation::getAutoGeneratedLocation(user->getLoc());
102-
auto *copy = B.createCopyValue(loc, use->get());
103-
use->set(copy);
102+
auto *copy = builder.createCopyValue(loc, use->get());
103+
instModCallbacks.createdNewInst(copy);
104+
instModCallbacks.setUseValue(use, copy);
104105

105106
++NumCopiesGenerated;
106107
LLVM_DEBUG(llvm::dbgs() << " Copying at last use " << *copy);
107108
}
108109

109110
// TODO: generalize this to handle multiple nondebug uses of the struct_extract.
110-
SILValue swift::convertExtractToDestructure(StructExtractInst *extract) {
111+
SILValue swift::convertExtractToDestructure(StructExtractInst *extract,
112+
InstModCallbacks *callbacks) {
111113
if (!hasOneNonDebugUse(extract))
112114
return nullptr;
113115

@@ -123,12 +125,20 @@ SILValue swift::convertExtractToDestructure(StructExtractInst *extract) {
123125
auto loc = extract->getLoc();
124126
auto *copy = builder.createCopyValue(loc, extract->getOperand());
125127
auto *destructure = builder.createDestructureStruct(loc, copy);
128+
if (callbacks) {
129+
callbacks->createdNewInst(copy);
130+
callbacks->createdNewInst(destructure);
131+
}
126132

127133
SILValue nonTrivialResult = destructure->getResult(extract->getFieldIndex());
128134
assert(!nonTrivialResult->getType().isTrivial(*destructure->getFunction())
129135
&& "field idx mismatch");
130136

131-
extractCopy->replaceAllUsesWith(nonTrivialResult);
137+
if (callbacks) {
138+
callbacks->replaceValueUsesWith(extractCopy, nonTrivialResult);
139+
} else {
140+
extractCopy->replaceAllUsesWith(nonTrivialResult);
141+
}
132142
return nonTrivialResult;
133143
}
134144

@@ -176,11 +186,13 @@ bool CanonicalizeOSSALifetime::computeBorrowLiveness() {
176186
// To use an existing outer copy, we could find its earliest consume. But the
177187
// new copy will immediately canonicalized and a canonical begin_borrow scope
178188
// have no outer uses of its first block.
179-
static CopyValueInst *createOuterCopy(BeginBorrowInst *beginBorrow) {
180-
SILBuilderWithScope B(beginBorrow);
189+
static CopyValueInst *createOuterCopy(BeginBorrowInst *beginBorrow,
190+
InstModCallbacks &instModCallbacks) {
191+
SILBuilderWithScope builder(beginBorrow);
181192

182193
auto loc = RegularLocation::getAutoGeneratedLocation(beginBorrow->getLoc());
183-
auto *copy = B.createCopyValue(loc, beginBorrow->getOperand());
194+
auto *copy = builder.createCopyValue(loc, beginBorrow->getOperand());
195+
instModCallbacks.createdNewInst(copy);
184196

185197
++NumCopiesGenerated;
186198
LLVM_DEBUG(llvm::dbgs() << " Outer copy " << *copy);
@@ -346,7 +358,7 @@ void CanonicalizeOSSALifetime::rewriteOuterBorrowUsesAndFindConsumes(
346358

347359
auto rewriteOuterUse = [&](Operand *use) {
348360
LLVM_DEBUG(llvm::dbgs() << " Use of outer copy " << *use->getUser());
349-
use->set(newIncomingValue);
361+
instModCallbacks.setUseValue(use, newIncomingValue);
350362
currentOuterUseInsts.push_back(use->getUser());
351363
outerUseInsts.insert(incomingValue->getDefiningInstruction());
352364
if (use->isLifetimeEnding()) {
@@ -444,18 +456,19 @@ void CanonicalizeOSSALifetime::rewriteOuterBorrowUsesAndFindConsumes(
444456
for (auto *boundaryEdge : boundary.boundaryEdges) {
445457
if (DeadEndBlocks::triviallyEndsInUnreachable(boundaryEdge))
446458
continue;
447-
448459
auto insertPt = boundaryEdge->begin();
449-
auto loc = insertPt->getLoc();
450-
SILBuilderWithScope(insertPt).createDestroyValue(loc, newIncomingValue);
460+
auto *dvi = SILBuilderWithScope(insertPt).createDestroyValue(
461+
insertPt->getLoc(), newIncomingValue);
462+
instModCallbacks.createdNewInst(dvi);
451463
}
452464

453465
for (SILInstruction *lastUser : boundary.lastUsers) {
454466
if (unclaimedConsumingUsers.erase(lastUser))
455467
continue;
456468

457469
SILBuilderWithScope::insertAfter(lastUser, [&](SILBuilder &b) {
458-
b.createDestroyValue(lastUser->getLoc(), newIncomingValue);
470+
auto *dvi = b.createDestroyValue(lastUser->getLoc(), newIncomingValue);
471+
instModCallbacks.createdNewInst(dvi);
459472
});
460473
}
461474
// Add copies for consuming users of newIncomingValue.
@@ -466,7 +479,7 @@ void CanonicalizeOSSALifetime::rewriteOuterBorrowUsesAndFindConsumes(
466479
// unclaimedConsumingUsers set after skipping the first copy.
467480
auto iterAndInserted = unclaimedConsumingUsers.insert(use->getUser());
468481
if (!iterAndInserted.second) {
469-
copyLiveUse(use);
482+
copyLiveUse(use, instModCallbacks);
470483
}
471484
}
472485
}
@@ -496,7 +509,8 @@ bool CanonicalizeOSSALifetime::consolidateBorrowScope() {
496509
if (outerUseInsts.empty()) {
497510
return true;
498511
}
499-
this->outerCopy = createOuterCopy(cast<BeginBorrowInst>(currentDef));
512+
this->outerCopy =
513+
createOuterCopy(cast<BeginBorrowInst>(currentDef), instModCallbacks);
500514

501515
defUseWorklist.clear();
502516
rewriteOuterBorrowUsesAndFindConsumes(currentDef, outerUseInsts);
@@ -761,6 +775,7 @@ void CanonicalizeOSSALifetime::insertDestroyOnCFGEdge(
761775
SILBuilderWithScope builder(pos);
762776
auto loc = RegularLocation::getAutoGeneratedLocation(pos->getLoc());
763777
auto *di = builder.createDestroyValue(loc, currentDef, needsPoison);
778+
instModCallbacks.createdNewInst(di);
764779

765780
consumes.recordFinalConsume(di);
766781

@@ -772,9 +787,10 @@ void CanonicalizeOSSALifetime::insertDestroyOnCFGEdge(
772787
///
773788
/// Create a final destroy, immediately after `pos`.
774789
static void insertDestroyAtInst(SILBasicBlock::iterator pos,
775-
DestroyValueInst *existingDestroy,
776-
SILValue def, bool needsPoison,
777-
CanonicalOSSAConsumeInfo &consumes) {
790+
DestroyValueInst *existingDestroy, SILValue def,
791+
bool needsPoison,
792+
CanonicalOSSAConsumeInfo &consumes,
793+
InstModCallbacks &callbacks) {
778794
if (existingDestroy) {
779795
for (; pos != existingDestroy->getIterator(); ++pos) {
780796
if (auto *debugVal = dyn_cast<DebugValueInst>(&*pos)) {
@@ -791,6 +807,7 @@ static void insertDestroyAtInst(SILBasicBlock::iterator pos,
791807
SILBuilderWithScope builder(pos);
792808
auto loc = RegularLocation::getAutoGeneratedLocation((*pos).getLoc());
793809
auto *di = builder.createDestroyValue(loc, def, needsPoison);
810+
callbacks.createdNewInst(di);
794811
consumes.recordFinalConsume(di);
795812

796813
++NumDestroysGenerated;
@@ -837,7 +854,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
837854
needsPoison = existingDestroyNeedsPoison;
838855
}
839856
insertDestroyAtInst(std::next(instIter), existingDestroy, currentDef,
840-
needsPoison, consumes);
857+
needsPoison, consumes, instModCallbacks);
841858
setChanged();
842859
}
843860
return;
@@ -889,7 +906,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
889906
needsPoison = existingDestroyNeedsPoison;
890907
}
891908
insertDestroyAtInst(instIter, existingDestroy, currentDef, needsPoison,
892-
consumes);
909+
consumes, instModCallbacks);
893910
setChanged();
894911
return;
895912
}
@@ -901,7 +918,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
901918
needsPoison = existingDestroyNeedsPoison;
902919
}
903920
insertDestroyAtInst(std::next(instIter), existingDestroy, currentDef,
904-
needsPoison, consumes);
921+
needsPoison, consumes, instModCallbacks);
905922
setChanged();
906923
return;
907924
}
@@ -1033,7 +1050,7 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
10331050
continue;
10341051
}
10351052
if (!visitUse(use)) {
1036-
copyLiveUse(use);
1053+
copyLiveUse(use, instModCallbacks);
10371054
setChanged();
10381055
}
10391056
}
@@ -1047,16 +1064,16 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
10471064
if (!reusedCopyOp && srcCopy->getParent() == use->getParentBlock()) {
10481065
reusedCopyOp = use;
10491066
} else {
1050-
copyLiveUse(use);
1067+
copyLiveUse(use, instModCallbacks);
10511068
setChanged();
10521069
}
10531070
}
10541071
}
10551072
if (!(reusedCopyOp && srcCopy->hasOneUse())) {
10561073
setChanged();
1057-
srcCopy->replaceAllUsesWith(srcCopy->getOperand());
1074+
instModCallbacks.replaceValueUsesWith(srcCopy, srcCopy->getOperand());
10581075
if (reusedCopyOp) {
1059-
reusedCopyOp->set(srcCopy);
1076+
instModCallbacks.setUseValue(reusedCopyOp, srcCopy);
10601077
} else {
10611078
if (instsToDelete.insert(srcCopy)) {
10621079
LLVM_DEBUG(llvm::dbgs() << " Removing " << *srcCopy);
@@ -1081,7 +1098,7 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
10811098
// Remove any dead, non-recovered debug_values.
10821099
for (auto *dvi : consumes.getDebugInstsAfterConsume()) {
10831100
LLVM_DEBUG(llvm::dbgs() << " Removing debug_value: " << *dvi);
1084-
dvi->eraseFromParent();
1101+
instModCallbacks.deleteInst(dvi);
10851102
}
10861103

10871104
// Remove the leftover copy_value and destroy_value instructions.
@@ -1100,7 +1117,7 @@ void CanonicalizeOSSALifetime::injectPoison() {
11001117
builder.getInsertionPoint()->getLoc());
11011118
auto *di = builder.createDestroyValue(loc, currentDef,
11021119
/*needsPoison*/ true);
1103-
1120+
instModCallbacks.createdNewInst(di);
11041121
++NumDestroysGenerated;
11051122
LLVM_DEBUG(llvm::dbgs() << " Destroy at last use " << *di);
11061123
};

0 commit comments

Comments
 (0)