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