]> granicus.if.org Git - clang/commitdiff
Added "PrinterHelper" interface (include/AST/PrinterHelper) that can
authorTed Kremenek <kremenek@apple.com>
Fri, 31 Aug 2007 21:30:12 +0000 (21:30 +0000)
committerTed Kremenek <kremenek@apple.com>
Fri, 31 Aug 2007 21:30:12 +0000 (21:30 +0000)
be passed as an (optional) argument to StmtPrinter to customize
printing of AST nodes.

Used new PrinterHelper interface to enhance printing and visualization
of CFGs.  The CFGs now illustrate the semantic connectives between
statements and terminators, wheras in the previous printing certain
expressions would (visible) be printed multiple times to reflect which
expressions used the results of other expressions.

The end result is that the CFG is easier to read for flow of
expression values (following principles similar to the LLVM IR).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@41651 91177308-0d34-0410-b5e6-96231b3b80d8

AST/CFG.cpp
AST/StmtPrinter.cpp
clang.xcodeproj/project.pbxproj
include/clang/AST/CFG.h
include/clang/AST/PrettyPrinter.h [new file with mode: 0644]
include/clang/AST/Stmt.h

index 89d7af99657d7dfcad07e218601c61445ea36824..512c69ec5b80221e6bbfe05b866b4e43ecf08dd0 100644 (file)
@@ -15,6 +15,7 @@
 #include "clang/AST/CFG.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/AST/PrettyPrinter.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Support/GraphWriter.h"
