Skip to content

Commit 340feb4

Browse files
committed
CanonicalOSSALifetime: rewrite consolidateBorrowScope
It now handles looking through forwarding consumes such as destructures. Expected to be NFC since borrow consolidation is still disabled by default. TODO: Write unit tests for various forwarding consumes in addition to destructure.
1 parent 3c045a8 commit 340feb4

File tree

6 files changed

+562
-175
lines changed

6 files changed

+562
-175
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,50 @@ class CanonicalOSSAConsumeInfo {
213213
SWIFT_ASSERT_ONLY_DECL(void dump() const LLVM_ATTRIBUTE_USED);
214214
};
215215

216+
// Worklist of pointer-like things that have an invalid default value. Avoid
217+
// revisiting nodes--suitable for DAGs, but pops finished nodes without
218+
// preserving them in the vector.
219+
//
220+
// The primary API has two methods: intialize() and pop(). Others are provided
221+
// for flexibility.
222+
//
223+
// TODO: make this a better utility.
224+
template <typename T, unsigned SmallSize> struct PtrWorklist {
225+
SmallPtrSet<T, SmallSize> ptrVisited;
226+
SmallVector<T, SmallSize> ptrVector;
227+
228+
PtrWorklist() = default;
229+
230+
PtrWorklist(const PtrWorklist &) = delete;
231+
232+
void initialize(T t) {
233+
clear();
234+
insert(t);
235+
}
236+
237+
template <typename R> void initializeRange(R &&range) {
238+
clear();
239+
ptrVisited.insert(range.begin(), range.end());
240+
ptrVector.append(range.begin(), range.end());
241+
}
242+
243+
T pop() { return empty() ? T() : ptrVector.pop_back_val(); }
244+
245+
bool empty() const { return ptrVector.empty(); }
246+
247+
unsigned size() const { return ptrVector.size(); }
248+
249+
void clear() {
250+
ptrVector.clear();
251+
ptrVisited.clear();
252+
}
253+
254+
void insert(T t) {
255+
if (ptrVisited.insert(t).second)
256+
ptrVector.push_back(t);
257+
}
258+
};
259+
216260
/// Canonicalize OSSA lifetimes.
217261
///
218262
/// Allows the allocation of analysis state to be reused across calls to
@@ -227,6 +271,10 @@ class CanonicalizeOSSALifetime {
227271
/// liveness may be pruned during canonicalization.
228272
bool pruneDebugMode;
229273

274+
/// If true, borrows scopes will be canonicalized allowing copies of
275+
/// guaranteed values to be eliminated.
276+
bool canonicalizeBorrowMode;
277+
230278
/// If true, then new destroy_value instructions will be poison.
231279
bool poisonRefsMode;
232280

@@ -237,8 +285,6 @@ class CanonicalizeOSSALifetime {
237285

238286
DominanceAnalysis *dominanceAnalysis;
239287

240-
DeadEndBlocks *deBlocks;
241-
242288
/// Current copied def for which this state describes the liveness.
243289
SILValue currentDef;
244290

@@ -262,11 +308,11 @@ class CanonicalizeOSSALifetime {
262308
/// outisde the pruned liveness at the time it is discovered.
263309
llvm::SmallPtrSet<DebugValueInst *, 8> debugValues;
264310

265-
/// Reuse a general worklist for def-use traversal.
266-
SmallSetVector<SILValue, 8> defUseWorklist;
311+
/// Reuse a general visited set for def-use traversal.
312+
PtrWorklist<SILValue, 8> defUseWorklist;
267313

268314
/// Reuse a general worklist for CFG traversal.
269-
SmallSetVector<SILBasicBlock *, 8> blockWorklist;
315+
PtrWorklist<SILBasicBlock *, 8> blockWorklist;
270316

271317
/// Pruned liveness for the extended live range including copies. For this
272318
/// purpose, only consuming instructions are considered "lifetime
@@ -288,13 +334,15 @@ class CanonicalizeOSSALifetime {
288334
CanonicalOSSAConsumeInfo consumes;
289335

290336
public:
291-
CanonicalizeOSSALifetime(bool pruneDebugMode, bool poisonRefsMode,
337+
CanonicalizeOSSALifetime(bool pruneDebugMode, bool canonicalizeBorrowMode,
338+
bool poisonRefsMode,
292339
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
293-
DominanceAnalysis *dominanceAnalysis,
294-
DeadEndBlocks *deBlocks)
295-
: pruneDebugMode(pruneDebugMode), poisonRefsMode(poisonRefsMode),
296-
accessBlockAnalysis(accessBlockAnalysis),
297-
dominanceAnalysis(dominanceAnalysis), deBlocks(deBlocks) {}
340+
DominanceAnalysis *dominanceAnalysis)
341+
: pruneDebugMode(pruneDebugMode),
342+
canonicalizeBorrowMode(canonicalizeBorrowMode),
343+
poisonRefsMode(poisonRefsMode),
344+
accessBlockAnalysis(accessBlockAnalysis),
345+
dominanceAnalysis(dominanceAnalysis) {}
298346

299347
SILValue getCurrentDef() const { return currentDef; }
300348

@@ -351,6 +399,15 @@ class CanonicalizeOSSALifetime {
351399

352400
bool consolidateBorrowScope();
353401

402+
bool findBorrowScopeUses(llvm::SmallPtrSetImpl<SILInstruction *> &useInsts);
403+
404+
void filterOuterBorrowUseInsts(
405+
llvm::SmallPtrSetImpl<SILInstruction *> &outerUseInsts);
406+
407+
void rewriteOuterBorrowUsesAndFindConsumes(
408+
SILValue incomingValue,
409+
llvm::SmallPtrSetImpl<SILInstruction *> &outerUseInsts);
410+
354411
bool computeCanonicalLiveness();
355412

356413
bool endsAccessOverlappingPrunedBoundary(SILInstruction *inst);

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define DEBUG_TYPE "copy-propagation"
2727

2828
#include "swift/SIL/BasicBlockUtils.h"
29+
#include "swift/SIL/SILUndef.h"
2930
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
3031
#include "swift/SILOptimizer/PassManager/Passes.h"
3132
#include "swift/SILOptimizer/PassManager/Transforms.h"
@@ -34,6 +35,12 @@
3435

3536
using namespace swift;
3637

38+
// This only applies to -O copy-propagation.
39+
llvm::cl::opt<bool>
40+
EnableRewriteBorrows("canonical-ossa-rewrite-borrows",
41+
llvm::cl::init(false),
42+
llvm::cl::desc("Enable rewriting borrow scopes"));
43+
3744
//===----------------------------------------------------------------------===//
3845
// CopyPropagation: Top-Level Function Transform.
3946
//===----------------------------------------------------------------------===//
@@ -44,13 +51,17 @@ class CopyPropagation : public SILFunctionTransform {
4451
bool pruneDebug;
4552
/// True of all values should be canonicalized.
4653
bool canonicalizeAll;
54+
/// If true, then borrow scopes will be canonicalized, allowing copies of
55+
/// guaranteed values to be optimized. Does *not* shrink the borrow scope.
56+
bool canonicalizeBorrows;
4757
/// If true, then new destroy_value instructions will be poison.
4858
bool poisonRefs;
4959

5060
public:
51-
CopyPropagation(bool pruneDebug, bool canonicalizeAll, bool poisonRefs)
52-
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
53-
poisonRefs(poisonRefs) {}
61+
CopyPropagation(bool pruneDebug, bool canonicalizeAll,
62+
bool canonicalizeBorrows, bool poisonRefs)
63+
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll),
64+
canonicalizeBorrows(canonicalizeBorrows), poisonRefs(poisonRefs) {}
5465

5566
/// The entry point to this function transformation.
5667
void run() override;
@@ -71,12 +82,18 @@ static bool isCopyDead(CopyValueInst *copy, bool pruneDebug) {
7182
return true;
7283
}
7384

85+
static bool isBorrowDead(BeginBorrowInst *borrow) {
86+
return llvm::all_of(borrow->getUses(), [](Operand *use) {
87+
SILInstruction *user = use->getUser();
88+
return isa<EndBorrowInst>(user) || user->isDebugInstruction();
89+
});
90+
}
91+
7492
/// Top-level pass driver.
7593
void CopyPropagation::run() {
7694
auto *f = getFunction();
7795
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
7896
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
79-
auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
8097

8198
// Debug label for unit testing.
8299
LLVM_DEBUG(llvm::dbgs() << "*** CopyPropagation: " << f->getName() << "\n");
@@ -115,6 +132,12 @@ void CopyPropagation::run() {
115132
|| SILValue(extract).getOwnershipKind() != OwnershipKind::Guaranteed)
116133
continue;
117134

135+
// Bail-out if we won't rewrite borrows because that currently regresses
136+
// Breadcrumbs.MutatedUTF16ToIdx.Mixed/Breadcrumbs.MutatedIdxToUTF16.Mixed.
137+
// Also, mandatory copyprop does not need to rewrite destructures.
138+
if (!canonicalizeBorrows)
139+
continue;
140+
118141
if (SILValue destructuredResult = convertExtractToDestructure(extract)) {
119142
// Remove to-be-deleted instructions from copiedDeds. The extract cannot
120143
// be in the copiedDefs set since getCanonicalCopiedDef does not allow a
@@ -138,14 +161,13 @@ void CopyPropagation::run() {
138161
}
139162
}
140163
// Perform copy propgation for each copied value.
141-
CanonicalizeOSSALifetime canonicalizer(pruneDebug, poisonRefs,
142-
accessBlockAnalysis,
143-
dominanceAnalysis,
144-
deBlocksAnalysis->get(f));
164+
CanonicalizeOSSALifetime canonicalizer(pruneDebug, canonicalizeBorrows,
165+
poisonRefs, accessBlockAnalysis,
166+
dominanceAnalysis);
145167
// Cleanup dead copies. If getCanonicalCopiedDef returns a copy (because the
146-
// copy's source operand is unrecgonized), then thecan copy is itself treated
168+
// copy's source operand is unrecgonized), then the copy is itself treated
147169
// like a def and may be dead after canonicalization.
148-
llvm::SmallVector<CopyValueInst *, 4> deadCopies;
170+
llvm::SmallVector<SILInstruction *, 4> deadCopies;
149171
for (auto &def : copiedDefs) {
150172
// Canonicalized this def.
151173
canonicalizer.canonicalizeValueLifetime(def);
@@ -155,6 +177,14 @@ void CopyPropagation::run() {
155177
deadCopies.push_back(copy);
156178
}
157179
}
180+
// Dead borrow scopes must be removed as uses before canonicalizing the
181+
// outer copy.
182+
if (auto *borrow = dyn_cast<BeginBorrowInst>(def)) {
183+
if (isBorrowDead(borrow)) {
184+
borrow->setOperand(SILUndef::get(borrow->getType(), *f));
185+
deadCopies.push_back(borrow);
186+
}
187+
}
158188
// Canonicalize any new outer copy.
159189
if (SILValue outerCopy = canonicalizer.createdOuterCopy()) {
160190
SILValue outerDef = canonicalizer.getCanonicalCopiedDef(outerCopy);
@@ -165,26 +195,29 @@ void CopyPropagation::run() {
165195
}
166196
if (canonicalizer.hasChanged() || !deadCopies.empty()) {
167197
InstructionDeleter deleter;
168-
for (auto *copy : deadCopies) {
169-
deleter.recursivelyDeleteUsersIfDead(copy);
198+
for (auto *copyOrBorrow : deadCopies) {
199+
deleter.recursivelyDeleteUsersIfDead(copyOrBorrow);
170200
}
171201
// Preserves NonLocalAccessBlockAnalysis.
172202
accessBlockAnalysis->lockInvalidation();
173203
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
174204
accessBlockAnalysis->unlockInvalidation();
175205
if (f->getModule().getOptions().VerifySILOwnership) {
206+
auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
176207
f->verifyOwnership(deBlocksAnalysis->get(f));
177208
}
178209
}
179210
}
180211

181212
SILTransform *swift::createMandatoryCopyPropagation() {
182213
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true,
214+
/*canonicalizeBorrows*/ false,
183215
/*poisonRefs*/ true);
184216
}
185217

186218
SILTransform *swift::createCopyPropagation() {
187219
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ false,
220+
/*canonicalizeBorrows*/ EnableRewriteBorrows,
188221
/*poisonRefs*/ false);
189222
}
190223

0 commit comments

Comments
 (0)