Skip to content

Commit 9a819f5

Browse files
committed
[ShrinkBorrowScope] Adopt VisitBarrierAccessScopes.
Avoids hoisting borrow scopes into unrelated access scopes which could introduce exclusivity violations. rdar://93060369
1 parent 4c23cd7 commit 9a819f5

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

lib/SILOptimizer/Utils/ShrinkBorrowScope.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/SIL/SILBasicBlock.h"
2222
#include "swift/SIL/SILInstruction.h"
2323
#include "swift/SILOptimizer/Analysis/Reachability.h"
24+
#include "swift/SILOptimizer/Analysis/VisitBarrierAccessScopes.h"
2425
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
2526
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2627
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
@@ -134,10 +135,13 @@ struct DeinitBarriers final {
134135
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
135136
};
136137

138+
class BarrierAccessScopeFinder;
139+
137140
/// Works backwards from the current location of end_borrows to the earliest
138141
/// place they can be hoisted to.
139142
///
140143
/// Implements IterativeBackwardReachability::Effects.
144+
/// Implements BarrierAccessScopeFinder::Effects
141145
class DataFlow final {
142146
public:
143147
using Reachability = IterativeBackwardReachability<DataFlow>;
@@ -166,6 +170,8 @@ class DataFlow final {
166170

167171
private:
168172
friend Reachability;
173+
friend class BarrierAccessScopeFinder;
174+
friend class VisitBarrierAccessScopes<DataFlow, BarrierAccessScopeFinder>;
169175

170176
Classification classifyInstruction(SILInstruction *);
171177

@@ -178,6 +184,33 @@ class DataFlow final {
178184
Optional<Effect> effectForPhi(SILBasicBlock *);
179185
};
180186

187+
/// Finds end_access instructions which are barriers to hoisting because the
188+
/// access scopes they contain barriers to hoisting. Hoisting end_borrows into
189+
/// such access scopes could introduce exclusivity violations.
190+
///
191+
/// Implements BarrierAccessScopeFinder::Visitor
192+
class BarrierAccessScopeFinder final {
193+
using Impl = VisitBarrierAccessScopes<DataFlow, BarrierAccessScopeFinder>;
194+
Context const &context;
195+
Impl impl;
196+
DataFlow &dataflow;
197+
Optional<SmallVector<SILBasicBlock *, 16>> cachedRoots;
198+
199+
public:
200+
BarrierAccessScopeFinder(Context const &context, DataFlow &dataflow)
201+
: context(context), impl(&context.function, dataflow, *this),
202+
dataflow(dataflow) {}
203+
204+
void find();
205+
206+
private:
207+
friend Impl;
208+
209+
ArrayRef<SILBasicBlock *> roots();
210+
bool isInRegion(SILBasicBlock *);
211+
void visitBarrierAccessScope(BeginAccessInst *);
212+
};
213+
181214
/// Whether the specified value is %lifetime or its iterated copy_value.
182215
///
183216
/// In other words, it has to be a simple extended def of %lifetime.
@@ -198,6 +231,8 @@ bool isSimpleExtendedIntroducerDef(Context const &context, SILValue value) {
198231

199232
void DataFlow::run() {
200233
reachability.initialize();
234+
BarrierAccessScopeFinder finder(context, *this);
235+
finder.find();
201236
reachability.solve();
202237
recordCopies = true;
203238
reachability.findBarriers(barriers.instructions, barriers.phis,
@@ -217,6 +252,11 @@ DataFlow::classifyInstruction(SILInstruction *instruction) {
217252
if (uses.users.contains(instruction)) {
218253
return Classification::Barrier;
219254
}
255+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
256+
return barrierAccessScopes.contains(eai->getBeginAccess())
257+
? Classification::Barrier
258+
: Classification::Other;
259+
}
220260
if (isDeinitBarrier(instruction)) {
221261
return Classification::Barrier;
222262
}
@@ -260,6 +300,33 @@ Optional<DataFlow::Effect> DataFlow::effectForPhi(SILBasicBlock *block) {
260300
return isBarrier ? Optional<Effect>{Effect::Kill} : llvm::None;
261301
}
262302

303+
void BarrierAccessScopeFinder::find() { impl.visit(); }
304+
305+
ArrayRef<SILBasicBlock *> BarrierAccessScopeFinder::roots() {
306+
if (cachedRoots)
307+
return *cachedRoots;
308+
309+
cachedRoots = SmallVector<SILBasicBlock *, 16>{};
310+
BasicBlockSet seenRoots(&context.function);
311+
for (auto *gen : dataflow.gens()) {
312+
seenRoots.insert(gen->getParent());
313+
cachedRoots->push_back(gen->getParent());
314+
}
315+
316+
return *cachedRoots;
317+
}
318+
319+
bool BarrierAccessScopeFinder::isInRegion(SILBasicBlock *block) {
320+
return dataflow.result.discoveredBlocks.contains(block);
321+
}
322+
323+
void BarrierAccessScopeFinder::visitBarrierAccessScope(BeginAccessInst *bai) {
324+
dataflow.barrierAccessScopes.insert(bai);
325+
for (auto *eai : bai->getEndAccesses()) {
326+
dataflow.reachability.addKill(eai);
327+
}
328+
}
329+
263330
/// Hoist the scope ends of %lifetime, rewriting copies and borrows along the
264331
/// way.
265332
class Rewriter final {

test/SILOptimizer/shrink_borrow_scope.sil

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,3 +1097,76 @@ bb0(%0 : $*ClassWrapper):
10971097
// =============================================================================
10981098
// instruction tests }}
10991099
// =============================================================================
1100+
1101+
// =============================================================================
1102+
// access scope tests {{
1103+
// =============================================================================
1104+
1105+
// Don't hoist into an access scope that contains a barrier.
1106+
//
1107+
// CHECK-LABEL: sil [ossa] @nofold_scoped_load_barrier : {{.*}} {
1108+
// CHECK: end_access
1109+
// CHECK: end_access
1110+
// CHECK: end_borrow
1111+
// CHECK-LABEL: // end sil function 'nofold_scoped_load_barrier'
1112+
sil [ossa] @nofold_scoped_load_barrier : $@convention(thin) (@owned C, @owned C) -> (@owned C) {
1113+
entry(%instance : @owned $C, %other : @owned $C):
1114+
%lifetime = begin_borrow [lexical] %instance : $C
1115+
apply undef(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
1116+
%addr = alloc_stack $C
1117+
%store_scope = begin_access [modify] [static] %addr : $*C
1118+
store %other to [init] %store_scope : $*C
1119+
end_access %store_scope : $*C
1120+
%load_scope = begin_access [read] [static] %addr : $*C
1121+
%value = load [copy] %load_scope : $*C
1122+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
1123+
apply %barrier() : $@convention(thin) () -> ()
1124+
end_access %load_scope : $*C
1125+
destroy_addr %addr : $*C
1126+
dealloc_stack %addr : $*C
1127+
end_borrow %lifetime : $C
1128+
destroy_value %instance : $C
1129+
return %value : $C
1130+
}
1131+
1132+
// Access scopes that are open at barrier blocks are barriers. Otherwise, we
1133+
// would hoist end_borrows into the scopes when the end_borrows are hoisted up
1134+
// to the begin of blocks whose predecessor is the barrier block.
1135+
//
1136+
// CHECK-LABEL: sil [ossa] @nohoist_into_access_scope_barred_by_barrier_block : {{.*}} {
1137+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C, [[INOUT:%[^,]+]] : $*C):
1138+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]]
1139+
// CHECK: [[SCOPE:%[^,]+]] = begin_access [modify] [static] [[INOUT]] : $*C
1140+
// CHECK: cond_br undef, [[LEFT:bb[0-9]+]],
1141+
// CHECK: [[LEFT]]:
1142+
// CHECK: end_access [[SCOPE]] : $*C
1143+
// CHECK-NEXT: end_borrow [[LIFETIME]] : $C
1144+
// CHECK-LABEL: } // end sil function 'nohoist_into_access_scope_barred_by_barrier_block'
1145+
sil [ossa] @nohoist_into_access_scope_barred_by_barrier_block : $@convention(thin) (@owned C, @inout C) -> () {
1146+
entry(%instance : @owned $C, %second : $*C):
1147+
%lifetime = begin_borrow [lexical] %instance : $C
1148+
%scope = begin_access [modify] [static] %second : $*C
1149+
cond_br undef, left, right
1150+
1151+
left:
1152+
end_access %scope : $*C
1153+
%ignore = tuple ()
1154+
end_borrow %lifetime : $C
1155+
destroy_value %instance : $C
1156+
br exit
1157+
1158+
right:
1159+
end_access %scope : $*C
1160+
apply undef(%lifetime) : $@convention(thin) (@guaranteed C) -> ()
1161+
end_borrow %lifetime : $C
1162+
destroy_value %instance : $C
1163+
br exit
1164+
1165+
exit:
1166+
%retval = tuple ()
1167+
return %retval : $()
1168+
}
1169+
1170+
// =============================================================================
1171+
// access scope tests }}
1172+
// =============================================================================

0 commit comments

Comments
 (0)