1 | //===-- SelectionDAGPrinter.cpp - Implement SelectionDAG::viewGraph() -----===// |
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 implements the SelectionDAG::viewGraph method. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "ScheduleDAGSDNodes.h" |
14 | #include "llvm/ADT/DenseSet.h" |
15 | #include "llvm/ADT/StringExtras.h" |
16 | #include "llvm/CodeGen/MachineFunction.h" |
17 | #include "llvm/CodeGen/SelectionDAG.h" |
18 | #include "llvm/Support/Debug.h" |
19 | #include "llvm/Support/GraphWriter.h" |
20 | #include "llvm/Support/raw_ostream.h" |
21 | using namespace llvm; |
22 | |
23 | #define DEBUG_TYPE "dag-printer" |
24 | |
25 | namespace llvm { |
26 | template<> |
27 | struct DOTGraphTraits<SelectionDAG*> : public DefaultDOTGraphTraits { |
28 | |
29 | explicit DOTGraphTraits(bool isSimple=false) : |
30 | DefaultDOTGraphTraits(isSimple) {} |
31 | |
32 | static bool hasEdgeDestLabels() { |
33 | return true; |
34 | } |
35 | |
36 | static unsigned numEdgeDestLabels(const void *Node) { |
37 | return ((const SDNode *) Node)->getNumValues(); |
38 | } |
39 | |
40 | static std::string getEdgeDestLabel(const void *Node, unsigned i) { |
41 | return ((const SDNode *) Node)->getValueType(ResNo: i).getEVTString(); |
42 | } |
43 | |
44 | template<typename EdgeIter> |
45 | static std::string getEdgeSourceLabel(const void *Node, EdgeIter I) { |
46 | return itostr(I - SDNodeIterator::begin(N: (const SDNode *) Node)); |
47 | } |
48 | |
49 | /// edgeTargetsEdgeSource - This method returns true if this outgoing edge |
50 | /// should actually target another edge source, not a node. If this method |
51 | /// is implemented, getEdgeTarget should be implemented. |
52 | template<typename EdgeIter> |
53 | static bool edgeTargetsEdgeSource(const void *Node, EdgeIter I) { |
54 | return true; |
55 | } |
56 | |
57 | /// getEdgeTarget - If edgeTargetsEdgeSource returns true, this method is |
58 | /// called to determine which outgoing edge of Node is the target of this |
59 | /// edge. |
60 | template<typename EdgeIter> |
61 | static EdgeIter getEdgeTarget(const void *Node, EdgeIter I) { |
62 | SDNode *TargetNode = *I; |
63 | SDNodeIterator NI = SDNodeIterator::begin(N: TargetNode); |
64 | std::advance(NI, I.getNode()->getOperand(I.getOperand()).getResNo()); |
65 | return NI; |
66 | } |
67 | |
68 | static std::string getGraphName(const SelectionDAG *G) { |
69 | return std::string(G->getMachineFunction().getName()); |
70 | } |
71 | |
72 | static bool renderGraphFromBottomUp() { |
73 | return true; |
74 | } |
75 | |
76 | static std::string getNodeIdentifierLabel(const SDNode *Node, |
77 | const SelectionDAG *Graph) { |
78 | std::string R; |
79 | raw_string_ostream OS(R); |
80 | #ifndef NDEBUG |
81 | OS << 't' << Node->PersistentId; |
82 | #else |
83 | OS << static_cast<const void *>(Node); |
84 | #endif |
85 | return R; |
86 | } |
87 | |
88 | /// If you want to override the dot attributes printed for a particular |
89 | /// edge, override this method. |
90 | template<typename EdgeIter> |
91 | static std::string getEdgeAttributes(const void *Node, EdgeIter EI, |
92 | const SelectionDAG *Graph) { |
93 | SDValue Op = EI.getNode()->getOperand(EI.getOperand()); |
94 | EVT VT = Op.getValueType(); |
95 | if (VT == MVT::Glue) |
96 | return "color=red,style=bold" ; |
97 | else if (VT == MVT::Other) |
98 | return "color=blue,style=dashed" ; |
99 | return "" ; |
100 | } |
101 | |
102 | |
103 | static std::string getSimpleNodeLabel(const SDNode *Node, |
104 | const SelectionDAG *G) { |
105 | std::string Result = Node->getOperationName(G); |
106 | { |
107 | raw_string_ostream OS(Result); |
108 | Node->print_details(OS, G); |
109 | } |
110 | return Result; |
111 | } |
112 | std::string getNodeLabel(const SDNode *Node, const SelectionDAG *Graph); |
113 | static std::string getNodeAttributes(const SDNode *N, |
114 | const SelectionDAG *Graph) { |
115 | #ifndef NDEBUG |
116 | const std::string &Attrs = Graph->getGraphAttrs(N); |
117 | if (!Attrs.empty()) { |
118 | if (Attrs.find("shape=" ) == std::string::npos) |
119 | return std::string("shape=Mrecord," ) + Attrs; |
120 | else |
121 | return Attrs; |
122 | } |
123 | #endif |
124 | return "shape=Mrecord" ; |
125 | } |
126 | |
127 | static void addCustomGraphFeatures(SelectionDAG *G, |
128 | GraphWriter<SelectionDAG*> &GW) { |
129 | GW.emitSimpleNode(ID: nullptr, Attr: "plaintext=circle" , Label: "GraphRoot" ); |
130 | if (G->getRoot().getNode()) |
131 | GW.emitEdge(SrcNodeID: nullptr, SrcNodePort: -1, DestNodeID: G->getRoot().getNode(), DestNodePort: G->getRoot().getResNo(), |
132 | Attrs: "color=blue,style=dashed" ); |
133 | } |
134 | }; |
135 | } |
136 | |
137 | std::string DOTGraphTraits<SelectionDAG*>::getNodeLabel(const SDNode *Node, |
138 | const SelectionDAG *G) { |
139 | return DOTGraphTraits<SelectionDAG*>::getSimpleNodeLabel(Node, G); |
140 | } |
141 | |
142 | |
143 | /// viewGraph - Pop up a ghostview window with the reachable parts of the DAG |
144 | /// rendered using 'dot'. |
145 | /// |
146 | void SelectionDAG::viewGraph(const std::string &Title) { |
147 | // This code is only for debugging! |
148 | #ifndef NDEBUG |
149 | ViewGraph(this, "dag." + getMachineFunction().getName(), |
150 | false, Title); |
151 | #else |
152 | errs() << "SelectionDAG::viewGraph is only available in debug builds on " |
153 | << "systems with Graphviz or gv!\n" ; |
154 | #endif // NDEBUG |
155 | } |
156 | |
157 | // This overload is defined out-of-line here instead of just using a |
158 | // default parameter because this is easiest for gdb to call. |
159 | void SelectionDAG::viewGraph() { |
160 | viewGraph(Title: "" ); |
161 | } |
162 | |
163 | /// Just dump dot graph to a user-provided path and title. |
164 | /// This doesn't open the dot viewer program and |
165 | /// helps visualization when outside debugging session. |
166 | /// FileName expects absolute path. If provided |
167 | /// without any path separators then the file |
168 | /// will be created in the current directory. |
169 | /// Error will be emitted if the path is insane. |
170 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
171 | LLVM_DUMP_METHOD void SelectionDAG::dumpDotGraph(const Twine &FileName, |
172 | const Twine &Title) { |
173 | dumpDotGraphToFile(this, FileName, Title); |
174 | } |
175 | #endif |
176 | |
177 | /// clearGraphAttrs - Clear all previously defined node graph attributes. |
178 | /// Intended to be used from a debugging tool (eg. gdb). |
179 | void SelectionDAG::clearGraphAttrs() { |
180 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS |
181 | NodeGraphAttrs.clear(); |
182 | #else |
183 | errs() << "SelectionDAG::clearGraphAttrs is only available in builds with " |
184 | << "ABI breaking checks enabled on systems with Graphviz or gv!\n" ; |
185 | #endif |
186 | } |
187 | |
188 | |
189 | /// setGraphAttrs - Set graph attributes for a node. (eg. "color=red".) |
190 | /// |
191 | void SelectionDAG::setGraphAttrs(const SDNode *N, const char *Attrs) { |
192 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS |
193 | NodeGraphAttrs[N] = Attrs; |
194 | #else |
195 | errs() << "SelectionDAG::setGraphAttrs is only available in builds with " |
196 | << "ABI breaking checks enabled on systems with Graphviz or gv!\n" ; |
197 | #endif |
198 | } |
199 | |
200 | |
201 | /// getGraphAttrs - Get graph attributes for a node. (eg. "color=red".) |
202 | /// Used from getNodeAttributes. |
203 | std::string SelectionDAG::getGraphAttrs(const SDNode *N) const { |
204 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS |
205 | std::map<const SDNode *, std::string>::const_iterator I = |
206 | NodeGraphAttrs.find(N); |
207 | |
208 | if (I != NodeGraphAttrs.end()) |
209 | return I->second; |
210 | else |
211 | return "" ; |
212 | #else |
213 | errs() << "SelectionDAG::getGraphAttrs is only available in builds with " |
214 | << "ABI breaking checks enabled on systems with Graphviz or gv!\n" ; |
215 | return std::string(); |
216 | #endif |
217 | } |
218 | |
219 | /// setGraphColor - Convenience for setting node color attribute. |
220 | /// |
221 | void SelectionDAG::setGraphColor(const SDNode *N, const char *Color) { |
222 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS |
223 | NodeGraphAttrs[N] = std::string("color=" ) + Color; |
224 | #else |
225 | errs() << "SelectionDAG::setGraphColor is only available in builds with " |
226 | << "ABI breaking checks enabled on systems with Graphviz or gv!\n" ; |
227 | #endif |
228 | } |
229 | |
230 | /// setSubgraphColorHelper - Implement setSubgraphColor. Return |
231 | /// whether we truncated the search. |
232 | /// |
233 | bool SelectionDAG::setSubgraphColorHelper(SDNode *N, const char *Color, DenseSet<SDNode *> &visited, |
234 | int level, bool &printed) { |
235 | bool hit_limit = false; |
236 | |
237 | #ifndef NDEBUG |
238 | if (level >= 20) { |
239 | if (!printed) { |
240 | printed = true; |
241 | LLVM_DEBUG(dbgs() << "setSubgraphColor hit max level\n" ); |
242 | } |
243 | return true; |
244 | } |
245 | |
246 | unsigned oldSize = visited.size(); |
247 | visited.insert(N); |
248 | if (visited.size() != oldSize) { |
249 | setGraphColor(N, Color); |
250 | for(SDNodeIterator i = SDNodeIterator::begin(N), iend = SDNodeIterator::end(N); |
251 | i != iend; |
252 | ++i) { |
253 | hit_limit = setSubgraphColorHelper(*i, Color, visited, level+1, printed) || hit_limit; |
254 | } |
255 | } |
256 | #else |
257 | errs() << "SelectionDAG::setSubgraphColor is only available in debug builds" |
258 | << " on systems with Graphviz or gv!\n" ; |
259 | #endif |
260 | return hit_limit; |
261 | } |
262 | |
263 | /// setSubgraphColor - Convenience for setting subgraph color attribute. |
264 | /// |
265 | void SelectionDAG::setSubgraphColor(SDNode *N, const char *Color) { |
266 | #ifndef NDEBUG |
267 | DenseSet<SDNode *> visited; |
268 | bool printed = false; |
269 | if (setSubgraphColorHelper(N, Color, visited, 0, printed)) { |
270 | // Visually mark that we hit the limit |
271 | if (strcmp(Color, "red" ) == 0) { |
272 | setSubgraphColorHelper(N, "blue" , visited, 0, printed); |
273 | } else if (strcmp(Color, "yellow" ) == 0) { |
274 | setSubgraphColorHelper(N, "green" , visited, 0, printed); |
275 | } |
276 | } |
277 | |
278 | #else |
279 | errs() << "SelectionDAG::setSubgraphColor is only available in debug builds" |
280 | << " on systems with Graphviz or gv!\n" ; |
281 | #endif |
282 | } |
283 | |
284 | std::string ScheduleDAGSDNodes::getGraphNodeLabel(const SUnit *SU) const { |
285 | std::string s; |
286 | raw_string_ostream O(s); |
287 | O << "SU(" << SU->NodeNum << "): " ; |
288 | if (SU->getNode()) { |
289 | SmallVector<SDNode *, 4> GluedNodes; |
290 | for (SDNode *N = SU->getNode(); N; N = N->getGluedNode()) |
291 | GluedNodes.push_back(Elt: N); |
292 | while (!GluedNodes.empty()) { |
293 | O << DOTGraphTraits<SelectionDAG*> |
294 | ::getSimpleNodeLabel(Node: GluedNodes.back(), G: DAG); |
295 | GluedNodes.pop_back(); |
296 | if (!GluedNodes.empty()) |
297 | O << "\n " ; |
298 | } |
299 | } else { |
300 | O << "CROSS RC COPY" ; |
301 | } |
302 | return s; |
303 | } |
304 | |
305 | void ScheduleDAGSDNodes::getCustomGraphFeatures(GraphWriter<ScheduleDAG*> &GW) const { |
306 | if (DAG) { |
307 | // Draw a special "GraphRoot" node to indicate the root of the graph. |
308 | GW.emitSimpleNode(ID: nullptr, Attr: "plaintext=circle" , Label: "GraphRoot" ); |
309 | const SDNode *N = DAG->getRoot().getNode(); |
310 | if (N && N->getNodeId() != -1) |
311 | GW.emitEdge(SrcNodeID: nullptr, SrcNodePort: -1, DestNodeID: &SUnits[N->getNodeId()], DestNodePort: -1, |
312 | Attrs: "color=blue,style=dashed" ); |
313 | } |
314 | } |
315 | |