diff --git a/llvm/include/llvm/Analysis/CallPrinter.h b/llvm/include/llvm/Analysis/CallPrinter.h --- a/llvm/include/llvm/Analysis/CallPrinter.h +++ b/llvm/include/llvm/Analysis/CallPrinter.h @@ -14,10 +14,24 @@ #ifndef LLVM_ANALYSIS_CALLPRINTER_H #define LLVM_ANALYSIS_CALLPRINTER_H +#include "llvm/IR/PassManager.h" + namespace llvm { class ModulePass; +/// Pass for printing the call graph to a dot file +class CallGraphDOTPrinterPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +/// Pass for viewing the call graph +class CallGraphViewerPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + ModulePass *createCallGraphViewerPass(); ModulePass *createCallGraphDOTPrinterPass(); diff --git a/llvm/lib/Analysis/CallPrinter.cpp b/llvm/lib/Analysis/CallPrinter.cpp --- a/llvm/lib/Analysis/CallPrinter.cpp +++ b/llvm/lib/Analysis/CallPrinter.cpp @@ -217,6 +217,71 @@ } // end llvm namespace +namespace { +void doCallGraphDOTPrinting( + Module &M, function_ref LookupBFI) { + std::string Filename; + if (!CallGraphDotFilenamePrefix.empty()) + Filename = (CallGraphDotFilenamePrefix + ".callgraph.dot"); + else + Filename = (std::string(M.getModuleIdentifier()) + ".callgraph.dot"); + errs() << "Writing '" << Filename << "'..."; + + std::error_code EC; + raw_fd_ostream File(Filename, EC, sys::fs::OF_Text); + + CallGraph CG(M); + CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); + + if (!EC) + WriteGraph(File, &CFGInfo); + else + errs() << " error opening file for writing!"; + errs() << "\n"; +} + +void viewCallGraph(Module &M, + function_ref LookupBFI) { + CallGraph CG(M); + CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); + + std::string Title = + DOTGraphTraits::getGraphName(&CFGInfo); + ViewGraph(&CFGInfo, "callgraph", true, Title); +} +} // namespace + +namespace llvm { +PreservedAnalyses CallGraphDOTPrinterPass::run(Module &M, + ModuleAnalysisManager &AM) { + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + + auto LookupBFI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + + doCallGraphDOTPrinting(M, LookupBFI); + + return PreservedAnalyses::all(); +} + +PreservedAnalyses CallGraphViewerPass::run(Module &M, + ModuleAnalysisManager &AM) { + + FunctionAnalysisManager &FAM = + AM.getResult(M).getManager(); + + auto LookupBFI = [&FAM](Function &F) { + return &FAM.getResult(F); + }; + + viewCallGraph(M, LookupBFI); + + return PreservedAnalyses::all(); +} +} // namespace llvm + namespace { // Viewer class CallGraphViewer : public ModulePass { @@ -239,12 +304,7 @@ return &this->getAnalysis(F).getBFI(); }; - CallGraph CG(M); - CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); - - std::string Title = - DOTGraphTraits::getGraphName(&CFGInfo); - ViewGraph(&CFGInfo, "callgraph", true, Title); + viewCallGraph(M, LookupBFI); return false; } @@ -271,24 +331,7 @@ return &this->getAnalysis(F).getBFI(); }; - std::string Filename; - if (!CallGraphDotFilenamePrefix.empty()) - Filename = (CallGraphDotFilenamePrefix + ".callgraph.dot"); - else - Filename = (std::string(M.getModuleIdentifier()) + ".callgraph.dot"); - errs() << "Writing '" << Filename << "'..."; - - std::error_code EC; - raw_fd_ostream File(Filename, EC, sys::fs::OF_Text); - - CallGraph CG(M); - CallGraphDOTInfo CFGInfo(&M, &CG, LookupBFI); - - if (!EC) - WriteGraph(File, &CFGInfo); - else - errs() << " error opening file for writing!"; - errs() << "\n"; + doCallGraphDOTPrinting(M, LookupBFI); return false; } diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -27,6 +27,7 @@ #include "llvm/Analysis/CFLSteensAliasAnalysis.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/CallPrinter.h" #include "llvm/Analysis/CostModel.h" #include "llvm/Analysis/CycleAnalysis.h" #include "llvm/Analysis/DDG.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -89,6 +89,8 @@ MODULE_PASS("pgo-instr-use", PGOInstrumentationUse()) MODULE_PASS("print-profile-summary", ProfileSummaryPrinterPass(dbgs())) MODULE_PASS("print-callgraph", CallGraphPrinterPass(dbgs())) +MODULE_PASS("dot-callgraph", CallGraphDOTPrinterPass()) +MODULE_PASS("view-callgraph", CallGraphViewerPass()) MODULE_PASS("print", PrintModulePass(dbgs())) MODULE_PASS("print-lcg", LazyCallGraphPrinterPass(dbgs())) MODULE_PASS("print-lcg-dot", LazyCallGraphDOTPrinterPass(dbgs())) diff --git a/llvm/test/Other/heat-colors-graphs.ll b/llvm/test/Other/heat-colors-graphs.ll --- a/llvm/test/Other/heat-colors-graphs.ll +++ b/llvm/test/Other/heat-colors-graphs.ll @@ -1,9 +1,11 @@ ; RUN: opt %s -dot-cfg -cfg-heat-colors -cfg-dot-filename-prefix=%t -disable-output -; RUN: FileCheck %s -input-file=%t.f.dot +; RUN: FileCheck %s -input-file=%t.f.dot --check-prefixes=CHECK-CFG,CHECK-BOTH +; RUN: opt %s -dot-callgraph -callgraph-heat-colors -callgraph-dot-filename-prefix=%t -disable-output +; RUN: FileCheck %s -input-file=%t.callgraph.dot --check-prefix=CHECK-BOTH -; CHECK: color="#[[#%x,]]", style={{[a-z]+}}, fillcolor="#[[#%x,]]" -; CHECK: color="#[[#%x,]]", style={{[a-z]+}}, fillcolor="#[[#%x,]]" -; CHECK: color="#[[#%x,]]", style={{[a-z]+}}, fillcolor="#[[#%x,]]" +; CHECK-BOTH: color="#[[#%x,]]", style={{[a-z]+}}, fillcolor="#[[#%x,]]" +; CHECK-CFG: color="#[[#%x,]]", style={{[a-z]+}}, fillcolor="#[[#%x,]]" +; CHECK-CFG: color="#[[#%x,]]", style={{[a-z]+}}, fillcolor="#[[#%x,]]" define void @f(i32) { entry: diff --git a/llvm/test/Other/heat-colors-multigraph.ll b/llvm/test/Other/heat-colors-multigraph.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/heat-colors-multigraph.ll @@ -0,0 +1,16 @@ +; RUN: opt %s -dot-callgraph -callgraph-multigraph -callgraph-dot-filename-prefix=%t -disable-output +; RUN: FileCheck %s -input-file=%t.callgraph.dot --check-prefix=CHECK-MULTIGRAPH +; RUN: opt %s -dot-callgraph -callgraph-dot-filename-prefix=%t -disable-output +; RUN: FileCheck %s -input-file=%t.callgraph.dot --check-prefix=CHECK + +; CHECK-MULTIGRAPH: {external caller} +; CHECK-NOT: {external caller} + +define void @bar() { + ret void +} + +define void @foo() { + call void @bar() + ret void +} \ No newline at end of file