Skip to content

Commit 5bd541c

Browse files
Merge pull request #58934 from nate-chandler/rdar93186505
[ShrinkBorrowScope] Adopt iterative dataflow.
2 parents 6b5ca84 + 3c7ad60 commit 5bd541c

File tree

2 files changed

+207
-91
lines changed

2 files changed

+207
-91
lines changed

lib/SILOptimizer/Utils/ShrinkBorrowScope.cpp

Lines changed: 131 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/SIL/SILBasicBlock.h"
2222
#include "swift/SIL/SILInstruction.h"
2323
#include "swift/SILOptimizer/Analysis/Reachability.h"
24+
#include "swift/SILOptimizer/Analysis/VisitBarrierAccessScopes.h"
2425
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
2526
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2627
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
@@ -49,6 +50,8 @@ struct Context final {
4950
/// introducer->getOperand()
5051
SILValue const borrowee;
5152

53+
SILBasicBlock *defBlock;
54+
5255
SILFunction &function;
5356

5457
/// The copy_value instructions that the utility creates or changes.
@@ -62,7 +65,8 @@ struct Context final {
6265
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts,
6366
InstructionDeleter &deleter)
6467
: introducer(introducer), borrowedValue(BorrowedValue(&introducer)),
65-
borrowee(introducer.getOperand()), function(*introducer.getFunction()),
68+
borrowee(introducer.getOperand()), defBlock(introducer.getParent()),
69+
function(*introducer.getFunction()),
6670
modifiedCopyValueInsts(modifiedCopyValueInsts), deleter(deleter) {}
6771
Context(Context const &) = delete;
6872
Context &operator=(Context const &) = delete;
@@ -75,7 +79,7 @@ struct Usage final {
7579
SmallPtrSet<SILInstruction *, 16> users;
7680
// The instructions from which the shrinking starts, the scope ending
7781
// instructions.
78-
llvm::SmallSetVector<SILInstruction *, 4> ends;
82+
llvm::SmallVector<SILInstruction *, 4> ends;
7983

8084
Usage(){};
8185
Usage(Usage const &) = delete;
@@ -95,7 +99,7 @@ bool findUsage(Context const &context, Usage &usage) {
9599
// If a scope ending instruction is not an end_borrow, bail out.
96100
if (!isa<EndBorrowInst>(instruction))
97101
return false;
98-
usage.ends.insert(instruction);
102+
usage.ends.push_back(instruction);
99103
}
100104

101105
SmallVector<Operand *, 16> uses;
@@ -112,81 +116,96 @@ bool findUsage(Context const &context, Usage &usage) {
112116

113117
/// How end_borrow hoisting is obstructed.
114118
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-
121119
/// Copies to be rewritten as copies of %borrowee.
122120
SmallVector<CopyValueInst *, 4> copies;
123121

124122
/// Instructions above which end_borrows cannot be hoisted.
125-
SmallVector<SILInstruction *, 4> barriers;
123+
SmallVector<SILInstruction *, 4> instructions;
126124

127125
/// Blocks one of whose phis is a barrier and consequently out of which
128126
/// end_borrows cannot be hoisted.
129-
SmallVector<SILBasicBlock *, 4> phiBarriers;
127+
SmallVector<SILBasicBlock *, 4> phis;
130128

131-
DeinitBarriers(Context &context)
132-
: hoistingReachesBeginBlocks(&context.function),
133-
hoistingReachesEndBlocks(&context.function) {}
129+
/// Blocks whose single predecessors has another successor to the top of which
130+
/// end_borrows cannot be hoisted.
131+
SmallVector<SILBasicBlock *, 4> blocks;
132+
133+
DeinitBarriers(Context &context) {}
134134
DeinitBarriers(DeinitBarriers const &) = delete;
135135
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
136136
};
137137

138+
class BarrierAccessScopeFinder;
139+
138140
/// Works backwards from the current location of end_borrows to the earliest
139141
/// place they can be hoisted to.
140142
///
141-
/// Implements BackwardReachability::BlockReachability.
142-
class DataFlow final {
143+
/// Implements IterativeBackwardReachability::Effects.
144+
/// Implements IterativeBackwardReachability::findBarrier::Visitor.
145+
/// Implements VisitBarrierAccessScopes::Effects
146+
class Dataflow final {
147+
public:
148+
using Reachability = IterativeBackwardReachability<Dataflow>;
149+
using Effect = Reachability::Effect;
150+
151+
private:
143152
Context const &context;
144153
Usage const &uses;
145-
DeinitBarriers &result;
154+
DeinitBarriers &barriers;
155+
Reachability::Result result;
156+
Reachability reachability;
157+
SmallPtrSet<BeginAccessInst *, 8> barrierAccessScopes;
158+
bool recordCopies = false;
146159

147160
enum class Classification { Barrier, Copy, Other };
148161

149-
BackwardReachability<DataFlow> reachability;
150-
151162
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-
}
161-
DataFlow(DataFlow const &) = delete;
162-
DataFlow &operator=(DataFlow const &) = delete;
163+
Dataflow(Context const &context, Usage const &uses, DeinitBarriers &barriers)
164+
: context(context), uses(uses), barriers(barriers),
165+
result(&context.function),
166+
reachability(&context.function, context.defBlock, *this, result) {}
167+
Dataflow(Dataflow const &) = delete;
168+
Dataflow &operator=(Dataflow const &) = delete;
163169

164-
void run() { reachability.solveBackward(); }
170+
void run();
165171

166172
private:
167-
friend class BackwardReachability<DataFlow>;
173+
friend Reachability;
174+
friend class BarrierAccessScopeFinder;
175+
friend class VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
168176

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

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

177-
void markReachableEnd(SILBasicBlock *block) {
178-
result.hoistingReachesEndBlocks.insert(block);
179-
}
181+
/// IterativeBackwardReachability::Effects
182+
/// VisitBarrierAccessScopes::Effects
180183

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

183-
bool classificationIsBarrier(Classification);
186+
Effect effectForInstruction(SILInstruction *);
187+
188+
Effect effectForPhi(SILBasicBlock *);
184189

185-
void visitedInstruction(SILInstruction *, Classification);
190+
/// VisitBarrierAccessScopes::Effects
186191

187-
bool checkReachableBarrier(SILInstruction *);
192+
auto localGens() { return result.localGens; }
188193

189-
bool checkReachablePhiBarrier(SILBasicBlock *);
194+
bool isLocalGen(SILInstruction *instruction) {
195+
return result.localGens.contains(instruction);
196+
}
197+
198+
/// IterativeBackwardReachability::findBarrier::Visitor.
199+
200+
void visitBarrierInstruction(SILInstruction *instruction) {
201+
barriers.instructions.push_back(instruction);
202+
}
203+
204+
void visitBarrierPhi(SILBasicBlock *block) { barriers.phis.push_back(block); }
205+
206+
void visitBarrierBlock(SILBasicBlock *block) {
207+
barriers.blocks.push_back(block);
208+
}
190209
};
191210

192211
/// Whether the specified value is %lifetime or its iterated copy_value.
@@ -207,8 +226,8 @@ bool isSimpleExtendedIntroducerDef(Context const &context, SILValue value) {
207226
}
208227
}
209228

210-
DataFlow::Classification
211-
DataFlow::classifyInstruction(SILInstruction *instruction) {
229+
Dataflow::Classification
230+
Dataflow::classifyInstruction(SILInstruction *instruction) {
212231
if (instruction == &context.introducer) {
213232
return Classification::Barrier;
214233
}
@@ -220,13 +239,18 @@ DataFlow::classifyInstruction(SILInstruction *instruction) {
220239
if (uses.users.contains(instruction)) {
221240
return Classification::Barrier;
222241
}
242+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
243+
return barrierAccessScopes.contains(eai->getBeginAccess())
244+
? Classification::Barrier
245+
: Classification::Other;
246+
}
223247
if (isDeinitBarrier(instruction)) {
224248
return Classification::Barrier;
225249
}
226250
return Classification::Other;
227251
}
228252

229-
bool DataFlow::classificationIsBarrier(Classification classification) {
253+
bool Dataflow::classificationIsBarrier(Classification classification) {
230254
switch (classification) {
231255
case Classification::Barrier:
232256
return true;
@@ -237,29 +261,17 @@ bool DataFlow::classificationIsBarrier(Classification classification) {
237261
llvm_unreachable("exhaustive switch not exhaustive?!");
238262
}
239263

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) {
264+
Dataflow::Effect Dataflow::effectForInstruction(SILInstruction *instruction) {
265+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
266+
return Effect::Gen();
257267
auto classification = classifyInstruction(instruction);
258-
visitedInstruction(instruction, classification);
259-
return classificationIsBarrier(classification);
268+
if (recordCopies && classification == Classification::Copy)
269+
barriers.copies.push_back(cast<CopyValueInst>(instruction));
270+
return classificationIsBarrier(classification) ? Effect::Kill()
271+
: Effect::NoEffect();
260272
}
261273

262-
bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
274+
Dataflow::Effect Dataflow::effectForPhi(SILBasicBlock *block) {
263275
assert(llvm::all_of(block->getArguments(),
264276
[&](auto argument) { return PhiValue(argument); }));
265277

@@ -268,10 +280,49 @@ bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
268280
return classificationIsBarrier(
269281
classifyInstruction(predecessor->getTerminator()));
270282
});
271-
if (isBarrier) {
272-
result.phiBarriers.push_back(block);
283+
return isBarrier ? Effect::Kill() : Effect::NoEffect();
284+
}
285+
286+
/// Finds end_access instructions which are barriers to hoisting because the
287+
/// access scopes they contain barriers to hoisting. Hoisting end_borrows into
288+
/// such access scopes could introduce exclusivity violations.
289+
///
290+
/// Implements BarrierAccessScopeFinder::Visitor
291+
class BarrierAccessScopeFinder final {
292+
using Impl = VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
293+
Context const &context;
294+
Impl impl;
295+
Dataflow &dataflow;
296+
297+
public:
298+
BarrierAccessScopeFinder(Context const &context, Dataflow &dataflow)
299+
: context(context), impl(&context.function, dataflow, *this),
300+
dataflow(dataflow) {}
301+
302+
void find() { impl.visit(); }
303+
304+
private:
305+
friend Impl;
306+
307+
bool isInRegion(SILBasicBlock *block) {
308+
return dataflow.result.discoveredBlocks.contains(block);
273309
}
274-
return isBarrier;
310+
311+
void visitBarrierAccessScope(BeginAccessInst *bai) {
312+
dataflow.barrierAccessScopes.insert(bai);
313+
for (auto *eai : bai->getEndAccesses()) {
314+
dataflow.reachability.addKill(eai);
315+
}
316+
}
317+
};
318+
319+
void Dataflow::run() {
320+
reachability.initialize();
321+
BarrierAccessScopeFinder finder(context, *this);
322+
finder.find();
323+
reachability.solve();
324+
recordCopies = true;
325+
reachability.findBarriers(*this);
275326
}
276327

277328
/// Hoist the scope ends of %lifetime, rewriting copies and borrows along the
@@ -311,7 +362,7 @@ bool Rewriter::run() {
311362
// A block is a phi barrier iff any of its predecessors' terminators get
312363
// classified as barriers. That happens when a copy of %lifetime is passed
313364
// to a phi.
314-
for (auto *block : barriers.phiBarriers) {
365+
for (auto *block : barriers.phis) {
315366
madeChange |= createEndBorrow(&block->front());
316367
}
317368

@@ -324,15 +375,11 @@ bool Rewriter::run() {
324375
// of a block P's successors B had reachable beginnings. If any of them
325376
// didn't, then BackwardReachability::meetOverSuccessors would never have
326377
// 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) {
378+
// classified (except for via effectForPhi, which doesn't record terminator
379+
// barriers).
380+
for (auto instruction : barriers.instructions) {
330381
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
331382
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()));
336383
for (auto *successor : successors) {
337384
madeChange |= createEndBorrow(&successor->front());
338385
}
@@ -356,12 +403,8 @@ bool Rewriter::run() {
356403
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
357404
//
358405
// 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-
}
406+
for (auto *block : barriers.blocks) {
407+
madeChange |= createEndBorrow(&block->front());
365408
}
366409

367410
if (madeChange) {
@@ -379,7 +422,7 @@ bool Rewriter::run() {
379422

380423
bool Rewriter::createEndBorrow(SILInstruction *insertionPoint) {
381424
if (auto *ebi = dyn_cast<EndBorrowInst>(insertionPoint)) {
382-
if (uses.ends.contains(insertionPoint)) {
425+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
383426
reusedEndBorrowInsts.insert(insertionPoint);
384427
return false;
385428
}
@@ -397,7 +440,7 @@ bool run(Context &context) {
397440
return false;
398441

399442
DeinitBarriers barriers(context);
400-
DataFlow flow(context, usage, barriers);
443+
Dataflow flow(context, usage, barriers);
401444
flow.run();
402445

403446
Rewriter rewriter(context, usage, barriers);

0 commit comments

Comments
 (0)