This patch adds automatic variable's dtors to the CFG. It uses LocalScope to
record the automatic variables in each scope. When building the CFG,
CleanupPoints are inserted at ReturnStmt, CompoundStmt and GotoStmt.

After the building process, dtors are inserted into the cleanup points.

To hook up the LocalScope when entering a CompoundStmt, now autoCreate a
block.
This may potentially create redundant blocks. But I think it's easy to
remove
them.

Some code is tricky. It is so to guarantee the correct order the dtors are
inserted. I'm not sure they are the best approach.

Later we would add an option to control the CFG build option. Currently
adding implicit dtors are disabled. If it is enabled, some c++ test cases
would
fail.

Some test cases that this patch can handle are attached. They can be tested
by changing

if (BuildOpts.AddImplicitDtors) {

to

if (true) {
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h	(版本 114267)
+++ include/clang/Analysis/CFG.h	(工作副本)
@@ -29,6 +29,7 @@
 
 namespace clang {
   class Decl;
+  class VarDecl;
   class Stmt;
   class Expr;
   class CFG;
@@ -62,7 +63,12 @@
 public:
   CFGElement() {}
   CFGElement(void *Ptr, unsigned Int) : Data1(Ptr, Int) {}
+  CFGElement(void *Ptr1, unsigned Int1, void *Ptr2, unsigned Int2)
+    : Data1(Ptr1, Int1), Data2(Ptr2, Int2) {}
 
+  void *getPtr1() { return Data1.getPointer(); }
+  void *getPtr2() { return Data2.getPointer(); }
+
   Kind getKind() const { return static_cast<Kind>(Data1.getInt()); }
 
   Kind getDtorKind() const {
@@ -110,6 +116,11 @@
 
 class CFGImplicitDtor : public CFGElement {
 public:
+  CFGImplicitDtor() {}
+
+  CFGImplicitDtor(void *Ptr1, void *Ptr2, unsigned Int)
+    : CFGElement(Ptr1, Dtor, Ptr2, Int) {}
+
   static bool classof(const CFGElement *E) {
     return E->getKind() == Dtor;
   }
@@ -117,6 +128,15 @@
 
 class CFGAutomaticObjDtor: public CFGImplicitDtor {
 public:
+  CFGAutomaticObjDtor() {}
+
+  CFGAutomaticObjDtor(VarDecl *D, void *p) 
+    : CFGImplicitDtor(D, p, AutomaticObjectDtor - DTOR_BEGIN) {}
+
+  VarDecl *getVarDecl() {
+    return static_cast<VarDecl *>(getPtr1());
+  }
+
   static bool classof(const CFGElement *E) {
     return E->getKind() == Dtor && E->getDtorKind() == AutomaticObjectDtor;
   }
@@ -181,6 +201,10 @@
     typedef ImplTy::const_iterator                        const_reverse_iterator;
   
     void push_back(CFGElement e, BumpVectorContext &C) { Impl.push_back(e, C); }
+    reverse_iterator insert(CFGElement e, reverse_iterator i, 
+                            BumpVectorContext &C) {
+      return Impl.insert(i, 1, e, C);
+    }
     CFGElement front() const { return Impl.back(); }
     CFGElement back() const { return Impl.front(); }
     
@@ -387,6 +411,11 @@
   void appendStmt(Stmt* Statement, BumpVectorContext &C, bool asLValue) {
     Elements.push_back(CFGStmt(Statement, asLValue), C);
   }
+
+  reverse_iterator insertElement(CFGElement E, reverse_iterator I, 
+                                 BumpVectorContext &C) {
+    return Elements.insert(E, I, C);
+  }
 };
 
 /// CFG - Represents a source-level, intra-procedural CFG that represents the
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp	(版本 114267)
+++ lib/Analysis/CFG.cpp	(工作副本)
@@ -52,6 +52,69 @@
   Kind k;
 };
 
+class LocalScope {
+public:
+  typedef llvm::SmallVectorImpl<VarDecl *>::iterator iterator;  
+  typedef llvm::SmallVectorImpl<VarDecl *>::reverse_iterator reverse_iterator;
+private:
+  // Variables declared early are pushed later into the vector.
+  llvm::SmallVector<VarDecl *, 4> LocalVars;
+
+  // The position of the scope. The integer is the number of variables declared
+  // after this scope in the parent scope, i.e., it is the size of LocalVars
+  // in the parent scope when this scope is created.
+  std::pair<LocalScope *, unsigned> Parent;
+
+public:
+  LocalScope(std::pair<LocalScope*, unsigned> P) : Parent(P) {}
+
+  void AddVar(VarDecl *VD) {
+    LocalVars.push_back(VD);
+  }
+
+  LocalScope *getParentScope() { return Parent.first; }
+  unsigned getParentPos() { return Parent.second; }
+
+  VarDecl *operator[](unsigned i) { return LocalVars[i]; }
+
+  unsigned size() const { return LocalVars.size(); }
+
+  iterator begin() { return LocalVars.begin(); }
+  iterator end()   { return LocalVars.end(); }
+
+  reverse_iterator rbegin() { return LocalVars.rbegin(); }
+  reverse_iterator rend()   { return LocalVars.rend(); }
+};
+
+typedef std::pair<LocalScope *, unsigned> ScopePosition;
+
+// There are two kinds of cleanup points for now. For EndCompoundStmtKind,
+// only dtors of local variables in the scope need to be inserted. For GotoKind,
+// dtors of local variables from the goto point up to the scope of the
+// destination LabelStmt need to be inserted.
+struct CleanupPoint {
+  // The kind of the cleanup point.
+  enum CleanupKind { EndCompoundStmtKind, GotoKind } Kind;
+
+  // Where the dtors are to be inserted.
+  CFGBlock *InsertBlock;
+  // The index to be insert at.
+  unsigned InsertPos;
+
+  // The scope this CleanupPoint belongs to.
+  LocalScope *Scope;
+
+  // The index of the last VarDecl before this cleanup point.
+  unsigned Pos;
+
+  // For Goto cleanup we need to know the destination label.
+  LabelStmt *Label;
+
+  CleanupPoint(CleanupKind K, CFGBlock *B, unsigned I,
+               LocalScope *S, unsigned P, LabelStmt *L)
+    : Kind(K), InsertBlock(B), InsertPos(I), Scope(S), Pos(P), Label(L) {}
+};
+
 /// CFGBuilder - This class implements CFG construction from an AST.
 ///   The builder is stateful: an instance of the builder should be used to only
 ///   construct a single CFG.
@@ -78,6 +141,8 @@
   CFGBlock* DefaultCaseBlock;
   CFGBlock* TryTerminatedBlock;
 
+  LocalScope *CurrentScope;
+
   // LabelMap records the mapping from Label expressions to their blocks.
   typedef llvm::DenseMap<LabelStmt*,CFGBlock*> LabelMapTy;
   LabelMapTy LabelMap;
@@ -91,6 +156,15 @@
   typedef llvm::SmallPtrSet<LabelStmt*,5> LabelSetTy;
   LabelSetTy AddressTakenLabels;
 
+  // Map labels to scopes. Used when adding dtors for goto cleanups.
+  llvm::DenseMap<LabelStmt *, LocalScope *> LabelScope;
+
+  // Cleanup points we would revisit after the CFG is built.
+  std::vector<CleanupPoint> CleanupPoints;
+
+  // BumpPtrAllocator used to store all LocalScopes.
+  llvm::BumpPtrAllocator ScopeAllocator;
+
   bool badCFG;
   CFG::BuildOptions BuildOpts;
 
@@ -99,7 +173,8 @@
                           Block(NULL), Succ(NULL),
                           ContinueTargetBlock(NULL), BreakTargetBlock(NULL),
                           SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL),
-                          TryTerminatedBlock(NULL), badCFG(false) {}
+                          TryTerminatedBlock(NULL), CurrentScope(NULL),
+                          badCFG(false) {}
 
   // buildCFG - Used by external clients to construct the CFG.
   CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C,
@@ -155,6 +230,12 @@
   void autoCreateBlock() { if (!Block) Block = createBlock(); }
   CFGBlock *createBlock(bool add_successor = true);
 
+  void PushScope();
+  void PopScope();
+  void AddVarToLocalScope(VarDecl *VD);
+  void AddCleanupPoint(CFGBlock *B);
+  void AddCleanupPoint(CFGBlock *B, LabelStmt *L);
+  
   CFGBlock *addStmt(Stmt *S) {
     return Visit(S, AddStmtChoice::AlwaysAdd);
   }
@@ -288,6 +369,55 @@
   // Create an empty entry block that has no predecessors.
   cfg->setEntry(createBlock());
 
+  if (BuildOpts.AddImplicitDtors) {
+    // Add dtors at all cleanup points. We must start from cleanup points
+    // created later, since they correspond to inner scopes, whose local var's 
+    // dtor should be emitted first.
+    for (std::vector<CleanupPoint>::reverse_iterator I = CleanupPoints.rbegin(),
+           E = CleanupPoints.rend(); I != E; ++I) {
+      CleanupPoint CP = *I;
+      if (CP.Kind == CleanupPoint::EndCompoundStmtKind) {
+        CFGBlock *InsertBlock = CP.InsertBlock;
+        CFGBlock::reverse_iterator IP = InsertBlock->rbegin();
+        IP += CP.InsertPos;
+        // Start from the earliest declared variable, because they should be 
+        // destructed last.
+        for (unsigned i = CP.Scope->size(), e = 0; i != e; --i) {
+          VarDecl *VD = (*CP.Scope)[i-1];
+          IP = InsertBlock->insertElement(CFGAutomaticObjDtor(VD, IP), IP,
+                                          cfg->getBumpVectorContext());
+        }
+      } else {
+        assert(CP.Kind == CleanupPoint::GotoKind);
+        LocalScope *Scope = CP.Scope;
+        LocalScope *EndScope = LabelScope[CP.Label];
+        CFGBlock *InsertBlock = CP.InsertBlock;
+        CFGBlock::reverse_iterator IP = InsertBlock->rbegin();
+        IP += CP.InsertPos;
+        // Start from the earliest declared variable, because they should be
+        // destructed last. Start from the outer most scope.
+        std::vector<std::pair<LocalScope*, unsigned> > Scopes;
+
+        unsigned p = CP.Pos;
+        while (Scope != EndScope) {
+          Scopes.push_back(std::make_pair(Scope, p));
+          p = Scope->getParentPos();
+          Scope = Scope->getParentScope();
+        }
+
+        for (unsigned SI = Scopes.size(); SI != 0; --SI) {
+          LocalScope *S = Scopes[SI-1].first;
+          unsigned e = Scopes[SI-1].second;
+          unsigned i = S->size();
+          for (; i != e; --i) {
+            VarDecl *VD = (*S)[i-1];
+            IP = CP.InsertBlock->insertElement(CFGAutomaticObjDtor(VD, IP), IP,
+                                               cfg->getBumpVectorContext());
+          }
+        }
+      }
+    }
+  }
   return cfg.take();
 }
 
@@ -300,6 +430,41 @@
   return B;
 }
 
+void CFGBuilder::PushScope() {
+  ScopePosition P(0, 0);
+  if (CurrentScope) {
+    P.first = CurrentScope;
+    P.second = CurrentScope->size();
+  }
+  void *Scope = ScopeAllocator.Allocate<LocalScope>();
+  new (Scope) LocalScope(P);
+  CurrentScope = static_cast<LocalScope *>(Scope);
+}
+
+void CFGBuilder::PopScope() {
+  CurrentScope = CurrentScope->getParentScope();
+}
+
+void CFGBuilder::AddVarToLocalScope(VarDecl *VD) {
+  QualType Ty = VD->getType();
+  if (const RecordType *RT = Ty->getAs<RecordType>())
+    if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl()))
+      if (!ClassDecl->hasTrivialDestructor())
+        CurrentScope->AddVar(VD);
+}
+
+void CFGBuilder::AddCleanupPoint(CFGBlock *B) {
+  CleanupPoint CP(CleanupPoint::EndCompoundStmtKind, B, B->size(), CurrentScope,
+                  CurrentScope->size(), 0);
+  CleanupPoints.push_back(CP);
+}
+
+void CFGBuilder::AddCleanupPoint(CFGBlock *B, LabelStmt *L) {
+  CleanupPoint CP(CleanupPoint::GotoKind, B, B->size(), CurrentScope,
+                  CurrentScope->size(), L);
+  CleanupPoints.push_back(CP);
+}
+
 /// Visit - Walk the subtree of a statement and add extra
 ///   blocks for ternary operators, &&, and ||.  We also process "," and
 ///   DeclStmts (which may contain nested control-flow).
