1 //===- llvm/Analysis/DDG.h --------------------------------------*- C++ -*-===// 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 // 9 // This file defines the Data-Dependence Graph (DDG). 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_ANALYSIS_DDG_H 14 #define LLVM_ANALYSIS_DDG_H 15 16 #include "llvm/ADT/DenseMap.h" 17 #include "llvm/ADT/DirectedGraph.h" 18 #include "llvm/Analysis/DependenceAnalysis.h" 19 #include "llvm/Analysis/DependenceGraphBuilder.h" 20 #include "llvm/Analysis/LoopAnalysisManager.h" 21 #include "llvm/IR/Instructions.h" 22 23 namespace llvm { 24 class DDGNode; 25 class DDGEdge; 26 using DDGNodeBase = DGNode<DDGNode, DDGEdge>; 27 using DDGEdgeBase = DGEdge<DDGNode, DDGEdge>; 28 using DDGBase = DirectedGraph<DDGNode, DDGEdge>; 29 class LPMUpdater; 30 31 /// Data Dependence Graph Node 32 /// The graph can represent the following types of nodes: 33 /// 1. Single instruction node containing just one instruction. 34 /// 2. Multiple instruction node where two or more instructions from 35 /// the same basic block are merged into one node. 36 /// 3. Pi-block node which is a group of other DDG nodes that are part of a 37 /// strongly-connected component of the graph. 38 /// A pi-block node contains more than one single or multiple instruction 39 /// nodes. The root node cannot be part of a pi-block. 40 /// 4. Root node is a special node that connects to all components such that 41 /// there is always a path from it to any node in the graph. 42 class DDGNode : public DDGNodeBase { 43 public: 44 using InstructionListType = SmallVectorImpl<Instruction *>; 45 46 enum class NodeKind { 47 Unknown, 48 SingleInstruction, 49 MultiInstruction, 50 PiBlock, 51 Root, 52 }; 53 54 DDGNode() = delete; DDGNode(const NodeKind K)55 DDGNode(const NodeKind K) : DDGNodeBase(), Kind(K) {} DDGNode(const DDGNode & N)56 DDGNode(const DDGNode &N) : DDGNodeBase(N), Kind(N.Kind) {} DDGNode(DDGNode && N)57 DDGNode(DDGNode &&N) : DDGNodeBase(std::move(N)), Kind(N.Kind) {} 58 virtual ~DDGNode() = 0; 59 60 DDGNode &operator=(const DDGNode &N) { 61 DGNode::operator=(N); 62 Kind = N.Kind; 63 return *this; 64 } 65 66 DDGNode &operator=(DDGNode &&N) { 67 DGNode::operator=(std::move(N)); 68 Kind = N.Kind; 69 return *this; 70 } 71 72 /// Getter for the kind of this node. getKind()73 NodeKind getKind() const { return Kind; } 74 75 /// Collect a list of instructions, in \p IList, for which predicate \p Pred 76 /// evaluates to true when iterating over instructions of this node. Return 77 /// true if at least one instruction was collected, and false otherwise. 78 bool collectInstructions(llvm::function_ref<bool(Instruction *)> const &Pred, 79 InstructionListType &IList) const; 80 81 protected: 82 /// Setter for the kind of this node. setKind(NodeKind K)83 void setKind(NodeKind K) { Kind = K; } 84 85 private: 86 NodeKind Kind; 87 }; 88 89 /// Subclass of DDGNode representing the root node of the graph. 90 /// There should only be one such node in a given graph. 91 class RootDDGNode : public DDGNode { 92 public: RootDDGNode()93 RootDDGNode() : DDGNode(NodeKind::Root) {} 94 RootDDGNode(const RootDDGNode &N) = delete; RootDDGNode(RootDDGNode && N)95 RootDDGNode(RootDDGNode &&N) : DDGNode(std::move(N)) {} ~RootDDGNode()96 ~RootDDGNode() {} 97 98 /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc. classof(const DDGNode * N)99 static bool classof(const DDGNode *N) { 100 return N->getKind() == NodeKind::Root; 101 } classof(const RootDDGNode * N)102 static bool classof(const RootDDGNode *N) { return true; } 103 }; 104 105 /// Subclass of DDGNode representing single or multi-instruction nodes. 106 class SimpleDDGNode : public DDGNode { 107 public: 108 SimpleDDGNode() = delete; 109 SimpleDDGNode(Instruction &I); 110 SimpleDDGNode(const SimpleDDGNode &N); 111 SimpleDDGNode(SimpleDDGNode &&N); 112 ~SimpleDDGNode(); 113 114 SimpleDDGNode &operator=(const SimpleDDGNode &N) { 115 DDGNode::operator=(N); 116 InstList = N.InstList; 117 return *this; 118 } 119 120 SimpleDDGNode &operator=(SimpleDDGNode &&N) { 121 DDGNode::operator=(std::move(N)); 122 InstList = std::move(N.InstList); 123 return *this; 124 } 125 126 /// Get the list of instructions in this node. getInstructions()127 const InstructionListType &getInstructions() const { 128 assert(!InstList.empty() && "Instruction List is empty."); 129 return InstList; 130 } getInstructions()131 InstructionListType &getInstructions() { 132 return const_cast<InstructionListType &>( 133 static_cast<const SimpleDDGNode *>(this)->getInstructions()); 134 } 135 136 /// Get the first/last instruction in the node. getFirstInstruction()137 Instruction *getFirstInstruction() const { return getInstructions().front(); } getLastInstruction()138 Instruction *getLastInstruction() const { return getInstructions().back(); } 139 140 /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc. classof(const DDGNode * N)141 static bool classof(const DDGNode *N) { 142 return N->getKind() == NodeKind::SingleInstruction || 143 N->getKind() == NodeKind::MultiInstruction; 144 } classof(const SimpleDDGNode * N)145 static bool classof(const SimpleDDGNode *N) { return true; } 146 147 private: 148 /// Append the list of instructions in \p Input to this node. appendInstructions(const InstructionListType & Input)149 void appendInstructions(const InstructionListType &Input) { 150 setKind((InstList.size() == 0 && Input.size() == 1) 151 ? NodeKind::SingleInstruction 152 : NodeKind::MultiInstruction); 153 InstList.insert(InstList.end(), Input.begin(), Input.end()); 154 } appendInstructions(const SimpleDDGNode & Input)155 void appendInstructions(const SimpleDDGNode &Input) { 156 appendInstructions(Input.getInstructions()); 157 } 158 159 /// List of instructions associated with a single or multi-instruction node. 160 SmallVector<Instruction *, 2> InstList; 161 }; 162 163 /// Subclass of DDGNode representing a pi-block. A pi-block represents a group 164 /// of DDG nodes that are part of a strongly-connected component of the graph. 165 /// Replacing all the SCCs with pi-blocks results in an acyclic representation 166 /// of the DDG. For example if we have: 167 /// {a -> b}, {b -> c, d}, {c -> a} 168 /// the cycle a -> b -> c -> a is abstracted into a pi-block "p" as follows: 169 /// {p -> d} with "p" containing: {a -> b}, {b -> c}, {c -> a} 170 class PiBlockDDGNode : public DDGNode { 171 public: 172 using PiNodeList = SmallVector<DDGNode *, 4>; 173 174 PiBlockDDGNode() = delete; 175 PiBlockDDGNode(const PiNodeList &List); 176 PiBlockDDGNode(const PiBlockDDGNode &N); 177 PiBlockDDGNode(PiBlockDDGNode &&N); 178 ~PiBlockDDGNode(); 179 180 PiBlockDDGNode &operator=(const PiBlockDDGNode &N) { 181 DDGNode::operator=(N); 182 NodeList = N.NodeList; 183 return *this; 184 } 185 186 PiBlockDDGNode &operator=(PiBlockDDGNode &&N) { 187 DDGNode::operator=(std::move(N)); 188 NodeList = std::move(N.NodeList); 189 return *this; 190 } 191 192 /// Get the list of nodes in this pi-block. getNodes()193 const PiNodeList &getNodes() const { 194 assert(!NodeList.empty() && "Node list is empty."); 195 return NodeList; 196 } getNodes()197 PiNodeList &getNodes() { 198 return const_cast<PiNodeList &>( 199 static_cast<const PiBlockDDGNode *>(this)->getNodes()); 200 } 201 202 /// Define classof to be able to use isa<>, cast<>, dyn_cast<>, etc. classof(const DDGNode * N)203 static bool classof(const DDGNode *N) { 204 return N->getKind() == NodeKind::PiBlock; 205 } 206 207 private: 208 /// List of nodes in this pi-block. 209 PiNodeList NodeList; 210 }; 211 212 /// Data Dependency Graph Edge. 213 /// An edge in the DDG can represent a def-use relationship or 214 /// a memory dependence based on the result of DependenceAnalysis. 215 /// A rooted edge connects the root node to one of the components 216 /// of the graph. 217 class DDGEdge : public DDGEdgeBase { 218 public: 219 /// The kind of edge in the DDG 220 enum class EdgeKind { 221 Unknown, 222 RegisterDefUse, 223 MemoryDependence, 224 Rooted, 225 Last = Rooted // Must be equal to the largest enum value. 226 }; 227 228 explicit DDGEdge(DDGNode &N) = delete; DDGEdge(DDGNode & N,EdgeKind K)229 DDGEdge(DDGNode &N, EdgeKind K) : DDGEdgeBase(N), Kind(K) {} DDGEdge(const DDGEdge & E)230 DDGEdge(const DDGEdge &E) : DDGEdgeBase(E), Kind(E.getKind()) {} DDGEdge(DDGEdge && E)231 DDGEdge(DDGEdge &&E) : DDGEdgeBase(std::move(E)), Kind(E.Kind) {} 232 DDGEdge &operator=(const DDGEdge &E) { 233 DDGEdgeBase::operator=(E); 234 Kind = E.Kind; 235 return *this; 236 } 237 238 DDGEdge &operator=(DDGEdge &&E) { 239 DDGEdgeBase::operator=(std::move(E)); 240 Kind = E.Kind; 241 return *this; 242 } 243 244 /// Get the edge kind getKind()245 EdgeKind getKind() const { return Kind; }; 246 247 /// Return true if this is a def-use edge, and false otherwise. isDefUse()248 bool isDefUse() const { return Kind == EdgeKind::RegisterDefUse; } 249 250 /// Return true if this is a memory dependence edge, and false otherwise. isMemoryDependence()251 bool isMemoryDependence() const { return Kind == EdgeKind::MemoryDependence; } 252 253 /// Return true if this is an edge stemming from the root node, and false 254 /// otherwise. isRooted()255 bool isRooted() const { return Kind == EdgeKind::Rooted; } 256 257 private: 258 EdgeKind Kind; 259 }; 260 261 /// Encapsulate some common data and functionality needed for different 262 /// variations of data dependence graphs. 263 template <typename NodeType> class DependenceGraphInfo { 264 public: 265 using DependenceList = SmallVector<std::unique_ptr<Dependence>, 1>; 266 267 DependenceGraphInfo() = delete; 268 DependenceGraphInfo(const DependenceGraphInfo &G) = delete; DependenceGraphInfo(const std::string & N,const DependenceInfo & DepInfo)269 DependenceGraphInfo(const std::string &N, const DependenceInfo &DepInfo) 270 : Name(N), DI(DepInfo), Root(nullptr) {} DependenceGraphInfo(DependenceGraphInfo && G)271 DependenceGraphInfo(DependenceGraphInfo &&G) 272 : Name(std::move(G.Name)), DI(std::move(G.DI)), Root(G.Root) {} ~DependenceGraphInfo()273 virtual ~DependenceGraphInfo() {} 274 275 /// Return the label that is used to name this graph. getName()276 const StringRef getName() const { return Name; } 277 278 /// Return the root node of the graph. getRoot()279 NodeType &getRoot() const { 280 assert(Root && "Root node is not available yet. Graph construction may " 281 "still be in progress\n"); 282 return *Root; 283 } 284 285 protected: 286 // Name of the graph. 287 std::string Name; 288 289 // Store a copy of DependenceInfo in the graph, so that individual memory 290 // dependencies don't need to be stored. Instead when the dependence is 291 // queried it is recomputed using @DI. 292 const DependenceInfo DI; 293 294 // A special node in the graph that has an edge to every connected component of 295 // the graph, to ensure all nodes are reachable in a graph walk. 296 NodeType *Root = nullptr; 297 }; 298 299 using DDGInfo = DependenceGraphInfo<DDGNode>; 300 301 /// Data Dependency Graph 302 class DataDependenceGraph : public DDGBase, public DDGInfo { 303 friend AbstractDependenceGraphBuilder<DataDependenceGraph>; 304 friend class DDGBuilder; 305 306 public: 307 using NodeType = DDGNode; 308 using EdgeType = DDGEdge; 309 310 DataDependenceGraph() = delete; 311 DataDependenceGraph(const DataDependenceGraph &G) = delete; DataDependenceGraph(DataDependenceGraph && G)312 DataDependenceGraph(DataDependenceGraph &&G) 313 : DDGBase(std::move(G)), DDGInfo(std::move(G)) {} 314 DataDependenceGraph(Function &F, DependenceInfo &DI); 315 DataDependenceGraph(Loop &L, LoopInfo &LI, DependenceInfo &DI); 316 ~DataDependenceGraph(); 317 318 /// If node \p N belongs to a pi-block return a pointer to the pi-block, 319 /// otherwise return null. 320 const PiBlockDDGNode *getPiBlock(const NodeType &N) const; 321 322 protected: 323 /// Add node \p N to the graph, if it's not added yet, and keep track of the 324 /// root node as well as pi-blocks and their members. Return true if node is 325 /// successfully added. 326 bool addNode(NodeType &N); 327 328 private: 329 using PiBlockMapType = DenseMap<const NodeType *, const PiBlockDDGNode *>; 330 331 /// Mapping from graph nodes to their containing pi-blocks. If a node is not 332 /// part of a pi-block, it will not appear in this map. 333 PiBlockMapType PiBlockMap; 334 }; 335 336 /// Concrete implementation of a pure data dependence graph builder. This class 337 /// provides custom implementation for the pure-virtual functions used in the 338 /// generic dependence graph build algorithm. 339 /// 340 /// For information about time complexity of the build algorithm see the 341 /// comments near the declaration of AbstractDependenceGraphBuilder. 342 class DDGBuilder : public AbstractDependenceGraphBuilder<DataDependenceGraph> { 343 public: DDGBuilder(DataDependenceGraph & G,DependenceInfo & D,const BasicBlockListType & BBs)344 DDGBuilder(DataDependenceGraph &G, DependenceInfo &D, 345 const BasicBlockListType &BBs) 346 : AbstractDependenceGraphBuilder(G, D, BBs) {} createRootNode()347 DDGNode &createRootNode() final override { 348 auto *RN = new RootDDGNode(); 349 assert(RN && "Failed to allocate memory for DDG root node."); 350 Graph.addNode(*RN); 351 return *RN; 352 } createFineGrainedNode(Instruction & I)353 DDGNode &createFineGrainedNode(Instruction &I) final override { 354 auto *SN = new SimpleDDGNode(I); 355 assert(SN && "Failed to allocate memory for simple DDG node."); 356 Graph.addNode(*SN); 357 return *SN; 358 } createPiBlock(const NodeListType & L)359 DDGNode &createPiBlock(const NodeListType &L) final override { 360 auto *Pi = new PiBlockDDGNode(L); 361 assert(Pi && "Failed to allocate memory for pi-block node."); 362 Graph.addNode(*Pi); 363 return *Pi; 364 } createDefUseEdge(DDGNode & Src,DDGNode & Tgt)365 DDGEdge &createDefUseEdge(DDGNode &Src, DDGNode &Tgt) final override { 366 auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::RegisterDefUse); 367 assert(E && "Failed to allocate memory for edge"); 368 Graph.connect(Src, Tgt, *E); 369 return *E; 370 } createMemoryEdge(DDGNode & Src,DDGNode & Tgt)371 DDGEdge &createMemoryEdge(DDGNode &Src, DDGNode &Tgt) final override { 372 auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::MemoryDependence); 373 assert(E && "Failed to allocate memory for edge"); 374 Graph.connect(Src, Tgt, *E); 375 return *E; 376 } createRootedEdge(DDGNode & Src,DDGNode & Tgt)377 DDGEdge &createRootedEdge(DDGNode &Src, DDGNode &Tgt) final override { 378 auto *E = new DDGEdge(Tgt, DDGEdge::EdgeKind::Rooted); 379 assert(E && "Failed to allocate memory for edge"); 380 assert(isa<RootDDGNode>(Src) && "Expected root node"); 381 Graph.connect(Src, Tgt, *E); 382 return *E; 383 } 384 getNodesInPiBlock(const DDGNode & N)385 const NodeListType &getNodesInPiBlock(const DDGNode &N) final override { 386 auto *PiNode = dyn_cast<const PiBlockDDGNode>(&N); 387 assert(PiNode && "Expected a pi-block node."); 388 return PiNode->getNodes(); 389 } 390 391 bool shouldCreatePiBlocks() const final override; 392 }; 393 394 raw_ostream &operator<<(raw_ostream &OS, const DDGNode &N); 395 raw_ostream &operator<<(raw_ostream &OS, const DDGNode::NodeKind K); 396 raw_ostream &operator<<(raw_ostream &OS, const DDGEdge &E); 397 raw_ostream &operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K); 398 raw_ostream &operator<<(raw_ostream &OS, const DataDependenceGraph &G); 399 400 //===--------------------------------------------------------------------===// 401 // DDG Analysis Passes 402 //===--------------------------------------------------------------------===// 403 404 /// Analysis pass that builds the DDG for a loop. 405 class DDGAnalysis : public AnalysisInfoMixin<DDGAnalysis> { 406 public: 407 using Result = std::unique_ptr<DataDependenceGraph>; 408 Result run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR); 409 410 private: 411 friend AnalysisInfoMixin<DDGAnalysis>; 412 static AnalysisKey Key; 413 }; 414 415 /// Textual printer pass for the DDG of a loop. 416 class DDGAnalysisPrinterPass : public PassInfoMixin<DDGAnalysisPrinterPass> { 417 public: DDGAnalysisPrinterPass(raw_ostream & OS)418 explicit DDGAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {} 419 PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM, 420 LoopStandardAnalysisResults &AR, LPMUpdater &U); 421 422 private: 423 raw_ostream &OS; 424 }; 425 426 //===--------------------------------------------------------------------===// 427 // GraphTraits specializations for the DDG 428 //===--------------------------------------------------------------------===// 429 430 /// non-const versions of the grapth trait specializations for DDG 431 template <> struct GraphTraits<DDGNode *> { 432 using NodeRef = DDGNode *; 433 434 static DDGNode *DDGGetTargetNode(DGEdge<DDGNode, DDGEdge> *P) { 435 return &P->getTargetNode(); 436 } 437 438 // Provide a mapped iterator so that the GraphTrait-based implementations can 439 // find the target nodes without having to explicitly go through the edges. 440 using ChildIteratorType = 441 mapped_iterator<DDGNode::iterator, decltype(&DDGGetTargetNode)>; 442 using ChildEdgeIteratorType = DDGNode::iterator; 443 444 static NodeRef getEntryNode(NodeRef N) { return N; } 445 static ChildIteratorType child_begin(NodeRef N) { 446 return ChildIteratorType(N->begin(), &DDGGetTargetNode); 447 } 448 static ChildIteratorType child_end(NodeRef N) { 449 return ChildIteratorType(N->end(), &DDGGetTargetNode); 450 } 451 452 static ChildEdgeIteratorType child_edge_begin(NodeRef N) { 453 return N->begin(); 454 } 455 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); } 456 }; 457 458 template <> 459 struct GraphTraits<DataDependenceGraph *> : public GraphTraits<DDGNode *> { 460 using nodes_iterator = DataDependenceGraph::iterator; 461 static NodeRef getEntryNode(DataDependenceGraph *DG) { 462 return &DG->getRoot(); 463 } 464 static nodes_iterator nodes_begin(DataDependenceGraph *DG) { 465 return DG->begin(); 466 } 467 static nodes_iterator nodes_end(DataDependenceGraph *DG) { return DG->end(); } 468 }; 469 470 /// const versions of the grapth trait specializations for DDG 471 template <> struct GraphTraits<const DDGNode *> { 472 using NodeRef = const DDGNode *; 473 474 static const DDGNode *DDGGetTargetNode(const DGEdge<DDGNode, DDGEdge> *P) { 475 return &P->getTargetNode(); 476 } 477 478 // Provide a mapped iterator so that the GraphTrait-based implementations can 479 // find the target nodes without having to explicitly go through the edges. 480 using ChildIteratorType = 481 mapped_iterator<DDGNode::const_iterator, decltype(&DDGGetTargetNode)>; 482 using ChildEdgeIteratorType = DDGNode::const_iterator; 483 484 static NodeRef getEntryNode(NodeRef N) { return N; } 485 static ChildIteratorType child_begin(NodeRef N) { 486 return ChildIteratorType(N->begin(), &DDGGetTargetNode); 487 } 488 static ChildIteratorType child_end(NodeRef N) { 489 return ChildIteratorType(N->end(), &DDGGetTargetNode); 490 } 491 492 static ChildEdgeIteratorType child_edge_begin(NodeRef N) { 493 return N->begin(); 494 } 495 static ChildEdgeIteratorType child_edge_end(NodeRef N) { return N->end(); } 496 }; 497 498 template <> 499 struct GraphTraits<const DataDependenceGraph *> 500 : public GraphTraits<const DDGNode *> { 501 using nodes_iterator = DataDependenceGraph::const_iterator; 502 static NodeRef getEntryNode(const DataDependenceGraph *DG) { 503 return &DG->getRoot(); 504 } 505 static nodes_iterator nodes_begin(const DataDependenceGraph *DG) { 506 return DG->begin(); 507 } 508 static nodes_iterator nodes_end(const DataDependenceGraph *DG) { 509 return DG->end(); 510 } 511 }; 512 513 } // namespace llvm 514 515 #endif // LLVM_ANALYSIS_DDG_H 516