diff options
Diffstat (limited to 'lib/Analysis/CFG.cpp')
-rw-r--r-- | lib/Analysis/CFG.cpp | 449 |
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 |