@@ -921,48 +922,65 @@ 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() const { print(std::cerr); }
-
-/// print - A simple pretty printer of a CFG that outputs to an ostream.
-void CFG::print(std::ostream& OS) const {
-  
-  // Print the entry block.
-  getEntry().print(OS,this);
+namespace {
 
-  // Iterate through the CFGBlocks and print them one by one.
-  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;
-    I->print(OS,this);
+class StmtPrinterHelper : public PrinterHelper {
+  typedef llvm::DenseMap<Stmt*,std::pair<unsigned,unsigned> > StmtMapTy;
+  StmtMapTy StmtMap;
+  signed CurrentBlock;
+  unsigned CurrentStmt;
+public:
+  StmtPrinterHelper(const CFG* cfg) : CurrentBlock(0), CurrentStmt(0) {
+    for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I ) {
+      unsigned j = 1;
+      for (CFGBlock::const_iterator BI = I->begin(), BEnd = I->end() ;
+           BI != BEnd; ++BI, ++j )
+        StmtMap[*BI] = std::make_pair(I->getBlockID(),j);
+      }
   }
+            
+  virtual ~StmtPrinterHelper() {}
   
-  // Print the exit block.
-  getExit().print(OS,this);
-}
+  void setBlockID(signed i) { CurrentBlock = i; }
+  void setStmtID(unsigned i) { CurrentStmt = i; }
+  
+  virtual bool handledStmt(Stmt* E, std::ostream& OS) {
+    StmtMapTy::iterator I = StmtMap.find(E);
 
-namespace {
+    if (I == StmtMap.end())
+      return false;
+    
+    if (CurrentBlock >= 0 && I->second.first == (unsigned) CurrentBlock 
+                          && I->second.second == CurrentStmt)
+      return false;
+      
+    OS << "[B" << I->second.first << "." << I->second.second << "]";
+      return true;
+  }
+};
 
 class CFGBlockTerminatorPrint : public StmtVisitor<CFGBlockTerminatorPrint,
-                                                   void > {
+void > {
   std::ostream& OS;
+  StmtPrinterHelper* Helper;
 public:
-  CFGBlockTerminatorPrint(std::ostream& os) : OS(os) {}
+  CFGBlockTerminatorPrint(std::ostream& os, StmtPrinterHelper* helper)
+    : OS(os), Helper(helper) {}
   
   void VisitIfStmt(IfStmt* I) {
     OS << "if ";
-    I->getCond()->printPretty(OS);
+    I->getCond()->printPretty(OS,Helper);
     OS << "\n";
   }
   
   // Default case.
-  void VisitStmt(Stmt* S) { S->printPretty(OS); }
+  void VisitStmt(Stmt* S) { S->printPretty(OS,Helper); }
   
   void VisitForStmt(ForStmt* F) {
     OS << "for (" ;
     if (F->getInit()) OS << "...";
     OS << "; ";
-    if (Stmt* C = F->getCond()) C->printPretty(OS);
+    if (Stmt* C = F->getCond()) C->printPretty(OS,Helper);
     OS << "; ";
     if (F->getInc()) OS << "...";
     OS << ")\n";                                                       
@@ -970,48 +988,52 @@ public:
   
   void VisitWhileStmt(WhileStmt* W) {
     OS << "while " ;
-    if (Stmt* C = W->getCond()) C->printPretty(OS);
+    if (Stmt* C = W->getCond()) C->printPretty(OS,Helper);
     OS << "\n";
   }
   
   void VisitDoStmt(DoStmt* D) {
     OS << "do ... while ";
-    if (Stmt* C = D->getCond()) C->printPretty(OS);
+    if (Stmt* C = D->getCond()) C->printPretty(OS,Helper);
     OS << '\n';
   }
-                                                       
+  
   void VisitSwitchStmt(SwitchStmt* S) {
     OS << "switch ";
-    S->getCond()->printPretty(OS);
+    S->getCond()->printPretty(OS,Helper);
     OS << '\n';
   }
   
   void VisitExpr(Expr* E) {
-    E->printPretty(OS);
+    E->printPretty(OS,Helper);
     OS << '\n';
   }                                                       
 };
-} // end anonymous namespace
-
-/// dump - A simply pretty printer of a CFGBlock that outputs to stderr.
-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, const CFG* cfg, bool print_edges) const {
-
+  
+  
+void print_block(std::ostream& OS, const CFG* cfg, const CFGBlock& B,
+                 StmtPrinterHelper* Helper, bool print_edges) {
+  if (Helper) Helper->setBlockID(B.getBlockID());
+  
   // 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 << "\n [ B" << B.getBlockID();  
+    
+  if (&B == &cfg->getEntry())
+    OS << " (ENTRY) ]\n";
+  else if (&B == &cfg->getExit())
+    OS << " (EXIT) ]\n";
+  else if (&B == cfg->getIndirectGotoBlock())
     OS << " (INDIRECT GOTO DISPATCH) ]\n";
-  }
-  else OS << " ]\n";
-
+  else
+    OS << " ]\n";
   // Print the label of this block.
-  if (Stmt* S = const_cast<Stmt*>(getLabel())) {
-    if (print_edges) OS << "    ";
+  if (Stmt* S = const_cast<Stmt*>(B.getLabel())) {
+
+    if (print_edges)
+      OS << "    ";
+  
     if (LabelStmt* L = dyn_cast<LabelStmt>(S))
       OS << L->getName();
     else if (CaseStmt* C = dyn_cast<CaseStmt>(S)) {
@@ -1021,70 +1043,148 @@ void CFGBlock::print(std::ostream& OS, const CFG* cfg, bool print_edges) const {
         OS << " ... ";
         C->getRHS()->printPretty(OS);
       }
-    }
-    else if (DefaultStmt* D = dyn_cast<DefaultStmt>(D)) {
+    }  
+    else if (DefaultStmt* D = dyn_cast<DefaultStmt>(D))
       OS << "default";
-    }
-    else assert(false && "Invalid label statement in CFGBlock.");
-    
+    else
+      assert(false && "Invalid label statement in CFGBlock.");
     OS << ":\n";
   }
-  
   // Iterate through the statements in the block and print them.
   unsigned j = 1;
-  for (const_iterator I = Stmts.begin(), E = Stmts.end() ; I != E ; ++I, ++j ) {
+  
+  for (CFGBlock::const_iterator I = B.begin(), E = B.end() ;
+       I != E ; ++I, ++j ) {
+       
     // Print the statement # in the basic block and the statement itself.
-    if (print_edges) OS << "    ";
-    OS << std::setw(3) << j << ": ";        
-    (*I)->printPretty(OS);
-          
+    if (print_edges)
+      OS << "    ";
+      
+    OS << std::setw(3) << j << ": ";
+    
+    if (Helper)
+      Helper->setStmtID(j);
+      
+    (*I)->printPretty(OS, Helper);
     // Expressions need a newline.
     if (isa<Expr>(*I)) OS << '\n';
   }
-
   // Print the terminator of this block.
-  if (getTerminator()) {
-    if (print_edges) OS << "    ";
+  if (B.getTerminator()) {
+    if (print_edges)
+      OS << "    ";
+      
     OS << "  T: ";
-    CFGBlockTerminatorPrint(OS).Visit(const_cast<Stmt*>(getTerminator()));
+    
+    if (Helper) Helper->setBlockID(-1);
+    
+    CFGBlockTerminatorPrint TPrinter(OS,Helper);
+    TPrinter.Visit(const_cast<Stmt*>(B.getTerminator()));
   }
-  
   if (print_edges) {
     // Print the predecessors of this block.
-    OS << "    Predecessors (" << pred_size() << "):";
+    OS << "    Predecessors (" << B.pred_size() << "):";
     unsigned i = 0;
-    for (const_pred_iterator I = pred_begin(), E = pred_end(); I != E; ++I, ++i) {
-      if (i == 8 || (i-8) == 0) {
+
+    for (CFGBlock::const_pred_iterator I = B.pred_begin(), E = B.pred_end();
+         I != E; ++I, ++i) {
+                  
+      if (i == 8 || (i-8) == 0)
         OS << "\n     ";
-      }
+      
       OS << " B" << (*I)->getBlockID();
     }
+    
     OS << '\n';
-
     // Print the successors of this block.
-    OS << "    Successors (" << succ_size() << "):";
+    OS << "    Successors (" << B.succ_size() << "):";
     i = 0;
-    for (const_succ_iterator I = succ_begin(), E = succ_end(); I != E; ++I, ++i) {
-      if (i == 8 || (i-8) % 10 == 0) {
+
+    for (CFGBlock::const_succ_iterator I = B.succ_begin(), E = B.succ_end();
+         I != E; ++I, ++i) {
+         
+      if (i == 8 || (i-8) % 10 == 0)
         OS << "\n    ";
-      }
+
       OS << " B" << (*I)->getBlockID();
     }
+    
     OS << '\n';
   }
+}                   
+
+} // end anonymous namespace
+
+/// dump - A simple pretty printer of a CFG that outputs to stderr.
+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) const {
+  
+  StmtPrinterHelper Helper(this);
+  
+  // Print the entry block.
+  print_block(OS, this, getEntry(), &Helper, true);
+                    
+  // Iterate through the CFGBlocks and print them one by one.
+  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;
+      
+    print_block(OS, this, *I, &Helper, true);
+  }
+  
+  // Print the exit block.
+  print_block(OS, this, getExit(), &Helper, true);
+}  
+
+/// dump - A simply pretty printer of a CFGBlock that outputs to stderr.
+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, const CFG* cfg) const {
+  StmtPrinterHelper Helper(cfg);
+  print_block(OS, cfg, *this, &Helper, true);
 }
 
 //===----------------------------------------------------------------------===//
 // CFG Graphviz Visualization
 //===----------------------------------------------------------------------===//
 
+
+#ifndef NDEBUG
+namespace {
+  StmtPrinterHelper* GraphHelper;  
+}
+#endif
+
+void CFG::viewCFG() const {
+#ifndef NDEBUG
+  StmtPrinterHelper H(this);
+  GraphHelper = &H;
+  llvm::ViewGraph(this,"CFG");
+  GraphHelper = NULL;
+#else
+  std::cerr << "CFG::viewCFG is only available in debug builds on "
+  << "systems with Graphviz or gv!" << std::endl;
+#endif
+}
+
 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,false);
