Skip to content

Commit 6f8b62c

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 f5ad96c commit 6f8b62c

File tree

1 file changed

+300
-0
lines changed

1 file changed

+300
-0
lines changed
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
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/STLExtras.h"
25+
#include "llvm/ADT/SmallPtrSet.h"
26+
27+
namespace swift {
28+
29+
class BeginAccessInst;
30+
class EndAccessInst;
31+
class SILInstruction;
32+
class SILBasicBlock;
33+
class SILInstruction;
34+
35+
/// Visits the begin_access instructions corresponding to access scopes that
36+
/// must be regarded as barriers (in particular, their end_access instructions
37+
/// must be) because they contain other barriers.
38+
///
39+
/// interface Effects {
40+
/// typename Effect
41+
///
42+
/// /// The root instructions from which to walk.
43+
/// iterable gens()
44+
///
45+
/// /// The gens which are killed within the block where they occur.
46+
/// iterable localGens()
47+
///
48+
/// /// Whether the indicated instruction is killed within the specified
49+
/// /// block.
50+
/// bool isLocalGen(SILInstruction *)
51+
///
52+
/// /// The effect, if any, of the specified instruction.
53+
/// Effects::Effect effectForPhi(SILBasicBlock *)
54+
///
55+
/// /// The effect, if any, of the phis of the specified block.
56+
/// Effects::Effect effectForInstruction(SILInstruction *)
57+
/// }
58+
/// interface Visitor {
59+
/// /// Whether the indicated basic block is within the region of the graph
60+
/// /// that should be traversed.
61+
/// bool isInRegion(SILBasicBlock *)
62+
///
63+
/// /// Visit each discovered begin_access instruction which defines a barrier
64+
/// /// scope.
65+
/// void visitBarrierAccessScope(BeginAccessInst *)
66+
/// }
67+
template <typename Effects, typename Visitor>
68+
class VisitBarrierAccessScopes {
69+
using This = VisitBarrierAccessScopes<Effects, Visitor>;
70+
/// Describes the effect, if any, of each instruction and phi.
71+
Effects &effects;
72+
/// Describes the search region and visits the found barriers.
73+
Visitor &visitor;
74+
/// The function in which to find barrier access scopes.
75+
SILFunction *function;
76+
/// The blocks which have already been visited. Used to determine whether a
77+
/// block being visited is a barrier block.
78+
BasicBlockSet visited;
79+
80+
/// The access scopes that are live at the begin of each block after visiting
81+
/// all the block's predecessors and its instructions.
82+
llvm::DenseMap<SILBasicBlock *, llvm::SmallPtrSet<BeginAccessInst *, 2>>
83+
liveInAccessScopes;
84+
/// While visiting a block's instructions, the access scopes that are
85+
/// currently live.
86+
llvm::SmallPtrSet<BeginAccessInst *, 2> runningLiveAccessScopes;
87+
88+
public:
89+
VisitBarrierAccessScopes(SILFunction *function, Effects &effects,
90+
Visitor &visitor)
91+
: effects(effects), visitor(visitor), function(function),
92+
visited(function){};
93+
94+
/// Visit the begin_access instructions.
95+
///
96+
/// Sort the backwards-reachable, in-region blocks topologically. Then visit
97+
/// them in the reverse of that order, so that when any block is visited, all
98+
/// of its non-backedge* successors will already have been visited.
99+
///
100+
/// While visiting, which access scopes are live within blocks is tracked,
101+
/// storing the access scopes that are live at the begin of a block, and
102+
/// noting that every access scope live at the begin of any successor is also
103+
/// live at the end of a block.
104+
///
105+
/// * These are backedges in the reversed graph, that are ignored in a reverse
106+
/// depth-first search.
107+
///
108+
/// Finally, look through the instructions between local gens and their kills
109+
/// for further barrier access scopes.
110+
void visit() {
111+
// First, collect the gens to use as the roots of the region--those that
112+
// are non-local.
113+
//
114+
// Keep track of which gens are at the beginning of the region (i.e. none of
115+
// whose successors are in the region) so that we can avoid iterating over
116+
// the instructions below those gens.
117+
SmallVector<SILBasicBlock *, 32> rootBlocks;
118+
llvm::DenseMap<SILBasicBlock *, SILInstruction *> genForRoot;
119+
for (auto *instruction : effects.gens()) {
120+
if (effects.isLocalGen(instruction))
121+
continue;
122+
auto *block = instruction->getParent();
123+
rootBlocks.push_back(block);
124+
// If none of this block's successors are in the region, then we don't
125+
// need to visit the whole block--just the portion starting from this gen.
126+
if (!llvm::any_of(block->getSuccessorBlocks(),
127+
[&](SILBasicBlock *successor) {
128+
return visitor.isInRegion(successor);
129+
})) {
130+
genForRoot[block] = instruction;
131+
}
132+
}
133+
// Then do a backward DFS from those roots, and visit the blocks in reverse
134+
// post-order.
135+
SILCFGBackwardDFS<This> dfs(*this, rootBlocks);
136+
for (auto *block : dfs.reversePostOrder()) {
137+
// Check whether this block contains a non-local gen.
138+
auto iterator = genForRoot.find(block);
139+
if (iterator != genForRoot.end()) {
140+
visitBlockFromGen(iterator->getSecond());
141+
} else {
142+
visitBlock(block);
143+
}
144+
}
145+
// Finally, visit local gens which weren't visited already.
146+
for (auto *instruction : effects.localGens()) {
147+
auto *block = instruction->getParent();
148+
auto isInRegion = dfs.cachedVisited && dfs.cachedVisited->contains(block);
149+
auto visitedFullBlock = genForRoot.find(block) == genForRoot.end();
150+
if (isInRegion && visitedFullBlock)
151+
continue;
152+
// This local gen is either entirely outside the blocks that define the
153+
// region (!isInRegion) or it is in one of the bottom (i.e. one none of
154+
// whose successors are in the region) blocks in the region only the top
155+
// of which was already visited. Either way, the instructions between the
156+
// local gen and its kill have not yet been visited. Visit t hem now.
157+
auto foundLocalKill = visitBlockFromGenUntilBegin(instruction);
158+
assert(foundLocalKill && "local gen without local kill?!");
159+
(void)foundLocalKill;
160+
}
161+
}
162+
163+
/// visitSortedTopologicallyBackwards::Visitor
164+
165+
/// Whether the block is in the region of interest. Just a passhthrough to
166+
/// our visitor.
167+
bool isInRegion(SILBasicBlock *block) { return visitor.isInRegion(block); }
168+
169+
private:
170+
/// Entry points for visiting: they visit increasingly large portions of a
171+
/// block.
172+
/// - visitBlockFromGenUntilBegin: Instructions and phi until a kill.
173+
/// - visitBlockFromGen: Instructions, phi, and begin.
174+
/// - visitBlock: End, instructions, phi, and begin.
175+
176+
/// Visit instructions and phis starting from the specified local gen until a
177+
/// kill is found.
178+
bool visitBlockFromGenUntilBegin(SILInstruction *from) {
179+
assert(effects.effectForInstruction(from) == Effects::Effect::Gen());
180+
for (auto *instruction = from; instruction;
181+
instruction = instruction->getPreviousInstruction()) {
182+
if (visitInstruction(instruction))
183+
return true;
184+
}
185+
auto *block = from->getParent();
186+
if (block->hasPhi()) {
187+
if (visitPhi(block))
188+
return true;
189+
}
190+
return false;
191+
}
192+
193+
/// Visit a block from a non-local gen which begins the region.
194+
///
195+
/// Avoids visiting the portion of the block occurring after an initial gen.
196+
void visitBlockFromGen(SILInstruction *from) {
197+
auto *block = from->getParent();
198+
199+
assert(effects.effectForInstruction(from) == Effects::Effect::Gen());
200+
assert(!llvm::any_of(
201+
block->getSuccessorBlocks(),
202+
[&](SILBasicBlock *successor) { return visited.contains(successor); }));
203+
204+
visited.insert(block);
205+
bool foundLocalKill = visitBlockFromGenUntilBegin(from);
206+
assert(!foundLocalKill && "found local kill for non-local gen?!");
207+
(void)foundLocalKill;
208+
visitBlockBegin(block);
209+
}
210+
211+
/// Visit a block fully; its end, its body, its phi, and its begin.
212+
void visitBlock(SILBasicBlock *block) {
213+
visitBlockEnd(block);
214+
for (auto &instruction : llvm::reverse(*block)) {
215+
visitInstruction(&instruction);
216+
}
217+
if (block->hasPhi())
218+
visitPhi(block);
219+
visitBlockBegin(block);
220+
}
221+
222+
/// Visit block components:
223+
/// - block end
224+
/// - instruction
225+
/// - phi
226+
/// - block begin
227+
228+
/// Visit an instruction. Returns whether it is a barrier.
229+
bool visitInstruction(SILInstruction *instruction) {
230+
if (auto *eai = dyn_cast<EndAccessInst>(instruction)) {
231+
runningLiveAccessScopes.insert(eai->getBeginAccess());
232+
} else if (auto *bai = dyn_cast<BeginAccessInst>(instruction)) {
233+
runningLiveAccessScopes.erase(bai);
234+
}
235+
return handleEffect(effects.effectForInstruction(instruction));
236+
}
237+
238+
/// Visit a phi. Returns whether it is a barrier.
239+
bool visitPhi(SILBasicBlock *block) {
240+
return handleEffect(effects.effectForPhi(block));
241+
}
242+
243+
/// Visit a block begin. If any access scopes are live, record them for use
244+
/// (unioning) when the end of a predecessor is visited.
245+
void visitBlockBegin(SILBasicBlock *block) {
246+
if (!runningLiveAccessScopes.empty()) {
247+
liveInAccessScopes[block] = runningLiveAccessScopes;
248+
}
249+
}
250+
251+
/// Visit a block end. Set the running access scopes for the block to be the
252+
/// union of all access scopes live at the begin of successor blocks. Returns
253+
/// whether the block is a barrier.
254+
bool visitBlockEnd(SILBasicBlock *block) {
255+
visited.insert(block);
256+
runningLiveAccessScopes.clear();
257+
for (auto *successor : block->getSuccessorBlocks()) {
258+
auto iterator = liveInAccessScopes.find(successor);
259+
if (iterator != liveInAccessScopes.end()) {
260+
for (auto *bai : iterator->getSecond()) {
261+
runningLiveAccessScopes.insert(bai);
262+
}
263+
}
264+
}
265+
// If any of this block's predecessors haven't already been visited, it
266+
// means that they aren't in the region and consequently this block is a
267+
// barrier block.
268+
if (llvm::any_of(block->getSuccessorBlocks(), [&](SILBasicBlock *block) {
269+
return !visited.contains(block);
270+
})) {
271+
handleBarrier();
272+
return true;
273+
}
274+
275+
return false;
276+
}
277+
278+
/// Effect procecessing
279+
280+
bool handleEffect(typename Effects::Effect effect) {
281+
switch (effect.value) {
282+
case Effects::Effect::Value::NoEffect:
283+
return false;
284+
case Effects::Effect::Value::Gen:
285+
runningLiveAccessScopes.clear();
286+
return false;
287+
case Effects::Effect::Value::Kill:
288+
handleBarrier();
289+
return true;
290+
}
291+
}
292+
293+
void handleBarrier() {
294+
for (auto *scope : runningLiveAccessScopes) {
295+
visitor.visitBarrierAccessScope(scope);
296+
}
297+
}
298+
};
299+
300+
} // end namespace swift

0 commit comments

Comments
 (0)