263 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines a `-dot-cfg` analysis pass, which emits the10// `<prefix>.<fnname>.dot` file for each function in the program, with a graph11// of the CFG for that function. The default value for `<prefix>` is `cfg` but12// can be customized as needed.13//14// The other main feature of this file is that it implements the15// Function::viewCFG method, which is useful for debugging passes which operate16// on the CFG.17//18//===----------------------------------------------------------------------===//19 20#include "llvm/Analysis/CFGPrinter.h"21#include "llvm/ADT/PostOrderIterator.h"22#include "llvm/IR/ModuleSlotTracker.h"23#include "llvm/Support/CommandLine.h"24#include "llvm/Support/FileSystem.h"25#include "llvm/Support/GraphWriter.h"26 27using namespace llvm;28 29static cl::opt<std::string>30 CFGFuncName("cfg-func-name", cl::Hidden,31 cl::desc("The name of a function (or its substring)"32 " whose CFG is viewed/printed."));33 34static cl::opt<std::string> CFGDotFilenamePrefix(35 "cfg-dot-filename-prefix", cl::Hidden,36 cl::desc("The prefix used for the CFG dot file names."));37 38static cl::opt<bool> HideUnreachablePaths("cfg-hide-unreachable-paths",39 cl::init(false));40 41static cl::opt<bool> HideDeoptimizePaths("cfg-hide-deoptimize-paths",42 cl::init(false));43 44static cl::opt<double> HideColdPaths(45 "cfg-hide-cold-paths", cl::init(0.0),46 cl::desc("Hide blocks with relative frequency below the given value"));47 48static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),49 cl::Hidden,50 cl::desc("Show heat colors in CFG"));51 52static cl::opt<bool> UseRawEdgeWeight("cfg-raw-weights", cl::init(false),53 cl::Hidden,54 cl::desc("Use raw weights for labels. "55 "Use percentages as default."));56 57static cl::opt<bool>58 ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,59 cl::desc("Show edges labeled with weights"));60 61static void writeCFGToDotFile(Function &F, BlockFrequencyInfo *BFI,62 BranchProbabilityInfo *BPI, uint64_t MaxFreq,63 bool CFGOnly = false) {64 std::string Filename =65 (CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();66 errs() << "Writing '" << Filename << "'...";67 68 std::error_code EC;69 raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);70 71 DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);72 CFGInfo.setHeatColors(ShowHeatColors);73 CFGInfo.setEdgeWeights(ShowEdgeWeight);74 CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);75 76 if (!EC)77 WriteGraph(File, &CFGInfo, CFGOnly);78 else79 errs() << " error opening file for writing!";80 errs() << "\n";81}82 83static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,84 const BranchProbabilityInfo *BPI, uint64_t MaxFreq,85 bool CFGOnly = false) {86 DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);87 CFGInfo.setHeatColors(ShowHeatColors);88 CFGInfo.setEdgeWeights(ShowEdgeWeight);89 CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);90 91 ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly);92}93 94DOTFuncInfo::DOTFuncInfo(const Function *F, const BlockFrequencyInfo *BFI,95 const BranchProbabilityInfo *BPI, uint64_t MaxFreq)96 : F(F), BFI(BFI), BPI(BPI), MaxFreq(MaxFreq) {97 ShowHeat = false;98 EdgeWeights = !!BPI; // Print EdgeWeights when BPI is available.99 RawWeights = !!BFI; // Print RawWeights when BFI is available.100}101 102DOTFuncInfo::~DOTFuncInfo() = default;103 104ModuleSlotTracker *DOTFuncInfo::getModuleSlotTracker() {105 if (!MSTStorage)106 MSTStorage = std::make_unique<ModuleSlotTracker>(F->getParent());107 return &*MSTStorage;108}109 110PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) {111 if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))112 return PreservedAnalyses::all();113 auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);114 auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);115 viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));116 return PreservedAnalyses::all();117}118 119PreservedAnalyses CFGOnlyViewerPass::run(Function &F,120 FunctionAnalysisManager &AM) {121 if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))122 return PreservedAnalyses::all();123 auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);124 auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);125 viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);126 return PreservedAnalyses::all();127}128 129PreservedAnalyses CFGPrinterPass::run(Function &F,130 FunctionAnalysisManager &AM) {131 if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))132 return PreservedAnalyses::all();133 auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);134 auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);135 writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));136 return PreservedAnalyses::all();137}138 139PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,140 FunctionAnalysisManager &AM) {141 if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))142 return PreservedAnalyses::all();143 auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);144 auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);145 writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);146 return PreservedAnalyses::all();147}148 149/// viewCFG - This function is meant for use from the debugger. You can just150/// say 'call F->viewCFG()' and a ghostview window should pop up from the151/// program, displaying the CFG of the current function. This depends on there152/// being a 'dot' and 'gv' program in your path.153///154void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }155 156void Function::viewCFG(const char *OutputFileName) const {157 viewCFG(false, nullptr, nullptr, OutputFileName);158}159 160void Function::viewCFG(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,161 const BranchProbabilityInfo *BPI,162 const char *OutputFileName) const {163 if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))164 return;165 DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);166 ViewGraph(&CFGInfo, OutputFileName ? OutputFileName : "cfg" + getName(),167 ViewCFGOnly);168}169 170/// viewCFGOnly - This function is meant for use from the debugger. It works171/// just like viewCFG, but it does not include the contents of basic blocks172/// into the nodes, just the label. If you are only interested in the CFG173/// this can make the graph smaller.174///175void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }176 177void Function::viewCFGOnly(const char *OutputFileName) const {178 viewCFG(true, nullptr, nullptr, OutputFileName);179}180 181void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,182 const BranchProbabilityInfo *BPI) const {183 viewCFG(true, BFI, BPI);184}185 186/// Find all blocks on the paths which terminate with a deoptimize or 187/// unreachable (i.e. all blocks which are post-dominated by a deoptimize 188/// or unreachable). These paths are hidden if the corresponding cl::opts189/// are enabled.190void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(191 const Function *F) {192 auto evaluateBB = [&](const BasicBlock *Node) {193 if (succ_empty(Node)) {194 const Instruction *TI = Node->getTerminator();195 isOnDeoptOrUnreachablePath[Node] =196 (HideUnreachablePaths && isa<UnreachableInst>(TI)) ||197 (HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());198 return;199 }200 isOnDeoptOrUnreachablePath[Node] =201 llvm::all_of(successors(Node), [this](const BasicBlock *BB) {202 return isOnDeoptOrUnreachablePath[BB];203 });204 };205 /// The post order traversal iteration is done to know the status of206 /// isOnDeoptOrUnreachablePath for all the successors on the current BB.207 llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);208}209 210bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,211 const DOTFuncInfo *CFGInfo) {212 if (HideColdPaths.getNumOccurrences() > 0)213 if (auto *BFI = CFGInfo->getBFI()) {214 BlockFrequency NodeFreq = BFI->getBlockFreq(Node);215 BlockFrequency EntryFreq = BFI->getEntryFreq();216 // Hide blocks with relative frequency below HideColdPaths threshold.217 if ((double)NodeFreq.getFrequency() / EntryFreq.getFrequency() <218 HideColdPaths)219 return true;220 }221 if (HideUnreachablePaths || HideDeoptimizePaths) {222 if (!isOnDeoptOrUnreachablePath.contains(Node))223 computeDeoptOrUnreachablePaths(Node->getParent());224 return isOnDeoptOrUnreachablePath[Node];225 }226 return false;227}228 229std::string DOTGraphTraits<DOTFuncInfo *>::getCompleteNodeLabel(230 const BasicBlock *Node, DOTFuncInfo *CFGInfo,231 function_ref<void(raw_string_ostream &, const BasicBlock &)>232 HandleBasicBlock,233 function_ref<void(std::string &, unsigned &, unsigned)> HandleComment) {234 if (HandleBasicBlock)235 return CompleteNodeLabelString(Node, HandleBasicBlock, HandleComment);236 237 // Default basic block printing238 std::optional<ModuleSlotTracker> MSTStorage;239 ModuleSlotTracker *MST = nullptr;240 241 if (CFGInfo) {242 MST = CFGInfo->getModuleSlotTracker();243 } else {244 MSTStorage.emplace(Node->getModule());245 MST = &*MSTStorage;246 }247 248 return CompleteNodeLabelString(249 Node,250 function_ref<void(raw_string_ostream &, const BasicBlock &)>(251 [MST](raw_string_ostream &OS, const BasicBlock &Node) -> void {252 // Prepend label name253 Node.printAsOperand(OS, false, *MST);254 OS << ":\n";255 256 for (const Instruction &Inst : Node) {257 Inst.print(OS, *MST, /* IsForDebug */ false);258 OS << '\n';259 }260 }),261 HandleComment);262}263