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/ADT/PostOrderIterator.h"
21 #include "llvm/Analysis/CFGPrinter.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> CFGFuncName(
31     "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 namespace {
46   struct CFGViewerLegacyPass : public FunctionPass {
47     static char ID; // Pass identifcation, replacement for typeid
48     CFGViewerLegacyPass() : FunctionPass(ID) {
49       initializeCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry());
50     }
51 
52     bool runOnFunction(Function &F) override {
53       F.viewCFG();
54       return false;
55     }
56 
57     void print(raw_ostream &OS, const Module* = nullptr) const override {}
58 
59     void getAnalysisUsage(AnalysisUsage &AU) const override {
60       AU.setPreservesAll();
61     }
62   };
63 }
64 
65 char CFGViewerLegacyPass::ID = 0;
66 INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false, true)
67 
68 PreservedAnalyses CFGViewerPass::run(Function &F,
69                                      FunctionAnalysisManager &AM) {
70   F.viewCFG();
71   return PreservedAnalyses::all();
72 }
73 
74 
75 namespace {
76   struct CFGOnlyViewerLegacyPass : public FunctionPass {
77     static char ID; // Pass identifcation, replacement for typeid
78     CFGOnlyViewerLegacyPass() : FunctionPass(ID) {
79       initializeCFGOnlyViewerLegacyPassPass(*PassRegistry::getPassRegistry());
80     }
81 
82     bool runOnFunction(Function &F) override {
83       F.viewCFGOnly();
84       return false;
85     }
86 
87     void print(raw_ostream &OS, const Module* = nullptr) const override {}
88 
89     void getAnalysisUsage(AnalysisUsage &AU) const override {
90       AU.setPreservesAll();
91     }
92   };
93 }
94 
95 char CFGOnlyViewerLegacyPass::ID = 0;
96 INITIALIZE_PASS(CFGOnlyViewerLegacyPass, "view-cfg-only",
97                 "View CFG of function (with no function bodies)", false, true)
98 
99 PreservedAnalyses CFGOnlyViewerPass::run(Function &F,
100                                          FunctionAnalysisManager &AM) {
101   F.viewCFGOnly();
102   return PreservedAnalyses::all();
103 }
104 
105 static void writeCFGToDotFile(Function &F, bool CFGOnly = false) {
106   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
107      return;
108   std::string Filename =
109       (CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();
110   errs() << "Writing '" << Filename << "'...";
111 
112   std::error_code EC;
113   raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);
114 
115   if (!EC)
116     WriteGraph(File, (const Function*)&F, CFGOnly);
117   else
118     errs() << "  error opening file for writing!";
119   errs() << "\n";
120 }
121 
122 namespace {
123   struct CFGPrinterLegacyPass : public FunctionPass {
124     static char ID; // Pass identification, replacement for typeid
125     CFGPrinterLegacyPass() : FunctionPass(ID) {
126       initializeCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
127     }
128 
129     bool runOnFunction(Function &F) override {
130       writeCFGToDotFile(F);
131       return false;
132     }
133 
134     void print(raw_ostream &OS, const Module* = nullptr) const override {}
135 
136     void getAnalysisUsage(AnalysisUsage &AU) const override {
137       AU.setPreservesAll();
138     }
139   };
140 }
141 
142 char CFGPrinterLegacyPass::ID = 0;
143 INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg", "Print CFG of function to 'dot' file",
144                 false, true)
145 
146 PreservedAnalyses CFGPrinterPass::run(Function &F,
147                                       FunctionAnalysisManager &AM) {
148   writeCFGToDotFile(F);
149   return PreservedAnalyses::all();
150 }
151 
152 namespace {
153   struct CFGOnlyPrinterLegacyPass : public FunctionPass {
154     static char ID; // Pass identification, replacement for typeid
155     CFGOnlyPrinterLegacyPass() : FunctionPass(ID) {
156       initializeCFGOnlyPrinterLegacyPassPass(*PassRegistry::getPassRegistry());
157     }
158 
159     bool runOnFunction(Function &F) override {
160       writeCFGToDotFile(F, /*CFGOnly=*/true);
161       return false;
162     }
163     void print(raw_ostream &OS, const Module* = nullptr) const override {}
164 
165     void getAnalysisUsage(AnalysisUsage &AU) const override {
166       AU.setPreservesAll();
167     }
168   };
169 }
170 
171 char CFGOnlyPrinterLegacyPass::ID = 0;
172 INITIALIZE_PASS(CFGOnlyPrinterLegacyPass, "dot-cfg-only",
173    "Print CFG of function to 'dot' file (with no function bodies)",
174    false, true)
175 
176 PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,
177                                           FunctionAnalysisManager &AM) {
178   writeCFGToDotFile(F, /*CFGOnly=*/true);
179   return PreservedAnalyses::all();
180 }
181 
182 /// viewCFG - This function is meant for use from the debugger.  You can just
183 /// say 'call F->viewCFG()' and a ghostview window should pop up from the
184 /// program, displaying the CFG of the current function.  This depends on there
185 /// being a 'dot' and 'gv' program in your path.
186 ///
187 void Function::viewCFG() const {
188   if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
189      return;
190   ViewGraph(this, "cfg" + getName());
191 }
192 
193 /// viewCFGOnly - This function is meant for use from the debugger.  It works
194 /// just like viewCFG, but it does not include the contents of basic blocks
195 /// into the nodes, just the label.  If you are only interested in the CFG
196 /// this can make the graph smaller.
197 ///
198 void Function::viewCFGOnly() const {
199   if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
200      return;
201   ViewGraph(this, "cfg" + getName(), true);
202 }
203 
204 FunctionPass *llvm::createCFGPrinterLegacyPassPass () {
205   return new CFGPrinterLegacyPass();
206 }
207 
208 FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass () {
209   return new CFGOnlyPrinterLegacyPass();
210 }
211 
212 void DOTGraphTraits<const Function *>::computeHiddenNodes(const Function *F) {
213   auto evaluateBB = [&](const BasicBlock *Node) {
214     if (succ_begin(Node) == succ_end(Node)) {
215       const Instruction *TI = Node->getTerminator();
216       isHiddenBasicBlock[Node] =
217         (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||
218         (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());
219       return;
220     }
221     isHiddenBasicBlock[Node] = std::all_of(
222         succ_begin(Node), succ_end(Node),
223         [this](const BasicBlock *BB) { return isHiddenBasicBlock[BB]; });
224   };
225   /// The post order traversal iteration is done to know the status of
226   /// isHiddenBasicBlock for all the successors on the current BB.
227   for_each(po_begin(&F->getEntryBlock()), po_end(&F->getEntryBlock()),
228            evaluateBB);
229 }
230 
231 bool DOTGraphTraits<const Function *>::isNodeHidden(const BasicBlock *Node) {
232   // If both restricting flags are false, all nodes are displayed.
233   if (!HideUnreachablePaths && !HideDeoptimizePaths)
234     return false;
235   if (isHiddenBasicBlock.find(Node) == isHiddenBasicBlock.end())
236     computeHiddenNodes(Node->getParent());
237   return isHiddenBasicBlock[Node];
238 }
239