Changeset View
Changeset View
Standalone View
Standalone View
lib/Analysis/CFG.cpp
Show First 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | |||||
private: | private: | ||||
BumpVectorContext ctx; | BumpVectorContext ctx; | ||||
/// Automatic variables in order of declaration. | /// Automatic variables in order of declaration. | ||||
AutomaticVarsTy Vars; | AutomaticVarsTy Vars; | ||||
/// Iterator to variable in previous scope that was declared just before | /// Iterator to variable in previous scope that was declared just before | ||||
/// begin of this scope. | /// begin of this scope. | ||||
const_iterator Prev; | const_iterator Prev; | ||||
/// Statement that triggered creation of this LocalScope object | |||||
dcoughlin: I think it would be helpful to name this something more expressive (perhaps "TriggerStmt") so… | |||||
const Stmt *TriggerScopeStmt; | |||||
public: | public: | ||||
/// Constructs empty scope linked to previous scope in specified place. | /// Constructs empty scope linked to previous scope in specified place. | ||||
LocalScope(BumpVectorContext ctx, const_iterator P) | LocalScope(BumpVectorContext ctx, const_iterator P, Stmt *S) | ||||
: ctx(std::move(ctx)), Vars(this->ctx, 4), Prev(P) {} | : ctx(std::move(ctx)), Vars(this->ctx, 4), Prev(P), TriggerScopeStmt(S) {} | ||||
/// Begin of scope in direction of CFG building (backwards). | /// Begin of scope in direction of CFG building (backwards). | ||||
const_iterator begin() const { return const_iterator(*this, Vars.size()); } | const_iterator begin() const { return const_iterator(*this, Vars.size()); } | ||||
void addVar(VarDecl *VD) { | void addVar(VarDecl *VD) { | ||||
Vars.push_back(VD, ctx); | Vars.push_back(VD, ctx); | ||||
} | } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | class CFGBuilder { | ||||
CFGBlock *Block; | CFGBlock *Block; | ||||
CFGBlock *Succ; | CFGBlock *Succ; | ||||
JumpTarget ContinueJumpTarget; | JumpTarget ContinueJumpTarget; | ||||
JumpTarget BreakJumpTarget; | JumpTarget BreakJumpTarget; | ||||
CFGBlock *SwitchTerminatedBlock; | CFGBlock *SwitchTerminatedBlock; | ||||
CFGBlock *DefaultCaseBlock; | CFGBlock *DefaultCaseBlock; | ||||
CFGBlock *TryTerminatedBlock; | CFGBlock *TryTerminatedBlock; | ||||
Stmt *TerminatedLoop; | |||||
bool InSwitchNest; | |||||
// Current position in local scope. | // Current position in local scope. | ||||
LocalScope::const_iterator ScopePos; | LocalScope::const_iterator ScopePos; | ||||
// LabelMap records the mapping from Label expressions to their jump targets. | // LabelMap records the mapping from Label expressions to their jump targets. | ||||
typedef llvm::DenseMap<LabelDecl*, JumpTarget> LabelMapTy; | typedef llvm::DenseMap<LabelDecl*, JumpTarget> LabelMapTy; | ||||
LabelMapTy LabelMap; | LabelMapTy LabelMap; | ||||
Show All 22 Lines | class CFGBuilder { | ||||
CachedBoolEvalsTy CachedBoolEvals; | CachedBoolEvalsTy CachedBoolEvals; | ||||
public: | public: | ||||
explicit CFGBuilder(ASTContext *astContext, | explicit CFGBuilder(ASTContext *astContext, | ||||
const CFG::BuildOptions &buildOpts) | const CFG::BuildOptions &buildOpts) | ||||
: Context(astContext), cfg(new CFG()), // crew a new CFG | : Context(astContext), cfg(new CFG()), // crew a new CFG | ||||
Block(nullptr), Succ(nullptr), | Block(nullptr), Succ(nullptr), | ||||
SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr), | SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr), | ||||
TryTerminatedBlock(nullptr), badCFG(false), BuildOpts(buildOpts), | TryTerminatedBlock(nullptr), TerminatedLoop(nullptr), InSwitchNest(false), | ||||
switchExclusivelyCovered(false), switchCond(nullptr), | badCFG(false), BuildOpts(buildOpts), | ||||
cachedEntry(nullptr), lastLookup(nullptr) {} | switchExclusivelyCovered(false), | ||||
switchCond(0), cachedEntry(nullptr), lastLookup(nullptr) {} | |||||
// buildCFG - Used by external clients to construct the CFG. | // buildCFG - Used by external clients to construct the CFG. | ||||
std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement); | std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement); | ||||
bool alwaysAdd(const Stmt *stmt); | bool alwaysAdd(const Stmt *stmt); | ||||
private: | private: | ||||
// Visitors to walk an AST and construct the CFG. | // Visitors to walk an AST and construct the CFG. | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | private: | ||||
CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); | CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); | ||||
CFGBlock *VisitWhileStmt(WhileStmt *W); | CFGBlock *VisitWhileStmt(WhileStmt *W); | ||||
CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd); | CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd); | ||||
CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc); | CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc); | ||||
CFGBlock *VisitChildren(Stmt *S); | CFGBlock *VisitChildren(Stmt *S); | ||||
CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); | CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); | ||||
void CreateScopeEndBlockForIfStmt(IfStmt *I); | |||||
/// When creating the CFG for temporary destructors, we want to mirror the | /// When creating the CFG for temporary destructors, we want to mirror the | ||||
/// branch structure of the corresponding constructor calls. | /// branch structure of the corresponding constructor calls. | ||||
/// Thus, while visiting a statement for temporary destructors, we keep a | /// Thus, while visiting a statement for temporary destructors, we keep a | ||||
/// context to keep track of the following information: | /// context to keep track of the following information: | ||||
/// - whether a subexpression is executed unconditionally | /// - whether a subexpression is executed unconditionally | ||||
/// - if a subexpression is executed conditionally, the first | /// - if a subexpression is executed conditionally, the first | ||||
/// CXXBindTemporaryExpr we encounter in that subexpression (which | /// CXXBindTemporaryExpr we encounter in that subexpression (which | ||||
/// corresponds to the last temporary destructor we have to call for this | /// corresponds to the last temporary destructor we have to call for this | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | private: | ||||
CFGBlock *createBlock(bool add_successor = true); | CFGBlock *createBlock(bool add_successor = true); | ||||
CFGBlock *createNoReturnBlock(); | CFGBlock *createNoReturnBlock(); | ||||
CFGBlock *addStmt(Stmt *S) { | CFGBlock *addStmt(Stmt *S) { | ||||
return Visit(S, AddStmtChoice::AlwaysAdd); | return Visit(S, AddStmtChoice::AlwaysAdd); | ||||
} | } | ||||
CFGBlock *addInitializer(CXXCtorInitializer *I); | CFGBlock *addInitializer(CXXCtorInitializer *I); | ||||
void addAutomaticObjDtors(LocalScope::const_iterator B, | void addAutomaticObjDtors(LocalScope::const_iterator B, | ||||
LocalScope::const_iterator E, Stmt *S); | LocalScope::const_iterator E, | ||||
Stmt *TriggerAutoObjDtorsStmt, | |||||
Stmt *TriggerScopeStmt = nullptr); | |||||
void addLifetimeEnds(LocalScope::const_iterator B, | void addLifetimeEnds(LocalScope::const_iterator B, | ||||
LocalScope::const_iterator E, Stmt *S); | LocalScope::const_iterator E, Stmt *S); | ||||
void addAutomaticObjHandling(LocalScope::const_iterator B, | void addAutomaticObjHandling(LocalScope::const_iterator B, | ||||
LocalScope::const_iterator E, Stmt *S); | LocalScope::const_iterator E, | ||||
Stmt *TriggerAutoObjDtorsStmt = nullptr, | |||||
Stmt *TriggerScopeStmt = nullptr); | |||||
void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); | void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); | ||||
// Local scopes creation. | // Local scopes creation. | ||||
LocalScope* createOrReuseLocalScope(LocalScope* Scope); | LocalScope *createOrReuseLocalScope(LocalScope *Scope, | ||||
Stmt *TriggerScopeStmt); | |||||
void addLocalScopeForStmt(Stmt *S); | void addLocalScopeForStmt(Stmt *S, Stmt *TriggerScopeStmt); | ||||
LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, | LocalScope *addLocalScopeForDeclStmt(DeclStmt *DS, Stmt *TriggerScopeStmt, | ||||
LocalScope *Scope = nullptr); | |||||
LocalScope *addLocalScopeForVarDecl(VarDecl *VD, Stmt *TriggerScopeStmt, | |||||
LocalScope* Scope = nullptr); | LocalScope *Scope = nullptr); | ||||
LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = nullptr); | |||||
void addLocalScopeAndDtors(Stmt *S); | void addLocalScopeAndDtors(Stmt *S, Stmt *TriggerScopeStmt); | ||||
// Interface to CFGBlock - adding CFGElements. | // Interface to CFGBlock - adding CFGElements. | ||||
void appendStmt(CFGBlock *B, const Stmt *S) { | void appendStmt(CFGBlock *B, const Stmt *S) { | ||||
if (alwaysAdd(S) && cachedEntry) | if (alwaysAdd(S) && cachedEntry) | ||||
cachedEntry->second = B; | cachedEntry->second = B; | ||||
// All block-level expressions should have already been IgnoreParens()ed. | // All block-level expressions should have already been IgnoreParens()ed. | ||||
assert(!isa<Expr>(S) || cast<Expr>(S)->IgnoreParens() == S); | assert(!isa<Expr>(S) || cast<Expr>(S)->IgnoreParens() == S); | ||||
B->appendStmt(const_cast<Stmt*>(S), cfg->getBumpVectorContext()); | B->appendStmt(const_cast<Stmt*>(S), cfg->getBumpVectorContext()); | ||||
} | } | ||||
void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { | void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) { | ||||
B->appendInitializer(I, cfg->getBumpVectorContext()); | B->appendInitializer(I, cfg->getBumpVectorContext()); | ||||
} | } | ||||
void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { | void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) { | ||||
B->appendNewAllocator(NE, cfg->getBumpVectorContext()); | B->appendNewAllocator(NE, cfg->getBumpVectorContext()); | ||||
} | } | ||||
bool needAddScopes() { | |||||
// FIXME: Support SwitchStmt | |||||
return !InSwitchNest && BuildOpts.AddScopes; | |||||
} | |||||
void appendScopeBegin(CFGBlock *B, const Stmt *S) { | |||||
if (needAddScopes()) | |||||
B->appendScopeBegin(S, cfg->getBumpVectorContext()); | |||||
} | |||||
void appendScopeEnd(CFGBlock *B, const Stmt *TriggerStmt, | |||||
const Stmt *TerminatedStmt) { | |||||
if (needAddScopes()) | |||||
B->appendScopeEnd(TriggerStmt, TerminatedStmt, | |||||
cfg->getBumpVectorContext()); | |||||
} | |||||
void prependScopeEnd(CFGBlock *B, const Stmt *TriggerStmt, | |||||
const Stmt *TerminatedStmt) { | |||||
if (needAddScopes()) | |||||
B->prependScopeEnd(TriggerStmt, TerminatedStmt, | |||||
cfg->getBumpVectorContext()); | |||||
} | |||||
void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { | void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) { | ||||
B->appendBaseDtor(BS, cfg->getBumpVectorContext()); | B->appendBaseDtor(BS, cfg->getBumpVectorContext()); | ||||
} | } | ||||
void appendMemberDtor(CFGBlock *B, FieldDecl *FD) { | void appendMemberDtor(CFGBlock *B, FieldDecl *FD) { | ||||
B->appendMemberDtor(FD, cfg->getBumpVectorContext()); | B->appendMemberDtor(FD, cfg->getBumpVectorContext()); | ||||
} | } | ||||
void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) { | void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) { | ||||
B->appendTemporaryDtor(E, cfg->getBumpVectorContext()); | B->appendTemporaryDtor(E, cfg->getBumpVectorContext()); | ||||
▲ Show 20 Lines • Show All 538 Lines • ▼ Show 20 Lines | if (HasTemporaries) { | ||||
// For expression with temporaries go directly to subexpression to omit | // For expression with temporaries go directly to subexpression to omit | ||||
// generating destructors for the second time. | // generating destructors for the second time. | ||||
return Visit(cast<ExprWithCleanups>(Init)->getSubExpr()); | return Visit(cast<ExprWithCleanups>(Init)->getSubExpr()); | ||||
} | } | ||||
if (BuildOpts.AddCXXDefaultInitExprInCtors) { | if (BuildOpts.AddCXXDefaultInitExprInCtors) { | ||||
if (CXXDefaultInitExpr *Default = dyn_cast<CXXDefaultInitExpr>(Init)) { | if (CXXDefaultInitExpr *Default = dyn_cast<CXXDefaultInitExpr>(Init)) { | ||||
// In general, appending the expression wrapped by a CXXDefaultInitExpr | // In general, appending the expression wrapped by a CXXDefaultInitExpr | ||||
// may cause the same Expr to appear more than once in the CFG. Doing it | // may cause the same Expr to appear more than once in the CFG. Doing it | ||||
// here is safe because there's only one initializer per field. | // here is safe because there's only one initializer per field. | ||||
What is the difference between TriggerStmt and ScopeCreator? Do we need them both here? dcoughlin: What is the difference between TriggerStmt and ScopeCreator? Do we need them both here? | |||||
autoCreateBlock(); | autoCreateBlock(); | ||||
appendStmt(Block, Default); | appendStmt(Block, Default); | ||||
if (Stmt *Child = Default->getExpr()) | if (Stmt *Child = Default->getExpr()) | ||||
if (CFGBlock *R = Visit(Child)) | if (CFGBlock *R = Visit(Child)) | ||||
Block = R; | Block = R; | ||||
return Block; | return Block; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | while (true) { | ||||
break; | break; | ||||
} | } | ||||
return Init->getType(); | return Init->getType(); | ||||
} | } | ||||
void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, | void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B, | ||||
LocalScope::const_iterator E, | LocalScope::const_iterator E, | ||||
Stmt *S) { | Stmt *TriggerAutoObjDtorsStmt, | ||||
Stmt *TriggerScopeStmt) { | |||||
if (BuildOpts.AddImplicitDtors) | if (BuildOpts.AddImplicitDtors) | ||||
addAutomaticObjDtors(B, E, S); | addAutomaticObjDtors(B, E, TriggerAutoObjDtorsStmt, TriggerScopeStmt); | ||||
if (BuildOpts.AddLifetime) | if (BuildOpts.AddLifetime) | ||||
addLifetimeEnds(B, E, S); | addLifetimeEnds(B, E, TriggerScopeStmt); | ||||
} | } | ||||
/// Add to current block automatic objects that leave the scope. | /// Add to current block automatic objects that leave the scope. | ||||
void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, | void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, | ||||
LocalScope::const_iterator E, Stmt *S) { | LocalScope::const_iterator E, Stmt *S) { | ||||
if (!BuildOpts.AddLifetime) | if (!BuildOpts.AddLifetime) | ||||
return; | return; | ||||
Show All 32 Lines | void CFGBuilder::addLifetimeEnds(LocalScope::const_iterator B, | ||||
for (SmallVectorImpl<VarDecl *>::reverse_iterator | for (SmallVectorImpl<VarDecl *>::reverse_iterator | ||||
I = DeclsNonTrivial.rbegin(), | I = DeclsNonTrivial.rbegin(), | ||||
E = DeclsNonTrivial.rend(); | E = DeclsNonTrivial.rend(); | ||||
I != E; ++I) | I != E; ++I) | ||||
appendLifetimeEnds(Block, *I, S); | appendLifetimeEnds(Block, *I, S); | ||||
} | } | ||||
/// addAutomaticObjDtors - Add to current block automatic objects destructors | /// addAutomaticObjDtors - Add to current block automatic objects destructors | ||||
/// for objects in range of local scope positions. Use S as trigger statement | /// for objects in range of local scope positions. | ||||
/// for destructors. | /// Use `TriggerAutoObjDtorsStmt` as trigger statement for destructors, and | ||||
/// `TriggerScopeStmt` as trigger statement for ending scope. | |||||
Not Done ReplyInline ActionsYes. I have renamed TriggerStmt to TriggerAutoObjDtorsStmt and ScopeCreator to TriggerScopeStmt. The former tells us when to add ScopeEnd element and the former ScopeBegin element. Addition of TriggerScopeStmt was necessary because TriggerAutoObjDtorsStmt doesn't tell us anything about the present scope's context; only that the present scope is going to end. bshastry: Yes. I have renamed TriggerStmt to TriggerAutoObjDtorsStmt and ScopeCreator to TriggerScopeStmt. | |||||
void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, | void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, | ||||
LocalScope::const_iterator E, Stmt *S) { | LocalScope::const_iterator E, | ||||
Stmt *TriggerAutoObjDtorsStmt, | |||||
Stmt *TriggerScopeStmt) { | |||||
if (!BuildOpts.AddImplicitDtors) | if (!BuildOpts.AddImplicitDtors) | ||||
return; | return; | ||||
if (B == E) | if (B == E) | ||||
return; | return; | ||||
// We need to append the destructors in reverse order, but any one of them | // We need to append the destructors in reverse order, but any one of them | ||||
// may be a no-return destructor which changes the CFG. As a result, buffer | // may be a no-return destructor which changes the CFG. As a result, buffer | ||||
Show All 16 Lines | for (SmallVectorImpl<VarDecl*>::reverse_iterator I = Decls.rbegin(), | ||||
} | } | ||||
Ty = Context->getBaseElementType(Ty); | Ty = Context->getBaseElementType(Ty); | ||||
if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn()) | if (Ty->getAsCXXRecordDecl()->isAnyDestructorNoReturn()) | ||||
Block = createNoReturnBlock(); | Block = createNoReturnBlock(); | ||||
else | else | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
appendAutomaticObjDtor(Block, *I, S); | appendAutomaticObjDtor(Block, *I, TriggerAutoObjDtorsStmt); | ||||
} | } | ||||
} | } | ||||
/// addImplicitDtorsForDestructor - Add implicit destructors generated for | /// addImplicitDtorsForDestructor - Add implicit destructors generated for | ||||
/// base and member objects in destructor. | /// base and member objects in destructor. | ||||
void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { | void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) { | ||||
assert (BuildOpts.AddImplicitDtors | assert (BuildOpts.AddImplicitDtors | ||||
&& "Can be called only when dtors should be added"); | && "Can be called only when dtors should be added"); | ||||
Show All 29 Lines | if (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { | ||||
QT = AT->getElementType(); | QT = AT->getElementType(); | ||||
} | } | ||||
if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) | if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) | ||||
if (!CD->hasTrivialDestructor()) { | if (!CD->hasTrivialDestructor()) { | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
appendMemberDtor(Block, FI); | appendMemberDtor(Block, FI); | ||||
} | } | ||||
} | } | ||||
I think it is very important to document the different between 'S' and 'ScopeCreator' here. dcoughlin: I think it is very important to document the different between 'S' and 'ScopeCreator' here. | |||||
} | } | ||||
/// createOrReuseLocalScope - If Scope is NULL create new LocalScope. Either | /// createOrReuseLocalScope - If Scope is NULL create new LocalScope. Either | ||||
/// way return valid LocalScope object. | /// way return valid LocalScope object. | ||||
LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { | LocalScope *CFGBuilder::createOrReuseLocalScope(LocalScope *Scope, | ||||
Stmt *TriggerScopeStmt) { | |||||
Not Done ReplyInline ActionsIt seems that something has moved asterisks to a weird spot, might be a rebase artifact or accidental tooling. NoQ: It seems that something has moved asterisks to a weird spot, might be a rebase artifact or… | |||||
Not Done ReplyInline ActionsUhm, it was always this way, just weird history when i looked at the diff between diffs, nvm, sorry. NoQ: Uhm, it was always this way, just weird history when i looked at the diff between diffs, nvm… | |||||
if (Scope) | if (Scope) | ||||
return Scope; | return Scope; | ||||
llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); | llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); | ||||
return new (alloc.Allocate<LocalScope>()) | return new (alloc.Allocate<LocalScope>()) | ||||
LocalScope(BumpVectorContext(alloc), ScopePos); | LocalScope(BumpVectorContext(alloc), ScopePos, TriggerScopeStmt); | ||||
} | } | ||||
/// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement | /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement | ||||
/// that should create implicit scope (e.g. if/else substatements). | /// that should create implicit scope (e.g. if/else substatements). | ||||
void CFGBuilder::addLocalScopeForStmt(Stmt *S) { | void CFGBuilder::addLocalScopeForStmt(Stmt *S, Stmt *TriggerScopeStmt) { | ||||
if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) | if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) | ||||
return; | return; | ||||
LocalScope *Scope = nullptr; | LocalScope *Scope = nullptr; | ||||
// For compound statement we will be creating explicit scope. | // For compound statement we will be creating explicit scope. | ||||
if (CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) { | if (CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) { | ||||
for (auto *BI : CS->body()) { | for (auto *BI : CS->body()) { | ||||
Stmt *SI = BI->stripLabelLikeStatements(); | Stmt *SI = BI->stripLabelLikeStatements(); | ||||
if (DeclStmt *DS = dyn_cast<DeclStmt>(SI)) | if (DeclStmt *DS = dyn_cast<DeclStmt>(SI)) | ||||
Scope = addLocalScopeForDeclStmt(DS, Scope); | Scope = addLocalScopeForDeclStmt(DS, TriggerScopeStmt, Scope); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
// For any other statement scope will be implicit and as such will be | // For any other statement scope will be implicit and as such will be | ||||
// interesting only for DeclStmt. | // interesting only for DeclStmt. | ||||
if (DeclStmt *DS = dyn_cast<DeclStmt>(S->stripLabelLikeStatements())) | if (DeclStmt *DS = dyn_cast<DeclStmt>(S->stripLabelLikeStatements())) | ||||
addLocalScopeForDeclStmt(DS); | addLocalScopeForDeclStmt(DS, TriggerScopeStmt); | ||||
} | } | ||||
/// addLocalScopeForDeclStmt - Add LocalScope for declaration statement. Will | /// addLocalScopeForDeclStmt - Add LocalScope for declaration statement. Will | ||||
/// reuse Scope if not NULL. | /// reuse Scope if not NULL. | ||||
LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, | LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, | ||||
Stmt *TriggerScopeStmt, | |||||
LocalScope* Scope) { | LocalScope* Scope) { | ||||
if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) | if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) | ||||
return Scope; | return Scope; | ||||
for (auto *DI : DS->decls()) | for (auto *DI : DS->decls()) | ||||
if (VarDecl *VD = dyn_cast<VarDecl>(DI)) | if (VarDecl *VD = dyn_cast<VarDecl>(DI)) | ||||
Scope = addLocalScopeForVarDecl(VD, Scope); | Scope = addLocalScopeForVarDecl(VD, TriggerScopeStmt, Scope); | ||||
return Scope; | return Scope; | ||||
} | } | ||||
bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { | bool CFGBuilder::hasTrivialDestructor(VarDecl *VD) { | ||||
// Check for const references bound to temporary. Set type to pointee. | // Check for const references bound to temporary. Set type to pointee. | ||||
QualType QT = VD->getType(); | QualType QT = VD->getType(); | ||||
if (QT.getTypePtr()->isReferenceType()) { | if (QT.getTypePtr()->isReferenceType()) { | ||||
// Attempt to determine whether this declaration lifetime-extends a | // Attempt to determine whether this declaration lifetime-extends a | ||||
Show All 27 Lines | if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) | ||||
return !CD->hasDefinition() || CD->hasTrivialDestructor(); | return !CD->hasDefinition() || CD->hasTrivialDestructor(); | ||||
return true; | return true; | ||||
} | } | ||||
/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will | /// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will | ||||
/// create add scope for automatic objects and temporary objects bound to | /// create add scope for automatic objects and temporary objects bound to | ||||
/// const reference. Will reuse Scope if not NULL. | /// const reference. Will reuse Scope if not NULL. | ||||
LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, | LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, | ||||
Stmt *TriggerScopeStmt, | |||||
LocalScope* Scope) { | LocalScope* Scope) { | ||||
assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && | assert(!(BuildOpts.AddImplicitDtors && BuildOpts.AddLifetime) && | ||||
"AddImplicitDtors and AddLifetime cannot be used at the same time"); | "AddImplicitDtors and AddLifetime cannot be used at the same time"); | ||||
if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) | if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime) | ||||
return Scope; | return Scope; | ||||
// Check if variable is local. | // Check if variable is local. | ||||
switch (VD->getStorageClass()) { | switch (VD->getStorageClass()) { | ||||
case SC_None: | case SC_None: | ||||
case SC_Auto: | case SC_Auto: | ||||
case SC_Register: | case SC_Register: | ||||
break; | break; | ||||
default: return Scope; | default: return Scope; | ||||
} | } | ||||
if (BuildOpts.AddImplicitDtors) { | if (BuildOpts.AddImplicitDtors) { | ||||
if (!hasTrivialDestructor(VD)) { | if (!hasTrivialDestructor(VD)) { | ||||
// Add the variable to scope | // Add the variable to scope | ||||
Scope = createOrReuseLocalScope(Scope); | Scope = createOrReuseLocalScope(Scope, TriggerScopeStmt); | ||||
Scope->addVar(VD); | Scope->addVar(VD); | ||||
ScopePos = Scope->begin(); | ScopePos = Scope->begin(); | ||||
} | } | ||||
return Scope; | return Scope; | ||||
} | } | ||||
assert(BuildOpts.AddLifetime); | assert(BuildOpts.AddLifetime); | ||||
// Add the variable to scope | // Add the variable to scope | ||||
Scope = createOrReuseLocalScope(Scope); | Scope = createOrReuseLocalScope(Scope, TriggerScopeStmt); | ||||
Scope->addVar(VD); | Scope->addVar(VD); | ||||
ScopePos = Scope->begin(); | ScopePos = Scope->begin(); | ||||
return Scope; | return Scope; | ||||
} | } | ||||
/// addLocalScopeAndDtors - For given statement add local scope for it and | /// addLocalScopeAndDtors - For given statements `TriggerAutoObjDtorsStmt`, and | ||||
/// add destructors that will cleanup the scope. Will reuse Scope if not NULL. | /// `TriggerScopeStmt`, add local scope for it and add destructors that will | ||||
void CFGBuilder::addLocalScopeAndDtors(Stmt *S) { | /// cleanup the scope. Will reuse Scope if not NULL. | ||||
/// `TriggerAutoObjDtorsStmt` is the last statement in the present scope after | |||||
/// which auto obj destructors (if any) will be added to cleanup scope. | |||||
/// `TriggerScopeStmt` is the statement that led to the creation of the Scope | |||||
/// object. | |||||
/// | |||||
/// For example: | |||||
/// if (condition) { // TriggerScopeStmt | |||||
/// std::vector<int> IntVec; | |||||
/// IntVec.push_back(0); // TriggerAutoObjDtorsStmt | |||||
/// } | |||||
/// | |||||
/// IfStmt is the `TriggerScopeStmt`; IntVec.push_back is | |||||
/// `TriggerAutoObjDtorsStmt`. | |||||
/// Eventually, `TriggerScopeStmt` is used inside CFGScopeBegin/End | |||||
/// blocks. In the example above, we will have CFGScope blocks, like | |||||
/// so: CFGScopeBegin(IfStmt) ... CFGScopeEnd(IfStmt). | |||||
void CFGBuilder::addLocalScopeAndDtors(Stmt *TriggerAutoObjDtorsStmt, | |||||
Stmt *TriggerScopeStmt) { | |||||
LocalScope::const_iterator scopeBeginPos = ScopePos; | LocalScope::const_iterator scopeBeginPos = ScopePos; | ||||
addLocalScopeForStmt(S); | addLocalScopeForStmt(TriggerAutoObjDtorsStmt, TriggerScopeStmt); | ||||
addAutomaticObjHandling(ScopePos, scopeBeginPos, S); | addAutomaticObjHandling(ScopePos, scopeBeginPos, TriggerAutoObjDtorsStmt, | ||||
TriggerScopeStmt); | |||||
} | } | ||||
/// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for | /// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for | ||||
/// variables with automatic storage duration to CFGBlock's elements vector. | /// variables with automatic storage duration to CFGBlock's elements vector. | ||||
/// Elements will be prepended to physical beginning of the vector which | /// Elements will be prepended to physical beginning of the vector which | ||||
/// happens to be logical end. Use blocks terminator as statement that specifies | /// happens to be logical end. Use blocks terminator as statement that specifies | ||||
/// destructors call site. | /// destructors call site. | ||||
/// FIXME: This mechanism for adding automatic destructors doesn't handle | /// FIXME: This mechanism for adding automatic destructors doesn't handle | ||||
▲ Show 20 Lines • Show All 408 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) { | ||||
// Now create a new block that ends with the break statement. | // Now create a new block that ends with the break statement. | ||||
Block = createBlock(false); | Block = createBlock(false); | ||||
Block->setTerminator(B); | Block->setTerminator(B); | ||||
// If there is no target for the break, then we are looking at an incomplete | // If there is no target for the break, then we are looking at an incomplete | ||||
// AST. This means that the CFG cannot be constructed. | // AST. This means that the CFG cannot be constructed. | ||||
if (BreakJumpTarget.block) { | if (BreakJumpTarget.block) { | ||||
addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B); | addAutomaticObjHandling(ScopePos, BreakJumpTarget.scopePosition, B, B); | ||||
prependScopeEnd(Block, B, TerminatedLoop); | |||||
addSuccessor(Block, BreakJumpTarget.block); | addSuccessor(Block, BreakJumpTarget.block); | ||||
} else | } else | ||||
badCFG = true; | badCFG = true; | ||||
return Block; | return Block; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C, | ||||
// See if this is a known constant. | // See if this is a known constant. | ||||
const TryResult& KnownVal = tryEvaluateBool(C->getCond()); | const TryResult& KnownVal = tryEvaluateBool(C->getCond()); | ||||
addSuccessor(Block, KnownVal.isFalse() ? nullptr : LHSBlock); | addSuccessor(Block, KnownVal.isFalse() ? nullptr : LHSBlock); | ||||
addSuccessor(Block, KnownVal.isTrue() ? nullptr : RHSBlock); | addSuccessor(Block, KnownVal.isTrue() ? nullptr : RHSBlock); | ||||
Block->setTerminator(C); | Block->setTerminator(C); | ||||
return addStmt(C->getCond()); | return addStmt(C->getCond()); | ||||
} | } | ||||
// If some block is terminated by break, continue or return don't need to emit | |||||
// ScopeEnd right away and we leave this to corresponding visitor. | |||||
static bool shouldDeferScopeEnd(Stmt *S) { | |||||
return isa<BreakStmt>(S) || isa<ContinueStmt>(S) || isa<ReturnStmt>(S); | |||||
} | |||||
CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { | CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { | ||||
SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); | |||||
LocalScope::const_iterator scopeBeginPos = ScopePos; | LocalScope::const_iterator scopeBeginPos = ScopePos; | ||||
addLocalScopeForStmt(C); | addLocalScopeForStmt(C, C); | ||||
if (!C->body_empty() && !isa<ReturnStmt>(*C->body_rbegin())) { | if (!C->body_empty() && !isa<ReturnStmt>(*C->body_rbegin())) | ||||
// If the body ends with a ReturnStmt, the dtors will be added in | // If the body ends with a ReturnStmt, the dtors will be added in | ||||
// VisitReturnStmt. | // VisitReturnStmt. | ||||
addAutomaticObjHandling(ScopePos, scopeBeginPos, C); | addAutomaticObjHandling(ScopePos, scopeBeginPos, C, C); | ||||
if (!C->body_empty() && needAddScopes() && | |||||
!shouldDeferScopeEnd(*C->body_rbegin())) { | |||||
autoCreateBlock(); | |||||
prependScopeEnd(Block, C, C); | |||||
} | } | ||||
CFGBlock *LastBlock = Block; | CFGBlock *LastBlock = Block; | ||||
for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend(); | for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend(); | ||||
I != E; ++I ) { | I != E; ++I ) { | ||||
// If we hit a segment of code just containing ';' (NullStmts), we can | // If we hit a segment of code just containing ';' (NullStmts), we can | ||||
// get a null block back. In such cases, just use the LastBlock | // get a null block back. In such cases, just use the LastBlock | ||||
if (CFGBlock *newBlock = addStmt(*I)) | if (CFGBlock *newBlock = addStmt(*I)) | ||||
LastBlock = newBlock; | LastBlock = newBlock; | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (needAddScopes() && !C->body_empty()) { | |||||
if (!LastBlock) | |||||
LastBlock = createBlock(); | |||||
appendScopeBegin(LastBlock, C); | |||||
} | |||||
return LastBlock; | return LastBlock; | ||||
} | } | ||||
CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, | CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, | ||||
AddStmtChoice asc) { | AddStmtChoice asc) { | ||||
const BinaryConditionalOperator *BCO = dyn_cast<BinaryConditionalOperator>(C); | const BinaryConditionalOperator *BCO = dyn_cast<BinaryConditionalOperator>(C); | ||||
const OpaqueValueExpr *opaqueValue = (BCO ? BCO->getOpaqueValue() : nullptr); | const OpaqueValueExpr *opaqueValue = (BCO ? BCO->getOpaqueValue() : nullptr); | ||||
▲ Show 20 Lines • Show All 177 Lines • ▼ Show 20 Lines | if (blockAfterStaticInit) { | ||||
addSuccessor(Block, blockAfterStaticInit); | addSuccessor(Block, blockAfterStaticInit); | ||||
addSuccessor(Block, B); | addSuccessor(Block, B); | ||||
B = Block; | B = Block; | ||||
} | } | ||||
return B; | return B; | ||||
} | } | ||||
void CFGBuilder::CreateScopeEndBlockForIfStmt(IfStmt *I) { | |||||
autoCreateBlock(); | |||||
prependScopeEnd(Block, I, I); | |||||
Succ = Block; | |||||
} | |||||
CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { | CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { | ||||
// We may see an if statement in the middle of a basic block, or it may be the | // We may see an if statement in the middle of a basic block, or it may be the | ||||
// first statement we are processing. In either case, we create a new basic | // first statement we are processing. In either case, we create a new basic | ||||
// block. First, we create the blocks for the then...else statements, and | // block. First, we create the blocks for the then...else statements, and | ||||
// then we create the block containing the if statement. If we were in the | // then we create the block containing the if statement. If we were in the | ||||
// middle of a block, we stop processing that block. That block is then the | // middle of a block, we stop processing that block. That block is then the | ||||
// implicit successor for the "then" and "else" clauses. | // implicit successor for the "then" and "else" clauses. | ||||
// Save local scope position because in case of condition variable ScopePos | // Save local scope position because in case of condition variable ScopePos | ||||
// won't be restored when traversing AST. | // won't be restored when traversing AST. | ||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | ||||
// Create local scope for C++17 if init-stmt if one exists. | // Create local scope for C++17 if init-stmt if one exists. | ||||
if (Stmt *Init = I->getInit()) | if (Stmt *Init = I->getInit()) | ||||
addLocalScopeForStmt(Init); | addLocalScopeForStmt(Init, I); | ||||
// Create local scope for possible condition variable. | // Create local scope for possible condition variable. | ||||
// Store scope position. Add implicit destructor. | // Store scope position. Add implicit destructor. | ||||
if (VarDecl *VD = I->getConditionVariable()) | if (VarDecl *VD = I->getConditionVariable()) | ||||
addLocalScopeForVarDecl(VD); | addLocalScopeForVarDecl(VD, I); | ||||
addAutomaticObjHandling(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 | // The block we were processing is now finished. Make it the successor | ||||
// block. | // block. | ||||
if (Block) { | if (Block) { | ||||
Succ = Block; | Succ = Block; | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// If the IfStmt contains a condition variable, its scope to the CFG. | |||||
if (const DeclStmt *DS = I->getConditionVariableDeclStmt()) | |||||
appendScopeEnd(Succ, DS, DS); | |||||
// Process the false branch. | // Process the false branch. | ||||
CFGBlock *ElseBlock = Succ; | CFGBlock *ElseBlock = Succ; | ||||
if (Stmt *Else = I->getElse()) { | if (Stmt *Else = I->getElse()) { | ||||
SaveAndRestore<CFGBlock*> sv(Succ); | SaveAndRestore<CFGBlock*> sv(Succ); | ||||
// NULL out Block so that the recursive call to Visit will | // NULL out Block so that the recursive call to Visit will | ||||
// create a new basic block. | // create a new basic block. | ||||
Block = nullptr; | Block = nullptr; | ||||
// If branch is not a compound statement create implicit scope | // If branch is not a compound statement create implicit scope | ||||
// and add destructors. | // and add destructors. | ||||
if (!isa<CompoundStmt>(Else)) | if (!isa<CompoundStmt>(Else)) { | ||||
addLocalScopeAndDtors(Else); | addLocalScopeAndDtors(Else, I); | ||||
if (needAddScopes() && !shouldDeferScopeEnd(Else)) | |||||
CreateScopeEndBlockForIfStmt(I); | |||||
} | |||||
ElseBlock = addStmt(Else); | ElseBlock = addStmt(Else); | ||||
if (!ElseBlock) // Can occur when the Else body has all NullStmts. | if (!ElseBlock) // Can occur when the Else body has all NullStmts. | ||||
ElseBlock = sv.get(); | ElseBlock = sv.get(); | ||||
else if (Block) { | else if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (!isa<CompoundStmt>(Else)) | |||||
appendScopeBegin(ElseBlock, I); | |||||
} | } | ||||
// Process the true branch. | // Process the true branch. | ||||
CFGBlock *ThenBlock; | CFGBlock *ThenBlock; | ||||
{ | { | ||||
Stmt *Then = I->getThen(); | Stmt *Then = I->getThen(); | ||||
assert(Then); | assert(Then); | ||||
SaveAndRestore<CFGBlock*> sv(Succ); | SaveAndRestore<CFGBlock*> sv(Succ); | ||||
Block = nullptr; | Block = nullptr; | ||||
// If branch is not a compound statement create implicit scope | // If branch is not a compound statement create implicit scope | ||||
// and add destructors. | // and add destructors. | ||||
if (!isa<CompoundStmt>(Then)) | if (!isa<CompoundStmt>(Then)) { | ||||
addLocalScopeAndDtors(Then); | addLocalScopeAndDtors(Then, I); | ||||
if (needAddScopes() && !shouldDeferScopeEnd(Then)) | |||||
CreateScopeEndBlockForIfStmt(I); | |||||
} | |||||
ThenBlock = addStmt(Then); | ThenBlock = addStmt(Then); | ||||
if (!ThenBlock) { | if (!ThenBlock) { | ||||
// We can reach here if the "then" body has all NullStmts. | // We can reach here if the "then" body has all NullStmts. | ||||
// Create an empty block so we can distinguish between true and false | // Create an empty block so we can distinguish between true and false | ||||
// branches in path-sensitive analyses. | // branches in path-sensitive analyses. | ||||
ThenBlock = createBlock(false); | ThenBlock = createBlock(false); | ||||
addSuccessor(ThenBlock, sv.get()); | addSuccessor(ThenBlock, sv.get()); | ||||
} else if (Block) { | } else if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (!isa<CompoundStmt>(Then)) | |||||
appendScopeBegin(ThenBlock, I); | |||||
} | } | ||||
// Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by | // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by | ||||
// having these handle the actual control-flow jump. Note that | // having these handle the actual control-flow jump. Note that | ||||
// if we introduce a condition variable, e.g. "if (int x = exp1 || exp2)" | // if we introduce a condition variable, e.g. "if (int x = exp1 || exp2)" | ||||
// we resort to the old control-flow behavior. This special handling | // we resort to the old control-flow behavior. This special handling | ||||
// removes infeasible paths from the control-flow graph by having the | // removes infeasible paths from the control-flow graph by having the | ||||
// control-flow transfer of '&&' or '||' go directly into the then/else | // control-flow transfer of '&&' or '||' go directly into the then/else | ||||
Show All 25 Lines | else { | ||||
// created blocks will be pointed to be "Block". | // created blocks will be pointed to be "Block". | ||||
LastBlock = addStmt(I->getCond()); | LastBlock = addStmt(I->getCond()); | ||||
// If the IfStmt contains a condition variable, add it and its | // If the IfStmt contains a condition variable, add it and its | ||||
// initializer to the CFG. | // initializer to the CFG. | ||||
if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) { | if (const DeclStmt* DS = I->getConditionVariableDeclStmt()) { | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
LastBlock = addStmt(const_cast<DeclStmt *>(DS)); | LastBlock = addStmt(const_cast<DeclStmt *>(DS)); | ||||
appendScopeBegin(LastBlock, DS); | |||||
} | } | ||||
} | } | ||||
// Finally, if the IfStmt contains a C++17 init-stmt, add it to the CFG. | // Finally, if the IfStmt contains a C++17 init-stmt, add it to the CFG. | ||||
if (Stmt *Init = I->getInit()) { | if (Stmt *Init = I->getInit()) { | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
LastBlock = addStmt(Init); | LastBlock = addStmt(Init); | ||||
} | } | ||||
return LastBlock; | return LastBlock; | ||||
} | } | ||||
CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { | CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { | ||||
// If we were in the middle of a block we stop processing that block. | // If we were in the middle of a block we stop processing that block. | ||||
// | // | ||||
// NOTE: If a "return" appears in the middle of a block, this means that the | // NOTE: If a "return" appears in the middle of a block, this means that the | ||||
// code afterwards is DEAD (unreachable). We still keep a basic block | // code afterwards is DEAD (unreachable). We still keep a basic block | ||||
// for that code; a simple "mark-and-sweep" from the entry block will be | // for that code; a simple "mark-and-sweep" from the entry block will be | ||||
// able to report such dead blocks. | // able to report such dead blocks. | ||||
// Create the new block. | // Create the new block. | ||||
Block = createBlock(false); | Block = createBlock(false); | ||||
addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R); | addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R, R); | ||||
// If the one of the destructors does not return, we already have the Exit | // If the one of the destructors does not return, we already have the Exit | ||||
// block as a successor. | // block as a successor. | ||||
if (!Block->hasNoReturnElement()) | if (!Block->hasNoReturnElement()) { | ||||
prependScopeEnd(Block, R, R); | |||||
addSuccessor(Block, &cfg->getExit()); | addSuccessor(Block, &cfg->getExit()); | ||||
} | |||||
// Add the return statement to the block. This may create new blocks if R | // Add the return statement to the block. This may create new blocks if R | ||||
// contains control-flow (short-circuit operations). | // contains control-flow (short-circuit operations). | ||||
return VisitStmt(R, AddStmtChoice::AlwaysAdd); | return VisitStmt(R, AddStmtChoice::AlwaysAdd); | ||||
} | } | ||||
CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) { | CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) { | ||||
// Get the block of the labeled statement. Add it to our map. | // Get the block of the labeled statement. Add it to our map. | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { | ||||
// Save local scope position because in case of condition variable ScopePos | // Save local scope position because in case of condition variable ScopePos | ||||
// won't be restored when traversing AST. | // won't be restored when traversing AST. | ||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | ||||
// Create local scope for init statement and possible condition variable. | // Create local scope for init statement and possible condition variable. | ||||
// Add destructor for init statement and condition variable. | // Add destructor for init statement and condition variable. | ||||
// Store scope position for continue statement. | // Store scope position for continue statement. | ||||
if (Stmt *Init = F->getInit()) | if (Stmt *Init = F->getInit()) | ||||
addLocalScopeForStmt(Init); | addLocalScopeForStmt(Init, F); | ||||
LocalScope::const_iterator LoopBeginScopePos = ScopePos; | LocalScope::const_iterator LoopBeginScopePos = ScopePos; | ||||
if (VarDecl *VD = F->getConditionVariable()) | if (VarDecl *VD = F->getConditionVariable()) | ||||
addLocalScopeForVarDecl(VD); | addLocalScopeForVarDecl(VD, F); | ||||
LocalScope::const_iterator ContinueScopePos = ScopePos; | LocalScope::const_iterator ContinueScopePos = ScopePos; | ||||
addAutomaticObjHandling(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 | // "for" is a control-flow statement. Thus we stop processing the current | ||||
// block. | // block. | ||||
if (Block) { | if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { | ||||
ContinueJumpTarget.block->setLoopTarget(F); | ContinueJumpTarget.block->setLoopTarget(F); | ||||
// Loop body should end with destructor of Condition variable (if any). | // Loop body should end with destructor of Condition variable (if any). | ||||
addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F); | addAutomaticObjHandling(ScopePos, LoopBeginScopePos, F); | ||||
// If body is not a compound statement create implicit scope | // If body is not a compound statement create implicit scope | ||||
// and add destructors. | // and add destructors. | ||||
if (!isa<CompoundStmt>(F->getBody())) | if (!isa<CompoundStmt>(F->getBody())) | ||||
addLocalScopeAndDtors(F->getBody()); | addLocalScopeAndDtors(F->getBody(), F); | ||||
{ | |||||
SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop); | |||||
TerminatedLoop = F; | |||||
// Now populate the body block, and in the process create new blocks as we | // Now populate the body block, and in the process create new blocks as we | ||||
// walk the body of the loop. | // walk the body of the loop. | ||||
BodyBlock = addStmt(F->getBody()); | BodyBlock = addStmt(F->getBody()); | ||||
} | |||||
if (!BodyBlock) { | if (!BodyBlock) { | ||||
// In the case of "for (...;...;...);" we can have a null BodyBlock. | // In the case of "for (...;...;...);" we can have a null BodyBlock. | ||||
// Use the continue jump target as the proxy for the body. | // Use the continue jump target as the proxy for the body. | ||||
BodyBlock = ContinueJumpTarget.block; | BodyBlock = ContinueJumpTarget.block; | ||||
} | } | ||||
else if (badCFG) | else if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (!isa<CompoundStmt>(F->getBody())) { | |||||
appendScopeBegin(BodyBlock, F); | |||||
if (!shouldDeferScopeEnd(F->getBody())) | |||||
prependScopeEnd(BodyBlock, F, F); | |||||
} | |||||
// Because of short-circuit evaluation, the condition of the loop can span | // Because of short-circuit evaluation, the condition of the loop can span | ||||
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that | // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that | ||||
// evaluate the condition. | // evaluate the condition. | ||||
CFGBlock *EntryConditionBlock = nullptr, *ExitConditionBlock = nullptr; | CFGBlock *EntryConditionBlock = nullptr, *ExitConditionBlock = nullptr; | ||||
do { | do { | ||||
Expr *C = F->getCond(); | Expr *C = F->getCond(); | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { | ||||
// The condition block is the implicit successor for any code above the loop. | // The condition block is the implicit successor for any code above the loop. | ||||
Succ = EntryConditionBlock; | Succ = EntryConditionBlock; | ||||
// If the loop contains initialization, create a new block for those | // If the loop contains initialization, create a new block for those | ||||
// statements. This block can also contain statements that precede the loop. | // statements. This block can also contain statements that precede the loop. | ||||
if (Stmt *I = F->getInit()) { | if (Stmt *I = F->getInit()) { | ||||
Block = createBlock(); | Block = createBlock(); | ||||
return addStmt(I); | CFGBlock *InitBlock = addStmt(I); | ||||
appendScopeBegin(EntryConditionBlock, I); | |||||
prependScopeEnd(TransitionBlock, I, I); | |||||
appendScopeEnd(LoopSuccessor, I, I); | |||||
return InitBlock; | |||||
} | } | ||||
// There is no loop initialization. We are thus basically a while loop. | // There is no loop initialization. We are thus basically a while loop. | ||||
// NULL out Block to force lazy block construction. | // NULL out Block to force lazy block construction. | ||||
Block = nullptr; | Block = nullptr; | ||||
Succ = EntryConditionBlock; | Succ = EntryConditionBlock; | ||||
return EntryConditionBlock; | return EntryConditionBlock; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 181 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { | ||||
// Save local scope position because in case of condition variable ScopePos | // Save local scope position because in case of condition variable ScopePos | ||||
// won't be restored when traversing AST. | // won't be restored when traversing AST. | ||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | ||||
// Create local scope for possible condition variable. | // Create local scope for possible condition variable. | ||||
// Store scope position for continue statement. | // Store scope position for continue statement. | ||||
LocalScope::const_iterator LoopBeginScopePos = ScopePos; | LocalScope::const_iterator LoopBeginScopePos = ScopePos; | ||||
if (VarDecl *VD = W->getConditionVariable()) { | if (VarDecl *VD = W->getConditionVariable()) { | ||||
addLocalScopeForVarDecl(VD); | addLocalScopeForVarDecl(VD, W); | ||||
addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); | addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); | ||||
} | } | ||||
// "while" is a control-flow statement. Thus we stop processing the current | // "while" is a control-flow statement. Thus we stop processing the current | ||||
// block. | // block. | ||||
if (Block) { | if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
LoopSuccessor = Block; | LoopSuccessor = Block; | ||||
Block = nullptr; | Block = nullptr; | ||||
} else { | } else { | ||||
LoopSuccessor = Succ; | LoopSuccessor = Succ; | ||||
} | } | ||||
CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr; | CFGBlock *BodyBlock = nullptr, *TransitionBlock = nullptr; | ||||
// If this block contains a condition variable, add scope end to it now. | |||||
if (VarDecl *VD = W->getConditionVariable()) | |||||
if (Expr *Init = VD->getInit()) | |||||
appendScopeEnd(LoopSuccessor, Init, Init); | |||||
// Process the loop body. | // Process the loop body. | ||||
{ | { | ||||
assert(W->getBody()); | assert(W->getBody()); | ||||
// Save the current values for Block, Succ, continue and break targets. | // Save the current values for Block, Succ, continue and break targets. | ||||
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); | SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); | ||||
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), | SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), | ||||
save_break(BreakJumpTarget); | save_break(BreakJumpTarget); | ||||
// Create an empty block to represent the transition block for looping back | // Create an empty block to represent the transition block for looping back | ||||
// to the head of the loop. | // to the head of the loop. | ||||
Succ = TransitionBlock = createBlock(false); | Succ = TransitionBlock = createBlock(false); | ||||
TransitionBlock->setLoopTarget(W); | TransitionBlock->setLoopTarget(W); | ||||
ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos); | ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos); | ||||
// All breaks should go to the code following the loop. | // All breaks should go to the code following the loop. | ||||
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); | BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); | ||||
// Loop body should end with destructor of Condition variable (if any). | // Loop body should end with destructor of Condition variable (if any). | ||||
addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); | addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W); | ||||
// If body is not a compound statement create implicit scope | // If body is not a compound statement create implicit scope | ||||
// and add destructors. | // and add destructors. | ||||
if (!isa<CompoundStmt>(W->getBody())) | if (!isa<CompoundStmt>(W->getBody())) | ||||
addLocalScopeAndDtors(W->getBody()); | addLocalScopeAndDtors(W->getBody(), W); | ||||
{ | |||||
SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop); | |||||
TerminatedLoop = W; | |||||
// Create the body. The returned block is the entry to the loop body. | // Create the body. The returned block is the entry to the loop body. | ||||
BodyBlock = addStmt(W->getBody()); | BodyBlock = addStmt(W->getBody()); | ||||
} | |||||
if (!BodyBlock) | if (!BodyBlock) | ||||
BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" | BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" | ||||
else if (Block && badCFG) | else if (Block && badCFG) | ||||
return nullptr; | return nullptr; | ||||
if (!isa<CompoundStmt>(W->getBody())) { | |||||
appendScopeBegin(BodyBlock, W); | |||||
if (!shouldDeferScopeEnd(W->getBody())) | |||||
prependScopeEnd(BodyBlock, W, W); | |||||
} | |||||
} | } | ||||
// Because of short-circuit evaluation, the condition of the loop can span | // Because of short-circuit evaluation, the condition of the loop can span | ||||
// multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that | // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that | ||||
// evaluate the condition. | // evaluate the condition. | ||||
CFGBlock *EntryConditionBlock = nullptr, *ExitConditionBlock = nullptr; | CFGBlock *EntryConditionBlock = nullptr, *ExitConditionBlock = nullptr; | ||||
do { | do { | ||||
Show All 20 Lines | do { | ||||
// If this block contains a condition variable, add both the condition | // If this block contains a condition variable, add both the condition | ||||
// variable and initializer to the CFG. | // variable and initializer to the CFG. | ||||
if (VarDecl *VD = W->getConditionVariable()) { | if (VarDecl *VD = W->getConditionVariable()) { | ||||
if (Expr *Init = VD->getInit()) { | if (Expr *Init = VD->getInit()) { | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
appendStmt(Block, W->getConditionVariableDeclStmt()); | appendStmt(Block, W->getConditionVariableDeclStmt()); | ||||
EntryConditionBlock = addStmt(Init); | EntryConditionBlock = addStmt(Init); | ||||
appendScopeBegin(EntryConditionBlock, Init); | |||||
appendScopeEnd(TransitionBlock, Init, Init); | |||||
assert(Block == EntryConditionBlock); | assert(Block == EntryConditionBlock); | ||||
} | } | ||||
} | } | ||||
if (Block && badCFG) | if (Block && badCFG) | ||||
return nullptr; | return nullptr; | ||||
// See if this is a known constant. | // See if this is a known constant. | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | CFGBlock *BodyBlock = nullptr; | ||||
BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); | BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); | ||||
// NULL out Block to force lazy instantiation of blocks for the body. | // NULL out Block to force lazy instantiation of blocks for the body. | ||||
Block = nullptr; | Block = nullptr; | ||||
// If body is not a compound statement create implicit scope | // If body is not a compound statement create implicit scope | ||||
// and add destructors. | // and add destructors. | ||||
if (!isa<CompoundStmt>(D->getBody())) | if (!isa<CompoundStmt>(D->getBody())) | ||||
addLocalScopeAndDtors(D->getBody()); | addLocalScopeAndDtors(D->getBody(), D); | ||||
{ | |||||
SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop); | |||||
TerminatedLoop = D; | |||||
// Create the body. The returned block is the entry to the loop body. | // Create the body. The returned block is the entry to the loop body. | ||||
BodyBlock = addStmt(D->getBody()); | BodyBlock = addStmt(D->getBody()); | ||||
} | |||||
if (!BodyBlock) | if (!BodyBlock) | ||||
BodyBlock = EntryConditionBlock; // can happen for "do ; while(...)" | BodyBlock = EntryConditionBlock; // can happen for "do ; while(...)" | ||||
else if (Block) { | else if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
if (!isa<CompoundStmt>(D->getBody())) { | |||||
appendScopeBegin(BodyBlock, D); | |||||
if (!shouldDeferScopeEnd(D->getBody())) | |||||
prependScopeEnd(BodyBlock, D, D); | |||||
} | |||||
// Add an intermediate block between the BodyBlock and the | // Add an intermediate block between the BodyBlock and the | ||||
// ExitConditionBlock to represent the "loop back" transition. Create an | // ExitConditionBlock to represent the "loop back" transition. Create an | ||||
// empty block to represent the transition block for looping back to the | // empty block to represent the transition block for looping back to the | ||||
// head of the loop. | // head of the loop. | ||||
// FIXME: Can we do this more efficiently without adding another block? | // FIXME: Can we do this more efficiently without adding another block? | ||||
Block = nullptr; | Block = nullptr; | ||||
Succ = BodyBlock; | Succ = BodyBlock; | ||||
CFGBlock *LoopBackBlock = createBlock(); | CFGBlock *LoopBackBlock = createBlock(); | ||||
Show All 27 Lines | CFGBlock *CFGBuilder::VisitContinueStmt(ContinueStmt *C) { | ||||
// Now create a new block that ends with the continue statement. | // Now create a new block that ends with the continue statement. | ||||
Block = createBlock(false); | Block = createBlock(false); | ||||
Block->setTerminator(C); | Block->setTerminator(C); | ||||
// If there is no target for the continue, then we are looking at an | // If there is no target for the continue, then we are looking at an | ||||
// incomplete AST. This means the CFG cannot be constructed. | // incomplete AST. This means the CFG cannot be constructed. | ||||
if (ContinueJumpTarget.block) { | if (ContinueJumpTarget.block) { | ||||
addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C); | addAutomaticObjHandling(ScopePos, ContinueJumpTarget.scopePosition, C, C); | ||||
prependScopeEnd(Block, C, TerminatedLoop); | |||||
addSuccessor(Block, ContinueJumpTarget.block); | addSuccessor(Block, ContinueJumpTarget.block); | ||||
} else | } else | ||||
badCFG = true; | badCFG = true; | ||||
return Block; | return Block; | ||||
} | } | ||||
CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, | CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, | ||||
Show All 31 Lines | CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { | ||||
CFGBlock *SwitchSuccessor = nullptr; | CFGBlock *SwitchSuccessor = nullptr; | ||||
// Save local scope position because in case of condition variable ScopePos | // Save local scope position because in case of condition variable ScopePos | ||||
// won't be restored when traversing AST. | // won't be restored when traversing AST. | ||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | ||||
// Create local scope for C++17 switch init-stmt if one exists. | // Create local scope for C++17 switch init-stmt if one exists. | ||||
if (Stmt *Init = Terminator->getInit()) | if (Stmt *Init = Terminator->getInit()) | ||||
addLocalScopeForStmt(Init); | addLocalScopeForStmt(Init, Terminator); | ||||
// Create local scope for possible condition variable. | // Create local scope for possible condition variable. | ||||
// Store scope position. Add implicit destructor. | // Store scope position. Add implicit destructor. | ||||
if (VarDecl *VD = Terminator->getConditionVariable()) | if (VarDecl *VD = Terminator->getConditionVariable()) | ||||
addLocalScopeForVarDecl(VD); | addLocalScopeForVarDecl(VD, Terminator); | ||||
addAutomaticObjHandling(ScopePos, save_scope_pos.get(), Terminator); | addAutomaticObjHandling(ScopePos, save_scope_pos.get(), Terminator); | ||||
if (Block) { | if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
SwitchSuccessor = Block; | SwitchSuccessor = Block; | ||||
} else SwitchSuccessor = Succ; | } else SwitchSuccessor = Succ; | ||||
Show All 32 Lines | CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { | ||||
Expr::EvalResult result; | Expr::EvalResult result; | ||||
bool b = tryEvaluate(Terminator->getCond(), result); | bool b = tryEvaluate(Terminator->getCond(), result); | ||||
SaveAndRestore<Expr::EvalResult*> save_switchCond(switchCond, | SaveAndRestore<Expr::EvalResult*> save_switchCond(switchCond, | ||||
b ? &result : nullptr); | b ? &result : nullptr); | ||||
// If body is not a compound statement create implicit scope | // If body is not a compound statement create implicit scope | ||||
// and add destructors. | // and add destructors. | ||||
if (!isa<CompoundStmt>(Terminator->getBody())) | if (!isa<CompoundStmt>(Terminator->getBody())) | ||||
addLocalScopeAndDtors(Terminator->getBody()); | addLocalScopeAndDtors(Terminator->getBody(), Terminator); | ||||
// If the SwitchStmt contains a condition variable, end its scope after switch. | |||||
if (VarDecl *VD = Terminator->getConditionVariable()) | |||||
if (Expr *Init = VD->getInit()) | |||||
appendScopeEnd(Succ, Terminator->getConditionVariableDeclStmt(), Init); | |||||
{ | |||||
SaveAndRestore<bool> save_InSwitchNest(InSwitchNest); | |||||
InSwitchNest = true; | |||||
addStmt(Terminator->getBody()); | addStmt(Terminator->getBody()); | ||||
if (Block) { | if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// If we have no "default:" case, the default transition is to the code | // If we have no "default:" case, the default transition is to the code | ||||
// following the switch body. Moreover, take into account if all the | // following the switch body. Moreover, take into account if all the | ||||
// cases of a switch are covered (e.g., switching on an enum value). | // cases of a switch are covered (e.g., switching on an enum value). | ||||
// | // | ||||
// Note: We add a successor to a switch that is considered covered yet has no | // Note: We add a successor to a switch that is considered covered yet has | ||||
// no | |||||
// case statements if the enumeration has no enumerators. | // case statements if the enumeration has no enumerators. | ||||
bool SwitchAlwaysHasSuccessor = false; | bool SwitchAlwaysHasSuccessor = false; | ||||
SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; | SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; | ||||
SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() && | SwitchAlwaysHasSuccessor |= | ||||
Terminator->getSwitchCaseList(); | Terminator->isAllEnumCasesCovered() && Terminator->getSwitchCaseList(); | ||||
addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, | addSuccessor(SwitchTerminatedBlock, DefaultCaseBlock, | ||||
!SwitchAlwaysHasSuccessor); | !SwitchAlwaysHasSuccessor); | ||||
} | |||||
// Add the terminator and condition in the switch block. | // Add the terminator and condition in the switch block. | ||||
SwitchTerminatedBlock->setTerminator(Terminator); | SwitchTerminatedBlock->setTerminator(Terminator); | ||||
Block = SwitchTerminatedBlock; | Block = SwitchTerminatedBlock; | ||||
CFGBlock *LastBlock = addStmt(Terminator->getCond()); | CFGBlock *LastBlock = addStmt(Terminator->getCond()); | ||||
// If the SwitchStmt contains a condition variable, add both the | // If the SwitchStmt contains a condition variable, add both the | ||||
// SwitchStmt and the condition variable initialization to the CFG. | // SwitchStmt and the condition variable initialization to the CFG. | ||||
if (VarDecl *VD = Terminator->getConditionVariable()) { | if (VarDecl *VD = Terminator->getConditionVariable()) { | ||||
if (Expr *Init = VD->getInit()) { | if (Expr *Init = VD->getInit()) { | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
appendStmt(Block, Terminator->getConditionVariableDeclStmt()); | auto VDDeclStmt = Terminator->getConditionVariableDeclStmt(); | ||||
appendStmt(Block, VDDeclStmt); | |||||
LastBlock = addStmt(Init); | LastBlock = addStmt(Init); | ||||
appendScopeBegin(LastBlock, VDDeclStmt); | |||||
} | } | ||||
} | } | ||||
// Finally, if the SwitchStmt contains a C++17 init-stmt, add it to the CFG. | // Finally, if the SwitchStmt contains a C++17 init-stmt, add it to the CFG. | ||||
if (Stmt *Init = Terminator->getInit()) { | if (Stmt *Init = Terminator->getInit()) { | ||||
autoCreateBlock(); | autoCreateBlock(); | ||||
LastBlock = addStmt(Init); | LastBlock = addStmt(Init); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) { | ||||
// Save local scope position because in case of exception variable ScopePos | // Save local scope position because in case of exception variable ScopePos | ||||
// won't be restored when traversing AST. | // won't be restored when traversing AST. | ||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | ||||
// Create local scope for possible exception variable. | // Create local scope for possible exception variable. | ||||
// Store scope position. Add implicit destructor. | // Store scope position. Add implicit destructor. | ||||
if (VarDecl *VD = CS->getExceptionDecl()) { | if (VarDecl *VD = CS->getExceptionDecl()) { | ||||
LocalScope::const_iterator BeginScopePos = ScopePos; | LocalScope::const_iterator BeginScopePos = ScopePos; | ||||
addLocalScopeForVarDecl(VD); | addLocalScopeForVarDecl(VD, CS); | ||||
addAutomaticObjHandling(ScopePos, BeginScopePos, CS); | addAutomaticObjHandling(ScopePos, BeginScopePos, CS); | ||||
} | } | ||||
if (CS->getHandlerBlock()) | if (CS->getHandlerBlock()) | ||||
addStmt(CS->getHandlerBlock()); | addStmt(CS->getHandlerBlock()); | ||||
CFGBlock *CatchBlock = Block; | CFGBlock *CatchBlock = Block; | ||||
if (!CatchBlock) | if (!CatchBlock) | ||||
Show All 33 Lines | CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { | ||||
// } | // } | ||||
// } | // } | ||||
// Save local scope position before the addition of the implicit variables. | // Save local scope position before the addition of the implicit variables. | ||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); | ||||
// Create local scopes and destructors for range, begin and end variables. | // Create local scopes and destructors for range, begin and end variables. | ||||
if (Stmt *Range = S->getRangeStmt()) | if (Stmt *Range = S->getRangeStmt()) | ||||
addLocalScopeForStmt(Range); | addLocalScopeForStmt(Range, S); | ||||
if (Stmt *Begin = S->getBeginStmt()) | if (Stmt *Begin = S->getBeginStmt()) | ||||
addLocalScopeForStmt(Begin); | addLocalScopeForStmt(Begin, S); | ||||
if (Stmt *End = S->getEndStmt()) | if (Stmt *End = S->getEndStmt()) | ||||
addLocalScopeForStmt(End); | addLocalScopeForStmt(End, S); | ||||
addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S); | addAutomaticObjHandling(ScopePos, save_scope_pos.get(), S); | ||||
LocalScope::const_iterator ContinueScopePos = ScopePos; | LocalScope::const_iterator ContinueScopePos = ScopePos; | ||||
Stmt *LoopVarStmt = S->getLoopVarStmt(); | |||||
// "for" is a control-flow statement. Thus we stop processing the current | // "for" is a control-flow statement. Thus we stop processing the current | ||||
// block. | // block. | ||||
CFGBlock *LoopSuccessor = nullptr; | CFGBlock *LoopSuccessor = nullptr; | ||||
if (Block) { | if (Block) { | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
LoopSuccessor = Block; | LoopSuccessor = Block; | ||||
} else | } else | ||||
Show All 36 Lines | // Now create the loop body. | ||||
// Save the current values for Block, Succ, and continue targets. | // Save the current values for Block, Succ, and continue targets. | ||||
SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); | SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); | ||||
SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); | SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); | ||||
// Generate increment code in its own basic block. This is the target of | // Generate increment code in its own basic block. This is the target of | ||||
// continue statements. | // continue statements. | ||||
Block = nullptr; | Block = nullptr; | ||||
Succ = addStmt(S->getInc()); | Succ = addStmt(S->getInc()); | ||||
prependScopeEnd(Succ, LoopVarStmt, LoopVarStmt); | |||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); | ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); | ||||
// The starting block for the loop increment is the block that should | // The starting block for the loop increment is the block that should | ||||
// represent the 'loop target' for looping back to the start of the loop. | // represent the 'loop target' for looping back to the start of the loop. | ||||
ContinueJumpTarget.block->setLoopTarget(S); | ContinueJumpTarget.block->setLoopTarget(S); | ||||
// Finish up the increment block and prepare to start the loop body. | // Finish up the increment block and prepare to start the loop body. | ||||
assert(Block); | assert(Block); | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
Block = nullptr; | Block = nullptr; | ||||
// Add implicit scope and dtors for loop variable. | // Add implicit scope and dtors for loop variable. | ||||
addLocalScopeAndDtors(S->getLoopVarStmt()); | addLocalScopeAndDtors(S->getLoopVarStmt(), S); | ||||
CFGBlock *BodyBlock = nullptr; | |||||
{ | |||||
SaveAndRestore<Stmt *> save_TerminatedLoop(TerminatedLoop); | |||||
TerminatedLoop = S; | |||||
// Populate a new block to contain the loop body and loop variable. | // Populate a new block to contain the loop body and loop variable. | ||||
addStmt(S->getBody()); | BodyBlock = addStmt(S->getBody()); | ||||
} | |||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
CFGBlock *LoopVarStmtBlock = addStmt(S->getLoopVarStmt()); | CFGBlock *LoopVarStmtBlock = addStmt(S->getLoopVarStmt()); | ||||
if (badCFG) | if (badCFG) | ||||
return nullptr; | return nullptr; | ||||
if (!isa<CompoundStmt>(S->getBody())) { | |||||
appendScopeBegin(BodyBlock, S); | |||||
if (!shouldDeferScopeEnd(S->getBody())) | |||||
prependScopeEnd(BodyBlock, S, S); | |||||
} | |||||
appendScopeBegin(BodyBlock, LoopVarStmt); | |||||
// This new body block is a successor to our condition block. | // This new body block is a successor to our condition block. | ||||
addSuccessor(ConditionBlock, | addSuccessor(ConditionBlock, | ||||
KnownVal.isFalse() ? nullptr : LoopVarStmtBlock); | KnownVal.isFalse() ? nullptr : LoopVarStmtBlock); | ||||
} | } | ||||
// Link up the condition block with the code that follows the loop (the | // Link up the condition block with the code that follows the loop (the | ||||
// false branch). | // false branch). | ||||
addSuccessor(ConditionBlock, KnownVal.isTrue() ? nullptr : LoopSuccessor); | addSuccessor(ConditionBlock, KnownVal.isTrue() ? nullptr : LoopSuccessor); | ||||
▲ Show 20 Lines • Show All 375 Lines • ▼ Show 20 Lines | |||||
const CXXDestructorDecl * | const CXXDestructorDecl * | ||||
CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { | CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { | ||||
switch (getKind()) { | switch (getKind()) { | ||||
case CFGElement::Statement: | case CFGElement::Statement: | ||||
case CFGElement::Initializer: | case CFGElement::Initializer: | ||||
case CFGElement::NewAllocator: | case CFGElement::NewAllocator: | ||||
case CFGElement::LifetimeEnds: | case CFGElement::LifetimeEnds: | ||||
case CFGElement::ScopeBegin: | |||||
case CFGElement::ScopeEnd: | |||||
llvm_unreachable("getDestructorDecl should only be used with " | llvm_unreachable("getDestructorDecl should only be used with " | ||||
"ImplicitDtors"); | "ImplicitDtors"); | ||||
case CFGElement::AutomaticObjectDtor: { | case CFGElement::AutomaticObjectDtor: { | ||||
const VarDecl *var = castAs<CFGAutomaticObjDtor>().getVarDecl(); | const VarDecl *var = castAs<CFGAutomaticObjDtor>().getVarDecl(); | ||||
QualType ty = var->getType(); | QualType ty = var->getType(); | ||||
// FIXME: See CFGBuilder::addLocalScopeForVarDecl. | // FIXME: See CFGBuilder::addLocalScopeForVarDecl. | ||||
// | // | ||||
▲ Show 20 Lines • Show All 400 Lines • ▼ Show 20 Lines | if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) { | ||||
OS << " (Implicit destructor)\n"; | OS << " (Implicit destructor)\n"; | ||||
} else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) { | } else if (Optional<CFGLifetimeEnds> DE = E.getAs<CFGLifetimeEnds>()) { | ||||
const VarDecl *VD = DE->getVarDecl(); | const VarDecl *VD = DE->getVarDecl(); | ||||
Helper.handleDecl(VD, OS); | Helper.handleDecl(VD, OS); | ||||
OS << " (Lifetime ends)\n"; | OS << " (Lifetime ends)\n"; | ||||
} else if (Optional<CFGScopeBegin> SB = E.getAs<CFGScopeBegin>()) { | |||||
OS << "CFGScopeBegin("; | |||||
if (const Stmt *S = SB->getTriggerStmt()) | |||||
OS << S->getStmtClassName(); | |||||
OS << ")\n"; | |||||
} else if (Optional<CFGScopeEnd> SE = E.getAs<CFGScopeEnd>()) { | |||||
OS << "CFGScopeEnd("; | |||||
if (const Stmt *S = SE->getTriggerStmt()) | |||||
OS << S->getStmtClassName(); | |||||
OS << ")\n"; | |||||
} else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { | } else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) { | ||||
OS << "CFGNewAllocator("; | OS << "CFGNewAllocator("; | ||||
if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) | if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr()) | ||||
AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); | AllocExpr->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); | ||||
OS << ")\n"; | OS << ")\n"; | ||||
} else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) { | } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) { | ||||
const CXXRecordDecl *RD = DE->getCXXRecordDecl(); | const CXXRecordDecl *RD = DE->getCXXRecordDecl(); | ||||
if (!RD) | if (!RD) | ||||
▲ Show 20 Lines • Show All 374 Lines • Show Last 20 Lines |
I think it would be helpful to name this something more expressive (perhaps "TriggerStmt") so it is immediately clear what the relationship between the statement the LocalScope is.