Skip to content

Commit 180095a

Browse files
committed
[ShrinkBorrowScope] Adopt iterative dataflow.
Adopt IterativeBackwardReachability. Enables hoisting end_borrow instructions over loops. That in turn enables hoisting destroy_values over those same loops. rdar://93186505
1 parent bdc1f40 commit 180095a

File tree

2 files changed

+73
-85
lines changed

2 files changed

+73
-85
lines changed

lib/SILOptimizer/Utils/ShrinkBorrowScope.cpp

Lines changed: 70 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct Context final {
4949
/// introducer->getOperand()
5050
SILValue const borrowee;
5151

52+
SILBasicBlock *defBlock;
53+
5254
SILFunction &function;
5355

5456
/// The copy_value instructions that the utility creates or changes.
@@ -62,7 +64,8 @@ struct Context final {
6264
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts,
6365
InstructionDeleter &deleter)
6466
: introducer(introducer), borrowedValue(BorrowedValue(&introducer)),
65-
borrowee(introducer.getOperand()), function(*introducer.getFunction()),
67+
borrowee(introducer.getOperand()), defBlock(introducer.getParent()),
68+
function(*introducer.getFunction()),
6669
modifiedCopyValueInsts(modifiedCopyValueInsts), deleter(deleter) {}
6770
Context(Context const &) = delete;
6871
Context &operator=(Context const &) = delete;
@@ -75,7 +78,7 @@ struct Usage final {
7578
SmallPtrSet<SILInstruction *, 16> users;
7679
// The instructions from which the shrinking starts, the scope ending
7780
// instructions.
78-
llvm::SmallSetVector<SILInstruction *, 4> ends;
81+
llvm::SmallVector<SILInstruction *, 4> ends;
7982

8083
Usage(){};
8184
Usage(Usage const &) = delete;
@@ -95,7 +98,7 @@ bool findUsage(Context const &context, Usage &usage) {
9598
// If a scope ending instruction is not an end_borrow, bail out.
9699
if (!isa<EndBorrowInst>(instruction))
97100
return false;
98-
usage.ends.insert(instruction);
101+
usage.ends.push_back(instruction);
99102
}
100103

101104
SmallVector<Operand *, 16> uses;
@@ -112,81 +115,82 @@ bool findUsage(Context const &context, Usage &usage) {
112115

113116
/// How end_borrow hoisting is obstructed.
114117
struct DeinitBarriers final {
115-
/// Blocks up to "before the beginning" of which hoisting was able to proceed.
116-
BasicBlockSetVector hoistingReachesBeginBlocks;
117-
118-
/// Blocks to "after the end" of which hoisting was able to proceed.
119-
BasicBlockSet hoistingReachesEndBlocks;
120-
121118
/// Copies to be rewritten as copies of %borrowee.
122119
SmallVector<CopyValueInst *, 4> copies;
123120

124121
/// Instructions above which end_borrows cannot be hoisted.
125-
SmallVector<SILInstruction *, 4> barriers;
122+
SmallVector<SILInstruction *, 4> instructions;
126123

127124
/// Blocks one of whose phis is a barrier and consequently out of which
128125
/// end_borrows cannot be hoisted.
129-
SmallVector<SILBasicBlock *, 4> phiBarriers;
126+
SmallVector<SILBasicBlock *, 4> phis;
127+
128+
/// Blocks whose single predecessors has another successor to the top of which
129+
/// end_borrows cannot be hoisted.
130+
SmallVector<SILBasicBlock *, 4> blocks;
130131

131-
DeinitBarriers(Context &context)
132-
: hoistingReachesBeginBlocks(&context.function),
133-
hoistingReachesEndBlocks(&context.function) {}
132+
DeinitBarriers(Context &context) {}
134133
DeinitBarriers(DeinitBarriers const &) = delete;
135134
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
136135
};
137136

138137
/// Works backwards from the current location of end_borrows to the earliest
139138
/// place they can be hoisted to.
140139
///
141-
/// Implements BackwardReachability::BlockReachability.
140+
/// Implements IterativeBackwardReachability::Effects.
141+
/// Implements IterativeBackwardReachability::findBarrier::Visitor.
142142
class Dataflow final {
143+
public:
144+
using Reachability = IterativeBackwardReachability<Dataflow>;
145+
using Effect = Reachability::Effect;
146+
147+
private:
143148
Context const &context;
144149
Usage const &uses;
145-
DeinitBarriers &result;
150+
DeinitBarriers &barriers;
151+
Reachability::Result result;
152+
Reachability reachability;
153+
SmallPtrSet<BeginAccessInst *, 8> barrierAccessScopes;
154+
bool recordCopies = false;
146155

147156
enum class Classification { Barrier, Copy, Other };
148157

149-
BackwardReachability<Dataflow> reachability;
150-
151158
public:
152-
Dataflow(Context const &context, Usage const &uses, DeinitBarriers &result)
153-
: context(context), uses(uses), result(result),
154-
reachability(&context.function, *this) {
155-
// Seed reachability with the scope ending uses from which the backwards
156-
// data flow will begin.
157-
for (auto *end : uses.ends) {
158-
reachability.initLastUse(end);
159-
}
160-
}
159+
Dataflow(Context const &context, Usage const &uses, DeinitBarriers &barriers)
160+
: context(context), uses(uses), barriers(barriers),
161+
result(&context.function),
162+
reachability(&context.function, context.defBlock, *this, result) {}
161163
Dataflow(Dataflow const &) = delete;
162164
Dataflow &operator=(Dataflow const &) = delete;
163165

164-
void run() { reachability.solveBackward(); }
166+
void run();
165167

166168
private:
167-
friend class BackwardReachability<Dataflow>;
169+
friend Reachability;
168170

169-
bool hasReachableBegin(SILBasicBlock *block) {
170-
return result.hoistingReachesBeginBlocks.contains(block);
171-
}
171+
Classification classifyInstruction(SILInstruction *);
172172

173-
void markReachableBegin(SILBasicBlock *block) {
174-
result.hoistingReachesBeginBlocks.insert(block);
175-
}
173+
bool classificationIsBarrier(Classification);
176174

177-
void markReachableEnd(SILBasicBlock *block) {
178-
result.hoistingReachesEndBlocks.insert(block);
179-
}
175+
/// Implements IterativeBackwardReachability::Effects.
180176

181-
Classification classifyInstruction(SILInstruction *);
177+
ArrayRef<SILInstruction *> gens() { return uses.ends; }
182178

183-
bool classificationIsBarrier(Classification);
179+
Effect effectForInstruction(SILInstruction *);
184180

185-
void visitedInstruction(SILInstruction *, Classification);
181+
Effect effectForPhi(SILBasicBlock *);
186182

187-
bool checkReachableBarrier(SILInstruction *);
183+
/// IterativeBackwardReachability::findBarrier::Visitor.
188184

189-
bool checkReachablePhiBarrier(SILBasicBlock *);
185+
void visitBarrierInstruction(SILInstruction *instruction) {
186+
barriers.instructions.push_back(instruction);
187+
}
188+
189+
void visitBarrierPhi(SILBasicBlock *block) { barriers.phis.push_back(block); }
190+
191+
void visitBarrierBlock(SILBasicBlock *block) {
192+
barriers.blocks.push_back(block);
193+
}
190194
};
191195

192196
/// Whether the specified value is %lifetime or its iterated copy_value.
@@ -237,29 +241,17 @@ bool Dataflow::classificationIsBarrier(Classification classification) {
237241
llvm_unreachable("exhaustive switch not exhaustive?!");
238242
}
239243

240-
void Dataflow::visitedInstruction(SILInstruction *instruction,
241-
Classification classification) {
242-
assert(classifyInstruction(instruction) == classification);
243-
switch (classification) {
244-
case Classification::Barrier:
245-
result.barriers.push_back(instruction);
246-
return;
247-
case Classification::Copy:
248-
result.copies.push_back(cast<CopyValueInst>(instruction));
249-
return;
250-
case Classification::Other:
251-
return;
252-
}
253-
llvm_unreachable("exhaustive switch not exhaustive?!");
254-
}
255-
256-
bool Dataflow::checkReachableBarrier(SILInstruction *instruction) {
244+
Dataflow::Effect Dataflow::effectForInstruction(SILInstruction *instruction) {
245+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
246+
return Effect::Gen();
257247
auto classification = classifyInstruction(instruction);
258-
visitedInstruction(instruction, classification);
259-
return classificationIsBarrier(classification);
248+
if (recordCopies && classification == Classification::Copy)
249+
barriers.copies.push_back(cast<CopyValueInst>(instruction));
250+
return classificationIsBarrier(classification) ? Effect::Kill()
251+
: Effect::NoEffect();
260252
}
261253

262-
bool Dataflow::checkReachablePhiBarrier(SILBasicBlock *block) {
254+
Dataflow::Effect Dataflow::effectForPhi(SILBasicBlock *block) {
263255
assert(llvm::all_of(block->getArguments(),
264256
[&](auto argument) { return PhiValue(argument); }));
265257

@@ -268,10 +260,14 @@ bool Dataflow::checkReachablePhiBarrier(SILBasicBlock *block) {
268260
return classificationIsBarrier(
269261
classifyInstruction(predecessor->getTerminator()));
270262
});
271-
if (isBarrier) {
272-
result.phiBarriers.push_back(block);
273-
}
274-
return isBarrier;
263+
return isBarrier ? Effect::Kill() : Effect::NoEffect();
264+
}
265+
266+
void Dataflow::run() {
267+
reachability.initialize();
268+
reachability.solve();
269+
recordCopies = true;
270+
reachability.findBarriers(*this);
275271
}
276272

277273
/// Hoist the scope ends of %lifetime, rewriting copies and borrows along the
@@ -311,7 +307,7 @@ bool Rewriter::run() {
311307
// A block is a phi barrier iff any of its predecessors' terminators get
312308
// classified as barriers. That happens when a copy of %lifetime is passed
313309
// to a phi.
314-
for (auto *block : barriers.phiBarriers) {
310+
for (auto *block : barriers.phis) {
315311
madeChange |= createEndBorrow(&block->front());
316312
}
317313

@@ -324,15 +320,11 @@ bool Rewriter::run() {
324320
// of a block P's successors B had reachable beginnings. If any of them
325321
// didn't, then BackwardReachability::meetOverSuccessors would never have
326322
// returned true for P, so none of its instructions would ever have been
327-
// classified (except for via checkReachablePhiBarrier, which doesn't record
328-
// terminator barriers).
329-
for (auto instruction : barriers.barriers) {
323+
// classified (except for via effectForPhi, which doesn't record terminator
324+
// barriers).
325+
for (auto instruction : barriers.instructions) {
330326
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
331327
auto successors = terminator->getParentBlock()->getSuccessorBlocks();
332-
// In order for the instruction to have been classified as a barrier,
333-
// reachability would have had to reach the block containing it.
334-
assert(barriers.hoistingReachesEndBlocks.contains(
335-
terminator->getParentBlock()));
336328
for (auto *successor : successors) {
337329
madeChange |= createEndBorrow(&successor->front());
338330
}
@@ -356,12 +348,8 @@ bool Rewriter::run() {
356348
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
357349
//
358350
// control-flow-boundary(B) := beginning-reachable(B) && !end-reachable(P)
359-
for (auto *block : barriers.hoistingReachesBeginBlocks) {
360-
if (auto *predecessor = block->getSinglePredecessorBlock()) {
361-
if (!barriers.hoistingReachesEndBlocks.contains(predecessor)) {
362-
madeChange |= createEndBorrow(&block->front());
363-
}
364-
}
351+
for (auto *block : barriers.blocks) {
352+
madeChange |= createEndBorrow(&block->front());
365353
}
366354

367355
if (madeChange) {
@@ -379,7 +367,7 @@ bool Rewriter::run() {
379367

380368
bool Rewriter::createEndBorrow(SILInstruction *insertionPoint) {
381369
if (auto *ebi = dyn_cast<EndBorrowInst>(insertionPoint)) {
382-
if (uses.ends.contains(insertionPoint)) {
370+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
383371
reusedEndBorrowInsts.insert(insertionPoint);
384372
return false;
385373
}

test/SILOptimizer/shrink_borrow_scope.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,13 +457,14 @@ exit:
457457
// loop tests {{
458458
// =============================================================================
459459

460-
// Don't hoist over loop without uses.
461-
// TODO: Eventually, we should hoist over such loops.
460+
// Hoist over loop without uses.
461+
//
462462
// CHECK-LABEL: sil [ossa] @hoist_over_loop_1 : {{.*}} {
463463
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C):
464464
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [[INSTANCE]]
465465
// CHECK: [[CALLEE_GUARANTEED:%[^,]+]] = function_ref @callee_guaranteed
466466
// CHECK: {{%[^,]+}} = apply [[CALLEE_GUARANTEED]]([[LIFETIME]])
467+
// CHECK: end_borrow [[LIFETIME]]
467468
// CHECK: br [[LOOP_HEADER:bb[0-9]+]]
468469
// CHECK: [[LOOP_HEADER]]:
469470
// CHECK: br [[LOOP_BODY:bb[0-9]+]]
@@ -474,7 +475,6 @@ exit:
474475
// CHECK: [[LOOP_BACKEDGE]]:
475476
// CHECK: br [[LOOP_HEADER]]
476477
// CHECK: [[EXIT]]:
477-
// CHECK: end_borrow [[LIFETIME]]
478478
// CHECK: return [[INSTANCE]]
479479
// CHECK-LABEL: } // end sil function 'hoist_over_loop_1'
480480
sil [ossa] @hoist_over_loop_1 : $@convention(thin) (@owned C) -> @owned C {

0 commit comments

Comments
 (0)