@@ -663,8 +828,15 @@
 
 
 CFGBlock* CFGBuilder::VisitCompoundStmt(CompoundStmt* C) {
+  // Creates a block for the LocalScope to hook on. This may potentially
+  // increase the number of empty blocks.
+  autoCreateBlock();
+
   CFGBlock* LastBlock = Block;
 
+  PushScope();
+  AddCleanupPoint(LastBlock);
+
   for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend();
        I != E; ++I ) {
     // If we hit a segment of code just containing ';' (NullStmts), we can
@@ -676,6 +848,8 @@
       return NULL;
   }
 
+  PopScope();
+
   return LastBlock;
 }
 
@@ -788,6 +962,8 @@
   if (!VD)
     return Block;
 
+  AddVarToLocalScope(VD);
+
   Expr *Init = VD->getInit();
 
   if (Init) {
@@ -909,7 +1085,9 @@
 
   // Add the return statement to the block.  This may create new blocks if R
   // contains control-flow (short-circuit operations).
-  return VisitStmt(R, AddStmtChoice::AlwaysAdd);
+  CFGBlock *B = VisitStmt(R, AddStmtChoice::AlwaysAdd);
+  AddCleanupPoint(B);
+  return B;
 }
 
 CFGBlock* CFGBuilder::VisitLabelStmt(LabelStmt* L) {
@@ -923,6 +1101,9 @@
   assert(LabelMap.find(L) == LabelMap.end() && "label already in map");
   LabelMap[ L ] = LabelBlock;
 
+  // Record the scope of this label.
+  LabelScope[L] = CurrentScope;
+
   // Labels partition blocks, so this is the end of the basic block we were
   // processing (L is the block's label).  Because this is label (and we have
   // already processed the substatement) there is no extra control-flow to worry
@@ -945,6 +1126,7 @@
   // block and create a new one.
 
   Block = createBlock(false);
+  AddCleanupPoint(Block, G->getLabel());
   Block->setTerminator(G);
 
   // If we already know the mapping to the label block add the successor now.
@@ -2098,6 +2280,15 @@
 
 static void print_stmt(llvm::raw_ostream &OS, StmtPrinterHelper* Helper,
                        const CFGElement &E) {
+  // FIXME: this is a hack. The printing methods needs refactoring to
+  // accommodate CFGElemnts.
+  CFGAutomaticObjDtor Dtor = E.getAs<CFGAutomaticObjDtor>();
+  if (Dtor.isValid()) {
+    VarDecl *VD = Dtor.getVarDecl();
+    OS << "dtor: " << VD->getName() << "\n";
+    return;
+  }
+
   CFGStmt CS = E.getAs<CFGStmt>();
   if (!CS)
     return;
struct A {
  A() {}
  ~A() {}
};

void f() {
  A x;
  A y;
}
struct A {
  ~A(){}
};

void f(int x) {
  while (1) {
    if (x)
      break;
    A a;
  }
}
#include <cstdio>
struct A {
  A() { printf("A()\n"); }
  ~A() { printf("~A()\n"); }
};

int main(int argc, char ** argv) {
  A x;
  {
    A y;
    int n;
    {
      A z;
      if (argc == 1)
        goto foo;
      else {
        int x;
        x = 3;
      }
    }
    n = 3;
  }
 foo:
  printf("hello\n");
  return 0;
}
#include <cstdio>
struct A {
  A() { printf("A()\n"); }
  ~A() { printf("~A()\n"); }
};

int main(int argc, char ** argv) {
  int n;
  {
    A a; 
    {
      A x;
      {
        A y;
        {
          A z;
          if (argc == 1)
            goto foo;
          else {
            int x;
            x = 3;
          }
        }
        n = 3;
      }
      n = 4;
    }
    n = 5;
  }
 foo:
  printf("hello\n");
  return 0;
}
struct A {
  A() {}
  ~A() {}
};

void f() {
  int n;
  A x;
  A y;
  {
    A a;
    A b;
    goto foo; 
  }
 foo:
  n = 3;
}
#include <cstdio>

struct A {
  A() {}
  ~A() {printf("dtor\n");}
};

int main() {
  int n;
  {
    A b1,b2;
    {
      A a;
      A a2;
      if (n)
        goto foo;
      A b;
    }
    
  }
 foo:
  n = 3;
}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to