Skip to content

Commit 1e0ac12

Browse files
committed
[LexicalDestroyHoisting] Adopt VisitBarrierAccessScopes.
Avoids hoisting destroy_values into unrelated access scopes which could introduce exclusivity violations.
1 parent 13b3e67 commit 1e0ac12

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

lib/SILOptimizer/Utils/LexicalDestroyHoisting.cpp

Lines changed: 54 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"
@@ -112,11 +113,14 @@ struct DeinitBarriers final {
112113
DeinitBarriers &operator=(DeinitBarriers const &) = delete;
113114
};
114115

116+
class BarrierAccessScopeFinder;
117+
115118
/// Works backwards from the current location of destroy_values to the earliest
116119
/// place they can be hoisted to.
117120
///
118121
/// Implements IterativeBackwardReachability::Effects
119122
/// Implements IterativeBackwardReachability::bindBarriers::Visitor
123+
/// Implements VisitBarrierAccessScopes::Effects
120124
class Dataflow final {
121125
using Reachability = IterativeBackwardReachability<Dataflow>;
122126
using Effect = Reachability::Effect;
@@ -125,6 +129,7 @@ class Dataflow final {
125129
DeinitBarriers &barriers;
126130
Reachability::Result result;
127131
Reachability reachability;
132+
SmallPtrSet<BeginAccessInst *, 8> barrierAccessScopes;
128133

129134
enum class Classification { Barrier, Other };
130135

@@ -140,18 +145,29 @@ class Dataflow final {
140145

141146
private:
142147
friend Reachability;
148+
friend class BarrierAccessScopeFinder;
149+
friend class VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
143150

144151
Classification classifyInstruction(SILInstruction *);
145152

146153
bool classificationIsBarrier(Classification);
147154

148155
/// IterativeBackwardReachability::Effects
156+
/// VisitBarrierAccessScopes::Effects
149157

150158
ArrayRef<SILInstruction *> gens() { return uses.ends; }
151159

152160
Effect effectForInstruction(SILInstruction *);
153161
Effect effectForPhi(SILBasicBlock *);
154162

163+
/// VisitBarrierAccessScopes::Effects
164+
165+
auto localGens() { return result.localGens; }
166+
167+
bool isLocalGen(SILInstruction *instruction) {
168+
return result.localGens.contains(instruction);
169+
}
170+
155171
/// IterativeBackwardReachability::bindBarriers::Visitor
156172

157173
void visitBarrierInstruction(SILInstruction *instruction) {
@@ -173,6 +189,11 @@ Dataflow::classifyInstruction(SILInstruction *instruction) {
173189
if (uses.users.contains(instruction)) {
174190
return Classification::Barrier;
175191
}
192+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
193+
return barrierAccessScopes.contains(eai->getBeginAccess())
194+
? Classification::Barrier
195+
: Classification::Other;
196+
}
176197
if (isDeinitBarrier(instruction)) {
177198
return Classification::Barrier;
178199
}
@@ -209,8 +230,41 @@ Dataflow::Effect Dataflow::effectForPhi(SILBasicBlock *block) {
209230
return isBarrier ? Effect::Kill() : Effect::NoEffect();
210231
}
211232

233+
/// Finds end_access instructions which are barriers to hoisting because the
234+
/// access scopes they contain barriers to hoisting. Hoisting destroy_values
235+
/// into such access scopes could introduce exclusivity violations.
236+
///
237+
/// Implements BarrierAccessScopeFinder::Visitor
238+
class BarrierAccessScopeFinder final {
239+
using Impl = VisitBarrierAccessScopes<Dataflow, BarrierAccessScopeFinder>;
240+
Impl impl;
241+
Dataflow &dataflow;
242+
243+
public:
244+
BarrierAccessScopeFinder(Context const &context, Dataflow &dataflow)
245+
: impl(&context.function, dataflow, *this), dataflow(dataflow) {}
246+
247+
void find() { impl.visit(); }
248+
249+
private:
250+
friend Impl;
251+
252+
bool isInRegion(SILBasicBlock *block) {
253+
return dataflow.result.discoveredBlocks.contains(block);
254+
}
255+
256+
void visitBarrierAccessScope(BeginAccessInst *bai) {
257+
dataflow.barrierAccessScopes.insert(bai);
258+
for (auto *eai : bai->getEndAccesses()) {
259+
dataflow.reachability.addKill(eai);
260+
}
261+
}
262+
};
263+
212264
void Dataflow::run() {
213265
reachability.initialize();
266+
BarrierAccessScopeFinder finder(context, *this);
267+
finder.find();
214268
reachability.solve();
215269
reachability.findBarriers(*this);
216270
}

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)