Skip to content

Commit eff9294

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

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,11 +135,14 @@ 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.
141144
/// Implements IterativeBackwardReachability::findBarrier::Visitor.
145+
/// Implements BarrierAccessScopeFinder::Effects
142146
class Dataflow final {
143147
public:
144148
using Reachability = IterativeBackwardReachability<Dataflow>;
@@ -167,6 +171,8 @@ class Dataflow final {
167171

168172
private:
169173
friend Reachability;
174+
friend class BarrierAccessScopeFinder;
175+
friend class VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
170176

171177
Classification classifyInstruction(SILInstruction *);
172178

@@ -181,6 +187,33 @@ class Dataflow final {
181187
void visitBarrierBlock(SILBasicBlock *);
182188
};
183189

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

202235
void Dataflow::run() {
203236
reachability.initialize();
237+
BarrierAccessScopeFinder finder(context, *this);
238+
finder.find();
204239
reachability.solve();
205240
recordCopies = true;
206241
reachability.findBarriers(*this);
@@ -219,6 +254,11 @@ Dataflow::classifyInstruction(SILInstruction *instruction) {
219254
if (uses.users.contains(instruction)) {
220255
return Classification::Barrier;
221256
}
257+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
258+
return barrierAccessScopes.contains(eai->getBeginAccess())
259+
? Classification::Barrier
260+
: Classification::Other;
261+
}
222262
if (isDeinitBarrier(instruction)) {
223263
return Classification::Barrier;
224264
}
@@ -272,6 +312,33 @@ void Dataflow::visitBarrierBlock(SILBasicBlock *block) {
272312
barriers.blocks.push_back(block);
273313
}
274314

315+
void BarrierAccessScopeFinder::find() { impl.visit(); }
316+
317+
ArrayRef<SILBasicBlock *> BarrierAccessScopeFinder::roots() {
318+
if (cachedRoots)
319+
return *cachedRoots;
320+
321+
cachedRoots = SmallVector<SILBasicBlock *, 16>{};
322+
BasicBlockSet seenRoots(&context.function);
323+
for (auto *gen : dataflow.gens()) {
324+
seenRoots.insert(gen->getParent());
325+
cachedRoots->push_back(gen->getParent());
326+
}
327+
328+
return *cachedRoots;
329+
}
330+
331+
bool BarrierAccessScopeFinder::isInRegion(SILBasicBlock *block) {
332+
return dataflow.result.discoveredBlocks.contains(block);
333+
}
334+
335+
void BarrierAccessScopeFinder::visitBarrierAccessScope(BeginAccessInst *bai) {
336+
dataflow.barrierAccessScopes.insert(bai);
337+
for (auto *eai : bai->getEndAccesses()) {
338+
dataflow.reachability.addKill(eai);
339+
}
340+
}
341+
275342
/// Hoist the scope ends of %lifetime, rewriting copies and borrows along the
276343
/// way.
277344
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)