+    print_block(Out,Graph, *Node, GraphHelper, false);
     std::string OutStr = Out.str();
 
     if (OutStr[0] == '\n') OutStr.erase(OutStr.begin());
@@ -1100,12 +1200,3 @@ struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits {
   }
 };
 } // 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
-}
index 69030804225dbeb8d8346445e4fda146f7cc725f..28bf446e0b6c704f5c2bb1377a92f9c248ae520a 100644 (file)
@@ -15,6 +15,7 @@
 #include "clang/AST/StmtVisitor.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/PrettyPrinter.h"
 #include "clang/Lex/IdentifierTable.h"
 #include "llvm/Support/Compiler.h"
 #include <iostream>
@@ -29,8 +30,10 @@ namespace  {
   class VISIBILITY_HIDDEN StmtPrinter : public StmtVisitor<StmtPrinter> {
     std::ostream &OS;
     unsigned IndentLevel;
+    clang::PrinterHelper* Helper;
   public:
-    StmtPrinter(std::ostream &os) : OS(os), IndentLevel(0) {}
+    StmtPrinter(std::ostream &os, PrinterHelper* helper) : 
+      OS(os), IndentLevel(0), Helper(helper) {}
     
     void PrintStmt(Stmt *S, int SubIndent = 1) {
       IndentLevel += SubIndent;
@@ -67,6 +70,12 @@ namespace  {
     bool PrintOffsetOfDesignator(Expr *E);
     void VisitUnaryOffsetOf(UnaryOperator *Node);
     
+    void Visit(Stmt* S) {    
+      if (Helper && Helper->handledStmt(S,OS))
+          return;
+      else StmtVisitor<StmtPrinter>::Visit(S);
+    }
+    
     void VisitStmt(Stmt *Node);
 #define STMT(N, CLASS, PARENT) \
     void Visit##CLASS(CLASS *Node);
@@ -593,12 +602,19 @@ void Stmt::dumpPretty() const {
   printPretty(std::cerr);
 }
 
-void Stmt::printPretty(std::ostream &OS) const {
+void Stmt::printPretty(std::ostream &OS, PrinterHelper* Helper) const {
   if (this == 0) {
     OS << "<NULL>";
     return;
   }
 
-  StmtPrinter P(OS);
+  StmtPrinter P(OS, Helper);
   P.Visit(const_cast<Stmt*>(this));
 }
+
+//===----------------------------------------------------------------------===//
+// PrinterHelper
+//===----------------------------------------------------------------------===//
+
+// Implement virtual destructor.
+PrinterHelper::~PrinterHelper() {}
\ No newline at end of file
index cb6a73d08b148b44c744f17e326e5c3b90781dbc..4beeaef7a6ba247c5c367d55649c8b312942ed37 100644 (file)
                1A869AA70BA21ABA008DA07A /* LiteralSupport.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = LiteralSupport.cpp; sourceTree = "<group>"; };
                1ABC36930C7A4BDC006DB0AB /* CGBuiltin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CGBuiltin.cpp; path = CodeGen/CGBuiltin.cpp; sourceTree = "<group>"; };
                35260CA40C7F75C000D66CE9 /* ExprCXX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExprCXX.cpp; path = AST/ExprCXX.cpp; sourceTree = "<group>"; };
+               3547129D0C88881300B3E1D5 /* PrettyPrinter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PrettyPrinter.h; path = clang/AST/PrettyPrinter.h; sourceTree = "<group>"; };
                84D9A8870C1A57E100AC7ABC /* AttributeList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = AttributeList.cpp; path = Parse/AttributeList.cpp; sourceTree = "<group>"; };
                84D9A88B0C1A581300AC7ABC /* AttributeList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AttributeList.h; path = clang/Parse/AttributeList.h; sourceTree = "<group>"; };
                8DD76F6C0486A84900D96B5E /* clang */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = clang; sourceTree = BUILT_PRODUCTS_DIR; };
                                DE0FCA620A95859D00248FD5 /* Expr.h */,
                                1A30A9E80B93A4C800201A91 /* ExprCXX.h */,
                                DE6951C60C4D1F5D00A5826B /* RecordLayout.h */,
+                               3547129D0C88881300B3E1D5 /* PrettyPrinter.h */,
                                DE3452800AEF1B1800DBC861 /* Stmt.h */,
                                DE345F210AFD347900DBC861 /* StmtNodes.def */,
                                DE345C190AFC658B00DBC861 /* StmtVisitor.h */,
                08FB7793FE84155DC02AAC07 /* Project object */ = {
                        isa = PBXProject;
                        buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "clang" */;
+                       compatibilityVersion = "Xcode 2.4";
                        hasScannedForEncodings = 1;
                        mainGroup = 08FB7794FE84155DC02AAC07 /* clang */;
                        projectDirPath = "";
index a09d9346e9f96f4fc6250d52fb6edd9ab2b20596..591668411b9d29906abe3c01c852f42c56260fca 100644 (file)
@@ -22,8 +22,9 @@
 
 namespace clang {
 
-class Stmt;
-class CFG;
+  class Stmt;
+  class CFG;
+  class PrinterHelper;
   
 /// CFGBlock - Represents a single basic block in a source-level CFG.
 ///  It consists of:
@@ -160,7 +161,7 @@ public:
   unsigned getBlockID() const { return BlockID; }
   
   void dump(const CFG* cfg) const;
-  void print(std::ostream& OS, const CFG* cfg, bool print_edges = true) const;
+  void print(std::ostream& OS, const CFG* cfg) const;
 };
   
 
diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h
new file mode 100644 (file)
index 0000000..434110e
--- /dev/null
@@ -0,0 +1,31 @@
+//===--- PrettyPrinter.h - Classes for aiding with AST printing -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file was developed by Ted Kremenek and is distributed under
+// the University of Illinois Open Source License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the PrinterHelper interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_PRETTY_PRINTER_H
+#define LLVM_CLANG_AST_PRETTY_PRINTER_H
+
+#include <iosfwd>
+
+namespace clang {
+
+class Stmt;
+  
+class PrinterHelper {
+public:
+  virtual ~PrinterHelper();
+  virtual bool handledStmt(Stmt* E, std::ostream& OS) = 0;
+};
+
+} // end namespace clang
+
+#endif
\ No newline at end of file
index 025be37b9506db75b2b44e1cdf03c2ad6a6f23c7..de430a91934ca58539a67435e54a2f86d1e81ca0 100644 (file)
@@ -25,7 +25,8 @@ namespace clang {
   class IdentifierInfo;
   class SourceManager;
   class SwitchStmt;
-  
+  class PrinterHelper;
+    
 /// Stmt - This represents one statement.
 ///
 class Stmt {
@@ -67,7 +68,7 @@ public:
   /// dumpPretty/printPretty - These two methods do a "pretty print" of the AST
   /// back to its original source language syntax.
   void dumpPretty() const;
-  void printPretty(std::ostream &OS) const;
+  void printPretty(std::ostream &OS, PrinterHelper* = NULL) const;
   
   // Implement isa<T> support.
   static bool classof(const Stmt *) { return true; }