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