Index: include/llvm/Analysis/CFGPrinter.h =================================================================== --- include/llvm/Analysis/CFGPrinter.h +++ include/llvm/Analysis/CFGPrinter.h @@ -24,44 +24,113 @@ #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/PassManager.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/GraphWriter.h" +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include "llvm/Analysis/HeatUtils.h" + namespace llvm { -class CFGViewerPass - : public PassInfoMixin { +class CFGViewerPass : public PassInfoMixin { public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; -class CFGOnlyViewerPass - : public PassInfoMixin { +class CFGOnlyViewerPass : public PassInfoMixin { public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; -class CFGPrinterPass - : public PassInfoMixin { +class CFGPrinterPass : public PassInfoMixin { public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; -class CFGOnlyPrinterPass - : public PassInfoMixin { +class CFGOnlyPrinterPass : public PassInfoMixin { public: PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); }; -template<> -struct DOTGraphTraits : public DefaultDOTGraphTraits { +class CFGDOTInfo { +private: + const BlockFrequencyInfo *BFI; + const Function *F; + uint64_t MaxFreq; + bool ShowHeat; + bool Heuristic; + bool EdgeWeights; + bool RawWeights; - DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} +public: + CFGDOTInfo(const Function *F) : CFGDOTInfo(F, nullptr, 0) {} - static std::string getGraphName(const Function *F) { - return "CFG for '" + F->getName().str() + "' function"; + CFGDOTInfo(const Function *F, const BlockFrequencyInfo *BFI, + uint64_t MaxFreq) { + this->BFI = BFI; + this->F = F; + this->MaxFreq = MaxFreq; + this->ShowHeat = true; + this->Heuristic = true; + this->EdgeWeights = true; + this->RawWeights = false; } - static std::string getSimpleNodeLabel(const BasicBlock *Node, - const Function *) { + const BlockFrequencyInfo *getBFI() { return BFI; } + + const Function *getF() { return this->F; } + + uint64_t getMaxFreq() { return MaxFreq; } + + uint64_t getFreq(const BasicBlock *BB) { + return getBlockFreq(BB, BFI, Heuristic); + } + + void setHeatColors(bool ShowHeat) { this->ShowHeat = ShowHeat; } + + bool showHeatColors() { return ShowHeat; } + + void setHeuristic(bool Heuristic) { this->Heuristic = Heuristic; } + + bool useHeuristic() { return Heuristic; } + + void setRawEdgeWeights(bool RawWeights) { this->RawWeights = RawWeights; } + + bool useRawEdgeWeights() { return RawWeights; } + + void setEdgeWeights(bool EdgeWeights) { this->EdgeWeights = EdgeWeights; } + + bool showEdgeWeights() { return EdgeWeights; } +}; + +template <> +struct GraphTraits : public GraphTraits { + static NodeRef getEntryNode(CFGDOTInfo *CFGInfo) { + return &(CFGInfo->getF()->getEntryBlock()); + } + + // nodes_iterator/begin/end - Allow iteration over all nodes in the graph + using nodes_iterator = pointer_iterator; + + static nodes_iterator nodes_begin(CFGDOTInfo *CFGInfo) { + return nodes_iterator(CFGInfo->getF()->begin()); + } + + static nodes_iterator nodes_end(CFGDOTInfo *CFGInfo) { + return nodes_iterator(CFGInfo->getF()->end()); + } + + static size_t size(CFGDOTInfo *CFGInfo) { return CFGInfo->getF()->size(); } +}; + +template <> struct DOTGraphTraits : public DefaultDOTGraphTraits { + + DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} + + static std::string getGraphName(CFGDOTInfo *CFGInfo) { + return "CFG for '" + CFGInfo->getF()->getName().str() + "' function"; + } + + static std::string getSimpleNodeLabel(const BasicBlock *Node, CFGDOTInfo *) { if (!Node->getName().empty()) return Node->getName().str(); @@ -73,7 +142,7 @@ } static std::string getCompleteNodeLabel(const BasicBlock *Node, - const Function *) { + CFGDOTInfo *) { enum { MaxColumns = 80 }; std::string Str; raw_string_ostream OS(Str); @@ -85,22 +154,23 @@ OS << *Node; std::string OutStr = OS.str(); - if (OutStr[0] == '\n') OutStr.erase(OutStr.begin()); + if (OutStr[0] == '\n') + OutStr.erase(OutStr.begin()); // Process string output to make it nicer... unsigned ColNum = 0; unsigned LastSpace = 0; for (unsigned i = 0; i != OutStr.length(); ++i) { - if (OutStr[i] == '\n') { // Left justify + if (OutStr[i] == '\n') { // Left justify OutStr[i] = '\\'; - OutStr.insert(OutStr.begin()+i+1, 'l'); + OutStr.insert(OutStr.begin() + i + 1, 'l'); ColNum = 0; LastSpace = 0; - } else if (OutStr[i] == ';') { // Delete comments! - unsigned Idx = OutStr.find('\n', i+1); // Find end of line - OutStr.erase(OutStr.begin()+i, OutStr.begin()+Idx); + } else if (OutStr[i] == ';') { // Delete comments! + unsigned Idx = OutStr.find('\n', i + 1); // Find end of line + OutStr.erase(OutStr.begin() + i, OutStr.begin() + Idx); --i; - } else if (ColNum == MaxColumns) { // Wrap lines. + } else if (ColNum == MaxColumns) { // Wrap lines. // Wrap very long names even though we can't find a space. if (!LastSpace) LastSpace = i; @@ -108,8 +178,7 @@ ColNum = i - LastSpace; LastSpace = 0; i += 3; // The loop will advance 'i' again. - } - else + } else ++ColNum; if (OutStr[i] == ' ') LastSpace = i; @@ -117,12 +186,11 @@ return OutStr; } - std::string getNodeLabel(const BasicBlock *Node, - const Function *Graph) { + std::string getNodeLabel(const BasicBlock *Node, CFGDOTInfo *CFGInfo) { if (isSimple()) - return getSimpleNodeLabel(Node, Graph); + return getSimpleNodeLabel(Node, CFGInfo); else - return getCompleteNodeLabel(Node, Graph); + return getCompleteNodeLabel(Node, CFGInfo); } static std::string getEdgeSourceLabel(const BasicBlock *Node, @@ -136,7 +204,8 @@ if (const SwitchInst *SI = dyn_cast(Node->getTerminator())) { unsigned SuccNo = I.getSuccessorIndex(); - if (SuccNo == 0) return "def"; + if (SuccNo == 0) + return "def"; std::string Str; raw_string_ostream OS(Str); @@ -149,39 +218,84 @@ /// Display the raw branch weights from PGO. std::string getEdgeAttributes(const BasicBlock *Node, succ_const_iterator I, - const Function *F) { + CFGDOTInfo *CFGInfo) { + + if (!CFGInfo->showEdgeWeights()) + return ""; + const TerminatorInst *TI = Node->getTerminator(); if (TI->getNumSuccessors() == 1) return ""; - MDNode *WeightsNode = TI->getMetadata(LLVMContext::MD_prof); - if (!WeightsNode) - return ""; + std::string Attrs = ""; - MDString *MDName = cast(WeightsNode->getOperand(0)); - if (MDName->getString() != "branch_weights") - return ""; + if (CFGInfo->useRawEdgeWeights()) { + MDNode *WeightsNode = TI->getMetadata(LLVMContext::MD_prof); + if (!WeightsNode) + return ""; - unsigned OpNo = I.getSuccessorIndex() + 1; - if (OpNo >= WeightsNode->getNumOperands()) - return ""; - ConstantInt *Weight = - mdconst::dyn_extract(WeightsNode->getOperand(OpNo)); - if (!Weight) - return ""; + MDString *MDName = cast(WeightsNode->getOperand(0)); + if (MDName->getString() != "branch_weights") + return ""; - // Prepend a 'W' to indicate that this is a weight rather than the actual - // profile count (due to scaling). - Twine Attrs = "label=\"W:" + Twine(Weight->getZExtValue()) + "\""; - return Attrs.str(); + unsigned OpNo = I.getSuccessorIndex() + 1; + if (OpNo >= WeightsNode->getNumOperands()) + return ""; + ConstantInt *Weight = + mdconst::dyn_extract(WeightsNode->getOperand(OpNo)); + if (!Weight) + return ""; + + // Prepend a 'W' to indicate that this is a weight rather than the actual + // profile count (due to scaling). + Attrs = "label=\"W:" + std::to_string(Weight->getZExtValue()) + "\""; + } else { + uint64_t total = 0; + for (unsigned i = 0; i < TI->getNumSuccessors(); i++) { + total += CFGInfo->getFreq(TI->getSuccessor(i)); + } + + unsigned OpNo = I.getSuccessorIndex(); + + if (OpNo >= TI->getNumSuccessors()) + return ""; + + BasicBlock *SuccBB = TI->getSuccessor(OpNo); + + double val = 0.0; + if (CFGInfo->getFreq(SuccBB) > 0) { + double freq = CFGInfo->getFreq(SuccBB); + //rounding with precision of 2 decimal places + val = (int(round((freq / double(total)) * 10000))) / 10000.0; + } + //formating value to percentage + Attrs = formatv("label=\"{0:P}\"", val); + } + return Attrs; } + + std::string getNodeAttributes(const BasicBlock *Node, CFGDOTInfo *CFGInfo) { + std::string attrs = ""; + if (CFGInfo->showHeatColors()) { + uint64_t freq = CFGInfo->getFreq(Node); + std::string color = getHeatColor(freq, CFGInfo->getMaxFreq()); + std::string edgeColor = (freq <= (CFGInfo->getMaxFreq() / 2)) + ? (getHeatColor(0)) + : (getHeatColor(1)); + + attrs = "color=\"" + edgeColor + "ff\", style=filled, fillcolor=\"" + + color + "70\""; + } + return attrs; + } }; -} // End llvm namespace +} // namespace llvm + namespace llvm { - class FunctionPass; - FunctionPass *createCFGPrinterLegacyPassPass (); - FunctionPass *createCFGOnlyPrinterLegacyPassPass (); -} // End llvm namespace +class FunctionPass; +FunctionPass *createCFGPrinterLegacyPassPass(); +FunctionPass *createCFGOnlyPrinterLegacyPassPass(); +} // namespace llvm #endif Index: include/llvm/Analysis/HeatUtils.h =================================================================== --- include/llvm/Analysis/HeatUtils.h +++ include/llvm/Analysis/HeatUtils.h @@ -0,0 +1,49 @@ +//===-- HeatUtils.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_HEATUTILS_H +#define LLVM_ANALYSIS_HEATUTILS_H + +#include "llvm/Analysis/BlockFrequencyInfo.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" + +#include + +namespace llvm { + +bool hasProfiling(Module &M); + +uint64_t getBlockFreq(const BasicBlock *BB, const BlockFrequencyInfo *BFI, + bool useHeuristic = true); + +uint64_t getNumOfCalls(Function &callerFunction, Function &calledFunction, + function_ref LookupBFI, + bool useHeuristic = true); + +uint64_t getMaxFreq(const Function &F, const BlockFrequencyInfo *BFI, + bool useHeuristic = true); + +uint64_t getMaxFreq(Module &M, + function_ref LookupBFI, + bool useHeuristic = true); + +std::string getHeatColor(uint64_t freq, uint64_t maxFreq); + +std::string getHeatColor(double percent); + +} // namespace llvm + +#endif Index: lib/Analysis/CFGPrinter.cpp =================================================================== --- lib/Analysis/CFGPrinter.cpp +++ lib/Analysis/CFGPrinter.cpp @@ -20,57 +20,117 @@ #include "llvm/Analysis/CFGPrinter.h" #include "llvm/Pass.h" #include "llvm/Support/FileSystem.h" + using namespace llvm; -namespace { - struct CFGViewerLegacyPass : public FunctionPass { - static char ID; // Pass identifcation, replacement for typeid - CFGViewerLegacyPass() : FunctionPass(ID) { - initializeCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry()); - } +static cl::opt ShowHeatColors("cfg-heat-colors", cl::init(false), + cl::Hidden, + cl::desc("Show heat colors in CFG")); - bool runOnFunction(Function &F) override { - F.viewCFG(); - return false; - } +static cl::opt UseRawEdgeWeight("cfg-raw-weights", cl::init(false), + cl::Hidden, + cl::desc("Use raw profiling weights")); - void print(raw_ostream &OS, const Module* = nullptr) const override {} +static cl::opt ShowEdgeWeight("cfg-weights", cl::init(false), cl::Hidden, + cl::desc("Show edge labels with weights")); - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - }; +static void writeHeatCFGToDotFile(Function &F, BlockFrequencyInfo *BFI, + 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, 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 viewHeatCFGToDotFile(Function &F, BlockFrequencyInfo *BFI, + uint64_t MaxFreq, bool UseHeuristic, + bool isSimple) { + CFGDOTInfo CFGInfo(&F, BFI, MaxFreq); + CFGInfo.setHeuristic(UseHeuristic); + CFGInfo.setHeatColors(ShowHeatColors); + CFGInfo.setEdgeWeights(ShowEdgeWeight); + CFGInfo.setRawEdgeWeights(UseRawEdgeWeight); + + ViewGraph(&CFGInfo, "cfg." + F.getName(), isSimple); +} + +namespace { + +struct CFGViewerLegacyPass : public FunctionPass { + static char ID; // Pass identifcation, replacement for typeid + CFGViewerLegacyPass() : FunctionPass(ID) { + initializeCFGViewerLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override { + auto *BFI = &getAnalysis().getBFI(); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + viewHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/false); + // F.viewCFG(); + return false; + } + + void print(raw_ostream &OS, const Module * = nullptr) const override {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.setPreservesAll(); + } +}; +} // namespace + char CFGViewerLegacyPass::ID = 0; -INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false, true) +INITIALIZE_PASS(CFGViewerLegacyPass, "view-cfg", "View CFG of function", false, + true) -PreservedAnalyses CFGViewerPass::run(Function &F, - FunctionAnalysisManager &AM) { - F.viewCFG(); +PreservedAnalyses CFGViewerPass::run(Function &F, FunctionAnalysisManager &AM) { + auto *BFI = &AM.getResult(F); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + viewHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/false); + // F.viewCFG(); return PreservedAnalyses::all(); } - namespace { - struct CFGOnlyViewerLegacyPass : public FunctionPass { - static char ID; // Pass identifcation, replacement for typeid - CFGOnlyViewerLegacyPass() : FunctionPass(ID) { - initializeCFGOnlyViewerLegacyPassPass(*PassRegistry::getPassRegistry()); - } +struct CFGOnlyViewerLegacyPass : public FunctionPass { + static char ID; // Pass identifcation, replacement for typeid + CFGOnlyViewerLegacyPass() : FunctionPass(ID) { + initializeCFGOnlyViewerLegacyPassPass(*PassRegistry::getPassRegistry()); + } - bool runOnFunction(Function &F) override { - F.viewCFGOnly(); - return false; - } + bool runOnFunction(Function &F) override { + auto *BFI = &getAnalysis().getBFI(); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + viewHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/true); + // F.viewCFGOnly(); + 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 { - AU.setPreservesAll(); - } - }; -} + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.setPreservesAll(); + } +}; +} // namespace char CFGOnlyViewerLegacyPass::ID = 0; INITIALIZE_PASS(CFGOnlyViewerLegacyPass, "view-cfg-only", @@ -78,81 +138,88 @@ PreservedAnalyses CFGOnlyViewerPass::run(Function &F, FunctionAnalysisManager &AM) { - F.viewCFGOnly(); + + auto *BFI = &AM.getResult(F); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + viewHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/true); + // F.viewCFGOnly(); return PreservedAnalyses::all(); } -static void writeCFGToDotFile(Function &F) { - 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); - else - errs() << " error opening file for writing!"; - errs() << "\n"; -} - namespace { - struct CFGPrinterLegacyPass : public FunctionPass { - static char ID; // Pass identification, replacement for typeid - CFGPrinterLegacyPass() : FunctionPass(ID) { - initializeCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); - } +struct CFGPrinterLegacyPass : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + CFGPrinterLegacyPass() : FunctionPass(ID) { + initializeCFGPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); + } - bool runOnFunction(Function &F) override { - writeCFGToDotFile(F); - return false; - } + bool runOnFunction(Function &F) override { + auto *BFI = &getAnalysis().getBFI(); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + writeHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*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 { - AU.setPreservesAll(); - } - }; -} + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.setPreservesAll(); + } +}; +} // namespace char CFGPrinterLegacyPass::ID = 0; -INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg", "Print CFG of function to 'dot' file", - false, true) +INITIALIZE_PASS(CFGPrinterLegacyPass, "dot-cfg", + "Print CFG of function to 'dot' file", false, true) PreservedAnalyses CFGPrinterPass::run(Function &F, FunctionAnalysisManager &AM) { - writeCFGToDotFile(F); + auto *BFI = &AM.getResult(F); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + writeHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/false); return PreservedAnalyses::all(); } namespace { - struct CFGOnlyPrinterLegacyPass : public FunctionPass { - static char ID; // Pass identification, replacement for typeid - CFGOnlyPrinterLegacyPass() : FunctionPass(ID) { - initializeCFGOnlyPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); - } +struct CFGOnlyPrinterLegacyPass : public FunctionPass { + static char ID; // Pass identification, replacement for typeid + CFGOnlyPrinterLegacyPass() : FunctionPass(ID) { + initializeCFGOnlyPrinterLegacyPassPass(*PassRegistry::getPassRegistry()); + } - bool runOnFunction(Function &F) override { - writeCFGToDotFile(F); - return false; - } - void print(raw_ostream &OS, const Module* = nullptr) const override {} + bool runOnFunction(Function &F) override { + auto *BFI = &this->getAnalysis().getBFI(); - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - }; -} + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + writeHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/true); + return false; + } + void print(raw_ostream &OS, const Module * = nullptr) const override {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.setPreservesAll(); + } +}; +} // namespace + char CFGOnlyPrinterLegacyPass::ID = 0; INITIALIZE_PASS(CFGOnlyPrinterLegacyPass, "dot-cfg-only", - "Print CFG of function to 'dot' file (with no function bodies)", - false, true) + "Print CFG of function to 'dot' file (with no function bodies)", + false, true) PreservedAnalyses CFGOnlyPrinterPass::run(Function &F, FunctionAnalysisManager &AM) { - writeCFGToDotFile(F); + auto *BFI = &AM.getResult(F); + bool UseHeuristic = true; + uint64_t MaxFreq = getMaxFreq(F, BFI, UseHeuristic); + writeHeatCFGToDotFile(F, BFI, MaxFreq, UseHeuristic, /*isSimple=*/true); return PreservedAnalyses::all(); } @@ -162,7 +229,14 @@ /// being a 'dot' and 'gv' program in your path. /// void Function::viewCFG() const { - ViewGraph(this, "cfg" + getName()); + + CFGDOTInfo CFGInfo(this, nullptr, 0); + CFGInfo.setHeuristic(false); + CFGInfo.setHeatColors(false); + CFGInfo.setEdgeWeights(true); + CFGInfo.setRawEdgeWeights(true); + + ViewGraph(&CFGInfo, "cfg" + getName()); } /// viewCFGOnly - This function is meant for use from the debugger. It works @@ -171,14 +245,20 @@ /// this can make the graph smaller. /// void Function::viewCFGOnly() const { - ViewGraph(this, "cfg" + getName(), true); + + CFGDOTInfo CFGInfo(this, nullptr, 0); + CFGInfo.setHeuristic(false); + CFGInfo.setHeatColors(false); + CFGInfo.setEdgeWeights(true); + CFGInfo.setRawEdgeWeights(true); + + ViewGraph(&CFGInfo, "cfg" + getName(), true); } -FunctionPass *llvm::createCFGPrinterLegacyPassPass () { +FunctionPass *llvm::createCFGPrinterLegacyPassPass() { return new CFGPrinterLegacyPass(); } -FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass () { +FunctionPass *llvm::createCFGOnlyPrinterLegacyPassPass() { return new CFGOnlyPrinterLegacyPass(); } - Index: lib/Analysis/CMakeLists.txt =================================================================== --- lib/Analysis/CMakeLists.txt +++ lib/Analysis/CMakeLists.txt @@ -10,6 +10,7 @@ BlockFrequencyInfoImpl.cpp BranchProbabilityInfo.cpp CFG.cpp + HeatUtils.cpp CFGPrinter.cpp CFLAndersAliasAnalysis.cpp CFLSteensAliasAnalysis.cpp Index: lib/Analysis/CallPrinter.cpp =================================================================== --- lib/Analysis/CallPrinter.cpp +++ lib/Analysis/CallPrinter.cpp @@ -15,62 +15,290 @@ //===----------------------------------------------------------------------===// #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(false), + cl::Hidden, + cl::desc("Show heat colors in call-graph")); + +static cl::opt EstimateEdgeWeight("callgraph-estimate-weight", + cl::init(false), cl::Hidden, + cl::desc("Estimate edge weights")); + +static cl::opt + FullCallGraph("callgraph-full", cl::init(false), cl::Hidden, + cl::desc("Print full call-graph (using 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")); + namespace llvm { -template <> struct DOTGraphTraits : public DefaultDOTGraphTraits { +class CallGraphDOTInfo { +private: + CallGraph *CG; + Module *M; + DenseMap Freq; + uint64_t MaxFreq; + +public: + std::function LookupBFI; + + CallGraphDOTInfo(Module *M, CallGraph *CG, + function_ref LookupBFI) { + this->M = M; + this->CG = CG; + MaxFreq = 0; + + bool useHeuristic = !hasProfiling(*M); + + for (Function &F : *M) { + Freq[&F] = 0; + if (F.isDeclaration()) + continue; + uint64_t localMaxFreq = 0; + if (UseCallCounter) { + Optional EntryCount = F.getEntryCount(); + if (EntryCount.hasValue()) + localMaxFreq = EntryCount.getValue(); + } else { + localMaxFreq = llvm::getMaxFreq(F, LookupBFI(F), useHeuristic); + } + if (localMaxFreq >= MaxFreq) + MaxFreq = localMaxFreq; + Freq[&F] = localMaxFreq; + } + this->LookupBFI = LookupBFI; + 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; } + +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 { + 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()); + } - std::string getNodeLabel(CallGraphNode *Node, CallGraph *Graph) { + 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"; + 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 *F = Node->getFunction(); + if (F == nullptr || F->isDeclaration()) + return ""; + + Function *SuccFunction = (*I)->getFunction(); + if (SuccFunction == nullptr) + return ""; + + uint64_t counter = getNumOfCalls(*F, *SuccFunction, CGInfo->LookupBFI); + std::string Attrs = "label=\"" + std::to_string(counter) + "\""; + 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; + 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; + 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; Index: lib/Analysis/DomPrinter.cpp =================================================================== --- lib/Analysis/DomPrinter.cpp +++ lib/Analysis/DomPrinter.cpp @@ -38,13 +38,12 @@ 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); } }; Index: lib/Analysis/HeatUtils.cpp =================================================================== --- lib/Analysis/HeatUtils.cpp +++ lib/Analysis/HeatUtils.cpp @@ -0,0 +1,123 @@ +//===-- 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(Module &M) { + for (Function &F : M) { + for (BasicBlock &BB : F) { + Instruction *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(Function &callerFunction, Function &calledFunction, + function_ref LookupBFI, + bool useHeuristic) { + auto *BFI = LookupBFI(callerFunction); + uint64_t counter = 0; + for (BasicBlock &BB : callerFunction) { + uint64_t freq = getBlockFreq(&BB, BFI, useHeuristic); + for (Instruction &I : BB) { + if (CallInst *Call = dyn_cast(&I)) { + if (Call->getCalledFunction() == (&calledFunction)) + counter += freq; + } + } + } + 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 (Function &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 Index: lib/Analysis/RegionPrinter.cpp =================================================================== --- lib/Analysis/RegionPrinter.cpp +++ lib/Analysis/RegionPrinter.cpp @@ -47,11 +47,11 @@ 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"; Index: lib/Transforms/Scalar/NewGVN.cpp =================================================================== --- lib/Transforms/Scalar/NewGVN.cpp +++ lib/Transforms/Scalar/NewGVN.cpp @@ -895,7 +895,7 @@ #ifndef NDEBUG static std::string getBlockName(const BasicBlock *B) { - return DOTGraphTraits::getSimpleNodeLabel(B, nullptr); + return DOTGraphTraits::getSimpleNodeLabel(B, nullptr); } #endif