@@ -204,6 +204,14 @@ class DeinitBarriers {
204
204
// Debug instructions that are no longer within this lifetime after shrinking.
205
205
SmallVector<SILInstruction *, 4 > deadUsers;
206
206
207
+ // The access scopes which are hoisting barriers.
208
+ //
209
+ // They are hoisting barriers if they include any barriers. We need to be
210
+ // sure not to hoist a destroy_addr into an access scope and by doing so cause
211
+ // a deinit which had previously executed outside an access scope to start
212
+ // executing within it--that could violate exclusivity.
213
+ llvm::SmallPtrSet<BeginAccessInst *, 8 > barrierAccessScopes;
214
+
207
215
explicit DeinitBarriers (bool ignoreDeinitBarriers,
208
216
const KnownStorageUses &knownUses,
209
217
SILFunction *function)
@@ -215,11 +223,15 @@ class DeinitBarriers {
215
223
storageDefInst = rootValue->getDefiningInstruction ();
216
224
}
217
225
218
- void compute () { DestroyReachability (*this ).solveBackward (); }
226
+ void compute () {
227
+ FindBarrierAccessScopes (*this ).solveBackward ();
228
+ DestroyReachability (*this ).solveBackward ();
229
+ }
219
230
220
231
bool isBarrier (SILInstruction *instruction) const {
221
- return classificationIsBarrier (classifyInstruction (
222
- instruction, ignoreDeinitBarriers, storageDefInst, knownUses));
232
+ return classificationIsBarrier (
233
+ classifyInstruction (instruction, ignoreDeinitBarriers, storageDefInst,
234
+ barrierAccessScopes, knownUses));
223
235
};
224
236
225
237
private:
@@ -234,20 +246,136 @@ class DeinitBarriers {
234
246
235
247
Classification classifyInstruction (SILInstruction *inst) {
236
248
return classifyInstruction (inst, ignoreDeinitBarriers, storageDefInst,
237
- knownUses);
249
+ barrierAccessScopes, knownUses);
238
250
}
239
251
240
- static Classification classifyInstruction (SILInstruction *inst,
241
- bool ignoreDeinitBarriers,
242
- SILInstruction *storageDefInst,
243
- const KnownStorageUses &knownUses);
252
+ static Classification classifyInstruction (
253
+ SILInstruction *inst, bool ignoreDeinitBarriers,
254
+ SILInstruction *storageDefInst,
255
+ const llvm::SmallPtrSetImpl<BeginAccessInst *> &barrierAccessScopes,
256
+ const KnownStorageUses &knownUses);
244
257
245
258
void visitedInstruction (SILInstruction *instruction,
246
259
Classification classification);
247
260
248
261
static bool classificationIsBarrier (Classification classification);
249
262
250
263
// Implements BackwardReachability::BlockReachability
264
+ //
265
+ // Determine which end_access instructions must be treated as barriers.
266
+ //
267
+ // An end_access is a barrier if the access scope it ends contains any deinit
268
+ // barriers. Suppose that it weren't treated as a barrier. Then the
269
+ // destroy_addr would be hoisted up to the in-scope deinit barrier. That
270
+ // could result in a deinit being executed within the scope which was
271
+ // previously executed outside it. Executing a deinit in the scope could
272
+ // violate exclusivity.
273
+ //
274
+ // So before determining what ALL the barriers are, we need to determine which
275
+ // end_access instructions are barriers. Do that by observing which access
276
+ // scopes are open when encountering a barrier. The access scopes which are
277
+ // open are those for which we've seen an end_access instruction when walking
278
+ // backwards from the destroy_addrs. Add these access scopes to
279
+ // DeinitBarriers::barrierAccessScopes.
280
+ //
281
+ // Tracking which access scopes are open consists of two parts:
282
+ // (1) in-block analysis
283
+ // (2) cross-block analysis
284
+ // For (1), maintain a set of access scopes which are currently open. Insert
285
+ // and erase scopes when seeing begin_access and end_access instructions when
286
+ // they're visited in checkReachableBarrier. A stack can't be used here
287
+ // because access scopes are not necessarily nested.
288
+ // For (2), when entering a block, the access scope is the union of all the
289
+ // open access scopes in the block's predecessors.
290
+ class FindBarrierAccessScopes {
291
+ DeinitBarriers &result;
292
+ BasicBlockSetVector destroyReachesBeginBlocks;
293
+ llvm::DenseMap<SILBasicBlock *, llvm::SmallPtrSet<BeginAccessInst *, 2 >>
294
+ liveInAccessScopes;
295
+ llvm::SmallPtrSet<BeginAccessInst *, 2 > runningLiveAccessScopes;
296
+
297
+ BackwardReachability<FindBarrierAccessScopes> reachability;
298
+
299
+ public:
300
+ FindBarrierAccessScopes (DeinitBarriers &result)
301
+ : result(result),
302
+ destroyReachesBeginBlocks (result.knownUses.getFunction()),
303
+ reachability(result.knownUses.getFunction(), *this) {
304
+ // Seed backward reachability with destroy points.
305
+ for (SILInstruction *destroy : result.knownUses .originalDestroys ) {
306
+ reachability.initLastUse (destroy);
307
+ }
308
+ }
309
+
310
+ void markLiveAccessScopesAsBarriers () {
311
+ for (auto *scope : runningLiveAccessScopes) {
312
+ result.barrierAccessScopes .insert (scope);
313
+ }
314
+ }
315
+
316
+ bool hasReachableBegin (SILBasicBlock *block) {
317
+ return destroyReachesBeginBlocks.contains (block);
318
+ }
319
+
320
+ void markReachableBegin (SILBasicBlock *block) {
321
+ destroyReachesBeginBlocks.insert (block);
322
+ if (!runningLiveAccessScopes.empty ()) {
323
+ liveInAccessScopes[block] = runningLiveAccessScopes;
324
+ }
325
+ }
326
+
327
+ void markReachableEnd (SILBasicBlock *block) {
328
+ runningLiveAccessScopes.clear ();
329
+ for (auto *predecessor : block->getPredecessorBlocks ()) {
330
+ auto iterator = liveInAccessScopes.find (predecessor);
331
+ if (iterator != liveInAccessScopes.end ()) {
332
+ for (auto *bai : iterator->getSecond ()) {
333
+ runningLiveAccessScopes.insert (bai);
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ bool checkReachableBarrier (SILInstruction *inst) {
340
+ // For correctness, it is required that
341
+ // FindBarrierAccessScopes::checkReachableBarrier return true whenever
342
+ // DestroyReachability::checkReachableBarrier does, with one exception:
343
+ // DestryReachability::checkReachableBarrier will also return true for any
344
+ // end_access barrier that FindBarrierAccessScopes finds.
345
+ if (auto *eai = dyn_cast<EndAccessInst>(inst)) {
346
+ runningLiveAccessScopes.insert (eai->getBeginAccess ());
347
+ } else if (auto *bai = dyn_cast<BeginAccessInst>(inst)) {
348
+ runningLiveAccessScopes.erase (bai);
349
+ }
350
+ bool isBarrier = result.isBarrier (inst);
351
+ if (isBarrier) {
352
+ markLiveAccessScopesAsBarriers ();
353
+ }
354
+ // If we've seen a barrier, then we can stop looking for access scopes.
355
+ // Any that were open already have now been marked as barriers. And if
356
+ // none are open, the second data flow won't get beyond this barrier to
357
+ // face subsequent end_access instructions.
358
+ return isBarrier;
359
+ }
360
+
361
+ bool checkReachablePhiBarrier (SILBasicBlock *block) {
362
+ bool isBarrier =
363
+ llvm::any_of (block->getPredecessorBlocks (), [&](auto *predecessor) {
364
+ return result.isBarrier (block->getTerminator ());
365
+ });
366
+ if (isBarrier) {
367
+ // If there's a barrier preventing us from hoisting out of this block,
368
+ // then every open access scope contains a barrier, so all the
369
+ // corresponding end_access instructions are barriers too.
370
+ markLiveAccessScopesAsBarriers ();
371
+ }
372
+ return isBarrier;
373
+ }
374
+
375
+ void solveBackward () { reachability.solveBackward (); }
376
+ };
377
+
378
+ // Conforms to BackwardReachability::BlockReachability
251
379
class DestroyReachability {
252
380
DeinitBarriers &result;
253
381
@@ -284,7 +412,9 @@ class DeinitBarriers {
284
412
285
413
DeinitBarriers::Classification DeinitBarriers::classifyInstruction (
286
414
SILInstruction *inst, bool ignoreDeinitBarriers,
287
- SILInstruction *storageDefInst, const KnownStorageUses &knownUses) {
415
+ SILInstruction *storageDefInst,
416
+ const llvm::SmallPtrSetImpl<BeginAccessInst *> &barrierAccessScopes,
417
+ const KnownStorageUses &knownUses) {
288
418
if (knownUses.debugInsts .contains (inst)) {
289
419
return Classification::DeadUser;
290
420
}
@@ -297,6 +427,11 @@ DeinitBarriers::Classification DeinitBarriers::classifyInstruction(
297
427
if (!ignoreDeinitBarriers && isDeinitBarrier (inst)) {
298
428
return Classification::Barrier;
299
429
}
430
+ if (auto *eai = dyn_cast<EndAccessInst>(inst)) {
431
+ return barrierAccessScopes.contains (eai->getBeginAccess ())
432
+ ? Classification::Barrier
433
+ : Classification::Other;
434
+ }
300
435
return Classification::Other;
301
436
}
302
437
@@ -334,6 +469,11 @@ void DeinitBarriers::visitedInstruction(SILInstruction *instruction,
334
469
// / which is a storageUser and therefore a barrier.
335
470
bool DeinitBarriers::DestroyReachability::checkReachableBarrier (
336
471
SILInstruction *instruction) {
472
+ // For correctness, it is required that
473
+ // DestroyReachability::checkReachableBarrier return true whenever
474
+ // FindBarrierAccessScopes::checkReachableBarrier does. It must additionally
475
+ // return true when encountering an end_access barrier that
476
+ // FindBarrierAccessScope determined is a barrier.
337
477
auto classification = result.classifyInstruction (instruction);
338
478
result.visitedInstruction (instruction, classification);
339
479
return result.classificationIsBarrier (classification);
0 commit comments