Added GraphTraits to source-level CFGs (CFG and CFGBlock) to allow

(LLVM-provided) graph algorithms such as DFS and graph visualization
to work effortless on source-level CFGs.

Further cleanup on pretty printing of CFGs.  CFGBlock::dump and
CFGBlock::print now take the parent CFG as an argument.  This allows
CFGBlocks to print their own appropriate label indicating whether or
not they are the Entry/Exit/IndirectGotoBlock without the CFG::print
routine doing it instead.

Added Graphviz visualization for CFGs: CFG::viewCFG.  This employs the
GraphTraits just implemented.

Added "-view-cfg" mode the to clang driver.  This is identical to
"-dump-cfg" except that it calls Graphviz to visualize the CFGs
instead of dumping them to the terminal.

llvm-svn: 41580
This commit is contained in:
Ted Kremenek 2007-08-29 21:56:09 +00:00
parent 1ad4a6117b
commit 4e5f99da6c
5 changed files with 182 additions and 43 deletions

View File

@ -17,9 +17,13 @@
#include "clang/AST/StmtVisitor.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Config/config.h"
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <sstream>
using namespace clang;
namespace {
@ -871,44 +875,33 @@ CFG* CFG::buildCFG(Stmt* Statement) {
/// reverseStmts - Reverses the orders of statements within a CFGBlock.
void CFGBlock::reverseStmts() { std::reverse(Stmts.begin(),Stmts.end()); }
//===----------------------------------------------------------------------===//
// CFG pretty printing
//===----------------------------------------------------------------------===//
/// dump - A simple pretty printer of a CFG that outputs to stderr.
void CFG::dump() { print(std::cerr); }
void CFG::dump() const { print(std::cerr); }
/// print - A simple pretty printer of a CFG that outputs to an ostream.
void CFG::print(std::ostream& OS) {
// Print the Entry block.
if (begin() != end()) {
CFGBlock& Entry = getEntry();
OS << "\n [ B" << Entry.getBlockID() << " (ENTRY) ]\n";
Entry.print(OS);
}
void CFG::print(std::ostream& OS) const {
// Print the entry block.
getEntry().print(OS,this);
// Iterate through the CFGBlocks and print them one by one.
for (iterator I = Blocks.begin(), E = Blocks.end() ; I != E ; ++I) {
for (const_iterator I = Blocks.begin(), E = Blocks.end() ; I != E ; ++I) {
// Skip the entry block, because we already printed it.
if (&(*I) == &getEntry() || &(*I) == &getExit()) continue;
OS << "\n [ B" << I->getBlockID();
if (&(*I) == getIndirectGotoBlock())
OS << " (INDIRECT GOTO DISPATCH) ]\n";
else
OS << " ]\n";
I->print(OS);
I->print(OS,this);
}
// Print the Exit Block.
if (begin() != end()) {
CFGBlock& Exit = getExit();
OS << "\n [ B" << Exit.getBlockID() << " (EXIT) ]\n";
Exit.print(OS);
}
// Print the exit block.
getExit().print(OS,this);
OS << "\n";
}
namespace {
class CFGBlockTerminatorPrint : public StmtVisitor<CFGBlockTerminatorPrint,
@ -919,7 +912,7 @@ public:
void VisitIfStmt(IfStmt* I) {
OS << "if ";
I->getCond()->printPretty(std::cerr);
I->getCond()->printPretty(OS);
OS << "\n";
}
@ -962,16 +955,25 @@ public:
} // end anonymous namespace
/// dump - A simply pretty printer of a CFGBlock that outputs to stderr.
void CFGBlock::dump() { print(std::cerr); }
void CFGBlock::dump(const CFG* cfg) const { print(std::cerr,cfg); }
/// print - A simple pretty printer of a CFGBlock that outputs to an ostream.
/// Generally this will only be called from CFG::print.
void CFGBlock::print(std::ostream& OS) {
void CFGBlock::print(std::ostream& OS, const CFG* cfg) const {
// Print the header.
OS << "\n [ B" << getBlockID();
if (this == &cfg->getEntry()) { OS << " (ENTRY) ]\n"; }
else if (this == &cfg->getExit()) { OS << " (EXIT) ]\n"; }
else if (this == cfg->getIndirectGotoBlock()) {
OS << " (INDIRECT GOTO DISPATCH) ]\n";
}
else OS << " ]\n";
// Iterate through the statements in the block and print them.
OS << " ------------------------\n";
unsigned j = 1;
for (iterator I = Stmts.begin(), E = Stmts.end() ; I != E ; ++I, ++j ) {
for (const_iterator I = Stmts.begin(), E = Stmts.end() ; I != E ; ++I, ++j ) {
// Print the statement # in the basic block.
OS << " " << std::setw(3) << j << ": ";
@ -991,7 +993,7 @@ void CFGBlock::print(std::ostream& OS) {
// Print the predecessors of this block.
OS << " Predecessors (" << pred_size() << "):";
unsigned i = 0;
for (pred_iterator I = pred_begin(), E = pred_end(); I != E; ++I, ++i ) {
for (const_pred_iterator I = pred_begin(), E = pred_end(); I != E; ++I, ++i) {
if (i == 8 || (i-8) == 0) {
OS << "\n ";
}
@ -1008,7 +1010,7 @@ void CFGBlock::print(std::ostream& OS) {
// Print the successors of this block.
OS << " Successors (" << succ_size() << "):";
i = 0;
for (succ_iterator I = succ_begin(), E = succ_end(); I != E; ++I, ++i ) {
for (const_succ_iterator I = succ_begin(), E = succ_end(); I != E; ++I, ++i) {
if (i == 8 || (i-8) % 10 == 0) {
OS << "\n ";
}
@ -1016,3 +1018,39 @@ void CFGBlock::print(std::ostream& OS) {
}
OS << '\n';
}
//===----------------------------------------------------------------------===//
// CFG Graphviz Visualization
//===----------------------------------------------------------------------===//
namespace llvm {
template<>
struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits {
static std::string getNodeLabel(const CFGBlock* Node, const CFG* Graph) {
std::ostringstream Out;
Node->print(Out,Graph);
std::string OutStr = Out.str();
if (OutStr[0] == '\n') OutStr.erase(OutStr.begin());
// Process string output to make it nicer...
for (unsigned i = 0; i != OutStr.length(); ++i)
if (OutStr[i] == '\n') { // Left justify
OutStr[i] = '\\';
OutStr.insert(OutStr.begin()+i+1, 'l');
}
return OutStr;
}
};
} // end namespace llvm
void CFG::viewCFG() const {
#ifndef NDEBUG
llvm::ViewGraph(this,"CFG");
#else
std::cerr << "CFG::viewCFG is only available in debug builds on "
<< "systems with Graphviz or gv!" << std::endl;
#endif
}

View File

@ -155,7 +155,9 @@ void clang::DumpASTs(Preprocessor &PP, unsigned MainFileID, bool Stats) {
ASTStreamer_Terminate(Streamer);
}
void clang::DumpCFGs(Preprocessor &PP, unsigned MainFileID, bool Stats) {
void clang::DumpCFGs(Preprocessor &PP, unsigned MainFileID,
bool Stats, bool use_graphviz)
{
ASTContext Context(PP.getTargetInfo(), PP.getIdentifierTable());
ASTStreamerTy *Streamer = ASTStreamer_Init(PP, Context, MainFileID);
@ -164,8 +166,9 @@ void clang::DumpCFGs(Preprocessor &PP, unsigned MainFileID, bool Stats) {
if (FD->getBody()) {
PrintFunctionDeclStart(FD);
fprintf(stderr,"\n");
if (CFG* C = CFG::buildCFG(FD->getBody()))
C->dump();
if (CFG* C = CFG::buildCFG(FD->getBody())) {
if (use_graphviz) C->viewCFG(); else C->dump();
}
else
fprintf(stderr," Error processing CFG.\n");
}

View File

@ -23,7 +23,9 @@ class TypedefDecl;
void BuildASTs(Preprocessor &PP, unsigned MainFileID, bool Stats);
void PrintASTs(Preprocessor &PP, unsigned MainFileID, bool Stats);
void DumpASTs(Preprocessor &PP, unsigned MainFileID, bool Stats);
void DumpCFGs(Preprocessor &PP, unsigned MainFileID, bool Stats);
void DumpCFGs(Preprocessor &PP, unsigned MainFileID,
bool Stats, bool use_graphviz = false);
} // end clang namespace

View File

@ -53,6 +53,7 @@ enum ProgActions {
ParseASTCheck, // Parse ASTs and check diagnostics.
ParseAST, // Parse ASTs.
ParseCFGDump, // Parse ASTS. Build CFGs. Print CFGs.
ParseCFGView, // Parse ASTS. Build CFGs. View CFGs (Graphviz).
ParsePrintCallbacks, // Parse and print each callback.
ParseSyntaxOnly, // Parse and perform semantic analysis.
ParseNoop, // Parse with noop callbacks.
@ -86,7 +87,9 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore,
clEnumValN(ParseASTCheck, "parse-ast-check",
"Run parser, build ASTs, then check diagnostics"),
clEnumValN(ParseCFGDump, "dump-cfg",
"Run parser, build ASTs, then build and print CFGs."),
"Run parser, then build and print CFGs."),
clEnumValN(ParseCFGView, "view-cfg",
"Run parser, then build and view CFGs with Graphviz."),
clEnumValN(EmitLLVM, "emit-llvm",
"Build ASTs then convert to LLVM, emit .ll file"),
clEnumValEnd));
@ -840,6 +843,9 @@ static void ProcessInputFile(Preprocessor &PP, unsigned MainFileID,
case ParseCFGDump:
DumpCFGs(PP, MainFileID, Stats);
break;
case ParseCFGView:
DumpCFGs(PP, MainFileID, Stats, true);
break;
case EmitLLVM:
EmitLLVMFromASTs(PP, MainFileID, Stats);
break;

View File

@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/GraphTraits.h"
#include <list>
#include <vector>
#include <iosfwd>
@ -19,6 +20,7 @@
namespace clang {
class Stmt;
class CFG;
/// CFGBlock - Represents a single basic block in a source-level CFG.
/// It consists of:
@ -139,8 +141,8 @@ public:
unsigned getBlockID() const { return BlockID; }
void dump();
void print(std::ostream& OS);
void dump(const CFG* cfg) const;
void print(std::ostream& OS, const CFG* cfg) const;
};
@ -183,17 +185,105 @@ public:
const_reverse_iterator rend() const { return Blocks.rend(); }
CFGBlock& getEntry() { return *Entry; }
const CFGBlock& getEntry() const { return *Entry; }
CFGBlock& getExit() { return *Exit; }
CFGBlock* getIndirectGotoBlock() { return IndirectGotoBlock; }
const CFGBlock& getExit() const { return *Exit; }
CFGBlock* getIndirectGotoBlock() { return IndirectGotoBlock; }
const CFGBlock* getIndirectGotoBlock() const { return IndirectGotoBlock; }
// Utility
CFGBlock* createBlock(unsigned blockID);
static CFG* buildCFG(Stmt* AST);
void print(std::ostream& OS);
void dump();
void viewCFG() const;
void print(std::ostream& OS) const;
void dump() const;
void setEntry(CFGBlock *B) { Entry = B; }
void setIndirectGotoBlock(CFGBlock* B) { IndirectGotoBlock = B; }
};
} // end namespace clang
//===----------------------------------------------------------------------===//
// GraphTraits specializations for CFG basic block graphs (source-level CFGs)
//===----------------------------------------------------------------------===//
namespace llvm {
// Traits for: CFGBlock
template <> struct GraphTraits<clang::CFGBlock* > {
typedef clang::CFGBlock NodeType;
typedef clang::CFGBlock::succ_iterator ChildIteratorType;
static NodeType* getEntryNode(clang::CFGBlock* BB)
{ return BB; }
static inline ChildIteratorType child_begin(NodeType* N)
{ return N->succ_begin(); }
static inline ChildIteratorType child_end(NodeType* N)
{ return N->succ_end(); }
};
template <> struct GraphTraits<const clang::CFGBlock* > {
typedef const clang::CFGBlock NodeType;
typedef clang::CFGBlock::const_succ_iterator ChildIteratorType;
static NodeType* getEntryNode(const clang::CFGBlock* BB)
{ return BB; }
static inline ChildIteratorType child_begin(NodeType* N)
{ return N->succ_begin(); }
static inline ChildIteratorType child_end(NodeType* N)
{ return N->succ_end(); }
};
template <> struct GraphTraits<Inverse<const clang::CFGBlock*> > {
typedef const clang::CFGBlock NodeType;
typedef clang::CFGBlock::const_pred_iterator ChildIteratorType;
static NodeType *getEntryNode(Inverse<const clang::CFGBlock*> G)
{ return G.Graph; }
static inline ChildIteratorType child_begin(NodeType* N)
{ return N->pred_begin(); }
static inline ChildIteratorType child_end(NodeType* N)
{ return N->pred_end(); }
};
// Traits for: CFG
template <> struct GraphTraits<clang::CFG* >
: public GraphTraits<clang::CFGBlock* > {
typedef clang::CFG::iterator nodes_iterator;
static NodeType *getEntryNode(clang::CFG* F) { return &F->getEntry(); }
static nodes_iterator nodes_begin(clang::CFG* F) { return F->begin(); }
static nodes_iterator nodes_end(clang::CFG* F) { return F->end(); }
};
template <> struct GraphTraits< const clang::CFG* >
: public GraphTraits< const clang::CFGBlock* > {
typedef clang::CFG::const_iterator nodes_iterator;
static NodeType *getEntryNode( const clang::CFG* F) { return &F->getEntry(); }
static nodes_iterator nodes_begin( const clang::CFG* F) { return F->begin(); }
static nodes_iterator nodes_end( const clang::CFG* F) { return F->end(); }
};
template <> struct GraphTraits<Inverse<const clang::CFG*> >
: public GraphTraits<Inverse<const clang::CFGBlock*> > {
typedef clang::CFG::const_iterator nodes_iterator;
static NodeType *getEntryNode(const clang::CFG* F) { return &F->getExit(); }
static nodes_iterator nodes_begin(const clang::CFG* F) { return F->begin();}
static nodes_iterator nodes_end(const clang::CFG* F) { return F->end(); }
};
} // end llvm namespace