Index: llvm/include/llvm/Analysis/DOTGraphTraitsPass.h =================================================================== --- llvm/include/llvm/Analysis/DOTGraphTraitsPass.h +++ llvm/include/llvm/Analysis/DOTGraphTraitsPass.h @@ -181,6 +181,25 @@ std::string Name; }; +template +void WriteDOTGraphToFile(Function &F, GraphT &&Graph, + std::string FileNamePrefix, bool IsSimple) { + std::string Filename = FileNamePrefix + "." + F.getName().str() + ".dot"; + std::error_code EC; + + errs() << "Writing '" << Filename << "'..."; + + raw_fd_ostream File(Filename, EC, sys::fs::OF_TextWithCRLF); + std::string GraphName = DOTGraphTraits::getGraphName(Graph); + std::string Title = GraphName + " for '" + F.getName().str() + "' function"; + + if (!EC) + WriteGraph(File, Graph, IsSimple, Title); + else + errs() << " error opening file for writing!"; + errs() << "\n"; +} + } // end namespace llvm #endif Index: llvm/include/llvm/Analysis/DomPrinter.h =================================================================== --- llvm/include/llvm/Analysis/DomPrinter.h +++ llvm/include/llvm/Analysis/DomPrinter.h @@ -14,6 +14,20 @@ #ifndef LLVM_ANALYSIS_DOMPRINTER_H #define LLVM_ANALYSIS_DOMPRINTER_H +#include "llvm/IR/PassManager.h" + +namespace llvm { +class DomTreePrinterPass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +class DomTreeOnlyPrinterPass : public PassInfoMixin { +public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; +} // namespace llvm + namespace llvm { class FunctionPass; FunctionPass *createDomPrinterPass(); Index: llvm/lib/Analysis/DomPrinter.cpp =================================================================== --- llvm/lib/Analysis/DomPrinter.cpp +++ llvm/lib/Analysis/DomPrinter.cpp @@ -80,6 +80,19 @@ }; } +PreservedAnalyses DomTreePrinterPass::run(Function &F, + FunctionAnalysisManager &AM) { + WriteDOTGraphToFile(F, &AM.getResult(F), "dom", false); + return PreservedAnalyses::all(); +} + +PreservedAnalyses DomTreeOnlyPrinterPass::run(Function &F, + FunctionAnalysisManager &AM) { + WriteDOTGraphToFile(F, &AM.getResult(F), "domonly", + true); + return PreservedAnalyses::all(); +} + void DominatorTree::viewGraph(const Twine &Name, const Twine &Title) { #ifndef NDEBUG ViewGraph(this, Name, false, Title); Index: llvm/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/lib/Passes/PassBuilder.cpp +++ llvm/lib/Passes/PassBuilder.cpp @@ -35,6 +35,7 @@ #include "llvm/Analysis/DemandedBits.h" #include "llvm/Analysis/DependenceAnalysis.h" #include "llvm/Analysis/DivergenceAnalysis.h" +#include "llvm/Analysis/DomPrinter.h" #include "llvm/Analysis/DominanceFrontier.h" #include "llvm/Analysis/FunctionPropertiesAnalysis.h" #include "llvm/Analysis/GlobalsModRef.h" Index: llvm/lib/Passes/PassRegistry.def =================================================================== --- llvm/lib/Passes/PassRegistry.def +++ llvm/lib/Passes/PassRegistry.def @@ -254,6 +254,8 @@ FUNCTION_PASS("dse", DSEPass()) FUNCTION_PASS("dot-cfg", CFGPrinterPass()) FUNCTION_PASS("dot-cfg-only", CFGOnlyPrinterPass()) +FUNCTION_PASS("dot-dom", DomTreePrinterPass()) +FUNCTION_PASS("dot-dom-only", DomTreeOnlyPrinterPass()) FUNCTION_PASS("fix-irreducible", FixIrreduciblePass()) FUNCTION_PASS("flattencfg", FlattenCFGPass()) FUNCTION_PASS("make-guards-explicit", MakeGuardsExplicitPass()) Index: llvm/test/Analysis/Dominators/print-dot-dom.ll =================================================================== --- /dev/null +++ llvm/test/Analysis/Dominators/print-dot-dom.ll @@ -0,0 +1,71 @@ +; RUN: opt %s -passes=dot-dom -disable-output +; RUN: FileCheck %s -input-file=dom.test1.dot -check-prefix=TEST1 +; RUN: FileCheck %s -input-file=dom.test2.dot -check-prefix=TEST2 + +define void @test1() { +; TEST1: digraph "Dominator tree for 'test1' function" +; TEST1-NEXT: label="Dominator tree for 'test1' function" +; TEST1: Node0x[[EntryID:.*]] [shape=record,label="{entry: +; TEST1-NEXT: Node0x[[EntryID]] -> Node0x[[A_ID:.*]]; +; TEST1-NEXT: Node0x[[EntryID]] -> Node0x[[C_ID:.*]]; +; TEST1-NEXT: Node0x[[EntryID]] -> Node0x[[B_ID:.*]]; +; TEST1-NEXT: Node0x[[A_ID]] [shape=record,label="{a: +; TEST1-NEXT: Node0x[[C_ID]] [shape=record,label="{c: +; TEST1-NEXT: Node0x[[C_ID]] -> Node0x[[D_ID:.*]]; +; TEST1-NEXT: Node0x[[C_ID]] -> Node0x[[E_ID:.*]]; +; TEST1-NEXT: Node0x[[D_ID]] [shape=record,label="{d: +; TEST1-NEXT: Node0x[[E_ID]] [shape=record,label="{e: +; TEST1-NEXT: Node0x[[B_ID]] [shape=record,label="{b: + +entry: + br i1 undef, label %a, label %b + +a: + br label %c + +b: + br label %c + +c: + br i1 undef, label %d, label %e + +d: + ret void + +e: + ret void +} + +define void @test2() { +; TEST2: digraph "Dominator tree for 'test2' function" +; TEST2-NEXT: label="Dominator tree for 'test2' function" +; TEST2: Node0x[[EntryID:.*]] [shape=record,label="{entry: +; TEST2-NEXT: Node0x[[EntryID]] -> Node0x[[A_ID:.*]]; +; TEST2-NEXT: Node0x[[A_ID]] [shape=record,label="{a: +; TEST2-NEXT: Node0x[[A_ID]] -> Node0x[[B_ID:.*]]; +; TEST2-NEXT: Node0x[[B_ID]] [shape=record,label="{b: +; TEST2-NEXT: Node0x[[B_ID]] -> Node0x[[C_ID:.*]]; +; TEST2-NEXT: Node0x[[C_ID]] [shape=record,label="{c: +; TEST2-NEXT: Node0x[[C_ID]] -> Node0x[[D_ID:.*]]; +; TEST2-NEXT: Node0x[[C_ID]] -> Node0x[[E_ID:.*]]; +; TEST2-NEXT: Node0x[[D_ID]] [shape=record,label="{d: +; TEST2-NEXT: Node0x[[E_ID]] [shape=record,label="{e: + +entry: + br label %a + +a: + br label %b + +b: + br i1 undef, label %a, label %c + +c: + br i1 undef, label %d, label %e + +d: + br i1 undef, label %a, label %e + +e: + ret void +}