xref: /src/contrib/llvm-project/llvm/lib/Analysis/CFGPrinter.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1009b1c42SEd Schouten //===- CFGPrinter.cpp - DOT printer for the control flow graph ------------===//
2009b1c42SEd Schouten //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6009b1c42SEd Schouten //
7009b1c42SEd Schouten //===----------------------------------------------------------------------===//
8009b1c42SEd Schouten //
9d8e91e46SDimitry Andric // This file defines a `-dot-cfg` analysis pass, which emits the
10d8e91e46SDimitry Andric // `<prefix>.<fnname>.dot` file for each function in the program, with a graph
11d8e91e46SDimitry Andric // of the CFG for that function. The default value for `<prefix>` is `cfg` but
12d8e91e46SDimitry Andric // can be customized as needed.
13009b1c42SEd Schouten //
14009b1c42SEd Schouten // The other main feature of this file is that it implements the
15009b1c42SEd Schouten // Function::viewCFG method, which is useful for debugging passes which operate
16009b1c42SEd Schouten // on the CFG.
17009b1c42SEd Schouten //
18009b1c42SEd Schouten //===----------------------------------------------------------------------===//
19009b1c42SEd Schouten 
20009b1c42SEd Schouten #include "llvm/Analysis/CFGPrinter.h"
21cfca06d7SDimitry Andric #include "llvm/ADT/PostOrderIterator.h"
22706b4fc4SDimitry Andric #include "llvm/Support/CommandLine.h"
235ca98fd9SDimitry Andric #include "llvm/Support/FileSystem.h"
24145449b1SDimitry Andric #include "llvm/Support/GraphWriter.h"
25cfca06d7SDimitry Andric 
26009b1c42SEd Schouten using namespace llvm;
27009b1c42SEd Schouten 
28cfca06d7SDimitry Andric static cl::opt<std::string>
29cfca06d7SDimitry Andric     CFGFuncName("cfg-func-name", cl::Hidden,
30eb11fae6SDimitry Andric                 cl::desc("The name of a function (or its substring)"
31eb11fae6SDimitry Andric                          " whose CFG is viewed/printed."));
32eb11fae6SDimitry Andric 
33d8e91e46SDimitry Andric static cl::opt<std::string> CFGDotFilenamePrefix(
34d8e91e46SDimitry Andric     "cfg-dot-filename-prefix", cl::Hidden,
35d8e91e46SDimitry Andric     cl::desc("The prefix used for the CFG dot file names."));
36d8e91e46SDimitry Andric 
37cfca06d7SDimitry Andric static cl::opt<bool> HideUnreachablePaths("cfg-hide-unreachable-paths",
38cfca06d7SDimitry Andric                                           cl::init(false));
39cfca06d7SDimitry Andric 
40cfca06d7SDimitry Andric static cl::opt<bool> HideDeoptimizePaths("cfg-hide-deoptimize-paths",
41cfca06d7SDimitry Andric                                          cl::init(false));
42cfca06d7SDimitry Andric 
43344a3780SDimitry Andric static cl::opt<double> HideColdPaths(
44344a3780SDimitry Andric     "cfg-hide-cold-paths", cl::init(0.0),
45344a3780SDimitry Andric     cl::desc("Hide blocks with relative frequency below the given value"));
46344a3780SDimitry Andric 
47cfca06d7SDimitry Andric static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),
48cfca06d7SDimitry Andric                                     cl::Hidden,
49cfca06d7SDimitry Andric                                     cl::desc("Show heat colors in CFG"));
50cfca06d7SDimitry Andric 
51cfca06d7SDimitry Andric static cl::opt<bool> UseRawEdgeWeight("cfg-raw-weights", cl::init(false),
52cfca06d7SDimitry Andric                                       cl::Hidden,
53cfca06d7SDimitry Andric                                       cl::desc("Use raw weights for labels. "
54cfca06d7SDimitry Andric                                                "Use percentages as default."));
55cfca06d7SDimitry Andric 
56cfca06d7SDimitry Andric static cl::opt<bool>
57cfca06d7SDimitry Andric     ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,
58cfca06d7SDimitry Andric                    cl::desc("Show edges labeled with weights"));
59cfca06d7SDimitry Andric 
writeCFGToDotFile(Function & F,BlockFrequencyInfo * BFI,BranchProbabilityInfo * BPI,uint64_t MaxFreq,bool CFGOnly=false)60cfca06d7SDimitry Andric static void writeCFGToDotFile(Function &F, BlockFrequencyInfo *BFI,
61cfca06d7SDimitry Andric                               BranchProbabilityInfo *BPI, uint64_t MaxFreq,
62cfca06d7SDimitry Andric                               bool CFGOnly = false) {
63cfca06d7SDimitry Andric   std::string Filename =
64cfca06d7SDimitry Andric       (CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();
65cfca06d7SDimitry Andric   errs() << "Writing '" << Filename << "'...";
66cfca06d7SDimitry Andric 
67cfca06d7SDimitry Andric   std::error_code EC;
68344a3780SDimitry Andric   raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);
69cfca06d7SDimitry Andric 
70cfca06d7SDimitry Andric   DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
71cfca06d7SDimitry Andric   CFGInfo.setHeatColors(ShowHeatColors);
72cfca06d7SDimitry Andric   CFGInfo.setEdgeWeights(ShowEdgeWeight);
73cfca06d7SDimitry Andric   CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
74cfca06d7SDimitry Andric 
75cfca06d7SDimitry Andric   if (!EC)
76cfca06d7SDimitry Andric     WriteGraph(File, &CFGInfo, CFGOnly);
77cfca06d7SDimitry Andric   else
78cfca06d7SDimitry Andric     errs() << "  error opening file for writing!";
79cfca06d7SDimitry Andric   errs() << "\n";
80cfca06d7SDimitry Andric }
81cfca06d7SDimitry Andric 
viewCFG(Function & F,const BlockFrequencyInfo * BFI,const BranchProbabilityInfo * BPI,uint64_t MaxFreq,bool CFGOnly=false)82cfca06d7SDimitry Andric static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,
83cfca06d7SDimitry Andric                     const BranchProbabilityInfo *BPI, uint64_t MaxFreq,
84cfca06d7SDimitry Andric                     bool CFGOnly = false) {
85cfca06d7SDimitry Andric   DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);
86cfca06d7SDimitry Andric   CFGInfo.setHeatColors(ShowHeatColors);
87cfca06d7SDimitry Andric   CFGInfo.setEdgeWeights(ShowEdgeWeight);
88cfca06d7SDimitry Andric   CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);
89cfca06d7SDimitry Andric 
90cfca06d7SDimitry Andric   ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly);
91cfca06d7SDimitry Andric }
92cfca06d7SDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)93cfca06d7SDimitry Andric PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) {
94344a3780SDimitry Andric   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
95344a3780SDimitry Andric     return PreservedAnalyses::all();
96cfca06d7SDimitry Andric   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
97cfca06d7SDimitry Andric   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
98cfca06d7SDimitry Andric   viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));
99b915e9e0SDimitry Andric   return PreservedAnalyses::all();
100b915e9e0SDimitry Andric }
101b915e9e0SDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)102b915e9e0SDimitry Andric PreservedAnalyses CFGOnlyViewerPass::run(Function &F,
103b915e9e0SDimitry Andric                                          FunctionAnalysisManager &AM) {
104344a3780SDimitry Andric   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
105344a3780SDimitry Andric     return PreservedAnalyses::all();
106cfca06d7SDimitry Andric   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
107cfca06d7SDimitry Andric   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
108cfca06d7SDimitry Andric   viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
109b915e9e0SDimitry Andric   return PreservedAnalyses::all();
110cf099d11SDimitry Andric }
111009b1c42SEd Schouten 
run(Function & F,FunctionAnalysisManager & AM)112b915e9e0SDimitry Andric PreservedAnalyses CFGPrinterPass::run(Function &F,
113b915e9e0SDimitry Andric                                       FunctionAnalysisManager &AM) {
114344a3780SDimitry Andric   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
115344a3780SDimitry Andric     return PreservedAnalyses::all();
116cfca06d7SDimitry Andric   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
117cfca06d7SDimitry Andric   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
118cfca06d7SDimitry Andric   writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));
119b915e9e0SDimitry Andric   return PreservedAnalyses::all();
120b915e9e0SDimitry Andric }
121b915e9e0SDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)122b915e9e0SDimitry Andric PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,
123b915e9e0SDimitry Andric                                           FunctionAnalysisManager &AM) {
124344a3780SDimitry Andric   if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))
125344a3780SDimitry Andric     return PreservedAnalyses::all();
126cfca06d7SDimitry Andric   auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);
127cfca06d7SDimitry Andric   auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);
128cfca06d7SDimitry Andric   writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);
129b915e9e0SDimitry Andric   return PreservedAnalyses::all();
130b915e9e0SDimitry Andric }
131b915e9e0SDimitry Andric 
132009b1c42SEd Schouten /// viewCFG - This function is meant for use from the debugger.  You can just
133009b1c42SEd Schouten /// say 'call F->viewCFG()' and a ghostview window should pop up from the
134009b1c42SEd Schouten /// program, displaying the CFG of the current function.  This depends on there
135009b1c42SEd Schouten /// being a 'dot' and 'gv' program in your path.
136009b1c42SEd Schouten ///
viewCFG() const137cfca06d7SDimitry Andric void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }
138cfca06d7SDimitry Andric 
viewCFG(bool ViewCFGOnly,const BlockFrequencyInfo * BFI,const BranchProbabilityInfo * BPI) const139cfca06d7SDimitry Andric void Function::viewCFG(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,
140cfca06d7SDimitry Andric                        const BranchProbabilityInfo *BPI) const {
141eb11fae6SDimitry Andric   if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))
142eb11fae6SDimitry Andric     return;
143cfca06d7SDimitry Andric   DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);
144cfca06d7SDimitry Andric   ViewGraph(&CFGInfo, "cfg" + getName(), ViewCFGOnly);
145009b1c42SEd Schouten }
146009b1c42SEd Schouten 
147009b1c42SEd Schouten /// viewCFGOnly - This function is meant for use from the debugger.  It works
148009b1c42SEd Schouten /// just like viewCFG, but it does not include the contents of basic blocks
1495ca98fd9SDimitry Andric /// into the nodes, just the label.  If you are only interested in the CFG
1505ca98fd9SDimitry Andric /// this can make the graph smaller.
151009b1c42SEd Schouten ///
viewCFGOnly() const152cfca06d7SDimitry Andric void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }
153cfca06d7SDimitry Andric 
viewCFGOnly(const BlockFrequencyInfo * BFI,const BranchProbabilityInfo * BPI) const154cfca06d7SDimitry Andric void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,
155cfca06d7SDimitry Andric                            const BranchProbabilityInfo *BPI) const {
156cfca06d7SDimitry Andric   viewCFG(true, BFI, BPI);
157009b1c42SEd Schouten }
158009b1c42SEd Schouten 
159344a3780SDimitry Andric /// Find all blocks on the paths which terminate with a deoptimize or
160344a3780SDimitry Andric /// unreachable (i.e. all blocks which are post-dominated by a deoptimize
161344a3780SDimitry Andric /// or unreachable). These paths are hidden if the corresponding cl::opts
162344a3780SDimitry Andric /// are enabled.
computeDeoptOrUnreachablePaths(const Function * F)163344a3780SDimitry Andric void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(
164344a3780SDimitry Andric     const Function *F) {
165cfca06d7SDimitry Andric   auto evaluateBB = [&](const BasicBlock *Node) {
166b60736ecSDimitry Andric     if (succ_empty(Node)) {
167cfca06d7SDimitry Andric       const Instruction *TI = Node->getTerminator();
168344a3780SDimitry Andric       isOnDeoptOrUnreachablePath[Node] =
169cfca06d7SDimitry Andric           (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||
170cfca06d7SDimitry Andric           (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());
171cfca06d7SDimitry Andric       return;
172cfca06d7SDimitry Andric     }
173344a3780SDimitry Andric     isOnDeoptOrUnreachablePath[Node] =
174b60736ecSDimitry Andric         llvm::all_of(successors(Node), [this](const BasicBlock *BB) {
175344a3780SDimitry Andric           return isOnDeoptOrUnreachablePath[BB];
176b60736ecSDimitry Andric         });
177cfca06d7SDimitry Andric   };
178cfca06d7SDimitry Andric   /// The post order traversal iteration is done to know the status of
179344a3780SDimitry Andric   /// isOnDeoptOrUnreachablePath for all the successors on the current BB.
180344a3780SDimitry Andric   llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
181cfca06d7SDimitry Andric }
182cfca06d7SDimitry Andric 
isNodeHidden(const BasicBlock * Node,const DOTFuncInfo * CFGInfo)183b60736ecSDimitry Andric bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
184b60736ecSDimitry Andric                                                  const DOTFuncInfo *CFGInfo) {
185344a3780SDimitry Andric   if (HideColdPaths.getNumOccurrences() > 0)
186344a3780SDimitry Andric     if (auto *BFI = CFGInfo->getBFI()) {
187b1c73532SDimitry Andric       BlockFrequency NodeFreq = BFI->getBlockFreq(Node);
188b1c73532SDimitry Andric       BlockFrequency EntryFreq = BFI->getEntryFreq();
189344a3780SDimitry Andric       // Hide blocks with relative frequency below HideColdPaths threshold.
190b1c73532SDimitry Andric       if ((double)NodeFreq.getFrequency() / EntryFreq.getFrequency() <
191b1c73532SDimitry Andric           HideColdPaths)
192344a3780SDimitry Andric         return true;
193344a3780SDimitry Andric     }
194344a3780SDimitry Andric   if (HideUnreachablePaths || HideDeoptimizePaths) {
1957fa27ce4SDimitry Andric     if (!isOnDeoptOrUnreachablePath.contains(Node))
196344a3780SDimitry Andric       computeDeoptOrUnreachablePaths(Node->getParent());
197344a3780SDimitry Andric     return isOnDeoptOrUnreachablePath[Node];
198344a3780SDimitry Andric   }
199cfca06d7SDimitry Andric   return false;
200cfca06d7SDimitry Andric }
201