|
| 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