[cfe-commits] r156141 - /cfe/trunk/include/clang/AST/RecursiveASTVisitor.h

Argyrios Kyrtzidis akyrtzi at gmail.com
Thu May 3 18:00:35 PDT 2012


Author: akirtzidis
Date: Thu May  3 20:00:35 2012
New Revision: 156141

URL: http://llvm.org/viewvc/llvm-project?rev=156141&view=rev
Log:
Make RecursiveASTVisitor fully data recursive on Stmts, with
minimal disruption on its clients.

Unlike the previous data-recursive scheme, Traverse*Stmt methods are
always getting called. The base methods of RecursiveASTVisitor will enqueue
the sub-statements instead of calling TraverseStmt on them.

Clients that override a Traverse*Stmt method and call TraverseStmt will
still function as function-recursive traversal; if a client wants to
enqueue a sub-statement in its override method it can do it like this:

[inside the override method]
StmtQueueAction StmtQueue(*this);
StmtQueue.queue(Stmt->getSubStmt());

Should address rdar://11179167.

Modified:
    cfe/trunk/include/clang/AST/RecursiveASTVisitor.h

Modified: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/RecursiveASTVisitor.h?rev=156141&r1=156140&r2=156141&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/RecursiveASTVisitor.h (original)
+++ cfe/trunk/include/clang/AST/RecursiveASTVisitor.h Thu May  3 20:00:35 2012
@@ -114,6 +114,9 @@
 /// node are grouped together.  In other words, Visit*() methods for
 /// different nodes are never interleaved.
 ///
+/// Stmts are traversed internally using a data queue to avoid a stack overflow
+/// with hugely nested ASTs.
+///
 /// Clients of this visitor should subclass the visitor (providing
 /// themselves as the template argument, using the curiously recurring
 /// template pattern) and override any of the Traverse*, WalkUpFrom*,
@@ -148,13 +151,6 @@
   /// TypeLocs.
   bool shouldWalkTypesOfTypeLocs() const { return true; }
 
-  /// \brief Return whether \param S should be traversed using data recursion
-  /// to avoid a stack overflow with extreme cases.
-  bool shouldUseDataRecursionFor(Stmt *S) const {
-    return isa<BinaryOperator>(S) || isa<UnaryOperator>(S) ||
-           isa<CaseStmt>(S) || isa<CXXOperatorCallExpr>(S);
-  }
-
   /// \brief Recursively visit a statement or expression, by
   /// dispatching to Traverse*() based on the argument's dynamic type.
   ///
@@ -267,7 +263,8 @@
 #define OPERATOR(NAME)                                           \
   bool TraverseUnary##NAME(UnaryOperator *S) {                  \
     TRY_TO(WalkUpFromUnary##NAME(S));                           \
-    TRY_TO(TraverseStmt(S->getSubExpr()));                      \
+    StmtQueueAction StmtQueue(*this);                           \
+    StmtQueue.queue(S->getSubExpr());                           \
     return true;                                                \
   }                                                             \
   bool WalkUpFromUnary##NAME(UnaryOperator *S) {                \
@@ -286,8 +283,9 @@
 #define GENERAL_BINOP_FALLBACK(NAME, BINOP_TYPE)                \
   bool TraverseBin##NAME(BINOP_TYPE *S) {                       \
     TRY_TO(WalkUpFromBin##NAME(S));                             \
-    TRY_TO(TraverseStmt(S->getLHS()));                          \
-    TRY_TO(TraverseStmt(S->getRHS()));                          \
+    StmtQueueAction StmtQueue(*this);                           \
+    StmtQueue.queue(S->getLHS());                               \
+    StmtQueue.queue(S->getRHS());                               \
     return true;                                                \
   }                                                             \
   bool WalkUpFromBin##NAME(BINOP_TYPE *S) {                     \
@@ -405,109 +403,46 @@
   bool TraverseFunctionHelper(FunctionDecl *D);
   bool TraverseVarHelper(VarDecl *D);
 
