summaryrefslogtreecommitdiff
path: root/lib/Analysis/CFG.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Analysis/CFG.cpp')
-rw-r--r--lib/Analysis/CFG.cpp449
1 files changed, 316 insertions, 133 deletions
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 6a77455edeef..714b85d3a9ff 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -1,4 +1,4 @@
-//===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===//
+//===- CFG.cpp - Classes for representing and building CFGs ---------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -15,23 +15,53 @@
#include "clang/Analysis/CFG.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
-#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/OperationKinds.h"
#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/Support/BumpVector.h"
#include "clang/Basic/Builtins.h"
+#include "clang/Basic/ExceptionSpecificationType.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
-#include <memory>
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/DOTGraphTraits.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
using namespace clang;
-namespace {
-
static SourceLocation GetEndLoc(Decl *D) {
if (VarDecl *VD = dyn_cast<VarDecl>(D))
if (Expr *Ex = VD->getInit())
@@ -41,7 +71,7 @@ static SourceLocation GetEndLoc(Decl *D) {
/// Helper for tryNormalizeBinaryOperator. Attempts to extract an IntegerLiteral
/// or EnumConstantDecl from the given Expr. If it fails, returns nullptr.
-const Expr *tryTransformToIntOrEnumConstant(const Expr *E) {
+static const Expr *tryTransformToIntOrEnumConstant(const Expr *E) {
E = E->IgnoreParens();
if (isa<IntegerLiteral>(E))
return E;
@@ -111,6 +141,8 @@ static bool areExprTypesCompatible(const Expr *E1, const Expr *E2) {
return DC1 == DC2;
}
+namespace {
+
class CFGBuilder;
/// The CFG builder uses a recursive algorithm to build the CFG. When
@@ -125,7 +157,6 @@ class CFGBuilder;
/// contextual information. If AddStmtChoice is 'NotAlwaysAdd', then
/// the builder has an option not to add a subexpression as a
/// block-level expression.
-///
class AddStmtChoice {
public:
enum Kind { NotAlwaysAdd = 0, AlwaysAdd = 1 };
@@ -168,23 +199,24 @@ private:
///
class LocalScope {
public:
- typedef BumpVector<VarDecl*> AutomaticVarsTy;
+ friend class const_iterator;
+
+ using AutomaticVarsTy = BumpVector<VarDecl *>;
/// const_iterator - Iterates local scope backwards and jumps to previous
/// scope on reaching the beginning of currently iterated scope.
class const_iterator {
- const LocalScope* Scope;
+ const LocalScope* Scope = nullptr;
/// VarIter is guaranteed to be greater then 0 for every valid iterator.
/// Invalid iterator (with null Scope) has VarIter equal to 0.
- unsigned VarIter;
+ unsigned VarIter = 0;
public:
/// Create invalid iterator. Dereferencing invalid iterator is not allowed.
/// Incrementing invalid iterator is allowed and will result in invalid
/// iterator.
- const_iterator()
- : Scope(nullptr), VarIter(0) {}
+ const_iterator() = default;
/// Create valid iterator. In case when S.Prev is an invalid iterator and
/// I is equal to 0, this will create invalid iterator.
@@ -197,8 +229,8 @@ public:
}
VarDecl *const* operator->() const {
- assert (Scope && "Dereferencing invalid iterator is not allowed");
- assert (VarIter != 0 && "Iterator has invalid value of VarIter member");
+ assert(Scope && "Dereferencing invalid iterator is not allowed");
+ assert(VarIter != 0 && "Iterator has invalid value of VarIter member");
return &Scope->Vars[VarIter - 1];
}
VarDecl *operator*() const {
@@ -209,7 +241,7 @@ public:
if (!Scope)
return *this;
- assert (VarIter != 0 && "Iterator has invalid value of VarIter member");
+ assert(VarIter != 0 && "Iterator has invalid value of VarIter member");
--VarIter;
if (VarIter == 0)
*this = Scope->Prev;
@@ -236,13 +268,12 @@ public:
const_iterator shared_parent(const_iterator L);
};
- friend class const_iterator;
-
private:
BumpVectorContext ctx;
/// Automatic variables in order of declaration.
AutomaticVarsTy Vars;
+
/// Iterator to variable in previous scope that was declared just before
/// begin of this scope.
const_iterator Prev;
@@ -260,6 +291,8 @@ public:
}
};
+} // namespace
+
/// distance - Calculates distance from this to L. L must be reachable from this
/// (with use of ++ operator). Cost of calculating the distance is linear w.r.t.
/// number of scopes between this and L.
@@ -267,8 +300,8 @@ int LocalScope::const_iterator::distance(LocalScope::const_iterator L) {
int D = 0;
const_iterator F = *this;
while (F.Scope != L.Scope) {
- assert (F != const_iterator()
- && "L iterator is not reachable from F iterator.");
+ assert(F != const_iterator() &&
+ "L iterator is not reachable from F iterator.");
D += F.VarIter;
F = F.Scope->Prev;
}
@@ -300,16 +333,18 @@ LocalScope::const_iterator::shared_parent(LocalScope::const_iterator L) {
}
}
+namespace {
+
/// 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.
struct BlockScopePosPair {
- BlockScopePosPair() : block(nullptr) {}
+ CFGBlock *block = nullptr;
+ LocalScope::const_iterator scopePosition;
+
+ BlockScopePosPair() = default;
BlockScopePosPair(CFGBlock *b, LocalScope::const_iterator scopePos)
: block(b), scopePosition(scopePos) {}
-
- CFGBlock *block;
- LocalScope::const_iterator scopePosition;
};
/// TryResult - a class representing a variant over the values
@@ -317,37 +352,46 @@ struct BlockScopePosPair {
/// and is used by the CFGBuilder to decide if a branch condition
/// can be decided up front during CFG construction.
class TryResult {
- int X;
+ int X = -1;
+
public:
+ TryResult() = default;
TryResult(bool b) : X(b ? 1 : 0) {}
- TryResult() : X(-1) {}
bool isTrue() const { return X == 1; }
bool isFalse() const { return X == 0; }
bool isKnown() const { return X >= 0; }
+
void negate() {
assert(isKnown());
X ^= 0x1;
}
};
-TryResult bothKnownTrue(TryResult R1, TryResult R2) {
+} // namespace
+
+static TryResult bothKnownTrue(TryResult R1, TryResult R2) {
if (!R1.isKnown() || !R2.isKnown())
return TryResult();
return TryResult(R1.isTrue() && R2.isTrue());
}
+namespace {
+
class reverse_children {
llvm::SmallVector<Stmt *, 12> childrenBuf;
- ArrayRef<Stmt*> children;
+ ArrayRef<Stmt *> children;
+
public:
reverse_children(Stmt *S);
- typedef ArrayRef<Stmt*>::reverse_iterator iterator;
+ using iterator = ArrayRef<Stmt *>::reverse_iterator;
+
iterator begin() const { return children.rbegin(); }
iterator end() const { return children.rend(); }
};
+} // namespace
reverse_children::reverse_children(Stmt *S) {
if (CallExpr *CE = dyn_cast<CallExpr>(S)) {
@@ -374,6 +418,8 @@ reverse_children::reverse_children(Stmt *S) {
children = childrenBuf;
}
+namespace {
+
/// CFGBuilder - This class implements CFG construction from an AST.
/// The builder is stateful: an instance of the builder should be used to only
/// construct a single CFG.
@@ -387,62 +433,65 @@ reverse_children::reverse_children(Stmt *S) {
/// the AST in reverse order so that the successor of a basic block is
/// constructed prior to its predecessor. This allows us to nicely capture
/// implicit fall-throughs without extra basic blocks.
-///
class CFGBuilder {
- typedef BlockScopePosPair JumpTarget;
- typedef BlockScopePosPair JumpSource;
+ using JumpTarget = BlockScopePosPair;
+ using JumpSource = BlockScopePosPair;
ASTContext *Context;
std::unique_ptr<CFG> cfg;
- CFGBlock *Block;
- CFGBlock *Succ;
+ // Current block.
+ CFGBlock *Block = nullptr;
+
+ // Block after the current block.
+ CFGBlock *Succ = nullptr;
+
JumpTarget ContinueJumpTarget;
JumpTarget BreakJumpTarget;
- CFGBlock *SwitchTerminatedBlock;
- CFGBlock *DefaultCaseBlock;
- CFGBlock *TryTerminatedBlock;
+ JumpTarget SEHLeaveJumpTarget;
+ CFGBlock *SwitchTerminatedBlock = nullptr;
+ CFGBlock *DefaultCaseBlock = nullptr;
+
+ // This can point either to a try or a __try block. The frontend forbids
+ // mixing both kinds in one function, so having one for both is enough.
+ CFGBlock *TryTerminatedBlock = nullptr;
// Current position in local scope.
LocalScope::const_iterator ScopePos;
// LabelMap records the mapping from Label expressions to their jump targets.
- typedef llvm::DenseMap<LabelDecl*, JumpTarget> LabelMapTy;
+ using LabelMapTy = llvm::DenseMap<LabelDecl *, JumpTarget>;
LabelMapTy LabelMap;
// A list of blocks that end with a "goto" that must be backpatched to their
// resolved targets upon completion of CFG construction.
- typedef std::vector<JumpSource> BackpatchBlocksTy;
+ using BackpatchBlocksTy = std::vector<JumpSource>;
BackpatchBlocksTy BackpatchBlocks;
// A list of labels whose address has been taken (for indirect gotos).
- typedef llvm::SmallPtrSet<LabelDecl*, 5> LabelSetTy;
+ using LabelSetTy = llvm::SmallSetVector<LabelDecl *, 8>;
LabelSetTy AddressTakenLabels;
- bool badCFG;
+ bool badCFG = false;
const CFG::BuildOptions &BuildOpts;
// State to track for building switch statements.
- bool switchExclusivelyCovered;
- Expr::EvalResult *switchCond;
+ bool switchExclusivelyCovered = false;
+ Expr::EvalResult *switchCond = nullptr;
- CFG::BuildOptions::ForcedBlkExprs::value_type *cachedEntry;
- const Stmt *lastLookup;
+ CFG::BuildOptions::ForcedBlkExprs::value_type *cachedEntry = nullptr;
+ const Stmt *lastLookup = nullptr;
// Caches boolean evaluations of expressions to avoid multiple re-evaluations
// during construction of branches for chained logical operators.
- typedef llvm::DenseMap<Expr *, TryResult> CachedBoolEvalsTy;
+ using CachedBoolEvalsTy = llvm::DenseMap<Expr *, TryResult>;
CachedBoolEvalsTy CachedBoolEvals;
public:
explicit CFGBuilder(ASTContext *astContext,
- const CFG::BuildOptions &buildOpts)
- : Context(astContext), cfg(new CFG()), // crew a new CFG
- Block(nullptr), Succ(nullptr),
- SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr),
- TryTerminatedBlock(nullptr), badCFG(false), BuildOpts(buildOpts),
- switchExclusivelyCovered(false), switchCond(nullptr),
- cachedEntry(nullptr), lastLookup(nullptr) {}
+ const CFG::BuildOptions &buildOpts)
+ : Context(astContext), cfg(new CFG()), // crew a new CFG
+ BuildOpts(buildOpts) {}
// buildCFG - Used by external clients to construct the CFG.
std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement);
@@ -501,6 +550,10 @@ private:
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
CFGBlock *VisitReturnStmt(ReturnStmt *R);
+ CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S);
+ CFGBlock *VisitSEHFinallyStmt(SEHFinallyStmt *S);
+ CFGBlock *VisitSEHLeaveStmt(SEHLeaveStmt *S);
+ CFGBlock *VisitSEHTryStmt(SEHTryStmt *S);
CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc);
CFGBlock *VisitSwitchStmt(SwitchStmt *S);
CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
@@ -542,13 +595,9 @@ private:
/// if the CXXBindTemporaryExpr was marked executed, and otherwise
/// branches to the stored successor.
struct TempDtorContext {
- TempDtorContext()
- : IsConditional(false), KnownExecuted(true), Succ(nullptr),
- TerminatorExpr(nullptr) {}
-
+ TempDtorContext() = default;
TempDtorContext(TryResult KnownExecuted)
- : IsConditional(true), KnownExecuted(KnownExecuted), Succ(nullptr),
- TerminatorExpr(nullptr) {}
+ : IsConditional(true), KnownExecuted(KnownExecuted) {}
/// Returns whether we need to start a new branch for a temporary destructor
/// call. This is the case when the temporary destructor is
@@ -567,10 +616,10 @@ private:
TerminatorExpr = E;
}
- const bool IsConditional;
- const TryResult KnownExecuted;
- CFGBlock *Succ;
- CXXBindTemporaryExpr *TerminatorExpr;
+ const bool IsConditional = false;
+ const TryResult KnownExecuted = true;
+ CFGBlock *Succ = nullptr;
+ CXXBindTemporaryExpr *TerminatorExpr = nullptr;
};
// Visitors to walk an AST and generate destructors of temporaries in
@@ -601,7 +650,9 @@ private:
CFGBlock *addStmt(Stmt *S) {
return Visit(S, AddStmtChoice::AlwaysAdd);
}
+
CFGBlock *addInitializer(CXXCtorInitializer *I);
+ void addLoopExit(const Stmt *LoopStmt);
void addAutomaticObjDtors(LocalScope::const_iterator B,
LocalScope::const_iterator E, Stmt *S);
void addLifetimeEnds(LocalScope::const_iterator B,
@@ -621,6 +672,7 @@ private:
void addLocalScopeAndDtors(Stmt *S);
// Interface to CFGBlock - adding CFGElements.
+
void appendStmt(CFGBlock *B, const Stmt *S) {
if (alwaysAdd(S) && cachedEntry)
cachedEntry->second = B;
@@ -629,21 +681,27 @@ private:
assert(!isa<Expr>(S) || cast<Expr>(S)->IgnoreParens() == S);
B->appendStmt(const_cast<Stmt*>(S), cfg->getBumpVectorContext());
}
+
void appendInitializer(CFGBlock *B, CXXCtorInitializer *I) {
B->appendInitializer(I, cfg->getBumpVectorContext());
}
+
void appendNewAllocator(CFGBlock *B, CXXNewExpr *NE) {
B->appendNewAllocator(NE, cfg->getBumpVectorContext());
}
+
void appendBaseDtor(CFGBlock *B, const CXXBaseSpecifier *BS) {
B->appendBaseDtor(BS, cfg->getBumpVectorContext());
}
+
void appendMemberDtor(CFGBlock *B, FieldDecl *FD) {
B->appendMemberDtor(FD, cfg->getBumpVectorContext());
}
+
void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) {
B->appendTemporaryDtor(E, cfg->getBumpVectorContext());
}
+
void appendAutomaticObjDtor(CFGBlock *B, VarDecl *VD, Stmt *S) {
B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
}
@@ -652,6 +710,10 @@ private:
B->appendLifetimeEnds(VD, S, cfg->getBumpVectorContext());
}
+ void appendLoopExit(CFGBlock *B, const Stmt *LoopStmt) {
+ B->appendLoopExit(LoopStmt, cfg->getBumpVectorContext());
+ }
+
void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) {
B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext());
}
@@ -799,10 +861,10 @@ private:
const BinaryOperator *RHS =
dyn_cast<BinaryOperator>(B->getRHS()->IgnoreParens());
if (!LHS || !RHS)
- return TryResult();
+ return {};
if (!LHS->isComparisonOp() || !RHS->isComparisonOp())
- return TryResult();
+ return {};
const DeclRefExpr *Decl1;
const Expr *Expr1;
@@ -810,7 +872,7 @@ private:
std::tie(Decl1, BO1, Expr1) = tryNormalizeBinaryOperator(LHS);
if (!Decl1 || !Expr1)
- return TryResult();
+ return {};
const DeclRefExpr *Decl2;
const Expr *Expr2;
@@ -818,26 +880,26 @@ private:
std::tie(Decl2, BO2, Expr2) = tryNormalizeBinaryOperator(RHS);
if (!Decl2 || !Expr2)
- return TryResult();
+ return {};
// Check that it is the same variable on both sides.
if (Decl1->getDecl() != Decl2->getDecl())
- return TryResult();
+ return {};
// Make sure the user's intent is clear (e.g. they're comparing against two
// int literals, or two things from the same enum)
if (!areExprTypesCompatible(Expr1, Expr2))
- return TryResult();
+ return {};
llvm::APSInt L1, L2;
if (!Expr1->EvaluateAsInt(L1, *Context) ||
!Expr2->EvaluateAsInt(L2, *Context))
- return TryResult();
+ return {};
// Can't compare signed with unsigned or with different bit width.
if (L1.isSigned() != L2.isSigned() || L1.getBitWidth() != L2.getBitWidth())
- return TryResult();
+ return {};
// Values that will be used to determine if result of logical
// operator is always true/false
@@ -868,7 +930,7 @@ private:
Res2 = analyzeLogicOperatorCondition(BO2, Value, L2);
if (!Res1.isKnown() || !Res2.isKnown())
- return TryResult();
+ return {};
if (B->getOpcode() == BO_LAnd) {
AlwaysTrue &= (Res1.isTrue() && Res2.isTrue());
@@ -884,7 +946,7 @@ private:
BuildOpts.Observer->compareAlwaysTrue(B, AlwaysTrue);
return TryResult(AlwaysTrue);
}
- return TryResult();
+ return {};
}
/// Try and evaluate an expression to an integer constant.
@@ -901,7 +963,7 @@ private:
TryResult tryEvaluateBool(Expr *S) {
if (!BuildOpts.PruneTriviallyFalseEdges ||
S->isTypeDependent() || S->isValueDependent())
- return TryResult();
+ return {};
if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) {
if (Bop->isLogicalOp()) {
@@ -976,7 +1038,7 @@ private:
}
}
- return TryResult();
+ return {};
} else if (Bop->isEqualityOp()) {
TryResult BopRes = checkIncorrectEqualityOperator(Bop);
if (BopRes.isKnown())
@@ -992,12 +1054,14 @@ private:
if (E->EvaluateAsBooleanCondition(Result, *Context))
return Result;
- return TryResult();
+ return {};
}
bool hasTrivialDestructor(VarDecl *VD);
};
+} // namespace
+
inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder,
const Stmt *stmt) const {
return builder.alwaysAdd(stmt) || kind == AlwaysAdd;
@@ -1119,7 +1183,6 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
if (CFGBlock *B = cfg->getIndirectGotoBlock())
for (LabelSetTy::iterator I = AddressTakenLabels.begin(),
E = AddressTakenLabels.end(); I != E; ++I ) {
-
// Lookup the target block.
LabelMapTy::iterator LI = LabelMap.find(*I);
@@ -1253,6 +1316,15 @@ static QualType getReferenceInitTemporaryType(ASTContext &Context,
return Init->getType();
}
+// TODO: Support adding LoopExit element to the CFG in case where the loop is
+// ended by ReturnStmt, GotoStmt or ThrowExpr.
+void CFGBuilder::addLoopExit(const Stmt *LoopStmt){
+ if(!BuildOpts.AddLoopExit)
+ return;
+ autoCreateBlock();
+ appendLoopExit(Block, LoopStmt);
+}
+
void CFGBuilder::addAutomaticObjHandling(LocalScope::const_iterator B,
LocalScope::const_iterator E,
Stmt *S) {
@@ -1351,8 +1423,8 @@ void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B,
/// addImplicitDtorsForDestructor - Add implicit destructors generated for
/// base and member objects in destructor.
void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) {
- assert (BuildOpts.AddImplicitDtors
- && "Can be called only when dtors should be added");
+ assert(BuildOpts.AddImplicitDtors &&
+ "Can be called only when dtors should be added");
const CXXRecordDecl *RD = DD->getParent();
// At the end destroy virtual base objects.
@@ -1555,6 +1627,7 @@ void CFGBuilder::prependAutomaticObjLifetimeWithTerminator(
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).
@@ -1716,6 +1789,18 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::ReturnStmtClass:
return VisitReturnStmt(cast<ReturnStmt>(S));
+ case Stmt::SEHExceptStmtClass:
+ return VisitSEHExceptStmt(cast<SEHExceptStmt>(S));
+
+ case Stmt::SEHFinallyStmtClass:
+ return VisitSEHFinallyStmt(cast<SEHFinallyStmt>(S));
+
+ case Stmt::SEHLeaveStmtClass:
+ return VisitSEHLeaveStmt(cast<SEHLeaveStmt>(S));
+
+ case Stmt::SEHTryStmtClass:
+ return VisitSEHTryStmt(cast<SEHTryStmt>(S));
+
case Stmt::UnaryExprOrTypeTraitExprClass:
return VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S),
asc);
@@ -1797,7 +1882,6 @@ CFGBuilder::VisitLogicalOperator(BinaryOperator *B,
Stmt *Term,
CFGBlock *TrueBlock,
CFGBlock *FalseBlock) {
-
// Introspect the RHS. If it is a nested logical operation, we recursively
// build the CFG using this function. Otherwise, resort to default
// CFG construction behavior.
@@ -1886,7 +1970,6 @@ CFGBuilder::VisitLogicalOperator(BinaryOperator *B,
return std::make_pair(EntryLHSBlock, ExitBlock);
}
-
CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B,
AddStmtChoice asc) {
// && or ||
@@ -1948,7 +2031,6 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) {
} else
badCFG = true;
-
return Block;
}
@@ -2072,7 +2154,6 @@ CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C,
return addStmt(C->getCond());
}
-
CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) {
LocalScope::const_iterator scopeBeginPos = ScopePos;
addLocalScopeForStmt(C);
@@ -2423,7 +2504,6 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
return LastBlock;
}
-
CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
// If we were in the middle of a block we stop processing that block.
//
@@ -2447,6 +2527,117 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
return VisitStmt(R, AddStmtChoice::AlwaysAdd);
}
+CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
+ // SEHExceptStmt are treated like labels, so they are the first statement in a
+ // block.
+
+ // Save local scope position because in case of exception variable ScopePos
+ // won't be restored when traversing AST.
+ SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);
+
+ addStmt(ES->getBlock());
+ CFGBlock *SEHExceptBlock = Block;
+ if (!SEHExceptBlock)
+ SEHExceptBlock = createBlock();
+
+ appendStmt(SEHExceptBlock, ES);
+
+ // Also add the SEHExceptBlock as a label, like with regular labels.
+ SEHExceptBlock->setLabel(ES);
+
+ // Bail out if the CFG is bad.
+ if (badCFG)
+ return nullptr;
+
+ // We set Block to NULL to allow lazy creation of a new block (if necessary).
+ Block = nullptr;
+
+ return SEHExceptBlock;
+}
+
+CFGBlock *CFGBuilder::VisitSEHFinallyStmt(SEHFinallyStmt *FS) {
+ return VisitCompoundStmt(FS->getBlock());
+}
+
+CFGBlock *CFGBuilder::VisitSEHLeaveStmt(SEHLeaveStmt *LS) {
+ // "__leave" is a control-flow statement. Thus we stop processing the current
+ // block.
+ if (badCFG)
+ return nullptr;
+
+ // Now create a new block that ends with the __leave statement.
+ Block = createBlock(false);
+ Block->setTerminator(LS);
+
+ // If there is no target for the __leave, then we are looking at an incomplete
+ // AST. This means that the CFG cannot be constructed.
+ if (SEHLeaveJumpTarget.block) {
+ addAutomaticObjHandling(ScopePos, SEHLeaveJumpTarget.scopePosition, LS);
+ addSuccessor(Block, SEHLeaveJumpTarget.block);
+ } else
+ badCFG = true;
+
+ return Block;
+}
+
+CFGBlock *CFGBuilder::VisitSEHTryStmt(SEHTryStmt *Terminator) {
+ // "__try"/"__except"/"__finally" is a control-flow statement. Thus we stop
+ // processing the current block.
+ CFGBlock *SEHTrySuccessor = nullptr;
+
+ if (Block) {
+ if (badCFG)
+ return nullptr;
+ SEHTrySuccessor = Block;
+ } else SEHTrySuccessor = Succ;
+
+ // FIXME: Implement __finally support.
+ if (Terminator->getFinallyHandler())
+ return NYS();
+
+ CFGBlock *PrevSEHTryTerminatedBlock = TryTerminatedBlock;
+
+ // Create a new block that will contain the __try statement.
+ CFGBlock *NewTryTerminatedBlock = createBlock(false);
+
+ // Add the terminator in the __try block.
+ NewTryTerminatedBlock->setTerminator(Terminator);
+
+ if (SEHExceptStmt *Except = Terminator->getExceptHandler()) {
+ // The code after the try is the implicit successor if there's an __except.
+ Succ = SEHTrySuccessor;
+ Block = nullptr;
+ CFGBlock *ExceptBlock = VisitSEHExceptStmt(Except);
+ if (!ExceptBlock)
+ return nullptr;
+ // Add this block to the list of successors for the block with the try
+ // statement.
+ addSuccessor(NewTryTerminatedBlock, ExceptBlock);
+ }
+ if (PrevSEHTryTerminatedBlock)
+ addSuccessor(NewTryTerminatedBlock, PrevSEHTryTerminatedBlock);
+ else
+ addSuccessor(NewTryTerminatedBlock, &cfg->getExit());
+
+ // The code after the try is the implicit successor.
+ Succ = SEHTrySuccessor;
+
+ // Save the current "__try" context.
+ SaveAndRestore<CFGBlock *> save_try(TryTerminatedBlock,
+ NewTryTerminatedBlock);
+ cfg->addTryDispatchBlock(TryTerminatedBlock);
+
+ // Save the current value for the __leave target.
+ // All __leaves should go to the code following the __try
+ // (FIXME: or if the __try has a __finally, to the __finally.)
+ SaveAndRestore<JumpTarget> save_break(SEHLeaveJumpTarget);
+ SEHLeaveJumpTarget = JumpTarget(SEHTrySuccessor, ScopePos);
+
+ assert(Terminator->getTryBlock() && "__try must contain a non-NULL body");
+ Block = nullptr;
+ return addStmt(Terminator->getTryBlock());
+}
+
CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) {
// Get the block of the labeled statement. Add it to our map.
addStmt(L->getSubStmt());
@@ -2543,6 +2734,8 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
addAutomaticObjHandling(ScopePos, save_scope_pos.get(), F);
+ addLoopExit(F);
+
// "for" is a control-flow statement. Thus we stop processing the current
// block.
if (Block) {
@@ -2668,7 +2861,6 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
// false branch).
addSuccessor(ExitConditionBlock,
KnownVal.isTrue() ? nullptr : LoopSuccessor);
-
} while (false);
// Link up the loop-back block to the entry condition block.
@@ -2730,7 +2922,6 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
// the same with newVariable replaced with existingItem; the binding works
// the same except that for one ObjCForCollectionStmt::getElement() returns
// a DeclStmt and the other returns a DeclRefExpr.
- //
CFGBlock *LoopSuccessor = nullptr;
@@ -2882,6 +3073,7 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
addLocalScopeForVarDecl(VD);
addAutomaticObjHandling(ScopePos, LoopBeginScopePos, W);
}
+ addLoopExit(W);
// "while" is a control-flow statement. Thus we stop processing the current
// block.
@@ -2981,7 +3173,6 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
// false branch).
addSuccessor(ExitConditionBlock,
KnownVal.isTrue() ? nullptr : LoopSuccessor);
-
} while(false);
// Link up the loop-back block to the entry condition block.
@@ -2996,7 +3187,6 @@ CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) {
return EntryConditionBlock;
}
-
CFGBlock *CFGBuilder::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) {
// FIXME: For now we pretend that @catch and the code it contains does not
// exit.
@@ -3045,6 +3235,8 @@ CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) {
CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) {
CFGBlock *LoopSuccessor = nullptr;
+ addLoopExit(D);
+
// "do...while" is a control-flow statement. Thus we stop processing the
// current block.
if (Block) {
@@ -3167,7 +3359,6 @@ CFGBlock *CFGBuilder::VisitContinueStmt(ContinueStmt *C) {
CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
AddStmtChoice asc) {
-
if (asc.alwaysAdd(*this, E)) {
autoCreateBlock();
appendStmt(Block, E);
@@ -3688,7 +3879,6 @@ CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C,
CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE,
AddStmtChoice asc) {
-
autoCreateBlock();
appendStmt(Block, NE);
@@ -3991,8 +4181,6 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
return Block;
}
-} // end anonymous namespace
-
/// createBlock - Constructs and adds a new CFGBlock to the CFG. The block has
/// no successors or predecessors. If this is the first block created in the
/// CFG, it is automatically set to be the Entry and Exit of the CFG.
@@ -4025,6 +4213,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
case CFGElement::Statement:
case CFGElement::Initializer:
case CFGElement::NewAllocator:
+ case CFGElement::LoopExit:
case CFGElement::LifetimeEnds:
llvm_unreachable("getDestructorDecl should only be used with "
"ImplicitDtors");
@@ -4066,7 +4255,6 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
}
case CFGElement::BaseDtor:
case CFGElement::MemberDtor:
-
// Not yet supported.
return nullptr;
}
@@ -4084,14 +4272,14 @@ bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const {
//===----------------------------------------------------------------------===//
CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, bool IsReachable)
- : ReachableBlock(IsReachable ? B : nullptr),
- UnreachableBlock(!IsReachable ? B : nullptr,
- B && IsReachable ? AB_Normal : AB_Unreachable) {}
+ : ReachableBlock(IsReachable ? B : nullptr),
+ UnreachableBlock(!IsReachable ? B : nullptr,
+ B && IsReachable ? AB_Normal : AB_Unreachable) {}
CFGBlock::AdjacentBlock::AdjacentBlock(CFGBlock *B, CFGBlock *AlternateBlock)
- : ReachableBlock(B),
- UnreachableBlock(B == AlternateBlock ? nullptr : AlternateBlock,
- B == AlternateBlock ? AB_Alternate : AB_Normal) {}
+ : ReachableBlock(B),
+ UnreachableBlock(B == AlternateBlock ? nullptr : AlternateBlock,
+ B == AlternateBlock ? AB_Alternate : AB_Normal) {}
void CFGBlock::addSuccessor(AdjacentBlock Succ,
BumpVectorContext &C) {
@@ -4106,7 +4294,6 @@ void CFGBlock::addSuccessor(AdjacentBlock Succ,
bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F,
const CFGBlock *From, const CFGBlock *To) {
-
if (F.IgnoreNullPredecessors && !From)
return true;
@@ -4133,18 +4320,18 @@ bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F,
namespace {
class StmtPrinterHelper : public PrinterHelper {
- typedef llvm::DenseMap<const Stmt*,std::pair<unsigned,unsigned> > StmtMapTy;
- typedef llvm::DenseMap<const Decl*,std::pair<unsigned,unsigned> > DeclMapTy;
+ using StmtMapTy = llvm::DenseMap<const Stmt *, std::pair<unsigned, unsigned>>;
+ using DeclMapTy = llvm::DenseMap<const Decl *, std::pair<unsigned, unsigned>>;
+
StmtMapTy StmtMap;
DeclMapTy DeclMap;
- signed currentBlock;
- unsigned currStmt;
+ signed currentBlock = 0;
+ unsigned currStmt = 0;
const LangOptions &LangOpts;
-public:
+public:
StmtPrinterHelper(const CFG* cfg, const LangOptions &LO)
- : currentBlock(0), currStmt(0), LangOpts(LO)
- {
+ : LangOpts(LO) {
for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I ) {
unsigned j = 1;
for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ;
@@ -4199,7 +4386,7 @@ public:
}
}
- ~StmtPrinterHelper() override {}
+ ~StmtPrinterHelper() override = default;
const LangOptions &getLangOpts() const { return LangOpts; }
void setBlockID(signed i) { currentBlock = i; }
@@ -4235,20 +4422,17 @@ public:
return true;
}
};
-} // end anonymous namespace
-
-namespace {
class CFGBlockTerminatorPrint
- : public StmtVisitor<CFGBlockTerminatorPrint,void> {
-
+ : public StmtVisitor<CFGBlockTerminatorPrint,void> {
raw_ostream &OS;
StmtPrinterHelper* Helper;
PrintingPolicy Policy;
+
public:
CFGBlockTerminatorPrint(raw_ostream &os, StmtPrinterHelper* helper,
const PrintingPolicy &Policy)
- : OS(os), Helper(helper), Policy(Policy) {
+ : OS(os), Helper(helper), Policy(Policy) {
this->Policy.IncludeNewlines = false;
}
@@ -4302,6 +4486,10 @@ public:
OS << "try ...";
}
+ void VisitSEHTryStmt(SEHTryStmt *CS) {
+ OS << "__try ...";
+ }
+
void VisitAbstractConditionalOperator(AbstractConditionalOperator* C) {
if (Stmt *Cond = C->getCond())
Cond->printPretty(OS, Helper, Policy);
@@ -4353,7 +4541,8 @@ public:
Visit(T.getStmt());
}
};
-} // end anonymous namespace
+
+} // namespace
static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
const CFGElement &E) {
@@ -4403,7 +4592,6 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
// Expressions need a newline.
if (isa<Expr>(S))
OS << '\n';
-
} else if (Optional<CFGInitializer> IE = E.getAs<CFGInitializer>()) {
const CXXCtorInitializer *I = IE->getInitializer();
if (I->isBaseInitializer())
@@ -4422,7 +4610,6 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
else if (I->isDelegatingInitializer())
OS << " (Delegating initializer)\n";
else OS << " (Member initializer)\n";
-
} else if (Optional<CFGAutomaticObjDtor> DE =
E.getAs<CFGAutomaticObjDtor>()) {
const VarDecl *VD = DE->getVarDecl();
@@ -4435,13 +4622,14 @@ 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<CFGLoopExit> LE = E.getAs<CFGLoopExit>()) {
+ const Stmt *LoopStmt = LE->getLoopStmt();
+ OS << LoopStmt->getStmtClassName() << " (LoopExit)\n";
} else if (Optional<CFGNewAllocator> NE = E.getAs<CFGNewAllocator>()) {
OS << "CFGNewAllocator(";
if (const CXXNewExpr *AllocExpr = NE->getAllocatorExpr())
@@ -4460,14 +4648,12 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
const CXXBaseSpecifier *BS = BE->getBaseSpecifier();
OS << "~" << BS->getType()->getAsCXXRecordDecl()->getName() << "()";
OS << " (Base object destructor)\n";
-
} else if (Optional<CFGMemberDtor> ME = E.getAs<CFGMemberDtor>()) {
const FieldDecl *FD = ME->getFieldDecl();
const Type *T = FD->getType()->getBaseElementTypeUnsafe();
OS << "this->" << FD->getName();
OS << ".~" << T->getAsCXXRecordDecl()->getName() << "()";
OS << " (Member object destructor)\n";
-
} else if (Optional<CFGTemporaryDtor> TE = E.getAs<CFGTemporaryDtor>()) {
const CXXBindTemporaryExpr *BT = TE->getBindTemporaryExpr();
OS << "~";
@@ -4480,7 +4666,6 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
const CFGBlock &B,
StmtPrinterHelper &Helper, bool print_edges,
bool ShowColors) {
-
Helper.setBlockID(B.getBlockID());
// Print the header.
@@ -4505,7 +4690,6 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
// Print the label of this block.
if (Stmt *Label = const_cast<Stmt*>(B.getLabel())) {
-
if (print_edges)
OS << " ";
@@ -4531,7 +4715,11 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
else
OS << "...";
OS << ")";
-
+ } else if (SEHExceptStmt *ES = dyn_cast<SEHExceptStmt>(Label)) {
+ OS << "__except (";
+ ES->getFilterExpr()->printPretty(OS, &Helper,
+ PrintingPolicy(Helper.getLangOpts()), 0);
+ OS << ")";
} else
llvm_unreachable("Invalid label statement in CFGBlock.");
@@ -4543,7 +4731,6 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
for (CFGBlock::const_iterator I = B.begin(), E = B.end() ;
I != E ; ++I, ++j ) {
-
// Print the statement # in the basic block and the statement itself.
if (print_edges)
OS << " ";
@@ -4590,7 +4777,6 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
for (CFGBlock::const_pred_iterator I = B.pred_begin(), E = B.pred_end();
I != E; ++I, ++i) {
-
if (i % 10 == 8)
OS << "\n ";
@@ -4628,7 +4814,6 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
for (CFGBlock::const_succ_iterator I = B.succ_begin(), E = B.succ_end();
I != E; ++I, ++i) {
-
if (i % 10 == 8)
OS << "\n ";
@@ -4657,7 +4842,6 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
}
}
-
/// dump - A simple pretty printer of a CFG that outputs to stderr.
void CFG::dump(const LangOptions &LO, bool ShowColors) const {
print(llvm::errs(), LO, ShowColors);
@@ -4780,7 +4964,6 @@ Stmt *CFGBlock::getTerminatorCondition(bool StripParens) {
// CFG Graphviz Visualization
//===----------------------------------------------------------------------===//
-
#ifndef NDEBUG
static StmtPrinterHelper* GraphHelper;
#endif
@@ -4795,13 +4978,12 @@ void CFG::viewCFG(const LangOptions &LO) const {
}
namespace llvm {
+
template<>
struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits {
-
- DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {}
+ DOTGraphTraits(bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
static std::string getNodeLabel(const CFGBlock *Node, const CFG* Graph) {
-
#ifndef NDEBUG
std::string OutSStr;
llvm::raw_string_ostream Out(OutSStr);
@@ -4819,8 +5001,9 @@ struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits {
return OutStr;
#else
- return "";
+ return {};
#endif
}
};
-} // end namespace llvm
+
+} // namespace llvm