10b57cec5SDimitry Andric //===- CallPrinter.cpp - DOT printer for call graph -----------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines '-dot-callgraph', which emit a callgraph.<fnname>.dot 100b57cec5SDimitry Andric // containing the call graph of a module. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // There is also a pass available to directly call dotty ('-view-callgraph'). 130b57cec5SDimitry Andric // 140b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric #include "llvm/Analysis/CallPrinter.h" 175ffd83dbSDimitry Andric #include "llvm/ADT/DenseMap.h" 185ffd83dbSDimitry Andric #include "llvm/ADT/SmallSet.h" 1981ad6265SDimitry Andric #include "llvm/Analysis/BlockFrequencyInfo.h" 2081ad6265SDimitry Andric #include "llvm/Analysis/CallGraph.h" 2181ad6265SDimitry Andric #include "llvm/Analysis/HeatUtils.h" 2281ad6265SDimitry Andric #include "llvm/IR/Instructions.h" 23*0fca6ea1SDimitry Andric #include "llvm/IR/Module.h" 2481ad6265SDimitry Andric #include "llvm/InitializePasses.h" 2581ad6265SDimitry Andric #include "llvm/Support/CommandLine.h" 2681ad6265SDimitry Andric #include "llvm/Support/DOTGraphTraits.h" 2781ad6265SDimitry Andric #include "llvm/Support/GraphWriter.h" 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric using namespace llvm; 300b57cec5SDimitry Andric 3181ad6265SDimitry Andric namespace llvm { 3281ad6265SDimitry Andric template <class GraphType> struct GraphTraits; 33*0fca6ea1SDimitry Andric } // namespace llvm 3481ad6265SDimitry Andric 355ffd83dbSDimitry Andric // This option shows static (relative) call counts. 365ffd83dbSDimitry Andric // FIXME: 375ffd83dbSDimitry Andric // Need to show real counts when profile data is available 385ffd83dbSDimitry Andric static cl::opt<bool> ShowHeatColors("callgraph-heat-colors", cl::init(false), 395ffd83dbSDimitry Andric cl::Hidden, 405ffd83dbSDimitry Andric cl::desc("Show heat colors in call-graph")); 415ffd83dbSDimitry Andric 425ffd83dbSDimitry Andric static cl::opt<bool> 435ffd83dbSDimitry Andric ShowEdgeWeight("callgraph-show-weights", cl::init(false), cl::Hidden, 445ffd83dbSDimitry Andric cl::desc("Show edges labeled with weights")); 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric static cl::opt<bool> 475ffd83dbSDimitry Andric CallMultiGraph("callgraph-multigraph", cl::init(false), cl::Hidden, 485ffd83dbSDimitry Andric cl::desc("Show call-multigraph (do not remove parallel edges)")); 495ffd83dbSDimitry Andric 505ffd83dbSDimitry Andric static cl::opt<std::string> CallGraphDotFilenamePrefix( 515ffd83dbSDimitry Andric "callgraph-dot-filename-prefix", cl::Hidden, 525ffd83dbSDimitry Andric cl::desc("The prefix used for the CallGraph dot file names.")); 535ffd83dbSDimitry Andric 540b57cec5SDimitry Andric namespace llvm { 550b57cec5SDimitry Andric 565ffd83dbSDimitry Andric class CallGraphDOTInfo { 575ffd83dbSDimitry Andric private: 585ffd83dbSDimitry Andric Module *M; 595ffd83dbSDimitry Andric CallGraph *CG; 605ffd83dbSDimitry Andric DenseMap<const Function *, uint64_t> Freq; 615ffd83dbSDimitry Andric uint64_t MaxFreq; 620b57cec5SDimitry Andric 635ffd83dbSDimitry Andric public: 645ffd83dbSDimitry Andric std::function<BlockFrequencyInfo *(Function &)> LookupBFI; 650b57cec5SDimitry Andric 665ffd83dbSDimitry Andric CallGraphDOTInfo(Module *M, CallGraph *CG, 675ffd83dbSDimitry Andric function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) 685ffd83dbSDimitry Andric : M(M), CG(CG), LookupBFI(LookupBFI) { 695ffd83dbSDimitry Andric MaxFreq = 0; 700b57cec5SDimitry Andric 71fe6060f1SDimitry Andric for (Function &F : M->getFunctionList()) { 725ffd83dbSDimitry Andric uint64_t localSumFreq = 0; 735ffd83dbSDimitry Andric SmallSet<Function *, 16> Callers; 74fe6060f1SDimitry Andric for (User *U : F.users()) 755ffd83dbSDimitry Andric if (isa<CallInst>(U)) 765ffd83dbSDimitry Andric Callers.insert(cast<Instruction>(U)->getFunction()); 77fe6060f1SDimitry Andric for (Function *Caller : Callers) 78fe6060f1SDimitry Andric localSumFreq += getNumOfCalls(*Caller, F); 795ffd83dbSDimitry Andric if (localSumFreq >= MaxFreq) 805ffd83dbSDimitry Andric MaxFreq = localSumFreq; 81fe6060f1SDimitry Andric Freq[&F] = localSumFreq; 825ffd83dbSDimitry Andric } 835ffd83dbSDimitry Andric if (!CallMultiGraph) 845ffd83dbSDimitry Andric removeParallelEdges(); 855ffd83dbSDimitry Andric } 865ffd83dbSDimitry Andric 875ffd83dbSDimitry Andric Module *getModule() const { return M; } 885ffd83dbSDimitry Andric 895ffd83dbSDimitry Andric CallGraph *getCallGraph() const { return CG; } 905ffd83dbSDimitry Andric 915ffd83dbSDimitry Andric uint64_t getFreq(const Function *F) { return Freq[F]; } 925ffd83dbSDimitry Andric 935ffd83dbSDimitry Andric uint64_t getMaxFreq() { return MaxFreq; } 945ffd83dbSDimitry Andric 955ffd83dbSDimitry Andric private: 965ffd83dbSDimitry Andric void removeParallelEdges() { 975ffd83dbSDimitry Andric for (auto &I : (*CG)) { 985ffd83dbSDimitry Andric CallGraphNode *Node = I.second.get(); 995ffd83dbSDimitry Andric 1005ffd83dbSDimitry Andric bool FoundParallelEdge = true; 1015ffd83dbSDimitry Andric while (FoundParallelEdge) { 1025ffd83dbSDimitry Andric SmallSet<Function *, 16> Visited; 1035ffd83dbSDimitry Andric FoundParallelEdge = false; 1045ffd83dbSDimitry Andric for (auto CI = Node->begin(), CE = Node->end(); CI != CE; CI++) { 1055ffd83dbSDimitry Andric if (!(Visited.insert(CI->second->getFunction())).second) { 1065ffd83dbSDimitry Andric FoundParallelEdge = true; 1075ffd83dbSDimitry Andric Node->removeCallEdge(CI); 1085ffd83dbSDimitry Andric break; 1095ffd83dbSDimitry Andric } 1105ffd83dbSDimitry Andric } 1115ffd83dbSDimitry Andric } 1125ffd83dbSDimitry Andric } 1130b57cec5SDimitry Andric } 1140b57cec5SDimitry Andric }; 1150b57cec5SDimitry Andric 1165ffd83dbSDimitry Andric template <> 1175ffd83dbSDimitry Andric struct GraphTraits<CallGraphDOTInfo *> 1185ffd83dbSDimitry Andric : public GraphTraits<const CallGraphNode *> { 1195ffd83dbSDimitry Andric static NodeRef getEntryNode(CallGraphDOTInfo *CGInfo) { 1205ffd83dbSDimitry Andric // Start at the external node! 1215ffd83dbSDimitry Andric return CGInfo->getCallGraph()->getExternalCallingNode(); 1225ffd83dbSDimitry Andric } 1235ffd83dbSDimitry Andric 1245ffd83dbSDimitry Andric typedef std::pair<const Function *const, std::unique_ptr<CallGraphNode>> 1255ffd83dbSDimitry Andric PairTy; 1265ffd83dbSDimitry Andric static const CallGraphNode *CGGetValuePtr(const PairTy &P) { 1275ffd83dbSDimitry Andric return P.second.get(); 1285ffd83dbSDimitry Andric } 1295ffd83dbSDimitry Andric 1305ffd83dbSDimitry Andric // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 1315ffd83dbSDimitry Andric typedef mapped_iterator<CallGraph::const_iterator, decltype(&CGGetValuePtr)> 1325ffd83dbSDimitry Andric nodes_iterator; 1335ffd83dbSDimitry Andric 1345ffd83dbSDimitry Andric static nodes_iterator nodes_begin(CallGraphDOTInfo *CGInfo) { 1355ffd83dbSDimitry Andric return nodes_iterator(CGInfo->getCallGraph()->begin(), &CGGetValuePtr); 1365ffd83dbSDimitry Andric } 1375ffd83dbSDimitry Andric static nodes_iterator nodes_end(CallGraphDOTInfo *CGInfo) { 1385ffd83dbSDimitry Andric return nodes_iterator(CGInfo->getCallGraph()->end(), &CGGetValuePtr); 1395ffd83dbSDimitry Andric } 1405ffd83dbSDimitry Andric }; 1415ffd83dbSDimitry Andric 1425ffd83dbSDimitry Andric template <> 1435ffd83dbSDimitry Andric struct DOTGraphTraits<CallGraphDOTInfo *> : public DefaultDOTGraphTraits { 1445ffd83dbSDimitry Andric 1455ffd83dbSDimitry Andric DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} 1465ffd83dbSDimitry Andric 1475ffd83dbSDimitry Andric static std::string getGraphName(CallGraphDOTInfo *CGInfo) { 1485ffd83dbSDimitry Andric return "Call graph: " + 1495ffd83dbSDimitry Andric std::string(CGInfo->getModule()->getModuleIdentifier()); 1505ffd83dbSDimitry Andric } 1515ffd83dbSDimitry Andric 152e8d8bef9SDimitry Andric static bool isNodeHidden(const CallGraphNode *Node, 153e8d8bef9SDimitry Andric const CallGraphDOTInfo *CGInfo) { 1545ffd83dbSDimitry Andric if (CallMultiGraph || Node->getFunction()) 1555ffd83dbSDimitry Andric return false; 1565ffd83dbSDimitry Andric return true; 1575ffd83dbSDimitry Andric } 1585ffd83dbSDimitry Andric 1595ffd83dbSDimitry Andric std::string getNodeLabel(const CallGraphNode *Node, 1605ffd83dbSDimitry Andric CallGraphDOTInfo *CGInfo) { 1615ffd83dbSDimitry Andric if (Node == CGInfo->getCallGraph()->getExternalCallingNode()) 1625ffd83dbSDimitry Andric return "external caller"; 1635ffd83dbSDimitry Andric if (Node == CGInfo->getCallGraph()->getCallsExternalNode()) 1645ffd83dbSDimitry Andric return "external callee"; 1655ffd83dbSDimitry Andric 1665ffd83dbSDimitry Andric if (Function *Func = Node->getFunction()) 1675ffd83dbSDimitry Andric return std::string(Func->getName()); 1685ffd83dbSDimitry Andric return "external node"; 1695ffd83dbSDimitry Andric } 1705ffd83dbSDimitry Andric static const CallGraphNode *CGGetValuePtr(CallGraphNode::CallRecord P) { 1715ffd83dbSDimitry Andric return P.second; 1725ffd83dbSDimitry Andric } 1735ffd83dbSDimitry Andric 1745ffd83dbSDimitry Andric // nodes_iterator/begin/end - Allow iteration over all nodes in the graph 1755ffd83dbSDimitry Andric typedef mapped_iterator<CallGraphNode::const_iterator, 1765ffd83dbSDimitry Andric decltype(&CGGetValuePtr)> 1775ffd83dbSDimitry Andric nodes_iterator; 1785ffd83dbSDimitry Andric 1795ffd83dbSDimitry Andric std::string getEdgeAttributes(const CallGraphNode *Node, nodes_iterator I, 1805ffd83dbSDimitry Andric CallGraphDOTInfo *CGInfo) { 1815ffd83dbSDimitry Andric if (!ShowEdgeWeight) 1825ffd83dbSDimitry Andric return ""; 1835ffd83dbSDimitry Andric 1845ffd83dbSDimitry Andric Function *Caller = Node->getFunction(); 1855ffd83dbSDimitry Andric if (Caller == nullptr || Caller->isDeclaration()) 1865ffd83dbSDimitry Andric return ""; 1875ffd83dbSDimitry Andric 1885ffd83dbSDimitry Andric Function *Callee = (*I)->getFunction(); 1895ffd83dbSDimitry Andric if (Callee == nullptr) 1905ffd83dbSDimitry Andric return ""; 1915ffd83dbSDimitry Andric 1925ffd83dbSDimitry Andric uint64_t Counter = getNumOfCalls(*Caller, *Callee); 1935ffd83dbSDimitry Andric double Width = 1945ffd83dbSDimitry Andric 1 + 2 * (double(Counter) / CGInfo->getMaxFreq()); 1955ffd83dbSDimitry Andric std::string Attrs = "label=\"" + std::to_string(Counter) + 1965ffd83dbSDimitry Andric "\" penwidth=" + std::to_string(Width); 1975ffd83dbSDimitry Andric return Attrs; 1985ffd83dbSDimitry Andric } 1995ffd83dbSDimitry Andric 2005ffd83dbSDimitry Andric std::string getNodeAttributes(const CallGraphNode *Node, 2015ffd83dbSDimitry Andric CallGraphDOTInfo *CGInfo) { 2025ffd83dbSDimitry Andric Function *F = Node->getFunction(); 2035ffd83dbSDimitry Andric if (F == nullptr) 2045ffd83dbSDimitry Andric return ""; 205e8d8bef9SDimitry Andric std::string attrs; 2065ffd83dbSDimitry Andric if (ShowHeatColors) { 2075ffd83dbSDimitry Andric uint64_t freq = CGInfo->getFreq(F); 2085ffd83dbSDimitry Andric std::string color = getHeatColor(freq, CGInfo->getMaxFreq()); 2095ffd83dbSDimitry Andric std::string edgeColor = (freq <= (CGInfo->getMaxFreq() / 2)) 2105ffd83dbSDimitry Andric ? getHeatColor(0) 2115ffd83dbSDimitry Andric : getHeatColor(1); 2125ffd83dbSDimitry Andric attrs = "color=\"" + edgeColor + "ff\", style=filled, fillcolor=\"" + 2135ffd83dbSDimitry Andric color + "80\""; 2145ffd83dbSDimitry Andric } 2155ffd83dbSDimitry Andric return attrs; 2160b57cec5SDimitry Andric } 2170b57cec5SDimitry Andric }; 2180b57cec5SDimitry Andric 219*0fca6ea1SDimitry Andric } // namespace llvm 2200b57cec5SDimitry Andric 2210b57cec5SDimitry Andric namespace { 22281ad6265SDimitry Andric void doCallGraphDOTPrinting( 22381ad6265SDimitry Andric Module &M, function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) { 22481ad6265SDimitry Andric std::string Filename; 22581ad6265SDimitry Andric if (!CallGraphDotFilenamePrefix.empty()) 22681ad6265SDimitry Andric Filename = (CallGraphDotFilenamePrefix + ".callgraph.dot"); 22781ad6265SDimitry Andric else 22881ad6265SDimitry Andric Filename = (std::string(M.getModuleIdentifier()) + ".callgraph.dot"); 22981ad6265SDimitry Andric errs() << "Writing '" << Filename << "'..."; 23081ad6265SDimitry Andric 23181ad6265SDimitry Andric std::error_code EC; 23281ad6265SDimitry Andric raw_fd_ostream File(Filename, EC, sys::fs::OF_Text); 23381ad6265SDimitry Andric 23481ad6265SDimitry Andric CallGraph CG(M); 23581ad6265SDimitry Andric CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); 23681ad6265SDimitry Andric 23781ad6265SDimitry Andric if (!EC) 23881ad6265SDimitry Andric WriteGraph(File, &CFGInfo); 23981ad6265SDimitry Andric else 24081ad6265SDimitry Andric errs() << " error opening file for writing!"; 24181ad6265SDimitry Andric errs() << "\n"; 24281ad6265SDimitry Andric } 24381ad6265SDimitry Andric 24481ad6265SDimitry Andric void viewCallGraph(Module &M, 24581ad6265SDimitry Andric function_ref<BlockFrequencyInfo *(Function &)> LookupBFI) { 24681ad6265SDimitry Andric CallGraph CG(M); 24781ad6265SDimitry Andric CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); 24881ad6265SDimitry Andric 24981ad6265SDimitry Andric std::string Title = 25081ad6265SDimitry Andric DOTGraphTraits<CallGraphDOTInfo *>::getGraphName(&CFGInfo); 25181ad6265SDimitry Andric ViewGraph(&CFGInfo, "callgraph", true, Title); 25281ad6265SDimitry Andric } 25381ad6265SDimitry Andric } // namespace 25481ad6265SDimitry Andric 25581ad6265SDimitry Andric namespace llvm { 25681ad6265SDimitry Andric PreservedAnalyses CallGraphDOTPrinterPass::run(Module &M, 25781ad6265SDimitry Andric ModuleAnalysisManager &AM) { 25881ad6265SDimitry Andric FunctionAnalysisManager &FAM = 25981ad6265SDimitry Andric AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 26081ad6265SDimitry Andric 26181ad6265SDimitry Andric auto LookupBFI = [&FAM](Function &F) { 26281ad6265SDimitry Andric return &FAM.getResult<BlockFrequencyAnalysis>(F); 26381ad6265SDimitry Andric }; 26481ad6265SDimitry Andric 26581ad6265SDimitry Andric doCallGraphDOTPrinting(M, LookupBFI); 26681ad6265SDimitry Andric 26781ad6265SDimitry Andric return PreservedAnalyses::all(); 26881ad6265SDimitry Andric } 26981ad6265SDimitry Andric 27081ad6265SDimitry Andric PreservedAnalyses CallGraphViewerPass::run(Module &M, 27181ad6265SDimitry Andric ModuleAnalysisManager &AM) { 27281ad6265SDimitry Andric 27381ad6265SDimitry Andric FunctionAnalysisManager &FAM = 27481ad6265SDimitry Andric AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 27581ad6265SDimitry Andric 27681ad6265SDimitry Andric auto LookupBFI = [&FAM](Function &F) { 27781ad6265SDimitry Andric return &FAM.getResult<BlockFrequencyAnalysis>(F); 27881ad6265SDimitry Andric }; 27981ad6265SDimitry Andric 28081ad6265SDimitry Andric viewCallGraph(M, LookupBFI); 28181ad6265SDimitry Andric 28281ad6265SDimitry Andric return PreservedAnalyses::all(); 28381ad6265SDimitry Andric } 28481ad6265SDimitry Andric } // namespace llvm 28581ad6265SDimitry Andric 28681ad6265SDimitry Andric namespace { 2875ffd83dbSDimitry Andric // Viewer 2885ffd83dbSDimitry Andric class CallGraphViewer : public ModulePass { 2895ffd83dbSDimitry Andric public: 2900b57cec5SDimitry Andric static char ID; 2915ffd83dbSDimitry Andric CallGraphViewer() : ModulePass(ID) {} 2920b57cec5SDimitry Andric 2935ffd83dbSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 2945ffd83dbSDimitry Andric bool runOnModule(Module &M) override; 2950b57cec5SDimitry Andric }; 2960b57cec5SDimitry Andric 2975ffd83dbSDimitry Andric void CallGraphViewer::getAnalysisUsage(AnalysisUsage &AU) const { 2985ffd83dbSDimitry Andric ModulePass::getAnalysisUsage(AU); 2995ffd83dbSDimitry Andric AU.addRequired<BlockFrequencyInfoWrapperPass>(); 3005ffd83dbSDimitry Andric AU.setPreservesAll(); 3010b57cec5SDimitry Andric } 3025ffd83dbSDimitry Andric 3035ffd83dbSDimitry Andric bool CallGraphViewer::runOnModule(Module &M) { 3045ffd83dbSDimitry Andric auto LookupBFI = [this](Function &F) { 3055ffd83dbSDimitry Andric return &this->getAnalysis<BlockFrequencyInfoWrapperPass>(F).getBFI(); 3060b57cec5SDimitry Andric }; 3070b57cec5SDimitry Andric 30881ad6265SDimitry Andric viewCallGraph(M, LookupBFI); 3095ffd83dbSDimitry Andric 3105ffd83dbSDimitry Andric return false; 3115ffd83dbSDimitry Andric } 3125ffd83dbSDimitry Andric 3135ffd83dbSDimitry Andric // DOT Printer 3145ffd83dbSDimitry Andric 3155ffd83dbSDimitry Andric class CallGraphDOTPrinter : public ModulePass { 3165ffd83dbSDimitry Andric public: 3175ffd83dbSDimitry Andric static char ID; 3185ffd83dbSDimitry Andric CallGraphDOTPrinter() : ModulePass(ID) {} 3195ffd83dbSDimitry Andric 3205ffd83dbSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 3215ffd83dbSDimitry Andric bool runOnModule(Module &M) override; 3225ffd83dbSDimitry Andric }; 3235ffd83dbSDimitry Andric 3245ffd83dbSDimitry Andric void CallGraphDOTPrinter::getAnalysisUsage(AnalysisUsage &AU) const { 3255ffd83dbSDimitry Andric ModulePass::getAnalysisUsage(AU); 3265ffd83dbSDimitry Andric AU.addRequired<BlockFrequencyInfoWrapperPass>(); 3275ffd83dbSDimitry Andric AU.setPreservesAll(); 3285ffd83dbSDimitry Andric } 3295ffd83dbSDimitry Andric 3305ffd83dbSDimitry Andric bool CallGraphDOTPrinter::runOnModule(Module &M) { 3315ffd83dbSDimitry Andric auto LookupBFI = [this](Function &F) { 3325ffd83dbSDimitry Andric return &this->getAnalysis<BlockFrequencyInfoWrapperPass>(F).getBFI(); 3335ffd83dbSDimitry Andric }; 3345ffd83dbSDimitry Andric 33581ad6265SDimitry Andric doCallGraphDOTPrinting(M, LookupBFI); 3365ffd83dbSDimitry Andric 3375ffd83dbSDimitry Andric return false; 3385ffd83dbSDimitry Andric } 3395ffd83dbSDimitry Andric 3400b57cec5SDimitry Andric } // end anonymous namespace 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric char CallGraphViewer::ID = 0; 3430b57cec5SDimitry Andric INITIALIZE_PASS(CallGraphViewer, "view-callgraph", "View call graph", false, 3440b57cec5SDimitry Andric false) 3450b57cec5SDimitry Andric 3460b57cec5SDimitry Andric char CallGraphDOTPrinter::ID = 0; 3470b57cec5SDimitry Andric INITIALIZE_PASS(CallGraphDOTPrinter, "dot-callgraph", 3480b57cec5SDimitry Andric "Print call graph to 'dot' file", false, false) 3490b57cec5SDimitry Andric 3500b57cec5SDimitry Andric // Create methods available outside of this file, to use them 3510b57cec5SDimitry Andric // "include/llvm/LinkAllPasses.h". Otherwise the pass would be deleted by 3520b57cec5SDimitry Andric // the link time optimization. 3530b57cec5SDimitry Andric 3540b57cec5SDimitry Andric ModulePass *llvm::createCallGraphViewerPass() { return new CallGraphViewer(); } 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric ModulePass *llvm::createCallGraphDOTPrinterPass() { 3570b57cec5SDimitry Andric return new CallGraphDOTPrinter(); 3580b57cec5SDimitry Andric } 359