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.
97101struct 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-
131136public:
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
146146private:
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
325359bool 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