Skip to content

Commit 9f2b4a5

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 ca70a23 commit 9f2b4a5

File tree

2 files changed

+63
-85
lines changed

2 files changed

+63
-85
lines changed

lib/SILOptimizer/Utils/ShrinkBorrowScope.cpp

Lines changed: 60 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,67 @@ 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+
SmallSetVector<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+
SmallSetVector<SILBasicBlock *, 4> phis;
127+
128+
/// Blocks whose single predecessors has another successor to the top of which
129+
/// end_borrows cannot be hoisted.
130+
SmallSetVector<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.
142141
class DataFlow final {
142+
public:
143+
using Reachability = IterativeBackwardReachability<DataFlow>;
144+
using Effect = Reachability::Effect;
145+
146+
private:
143147
Context const &context;
144148
Usage const &uses;
145-
DeinitBarriers &result;
149+
DeinitBarriers &barriers;
150+
Reachability::Result result;
151+
Reachability reachability;
152+
SmallPtrSet<BeginAccessInst *, 8> barrierAccessScopes;
153+
bool recordCopies = false;
146154

147155
enum class Classification { Barrier, Copy, Other };
148156

149-
BackwardReachability<DataFlow> reachability;
150-
151157
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-
}
158+
DataFlow(Context const &context, Usage const &uses, DeinitBarriers &barriers)
159+
: context(context), uses(uses), barriers(barriers),
160+
result(&context.function),
161+
reachability(&context.function, context.defBlock, *this, result) {}
161162
DataFlow(DataFlow const &) = delete;
162163
DataFlow &operator=(DataFlow const &) = delete;
163164

164-
void run() { reachability.solveBackward(); }
165+
void run();
165166

166167
private:
167-
friend class BackwardReachability<DataFlow>;
168-
169-
bool hasReachableBegin(SILBasicBlock *block) {
170-
return result.hoistingReachesBeginBlocks.contains(block);
171-
}
172-
173-
void markReachableBegin(SILBasicBlock *block) {
174-
result.hoistingReachesBeginBlocks.insert(block);
175-
}
176-
177-
void markReachableEnd(SILBasicBlock *block) {
178-
result.hoistingReachesEndBlocks.insert(block);
179-
}
168+
friend class IterativeBackwardReachability<DataFlow>;
180169

181170
Classification classifyInstruction(SILInstruction *);
182171

183172
bool classificationIsBarrier(Classification);
184173

185-
void visitedInstruction(SILInstruction *, Classification);
174+
ArrayRef<SILInstruction *> gens();
186175

187-
bool checkReachableBarrier(SILInstruction *);
176+
Optional<Effect> effectForInstruction(SILInstruction *);
188177

189-
bool checkReachablePhiBarrier(SILBasicBlock *);
178+
Optional<Effect> effectForPhi(SILBasicBlock *);
190179
};
191180

192181
/// Whether the specified value is %lifetime or its iterated copy_value.
@@ -207,6 +196,14 @@ bool isSimpleExtendedIntroducerDef(Context const &context, SILValue value) {
207196
}
208197
}
209198

199+
void DataFlow::run() {
200+
reachability.initialize();
201+
reachability.solve();
202+
recordCopies = true;
203+
reachability.findBarriers(barriers.instructions, barriers.phis,
204+
barriers.blocks);
205+
}
206+
210207
DataFlow::Classification
211208
DataFlow::classifyInstruction(SILInstruction *instruction) {
212209
if (instruction == &context.introducer) {
@@ -237,29 +234,21 @@ bool DataFlow::classificationIsBarrier(Classification classification) {
237234
llvm_unreachable("exhaustive switch not exhaustive?!");
238235
}
239236

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-
}
237+
ArrayRef<SILInstruction *> DataFlow::gens() { return uses.ends; }
255238

256-
bool DataFlow::checkReachableBarrier(SILInstruction *instruction) {
239+
Optional<DataFlow::Effect>
240+
DataFlow::effectForInstruction(SILInstruction *instruction) {
241+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
242+
return {Effect::Gen};
257243
auto classification = classifyInstruction(instruction);
258-
visitedInstruction(instruction, classification);
259-
return classificationIsBarrier(classification);
244+
if (recordCopies && classification == Classification::Copy)
245+
barriers.copies.push_back(cast<CopyValueInst>(instruction));
246+
return classificationIsBarrier(classification)
247+
? Optional<Effect>{Effect::Kill}
248+
: llvm::None;
260249
}
261250

262-
bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
251+
Optional<DataFlow::Effect> DataFlow::effectForPhi(SILBasicBlock *block) {
263252
assert(llvm::all_of(block->getArguments(),
264253
[&](auto argument) { return PhiValue(argument); }));
265254

@@ -268,10 +257,7 @@ bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
268257
return classificationIsBarrier(
269258
classifyInstruction(predecessor->getTerminator()));
270259
});
271-
if (isBarrier) {
272-
result.phiBarriers.push_back(block);
273-
}
274-
return isBarrier;
260+
return isBarrier ? Optional<Effect>{Effect::Kill} : llvm::None;
275261
}
276262

277263
/// Hoist the scope ends of %lifetime, rewriting copies and borrows along the
@@ -311,7 +297,7 @@ bool Rewriter::run() {
311297
// A block is a phi barrier iff any of its predecessors' terminators get
312298
// classified as barriers. That happens when a copy of %lifetime is passed
313299
// to a phi.
314-
for (auto *block : barriers.phiBarriers) {
300+
for (auto *block : barriers.phis) {
315301
madeChange |= createEndBorrow(&block->front());
316302
}
317303

@@ -324,15 +310,11 @@ bool Rewriter::run() {
324310
// of a block P's successors B had reachable beginnings. If any of them
325311
// didn't, then BackwardReachability::meetOverSuccessors would never have
326312
// 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) {
313+
// classified (except for via effectForPhi, which doesn't record terminator
314+
// barriers).
315+
for (auto instruction : barriers.instructions) {
330316
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
331317
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()));
336318
for (auto *successor : successors) {
337319
madeChange |= createEndBorrow(&successor->front());
338320
}
@@ -356,12 +338,8 @@ bool Rewriter::run() {
356338
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
357339
//
358340
// 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-
}
341+
for (auto *block : barriers.blocks) {
342+
madeChange |= createEndBorrow(&block->front());
365343
}
366344

367345
if (madeChange) {
@@ -379,7 +357,7 @@ bool Rewriter::run() {
379357

380358
bool Rewriter::createEndBorrow(SILInstruction *insertionPoint) {
381359
if (auto *ebi = dyn_cast<EndBorrowInst>(insertionPoint)) {
382-
if (uses.ends.contains(insertionPoint)) {
360+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
383361
reusedEndBorrowInsts.insert(insertionPoint);
384362
return false;
385363
}

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)