21
21
#include " swift/SIL/SILBasicBlock.h"
22
22
#include " swift/SIL/SILInstruction.h"
23
23
#include " swift/SILOptimizer/Analysis/Reachability.h"
24
+ #include " swift/SILOptimizer/Analysis/VisitBarrierAccessScopes.h"
24
25
#include " swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
25
26
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
26
27
#include " swift/SILOptimizer/Utils/InstructionDeleter.h"
@@ -49,6 +50,8 @@ struct Context final {
49
50
// / introducer->getOperand()
50
51
SILValue const borrowee;
51
52
53
+ SILBasicBlock *defBlock;
54
+
52
55
SILFunction &function;
53
56
54
57
// / The copy_value instructions that the utility creates or changes.
@@ -62,7 +65,8 @@ struct Context final {
62
65
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts,
63
66
InstructionDeleter &deleter)
64
67
: introducer(introducer), borrowedValue(BorrowedValue(&introducer)),
65
- borrowee (introducer.getOperand()), function(*introducer.getFunction()),
68
+ borrowee (introducer.getOperand()), defBlock(introducer.getParent()),
69
+ function(*introducer.getFunction()),
66
70
modifiedCopyValueInsts(modifiedCopyValueInsts), deleter(deleter) {}
67
71
Context (Context const &) = delete;
68
72
Context &operator =(Context const &) = delete ;
@@ -75,7 +79,7 @@ struct Usage final {
75
79
SmallPtrSet<SILInstruction *, 16 > users;
76
80
// The instructions from which the shrinking starts, the scope ending
77
81
// instructions.
78
- llvm::SmallSetVector <SILInstruction *, 4 > ends;
82
+ llvm::SmallVector <SILInstruction *, 4 > ends;
79
83
80
84
Usage (){};
81
85
Usage (Usage const &) = delete ;
@@ -95,7 +99,7 @@ bool findUsage(Context const &context, Usage &usage) {
95
99
// If a scope ending instruction is not an end_borrow, bail out.
96
100
if (!isa<EndBorrowInst>(instruction))
97
101
return false ;
98
- usage.ends .insert (instruction);
102
+ usage.ends .push_back (instruction);
99
103
}
100
104
101
105
SmallVector<Operand *, 16 > uses;
@@ -112,81 +116,96 @@ bool findUsage(Context const &context, Usage &usage) {
112
116
113
117
// / How end_borrow hoisting is obstructed.
114
118
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
-
121
119
// / Copies to be rewritten as copies of %borrowee.
122
120
SmallVector<CopyValueInst *, 4 > copies;
123
121
124
122
// / Instructions above which end_borrows cannot be hoisted.
125
- SmallVector<SILInstruction *, 4 > barriers ;
123
+ SmallVector<SILInstruction *, 4 > instructions ;
126
124
127
125
// / Blocks one of whose phis is a barrier and consequently out of which
128
126
// / end_borrows cannot be hoisted.
129
- SmallVector<SILBasicBlock *, 4 > phiBarriers ;
127
+ SmallVector<SILBasicBlock *, 4 > phis ;
130
128
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) {}
134
134
DeinitBarriers (DeinitBarriers const &) = delete ;
135
135
DeinitBarriers &operator =(DeinitBarriers const &) = delete ;
136
136
};
137
137
138
+ class BarrierAccessScopeFinder ;
139
+
138
140
// / Works backwards from the current location of end_borrows to the earliest
139
141
// / place they can be hoisted to.
140
142
// /
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:
143
152
Context const &context;
144
153
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 ;
146
159
147
160
enum class Classification { Barrier, Copy, Other };
148
161
149
- BackwardReachability<DataFlow> reachability;
150
-
151
162
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 ;
163
169
164
- void run () { reachability. solveBackward (); }
170
+ void run ();
165
171
166
172
private:
167
- friend class BackwardReachability <DataFlow>;
173
+ friend Reachability;
174
+ friend class BarrierAccessScopeFinder ;
175
+ friend class VisitBarrierAccessScopes <Dataflow, BarrierAccessScopeFinder>;
168
176
169
- bool hasReachableBegin (SILBasicBlock *block) {
170
- return result.hoistingReachesBeginBlocks .contains (block);
171
- }
177
+ Classification classifyInstruction (SILInstruction *);
172
178
173
- void markReachableBegin (SILBasicBlock *block) {
174
- result.hoistingReachesBeginBlocks .insert (block);
175
- }
179
+ bool classificationIsBarrier (Classification);
176
180
177
- void markReachableEnd (SILBasicBlock *block) {
178
- result.hoistingReachesEndBlocks .insert (block);
179
- }
181
+ // / IterativeBackwardReachability::Effects
182
+ // / VisitBarrierAccessScopes::Effects
180
183
181
- Classification classifyInstruction ( SILInstruction *);
184
+ ArrayRef< SILInstruction *> gens () { return uses. ends ; }
182
185
183
- bool classificationIsBarrier (Classification);
186
+ Effect effectForInstruction (SILInstruction *);
187
+
188
+ Effect effectForPhi (SILBasicBlock *);
184
189
185
- void visitedInstruction (SILInstruction *, Classification);
190
+ // / VisitBarrierAccessScopes::Effects
186
191
187
- bool checkReachableBarrier (SILInstruction *);
192
+ auto localGens () { return result. localGens ; }
188
193
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
+ }
190
209
};
191
210
192
211
// / Whether the specified value is %lifetime or its iterated copy_value.
@@ -207,8 +226,8 @@ bool isSimpleExtendedIntroducerDef(Context const &context, SILValue value) {
207
226
}
208
227
}
209
228
210
- DataFlow ::Classification
211
- DataFlow ::classifyInstruction (SILInstruction *instruction) {
229
+ Dataflow ::Classification
230
+ Dataflow ::classifyInstruction (SILInstruction *instruction) {
212
231
if (instruction == &context.introducer ) {
213
232
return Classification::Barrier;
214
233
}
@@ -220,13 +239,18 @@ DataFlow::classifyInstruction(SILInstruction *instruction) {
220
239
if (uses.users .contains (instruction)) {
221
240
return Classification::Barrier;
222
241
}
242
+ if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
243
+ return barrierAccessScopes.contains (eai->getBeginAccess ())
244
+ ? Classification::Barrier
245
+ : Classification::Other;
246
+ }
223
247
if (isDeinitBarrier (instruction)) {
224
248
return Classification::Barrier;
225
249
}
226
250
return Classification::Other;
227
251
}
228
252
229
- bool DataFlow ::classificationIsBarrier (Classification classification) {
253
+ bool Dataflow ::classificationIsBarrier (Classification classification) {
230
254
switch (classification) {
231
255
case Classification::Barrier:
232
256
return true ;
@@ -237,29 +261,17 @@ bool DataFlow::classificationIsBarrier(Classification classification) {
237
261
llvm_unreachable (" exhaustive switch not exhaustive?!" );
238
262
}
239
263
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 ();
257
267
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 ();
260
272
}
261
273
262
- bool DataFlow::checkReachablePhiBarrier (SILBasicBlock *block) {
274
+ Dataflow::Effect Dataflow::effectForPhi (SILBasicBlock *block) {
263
275
assert (llvm::all_of (block->getArguments (),
264
276
[&](auto argument) { return PhiValue (argument); }));
265
277
@@ -268,10 +280,49 @@ bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
268
280
return classificationIsBarrier (
269
281
classifyInstruction (predecessor->getTerminator ()));
270
282
});
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);
273
309
}
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 );
275
326
}
276
327
277
328
// / Hoist the scope ends of %lifetime, rewriting copies and borrows along the
@@ -311,7 +362,7 @@ bool Rewriter::run() {
311
362
// A block is a phi barrier iff any of its predecessors' terminators get
312
363
// classified as barriers. That happens when a copy of %lifetime is passed
313
364
// to a phi.
314
- for (auto *block : barriers.phiBarriers ) {
365
+ for (auto *block : barriers.phis ) {
315
366
madeChange |= createEndBorrow (&block->front ());
316
367
}
317
368
@@ -324,15 +375,11 @@ bool Rewriter::run() {
324
375
// of a block P's successors B had reachable beginnings. If any of them
325
376
// didn't, then BackwardReachability::meetOverSuccessors would never have
326
377
// 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 ) {
330
381
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
331
382
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 ()));
336
383
for (auto *successor : successors) {
337
384
madeChange |= createEndBorrow (&successor->front ());
338
385
}
@@ -356,12 +403,8 @@ bool Rewriter::run() {
356
403
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
357
404
//
358
405
// 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 ());
365
408
}
366
409
367
410
if (madeChange) {
@@ -379,7 +422,7 @@ bool Rewriter::run() {
379
422
380
423
bool Rewriter::createEndBorrow (SILInstruction *insertionPoint) {
381
424
if (auto *ebi = dyn_cast<EndBorrowInst>(insertionPoint)) {
382
- if (uses.ends . contains (insertionPoint )) {
425
+ if (llvm::find ( uses.ends , insertionPoint) != uses. ends . end ( )) {
383
426
reusedEndBorrowInsts.insert (insertionPoint);
384
427
return false ;
385
428
}
@@ -397,7 +440,7 @@ bool run(Context &context) {
397
440
return false ;
398
441
399
442
DeinitBarriers barriers (context);
400
- DataFlow flow (context, usage, barriers);
443
+ Dataflow flow (context, usage, barriers);
401
444
flow.run ();
402
445
403
446
Rewriter rewriter (context, usage, barriers);
0 commit comments