[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