xref: /freebsd/contrib/llvm-project/llvm/lib/Analysis/CFGPrinter.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,
140                        const BranchProbabilityInfo *BPI) const {
141   if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
142     return;
143   DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);
144   ViewGraph(&CFGInfo, "cfg" + getName(), ViewCFGOnly);
145 }
146 
147 /// viewCFGOnly - This function is meant for use from the debugger.  It works
148 /// just like viewCFG, but it does not include the contents of basic blocks
149 /// into the nodes, just the label.  If you are only interested in the CFG
150 /// this can make the graph smaller.
151 ///
152 void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }
153 
154 void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,
155                            const BranchProbabilityInfo *BPI) const {
156   viewCFG(true, BFI, BPI);
157 }
158 
159 /// Find all blocks on the paths which terminate with a deoptimize or
160 /// unreachable (i.e. all blocks which are post-dominated by a deoptimize
161 /// or unreachable). These paths are hidden if the corresponding cl::opts
162 /// are enabled.
163 void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(
164     const Function *F) {
165   auto evaluateBB = [&](const BasicBlock *Node) {
166     if (succ_empty(Node)) {
167       const Instruction *TI = Node->getTerminator();
168       isOnDeoptOrUnreachablePath[Node] =
169           (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||
170           (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());
171       return;
172     }
173     isOnDeoptOrUnreachablePath[Node] =
174         llvm::all_of(successors(Node), [this](const BasicBlock *BB) {
175           return isOnDeoptOrUnreachablePath[BB];
176         });
177   };
178   /// The post order traversal iteration is done to know the status of
179   /// isOnDeoptOrUnreachablePath for all the successors on the current BB.
180   llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
181 }
182 
183 bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
184                                                  const DOTFuncInfo *CFGInfo) {
185   if (HideColdPaths.getNumOccurrences() > 0)
186     if (auto *BFI = CFGInfo->getBFI()) {
187       BlockFrequency NodeFreq = BFI->getBlockFreq(Node);
188       BlockFrequency EntryFreq = BFI->getEntryFreq();
189       // Hide blocks with relative frequency below HideColdPaths threshold.
190       if ((double)NodeFreq.getFrequency() / EntryFreq.getFrequency() <
191           HideColdPaths)
192         return true;
193     }
194   if (HideUnreachablePaths || HideDeoptimizePaths) {
195     if (!isOnDeoptOrUnreachablePath.contains(Node))
196       computeDeoptOrUnreachablePaths(Node->getParent());
197     return isOnDeoptOrUnreachablePath[Node];
198   }
199   return false;
200 }
201