Skip to content

Commit a298594

Browse files
committed
[LexicalDestroyFolding] Adopt VisitBarrierAccessScopes.
Avoids hoisting destroy_values into unrelated access scopes which could introduce exclusivity violations.
1 parent f1540b1 commit a298594

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

lib/SILOptimizer/Utils/LexicalDestroyHoisting.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
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"
@@ -111,10 +112,13 @@ struct DeinitBarriers final {
111112
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
112113
};
113114

115+
class BarrierAccessScopeFinder;
116+
114117
/// Works backwards from the current location of destroy_values to the earliest
115118
/// place they can be hoisted to.
116119
///
117120
/// Implements IterativeBackwardReachability::Effects
121+
/// Implements BarrierAccessScopeFinder::Effects
118122
class DataFlow final {
119123
using Reachability = IterativeBackwardReachability<DataFlow>;
120124
using Effect = Reachability::Effect;
@@ -123,6 +127,7 @@ class DataFlow final {
123127
DeinitBarriers &barriers;
124128
Reachability::Result result;
125129
Reachability reachability;
130+
SmallPtrSet<BeginAccessInst *, 8> barrierAccessScopes;
126131

127132
enum class Classification { Barrier, Other };
128133

@@ -138,6 +143,8 @@ class DataFlow final {
138143

139144
private:
140145
friend Reachability;
146+
friend class BarrierAccessScopeFinder;
147+
friend class VisitBarrierAccessScopes<DataFlow, BarrierAccessScopeFinder>;
141148

142149
Classification classifyInstruction(SILInstruction *);
143150

@@ -150,8 +157,37 @@ class DataFlow final {
150157
Optional<Effect> effectForPhi(SILBasicBlock *);
151158
};
152159

160+
/// Finds end_access instructions which are barriers to hoisting because the
161+
/// access scopes they contain barriers to hoisting. Hoisting destroy_values
162+
/// into such access scopes could introduce exclusivity violations.
163+
///
164+
/// Implements BarrierAccessScopeFinder::Visitor
165+
class BarrierAccessScopeFinder final {
166+
using Impl = VisitBarrierAccessScopes<DataFlow, BarrierAccessScopeFinder>;
167+
Context const &context;
168+
Impl impl;
169+
DataFlow &dataflow;
170+
Optional<SmallVector<SILBasicBlock *, 16>> cachedRoots;
171+
172+
public:
173+
BarrierAccessScopeFinder(Context const &context, DataFlow &dataflow)
174+
: context(context), impl(&context.function, dataflow, *this),
175+
dataflow(dataflow) {}
176+
177+
void find();
178+
179+
private:
180+
friend Impl;
181+
182+
ArrayRef<SILBasicBlock *> roots();
183+
bool isInRegion(SILBasicBlock *);
184+
void visitBarrierAccessScope(BeginAccessInst *);
185+
};
186+
153187
void DataFlow::run() {
154188
reachability.initialize();
189+
BarrierAccessScopeFinder finder(context, *this);
190+
finder.find();
155191
reachability.solve();
156192
reachability.findBarriers(barriers.instructions, barriers.phis,
157193
barriers.blocks);
@@ -165,6 +201,11 @@ DataFlow::classifyInstruction(SILInstruction *instruction) {
165201
if (uses.users.contains(instruction)) {
166202
return Classification::Barrier;
167203
}
204+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
205+
return barrierAccessScopes.contains(eai->getBeginAccess())
206+
? Classification::Barrier
207+
: Classification::Other;
208+
}
168209
if (isDeinitBarrier(instruction)) {
169210
return Classification::Barrier;
170211
}
@@ -204,6 +245,33 @@ Optional<DataFlow::Effect> DataFlow::effectForPhi(SILBasicBlock *block) {
204245
return isBarrier ? Optional<Effect>{Effect::Kill} : llvm::None;
205246
}
206247

248+
void BarrierAccessScopeFinder::find() { impl.visit(); }
249+
250+
ArrayRef<SILBasicBlock *> BarrierAccessScopeFinder::roots() {
251+
if (cachedRoots)
252+
return *cachedRoots;
253+
254+
cachedRoots = SmallVector<SILBasicBlock *, 16>{};
255+
BasicBlockSet seenRoots(&context.function);
256+
for (auto *gen : dataflow.gens()) {
257+
seenRoots.insert(gen->getParent());
258+
cachedRoots->push_back(gen->getParent());
259+
}
260+
261+
return *cachedRoots;
262+
}
263+
264+
bool BarrierAccessScopeFinder::isInRegion(SILBasicBlock *block) {
265+
return dataflow.result.discoveredBlocks.contains(block);
266+
}
267+
268+
void BarrierAccessScopeFinder::visitBarrierAccessScope(BeginAccessInst *bai) {
269+
dataflow.barrierAccessScopes.insert(bai);
270+
for (auto *eai : bai->getEndAccesses()) {
271+
dataflow.reachability.addKill(eai);
272+
}
273+
}
274+
207275
/// Hoist the destroy_values of %value.
208276
class Rewriter final {
209277
Context &context;

test/SILOptimizer/lexical_destroy_hoisting.sil

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,3 +460,67 @@ entry(%instance : @owned $C, %input : $S):
460460
// instruction tests }}
461461
// =============================================================================
462462

463+
// =============================================================================
464+
// access scope tests {{
465+
// =============================================================================
466+
467+
// Don't hoist into an access scope that contains a barrier.
468+
//
469+
// CHECK-LABEL: sil [ossa] @nofold_scoped_load_barrier : {{.*}} {
470+
// CHECK: end_access
471+
// CHECK: end_access
472+
// CHECK: destroy_value
473+
// CHECK-LABEL: // end sil function 'nofold_scoped_load_barrier'
474+
sil [ossa] @nofold_scoped_load_barrier : $@convention(thin) (@owned C, @owned C) -> (@owned C) {
475+
entry(%instance : @owned $C, %other : @owned $C):
476+
%addr = alloc_stack $C
477+
%store_scope = begin_access [modify] [static] %addr : $*C
478+
store %other to [init] %store_scope : $*C
479+
end_access %store_scope : $*C
480+
%load_scope = begin_access [read] [static] %addr : $*C
481+
%value = load [copy] %load_scope : $*C
482+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
483+
apply %barrier() : $@convention(thin) () -> ()
484+
end_access %load_scope : $*C
485+
destroy_addr %addr : $*C
486+
dealloc_stack %addr : $*C
487+
destroy_value %instance : $C
488+
return %value : $C
489+
}
490+
491+
// Access scopes that are open at barrier blocks are barriers. Otherwise, we
492+
// would hoist destroy_values into the scopes when the destroy_values are
493+
// hoisted up to the begin of blocks whose predecessor is the barrier block.
494+
//
495+
// CHECK-LABEL: sil [ossa] @nohoist_into_access_scope_barred_by_barrier_block : {{.*}} {
496+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C, [[INOUT:%[^,]+]] : $*C):
497+
// CHECK: [[SCOPE:%[^,]+]] = begin_access [modify] [static] [[INOUT]] : $*C
498+
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]],
499+
// CHECK: [[LEFT]]:
500+
// CHECK: end_access [[SCOPE]] : $*C
501+
// CHECK-NEXT: destroy_value [[INSTANCE]] : $C
502+
// CHECK-LABEL: } // end sil function 'nohoist_into_access_scope_barred_by_barrier_block'
503+
sil [ossa] @nohoist_into_access_scope_barred_by_barrier_block : $@convention(thin) (@owned C, @inout C) -> () {
504+
entry(%instance : @owned $C, %second : $*C):
505+
%scope = begin_access [modify] [static] %second : $*C
506+
cond_br undef, left, right
507+
508+
left:
509+
end_access %scope : $*C
510+
%ignore = tuple ()
511+
destroy_value %instance : $C
512+
br exit
513+
514+
right:
515+
end_access %scope : $*C
516+
apply undef(%instance) : $@convention(thin) (@owned C) -> ()
517+
br exit
518+
519+
exit:
520+
%retval = tuple ()
521+
return %retval : $()
522+
}
523+
524+
// =============================================================================
525+
// access scope tests }}
526+
// =============================================================================

0 commit comments

Comments
 (0)