-  struct EnqueueJob {
-    Stmt *S;
-    Stmt::child_iterator StmtIt;
-
-    EnqueueJob(Stmt *S) : S(S), StmtIt() {}
-  };
-  bool dataTraverse(Stmt *S);
-  bool dataTraverseNode(Stmt *S, bool &EnqueueChildren);
-};
-
-template<typename Derived>
-bool RecursiveASTVisitor<Derived>::dataTraverse(Stmt *S) {
-
-  SmallVector<EnqueueJob, 16> Queue;
-  Queue.push_back(S);
+  typedef SmallVector<Stmt *, 16> StmtsTy;
+  typedef SmallVector<StmtsTy *, 4> QueuesTy;
+  
+  QueuesTy Queues;
 
-  while (!Queue.empty()) {
-    EnqueueJob &job = Queue.back();
-    Stmt *CurrS = job.S;
-    if (!CurrS) {
-      Queue.pop_back();
-      continue;
+  class NewQueueRAII {
+    RecursiveASTVisitor &RAV;
+  public:
+    NewQueueRAII(StmtsTy &queue, RecursiveASTVisitor &RAV) : RAV(RAV) {
+      RAV.Queues.push_back(&queue);
     }
-
-    if (getDerived().shouldUseDataRecursionFor(CurrS)) {
-      if (job.StmtIt == Stmt::child_iterator()) {
-        bool EnqueueChildren = true;
-        if (!dataTraverseNode(CurrS, EnqueueChildren)) return false;
-        if (!EnqueueChildren) {
-          Queue.pop_back();
-          continue;
-        }
-        job.StmtIt = CurrS->child_begin();
-      } else {
-        ++job.StmtIt;
-      }
-
-      if (job.StmtIt != CurrS->child_end())
-        Queue.push_back(*job.StmtIt);
-      else
-        Queue.pop_back();
-      continue;
+    ~NewQueueRAII() {
+      RAV.Queues.pop_back();
     }
+  };
 
-    Queue.pop_back();
-    TRY_TO(TraverseStmt(CurrS));
+  StmtsTy &getCurrentQueue() {
+    assert(!Queues.empty() && "base TraverseStmt was never called?");
+    return *Queues.back();
   }
 
-  return true;
-}
-
-template<typename Derived>
-bool RecursiveASTVisitor<Derived>::dataTraverseNode(Stmt *S,
-                                                    bool &EnqueueChildren) {
-
-  // Dispatch to the corresponding WalkUpFrom* function only if the derived
-  // class didn't override Traverse* (and thus the traversal is trivial).
-  // The cast here is necessary to work around a bug in old versions of g++.
-#define DISPATCH_WALK(NAME, CLASS, VAR) \
-  if (&RecursiveASTVisitor::Traverse##NAME == \
-      (bool (RecursiveASTVisitor::*)(CLASS*))&Derived::Traverse##NAME) \
-    return getDerived().WalkUpFrom##NAME(static_cast<CLASS*>(VAR)); \
-  EnqueueChildren = false; \
-  return getDerived().Traverse##NAME(static_cast<CLASS*>(VAR));
-
-  if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
-    switch (BinOp->getOpcode()) {
-#define OPERATOR(NAME) \
-    case BO_##NAME: DISPATCH_WALK(Bin##NAME, BinaryOperator, S);
-
-    BINOP_LIST()
-#undef OPERATOR
-
-#define OPERATOR(NAME)                                          \
-    case BO_##NAME##Assign:                          \
-    DISPATCH_WALK(Bin##NAME##Assign, CompoundAssignOperator, S);
-
-    CAO_LIST()
-#undef OPERATOR
+public:
+  class StmtQueueAction {
+    StmtsTy &CurrQueue;
+    SmallVector<Stmt *, 8> Stmts;
+  public:
+    explicit StmtQueueAction(RecursiveASTVisitor &RAV)
+      : CurrQueue(RAV.getCurrentQueue()) { }
+
+    void queue(Stmt *S) {
+      Stmts.push_back(S);
     }
-  } else if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(S)) {
-    switch (UnOp->getOpcode()) {
-#define OPERATOR(NAME)                                                  \
-    case UO_##NAME: DISPATCH_WALK(Unary##NAME, UnaryOperator, S);
 
-    UNARYOP_LIST()
-#undef OPERATOR
+    ~StmtQueueAction() {
+      for (SmallVector<Stmt *, 8>::reverse_iterator
+             RI = Stmts.rbegin(), RE = Stmts.rend(); RI != RE; ++RI)
+        CurrQueue.push_back(*RI);
     }
-  }
-
-  // Top switch stmt: dispatch to TraverseFooStmt for each concrete FooStmt.
-  switch (S->getStmtClass()) {
-  case Stmt::NoStmtClass: break;
-#define ABSTRACT_STMT(STMT)
-#define STMT(CLASS, PARENT) \
-  case Stmt::CLASS##Class: DISPATCH_WALK(CLASS, CLASS, S);
-#include "clang/AST/StmtNodes.inc"
-  }
-
-#undef DISPATCH_WALK
-
-  return true;
-}
+  };
+};
 
 #define DISPATCH(NAME, CLASS, VAR) \
   return getDerived().Traverse##NAME(static_cast<CLASS*>(VAR))
@@ -517,47 +452,57 @@
   if (!S)
     return true;
 
-  if (getDerived().shouldUseDataRecursionFor(S))
-    return dataTraverse(S);
+  StmtsTy Queue;
+  Queue.push_back(S);
+  NewQueueRAII NQ(Queue, *this);
 
-  // If we have a binary expr, dispatch to the subcode of the binop.  A smart
-  // optimizer (e.g. LLVM) will fold this comparison into the switch stmt
-  // below.
-  if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
-    switch (BinOp->getOpcode()) {
-#define OPERATOR(NAME) \
-    case BO_##NAME: DISPATCH(Bin##NAME, BinaryOperator, S);
+  while (!Queue.empty()) {
+    S = Queue.pop_back_val();
+    if (!S)
+      continue;
+
+#define DISPATCH_STMT(NAME, CLASS, VAR) \
+    TRY_TO(Traverse##NAME(static_cast<CLASS*>(VAR))); continue
 
-    BINOP_LIST()
+    // If we have a binary expr, dispatch to the subcode of the binop.  A smart
+    // optimizer (e.g. LLVM) will fold this comparison into the switch stmt
+    // below.
+    if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
+      switch (BinOp->getOpcode()) {
+#define OPERATOR(NAME) \
+      case BO_##NAME: DISPATCH_STMT(Bin##NAME, BinaryOperator, S);
+  
+      BINOP_LIST()
 #undef OPERATOR
 #undef BINOP_LIST
-
+  
 #define OPERATOR(NAME)                                          \
-    case BO_##NAME##Assign:                          \
-      DISPATCH(Bin##NAME##Assign, CompoundAssignOperator, S);
-
-    CAO_LIST()
+      case BO_##NAME##Assign:                          \
+        DISPATCH_STMT(Bin##NAME##Assign, CompoundAssignOperator, S);
+  
+      CAO_LIST()
 #undef OPERATOR
 #undef CAO_LIST
-    }
-  } else if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(S)) {
-    switch (UnOp->getOpcode()) {
+      }
+    } else if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(S)) {
+      switch (UnOp->getOpcode()) {
 #define OPERATOR(NAME)                                                  \
-    case UO_##NAME: DISPATCH(Unary##NAME, UnaryOperator, S);
-
-    UNARYOP_LIST()
+      case UO_##NAME: DISPATCH_STMT(Unary##NAME, UnaryOperator, S);
+  
+      UNARYOP_LIST()
 #undef OPERATOR
 #undef UNARYOP_LIST
+      }
     }
-  }
-
-  // Top switch stmt: dispatch to TraverseFooStmt for each concrete FooStmt.
-  switch (S->getStmtClass()) {
-  case Stmt::NoStmtClass: break;
+  
+    // Top switch stmt: dispatch to TraverseFooStmt for each concrete FooStmt.
+    switch (S->getStmtClass()) {
+    case Stmt::NoStmtClass: break;
 #define ABSTRACT_STMT(STMT)
 #define STMT(CLASS, PARENT) \
-  case Stmt::CLASS##Class: DISPATCH(CLASS, CLASS, S);
+    case Stmt::CLASS##Class: DISPATCH_STMT(CLASS, CLASS, S);
 #include "clang/AST/StmtNodes.inc"
+    }
   }
 
   return true;
@@ -1797,23 +1742,24 @@
 template<typename Derived>                                              \
 bool RecursiveASTVisitor<Derived>::Traverse##STMT (STMT *S) {           \
   TRY_TO(WalkUpFrom##STMT(S));                                          \
+  StmtQueueAction StmtQueue(*this);                                     \
   { CODE; }                                                             \
   for (Stmt::child_range range = S->children(); range; ++range) {       \
-    TRY_TO(TraverseStmt(*range));                                       \
+    StmtQueue.queue(*range);                                            \
   }                                                                     \
   return true;                                                          \
 }
 
 DEF_TRAVERSE_STMT(AsmStmt, {
-    TRY_TO(TraverseStmt(S->getAsmString()));
+    StmtQueue.queue(S->getAsmString());
     for (unsigned I = 0, E = S->getNumInputs(); I < E; ++I) {
-      TRY_TO(TraverseStmt(S->getInputConstraintLiteral(I)));
+      StmtQueue.queue(S->getInputConstraintLiteral(I));
     }
     for (unsigned I = 0, E = S->getNumOutputs(); I < E; ++I) {
-      TRY_TO(TraverseStmt(S->getOutputConstraintLiteral(I)));
+      StmtQueue.queue(S->getOutputConstraintLiteral(I));
     }
     for (unsigned I = 0, E = S->getNumClobbers(); I < E; ++I) {
-      TRY_TO(TraverseStmt(S->getClobber(I)));
+      StmtQueue.queue(S->getClobber(I));
     }
     // children() iterates over inputExpr and outputExpr.
   })
@@ -1942,9 +1888,10 @@
   if (InitListExpr *Syn = S->getSyntacticForm())
     S = Syn;
   TRY_TO(WalkUpFromInitListExpr(S));
+  StmtQueueAction StmtQueue(*this);
   // All we need are the default actions.  FIXME: use a helper function.
   for (Stmt::child_range range = S->children(); range; ++range) {
-    TRY_TO(TraverseStmt(*range));
+    StmtQueue.queue(*range);
   }
   return true;
 }
@@ -1956,11 +1903,12 @@
 bool RecursiveASTVisitor<Derived>::
 TraverseGenericSelectionExpr(GenericSelectionExpr *S) {
   TRY_TO(WalkUpFromGenericSelectionExpr(S));
-  TRY_TO(TraverseStmt(S->getControllingExpr()));
+  StmtQueueAction StmtQueue(*this);
+  StmtQueue.queue(S->getControllingExpr());
   for (unsigned i = 0; i != S->getNumAssocs(); ++i) {
     if (TypeSourceInfo *TS = S->getAssocTypeSourceInfo(i))
       TRY_TO(TraverseTypeLoc(TS->getTypeLoc()));
-    TRY_TO(TraverseStmt(S->getAssocExpr(i)));
+    StmtQueue.queue(S->getAssocExpr(i));
   }
   return true;
 }
@@ -1971,13 +1919,14 @@
 bool RecursiveASTVisitor<Derived>::
 TraversePseudoObjectExpr(PseudoObjectExpr *S) {
   TRY_TO(WalkUpFromPseudoObjectExpr(S));
-  TRY_TO(TraverseStmt(S->getSyntacticForm()));
+  StmtQueueAction StmtQueue(*this);
+  StmtQueue.queue(S->getSyntacticForm());
   for (PseudoObjectExpr::semantics_iterator
          i = S->semantics_begin(), e = S->semantics_end(); i != e; ++i) {
     Expr *sub = *i;
     if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
       sub = OVE->getSourceExpr();
-    TRY_TO(TraverseStmt(sub));
+    StmtQueue.queue(sub);
   }
   return true;
 }
@@ -2041,7 +1990,7 @@
   })
 
 DEF_TRAVERSE_STMT(ExpressionTraitExpr, {
-    TRY_TO(TraverseStmt(S->getQueriedExpression()));
+    StmtQueue.queue(S->getQueriedExpression());
   })
 
 DEF_TRAVERSE_STMT(VAArgExpr, {
@@ -2081,7 +2030,8 @@
     }
   }
 
-  TRY_TO(TraverseStmt(S->getBody()));
+  StmtQueueAction StmtQueue(*this);
+  StmtQueue.queue(S->getBody());
   return true;
 }
 





More information about the cfe-commits mailing list