1 //===- CFGPrinter.cpp - DOT printer for the control flow 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 // This file defines a `-dot-cfg` analysis pass, which emits the
10 // `<prefix>.<fnname>.dot` file for each function in the program, with a graph
11 // of the CFG for that function. The default value for `<prefix>` is `cfg` but
12 // can be customized as needed.
13 //
14 // The other main feature of this file is that it implements the
15 // Function::viewCFG method, which is useful for debugging passes which operate
16 // on the CFG.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "llvm/Analysis/CFGPrinter.h"
21 #include "llvm/ADT/PostOrderIterator.h"
22 #include "llvm/InitializePasses.h"
23 #include "llvm/Pass.h"
24 #include "llvm/Support/CommandLine.h"
25 #include "llvm/Support/FileSystem.h"
26 #include <algorithm>
27 
28 using namespace llvm;
29 
30 static cl::opt<std::string>
31     CFGFuncName("cfg-func-name", cl::Hidden,
32                 cl::desc("The name of a function (or its substring)"
33                          " whose CFG is viewed/printed."));
34 
35 static cl::opt<std::string> CFGDotFilenamePrefix(
36     "cfg-dot-filename-prefix", cl::Hidden,
37     cl::desc("The prefix used for the CFG dot file names."));
38 
39 static cl::opt<bool> HideUnreachablePaths("cfg-hide-unreachable-paths",
40                                           cl::init(false));
41 
42 static cl::opt<bool> HideDeoptimizePaths("cfg-hide-deoptimize-paths",
43                                          cl::init(false));
44 
45 static cl::opt<double> HideColdPaths(
46     "cfg-hide-cold-paths", cl::init(0.0),
47     cl::desc("Hide blocks with relative frequency below the given value"));
48 
49 static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),
50                                     cl::Hidden,
51                                     cl::desc("Show heat colors in CFG"));
52 
53 static cl::opt<bool> UseRawEdgeWeight("cfg-raw-weights", cl::init(false),
54                                       cl::Hidden,
55                                       cl::desc("Use raw weights for labels. "
56                                                "Use percentages as default."));
57 
58 static cl::opt<bool>
59     ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,
60                    cl::desc("Show edges labeled with weights"));
61 
62 static void writeCFGToDotFile(Function &F, BlockFrequencyInfo *BFI,
63                               BranchProbabilityInfo *BPI, uint64_t MaxFreq,
64                               bool CFGOnly = false) {
65   std::string Filename =
66       (CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();
67   errs() << "Writing '" << Filename << "'...";
68 
69   std::error_code EC;
70   raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);
71 
72   DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
73   CFGInfo.setHeatColors(ShowHeatColors);
74   CFGInfo.setEdgeWeights(ShowEdgeWeight);
75   CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
76 
77   if (!EC)
78     WriteGraph(File, &CFGInfo, CFGOnly);
79   else
80     errs() << "  error opening file for writing!";
81   errs() << "\n";
82 }
83 
84 static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,
85                     const BranchProbabilityInfo *BPI, uint64_t MaxFreq,
86                     bool CFGOnly = false) {
87   DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
88   CFGInfo.setHeatColors(ShowHeatColors);
89   CFGInfo.setEdgeWeights(ShowEdgeWeight);
90   CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
91 
92   ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly);
93 }
94 
95 namespace {
96 struct CFGViewerLegacyPass : public FunctionPass {
97   static char ID; // Pass identifcation, replacement for typeid
98   CFGViewerLegacyPass() : FunctionPass(ID) {
99     initializeCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry());
100   }
101 
102   bool runOnFunction(Function &F) override {
103     auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
104     auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
105     viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));
106     return false;
107   }
108 
109   void print(raw_ostream &OS, const Module * = nullptr) const override {}
110 
111   void getAnalysisUsage(AnalysisUsage &AU) const override {
112     FunctionPass::getAnalysisUsage(AU);
113     AU.addRequired<BlockFrequencyInfoWrapperPass>();
114     AU.addRequired<BranchProbabilityInfoWrapperPass>();
115     AU.setPreservesAll();
116   }
117 };
118 }
119 
120 char CFGViewerLegacyPass::ID = 0;
121 INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false,
122                 true)
123 
124 PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) {
125   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
126   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
127   viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));
128   return PreservedAnalyses::all();
129 }
130 
131 namespace {
132 struct CFGOnlyViewerLegacyPass : public FunctionPass {
133   static char ID; // Pass identifcation, replacement for typeid
134   CFGOnlyViewerLegacyPass() : FunctionPass(ID) {
135     initializeCFGOnlyViewerLegacyPassPass(*PassRegistry::getPassRegistry());
136   }
137 
138   bool runOnFunction(Function &F) override {
139     auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
140     auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
141     viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
142     return false;
143   }
144 
145   void print(raw_ostream &OS, const Module * = nullptr) const override {}
146 
147   void getAnalysisUsage(AnalysisUsage &AU) const override {
148     FunctionPass::getAnalysisUsage(AU);
149     AU.addRequired<BlockFrequencyInfoWrapperPass>();
150     AU.addRequired<BranchProbabilityInfoWrapperPass>();
151     AU.setPreservesAll();
152   }
153 };
154 }
155 
156 char CFGOnlyViewerLegacyPass::ID = 0;
157 INITIALIZE_PASS(CFGOnlyViewerLegacyPass, "view-cfg-only",
158                 "View CFG of function (with no function bodies)", false, true)
159 
160 PreservedAnalyses CFGOnlyViewerPass::run(Function &F,
161                                          FunctionAnalysisManager &AM) {
162   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
163   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
164   viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
165   return PreservedAnalyses::all();
166 }
167 
168 namespace {
169 struct CFGPrinterLegacyPass : public FunctionPass {
170   static char ID; // Pass identification, replacement for typeid
171   CFGPrinterLegacyPass() : FunctionPass(ID) {
172     initializeCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
173   }
174 
175   bool runOnFunction(Function &F) override {
176     auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
177     auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
178     writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));
179     return false;
180   }
181 
182   void print(raw_ostream &OS, const Module * = nullptr) const override {}
183 
184   void getAnalysisUsage(AnalysisUsage &AU) const override {
185     FunctionPass::getAnalysisUsage(AU);
186     AU.addRequired<BlockFrequencyInfoWrapperPass>();
187     AU.addRequired<BranchProbabilityInfoWrapperPass>();
188     AU.setPreservesAll();
189   }
190 };
191 }
192 
193 char CFGPrinterLegacyPass::ID = 0;
194 INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg",
195                 "Print CFG of function to 'dot' file", false, true)
196 
197 PreservedAnalyses CFGPrinterPass::run(Function &F,
198                                       FunctionAnalysisManager &AM) {
199   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
200   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
201   writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));
202   return PreservedAnalyses::all();
203 }
204 
205 namespace {
206 struct CFGOnlyPrinterLegacyPass : public FunctionPass {
207   static char ID; // Pass identification, replacement for typeid
208   CFGOnlyPrinterLegacyPass() : FunctionPass(ID) {
209     initializeCFGOnlyPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
210   }
211 
212   bool runOnFunction(Function &F) override {
213     auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
214     auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
215     writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
216     return false;
217   }
218   void print(raw_ostream &OS, const Module * = nullptr) const override {}
219 
220   void getAnalysisUsage(AnalysisUsage &AU) const override {
221     FunctionPass::getAnalysisUsage(AU);
222     AU.addRequired<BlockFrequencyInfoWrapperPass>();
223     AU.addRequired<BranchProbabilityInfoWrapperPass>();
224     AU.setPreservesAll();
225   }
226 };
227 }
228 
229 char CFGOnlyPrinterLegacyPass::ID = 0;
230 INITIALIZE_PASS(CFGOnlyPrinterLegacyPass, "dot-cfg-only",
231                 "Print CFG of function to 'dot' file (with no function bodies)",
232                 false, true)
233 
234 PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,
235                                           FunctionAnalysisManager &AM) {
236   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
237   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
238   writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
239   return PreservedAnalyses::all();
240 }
241 
242 /// viewCFG - This function is meant for use from the debugger.  You can just
243 /// say 'call F->viewCFG()' and a ghostview window should pop up from the
244 /// program, displaying the CFG of the current function.  This depends on there
245 /// being a 'dot' and 'gv' program in your path.
246 ///
247 void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }
248 
249 void Function::viewCFG(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,
250                        const BranchProbabilityInfo *BPI) const {
251   if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
252     return;
253   DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);
254   ViewGraph(&CFGInfo, "cfg" + getName(), ViewCFGOnly);
255 }
256 
257 /// viewCFGOnly - This function is meant for use from the debugger.  It works
258 /// just like viewCFG, but it does not include the contents of basic blocks
259 /// into the nodes, just the label.  If you are only interested in the CFG
260 /// this can make the graph smaller.
261 ///
262 void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }
263 
264 void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,
265                            const BranchProbabilityInfo *BPI) const {
266   viewCFG(true, BFI, BPI);
267 }
268 
269 FunctionPass *llvm::createCFGPrinterLegacyPassPass() {
270   return new CFGPrinterLegacyPass();
271 }
272 
273 FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass() {
274   return new CFGOnlyPrinterLegacyPass();
275 }
276 
277 /// Find all blocks on the paths which terminate with a deoptimize or
278 /// unreachable (i.e. all blocks which are post-dominated by a deoptimize
279 /// or unreachable). These paths are hidden if the corresponding cl::opts
280 /// are enabled.
281 void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(
282     const Function *F) {
283   auto evaluateBB = [&](const BasicBlock *Node) {
284     if (succ_empty(Node)) {
285       const Instruction *TI = Node->getTerminator();
286       isOnDeoptOrUnreachablePath[Node] =
287           (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||
288           (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());
289       return;
290     }
291     isOnDeoptOrUnreachablePath[Node] =
292         llvm::all_of(successors(Node), [this](const BasicBlock *BB) {
293           return isOnDeoptOrUnreachablePath[BB];
294         });
295   };
296   /// The post order traversal iteration is done to know the status of
297   /// isOnDeoptOrUnreachablePath for all the successors on the current BB.
298   llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
299 }
300 
301 bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
302                                                  const DOTFuncInfo *CFGInfo) {
303   if (HideColdPaths.getNumOccurrences() > 0)
304     if (auto *BFI = CFGInfo->getBFI()) {
305       uint64_t NodeFreq = BFI->getBlockFreq(Node).getFrequency();
306       uint64_t EntryFreq = BFI->getEntryFreq();
307       // Hide blocks with relative frequency below HideColdPaths threshold.
308       if ((double)NodeFreq / EntryFreq < HideColdPaths)
309         return true;
310     }
311   if (HideUnreachablePaths || HideDeoptimizePaths) {
312     if (isOnDeoptOrUnreachablePath.find(Node) ==
313         isOnDeoptOrUnreachablePath.end())
314       computeDeoptOrUnreachablePaths(Node->getParent());
315     return isOnDeoptOrUnreachablePath[Node];
316   }
317   return false;
318 }
319