20
20
#include " swift/SIL/SILInstruction.h"
21
21
#include " swift/SIL/SILValue.h"
22
22
#include " swift/SILOptimizer/Analysis/Reachability.h"
23
+ #include " swift/SILOptimizer/Analysis/VisitBarrierAccessScopes.h"
23
24
#include " swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
24
25
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
25
26
#include " swift/SILOptimizer/Utils/InstructionDeleter.h"
@@ -43,14 +44,17 @@ struct Context final {
43
44
// / value->getDefiningInstruction()
44
45
SILInstruction *const definition;
45
46
47
+ SILBasicBlock *defBlock;
48
+
46
49
SILFunction &function;
47
50
48
51
InstructionDeleter &deleter;
49
52
50
53
Context (SILValue const &value, SILFunction &function,
51
54
InstructionDeleter &deleter)
52
55
: value(value), definition(value->getDefiningInstruction ()),
53
- function(function), deleter(deleter) {
56
+ defBlock(value->getParentBlock ()), function(function),
57
+ deleter(deleter) {
54
58
assert (value->isLexical ());
55
59
assert (value->getOwnershipKind () == OwnershipKind::Owned);
56
60
}
@@ -63,7 +67,7 @@ struct Usage final {
63
67
// / Instructions which are users of the simple (i.e. not reborrowed) value.
64
68
SmallPtrSet<SILInstruction *, 16 > users;
65
69
// The instructions from which the hoisting starts, the destroy_values.
66
- llvm::SmallSetVector <SILInstruction *, 4 > ends;
70
+ llvm::SmallVector <SILInstruction *, 4 > ends;
67
71
68
72
Usage (){};
69
73
Usage (Usage const &) = delete ;
@@ -85,7 +89,7 @@ bool findUsage(Context const &context, Usage &usage) {
85
89
// flow and determine whether any were reused. They aren't uses over which
86
90
// we can't hoist though.
87
91
if (isa<DestroyValueInst>(use->getUser ())) {
88
- usage.ends .insert (use->getUser ());
92
+ usage.ends .push_back (use->getUser ());
89
93
} else {
90
94
usage.users .insert (use->getUser ());
91
95
}
@@ -95,95 +99,108 @@ bool findUsage(Context const &context, Usage &usage) {
95
99
96
100
// / How destroy_value hoisting is obstructed.
97
101
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
-
104
102
// / Instructions above which destroy_values cannot be hoisted.
105
- SmallVector<SILInstruction *, 4 > barriers ;
103
+ SmallVector<SILInstruction *, 4 > instructions ;
106
104
107
105
// / Blocks one of whose phis is a barrier and consequently out of which
108
106
// / destroy_values cannot be hoisted.
109
- SmallVector<SILBasicBlock *, 4 > phiBarriers ;
107
+ SmallVector<SILBasicBlock *, 4 > phis ;
110
108
111
- DeinitBarriers (Context &context)
112
- : hoistingReachesBeginBlocks(&context.function),
113
- hoistingReachesEndBlocks ( &context.function ) {}
109
+ SmallVector<SILBasicBlock *, 4 > blocks;
110
+
111
+ DeinitBarriers (Context &context) {}
114
112
DeinitBarriers (DeinitBarriers const &) = delete ;
115
113
DeinitBarriers &operator =(DeinitBarriers const &) = delete ;
116
114
};
117
115
116
+ class BarrierAccessScopeFinder ;
117
+
118
118
// / Works backwards from the current location of destroy_values to the earliest
119
119
// / place they can be hoisted to.
120
120
// /
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;
123
127
Context const &context;
124
128
Usage const &uses;
125
- DeinitBarriers &result;
129
+ DeinitBarriers &barriers;
130
+ Reachability::Result result;
131
+ Reachability reachability;
132
+ SmallPtrSet<BeginAccessInst *, 8 > barrierAccessScopes;
126
133
127
134
enum class Classification { Barrier, Other };
128
135
129
- BackwardReachability<DataFlow> reachability;
130
-
131
136
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 ;
143
143
144
- void run () { reachability. solveBackward (); }
144
+ void run ();
145
145
146
146
private:
147
- friend class BackwardReachability <DataFlow>;
147
+ friend Reachability;
148
+ friend class BarrierAccessScopeFinder ;
149
+ friend class VisitBarrierAccessScopes <Dataflow, BarrierAccessScopeFinder>;
148
150
149
- bool hasReachableBegin (SILBasicBlock *block) {
150
- return result.hoistingReachesBeginBlocks .contains (block);
151
- }
151
+ Classification classifyInstruction (SILInstruction *);
152
152
153
- void markReachableBegin (SILBasicBlock *block) {
154
- result.hoistingReachesBeginBlocks .insert (block);
155
- }
153
+ bool classificationIsBarrier (Classification);
156
154
157
- void markReachableEnd (SILBasicBlock *block) {
158
- result.hoistingReachesEndBlocks .insert (block);
159
- }
155
+ // / IterativeBackwardReachability::Effects
156
+ // / VisitBarrierAccessScopes::Effects
160
157
161
- Classification classifyInstruction ( SILInstruction *);
158
+ ArrayRef< SILInstruction *> gens () { return uses. ends ; }
162
159
163
- bool classificationIsBarrier (Classification);
160
+ Effect effectForInstruction (SILInstruction *);
161
+ Effect effectForPhi (SILBasicBlock *);
162
+
163
+ // / VisitBarrierAccessScopes::Effects
164
+
165
+ auto localGens () { return result.localGens ; }
164
166
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
+ }
166
176
167
- bool checkReachableBarrier (SILInstruction *);
177
+ void visitBarrierPhi (SILBasicBlock *block) { barriers. phis . push_back (block); }
168
178
169
- bool checkReachablePhiBarrier (SILBasicBlock *);
179
+ void visitBarrierBlock (SILBasicBlock *block) {
180
+ barriers.blocks .push_back (block);
181
+ }
170
182
};
171
183
172
- DataFlow ::Classification
173
- DataFlow ::classifyInstruction (SILInstruction *instruction) {
184
+ Dataflow ::Classification
185
+ Dataflow ::classifyInstruction (SILInstruction *instruction) {
174
186
if (instruction == context.definition ) {
175
187
return Classification::Barrier;
176
188
}
177
189
if (uses.users .contains (instruction)) {
178
190
return Classification::Barrier;
179
191
}
192
+ if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
193
+ return barrierAccessScopes.contains (eai->getBeginAccess ())
194
+ ? Classification::Barrier
195
+ : Classification::Other;
196
+ }
180
197
if (isDeinitBarrier (instruction)) {
181
198
return Classification::Barrier;
182
199
}
183
200
return Classification::Other;
184
201
}
185
202
186
- bool DataFlow ::classificationIsBarrier (Classification classification) {
203
+ bool Dataflow ::classificationIsBarrier (Classification classification) {
187
204
switch (classification) {
188
205
case Classification::Barrier:
189
206
return true ;
@@ -193,26 +210,15 @@ bool DataFlow::classificationIsBarrier(Classification classification) {
193
210
llvm_unreachable (" exhaustive switch not exhaustive?!" );
194
211
}
195
212
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 ();
210
216
auto classification = classifyInstruction (instruction);
211
- visitedInstruction (instruction, classification);
212
- return classificationIsBarrier (classification );
217
+ return classificationIsBarrier ( classification) ? Effect::Kill ()
218
+ : Effect::NoEffect ( );
213
219
}
214
220
215
- bool DataFlow::checkReachablePhiBarrier (SILBasicBlock *block) {
221
+ Dataflow::Effect Dataflow::effectForPhi (SILBasicBlock *block) {
216
222
assert (llvm::all_of (block->getArguments (),
217
223
[&](auto argument) { return PhiValue (argument); }));
218
224
@@ -221,10 +227,46 @@ bool DataFlow::checkReachablePhiBarrier(SILBasicBlock *block) {
221
227
return classificationIsBarrier (
222
228
classifyInstruction (predecessor->getTerminator ()));
223
229
});
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
+ }
226
261
}
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 );
228
270
}
229
271
230
272
// / Hoist the destroy_values of %value.
@@ -256,7 +298,7 @@ bool Rewriter::run() {
256
298
//
257
299
// A block is a phi barrier iff any of its predecessors' terminators get
258
300
// classified as barriers.
259
- for (auto *block : barriers.phiBarriers ) {
301
+ for (auto *block : barriers.phis ) {
260
302
madeChange |= createDestroyValue (&block->front ());
261
303
}
262
304
@@ -271,13 +313,9 @@ bool Rewriter::run() {
271
313
// have returned true for P, so none of its instructions would ever have been
272
314
// classified (except for via checkReachablePhiBarrier, which doesn't record
273
315
// terminator barriers).
274
- for (auto instruction : barriers.barriers ) {
316
+ for (auto instruction : barriers.instructions ) {
275
317
if (auto *terminator = dyn_cast<TermInst>(instruction)) {
276
318
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 ()));
281
319
for (auto *successor : successors) {
282
320
madeChange |= createDestroyValue (&successor->front ());
283
321
}
@@ -301,12 +339,8 @@ bool Rewriter::run() {
301
339
// P not having a reachable end--see BackwardReachability::meetOverSuccessors.
302
340
//
303
341
// 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 ());
310
344
}
311
345
312
346
if (madeChange) {
@@ -324,7 +358,7 @@ bool Rewriter::run() {
324
358
325
359
bool Rewriter::createDestroyValue (SILInstruction *insertionPoint) {
326
360
if (auto *ebi = dyn_cast<DestroyValueInst>(insertionPoint)) {
327
- if (uses.ends . contains (insertionPoint )) {
361
+ if (llvm::find ( uses.ends , insertionPoint) != uses. ends . end ( )) {
328
362
reusedDestroyValueInsts.insert (insertionPoint);
329
363
return false ;
330
364
}
@@ -342,7 +376,7 @@ bool run(Context &context) {
342
376
return false ;
343
377
344
378
DeinitBarriers barriers (context);
345
- DataFlow flow (context, usage, barriers);
379
+ Dataflow flow (context, usage, barriers);
346
380
flow.run ();
347
381
348
382
Rewriter rewriter (context, usage, barriers);
0 commit comments