Skip to content

Commit 0ce821a

Browse files
Merge pull request #58939 from nate-chandler/rdar93369506
[LexicalDestroyHoisting] Adopt iterative dataflow.
2 parents 7cb0c28 + e68c12f commit 0ce821a

File tree

2 files changed

+182
-85
lines changed

2 files changed

+182
-85
lines changed

lib/SILOptimizer/Utils/LexicalDestroyHoisting.cpp

Lines changed: 117 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/SIL/SILInstruction.h"
2121
#include "swift/SIL/SILValue.h"
2222
#include "swift/SILOptimizer/Analysis/Reachability.h"
23+
#include "swift/SILOptimizer/Analysis/VisitBarrierAccessScopes.h"
2324
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
2425
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2526
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
@@ -43,14 +44,17 @@ struct Context final {
4344
/// value->getDefiningInstruction()
4445
SILInstruction *const definition;
4546

47+
SILBasicBlock *defBlock;
48+
4649
SILFunction &function;
4750

4851
InstructionDeleter &deleter;
4952

5053
Context(SILValue const &value, SILFunction &function,
5154
InstructionDeleter &deleter)
5255
: value(value), definition(value->getDefiningInstruction()),
53-
function(function), deleter(deleter) {
56+
defBlock(value->getParentBlock()), function(function),
57+
deleter(deleter) {
5458
assert(value->isLexical());
5559
assert(value->getOwnershipKind() == OwnershipKind::Owned);
5660
}
@@ -63,7 +67,7 @@ struct Usage final {
6367
/// Instructions which are users of the simple (i.e. not reborrowed) value.
6468
SmallPtrSet<SILInstruction *, 16> users;
6569
// The instructions from which the hoisting starts, the destroy_values.
66-
llvm::SmallSetVector<SILInstruction *, 4> ends;
70+
llvm::SmallVector<SILInstruction *, 4> ends;
6771

6872
Usage(){};
6973
Usage(Usage const &) = delete;
@@ -85,7 +89,7 @@ bool findUsage(Context const &context, Usage &usage) {
8589
// flow and determine whether any were reused. They aren't uses over which
8690
// we can't hoist though.
8791
if (isa<DestroyValueInst>(use->getUser())) {
88-
usage.ends.insert(use->getUser());
92+
usage.ends.push_back(use->getUser());
8993
} else {
9094
usage.users.insert(use->getUser());
9195
}
@@ -95,95 +99,108 @@ bool findUsage(Context const &context, Usage &usage) {
9599

96100
/// How destroy_value hoisting is obstructed.
97101
struct DeinitBarriers final {
98-
/// Blocks up to "before the beginning" of which hoisting was able to proceed.
99-
BasicBlockSetVector hoistingReachesBeginBlocks;
100-
101-
/// Blocks to "after the end" of which hoisting was able to proceed.
102-
BasicBlockSet hoistingReachesEndBlocks;
103-
104102
/// Instructions above which destroy_values cannot be hoisted.
105-
SmallVector<SILInstruction *, 4> barriers;
103+
SmallVector<SILInstruction *, 4> instructions;
106104

107105
/// Blocks one of whose phis is a barrier and consequently out of which
108106
/// destroy_values cannot be hoisted.
109-
SmallVector<SILBasicBlock *, 4> phiBarriers;
107+
SmallVector<SILBasicBlock *, 4> phis;
110108

111-
DeinitBarriers(Context &context)
112-
: hoistingReachesBeginBlocks(&context.function),
113-
hoistingReachesEndBlocks(&context.function) {}
109+
SmallVector<SILBasicBlock *, 4> blocks;
110+
111+
DeinitBarriers(Context &context) {}
114112
DeinitBarriers(DeinitBarriers const &) = delete;
115113
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
116114
};
117115

116+
class BarrierAccessScopeFinder;
117+
118118
/// Works backwards from the current location of destroy_values to the earliest
119119
/// place they can be hoisted to.
120120
///
121-
/// Implements BackwardReachability::BlockReachability.
122-
class DataFlow final {
121+
/// Implements IterativeBackwardReachability::Effects
122+
/// Implements IterativeBackwardReachability::bindBarriers::Visitor
123+
/// Implements VisitBarrierAccessScopes::Effects
124+
class Dataflow final {
125+
using Reachability = IterativeBackwardReachability<Dataflow>;
126+
using Effect = Reachability::Effect;
123127
Context const &context;
124128
Usage const &uses;
125-
DeinitBarriers &result;
129+
DeinitBarriers &barriers;
130+
Reachability::Result result;
131+
Reachability reachability;
132+
SmallPtrSet<BeginAccessInst *, 8> barrierAccessScopes;
126133

127134
enum class Classification { Barrier, Other };
128135

129-
BackwardReachability<DataFlow> reachability;
130-
131136
public:
132-
DataFlow(Context const &context, Usage const &uses, DeinitBarriers &result)
133-
: context(context), uses(uses), result(result),
134-
reachability(&context.function, *this) {
135-
// Seed reachability with the scope ending uses from which the backwards
136-
// data flow will begin.
137-
for (auto *end : uses.ends) {
138-
reachability.initLastUse(end);
139-
}
140-
}
141-
DataFlow(DataFlow const &) = delete;
142-
DataFlow &operator=(DataFlow const &) = delete;
137+
Dataflow(Context const &context, Usage const &uses, DeinitBarriers &barriers)
138+
: context(context), uses(uses), barriers(barriers),
139+
result(&context.function),
140+
reachability(&context.function, context.defBlock, *this, result) {}
141+
Dataflow(Dataflow const &) = delete;
142+
Dataflow &operator=(Dataflow const &) = delete;
143143

144-
void run() { reachability.solveBackward(); }
144+
void run();
145145

146146
private:
147-
friend class BackwardReachability<DataFlow>;
147+
friend Reachability;
148+
friend class BarrierAccessScopeFinder;
149+
friend class VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
148150

149-
bool hasReachableBegin(SILBasicBlock *block) {
150-
return result.hoistingReachesBeginBlocks.contains(block);
151-
}
151+
Classification classifyInstruction(SILInstruction *);
152152

153-
void markReachableBegin(SILBasicBlock *block) {
154-
result.hoistingReachesBeginBlocks.insert(block);
155-
}
153+
bool classificationIsBarrier(Classification);
156154

157-
void markReachableEnd(SILBasicBlock *block) {
158-
result.hoistingReachesEndBlocks.insert(block);
159-
}
155+
/// IterativeBackwardReachability::Effects
156+
/// VisitBarrierAccessScopes::Effects
160157

161-
Classification classifyInstruction(SILInstruction *);
158+
ArrayRef<SILInstruction *> gens() { return uses.ends; }
162159

163-
bool classificationIsBarrier(Classification);
160+
Effect effectForInstruction(SILInstruction *);
161+
Effect effectForPhi(SILBasicBlock *);
162+
163+
/// VisitBarrierAccessScopes::Effects
164+
165+
auto localGens() { return result.localGens; }
164166

165-
void visitedInstruction(SILInstruction *, Classification);
167+
bool isLocalGen(SILInstruction *instruction) {
168+
return result.localGens.contains(instruction);
169+
}
170+
171+
/// IterativeBackwardReachability::bindBarriers::Visitor
172+
173+
void visitBarrierInstruction(SILInstruction *instruction) {
174+
barriers.instructions.push_back(instruction);
175+
}
166176

167-
bool checkReachableBarrier(SILInstruction *);
177+
void visitBarrierPhi(SILBasicBlock *block) { barriers.phis.push_back(block); }
168178

169-
bool checkReachablePhiBarrier(SILBasicBlock *);
179+
void visitBarrierBlock(SILBasicBlock *block) {
180+
barriers.blocks.push_back(block);
181+
}
170182
};
171183

172-
DataFlow::Classification
173-
DataFlow::classifyInstruction(SILInstruction *instruction) {
184+
Dataflow::Classification
185+
Dataflow::classifyInstruction(SILInstruction *instruction) {
174186
if (instruction == context.definition) {
175187
return Classification::Barrier;
176188
}
177189
if (uses.users.contains(instruction)) {
178190
return Classification::Barrier;
179191
}
192+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
193+
return barrierAccessScopes.contains(eai->getBeginAccess())
194+
? Classification::Barrier
195+
: Classification::Other;
196+
}
180197
if (isDeinitBarrier(instruction)) {
181198
return Classification::Barrier;
182199
}
183200
return Classification::Other;
184201
}
185202

186-
bool DataFlow::classificationIsBarrier(Classification classification) {
203+
bool Dataflow::classificationIsBarrier(Classification classification) {
187204
switch (classification) {
188205
case Classification::Barrier:
189206
return true;
@@ -193,26 +210,15 @@ bool DataFlow::classificationIsBarrier(Classification classification) {
193210
llvm_unreachable("exhaustive switch not exhaustive?!");
194211
}
195212

196-
void DataFlow::visitedInstruction(SILInstruction *instruction,
197-
Classification classification) {
198-
assert(classifyInstruction(instruction) == classification);
199-
switch (classification) {
200-
case Classification::Barrier:
201-
result.barriers.push_back(instruction);
202-
return;
203-
case Classification::Other:
204-
return;
205-
}
206-
llvm_unreachable("exhaustive switch not exhaustive?!");
207-
}
208-
209-
bool DataFlow::checkReachableBarrier(SILInstruction *instruction) {
213+
Dataflow::Effect Dataflow::effectForInstruction(SILInstruction *instruction) {
214+
if (llvm::find(uses.ends, instruction) != uses.ends.end())
215+
return Effect::Gen();
210216
auto classification = classifyInstruction(instruction);
211-
visitedInstruction(instruction, classification);
212-
return classificationIsBarrier(classification);
217+
return classificationIsBarrier(classification) ? Effect::Kill()
218+
: Effect::NoEffect();
213219
}
214220

215-
bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
221+
Dataflow::Effect Dataflow::effectForPhi(SILBasicBlock *block) {
216222
assert(llvm::all_of(block->getArguments(),
217223
[&](auto argument) { return PhiValue(argument); }));
218224

@@ -221,10 +227,46 @@ bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
221227
return classificationIsBarrier(
222228
classifyInstruction(predecessor->getTerminator()));
223229
});
224-
if (isBarrier) {
225-
result.phiBarriers.push_back(block);
230+
return isBarrier ? Effect::Kill() : Effect::NoEffect();
231+
}
232+
233+
/// Finds end_access instructions which are barriers to hoisting because the
234+
/// access scopes they contain barriers to hoisting. Hoisting destroy_values
235+
/// into such access scopes could introduce exclusivity violations.
236+
///
237+
/// Implements BarrierAccessScopeFinder::Visitor
238+
class BarrierAccessScopeFinder final {
239+
using Impl = VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
240+
Impl impl;
241+
Dataflow &dataflow;
242+
243+
public:
244+
BarrierAccessScopeFinder(Context const &context, Dataflow &dataflow)
245+
: impl(&context.function, dataflow, *this), dataflow(dataflow) {}
246+
247+
void find() { impl.visit(); }
248+
249+
private:
250+
friend Impl;
251+
252+
bool isInRegion(SILBasicBlock *block) {
253+
return dataflow.result.discoveredBlocks.contains(block);
254+
}
255+
256+
void visitBarrierAccessScope(BeginAccessInst *bai) {
257+
dataflow.barrierAccessScopes.insert(bai);
258+
for (auto *eai : bai->getEndAccesses()) {
259+
dataflow.reachability.addKill(eai);
260+
}
226261
}
227-
return isBarrier;
262+
};
263+
264+
void Dataflow::run() {
265+
reachability.initialize();
266+
BarrierAccessScopeFinder finder(context, *this);
267+
finder.find();
268+
reachability.solve();
269+
reachability.findBarriers(*this);
228270
}
229271

230272
/// Hoist the destroy_values of %value.
@@ -256,7 +298,7 @@ bool Rewriter::run() {
256298
//
257299
// A block is a phi barrier iff any of its predecessors' terminators get
258300
// classified as barriers.
259-
for (auto *block : barriers.phiBarriers) {
301+
for (auto *block : barriers.phis) {
260302
madeChange |= createDestroyValue(&block->front());
261303
}
262304

@@ -271,13 +313,9 @@ bool Rewriter::run() {
271313
// have returned true for P, so none of its instructions would ever have been
272314
// classified (except for via checkReachablePhiBarrier, which doesn't record
273315
// terminator barriers).
274-
for (auto instruction : barriers.barriers) {
316+
for (auto instruction : barriers.instructions) {
275317
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
276318
auto successors = terminator->getParentBlock()->getSuccessorBlocks();
277-
// In order for the instruction to have been classified as a barrier,
278-
// reachability would have had to reach the block containing it.
279-
assert(barriers.hoistingReachesEndBlocks.contains(
280-
terminator->getParentBlock()));
281319
for (auto *successor : successors) {
282320
madeChange |= createDestroyValue(&successor->front());
283321
}
@@ -301,12 +339,8 @@ bool Rewriter::run() {
301339
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
302340
//
303341
// control-flow-boundary(B) := beginning-reachable(B) && !end-reachable(P)
304-
for (auto *block : barriers.hoistingReachesBeginBlocks) {
305-
if (auto *predecessor = block->getSinglePredecessorBlock()) {
306-
if (!barriers.hoistingReachesEndBlocks.contains(predecessor)) {
307-
madeChange |= createDestroyValue(&block->front());
308-
}
309-
}
342+
for (auto *block : barriers.blocks) {
343+
madeChange |= createDestroyValue(&block->front());
310344
}
311345

312346
if (madeChange) {
@@ -324,7 +358,7 @@ bool Rewriter::run() {
324358

325359
bool Rewriter::createDestroyValue(SILInstruction *insertionPoint) {
326360
if (auto *ebi = dyn_cast<DestroyValueInst>(insertionPoint)) {
327-
if (uses.ends.contains(insertionPoint)) {
361+
if (llvm::find(uses.ends, insertionPoint) != uses.ends.end()) {
328362
reusedDestroyValueInsts.insert(insertionPoint);
329363
return false;
330364
}
@@ -342,7 +376,7 @@ bool run(Context &context) {
342376
return false;
343377

344378
DeinitBarriers barriers(context);
345-
DataFlow flow(context, usage, barriers);
379+
Dataflow flow(context, usage, barriers);
346380
flow.run();
347381

348382
Rewriter rewriter(context, usage, barriers);

0 commit comments

Comments
 (0)