xref: /llvm-project/llvm/lib/Analysis/CFGPrinter.cpp (revision 07b6013e6f69bfdf46b9f2fa1bb4c76f9ef2376c)
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/Support/CommandLine.h"
23 #include "llvm/Support/FileSystem.h"
24 #include "llvm/Support/GraphWriter.h"
25 
26 using namespace llvm;
27 
28 static cl::opt<std::string>
29     CFGFuncName("cfg-func-name", cl::Hidden,
30                 cl::desc("The name of a function (or its substring)"
31                          " whose CFG is viewed/printed."));
32 
33 static cl::opt<std::string> CFGDotFilenamePrefix(
34     "cfg-dot-filename-prefix", cl::Hidden,
35     cl::desc("The prefix used for the CFG dot file names."));
36 
37 static cl::opt<bool> HideUnreachablePaths("cfg-hide-unreachable-paths",
38                                           cl::init(false));
39 
40 static cl::opt<bool> HideDeoptimizePaths("cfg-hide-deoptimize-paths",
41                                          cl::init(false));
42 
43 static cl::opt<double> HideColdPaths(
44     "cfg-hide-cold-paths", cl::init(0.0),
45     cl::desc("Hide blocks with relative frequency below the given value"));
46 
47 static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),
48                                     cl::Hidden,
49                                     cl::desc("Show heat colors in CFG"));
50 
51 static cl::opt<bool> UseRawEdgeWeight("cfg-raw-weights", cl::init(false),
52                                       cl::Hidden,
53                                       cl::desc("Use raw weights for labels. "
54                                                "Use percentages as default."));
55 
56 static cl::opt<bool>
57     ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,
58                    cl::desc("Show edges labeled with weights"));
59 
60 static void writeCFGToDotFile(Function &F, BlockFrequencyInfo *BFI,
61                               BranchProbabilityInfo *BPI, uint64_t MaxFreq,
62                               bool CFGOnly = false) {
63   std::string Filename =
64       (CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();
65   errs() << "Writing '" << Filename << "'...";
66 
67   std::error_code EC;
68   raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);
69 
70   DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
71   CFGInfo.setHeatColors(ShowHeatColors);
72   CFGInfo.setEdgeWeights(ShowEdgeWeight);
73   CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
74 
75   if (!EC)
76     WriteGraph(File, &CFGInfo, CFGOnly);
77   else
78     errs() << "  error opening file for writing!";
79   errs() << "\n";
80 }
81 
82 static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,
83                     const BranchProbabilityInfo *BPI, uint64_t MaxFreq,
84                     bool CFGOnly = false) {
85   DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
86   CFGInfo.setHeatColors(ShowHeatColors);
87   CFGInfo.setEdgeWeights(ShowEdgeWeight);
88   CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
89 
90   ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly);
91 }
92 
93 PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) {
94   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
95     return PreservedAnalyses::all();
96   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
97   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
98   viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));
99   return PreservedAnalyses::all();
100 }
101 
102 PreservedAnalyses CFGOnlyViewerPass::run(Function &F,
103                                          FunctionAnalysisManager &AM) {
104   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
105     return PreservedAnalyses::all();
106   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
107   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
108   viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
109   return PreservedAnalyses::all();
110 }
111 
112 PreservedAnalyses CFGPrinterPass::run(Function &F,
113                                       FunctionAnalysisManager &AM) {
114   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
115     return PreservedAnalyses::all();
116   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
117   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
118   writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));
119   return PreservedAnalyses::all();
120 }
121 
122 PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,
123                                           FunctionAnalysisManager &AM) {
124   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
125     return PreservedAnalyses::all();
126   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
127   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
128   writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
129   return PreservedAnalyses::all();
130 }
131 
132 /// viewCFG - This function is meant for use from the debugger.  You can just
133 /// say 'call F->viewCFG()' and a ghostview window should pop up from the
134 /// program, displaying the CFG of the current function.  This depends on there
135 /// being a 'dot' and 'gv' program in your path.
136 ///
137 void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }
138 
139 void Function::viewCFG(const char *OutputFileName) const {
140   viewCFG(false, nullptr, nullptr, OutputFileName);
141 }
142 
143 void Function::viewCFG(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,
144                        const BranchProbabilityInfo *BPI,
145                        const char *OutputFileName) const {
146   if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
147     return;
148   DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);
149   ViewGraph(&CFGInfo, OutputFileName ? OutputFileName : "cfg" + getName(),
150             ViewCFGOnly);
151 }
152 
153 /// viewCFGOnly - This function is meant for use from the debugger.  It works
154 /// just like viewCFG, but it does not include the contents of basic blocks
155 /// into the nodes, just the label.  If you are only interested in the CFG
156 /// this can make the graph smaller.
157 ///
158 void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }
159 
160 void Function::viewCFGOnly(const char *OutputFileName) const {
161   viewCFG(true, nullptr, nullptr, OutputFileName);
162 }
163 
164 void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,
165                            const BranchProbabilityInfo *BPI) const {
166   viewCFG(true, BFI, BPI);
167 }
168 
169 /// Find all blocks on the paths which terminate with a deoptimize or
170 /// unreachable (i.e. all blocks which are post-dominated by a deoptimize
171 /// or unreachable). These paths are hidden if the corresponding cl::opts
172 /// are enabled.
173 void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(
174     const Function *F) {
175   auto evaluateBB = [&](const BasicBlock *Node) {
176     if (succ_empty(Node)) {
177       const Instruction *TI = Node->getTerminator();
178       isOnDeoptOrUnreachablePath[Node] =
179           (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||
180           (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());
181       return;
182     }
183     isOnDeoptOrUnreachablePath[Node] =
184         llvm::all_of(successors(Node), [this](const BasicBlock *BB) {
185           return isOnDeoptOrUnreachablePath[BB];
186         });
187   };
188   /// The post order traversal iteration is done to know the status of
189   /// isOnDeoptOrUnreachablePath for all the successors on the current BB.
190   llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
191 }
192 
193 bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
194                                                  const DOTFuncInfo *CFGInfo) {
195   if (HideColdPaths.getNumOccurrences() > 0)
196     if (auto *BFI = CFGInfo->getBFI()) {
197       BlockFrequency NodeFreq = BFI->getBlockFreq(Node);
198       BlockFrequency EntryFreq = BFI->getEntryFreq();
199       // Hide blocks with relative frequency below HideColdPaths threshold.
200       if ((double)NodeFreq.getFrequency() / EntryFreq.getFrequency() <
201           HideColdPaths)
202         return true;
203     }
204   if (HideUnreachablePaths || HideDeoptimizePaths) {
205     if (!isOnDeoptOrUnreachablePath.contains(Node))
206       computeDeoptOrUnreachablePaths(Node->getParent());
207     return isOnDeoptOrUnreachablePath[Node];
208   }
209   return false;
210 }
211