Skip to content

Commit de5a224

Browse files
committed
[SILOpt] Added VisitBarrierAccessScopes utility.
The new utility finds access scopes which are barriers by finding access scopes which themselves contain barriers. This is necessary to (1) allow hoisting through access scopes when possible (i.e. not simply treating all end_access instructions as barriers) and (2) not hoist into access scopes that contain barriers and in so doing introduce exclusivity violations.
1 parent 7c806c9 commit de5a224

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
//===--- VisitBarrierAccessScopes.h - Find access scopes with barriers ----===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Given a region of a function, backwards-reachable from some set of roots, on
14+
// which gen/kill effects can be determined, determines which access scopes must
15+
// also be treated as kills in view of the rule that a kill within an access
16+
// scope makes the access scope itself a kill.
17+
//
18+
//===----------------------------------------------------------------------===//
19+
20+
#include "swift/SIL/BasicBlockDatastructures.h"
21+
#include "swift/SIL/BasicBlockUtils.h"
22+
#include "llvm/ADT/DenseMap.h"
23+
#include "llvm/ADT/Optional.h"
24+
#include "llvm/ADT/SmallPtrSet.h"
25+
26+
namespace swift {
27+
28+
class BeginAccessInst;
29+
class EndAccessInst;
30+
class SILInstruction;
31+
class SILBasicBlock;
32+
class SILInstruction;
33+
34+
/// Visits the begin_access instructions that must be regarded as barriers
35+
/// because they contain barriers.
36+
///
37+
/// interface Effects {
38+
/// /// The effect, if any, of the specified instruction.
39+
/// Optional<Effects::Effect> effectForPhi(SILBasicBlock *)
40+
///
41+
/// /// The effect, if any, of the phis of the specified block.
42+
/// Optional<Effects::Effect> effectForInstruction(SILInstruction *)
43+
/// }
44+
/// interface Visitor {
45+
/// /// Whether the indicated basic block is within the region of the graph
46+
/// /// that should be traversed.
47+
/// bool isInRegion(SILBasicBlock *block)
48+
///
49+
/// /// The roots from which all blocks in the region are backwards-reachable.
50+
/// ArrayRef<SILBasicBlock *> roots();
51+
///
52+
/// /// Visit each discovered begin_access instruction which defines a barrier
53+
/// /// scope.
54+
/// void visitBarrierAccessScope(BeginAccessInst *)
55+
/// }
56+
template <typename Effects, typename Visitor>
57+
class VisitBarrierAccessScopes {
58+
using This = VisitBarrierAccessScopes<Effects, Visitor>;
59+
/// Describes the effect, if any, of each instruction and phi.
60+
Effects &effects;
61+
/// Describes the search region and visits the found barriers.
62+
Visitor &visitor;
63+
/// The function in which to find barrier access scopes.
64+
SILFunction *function;
65+
/// The blocks which have already been visited. Used to determine whether a
66+
/// block being visited is a barrier block.
67+
BasicBlockSet visited;
68+
69+
/// The access scopes that are live at the begin of each block after visiting
70+
/// all the block's predecessors and its instructions.
71+
llvm::DenseMap<SILBasicBlock *, llvm::SmallPtrSet<BeginAccessInst *, 2>>
72+
liveInAccessScopes;
73+
/// While visiting a block's instructions, the access scopes that are
74+
/// currently live.
75+
llvm::SmallPtrSet<BeginAccessInst *, 2> runningLiveAccessScopes;
76+
/// The blocks in the region, in topological order.
77+
SmallVector<SILBasicBlock *, 32> order;
78+
79+
public:
80+
VisitBarrierAccessScopes(SILFunction *function, Effects &effects,
81+
Visitor &visitor)
82+
: effects(effects), visitor(visitor), function(function),
83+
visited(function){};
84+
85+
/// Visit the begin_access instructions.
86+
///
87+
/// Sort the backwards-reachable, in-region blocks topologically. Then visit
88+
/// them in the reverse of that order, so that when any block is visited, all
89+
/// of its non-backedge* successors will already have been visited.
90+
///
91+
/// While visiting, which access scopes are live within blocks is tracked,
92+
/// storing the access scopes that are live at the begin of a block, and
93+
/// noting that every access scope live at the begin of any successor is also
94+
/// live at the end of a block.
95+
///
96+
/// * These are backedges in the reversed graph, that are ignored in a reverse
97+
/// depth-first search.
98+
void visit() {
99+
visitSortedTopologicallyBackwards(function, visitor.roots(), *this);
100+
for (auto *block : llvm::reverse(order)) {
101+
visitBlock(block);
102+
}
103+
}
104+
105+
/// visitSortedTopologicallyBackwards::Visitor
106+
107+
/// Whether the block is in the region of interest. Just a passhthrough to
108+
/// our visitor.
109+
bool isInRegion(SILBasicBlock *block) { return visitor.isInRegion(block); }
110+
111+
/// Called for each block in the region in topological order. Record the
112+
/// order into \p order so that we can visit the blocks in the reverse of that
113+
/// order.
114+
void visit(SILBasicBlock *block) { order.push_back(block); }
115+
116+
private:
117+
/// Block visitation
118+
119+
void visitBlock(SILBasicBlock *block) {
120+
visitBlockEnd(block);
121+
for (auto &instruction : llvm::reverse(*block)) {
122+
visitInstruction(&instruction);
123+
}
124+
if (block->hasPhi())
125+
visitPhi(block);
126+
visitBlockBegin(block);
127+
}
128+
129+
void visitInstruction(SILInstruction *instruction) {
130+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
131+
runningLiveAccessScopes.insert(eai->getBeginAccess());
132+
} else if (auto *bai = dyn_cast<BeginAccessInst>(instruction)) {
133+
runningLiveAccessScopes.erase(bai);
134+
}
135+
handleEffect(effects.effectForInstruction(instruction));
136+
}
137+
138+
void visitPhi(SILBasicBlock *block) {
139+
handleEffect(effects.effectForPhi(block));
140+
}
141+
142+
void visitBlockBegin(SILBasicBlock *block) {
143+
if (!runningLiveAccessScopes.empty()) {
144+
liveInAccessScopes[block] = runningLiveAccessScopes;
145+
}
146+
}
147+
148+
void visitBlockEnd(SILBasicBlock *block) {
149+
visited.insert(block);
150+
runningLiveAccessScopes.clear();
151+
for (auto *successor : block->getSuccessorBlocks()) {
152+
auto iterator = liveInAccessScopes.find(successor);
153+
if (iterator != liveInAccessScopes.end()) {
154+
for (auto *bai : iterator->getSecond()) {
155+
runningLiveAccessScopes.insert(bai);
156+
}
157+
}
158+
}
159+
// If any of this block's predecessors haven't already been visited, it
160+
// means that they aren't in the region and consequently this block is a
161+
// barrier block.
162+
if (llvm::any_of(block->getSuccessorBlocks(), [&](SILBasicBlock *block) {
163+
return !visited.contains(block);
164+
}))
165+
handleBarrier();
166+
}
167+
168+
/// Effect procecessing
169+
170+
void handleEffect(typename Effects::Effect effect) {
171+
switch (effect.value) {
172+
case Effects::Effect::Value::NoEffect:
173+
return;
174+
case Effects::Effect::Value::Gen:
175+
runningLiveAccessScopes.clear();
176+
return;
177+
case Effects::Effect::Value::Kill:
178+
handleBarrier();
179+
return;
180+
}
181+
}
182+
183+
void handleBarrier() {
184+
for (auto *scope : runningLiveAccessScopes) {
185+
visitor.visitBarrierAccessScope(scope);
186+
}
187+
}
188+
};
189+
190+
} // end namespace swift

0 commit comments

Comments
 (0)