diff --git a/bolt/BinaryFunction.cpp b/bolt/BinaryFunction.cpp index a0fdf052ed02..fff33e4d8850 100644 --- a/bolt/BinaryFunction.cpp +++ b/bolt/BinaryFunction.cpp @@ -24,6 +24,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/GraphWriter.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -1316,6 +1317,76 @@ void BinaryFunction::modifyLayout(LayoutType Type, bool Split) { fixBranches(); } +namespace { + +#ifndef MAX_PATH +#define MAX_PATH 255 +#endif + +std::string constructFilename(std::string Filename, + std::string Annotation, + std::string Suffix) { + std::replace(Filename.begin(), Filename.end(), '/', '-'); + if (!Annotation.empty()) { + Annotation.insert(0, "-"); + } + if (Filename.size() + Annotation.size() + Suffix.size() > MAX_PATH) { + assert(Suffix.size() + Annotation.size() <= MAX_PATH); + dbgs() << "BOLT-WARNING: Filename \"" << Filename << Annotation << Suffix + << "\" exceeds the " << MAX_PATH << " size limit, truncating.\n"; + Filename.resize(MAX_PATH - (Suffix.size() + Annotation.size())); + } + Filename += Annotation; + Filename += Suffix; + return Filename; +} + +} + +void BinaryFunction::dumpGraph(raw_ostream& OS) const { + OS << "strict digraph \"" << getName() << "\" {\n"; + for (auto *BB : BasicBlocks) { + for (auto *Succ : BB->successors()) { + OS << "\"" << BB->getName() << "\" -> " + << "\"" << Succ->getName() << "\"\n"; + } + } + OS << "}\n"; +} + +void BinaryFunction::viewGraph() const { + SmallString Filename; + if (auto EC = sys::fs::createTemporaryFile("bolt-cfg", "dot", Filename)) { + dbgs() << "BOLT-WARNING: " << EC.message() << ", unable to create " + << " bolt-cfg-XXXXX.dot temporary file.\n"; + return; + } + dumpGraphToFile(Filename.str()); + if (DisplayGraph(Filename)) { + dbgs() << "BOLT-WARNING: Can't display " << Filename + << " with graphviz.\n"; + } + if (auto EC = sys::fs::remove(Filename)) { + dbgs() << "BOLT-WARNING: " << EC.message() << ", failed to remove " + << Filename.str() << "\n"; + } +} + +void BinaryFunction::dumpGraphForPass(std::string Annotation) const { + dumpGraphToFile(constructFilename(getName(), Annotation, ".dot")); +} + +void BinaryFunction::dumpGraphToFile(std::string Filename) const { + std::error_code EC; + raw_fd_ostream of(Filename, EC, sys::fs::F_None); + if (EC) { + dbgs() << "BOLT-WARNING: " << EC.message() << ", unable to open " + << Filename << " for output.\n"; + return; + } + dumpGraph(of); +} + const BinaryBasicBlock * BinaryFunction::getOriginalLayoutSuccessor(const BinaryBasicBlock *BB) const { auto I = std::upper_bound(begin(), end(), *BB); diff --git a/bolt/BinaryFunction.h b/bolt/BinaryFunction.h index 93dcf84897fe..9c2a54e62c43 100644 --- a/bolt/BinaryFunction.h +++ b/bolt/BinaryFunction.h @@ -392,9 +392,20 @@ public: void modifyLayout(LayoutType Type, bool Split); /// View CFG in graphviz program - void viewGraph(); + void viewGraph() const; - /// Get basic block index assuming it belongs to this function. + /// Dump CFG in graphviz format + void dumpGraph(raw_ostream& OS) const; + + /// Dump CFG in graphviz format to file. + void dumpGraphToFile(std::string Filename) const; + + /// Dump CFG in graphviz format to a file with a filename that is derived + /// from the function name and Annotation strings. Useful for dumping the + /// CFG after an optimization pass. + void dumpGraphForPass(std::string Annotation = "") const; + + /// Get basic block index assuming it belongs to this function. unsigned getIndex(const BinaryBasicBlock *BB) const { assert(BasicBlockIndices.find(BB) != BasicBlockIndices.end()); return BasicBlockIndices.find(BB)->second; diff --git a/bolt/BinaryPasses.cpp b/bolt/BinaryPasses.cpp index 5cc2bc6691e8..b525972d89e9 100644 --- a/bolt/BinaryPasses.cpp +++ b/bolt/BinaryPasses.cpp @@ -17,6 +17,7 @@ namespace opts { extern llvm::cl::opt PrintAll; +extern llvm::cl::opt DumpDotAll; extern llvm::cl::opt PrintReordered; extern llvm::cl::opt PrintEHRanges; extern llvm::cl::opt PrintUCE; @@ -343,6 +344,9 @@ void EliminateUnreachableBlocks::runOnFunction(BinaryFunction& Function) { if (opts::PrintAll || opts::PrintUCE) Function.print(errs(), "after unreachable code elimination", true); + + if (opts::DumpDotAll) + Function.dumpGraphForPass("unreachable-code"); } } @@ -379,6 +383,8 @@ void ReorderBasicBlocks::runOnFunctions( Function.modifyLayout(opts::ReorderBlocks, ShouldSplit); if (opts::PrintAll || opts::PrintReordered) Function.print(errs(), "after reordering blocks", true); + if (opts::DumpDotAll) + Function.dumpGraphForPass("reordering"); } } } @@ -409,6 +415,8 @@ void FixupFunctions::runOnFunctions( Function.updateEHRanges(); if (opts::PrintAll || opts::PrintEHRanges) Function.print(errs(), "after updating EH ranges", true); + if (opts::DumpDotAll) + Function.dumpGraphForPass("update-EH-ranges"); } } @@ -519,6 +527,9 @@ void SimplifyConditionalTailCalls::runOnFunctions( if (opts::PrintAll || opts::PrintReordered) { Function.print(errs(), "after tail call patching", true); } + if (opts::DumpDotAll) { + Function.dumpGraphForPass("tail-call-patching"); + } } } diff --git a/bolt/RewriteInstance.cpp b/bolt/RewriteInstance.cpp index ef9801f09b6e..3d80d5ef3e7f 100644 --- a/bolt/RewriteInstance.cpp +++ b/bolt/RewriteInstance.cpp @@ -148,6 +148,11 @@ cl::opt PrintAll("print-all", cl::desc("print functions after each stage"), cl::Hidden); +cl::opt +DumpDotAll("dump-dot-all", + cl::desc("dump function CFGs to graphviz format after each stage"), + cl::Hidden); + static cl::opt PrintCFG("print-cfg", cl::desc("print functions after CFG construction"), cl::Hidden); @@ -913,6 +918,9 @@ void RewriteInstance::disassembleFunctions() { if (opts::PrintAll || opts::PrintCFG) Function.print(errs(), "after building cfg", true); + if (opts::DumpDotAll) + Function.dumpGraphForPass("build-cfg"); + TotalScore += Function.getFunctionScore(); } // Iterate over all functions