blob: 115f5d6e814b839c58858873710ef2f4144a8c01 [file] [log] [blame]
Bardia Mahjourdb800c22019-09-18 17:43:45 +00001//===- 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"
bmahjourf0af11d82019-11-08 15:05:06 -050013#include "llvm/ADT/EnumeratedArray.h"
Bardia Mahjourdb800c22019-09-18 17:43:45 +000014#include "llvm/ADT/SCCIterator.h"
15#include "llvm/ADT/Statistic.h"
16#include "llvm/Analysis/DDG.h"
17
18using namespace llvm;
19
20#define DEBUG_TYPE "dgb"
21
22STATISTIC(TotalGraphs, "Number of dependence graphs created.");
23STATISTIC(TotalDefUseEdges, "Number of def-use edges created.");
24STATISTIC(TotalMemoryEdges, "Number of memory dependence edges created.");
25STATISTIC(TotalFineGrainedNodes, "Number of fine-grained nodes created.");
bmahjourf0af11d82019-11-08 15:05:06 -050026STATISTIC(TotalPiBlockNodes, "Number of pi-block nodes created.");
Bardia Mahjourdb800c22019-09-18 17:43:45 +000027STATISTIC(TotalConfusedEdges,
28 "Number of confused memory dependencies between two nodes.");
29STATISTIC(TotalEdgeReversals,
30 "Number of times the source and sink of dependence was reversed to "
31 "expose cycles in the graph.");
32
33using InstructionListType = SmallVector<Instruction *, 2>;
34
35//===--------------------------------------------------------------------===//
36// AbstractDependenceGraphBuilder implementation
37//===--------------------------------------------------------------------===//
38
39template <class G>
40void 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
Bardia Mahjour91b62d52019-10-01 19:32:42 +000051template <class G>
52void 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
bmahjourf0af11d82019-11-08 15:05:06 -050079template <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
Bardia Mahjourdb800c22019-09-18 17:43:45 +0000206template <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
255template <class G>
256void 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
356template class llvm::AbstractDependenceGraphBuilder<DataDependenceGraph>;
357template class llvm::DependenceGraphInfo<DDGNode>;