From 3b0535b424aca711375fa4e2a25c40509d6c6ed3 Mon Sep 17 00:00:00 2001 From: Sean Fertile Date: Fri, 29 Jun 2018 17:13:58 +0000 Subject: Extend CFGPrinter and CallPrinter with Heat Colors Extends the CFGPrinter and CallPrinter with heat colors based on heuristics or profiling information. The colors are enabled by default and can be toggled on/off for CFGPrinter by using the option -cfg-heat-colors for both -dot-cfg[-only] and -view-cfg[-only]. Similarly, the colors can be toggled on/off for CallPrinter by using the option -callgraph-heat-colors for both -dot-callgraph and -view-callgraph. Patch by Rodrigo Caetano Rocha! Differential Revision: https://reviews.llvm.org/D40425 llvm-svn: 335996 --- llvm/lib/Analysis/CFGPrinter.cpp | 250 +++++++++++++++++++++------ llvm/lib/Analysis/CMakeLists.txt | 1 + llvm/lib/Analysis/CallPrinter.cpp | 327 +++++++++++++++++++++++++++++++++--- llvm/lib/Analysis/DomPrinter.cpp | 9 +- llvm/lib/Analysis/HeatUtils.cpp | 130 ++++++++++++++ llvm/lib/Analysis/RegionPrinter.cpp | 8 +- 6 files changed, 639 insertions(+), 86 deletions(-) create mode 100644 llvm/lib/Analysis/HeatUtils.cpp (limited to 'llvm/lib/Analysis') diff --git a/llvm/lib/Analysis/CFGPrinter.cpp b/llvm/lib/Analysis/CFGPrinter.cpp index fb261755e5d..10be4e4bb95 100644 --- a/llvm/lib/Analysis/CFGPrinter.cpp +++ b/llvm/lib/Analysis/CFGPrinter.cpp @@ -22,53 +22,170 @@ #include "llvm/Support/FileSystem.h" using namespace llvm; +static cl::opt CFGHeatPerFunction("cfg-heat-per-function", + cl::init(false), cl::Hidden, + cl::desc("Heat CFG per function")); + +static cl::opt ShowHeatColors("cfg-heat-colors", cl::init(true), + cl::Hidden, + cl::desc("Show heat colors in CFG")); + +static cl::opt UseRawEdgeWeight("cfg-raw-weights", cl::init(false), + cl::Hidden, + cl::desc("Use raw weights for labels. " + "Use percentages as default.")); + +static cl::opt ShowEdgeWeight("cfg-weights", cl::init(true), cl::Hidden, + cl::desc("Show edges labeled with weights")); + +static void writeHeatCFGToDotFile(Function &F, BlockFrequencyInfo *BFI, + BranchProbabilityInfo *BPI, uint64_t MaxFreq, + bool UseHeuristic, bool isSimple) { + std::string Filename = ("cfg." + F.getName() + ".dot").str(); + errs() << "Writing '" << Filename << "'..."; + + std::error_code EC; + raw_fd_ostream File(Filename, EC, sys::fs::F_Text); + + CFGDOTInfo CFGInfo(&F, BFI, BPI, MaxFreq); + CFGInfo.setHeuristic(UseHeuristic); + CFGInfo.setHeatColors(ShowHeatColors); + CFGInfo.setEdgeWeights(ShowEdgeWeight); + CFGInfo.setRawEdgeWeights(UseRawEdgeWeight); + + if (!EC) + WriteGraph(File, &CFGInfo, isSimple); + else + errs() << " error opening file for writing!"; + errs() << "\n"; +} + +static void writeAllCFGsToDotFile(Module &M, + function_ref LookupBFI, + function_ref LookupBPI, + bool isSimple) { + bool UseHeuristic = true; + uint64_t MaxFreq = 0; + if (!CFGHeatPerFunction) + MaxFreq = getMaxFreq(M, LookupBFI, UseHeuristic); + + for (auto &F : M) { + if (F.isDeclaration()) continue; + auto *BFI = LookupBFI(F); + auto *BPI = LookupBPI(F); + if (CFGHeatPerFunction) + MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + writeHeatCFGToDotFile(F, BFI, BPI, MaxFreq, UseHeuristic, isSimple); + } + +} + +static void viewHeatCFG(Function &F, BlockFrequencyInfo *BFI, + BranchProbabilityInfo *BPI, uint64_t MaxFreq, + bool UseHeuristic, bool isSimple) { + CFGDOTInfo CFGInfo(&F, BFI, BPI, MaxFreq); + CFGInfo.setHeuristic(UseHeuristic); + CFGInfo.setHeatColors(ShowHeatColors); + CFGInfo.setEdgeWeights(ShowEdgeWeight); + CFGInfo.setRawEdgeWeights(UseRawEdgeWeight); + + ViewGraph(&CFGInfo, "cfg." + F.getName(), isSimple); +} + +static void viewAllCFGs(Module &M, + function_ref LookupBFI, + function_ref LookupBPI, + bool isSimple) { + bool UseHeuristic = true; + uint64_t MaxFreq = 0; + if (!CFGHeatPerFunction) + MaxFreq = getMaxFreq(M, LookupBFI, UseHeuristic); + + for (auto &F : M) { + if (F.isDeclaration()) continue; + auto *BFI = LookupBFI(F); + auto *BPI = LookupBPI(F); + if (CFGHeatPerFunction) + MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + viewHeatCFG(F, BFI, BPI, MaxFreq, UseHeuristic, isSimple); + } + +} + namespace { - struct CFGViewerLegacyPass : public FunctionPass { + struct CFGViewerLegacyPass : public ModulePass { static char ID; // Pass identifcation, replacement for typeid - CFGViewerLegacyPass() : FunctionPass(ID) { + CFGViewerLegacyPass() : ModulePass(ID) { initializeCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry()); } - bool runOnFunction(Function &F) override { - F.viewCFG(); + bool runOnModule(Module &M) override { + auto LookupBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + auto LookupBPI = [this](Function &F) { + return &this->getAnalysis(F).getBPI(); + }; + viewAllCFGs(M, LookupBFI, LookupBPI, /*isSimple=*/false); return false; } - void print(raw_ostream &OS, const Module* = nullptr) const override {} + void print(raw_ostream &OS, const Module * = nullptr) const override {} void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } + }; } char CFGViewerLegacyPass::ID = 0; INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false, true) -PreservedAnalyses CFGViewerPass::run(Function &F, - FunctionAnalysisManager &AM) { - F.viewCFG(); +PreservedAnalyses CFGViewerPass::run(Module &M, + ModuleAnalysisManager &AM) { + auto &FAM = AM.getResult(M).getManager(); + auto LookupBFI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + auto LookupBPI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + viewAllCFGs(M, LookupBFI, LookupBPI, /*isSimple=*/false); return PreservedAnalyses::all(); } namespace { - struct CFGOnlyViewerLegacyPass : public FunctionPass { + struct CFGOnlyViewerLegacyPass : public ModulePass { static char ID; // Pass identifcation, replacement for typeid - CFGOnlyViewerLegacyPass() : FunctionPass(ID) { + CFGOnlyViewerLegacyPass() : ModulePass(ID) { initializeCFGOnlyViewerLegacyPassPass(*PassRegistry::getPassRegistry()); } - bool runOnFunction(Function &F) override { - F.viewCFGOnly(); + bool runOnModule(Module &M) override { + auto LookupBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + auto LookupBPI = [this](Function &F) { + return &this->getAnalysis(F).getBPI(); + }; + viewAllCFGs(M, LookupBFI, LookupBPI, /*isSimple=*/true); return false; } - void print(raw_ostream &OS, const Module* = nullptr) const override {} + void print(raw_ostream &OS, const Module * = nullptr) const override {} void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } + }; } @@ -76,43 +193,46 @@ char CFGOnlyViewerLegacyPass::ID = 0; INITIALIZE_PASS(CFGOnlyViewerLegacyPass, "view-cfg-only", "View CFG of function (with no function bodies)", false, true) -PreservedAnalyses CFGOnlyViewerPass::run(Function &F, - FunctionAnalysisManager &AM) { - F.viewCFGOnly(); +PreservedAnalyses CFGOnlyViewerPass::run(Module &M, + ModuleAnalysisManager &AM) { + auto &FAM = AM.getResult(M).getManager(); + auto LookupBFI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + auto LookupBPI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + viewAllCFGs(M, LookupBFI, LookupBPI, /*isSimple=*/true); return PreservedAnalyses::all(); } -static void writeCFGToDotFile(Function &F, bool CFGOnly = false) { - std::string Filename = ("cfg." + F.getName() + ".dot").str(); - errs() << "Writing '" << Filename << "'..."; - - std::error_code EC; - raw_fd_ostream File(Filename, EC, sys::fs::F_Text); - - if (!EC) - WriteGraph(File, (const Function*)&F, CFGOnly); - else - errs() << " error opening file for writing!"; - errs() << "\n"; -} - namespace { - struct CFGPrinterLegacyPass : public FunctionPass { + struct CFGPrinterLegacyPass : public ModulePass { static char ID; // Pass identification, replacement for typeid - CFGPrinterLegacyPass() : FunctionPass(ID) { + CFGPrinterLegacyPass() : ModulePass(ID) { initializeCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); } - bool runOnFunction(Function &F) override { - writeCFGToDotFile(F); + bool runOnModule(Module &M) override { + auto LookupBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + auto LookupBPI = [this](Function &F) { + return &this->getAnalysis(F).getBPI(); + }; + writeAllCFGsToDotFile(M, LookupBFI, LookupBPI, /*isSimple=*/false); return false; } - void print(raw_ostream &OS, const Module* = nullptr) const override {} + void print(raw_ostream &OS, const Module * = nullptr) const override {} void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } + }; } @@ -120,28 +240,46 @@ char CFGPrinterLegacyPass::ID = 0; INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg", "Print CFG of function to 'dot' file", false, true) -PreservedAnalyses CFGPrinterPass::run(Function &F, - FunctionAnalysisManager &AM) { - writeCFGToDotFile(F); +PreservedAnalyses CFGPrinterPass::run(Module &M, + ModuleAnalysisManager &AM) { + auto &FAM = AM.getResult(M).getManager(); + auto LookupBFI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + auto LookupBPI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + writeAllCFGsToDotFile(M, LookupBFI, LookupBPI, /*isSimple=*/false); return PreservedAnalyses::all(); } namespace { - struct CFGOnlyPrinterLegacyPass : public FunctionPass { + struct CFGOnlyPrinterLegacyPass : public ModulePass { static char ID; // Pass identification, replacement for typeid - CFGOnlyPrinterLegacyPass() : FunctionPass(ID) { + CFGOnlyPrinterLegacyPass() : ModulePass(ID) { initializeCFGOnlyPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); } - bool runOnFunction(Function &F) override { - writeCFGToDotFile(F, /*CFGOnly=*/true); + bool runOnModule(Module &M) override { + auto LookupBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + auto LookupBPI = [this](Function &F) { + return &this->getAnalysis(F).getBPI(); + }; + writeAllCFGsToDotFile(M, LookupBFI, LookupBPI, /*isSimple=*/true); return false; } - void print(raw_ostream &OS, const Module* = nullptr) const override {} + + void print(raw_ostream &OS, const Module * = nullptr) const override {} void getAnalysisUsage(AnalysisUsage &AU) const override { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } + }; } @@ -150,9 +288,16 @@ INITIALIZE_PASS(CFGOnlyPrinterLegacyPass, "dot-cfg-only", "Print CFG of function to 'dot' file (with no function bodies)", false, true) -PreservedAnalyses CFGOnlyPrinterPass::run(Function &F, - FunctionAnalysisManager &AM) { - writeCFGToDotFile(F, /*CFGOnly=*/true); +PreservedAnalyses CFGOnlyPrinterPass::run(Module &M, + ModuleAnalysisManager &AM) { + auto &FAM = AM.getResult(M).getManager(); + auto LookupBFI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + auto LookupBPI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + writeAllCFGsToDotFile(M, LookupBFI, LookupBPI, /*isSimple=*/true); return PreservedAnalyses::all(); } @@ -162,7 +307,9 @@ PreservedAnalyses CFGOnlyPrinterPass::run(Function &F, /// being a 'dot' and 'gv' program in your path. /// void Function::viewCFG() const { - ViewGraph(this, "cfg" + getName()); + + CFGDOTInfo CFGInfo(this); + ViewGraph(&CFGInfo, "cfg" + getName()); } /// viewCFGOnly - This function is meant for use from the debugger. It works @@ -171,14 +318,15 @@ void Function::viewCFG() const { /// this can make the graph smaller. /// void Function::viewCFGOnly() const { - ViewGraph(this, "cfg" + getName(), true); + + CFGDOTInfo CFGInfo(this); + ViewGraph(&CFGInfo, "cfg" + getName(), true); } -FunctionPass *llvm::createCFGPrinterLegacyPassPass () { +ModulePass *llvm::createCFGPrinterLegacyPassPass() { return new CFGPrinterLegacyPass(); } -FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass () { +ModulePass *llvm::createCFGOnlyPrinterLegacyPassPass() { return new CFGOnlyPrinterLegacyPass(); } - diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index 8e8535abd05..6c0f408aea5 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -10,6 +10,7 @@ add_llvm_library(LLVMAnalysis BlockFrequencyInfoImpl.cpp BranchProbabilityInfo.cpp CFG.cpp + HeatUtils.cpp CFGPrinter.cpp CFLAndersAliasAnalysis.cpp CFLSteensAliasAnalysis.cpp diff --git a/llvm/lib/Analysis/CallPrinter.cpp b/llvm/lib/Analysis/CallPrinter.cpp index e7017e77652..a2e42bb3e63 100644 --- a/llvm/lib/Analysis/CallPrinter.cpp +++ b/llvm/lib/Analysis/CallPrinter.cpp @@ -15,62 +15,337 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/CallPrinter.h" + +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/DOTGraphTraitsPass.h" +#include "llvm/Analysis/HeatUtils.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" using namespace llvm; +static cl::opt ShowHeatColors("callgraph-heat-colors", cl::init(true), + cl::Hidden, + cl::desc("Show heat colors in call-graph")); + +static cl::opt + EstimateEdgeWeight("callgraph-weights", cl::init(false), cl::Hidden, + cl::desc("Show edges labeled with weights")); + +static cl::opt + FullCallGraph("callgraph-full", cl::init(false), cl::Hidden, + cl::desc("Show full call-graph (including external nodes)")); + +static cl::opt UseCallCounter( + "callgraph-call-count", cl::init(false), cl::Hidden, + cl::desc("Use function's call counter as a heat metric. " + "The default is the function's maximum block frequency.")); + namespace llvm { -template <> struct DOTGraphTraits : public DefaultDOTGraphTraits { +class CallGraphDOTInfo { +private: + Module *M; + CallGraph *CG; + DenseMap Freq; + uint64_t MaxFreq; + uint64_t MaxEdgeCount; + +public: + std::function LookupBFI; + + CallGraphDOTInfo(Module *M, CallGraph *CG, + function_ref LookupBFI) + : M(M), CG(CG), LookupBFI(LookupBFI) { + MaxFreq = 0; + MaxEdgeCount = 0; + + for (Function &F : *M) { + Freq[&F] = 0; + + if (FullCallGraph) { + for (User *U : F.users()) { + auto CS = CallSite(U); + if (!CS.getCaller()->isDeclaration()) { + uint64_t Counter = getNumOfCalls(CS, LookupBFI); + if (Counter > MaxEdgeCount) { + MaxEdgeCount = Counter; + } + } + } + } + + if (F.isDeclaration()) + continue; + uint64_t localMaxFreq = 0; + if (UseCallCounter) { + Function::ProfileCount EntryCount = F.getEntryCount(); + if (EntryCount.hasValue()) + localMaxFreq = EntryCount.getCount(); + } else { + localMaxFreq = llvm::getMaxFreq(F, LookupBFI(F)); + } + if (localMaxFreq >= MaxFreq) + MaxFreq = localMaxFreq; + Freq[&F] = localMaxFreq; + + if (!FullCallGraph) { + for (Function &Callee : *M) { + uint64_t Counter = getNumOfCalls(F, Callee, LookupBFI); + if (Counter > MaxEdgeCount) { + MaxEdgeCount = Counter; + } + } + } + } + if (!FullCallGraph) + removeParallelEdges(); + } + + Module *getModule() const { return M; } + CallGraph *getCallGraph() const { return CG; } + + uint64_t getFreq(const Function *F) { return Freq[F]; } + + uint64_t getMaxFreq() { return MaxFreq; } + + uint64_t getMaxEdgeCount() { return MaxEdgeCount; } + +private: + void removeParallelEdges() { + for (auto &I : (*CG)) { + CallGraphNode *Node = I.second.get(); + + bool FoundParallelEdge = true; + while (FoundParallelEdge) { + SmallSet Visited; + FoundParallelEdge = false; + for (auto CI = Node->begin(), CE = Node->end(); CI != CE; CI++) { + if (!Visited.count(CI->second->getFunction())) + Visited.insert(CI->second->getFunction()); + else { + FoundParallelEdge = true; + Node->removeCallEdge(CI); + break; + } + } + } + } + } +}; + +template <> +struct GraphTraits + : public GraphTraits { + static NodeRef getEntryNode(CallGraphDOTInfo *CGInfo) { + // Start at the external node! + return CGInfo->getCallGraph()->getExternalCallingNode(); + } + + typedef std::pair> + PairTy; + static const CallGraphNode *CGGetValuePtr(const PairTy &P) { + return P.second.get(); + } + + // nodes_iterator/begin/end - Allow iteration over all nodes in the graph + typedef mapped_iterator + nodes_iterator; + + static nodes_iterator nodes_begin(CallGraphDOTInfo *CGInfo) { + return nodes_iterator(CGInfo->getCallGraph()->begin(), &CGGetValuePtr); + } + static nodes_iterator nodes_end(CallGraphDOTInfo *CGInfo) { + return nodes_iterator(CGInfo->getCallGraph()->end(), &CGGetValuePtr); + } +}; + +template <> +struct DOTGraphTraits : public DefaultDOTGraphTraits { + + SmallSet VisitedCallSites; + DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} - static std::string getGraphName(CallGraph *Graph) { return "Call graph"; } + static std::string getGraphName(CallGraphDOTInfo *CGInfo) { + return "Call graph: " + + std::string(CGInfo->getModule()->getModuleIdentifier()); + } + + static bool isNodeHidden(const CallGraphNode *Node) { + if (FullCallGraph) + return false; + + if (Node->getFunction()) + return false; + + return true; + } + + std::string getNodeLabel(const CallGraphNode *Node, + CallGraphDOTInfo *CGInfo) { + if (Node == CGInfo->getCallGraph()->getExternalCallingNode()) + return "external caller"; + + if (Node == CGInfo->getCallGraph()->getCallsExternalNode()) + return "external callee"; - std::string getNodeLabel(CallGraphNode *Node, CallGraph *Graph) { if (Function *Func = Node->getFunction()) return Func->getName(); return "external node"; } -}; -struct AnalysisCallGraphWrapperPassTraits { - static CallGraph *getGraph(CallGraphWrapperPass *P) { - return &P->getCallGraph(); + static const CallGraphNode *CGGetValuePtr(CallGraphNode::CallRecord P) { + return P.second; + } + + // nodes_iterator/begin/end - Allow iteration over all nodes in the graph + typedef mapped_iterator + nodes_iterator; + + std::string getEdgeAttributes(const CallGraphNode *Node, nodes_iterator I, + CallGraphDOTInfo *CGInfo) { + if (!EstimateEdgeWeight) + return ""; + + Function *Caller = Node->getFunction(); + if (Caller == nullptr || Caller->isDeclaration()) + return ""; + + Function *Callee = (*I)->getFunction(); + if (Callee == nullptr) + return ""; + + uint64_t Counter = 0; + if (FullCallGraph) { + // looks for next call site between Caller and Callee + for (User *U : Callee->users()) { + auto CS = CallSite(U); + if (CS.getCaller() == Caller) { + if (VisitedCallSites.count(U)) + continue; + VisitedCallSites.insert(U); + Counter = getNumOfCalls(CS, CGInfo->LookupBFI); + break; + } + } + } else { + Counter = getNumOfCalls(*Caller, *Callee, CGInfo->LookupBFI); + } + + const unsigned MaxEdgeWidth = 3; + + double Width = + 1 + (MaxEdgeWidth - 1) * (double(Counter) / CGInfo->getMaxEdgeCount()); + std::string Attrs = "label=\"" + std::to_string(Counter) + + "\" penwidth=" + std::to_string(Width); + + return Attrs; + } + + std::string getNodeAttributes(const CallGraphNode *Node, + CallGraphDOTInfo *CGInfo) { + Function *F = Node->getFunction(); + if (F == nullptr || F->isDeclaration()) + return ""; + + std::string attrs = ""; + if (ShowHeatColors) { + uint64_t freq = CGInfo->getFreq(F); + std::string color = getHeatColor(freq, CGInfo->getMaxFreq()); + std::string edgeColor = (freq <= (CGInfo->getMaxFreq() / 2)) + ? getHeatColor(0) + : getHeatColor(1); + + attrs = "color=\"" + edgeColor + "ff\", style=filled, fillcolor=\"" + + color + "80\""; + } + return attrs; } }; -} // end llvm namespace +} // namespace llvm namespace { -struct CallGraphViewer - : public DOTGraphTraitsModuleViewer { +// Viewer + +class CallGraphViewer : public ModulePass { +public: static char ID; + CallGraphViewer() : ModulePass(ID) {} - CallGraphViewer() - : DOTGraphTraitsModuleViewer( - "callgraph", ID) { - initializeCallGraphViewerPass(*PassRegistry::getPassRegistry()); - } + void getAnalysisUsage(AnalysisUsage &AU) const override; + bool runOnModule(Module &M) override; }; -struct CallGraphDOTPrinter : public DOTGraphTraitsModulePrinter< - CallGraphWrapperPass, true, CallGraph *, - AnalysisCallGraphWrapperPassTraits> { +void CallGraphViewer::getAnalysisUsage(AnalysisUsage &AU) const { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.setPreservesAll(); +} + +bool CallGraphViewer::runOnModule(Module &M) { + auto LookupBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + + CallGraph CG(M); + CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); + + std::string Title = + DOTGraphTraits::getGraphName(&CFGInfo); + ViewGraph(&CFGInfo, "callgraph", true, Title); + + return false; +} + +// DOT Printer + +class CallGraphDOTPrinter : public ModulePass { +public: static char ID; + CallGraphDOTPrinter() : ModulePass(ID) {} - CallGraphDOTPrinter() - : DOTGraphTraitsModulePrinter( - "callgraph", ID) { - initializeCallGraphDOTPrinterPass(*PassRegistry::getPassRegistry()); - } + void getAnalysisUsage(AnalysisUsage &AU) const override; + bool runOnModule(Module &M) override; }; +void CallGraphDOTPrinter::getAnalysisUsage(AnalysisUsage &AU) const { + ModulePass::getAnalysisUsage(AU); + AU.addRequired(); + AU.setPreservesAll(); +} + +bool CallGraphDOTPrinter::runOnModule(Module &M) { + auto LookupBFI = [this](Function &F) { + return &this->getAnalysis(F).getBFI(); + }; + + std::string Filename = + (std::string(M.getModuleIdentifier()) + ".callgraph.dot"); + errs() << "Writing '" << Filename << "'..."; + + std::error_code EC; + raw_fd_ostream File(Filename, EC, sys::fs::F_Text); + + CallGraph CG(M); + CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); + + if (!EC) + WriteGraph(File, &CFGInfo); + else + errs() << " error opening file for writing!"; + errs() << "\n"; + + return false; +} + } // end anonymous namespace char CallGraphViewer::ID = 0; diff --git a/llvm/lib/Analysis/DomPrinter.cpp b/llvm/lib/Analysis/DomPrinter.cpp index 8abc0e7d0df..b1beb72234e 100644 --- a/llvm/lib/Analysis/DomPrinter.cpp +++ b/llvm/lib/Analysis/DomPrinter.cpp @@ -38,13 +38,12 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { if (!BB) return "Post dominance root node"; - if (isSimple()) - return DOTGraphTraits - ::getSimpleNodeLabel(BB, BB->getParent()); + return DOTGraphTraits + ::getSimpleNodeLabel(BB, nullptr); else - return DOTGraphTraits - ::getCompleteNodeLabel(BB, BB->getParent()); + return DOTGraphTraits + ::getCompleteNodeLabel(BB, nullptr); } }; diff --git a/llvm/lib/Analysis/HeatUtils.cpp b/llvm/lib/Analysis/HeatUtils.cpp new file mode 100644 index 00000000000..c328c4828dd --- /dev/null +++ b/llvm/lib/Analysis/HeatUtils.cpp @@ -0,0 +1,130 @@ +//===-- HeatUtils.cpp - Utility for printing heat colors --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Utility for printing heat colors based on heuristics or profiling +// information. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/HeatUtils.h" +#include "llvm/IR/Instructions.h" + +namespace llvm { + +static const unsigned heatSize = 100; +static const std::string heatPalette[heatSize] = { + "#3d50c3", "#4055c8", "#4358cb", "#465ecf", "#4961d2", "#4c66d6", "#4f69d9", + "#536edd", "#5572df", "#5977e3", "#5b7ae5", "#5f7fe8", "#6282ea", "#6687ed", + "#6a8bef", "#6c8ff1", "#7093f3", "#7396f5", "#779af7", "#7a9df8", "#7ea1fa", + "#81a4fb", "#85a8fc", "#88abfd", "#8caffe", "#8fb1fe", "#93b5fe", "#96b7ff", + "#9abbff", "#9ebeff", "#a1c0ff", "#a5c3fe", "#a7c5fe", "#abc8fd", "#aec9fc", + "#b2ccfb", "#b5cdfa", "#b9d0f9", "#bbd1f8", "#bfd3f6", "#c1d4f4", "#c5d6f2", + "#c7d7f0", "#cbd8ee", "#cedaeb", "#d1dae9", "#d4dbe6", "#d6dce4", "#d9dce1", + "#dbdcde", "#dedcdb", "#e0dbd8", "#e3d9d3", "#e5d8d1", "#e8d6cc", "#ead5c9", + "#ecd3c5", "#eed0c0", "#efcebd", "#f1ccb8", "#f2cab5", "#f3c7b1", "#f4c5ad", + "#f5c1a9", "#f6bfa6", "#f7bca1", "#f7b99e", "#f7b599", "#f7b396", "#f7af91", + "#f7ac8e", "#f7a889", "#f6a385", "#f5a081", "#f59c7d", "#f4987a", "#f39475", + "#f29072", "#f08b6e", "#ef886b", "#ed8366", "#ec7f63", "#e97a5f", "#e8765c", + "#e57058", "#e36c55", "#e16751", "#de614d", "#dc5d4a", "#d85646", "#d65244", + "#d24b40", "#d0473d", "#cc403a", "#ca3b37", "#c53334", "#c32e31", "#be242e", + "#bb1b2c", "#b70d28"}; + +bool hasProfiling(const Module &M) { + for (auto &F : M) { + for (auto &BB : F) { + auto *TI = BB.getTerminator(); + if (TI == nullptr) + continue; + if (TI->getMetadata(llvm::LLVMContext::MD_prof) != nullptr) + return true; + } + } + return false; +} + +uint64_t getBlockFreq(const BasicBlock *BB, const BlockFrequencyInfo *BFI, + bool useHeuristic) { + uint64_t freqVal = 0; + if (!useHeuristic) { + Optional freq = BFI->getBlockProfileCount(BB); + if (freq.hasValue()) + freqVal = freq.getValue(); + } else { + freqVal = BFI->getBlockFreq(BB).getFrequency(); + } + return freqVal; +} + +uint64_t getNumOfCalls(CallSite &CS, + function_ref LookupBFI, + bool useHeuristic) { + if (CS.getInstruction()==nullptr) return 0; + if (CS.getInstruction()->getParent()==nullptr) return 0; + BasicBlock *BB = CS.getInstruction()->getParent(); + return getBlockFreq(BB, LookupBFI(*CS.getCaller())); +} + +uint64_t getNumOfCalls(Function &callerFunction, Function &calledFunction, + function_ref LookupBFI, + bool useHeuristic) { + uint64_t counter = 0; + for (User *U : calledFunction.users()) { + if (isa(U)) { + auto CS = CallSite(U); + if (CS.getCaller() == (&callerFunction)) { + counter += getNumOfCalls(CS, LookupBFI); + } + } + } + return counter; +} + +uint64_t getMaxFreq(const Function &F, const BlockFrequencyInfo *BFI, + bool useHeuristic) { + uint64_t maxFreq = 0; + for (const BasicBlock &BB : F) { + uint64_t freqVal = getBlockFreq(&BB, BFI, useHeuristic); + if (freqVal >= maxFreq) + maxFreq = freqVal; + } + return maxFreq; +} + +uint64_t getMaxFreq(Module &M, + function_ref LookupBFI, + bool useHeuristic) { + uint64_t maxFreq = 0; + for (auto &F : M) { + if (F.isDeclaration()) + continue; + uint64_t localMaxFreq = getMaxFreq(F, LookupBFI(F), useHeuristic); + if (localMaxFreq >= maxFreq) + maxFreq = localMaxFreq; + } + return maxFreq; +} + +std::string getHeatColor(uint64_t freq, uint64_t maxFreq) { + if (freq > maxFreq) + freq = maxFreq; + unsigned colorId = + unsigned(round((double(freq) / maxFreq) * (heatSize - 1.0))); + return heatPalette[colorId]; +} + +std::string getHeatColor(double percent) { + if (percent > 1.0) + percent = 1.0; + if (percent < 0.0) + percent = 0.0; + unsigned colorId = unsigned(round(percent * (heatSize - 1.0))); + return heatPalette[colorId]; +} + +} // namespace llvm diff --git a/llvm/lib/Analysis/RegionPrinter.cpp b/llvm/lib/Analysis/RegionPrinter.cpp index 5986b8c4e0c..8fe45070390 100644 --- a/llvm/lib/Analysis/RegionPrinter.cpp +++ b/llvm/lib/Analysis/RegionPrinter.cpp @@ -47,11 +47,11 @@ struct DOTGraphTraits : public DefaultDOTGraphTraits { BasicBlock *BB = Node->getNodeAs(); if (isSimple()) - return DOTGraphTraits - ::getSimpleNodeLabel(BB, BB->getParent()); + return DOTGraphTraits + ::getSimpleNodeLabel(BB, nullptr); else - return DOTGraphTraits - ::getCompleteNodeLabel(BB, BB->getParent()); + return DOTGraphTraits + ::getCompleteNodeLabel(BB, nullptr); } return "Not implemented"; -- cgit v1.2.3