@@ -204,6 +204,14 @@ class DeinitBarriers {
204204 // Debug instructions that are no longer within this lifetime after shrinking.
205205 SmallVector<SILInstruction *, 4 > deadUsers;
206206
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+
207215 explicit DeinitBarriers (bool ignoreDeinitBarriers,
208216 const KnownStorageUses &knownUses,
209217 SILFunction *function)
@@ -215,11 +223,15 @@ class DeinitBarriers {
215223 storageDefInst = rootValue->getDefiningInstruction ();
216224 }
217225
218- void compute () { DestroyReachability (*this ).solveBackward (); }
226+ void compute () {
227+ FindBarrierAccessScopes (*this ).solveBackward ();
228+ DestroyReachability (*this ).solveBackward ();
229+ }
219230
220231 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));
223235 };
224236
225237private:
@@ -234,20 +246,136 @@ class DeinitBarriers {
234246
235247 Classification classifyInstruction (SILInstruction *inst) {
236248 return classifyInstruction (inst, ignoreDeinitBarriers, storageDefInst,
237- knownUses);
249+ barrierAccessScopes, knownUses);
238250 }
239251
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);
244257
245258 void visitedInstruction (SILInstruction *instruction,
246259 Classification classification);
247260
248261 static bool classificationIsBarrier (Classification classification);
249262
250263 // 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
251379 class DestroyReachability {
252380 DeinitBarriers &result;
253381
@@ -284,7 +412,9 @@ class DeinitBarriers {
284412
285413DeinitBarriers::Classification DeinitBarriers::classifyInstruction (
286414 SILInstruction *inst, bool ignoreDeinitBarriers,
287- SILInstruction *storageDefInst, const KnownStorageUses &knownUses) {
415+ SILInstruction *storageDefInst,
416+ const llvm::SmallPtrSetImpl<BeginAccessInst *> &barrierAccessScopes,
417+ const KnownStorageUses &knownUses) {
288418 if (knownUses.debugInsts .contains (inst)) {
289419 return Classification::DeadUser;
290420 }
@@ -297,6 +427,11 @@ DeinitBarriers::Classification DeinitBarriers::classifyInstruction(
297427 if (!ignoreDeinitBarriers && isDeinitBarrier (inst)) {
298428 return Classification::Barrier;
299429 }
430+ if (auto *eai = dyn_cast<EndAccessInst>(inst)) {
431+ return barrierAccessScopes.contains (eai->getBeginAccess ())
432+ ? Classification::Barrier
433+ : Classification::Other;
434+ }
300435 return Classification::Other;
301436}
302437
@@ -334,6 +469,11 @@ void DeinitBarriers::visitedInstruction(SILInstruction *instruction,
334469// / which is a storageUser and therefore a barrier.
335470bool DeinitBarriers::DestroyReachability::checkReachableBarrier (
336471 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.
337477 auto classification = result.classifyInstruction (instruction);
338478 result.visitedInstruction (instruction, classification);
339479 return result.classificationIsBarrier (classification);
0 commit comments