Home | History | Annotate | Line # | Download | only in Analysis
      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 
     28 using namespace llvm;
     29 
     30 static 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 
     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 static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),
     46                                     cl::Hidden,
     47                                     cl::desc("Show heat colors in CFG"));
     48 
     49 static 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 
     54 static cl::opt<bool>
     55     ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,
     56                    cl::desc("Show edges labeled with weights"));
     57 
     58 static 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::OF_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 
     80 static 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 
     91 namespace {
     92 struct 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 
    116 char CFGViewerLegacyPass::ID = 0;
    117 INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false,
    118                 true)
    119 
    120 PreservedAnalyses 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 
    127 namespace {
    128 struct 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 
    152 char CFGOnlyViewerLegacyPass::ID = 0;
    153 INITIALIZE_PASS(CFGOnlyViewerLegacyPass, "view-cfg-only",
    154                 "View CFG of function (with no function bodies)", false, true)
    155 
    156 PreservedAnalyses 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 
    164 namespace {
    165 struct 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 
    189 char CFGPrinterLegacyPass::ID = 0;
    190 INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg",
    191                 "Print CFG of function to 'dot' file", false, true)
    192 
    193 PreservedAnalyses 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 
    201 namespace {
    202 struct 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 
    225 char CFGOnlyPrinterLegacyPass::ID = 0;
    226 INITIALIZE_PASS(CFGOnlyPrinterLegacyPass, "dot-cfg-only",
    227                 "Print CFG of function to 'dot' file (with no function bodies)",
    228                 false, true)
    229 
    230 PreservedAnalyses 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 ///
    243 void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }
    244 
    245 void 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 ///
    258 void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }
    259 
    260 void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,
    261                            const BranchProbabilityInfo *BPI) const {
    262   viewCFG(true, BFI, BPI);
    263 }
    264 
    265 FunctionPass *llvm::createCFGPrinterLegacyPassPass() {
    266   return new CFGPrinterLegacyPass();
    267 }
    268 
    269 FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass() {
    270   return new CFGOnlyPrinterLegacyPass();
    271 }
    272 
    273 void DOTGraphTraits<DOTFuncInfo *>::computeHiddenNodes(const Function *F) {
    274   auto evaluateBB = [&](const BasicBlock *Node) {
    275     if (succ_empty(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] =
    283         llvm::all_of(successors(Node), [this](const BasicBlock *BB) {
    284           return isHiddenBasicBlock[BB];
    285         });
    286   };
    287   /// The post order traversal iteration is done to know the status of
    288   /// isHiddenBasicBlock for all the successors on the current BB.
    289   llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);
    290 }
    291 
    292 bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,
    293                                                  const DOTFuncInfo *CFGInfo) {
    294   // If both restricting flags are false, all nodes are displayed.
    295   if (!HideUnreachablePaths && !HideDeoptimizePaths)
    296     return false;
    297   if (isHiddenBasicBlock.find(Node) == isHiddenBasicBlock.end())
    298     computeHiddenNodes(Node->getParent());
    299   return isHiddenBasicBlock[Node];
    300 }
    301