diff options
Diffstat (limited to 'lib/Analysis/CFG.cpp')
| -rw-r--r-- | lib/Analysis/CFG.cpp | 224 | 
1 files changed, 179 insertions, 45 deletions
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 2a2b3d73b5ca..6a77455edeef 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -233,6 +233,7 @@ public:      }      int distance(const_iterator L); +    const_iterator shared_parent(const_iterator L);    };    friend class const_iterator; @@ -275,6 +276,30 @@ int LocalScope::const_iterator::distance(LocalScope::const_iterator L) {    return D;  } +/// Calculates the closest parent of this iterator +/// that is in a scope reachable through the parents of L. +/// I.e. when using 'goto' from this to L, the lifetime of all variables +/// between this and shared_parent(L) end. +LocalScope::const_iterator +LocalScope::const_iterator::shared_parent(LocalScope::const_iterator L) { +  llvm::SmallPtrSet<const LocalScope *, 4> ScopesOfL; +  while (true) { +    ScopesOfL.insert(L.Scope); +    if (L == const_iterator()) +      break; +    L = L.Scope->Prev; +  } + +  const_iterator F = *this; +  while (true) { +    if (ScopesOfL.count(F.Scope)) +      return F; +    assert(F != const_iterator() && +           "L iterator is not reachable from F iterator."); +    F = F.Scope->Prev; +  } +} +  /// Structure for specifying position in CFG during its build process. It  /// consists of CFGBlock that specifies position in CFG and  /// LocalScope::const_iterator that specifies position in LocalScope graph. @@ -579,6 +604,10 @@ private:    CFGBlock *addInitializer(CXXCtorInitializer *I);    void addAutomaticObjDtors(LocalScope::const_iterator B,                              LocalScope::const_iterator E, Stmt *S); +  void addLifetimeEnds(LocalScope::const_iterator B, +                       LocalScope::const_iterator E, Stmt *S); +  void addAutomaticObjHandling(LocalScope::const_iterator B, +                               LocalScope::const_iterator E, Stmt *S);    void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD);    // Local scopes creation. @@ -619,6 +648,10 @@ private:      B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());    } +  void appendLifetimeEnds(CFGBlock *B, VarDecl *VD, Stmt *S) { +    B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext()); +  } +    void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) {      B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext());    } @@ -626,6 +659,10 @@ private:    void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,        LocalScope::const_iterator B, LocalScope::const_iterator E); +  void prependAutomaticObjLifetimeWithTerminator(CFGBlock *Blk, +                                                 LocalScope::const_iterator B, +                                                 LocalScope::const_iterator E); +    void addSuccessor(CFGBlock *B, CFGBlock *S, bool IsReachable = true) {      B->addSuccessor(CFGBlock::AdjacentBlock(S, IsReachable),                      cfg->getBumpVectorContext()); @@ -957,7 +994,8 @@ private:      return TryResult();    } -   + +  bool hasTrivialDestructor(VarDecl *VD);  };  inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder, @@ -1031,6 +1069,9 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {    assert(Succ == &cfg->getExit());    Block = nullptr;  // the EXIT block is empty.  Create all other blocks lazily. +  assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && +         "AddImplicitDtors and AddLifetime cannot be used at the same time"); +    if (BuildOpts.AddImplicitDtors)      if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D))        addImplicitDtorsForDestructor(DD); @@ -1067,6 +1108,8 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {      if (LI == LabelMap.end()) continue;      JumpTarget JT = LI->second; +    prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition, +                                              JT.scopePosition);      prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,                                             JT.scopePosition);      addSuccessor(B, JT.block); @@ -1209,7 +1252,61 @@ static QualType getReferenceInitTemporaryType(ASTContext &Context,    return Init->getType();  } -   + +void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, +                                         LocalScope::const_iterator E, +                                         Stmt *S) { +  if (BuildOpts.AddImplicitDtors) +    addAutomaticObjDtors(B, E, S); +  if (BuildOpts.AddLifetime) +    addLifetimeEnds(B, E, S); +} + +/// Add to current block automatic objects that leave the scope. +void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, +                                 LocalScope::const_iterator E, Stmt *S) { +  if (!BuildOpts.AddLifetime) +    return; + +  if (B == E) +    return; + +  // To go from B to E, one first goes up the scopes from B to P +  // then sideways in one scope from P to P' and then down +  // the scopes from P' to E. +  // The lifetime of all objects between B and P end. +  LocalScope::const_iterator P = B.shared_parent(E); +  int dist = B.distance(P); +  if (dist <= 0) +    return; + +  // We need to perform the scope leaving in reverse order +  SmallVector<VarDecl *, 10> DeclsTrivial; +  SmallVector<VarDecl *, 10> DeclsNonTrivial; +  DeclsTrivial.reserve(dist); +  DeclsNonTrivial.reserve(dist); + +  for (LocalScope::const_iterator I = B; I != P; ++I) +    if (hasTrivialDestructor(*I)) +      DeclsTrivial.push_back(*I); +    else +      DeclsNonTrivial.push_back(*I); + +  autoCreateBlock(); +  // object with trivial destructor end their lifetime last (when storage +  // duration ends) +  for (SmallVectorImpl<VarDecl *>::reverse_iterator I = DeclsTrivial.rbegin(), +                                                    E = DeclsTrivial.rend(); +       I != E; ++I) +    appendLifetimeEnds(Block, *I, S); + +  for (SmallVectorImpl<VarDecl *>::reverse_iterator +           I = DeclsNonTrivial.rbegin(), +           E = DeclsNonTrivial.rend(); +       I != E; ++I) +    appendLifetimeEnds(Block, *I, S); +} +  /// addAutomaticObjDtors - Add to current block automatic objects destructors  /// for objects in range of local scope positions. Use S as trigger statement  /// for destructors. @@ -1309,7 +1406,7 @@ LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) {  /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement  /// that should create implicit scope (e.g. if/else substatements).   void CFGBuilder::addLocalScopeForStmt(Stmt *S) { -  if (!BuildOpts.AddImplicitDtors) +  if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime)      return;    LocalScope *Scope = nullptr; @@ -1334,7 +1431,7 @@ void CFGBuilder::addLocalScopeForStmt(Stmt *S) {  /// reuse Scope if not NULL.  LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS,                                                   LocalScope* Scope) { -  if (!BuildOpts.AddImplicitDtors) +  if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime)      return Scope;    for (auto *DI : DS->decls()) @@ -1343,23 +1440,7 @@ LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS,    return Scope;  } -/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will -/// create add scope for automatic objects and temporary objects bound to -/// const reference. Will reuse Scope if not NULL. -LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, -                                                LocalScope* Scope) { -  if (!BuildOpts.AddImplicitDtors) -    return Scope; - -  // Check if variable is local. -  switch (VD->getStorageClass()) { -  case SC_None: -  case SC_Auto: -  case SC_Register: -    break; -  default: return Scope; -  } - +bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) {    // Check for const references bound to temporary. Set type to pointee.    QualType QT = VD->getType();    if (QT.getTypePtr()->isReferenceType()) { @@ -1370,44 +1451,74 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD,      // temporaries, and a single declaration can extend multiple temporaries.      // We should look at the storage duration on each nested      // MaterializeTemporaryExpr instead. +      const Expr *Init = VD->getInit();      if (!Init) -      return Scope; +      return true;      // Lifetime-extending a temporary.      bool FoundMTE = false;      QT = getReferenceInitTemporaryType(*Context, Init, &FoundMTE);      if (!FoundMTE) -      return Scope; +      return true;    }    // Check for constant size array. Set type to array element type.    while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) {      if (AT->getSize() == 0) -      return Scope; +      return true;      QT = AT->getElementType();    }    // Check if type is a C++ class with non-trivial destructor.    if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) -    if (CD->hasDefinition() && !CD->hasTrivialDestructor()) { +    return !CD->hasDefinition() || CD->hasTrivialDestructor(); +  return true; +} + +/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will +/// create add scope for automatic objects and temporary objects bound to +/// const reference. Will reuse Scope if not NULL. +LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, +                                                LocalScope* Scope) { +  assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && +         "AddImplicitDtors and AddLifetime cannot be used at the same time"); +  if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) +    return Scope; + +  // Check if variable is local. +  switch (VD->getStorageClass()) { +  case SC_None: +  case SC_Auto: +  case SC_Register: +    break; +  default: return Scope; +  } + +  if (BuildOpts.AddImplicitDtors) { +    if (!hasTrivialDestructor(VD)) {        // Add the variable to scope        Scope = createOrReuseLocalScope(Scope);        Scope->addVar(VD);        ScopePos = Scope->begin();      } +    return Scope; +  } + +  assert(BuildOpts.AddLifetime); +  // Add the variable to scope +  Scope = createOrReuseLocalScope(Scope); +  Scope->addVar(VD); +  ScopePos = Scope->begin();    return Scope;  }  /// addLocalScopeAndDtors - For given statement add local scope for it and  /// add destructors that will cleanup the scope. Will reuse Scope if not NULL.  void CFGBuilder::addLocalScopeAndDtors(Stmt *S) { -  if (!BuildOpts.AddImplicitDtors) -    return; -    LocalScope::const_iterator scopeBeginPos = ScopePos;    addLocalScopeForStmt(S); -  addAutomaticObjDtors(ScopePos, scopeBeginPos, S); +  addAutomaticObjHandling(ScopePos, scopeBeginPos, S);  }  /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for @@ -1419,6 +1530,8 @@ void CFGBuilder::addLocalScopeAndDtors(Stmt *S) {  /// no-return destructors properly.  void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,      LocalScope::const_iterator B, LocalScope::const_iterator E) { +  if (!BuildOpts.AddImplicitDtors) +    return;    BumpVectorContext &C = cfg->getBumpVectorContext();    CFGBlock::iterator InsertPos      = Blk->beginAutomaticObjDtorsInsert(Blk->end(), B.distance(E), C); @@ -1427,6 +1540,21 @@ void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,                                              Blk->getTerminator());  } +/// prependAutomaticObjLifetimeWithTerminator - Prepend lifetime CFGElements for +/// variables with automatic storage duration to CFGBlock's elements vector. +/// Elements will be prepended to physical beginning of the vector which +/// happens to be logical end. Use blocks terminator as statement that specifies +/// where lifetime ends. +void CFGBuilder::prependAutomaticObjLifetimeWithTerminator( +    CFGBlock *Blk, LocalScope::const_iterator B, LocalScope::const_iterator E) { +  if (!BuildOpts.AddLifetime) +    return; +  BumpVectorContext &C = cfg->getBumpVectorContext(); +  CFGBlock::iterator InsertPos = +      Blk->beginLifetimeEndsInsert(Blk->end(), B.distance(E), C); +  for (LocalScope::const_iterator I = B; I != E; ++I) +    InsertPos = Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminator()); +}  /// 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). @@ -1815,7 +1943,7 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) {    // If there is no target for the break, then we are looking at an incomplete    // AST.  This means that the CFG cannot be constructed.    if (BreakJumpTarget.block) { -    addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B); +    addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B);      addSuccessor(Block, BreakJumpTarget.block);    } else      badCFG = true; @@ -1947,13 +2075,12 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C,  CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {    LocalScope::const_iterator scopeBeginPos = ScopePos; -  if (BuildOpts.AddImplicitDtors) { -    addLocalScopeForStmt(C); -  } +  addLocalScopeForStmt(C); +    if (!C->body_empty() && !isa<ReturnStmt>(*C->body_rbegin())) {      // If the body ends with a ReturnStmt, the dtors will be added in      // VisitReturnStmt. -    addAutomaticObjDtors(ScopePos, scopeBeginPos, C); +    addAutomaticObjHandling(ScopePos, scopeBeginPos, C);    }    CFGBlock *LastBlock = Block; @@ -2183,7 +2310,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {    if (VarDecl *VD = I->getConditionVariable())      addLocalScopeForVarDecl(VD); -  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), I); +  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), I);    // The block we were processing is now finished.  Make it the successor    // block. @@ -2308,7 +2435,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {    // Create the new block.    Block = createBlock(false); -  addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R); +  addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R);    // If the one of the destructors does not return, we already have the Exit    // block as a successor. @@ -2389,7 +2516,7 @@ CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) {      BackpatchBlocks.push_back(JumpSource(Block, ScopePos));    else {      JumpTarget JT = I->second; -    addAutomaticObjDtors(ScopePos, JT.scopePosition, G); +    addAutomaticObjHandling(ScopePos, JT.scopePosition, G);      addSuccessor(Block, JT.block);    } @@ -2414,7 +2541,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {      addLocalScopeForVarDecl(VD);    LocalScope::const_iterator ContinueScopePos = ScopePos; -  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F); +  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), F);    // "for" is a control-flow statement.  Thus we stop processing the current    // block. @@ -2466,7 +2593,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {     ContinueJumpTarget.block->setLoopTarget(F);      // Loop body should end with destructor of Condition variable (if any). -    addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F); +   addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F);      // If body is not a compound statement create implicit scope      // and add destructors. @@ -2753,7 +2880,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {    LocalScope::const_iterator LoopBeginScopePos = ScopePos;    if (VarDecl *VD = W->getConditionVariable()) {      addLocalScopeForVarDecl(VD); -    addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); +    addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W);    }    // "while" is a control-flow statement.  Thus we stop processing the current @@ -2788,7 +2915,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {      BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos);      // Loop body should end with destructor of Condition variable (if any). -    addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); +    addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W);      // If body is not a compound statement create implicit scope      // and add destructors. @@ -3030,7 +3157,7 @@ CFGBlock *CFGBuilder::VisitContinueStmt(ContinueStmt *C) {    // If there is no target for the continue, then we are looking at an    // incomplete AST.  This means the CFG cannot be constructed.    if (ContinueJumpTarget.block) { -    addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C); +    addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C);      addSuccessor(Block, ContinueJumpTarget.block);    } else      badCFG = true; @@ -3085,7 +3212,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) {    if (VarDecl *VD = Terminator->getConditionVariable())      addLocalScopeForVarDecl(VD); -  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), Terminator); +  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), Terminator);    if (Block) {      if (badCFG) @@ -3373,7 +3500,7 @@ CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) {    if (VarDecl *VD = CS->getExceptionDecl()) {      LocalScope::const_iterator BeginScopePos = ScopePos;      addLocalScopeForVarDecl(VD); -    addAutomaticObjDtors(ScopePos, BeginScopePos, CS); +    addAutomaticObjHandling(ScopePos, BeginScopePos, CS);    }    if (CS->getHandlerBlock()) @@ -3427,7 +3554,7 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {      addLocalScopeForStmt(Begin);    if (Stmt *End = S->getEndStmt())      addLocalScopeForStmt(End); -  addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S); +  addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S);    LocalScope::const_iterator ContinueScopePos = ScopePos; @@ -3898,6 +4025,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {      case CFGElement::Statement:      case CFGElement::Initializer:      case CFGElement::NewAllocator: +    case CFGElement::LifetimeEnds:        llvm_unreachable("getDestructorDecl should only be used with "                         "ImplicitDtors");      case CFGElement::AutomaticObjectDtor: { @@ -4308,6 +4436,12 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,      OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";      OS << " (Implicit destructor)\n"; +  } else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) { +    const VarDecl *VD = DE->getVarDecl(); +    Helper.handleDecl(VD, OS); + +    OS << " (Lifetime ends)\n"; +    } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) {      OS << "CFGNewAllocator(";      if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())  | 
