[cfe-commits] r41580 - in /cfe/trunk: AST/CFG.cpp Driver/ASTStreamers.cpp Driver/ASTStreamers.h Driver/clang.cpp include/clang/AST/CFG.h
Ted Kremenek
kremenek at apple.com
Wed Aug 29 14:56:11 PDT 2007
Author: kremenek
Date: Wed Aug 29 16:56:09 2007
New Revision: 41580
URL: http://llvm.org/viewvc/llvm-project?rev=41580&view=rev
Log:
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.
Modified:
cfe/trunk/AST/CFG.cpp
cfe/trunk/Driver/ASTStreamers.cpp
cfe/trunk/Driver/ASTStreamers.h
cfe/trunk/Driver/clang.cpp
cfe/trunk/include/clang/AST/CFG.h
Modified: cfe/trunk/AST/CFG.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/AST/CFG.cpp?rev=41580&r1=41579&r2=41580&view=diff
==============================================================================
--- cfe/trunk/AST/CFG.cpp (original)
+++ cfe/trunk/AST/CFG.cpp Wed Aug 29 16:56:09 2007
@@ -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 @@
/// 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 @@
void VisitIfStmt(IfStmt* I) {
OS << "if ";
- I->getCond()->printPretty(std::cerr);
+ I->getCond()->printPretty(OS);
OS << "\n";
}
@@ -962,16 +955,25 @@
} // 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 @@
// 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 @@
// 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 @@
}
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
+}
Modified: cfe/trunk/Driver/ASTStreamers.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Driver/ASTStreamers.cpp?rev=41580&r1=41579&r2=41580&view=diff
==============================================================================
--- cfe/trunk/Driver/ASTStreamers.cpp (original)
+++ cfe/trunk/Driver/ASTStreamers.cpp Wed Aug 29 16:56:09 2007
@@ -155,7 +155,9 @@
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 @@
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");
}
Modified: cfe/trunk/Driver/ASTStreamers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Driver/ASTStreamers.h?rev=41580&r1=41579&r2=41580&view=diff
==============================================================================
--- cfe/trunk/Driver/ASTStreamers.h (original)
+++ cfe/trunk/Driver/ASTStreamers.h Wed Aug 29 16:56:09 2007
@@ -23,7 +23,9 @@
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
Modified: cfe/trunk/Driver/clang.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/Driver/clang.cpp?rev=41580&r1=41579&r2=41580&view=diff
==============================================================================
--- cfe/trunk/Driver/clang.cpp (original)
+++ cfe/trunk/Driver/clang.cpp Wed Aug 29 16:56:09 2007
@@ -53,6 +53,7 @@
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 @@
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 @@
case ParseCFGDump:
DumpCFGs(PP, MainFileID, Stats);
break;
+ case ParseCFGView:
+ DumpCFGs(PP, MainFileID, Stats, true);
+ break;
case EmitLLVM:
EmitLLVMFromASTs(PP, MainFileID, Stats);
break;
Modified: cfe/trunk/include/clang/AST/CFG.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/CFG.h?rev=41580&r1=41579&r2=41580&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/CFG.h (original)
+++ cfe/trunk/include/clang/AST/CFG.h Wed Aug 29 16:56:09 2007
@@ -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 @@
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 @@
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
More information about the cfe-commits
mailing list