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.
114118struct 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-
151162public:
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
166172private:
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
380423bool 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