Path: blob/main/contrib/llvm-project/llvm/lib/Analysis/CFGPrinter.cpp
35233 views
//===- CFGPrinter.cpp - DOT printer for the control flow graph ------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines a `-dot-cfg` analysis pass, which emits the9// `<prefix>.<fnname>.dot` file for each function in the program, with a graph10// of the CFG for that function. The default value for `<prefix>` is `cfg` but11// can be customized as needed.12//13// The other main feature of this file is that it implements the14// Function::viewCFG method, which is useful for debugging passes which operate15// on the CFG.16//17//===----------------------------------------------------------------------===//1819#include "llvm/Analysis/CFGPrinter.h"20#include "llvm/ADT/PostOrderIterator.h"21#include "llvm/Support/CommandLine.h"22#include "llvm/Support/FileSystem.h"23#include "llvm/Support/GraphWriter.h"2425using namespace llvm;2627static cl::opt<std::string>28CFGFuncName("cfg-func-name", cl::Hidden,29cl::desc("The name of a function (or its substring)"30" whose CFG is viewed/printed."));3132static cl::opt<std::string> CFGDotFilenamePrefix(33"cfg-dot-filename-prefix", cl::Hidden,34cl::desc("The prefix used for the CFG dot file names."));3536static cl::opt<bool> HideUnreachablePaths("cfg-hide-unreachable-paths",37cl::init(false));3839static cl::opt<bool> HideDeoptimizePaths("cfg-hide-deoptimize-paths",40cl::init(false));4142static cl::opt<double> HideColdPaths(43"cfg-hide-cold-paths", cl::init(0.0),44cl::desc("Hide blocks with relative frequency below the given value"));4546static cl::opt<bool> ShowHeatColors("cfg-heat-colors", cl::init(true),47cl::Hidden,48cl::desc("Show heat colors in CFG"));4950static cl::opt<bool> UseRawEdgeWeight("cfg-raw-weights", cl::init(false),51cl::Hidden,52cl::desc("Use raw weights for labels. "53"Use percentages as default."));5455static cl::opt<bool>56ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden,57cl::desc("Show edges labeled with weights"));5859static void writeCFGToDotFile(Function &F, BlockFrequencyInfo *BFI,60BranchProbabilityInfo *BPI, uint64_t MaxFreq,61bool CFGOnly = false) {62std::string Filename =63(CFGDotFilenamePrefix + "." + F.getName() + ".dot").str();64errs() << "Writing '" << Filename << "'...";6566std::error_code EC;67raw_fd_ostream File(Filename, EC, sys::fs::OF_Text);6869DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);70CFGInfo.setHeatColors(ShowHeatColors);71CFGInfo.setEdgeWeights(ShowEdgeWeight);72CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);7374if (!EC)75WriteGraph(File, &CFGInfo, CFGOnly);76else77errs() << " error opening file for writing!";78errs() << "\n";79}8081static void viewCFG(Function &F, const BlockFrequencyInfo *BFI,82const BranchProbabilityInfo *BPI, uint64_t MaxFreq,83bool CFGOnly = false) {84DOTFuncInfo CFGInfo(&F, BFI, BPI, MaxFreq);85CFGInfo.setHeatColors(ShowHeatColors);86CFGInfo.setEdgeWeights(ShowEdgeWeight);87CFGInfo.setRawEdgeWeights(UseRawEdgeWeight);8889ViewGraph(&CFGInfo, "cfg." + F.getName(), CFGOnly);90}9192PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) {93if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))94return PreservedAnalyses::all();95auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);96auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);97viewCFG(F, BFI, BPI, getMaxFreq(F, BFI));98return PreservedAnalyses::all();99}100101PreservedAnalyses CFGOnlyViewerPass::run(Function &F,102FunctionAnalysisManager &AM) {103if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))104return PreservedAnalyses::all();105auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);106auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);107viewCFG(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);108return PreservedAnalyses::all();109}110111PreservedAnalyses CFGPrinterPass::run(Function &F,112FunctionAnalysisManager &AM) {113if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))114return PreservedAnalyses::all();115auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);116auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);117writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI));118return PreservedAnalyses::all();119}120121PreservedAnalyses CFGOnlyPrinterPass::run(Function &F,122FunctionAnalysisManager &AM) {123if (!CFGFuncName.empty() && !F.getName().contains(CFGFuncName))124return PreservedAnalyses::all();125auto *BFI = &AM.getResult<BlockFrequencyAnalysis>(F);126auto *BPI = &AM.getResult<BranchProbabilityAnalysis>(F);127writeCFGToDotFile(F, BFI, BPI, getMaxFreq(F, BFI), /*CFGOnly=*/true);128return PreservedAnalyses::all();129}130131/// viewCFG - This function is meant for use from the debugger. You can just132/// say 'call F->viewCFG()' and a ghostview window should pop up from the133/// program, displaying the CFG of the current function. This depends on there134/// being a 'dot' and 'gv' program in your path.135///136void Function::viewCFG() const { viewCFG(false, nullptr, nullptr); }137138void Function::viewCFG(bool ViewCFGOnly, const BlockFrequencyInfo *BFI,139const BranchProbabilityInfo *BPI) const {140if (!CFGFuncName.empty() && !getName().contains(CFGFuncName))141return;142DOTFuncInfo CFGInfo(this, BFI, BPI, BFI ? getMaxFreq(*this, BFI) : 0);143ViewGraph(&CFGInfo, "cfg" + getName(), ViewCFGOnly);144}145146/// viewCFGOnly - This function is meant for use from the debugger. It works147/// just like viewCFG, but it does not include the contents of basic blocks148/// into the nodes, just the label. If you are only interested in the CFG149/// this can make the graph smaller.150///151void Function::viewCFGOnly() const { viewCFGOnly(nullptr, nullptr); }152153void Function::viewCFGOnly(const BlockFrequencyInfo *BFI,154const BranchProbabilityInfo *BPI) const {155viewCFG(true, BFI, BPI);156}157158/// Find all blocks on the paths which terminate with a deoptimize or159/// unreachable (i.e. all blocks which are post-dominated by a deoptimize160/// or unreachable). These paths are hidden if the corresponding cl::opts161/// are enabled.162void DOTGraphTraits<DOTFuncInfo *>::computeDeoptOrUnreachablePaths(163const Function *F) {164auto evaluateBB = [&](const BasicBlock *Node) {165if (succ_empty(Node)) {166const Instruction *TI = Node->getTerminator();167isOnDeoptOrUnreachablePath[Node] =168(HideUnreachablePaths && isa<UnreachableInst>(TI)) ||169(HideDeoptimizePaths && Node->getTerminatingDeoptimizeCall());170return;171}172isOnDeoptOrUnreachablePath[Node] =173llvm::all_of(successors(Node), [this](const BasicBlock *BB) {174return isOnDeoptOrUnreachablePath[BB];175});176};177/// The post order traversal iteration is done to know the status of178/// isOnDeoptOrUnreachablePath for all the successors on the current BB.179llvm::for_each(post_order(&F->getEntryBlock()), evaluateBB);180}181182bool DOTGraphTraits<DOTFuncInfo *>::isNodeHidden(const BasicBlock *Node,183const DOTFuncInfo *CFGInfo) {184if (HideColdPaths.getNumOccurrences() > 0)185if (auto *BFI = CFGInfo->getBFI()) {186BlockFrequency NodeFreq = BFI->getBlockFreq(Node);187BlockFrequency EntryFreq = BFI->getEntryFreq();188// Hide blocks with relative frequency below HideColdPaths threshold.189if ((double)NodeFreq.getFrequency() / EntryFreq.getFrequency() <190HideColdPaths)191return true;192}193if (HideUnreachablePaths || HideDeoptimizePaths) {194if (!isOnDeoptOrUnreachablePath.contains(Node))195computeDeoptOrUnreachablePaths(Node->getParent());196return isOnDeoptOrUnreachablePath[Node];197}198return false;199}200201202