1 | //===- RegionPrinter.cpp - Print regions tree pass ------------------------===// |
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 | // Print out the region tree of a function using dotty/graphviz. |
9 | //===----------------------------------------------------------------------===// |
10 | |
11 | #include "llvm/Analysis/RegionPrinter.h" |
12 | #include "llvm/Analysis/DOTGraphTraitsPass.h" |
13 | #include "llvm/Analysis/RegionInfo.h" |
14 | #include "llvm/Analysis/RegionIterator.h" |
15 | #include "llvm/InitializePasses.h" |
16 | #include "llvm/Support/CommandLine.h" |
17 | #include "llvm/Support/raw_ostream.h" |
18 | #ifndef NDEBUG |
19 | #include "llvm/IR/LegacyPassManager.h" |
20 | #endif |
21 | |
22 | using namespace llvm; |
23 | |
24 | //===----------------------------------------------------------------------===// |
25 | /// onlySimpleRegion - Show only the simple regions in the RegionViewer. |
26 | static cl::opt<bool> |
27 | onlySimpleRegions("only-simple-regions" , |
28 | cl::desc("Show only simple regions in the graphviz viewer" ), |
29 | cl::Hidden, |
30 | cl::init(Val: false)); |
31 | |
32 | namespace llvm { |
33 | |
34 | std::string DOTGraphTraits<RegionNode *>::getNodeLabel(RegionNode *Node, |
35 | RegionNode *Graph) { |
36 | if (!Node->isSubRegion()) { |
37 | BasicBlock *BB = Node->getNodeAs<BasicBlock>(); |
38 | |
39 | if (isSimple()) |
40 | return DOTGraphTraits<DOTFuncInfo *>::getSimpleNodeLabel(Node: BB, nullptr); |
41 | else |
42 | return DOTGraphTraits<DOTFuncInfo *>::getCompleteNodeLabel(Node: BB, nullptr); |
43 | } |
44 | |
45 | return "Not implemented" ; |
46 | } |
47 | |
48 | template <> |
49 | struct DOTGraphTraits<RegionInfo *> : public DOTGraphTraits<RegionNode *> { |
50 | |
51 | DOTGraphTraits (bool isSimple = false) |
52 | : DOTGraphTraits<RegionNode*>(isSimple) {} |
53 | |
54 | static std::string getGraphName(const RegionInfo *) { return "Region Graph" ; } |
55 | |
56 | std::string getNodeLabel(RegionNode *Node, RegionInfo *G) { |
57 | return DOTGraphTraits<RegionNode *>::getNodeLabel( |
58 | Node, Graph: reinterpret_cast<RegionNode *>(G->getTopLevelRegion())); |
59 | } |
60 | |
61 | std::string getEdgeAttributes(RegionNode *srcNode, |
62 | GraphTraits<RegionInfo *>::ChildIteratorType CI, |
63 | RegionInfo *G) { |
64 | RegionNode *destNode = *CI; |
65 | |
66 | if (srcNode->isSubRegion() || destNode->isSubRegion()) |
67 | return "" ; |
68 | |
69 | // In case of a backedge, do not use it to define the layout of the nodes. |
70 | BasicBlock *srcBB = srcNode->getNodeAs<BasicBlock>(); |
71 | BasicBlock *destBB = destNode->getNodeAs<BasicBlock>(); |
72 | |
73 | Region *R = G->getRegionFor(BB: destBB); |
74 | |
75 | while (R && R->getParent()) |
76 | if (R->getParent()->getEntry() == destBB) |
77 | R = R->getParent(); |
78 | else |
79 | break; |
80 | |
81 | if (R && R->getEntry() == destBB && R->contains(BB: srcBB)) |
82 | return "constraint=false" ; |
83 | |
84 | return "" ; |
85 | } |
86 | |
87 | // Print the cluster of the subregions. This groups the single basic blocks |
88 | // and adds a different background color for each group. |
89 | static void printRegionCluster(const Region &R, GraphWriter<RegionInfo *> &GW, |
90 | unsigned depth = 0) { |
91 | raw_ostream &O = GW.getOStream(); |
92 | O.indent(NumSpaces: 2 * depth) << "subgraph cluster_" << static_cast<const void*>(&R) |
93 | << " {\n" ; |
94 | O.indent(NumSpaces: 2 * (depth + 1)) << "label = \"\";\n" ; |
95 | |
96 | if (!onlySimpleRegions || R.isSimple()) { |
97 | O.indent(NumSpaces: 2 * (depth + 1)) << "style = filled;\n" ; |
98 | O.indent(NumSpaces: 2 * (depth + 1)) << "color = " |
99 | << ((R.getDepth() * 2 % 12) + 1) << "\n" ; |
100 | |
101 | } else { |
102 | O.indent(NumSpaces: 2 * (depth + 1)) << "style = solid;\n" ; |
103 | O.indent(NumSpaces: 2 * (depth + 1)) << "color = " |
104 | << ((R.getDepth() * 2 % 12) + 2) << "\n" ; |
105 | } |
106 | |
107 | for (const auto &RI : R) |
108 | printRegionCluster(R: *RI, GW, depth: depth + 1); |
109 | |
110 | const RegionInfo &RI = *static_cast<const RegionInfo*>(R.getRegionInfo()); |
111 | |
112 | for (auto *BB : R.blocks()) |
113 | if (RI.getRegionFor(BB) == &R) |
114 | O.indent(NumSpaces: 2 * (depth + 1)) << "Node" |
115 | << static_cast<const void*>(RI.getTopLevelRegion()->getBBNode(BB)) |
116 | << ";\n" ; |
117 | |
118 | O.indent(NumSpaces: 2 * depth) << "}\n" ; |
119 | } |
120 | |
121 | static void addCustomGraphFeatures(const RegionInfo *G, |
122 | GraphWriter<RegionInfo *> &GW) { |
123 | raw_ostream &O = GW.getOStream(); |
124 | O << "\tcolorscheme = \"paired12\"\n" ; |
125 | printRegionCluster(R: *G->getTopLevelRegion(), GW, depth: 4); |
126 | } |
127 | }; |
128 | } // end namespace llvm |
129 | |
130 | namespace { |
131 | |
132 | struct RegionInfoPassGraphTraits { |
133 | static RegionInfo *getGraph(RegionInfoPass *RIP) { |
134 | return &RIP->getRegionInfo(); |
135 | } |
136 | }; |
137 | |
138 | struct RegionPrinter |
139 | : public DOTGraphTraitsPrinterWrapperPass< |
140 | RegionInfoPass, false, RegionInfo *, RegionInfoPassGraphTraits> { |
141 | static char ID; |
142 | RegionPrinter() |
143 | : DOTGraphTraitsPrinterWrapperPass<RegionInfoPass, false, RegionInfo *, |
144 | RegionInfoPassGraphTraits>("reg" , ID) { |
145 | initializeRegionPrinterPass(*PassRegistry::getPassRegistry()); |
146 | } |
147 | }; |
148 | char RegionPrinter::ID = 0; |
149 | |
150 | struct RegionOnlyPrinter |
151 | : public DOTGraphTraitsPrinterWrapperPass< |
152 | RegionInfoPass, true, RegionInfo *, RegionInfoPassGraphTraits> { |
153 | static char ID; |
154 | RegionOnlyPrinter() |
155 | : DOTGraphTraitsPrinterWrapperPass<RegionInfoPass, true, RegionInfo *, |
156 | RegionInfoPassGraphTraits>("reg" , ID) { |
157 | initializeRegionOnlyPrinterPass(*PassRegistry::getPassRegistry()); |
158 | } |
159 | }; |
160 | char RegionOnlyPrinter::ID = 0; |
161 | |
162 | struct RegionViewer |
163 | : public DOTGraphTraitsViewerWrapperPass< |
164 | RegionInfoPass, false, RegionInfo *, RegionInfoPassGraphTraits> { |
165 | static char ID; |
166 | RegionViewer() |
167 | : DOTGraphTraitsViewerWrapperPass<RegionInfoPass, false, RegionInfo *, |
168 | RegionInfoPassGraphTraits>("reg" , ID) { |
169 | initializeRegionViewerPass(*PassRegistry::getPassRegistry()); |
170 | } |
171 | }; |
172 | char RegionViewer::ID = 0; |
173 | |
174 | struct RegionOnlyViewer |
175 | : public DOTGraphTraitsViewerWrapperPass<RegionInfoPass, true, RegionInfo *, |
176 | RegionInfoPassGraphTraits> { |
177 | static char ID; |
178 | RegionOnlyViewer() |
179 | : DOTGraphTraitsViewerWrapperPass<RegionInfoPass, true, RegionInfo *, |
180 | RegionInfoPassGraphTraits>("regonly" , |
181 | ID) { |
182 | initializeRegionOnlyViewerPass(*PassRegistry::getPassRegistry()); |
183 | } |
184 | }; |
185 | char RegionOnlyViewer::ID = 0; |
186 | |
187 | } //end anonymous namespace |
188 | |
189 | INITIALIZE_PASS(RegionPrinter, "dot-regions" , |
190 | "Print regions of function to 'dot' file" , true, true) |
191 | |
192 | INITIALIZE_PASS( |
193 | RegionOnlyPrinter, "dot-regions-only" , |
194 | "Print regions of function to 'dot' file (with no function bodies)" , true, |
195 | true) |
196 | |
197 | INITIALIZE_PASS(RegionViewer, "view-regions" , "View regions of function" , |
198 | true, true) |
199 | |
200 | INITIALIZE_PASS(RegionOnlyViewer, "view-regions-only" , |
201 | "View regions of function (with no function bodies)" , |
202 | true, true) |
203 | |
204 | FunctionPass *llvm::createRegionPrinterPass() { return new RegionPrinter(); } |
205 | |
206 | FunctionPass *llvm::createRegionOnlyPrinterPass() { |
207 | return new RegionOnlyPrinter(); |
208 | } |
209 | |
210 | FunctionPass* llvm::createRegionViewerPass() { |
211 | return new RegionViewer(); |
212 | } |
213 | |
214 | FunctionPass* llvm::createRegionOnlyViewerPass() { |
215 | return new RegionOnlyViewer(); |
216 | } |
217 | |
218 | #ifndef NDEBUG |
219 | static void viewRegionInfo(RegionInfo *RI, bool ShortNames) { |
220 | assert(RI && "Argument must be non-null" ); |
221 | |
222 | llvm::Function *F = RI->getTopLevelRegion()->getEntry()->getParent(); |
223 | std::string GraphName = DOTGraphTraits<RegionInfo *>::getGraphName(RI); |
224 | |
225 | llvm::ViewGraph(RI, "reg" , ShortNames, |
226 | Twine(GraphName) + " for '" + F->getName() + "' function" ); |
227 | } |
228 | |
229 | static void invokeFunctionPass(const Function *F, FunctionPass *ViewerPass) { |
230 | assert(F && "Argument must be non-null" ); |
231 | assert(!F->isDeclaration() && "Function must have an implementation" ); |
232 | |
233 | // The viewer and analysis passes do not modify anything, so we can safely |
234 | // remove the const qualifier |
235 | auto NonConstF = const_cast<Function *>(F); |
236 | |
237 | llvm::legacy::FunctionPassManager FPM(NonConstF->getParent()); |
238 | FPM.add(ViewerPass); |
239 | FPM.doInitialization(); |
240 | FPM.run(*NonConstF); |
241 | FPM.doFinalization(); |
242 | } |
243 | |
244 | void llvm::viewRegion(RegionInfo *RI) { viewRegionInfo(RI, false); } |
245 | |
246 | void llvm::viewRegion(const Function *F) { |
247 | invokeFunctionPass(F, createRegionViewerPass()); |
248 | } |
249 | |
250 | void llvm::viewRegionOnly(RegionInfo *RI) { viewRegionInfo(RI, true); } |
251 | |
252 | void llvm::viewRegionOnly(const Function *F) { |
253 | invokeFunctionPass(F, createRegionOnlyViewerPass()); |
254 | } |
255 | #endif |
256 | |