1 //===- DependenceGraphBuilder.cpp ------------------------------------------==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // This file implements common steps of the build algorithm for construction 9 // of dependence graphs such as DDG and PDG. 10 //===----------------------------------------------------------------------===// 11 12 #include "llvm/Analysis/DependenceGraphBuilder.h" 13 #include "llvm/ADT/EnumeratedArray.h" 14 #include "llvm/ADT/SCCIterator.h" 15 #include "llvm/ADT/Statistic.h" 16 #include "llvm/Analysis/DDG.h" 17 18 using namespace llvm; 19 20 #define DEBUG_TYPE "dgb" 21 22 STATISTIC(TotalGraphs, "Number of dependence graphs created."); 23 STATISTIC(TotalDefUseEdges, "Number of def-use edges created."); 24 STATISTIC(TotalMemoryEdges, "Number of memory dependence edges created."); 25 STATISTIC(TotalFineGrainedNodes, "Number of fine-grained nodes created."); 26 STATISTIC(TotalPiBlockNodes, "Number of pi-block nodes created."); 27 STATISTIC(TotalConfusedEdges, 28 "Number of confused memory dependencies between two nodes."); 29 STATISTIC(TotalEdgeReversals, 30 "Number of times the source and sink of dependence was reversed to " 31 "expose cycles in the graph."); 32 33 using InstructionListType = SmallVector<Instruction *, 2>; 34 35 //===--------------------------------------------------------------------===// 36 // AbstractDependenceGraphBuilder implementation 37 //===--------------------------------------------------------------------===// 38 39 template <class G> 40 void AbstractDependenceGraphBuilder<G>::createFineGrainedNodes() { 41 ++TotalGraphs; 42 assert(IMap.empty() && "Expected empty instruction map at start"); 43 for (BasicBlock *BB : BBList) 44 for (Instruction &I : *BB) { 45 auto &NewNode = createFineGrainedNode(I); 46 IMap.insert(std::make_pair(&I, &NewNode)); 47 ++TotalFineGrainedNodes; 48 } 49 } 50 51 template <class G> 52 void AbstractDependenceGraphBuilder<G>::createAndConnectRootNode() { 53 // Create a root node that connects to every connected component of the graph. 54 // This is done to allow graph iterators to visit all the disjoint components 55 // of the graph, in a single walk. 56 // 57 // This algorithm works by going through each node of the graph and for each 58 // node N, do a DFS starting from N. A rooted edge is established between the 59 // root node and N (if N is not yet visited). All the nodes reachable from N 60 // are marked as visited and are skipped in the DFS of subsequent nodes. 61 // 62 // Note: This algorithm tries to limit the number of edges out of the root 63 // node to some extent, but there may be redundant edges created depending on 64 // the iteration order. For example for a graph {A -> B}, an edge from the 65 // root node is added to both nodes if B is visited before A. While it does 66 // not result in minimal number of edges, this approach saves compile-time 67 // while keeping the number of edges in check. 68 auto &RootNode = createRootNode(); 69 df_iterator_default_set<const NodeType *, 4> Visited; 70 for (auto *N : Graph) { 71 if (*N == RootNode) 72 continue; 73 for (auto I : depth_first_ext(N, Visited)) 74 if (I == N) 75 createRootedEdge(RootNode, *N); 76 } 77 } 78 79 template <class G> void AbstractDependenceGraphBuilder<G>::createPiBlocks() { 80 if (!shouldCreatePiBlocks()) 81 return; 82 83 LLVM_DEBUG(dbgs() << "==== Start of Creation of Pi-Blocks ===\n"); 84 85 // The overall algorithm is as follows: 86 // 1. Identify SCCs and for each SCC create a pi-block node containing all 87 // the nodes in that SCC. 88 // 2. Identify incoming edges incident to the nodes inside of the SCC and 89 // reconnect them to the pi-block node. 90 // 3. Identify outgoing edges from the nodes inside of the SCC to nodes 91 // outside of it and reconnect them so that the edges are coming out of the 92 // SCC node instead. 93 94 // Adding nodes as we iterate through the SCCs cause the SCC 95 // iterators to get invalidated. To prevent this invalidation, we first 96 // collect a list of nodes that are part of an SCC, and then iterate over 97 // those lists to create the pi-block nodes. Each element of the list is a 98 // list of nodes in an SCC. Note: trivial SCCs containing a single node are 99 // ignored. 100 SmallVector<NodeListType, 4> ListOfSCCs; 101 for (auto &SCC : make_range(scc_begin(&Graph), scc_end(&Graph))) { 102 if (SCC.size() > 1) 103 ListOfSCCs.emplace_back(SCC.begin(), SCC.end()); 104 } 105 106 for (NodeListType &NL : ListOfSCCs) { 107 LLVM_DEBUG(dbgs() << "Creating pi-block node with " << NL.size() 108 << " nodes in it.\n"); 109 110 NodeType &PiNode = createPiBlock(NL); 111 ++TotalPiBlockNodes; 112 113 // Build a set to speed up the lookup for edges whose targets 114 // are inside the SCC. 115 SmallPtrSet<NodeType *, 4> NodesInSCC(NL.begin(), NL.end()); 116 117 // We have the set of nodes in the SCC. We go through the set of nodes 118 // that are outside of the SCC and look for edges that cross the two sets. 119 for (NodeType *N : Graph) { 120 121 // Skip the SCC node and all the nodes inside of it. 122 if (*N == PiNode || NodesInSCC.count(N)) 123 continue; 124 125 for (NodeType *SCCNode : NL) { 126 127 enum Direction { 128 Incoming, // Incoming edges to the SCC 129 Outgoing, // Edges going ot of the SCC 130 DirectionCount // To make the enum usable as an array index. 131 }; 132 133 // Use these flags to help us avoid creating redundant edges. If there 134 // are more than one edges from an outside node to inside nodes, we only 135 // keep one edge from that node to the pi-block node. Similarly, if 136 // there are more than one edges from inside nodes to an outside node, 137 // we only keep one edge from the pi-block node to the outside node. 138 // There is a flag defined for each direction (incoming vs outgoing) and 139 // for each type of edge supported, using a two-dimensional boolean 140 // array. 141 using EdgeKind = typename EdgeType::EdgeKind; 142 EnumeratedArray<bool, EdgeKind> EdgeAlreadyCreated[DirectionCount]{ 143 false, false}; 144 145 auto createEdgeOfKind = [this](NodeType &Src, NodeType &Dst, 146 const EdgeKind K) { 147 switch (K) { 148 case EdgeKind::RegisterDefUse: 149 createDefUseEdge(Src, Dst); 150 break; 151 case EdgeKind::MemoryDependence: 152 createMemoryEdge(Src, Dst); 153 break; 154 case EdgeKind::Rooted: 155 createRootedEdge(Src, Dst); 156 break; 157 default: 158 llvm_unreachable("Unsupported type of edge."); 159 } 160 }; 161 162 auto reconnectEdges = [&](NodeType *Src, NodeType *Dst, NodeType *New, 163 const Direction Dir) { 164 if (!Src->hasEdgeTo(*Dst)) 165 return; 166 LLVM_DEBUG(dbgs() 167 << "reconnecting(" 168 << (Dir == Direction::Incoming ? "incoming)" : "outgoing)") 169 << ":\nSrc:" << *Src << "\nDst:" << *Dst 170 << "\nNew:" << *New << "\n"); 171 assert((Dir == Direction::Incoming || Dir == Direction::Outgoing) && 172 "Invalid direction."); 173 174 SmallVector<EdgeType *, 10> EL; 175 Src->findEdgesTo(*Dst, EL); 176 for (EdgeType *OldEdge : EL) { 177 EdgeKind Kind = OldEdge->getKind(); 178 if (!EdgeAlreadyCreated[Dir][Kind]) { 179 if (Dir == Direction::Incoming) { 180 createEdgeOfKind(*Src, *New, Kind); 181 LLVM_DEBUG(dbgs() << "created edge from Src to New.\n"); 182 } else if (Dir == Direction::Outgoing) { 183 createEdgeOfKind(*New, *Dst, Kind); 184 LLVM_DEBUG(dbgs() << "created edge from New to Dst.\n"); 185 } 186 EdgeAlreadyCreated[Dir][Kind] = true; 187 } 188 Src->removeEdge(*OldEdge); 189 destroyEdge(*OldEdge); 190 LLVM_DEBUG(dbgs() << "removed old edge between Src and Dst.\n\n"); 191 } 192 }; 193 194 // Process incoming edges incident to the pi-block node. 195 reconnectEdges(N, SCCNode, &PiNode, Direction::Incoming); 196 197 // Process edges that are coming out of the pi-block node. 198 reconnectEdges(SCCNode, N, &PiNode, Direction::Outgoing); 199 } 200 } 201 } 202 203 LLVM_DEBUG(dbgs() << "==== End of Creation of Pi-Blocks ===\n"); 204 } 205 206 template <class G> void AbstractDependenceGraphBuilder<G>::createDefUseEdges() { 207 for (NodeType *N : Graph) { 208 InstructionListType SrcIList; 209 N->collectInstructions([](const Instruction *I) { return true; }, SrcIList); 210 211 // Use a set to mark the targets that we link to N, so we don't add 212 // duplicate def-use edges when more than one instruction in a target node 213 // use results of instructions that are contained in N. 214 SmallPtrSet<NodeType *, 4> VisitedTargets; 215 216 for (Instruction *II : SrcIList) { 217 for (User *U : II->users()) { 218 Instruction *UI = dyn_cast<Instruction>(U); 219 if (!UI) 220 continue; 221 NodeType *DstNode = nullptr; 222 if (IMap.find(UI) != IMap.end()) 223 DstNode = IMap.find(UI)->second; 224 225 // In the case of loops, the scope of the subgraph is all the 226 // basic blocks (and instructions within them) belonging to the loop. We 227 // simply ignore all the edges coming from (or going into) instructions 228 // or basic blocks outside of this range. 229 if (!DstNode) { 230 LLVM_DEBUG( 231 dbgs() 232 << "skipped def-use edge since the sink" << *UI 233 << " is outside the range of instructions being considered.\n"); 234 continue; 235 } 236 237 // Self dependencies are ignored because they are redundant and 238 // uninteresting. 239 if (DstNode == N) { 240 LLVM_DEBUG(dbgs() 241 << "skipped def-use edge since the sink and the source (" 242 << N << ") are the same.\n"); 243 continue; 244 } 245 246 if (VisitedTargets.insert(DstNode).second) { 247 createDefUseEdge(*N, *DstNode); 248 ++TotalDefUseEdges; 249 } 250 } 251 } 252 } 253 } 254 255 template <class G> 256 void AbstractDependenceGraphBuilder<G>::createMemoryDependencyEdges() { 257 using DGIterator = typename G::iterator; 258 auto isMemoryAccess = [](const Instruction *I) { 259 return I->mayReadOrWriteMemory(); 260 }; 261 for (DGIterator SrcIt = Graph.begin(), E = Graph.end(); SrcIt != E; ++SrcIt) { 262 InstructionListType SrcIList; 263 (*SrcIt)->collectInstructions(isMemoryAccess, SrcIList); 264 if (SrcIList.empty()) 265 continue; 266 267 for (DGIterator DstIt = SrcIt; DstIt != E; ++DstIt) { 268 if (**SrcIt == **DstIt) 269 continue; 270 InstructionListType DstIList; 271 (*DstIt)->collectInstructions(isMemoryAccess, DstIList); 272 if (DstIList.empty()) 273 continue; 274 bool ForwardEdgeCreated = false; 275 bool BackwardEdgeCreated = false; 276 for (Instruction *ISrc : SrcIList) { 277 for (Instruction *IDst : DstIList) { 278 auto D = DI.depends(ISrc, IDst, true); 279 if (!D) 280 continue; 281 282 // If we have a dependence with its left-most non-'=' direction 283 // being '>' we need to reverse the direction of the edge, because 284 // the source of the dependence cannot occur after the sink. For 285 // confused dependencies, we will create edges in both directions to 286 // represent the possibility of a cycle. 287 288 auto createConfusedEdges = [&](NodeType &Src, NodeType &Dst) { 289 if (!ForwardEdgeCreated) { 290 createMemoryEdge(Src, Dst); 291 ++TotalMemoryEdges; 292 } 293 if (!BackwardEdgeCreated) { 294 createMemoryEdge(Dst, Src); 295 ++TotalMemoryEdges; 296 } 297 ForwardEdgeCreated = BackwardEdgeCreated = true; 298 ++TotalConfusedEdges; 299 }; 300 301 auto createForwardEdge = [&](NodeType &Src, NodeType &Dst) { 302 if (!ForwardEdgeCreated) { 303 createMemoryEdge(Src, Dst); 304 ++TotalMemoryEdges; 305 } 306 ForwardEdgeCreated = true; 307 }; 308 309 auto createBackwardEdge = [&](NodeType &Src, NodeType &Dst) { 310 if (!BackwardEdgeCreated) { 311 createMemoryEdge(Dst, Src); 312 ++TotalMemoryEdges; 313 } 314 BackwardEdgeCreated = true; 315 }; 316 317 if (D->isConfused()) 318 createConfusedEdges(**SrcIt, **DstIt); 319 else if (D->isOrdered() && !D->isLoopIndependent()) { 320 bool ReversedEdge = false; 321 for (unsigned Level = 1; Level <= D->getLevels(); ++Level) { 322 if (D->getDirection(Level) == Dependence::DVEntry::EQ) 323 continue; 324 else if (D->getDirection(Level) == Dependence::DVEntry::GT) { 325 createBackwardEdge(**SrcIt, **DstIt); 326 ReversedEdge = true; 327 ++TotalEdgeReversals; 328 break; 329 } else if (D->getDirection(Level) == Dependence::DVEntry::LT) 330 break; 331 else { 332 createConfusedEdges(**SrcIt, **DstIt); 333 break; 334 } 335 } 336 if (!ReversedEdge) 337 createForwardEdge(**SrcIt, **DstIt); 338 } else 339 createForwardEdge(**SrcIt, **DstIt); 340 341 // Avoid creating duplicate edges. 342 if (ForwardEdgeCreated && BackwardEdgeCreated) 343 break; 344 } 345 346 // If we've created edges in both directions, there is no more 347 // unique edge that we can create between these two nodes, so we 348 // can exit early. 349 if (ForwardEdgeCreated && BackwardEdgeCreated) 350 break; 351 } 352 } 353 } 354 } 355 356 template <class G> 357 void AbstractDependenceGraphBuilder<G>::sortNodesTopologically() { 358 359 // If we don't create pi-blocks, then we may not have a DAG. 360 if (!shouldCreatePiBlocks()) 361 return; 362 363 SmallVector<NodeType *, 64> NodesInPO; 364 for (NodeType *N : post_order(&Graph)) 365 NodesInPO.push_back(N); 366 367 Graph.Nodes.clear(); 368 for (auto &N : make_range(NodesInPO.rbegin(), NodesInPO.rend())) 369 Graph.Nodes.push_back(N); 370 } 371 372 template class llvm::AbstractDependenceGraphBuilder<DataDependenceGraph>; 373 template class llvm::DependenceGraphInfo<DDGNode>; 374