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
28using namespace llvm;
29
30static 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
35static 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
39static cl::opt<bool> HideUnreachablePaths("cfg-hide-unreachable-paths",
40                                          cl::init(false));
41
42static cl::opt<bool> HideDeoptimizePaths("cfg-hide-deoptimize-paths",
43                                         cl::init(false));
44
45static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),
46                                    cl::Hidden,
47                                    cl::desc("Show heat colors in CFG"));
48
49static cl::opt<bool> UseRawEdgeWeight("cfg-raw-weights", cl::init(false),
50                                      cl::Hidden,
51                                      cl::desc("Use raw weights for labels. "
52                                               "Use percentages as default."));
53
54static cl::opt<bool>
55    ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,
56                   cl::desc("Show edges labeled with weights"));
57
58static void writeCFGToDotFile(Function &F, BlockFrequencyInfo *BFI,
59                              BranchProbabilityInfo *BPI, uint64_t MaxFreq,
60                              bool CFGOnly = false) {
61  std::string Filename =
62      (CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();
63  errs() << "Writing '" << Filename << "'...";
64
65  std::error_code EC;
66  raw_fd_ostream File(Filename, EC, sys::fs::F_Text);
67
68  DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
69  CFGInfo.setHeatColors(ShowHeatColors);
70  CFGInfo.setEdgeWeights(ShowEdgeWeight);
71  CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
72
73  if (!EC)
74    WriteGraph(File, &CFGInfo, CFGOnly);
75  else
76    errs() << "  error opening file for writing!";
77  errs() << "\n";
78}
79
80static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,
81                    const BranchProbabilityInfo *BPI, uint64_t MaxFreq,
82                    bool CFGOnly = false) {
83  DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
84  CFGInfo.setHeatColors(ShowHeatColors);
85  CFGInfo.setEdgeWeights(ShowEdgeWeight);
86  CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
87
88  ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly);
89}
90
91namespace {
92struct CFGViewerLegacyPass : public FunctionPass {
93  static char ID; // Pass identifcation, replacement for typeid
94  CFGViewerLegacyPass() : FunctionPass(ID) {
95    initializeCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry());
96  }
97
98  bool runOnFunction(Function &F) override {
99    auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
100    auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
101    viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));
102    return false;
103  }
104
105  void print(raw_ostream &OS, const Module * = nullptr) const override {}
106
107  void getAnalysisUsage(AnalysisUsage &AU) const override {
108    FunctionPass::getAnalysisUsage(AU);
109    AU.addRequired<BlockFrequencyInfoWrapperPass>();
110    AU.addRequired<BranchProbabilityInfoWrapperPass>();
111    AU.setPreservesAll();
112  }
113};
114}
115
116char CFGViewerLegacyPass::ID = 0;
117INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false,
118                true)
119
120PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) {
121  auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
122  auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
123  viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));
124  return PreservedAnalyses::all();
125}
126
127namespace {
128struct CFGOnlyViewerLegacyPass : public FunctionPass {
129  static char ID; // Pass identifcation, replacement for typeid
130  CFGOnlyViewerLegacyPass() : FunctionPass(ID) {
131    initializeCFGOnlyViewerLegacyPassPass(*PassRegistry::getPassRegistry());
132  }
133
134  bool runOnFunction(Function &F) override {
135    auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
136    auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
137    viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
138    return false;
139  }
140
141  void print(raw_ostream &OS, const Module * = nullptr) const override {}
142
143  void getAnalysisUsage(AnalysisUsage &AU) const override {
144    FunctionPass::getAnalysisUsage(AU);
145    AU.addRequired<BlockFrequencyInfoWrapperPass>();
146    AU.addRequired<BranchProbabilityInfoWrapperPass>();
147    AU.setPreservesAll();
148  }
149};
150}
151
152char CFGOnlyViewerLegacyPass::ID = 0;
153INITIALIZE_PASS(CFGOnlyViewerLegacyPass, "view-cfg-only",
154                "View CFG of function (with no function bodies)", false, true)
155
156PreservedAnalyses CFGOnlyViewerPass::run(Function &F,
157                                         FunctionAnalysisManager &AM) {
158  auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
159  auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
160  viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
161  return PreservedAnalyses::all();
162}
163
164namespace {
165struct CFGPrinterLegacyPass : public FunctionPass {
166  static char ID; // Pass identification, replacement for typeid
167  CFGPrinterLegacyPass() : FunctionPass(ID) {
168    initializeCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
169  }
170
171  bool runOnFunction(Function &F) override {
172    auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
173    auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
174    writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));
175    return false;
176  }
177
178  void print(raw_ostream &OS, const Module * = nullptr) const override {}
179
180  void getAnalysisUsage(AnalysisUsage &AU) const override {
181    FunctionPass::getAnalysisUsage(AU);
182    AU.addRequired<BlockFrequencyInfoWrapperPass>();
183    AU.addRequired<BranchProbabilityInfoWrapperPass>();
184    AU.setPreservesAll();
185  }
186};
187}
188
189char CFGPrinterLegacyPass::ID = 0;
190INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg",
191                "Print CFG of function to 'dot' file", false, true)
192
193PreservedAnalyses CFGPrinterPass::run(Function &F,
194                                      FunctionAnalysisManager &AM) {
195  auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
196  auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
197  writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));
198  return PreservedAnalyses::all();
199}
200
201namespace {
202struct CFGOnlyPrinterLegacyPass : public FunctionPass {
203  static char ID; // Pass identification, replacement for typeid
204  CFGOnlyPrinterLegacyPass() : FunctionPass(ID) {
205    initializeCFGOnlyPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
206  }
207
208  bool runOnFunction(Function &F) override {
209    auto *BPI = &getAnalysis<BranchProbabilityInfoWrapperPass>().getBPI();
210    auto *BFI = &getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI();
211    writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
212    return false;
213  }
214  void print(raw_ostream &OS, const Module * = nullptr) const override {}
215
216  void getAnalysisUsage(AnalysisUsage &AU) const override {
217    FunctionPass::getAnalysisUsage(AU);
218    AU.addRequired<BlockFrequencyInfoWrapperPass>();
219    AU.addRequired<BranchProbabilityInfoWrapperPass>();
220    AU.setPreservesAll();
221  }
222};
223}
224
225char CFGOnlyPrinterLegacyPass::ID = 0;
226INITIALIZE_PASS(CFGOnlyPrinterLegacyPass, "dot-cfg-only",
227                "Print CFG of function to 'dot' file (with no function bodies)",
228                false, true)
229
230PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,
231                                          FunctionAnalysisManager &AM) {
232  auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
233  auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
234  writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
235  return PreservedAnalyses::all();
236}
237
238/// viewCFG - This function is meant for use from the debugger.  You can just
239/// say 'call F->viewCFG()' and a ghostview window should pop up from the
240/// program, displaying the CFG of the current function.  This depends on there
241/// being a 'dot' and 'gv' program in your path.
242///
243void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }
244
245void Function::viewCFG(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,
246                       const BranchProbabilityInfo *BPI) const {
247  if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
248    return;
249  DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);
250  ViewGraph(&CFGInfo, "cfg" + getName(), ViewCFGOnly);
251}
252
253/// viewCFGOnly - This function is meant for use from the debugger.  It works
254/// just like viewCFG, but it does not include the contents of basic blocks
255/// into the nodes, just the label.  If you are only interested in the CFG
256/// this can make the graph smaller.
257///
258void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }
259
260void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,
261                           const BranchProbabilityInfo *BPI) const {
262  viewCFG(true, BFI, BPI);
263}
264
265FunctionPass *llvm::createCFGPrinterLegacyPassPass() {
266  return new CFGPrinterLegacyPass();
267}
268
269FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass() {
270  return new CFGOnlyPrinterLegacyPass();
271}
272
273void DOTGraphTraits<DOTFuncInfo *>::computeHiddenNodes(const Function *F) {
274  auto evaluateBB = [&](const BasicBlock *Node) {
275    if (succ_begin(Node) == succ_end(Node)) {
276      const Instruction *TI = Node->getTerminator();
277      isHiddenBasicBlock[Node] =
278          (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||
279          (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());
280      return;
281    }
282    isHiddenBasicBlock[Node] = std::all_of(
283        succ_begin(Node), succ_end(Node),
284        [this](const BasicBlock *BB) { return isHiddenBasicBlock[BB]; });
285  };
286  /// The post order traversal iteration is done to know the status of
287  /// isHiddenBasicBlock for all the successors on the current BB.
288  for_each(po_begin(&F->getEntryBlock()), po_end(&F->getEntryBlock()),
289           evaluateBB);
290}
291
292bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node) {
293  // If both restricting flags are false, all nodes are displayed.
294  if (!HideUnreachablePaths && !HideDeoptimizePaths)
295    return false;
296  if (isHiddenBasicBlock.find(Node) == isHiddenBasicBlock.end())
297    computeHiddenNodes(Node->getParent());
298  return isHiddenBasicBlock[Node];
299}
300