Add option to dump CFGs in (simple) graphviz format during all passes.

Summary:
I noticed the BinaryFunction::viewGraph() method that hadn't been implemented
and decided I could use a simple DOT dumper for CFGs while working on the indirect
call optimization.

I've implemented the bare minimum for the dumper.  It's just nodes+BB labels with
dges. We can add more detailed information as needed/desired.

(cherry picked from FBD3509326)
This commit is contained in:
Bill Nell 2016-07-01 08:40:56 -07:00 committed by Maksim Panchenko
parent 6eb4e5b687
commit 260f6fbdb6
4 changed files with 103 additions and 2 deletions

View File

@ -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 <limits>
#include <queue>
@ -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<MAX_PATH> 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);

View File

@ -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;

View File

@ -17,6 +17,7 @@
namespace opts {
extern llvm::cl::opt<bool> PrintAll;
extern llvm::cl::opt<bool> DumpDotAll;
extern llvm::cl::opt<bool> PrintReordered;
extern llvm::cl::opt<bool> PrintEHRanges;
extern llvm::cl::opt<bool> 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");
}
}
}

View File

@ -148,6 +148,11 @@ cl::opt<bool>
PrintAll("print-all", cl::desc("print functions after each stage"),
cl::Hidden);
cl::opt<bool>
DumpDotAll("dump-dot-all",
cl::desc("dump function CFGs to graphviz format after each stage"),
cl::Hidden);
static cl::opt<bool>
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