1 | //===- DDG.cpp - Data Dependence Graph -------------------------------------==// |
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 | // The implementation for the data dependence graph. |
10 | //===----------------------------------------------------------------------===// |
11 | #include "llvm/Analysis/DDG.h" |
12 | #include "llvm/ADT/SCCIterator.h" |
13 | #include "llvm/Analysis/LoopInfo.h" |
14 | #include "llvm/Analysis/LoopIterator.h" |
15 | #include "llvm/Support/CommandLine.h" |
16 | |
17 | using namespace llvm; |
18 | |
19 | static cl::opt<bool> SimplifyDDG( |
20 | "ddg-simplify" , cl::init(Val: true), cl::Hidden, |
21 | cl::desc( |
22 | "Simplify DDG by merging nodes that have less interesting edges." )); |
23 | |
24 | static cl::opt<bool> CreatePiBlocks("ddg-pi-blocks" , cl::init(Val: true), cl::Hidden, |
25 | cl::desc("Create pi-block nodes." )); |
26 | |
27 | #define DEBUG_TYPE "ddg" |
28 | |
29 | template class llvm::DGEdge<DDGNode, DDGEdge>; |
30 | template class llvm::DGNode<DDGNode, DDGEdge>; |
31 | template class llvm::DirectedGraph<DDGNode, DDGEdge>; |
32 | |
33 | //===--------------------------------------------------------------------===// |
34 | // DDGNode implementation |
35 | //===--------------------------------------------------------------------===// |
36 | DDGNode::~DDGNode() = default; |
37 | |
38 | bool DDGNode::collectInstructions( |
39 | llvm::function_ref<bool(Instruction *)> const &Pred, |
40 | InstructionListType &IList) const { |
41 | assert(IList.empty() && "Expected the IList to be empty on entry." ); |
42 | if (isa<SimpleDDGNode>(Val: this)) { |
43 | for (Instruction *I : cast<const SimpleDDGNode>(Val: this)->getInstructions()) |
44 | if (Pred(I)) |
45 | IList.push_back(Elt: I); |
46 | } else if (isa<PiBlockDDGNode>(Val: this)) { |
47 | for (const DDGNode *PN : cast<const PiBlockDDGNode>(Val: this)->getNodes()) { |
48 | assert(!isa<PiBlockDDGNode>(PN) && "Nested PiBlocks are not supported." ); |
49 | SmallVector<Instruction *, 8> TmpIList; |
50 | PN->collectInstructions(Pred, IList&: TmpIList); |
51 | llvm::append_range(C&: IList, R&: TmpIList); |
52 | } |
53 | } else |
54 | llvm_unreachable("unimplemented type of node" ); |
55 | return !IList.empty(); |
56 | } |
57 | |
58 | raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode::NodeKind K) { |
59 | const char *Out; |
60 | switch (K) { |
61 | case DDGNode::NodeKind::SingleInstruction: |
62 | Out = "single-instruction" ; |
63 | break; |
64 | case DDGNode::NodeKind::MultiInstruction: |
65 | Out = "multi-instruction" ; |
66 | break; |
67 | case DDGNode::NodeKind::PiBlock: |
68 | Out = "pi-block" ; |
69 | break; |
70 | case DDGNode::NodeKind::Root: |
71 | Out = "root" ; |
72 | break; |
73 | case DDGNode::NodeKind::Unknown: |
74 | Out = "?? (error)" ; |
75 | break; |
76 | } |
77 | OS << Out; |
78 | return OS; |
79 | } |
80 | |
81 | raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode &N) { |
82 | OS << "Node Address:" << &N << ":" << N.getKind() << "\n" ; |
83 | if (isa<SimpleDDGNode>(Val: N)) { |
84 | OS << " Instructions:\n" ; |
85 | for (const Instruction *I : cast<const SimpleDDGNode>(Val: N).getInstructions()) |
86 | OS.indent(NumSpaces: 2) << *I << "\n" ; |
87 | } else if (isa<PiBlockDDGNode>(Val: &N)) { |
88 | OS << "--- start of nodes in pi-block ---\n" ; |
89 | auto &Nodes = cast<const PiBlockDDGNode>(Val: &N)->getNodes(); |
90 | unsigned Count = 0; |
91 | for (const DDGNode *N : Nodes) |
92 | OS << *N << (++Count == Nodes.size() ? "" : "\n" ); |
93 | OS << "--- end of nodes in pi-block ---\n" ; |
94 | } else if (!isa<RootDDGNode>(Val: N)) |
95 | llvm_unreachable("unimplemented type of node" ); |
96 | |
97 | OS << (N.getEdges().empty() ? " Edges:none!\n" : " Edges:\n" ); |
98 | for (const auto &E : N.getEdges()) |
99 | OS.indent(NumSpaces: 2) << *E; |
100 | return OS; |
101 | } |
102 | |
103 | //===--------------------------------------------------------------------===// |
104 | // SimpleDDGNode implementation |
105 | //===--------------------------------------------------------------------===// |
106 | |
107 | SimpleDDGNode::SimpleDDGNode(Instruction &I) |
108 | : DDGNode(NodeKind::SingleInstruction) { |
109 | assert(InstList.empty() && "Expected empty list." ); |
110 | InstList.push_back(Elt: &I); |
111 | } |
112 | |
113 | SimpleDDGNode::SimpleDDGNode(const SimpleDDGNode &N) |
114 | : DDGNode(N), InstList(N.InstList) { |
115 | assert(((getKind() == NodeKind::SingleInstruction && InstList.size() == 1) || |
116 | (getKind() == NodeKind::MultiInstruction && InstList.size() > 1)) && |
117 | "constructing from invalid simple node." ); |
118 | } |
119 | |
120 | SimpleDDGNode::SimpleDDGNode(SimpleDDGNode &&N) |
121 | : DDGNode(std::move(N)), InstList(std::move(N.InstList)) { |
122 | assert(((getKind() == NodeKind::SingleInstruction && InstList.size() == 1) || |
123 | (getKind() == NodeKind::MultiInstruction && InstList.size() > 1)) && |
124 | "constructing from invalid simple node." ); |
125 | } |
126 | |
127 | SimpleDDGNode::~SimpleDDGNode() { InstList.clear(); } |
128 | |
129 | //===--------------------------------------------------------------------===// |
130 | // PiBlockDDGNode implementation |
131 | //===--------------------------------------------------------------------===// |
132 | |
133 | PiBlockDDGNode::PiBlockDDGNode(const PiNodeList &List) |
134 | : DDGNode(NodeKind::PiBlock), NodeList(List) { |
135 | assert(!NodeList.empty() && "pi-block node constructed with an empty list." ); |
136 | } |
137 | |
138 | PiBlockDDGNode::PiBlockDDGNode(const PiBlockDDGNode &N) |
139 | : DDGNode(N), NodeList(N.NodeList) { |
140 | assert(getKind() == NodeKind::PiBlock && !NodeList.empty() && |
141 | "constructing from invalid pi-block node." ); |
142 | } |
143 | |
144 | PiBlockDDGNode::PiBlockDDGNode(PiBlockDDGNode &&N) |
145 | : DDGNode(std::move(N)), NodeList(std::move(N.NodeList)) { |
146 | assert(getKind() == NodeKind::PiBlock && !NodeList.empty() && |
147 | "constructing from invalid pi-block node." ); |
148 | } |
149 | |
150 | PiBlockDDGNode::~PiBlockDDGNode() { NodeList.clear(); } |
151 | |
152 | //===--------------------------------------------------------------------===// |
153 | // DDGEdge implementation |
154 | //===--------------------------------------------------------------------===// |
155 | |
156 | raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K) { |
157 | const char *Out; |
158 | switch (K) { |
159 | case DDGEdge::EdgeKind::RegisterDefUse: |
160 | Out = "def-use" ; |
161 | break; |
162 | case DDGEdge::EdgeKind::MemoryDependence: |
163 | Out = "memory" ; |
164 | break; |
165 | case DDGEdge::EdgeKind::Rooted: |
166 | Out = "rooted" ; |
167 | break; |
168 | case DDGEdge::EdgeKind::Unknown: |
169 | Out = "?? (error)" ; |
170 | break; |
171 | } |
172 | OS << Out; |
173 | return OS; |
174 | } |
175 | |
176 | raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge &E) { |
177 | OS << "[" << E.getKind() << "] to " << &E.getTargetNode() << "\n" ; |
178 | return OS; |
179 | } |
180 | |
181 | //===--------------------------------------------------------------------===// |
182 | // DataDependenceGraph implementation |
183 | //===--------------------------------------------------------------------===// |
184 | using BasicBlockListType = SmallVector<BasicBlock *, 8>; |
185 | |
186 | DataDependenceGraph::DataDependenceGraph(Function &F, DependenceInfo &D) |
187 | : DependenceGraphInfo(F.getName().str(), D) { |
188 | // Put the basic blocks in program order for correct dependence |
189 | // directions. |
190 | BasicBlockListType BBList; |
191 | for (const auto &SCC : make_range(x: scc_begin(G: &F), y: scc_end(G: &F))) |
192 | append_range(C&: BBList, R: SCC); |
193 | std::reverse(first: BBList.begin(), last: BBList.end()); |
194 | DDGBuilder(*this, D, BBList).populate(); |
195 | } |
196 | |
197 | DataDependenceGraph::DataDependenceGraph(Loop &L, LoopInfo &LI, |
198 | DependenceInfo &D) |
199 | : DependenceGraphInfo(Twine(L.getHeader()->getParent()->getName() + "." + |
200 | L.getHeader()->getName()) |
201 | .str(), |
202 | D) { |
203 | // Put the basic blocks in program order for correct dependence |
204 | // directions. |
205 | LoopBlocksDFS DFS(&L); |
206 | DFS.perform(LI: &LI); |
207 | BasicBlockListType BBList; |
208 | append_range(C&: BBList, R: make_range(x: DFS.beginRPO(), y: DFS.endRPO())); |
209 | DDGBuilder(*this, D, BBList).populate(); |
210 | } |
211 | |
212 | DataDependenceGraph::~DataDependenceGraph() { |
213 | for (auto *N : Nodes) { |
214 | for (auto *E : *N) |
215 | delete E; |
216 | delete N; |
217 | } |
218 | } |
219 | |
220 | bool DataDependenceGraph::addNode(DDGNode &N) { |
221 | if (!DDGBase::addNode(N)) |
222 | return false; |
223 | |
224 | // In general, if the root node is already created and linked, it is not safe |
225 | // to add new nodes since they may be unreachable by the root. However, |
226 | // pi-block nodes need to be added after the root node is linked, and they are |
227 | // always reachable by the root, because they represent components that are |
228 | // already reachable by root. |
229 | auto *Pi = dyn_cast<PiBlockDDGNode>(Val: &N); |
230 | assert((!Root || Pi) && |
231 | "Root node is already added. No more nodes can be added." ); |
232 | |
233 | if (isa<RootDDGNode>(Val: N)) |
234 | Root = &N; |
235 | |
236 | if (Pi) |
237 | for (DDGNode *NI : Pi->getNodes()) |
238 | PiBlockMap.insert(KV: std::make_pair(x&: NI, y&: Pi)); |
239 | |
240 | return true; |
241 | } |
242 | |
243 | const PiBlockDDGNode *DataDependenceGraph::getPiBlock(const NodeType &N) const { |
244 | if (!PiBlockMap.contains(Val: &N)) |
245 | return nullptr; |
246 | auto *Pi = PiBlockMap.find(Val: &N)->second; |
247 | assert(!PiBlockMap.contains(Pi) && "Nested pi-blocks detected." ); |
248 | return Pi; |
249 | } |
250 | |
251 | raw_ostream &llvm::operator<<(raw_ostream &OS, const DataDependenceGraph &G) { |
252 | for (DDGNode *Node : G) |
253 | // Avoid printing nodes that are part of a pi-block twice. They will get |
254 | // printed when the pi-block is printed. |
255 | if (!G.getPiBlock(N: *Node)) |
256 | OS << *Node << "\n" ; |
257 | OS << "\n" ; |
258 | return OS; |
259 | } |
260 | |
261 | //===--------------------------------------------------------------------===// |
262 | // DDGBuilder implementation |
263 | //===--------------------------------------------------------------------===// |
264 | |
265 | bool DDGBuilder::areNodesMergeable(const DDGNode &Src, |
266 | const DDGNode &Tgt) const { |
267 | // Only merge two nodes if they are both simple nodes and the consecutive |
268 | // instructions after merging belong to the same BB. |
269 | const auto *SimpleSrc = dyn_cast<const SimpleDDGNode>(Val: &Src); |
270 | const auto *SimpleTgt = dyn_cast<const SimpleDDGNode>(Val: &Tgt); |
271 | if (!SimpleSrc || !SimpleTgt) |
272 | return false; |
273 | |
274 | return SimpleSrc->getLastInstruction()->getParent() == |
275 | SimpleTgt->getFirstInstruction()->getParent(); |
276 | } |
277 | |
278 | void DDGBuilder::mergeNodes(DDGNode &A, DDGNode &B) { |
279 | DDGEdge &EdgeToFold = A.back(); |
280 | assert(A.getEdges().size() == 1 && EdgeToFold.getTargetNode() == B && |
281 | "Expected A to have a single edge to B." ); |
282 | assert(isa<SimpleDDGNode>(&A) && isa<SimpleDDGNode>(&B) && |
283 | "Expected simple nodes" ); |
284 | |
285 | // Copy instructions from B to the end of A. |
286 | cast<SimpleDDGNode>(Val: &A)->appendInstructions(Input: *cast<SimpleDDGNode>(Val: &B)); |
287 | |
288 | // Move to A any outgoing edges from B. |
289 | for (DDGEdge *BE : B) |
290 | Graph.connect(Src&: A, Dst&: BE->getTargetNode(), E&: *BE); |
291 | |
292 | A.removeEdge(E&: EdgeToFold); |
293 | destroyEdge(E&: EdgeToFold); |
294 | Graph.removeNode(N&: B); |
295 | destroyNode(N&: B); |
296 | } |
297 | |
298 | bool DDGBuilder::shouldSimplify() const { return SimplifyDDG; } |
299 | |
300 | bool DDGBuilder::shouldCreatePiBlocks() const { return CreatePiBlocks; } |
301 | |
302 | //===--------------------------------------------------------------------===// |
303 | // DDG Analysis Passes |
304 | //===--------------------------------------------------------------------===// |
305 | |
306 | /// DDG as a loop pass. |
307 | DDGAnalysis::Result DDGAnalysis::run(Loop &L, LoopAnalysisManager &AM, |
308 | LoopStandardAnalysisResults &AR) { |
309 | Function *F = L.getHeader()->getParent(); |
310 | DependenceInfo DI(F, &AR.AA, &AR.SE, &AR.LI); |
311 | return std::make_unique<DataDependenceGraph>(args&: L, args&: AR.LI, args&: DI); |
312 | } |
313 | AnalysisKey DDGAnalysis::Key; |
314 | |
315 | PreservedAnalyses DDGAnalysisPrinterPass::run(Loop &L, LoopAnalysisManager &AM, |
316 | LoopStandardAnalysisResults &AR, |
317 | LPMUpdater &U) { |
318 | OS << "'DDG' for loop '" << L.getHeader()->getName() << "':\n" ; |
319 | OS << *AM.getResult<DDGAnalysis>(IR&: L, ExtraArgs&: AR); |
320 | return PreservedAnalyses::all(); |
321 | } |
322 | |