diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Analysis')
22 files changed, 14354 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp b/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp new file mode 100644 index 000000000000..465f0c383486 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/AnalysisDeclContext.cpp @@ -0,0 +1,578 @@ +//== AnalysisDeclContext.cpp - Analysis context for Path Sens analysis -*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines AnalysisDeclContext, a class that manages the analysis context +// data for path sensitive analysis. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/AnalysisContext.h" +#include "BodyFarm.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; + +typedef llvm::DenseMap<const void *, ManagedAnalysis *> ManagedAnalysisMap; + +AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, + const Decl *d, + const CFG::BuildOptions &buildOptions) + : Manager(Mgr), + D(d), + cfgBuildOptions(buildOptions), + forcedBlkExprs(0), + builtCFG(false), + builtCompleteCFG(false), + ReferencedBlockVars(0), + ManagedAnalyses(0) +{ + cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; +} + +AnalysisDeclContext::AnalysisDeclContext(AnalysisDeclContextManager *Mgr, + const Decl *d) +: Manager(Mgr), + D(d), + forcedBlkExprs(0), + builtCFG(false), + builtCompleteCFG(false), + ReferencedBlockVars(0), + ManagedAnalyses(0) +{ + cfgBuildOptions.forcedBlkExprs = &forcedBlkExprs; +} + +AnalysisDeclContextManager::AnalysisDeclContextManager(bool useUnoptimizedCFG, + bool addImplicitDtors, + bool addInitializers, + bool addTemporaryDtors, + bool synthesizeBodies, + bool addStaticInitBranch) + : SynthesizeBodies(synthesizeBodies) +{ + cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG; + cfgBuildOptions.AddImplicitDtors = addImplicitDtors; + cfgBuildOptions.AddInitializers = addInitializers; + cfgBuildOptions.AddTemporaryDtors = addTemporaryDtors; + cfgBuildOptions.AddStaticInitBranches = addStaticInitBranch; +} + +void AnalysisDeclContextManager::clear() { + for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) + delete I->second; + Contexts.clear(); +} + +static BodyFarm &getBodyFarm(ASTContext &C) { + static BodyFarm *BF = new BodyFarm(C); + return *BF; +} + +Stmt *AnalysisDeclContext::getBody(bool &IsAutosynthesized) const { + IsAutosynthesized = false; + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + Stmt *Body = FD->getBody(); + if (!Body && Manager && Manager->synthesizeBodies()) { + IsAutosynthesized = true; + return getBodyFarm(getASTContext()).getBody(FD); + } + return Body; + } + else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->getBody(); + else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) + return BD->getBody(); + else if (const FunctionTemplateDecl *FunTmpl + = dyn_cast_or_null<FunctionTemplateDecl>(D)) + return FunTmpl->getTemplatedDecl()->getBody(); + + llvm_unreachable("unknown code decl"); +} + +Stmt *AnalysisDeclContext::getBody() const { + bool Tmp; + return getBody(Tmp); +} + +bool AnalysisDeclContext::isBodyAutosynthesized() const { + bool Tmp; + getBody(Tmp); + return Tmp; +} + +const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const { + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->getSelfDecl(); + if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) { + // See if 'self' was captured by the block. + for (BlockDecl::capture_const_iterator it = BD->capture_begin(), + et = BD->capture_end(); it != et; ++it) { + const VarDecl *VD = it->getVariable(); + if (VD->getName() == "self") + return dyn_cast<ImplicitParamDecl>(VD); + } + } + + return NULL; +} + +void AnalysisDeclContext::registerForcedBlockExpression(const Stmt *stmt) { + if (!forcedBlkExprs) + forcedBlkExprs = new CFG::BuildOptions::ForcedBlkExprs(); + // Default construct an entry for 'stmt'. + if (const Expr *e = dyn_cast<Expr>(stmt)) + stmt = e->IgnoreParens(); + (void) (*forcedBlkExprs)[stmt]; +} + +const CFGBlock * +AnalysisDeclContext::getBlockForRegisteredExpression(const Stmt *stmt) { + assert(forcedBlkExprs); + if (const Expr *e = dyn_cast<Expr>(stmt)) + stmt = e->IgnoreParens(); + CFG::BuildOptions::ForcedBlkExprs::const_iterator itr = + forcedBlkExprs->find(stmt); + assert(itr != forcedBlkExprs->end()); + return itr->second; +} + +/// Add each synthetic statement in the CFG to the parent map, using the +/// source statement's parent. +static void addParentsForSyntheticStmts(const CFG *TheCFG, ParentMap &PM) { + if (!TheCFG) + return; + + for (CFG::synthetic_stmt_iterator I = TheCFG->synthetic_stmt_begin(), + E = TheCFG->synthetic_stmt_end(); + I != E; ++I) { + PM.setParent(I->first, PM.getParent(I->second)); + } +} + +CFG *AnalysisDeclContext::getCFG() { + if (!cfgBuildOptions.PruneTriviallyFalseEdges) + return getUnoptimizedCFG(); + + if (!builtCFG) { + cfg.reset(CFG::buildCFG(D, getBody(), + &D->getASTContext(), cfgBuildOptions)); + // Even when the cfg is not successfully built, we don't + // want to try building it again. + builtCFG = true; + + if (PM) + addParentsForSyntheticStmts(cfg.get(), *PM); + } + return cfg.get(); +} + +CFG *AnalysisDeclContext::getUnoptimizedCFG() { + if (!builtCompleteCFG) { + SaveAndRestore<bool> NotPrune(cfgBuildOptions.PruneTriviallyFalseEdges, + false); + completeCFG.reset(CFG::buildCFG(D, getBody(), &D->getASTContext(), + cfgBuildOptions)); + // Even when the cfg is not successfully built, we don't + // want to try building it again. + builtCompleteCFG = true; + + if (PM) + addParentsForSyntheticStmts(completeCFG.get(), *PM); + } + return completeCFG.get(); +} + +CFGStmtMap *AnalysisDeclContext::getCFGStmtMap() { + if (cfgStmtMap) + return cfgStmtMap.get(); + + if (CFG *c = getCFG()) { + cfgStmtMap.reset(CFGStmtMap::Build(c, &getParentMap())); + return cfgStmtMap.get(); + } + + return 0; +} + +CFGReverseBlockReachabilityAnalysis *AnalysisDeclContext::getCFGReachablityAnalysis() { + if (CFA) + return CFA.get(); + + if (CFG *c = getCFG()) { + CFA.reset(new CFGReverseBlockReachabilityAnalysis(*c)); + return CFA.get(); + } + + return 0; +} + +void AnalysisDeclContext::dumpCFG(bool ShowColors) { + getCFG()->dump(getASTContext().getLangOpts(), ShowColors); +} + +ParentMap &AnalysisDeclContext::getParentMap() { + if (!PM) { + PM.reset(new ParentMap(getBody())); + if (const CXXConstructorDecl *C = dyn_cast<CXXConstructorDecl>(getDecl())) { + for (CXXConstructorDecl::init_const_iterator I = C->init_begin(), + E = C->init_end(); + I != E; ++I) { + PM->addStmt((*I)->getInit()); + } + } + if (builtCFG) + addParentsForSyntheticStmts(getCFG(), *PM); + if (builtCompleteCFG) + addParentsForSyntheticStmts(getUnoptimizedCFG(), *PM); + } + return *PM; +} + +PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() { + if (!PCA) + PCA.reset(new PseudoConstantAnalysis(getBody())); + return PCA.get(); +} + +AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + // Calling 'hasBody' replaces 'FD' in place with the FunctionDecl + // that has the body. + FD->hasBody(FD); + D = FD; + } + + AnalysisDeclContext *&AC = Contexts[D]; + if (!AC) + AC = new AnalysisDeclContext(this, D, cfgBuildOptions); + return AC; +} + +const StackFrameContext * +AnalysisDeclContext::getStackFrame(LocationContext const *Parent, const Stmt *S, + const CFGBlock *Blk, unsigned Idx) { + return getLocationContextManager().getStackFrame(this, Parent, S, Blk, Idx); +} + +const BlockInvocationContext * +AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent, + const clang::BlockDecl *BD, + const void *ContextData) { + return getLocationContextManager().getBlockInvocationContext(this, parent, + BD, ContextData); +} + +LocationContextManager & AnalysisDeclContext::getLocationContextManager() { + assert(Manager && + "Cannot create LocationContexts without an AnalysisDeclContextManager!"); + return Manager->getLocationContextManager(); +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling. +//===----------------------------------------------------------------------===// + +void LocationContext::ProfileCommon(llvm::FoldingSetNodeID &ID, + ContextKind ck, + AnalysisDeclContext *ctx, + const LocationContext *parent, + const void *data) { + ID.AddInteger(ck); + ID.AddPointer(ctx); + ID.AddPointer(parent); + ID.AddPointer(data); +} + +void StackFrameContext::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getAnalysisDeclContext(), getParent(), CallSite, Block, Index); +} + +void ScopeContext::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getAnalysisDeclContext(), getParent(), Enter); +} + +void BlockInvocationContext::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getAnalysisDeclContext(), getParent(), BD, ContextData); +} + +//===----------------------------------------------------------------------===// +// LocationContext creation. +//===----------------------------------------------------------------------===// + +template <typename LOC, typename DATA> +const LOC* +LocationContextManager::getLocationContext(AnalysisDeclContext *ctx, + const LocationContext *parent, + const DATA *d) { + llvm::FoldingSetNodeID ID; + LOC::Profile(ID, ctx, parent, d); + void *InsertPos; + + LOC *L = cast_or_null<LOC>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); + + if (!L) { + L = new LOC(ctx, parent, d); + Contexts.InsertNode(L, InsertPos); + } + return L; +} + +const StackFrameContext* +LocationContextManager::getStackFrame(AnalysisDeclContext *ctx, + const LocationContext *parent, + const Stmt *s, + const CFGBlock *blk, unsigned idx) { + llvm::FoldingSetNodeID ID; + StackFrameContext::Profile(ID, ctx, parent, s, blk, idx); + void *InsertPos; + StackFrameContext *L = + cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos)); + if (!L) { + L = new StackFrameContext(ctx, parent, s, blk, idx); + Contexts.InsertNode(L, InsertPos); + } + return L; +} + +const ScopeContext * +LocationContextManager::getScope(AnalysisDeclContext *ctx, + const LocationContext *parent, + const Stmt *s) { + return getLocationContext<ScopeContext, Stmt>(ctx, parent, s); +} + +const BlockInvocationContext * +LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx, + const LocationContext *parent, + const BlockDecl *BD, + const void *ContextData) { + llvm::FoldingSetNodeID ID; + BlockInvocationContext::Profile(ID, ctx, parent, BD, ContextData); + void *InsertPos; + BlockInvocationContext *L = + cast_or_null<BlockInvocationContext>(Contexts.FindNodeOrInsertPos(ID, + InsertPos)); + if (!L) { + L = new BlockInvocationContext(ctx, parent, BD, ContextData); + Contexts.InsertNode(L, InsertPos); + } + return L; +} + +//===----------------------------------------------------------------------===// +// LocationContext methods. +//===----------------------------------------------------------------------===// + +const StackFrameContext *LocationContext::getCurrentStackFrame() const { + const LocationContext *LC = this; + while (LC) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) + return SFC; + LC = LC->getParent(); + } + return NULL; +} + +bool LocationContext::inTopFrame() const { + return getCurrentStackFrame()->inTopFrame(); +} + +bool LocationContext::isParentOf(const LocationContext *LC) const { + do { + const LocationContext *Parent = LC->getParent(); + if (Parent == this) + return true; + else + LC = Parent; + } while (LC); + + return false; +} + +void LocationContext::dumpStack(raw_ostream &OS, StringRef Indent) const { + ASTContext &Ctx = getAnalysisDeclContext()->getASTContext(); + PrintingPolicy PP(Ctx.getLangOpts()); + PP.TerseOutput = 1; + + unsigned Frame = 0; + for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) { + switch (LCtx->getKind()) { + case StackFrame: + OS << Indent << '#' << Frame++ << ' '; + cast<StackFrameContext>(LCtx)->getDecl()->print(OS, PP); + OS << '\n'; + break; + case Scope: + OS << Indent << " (scope)\n"; + break; + case Block: + OS << Indent << " (block context: " + << cast<BlockInvocationContext>(LCtx)->getContextData() + << ")\n"; + break; + } + } +} + +void LocationContext::dumpStack() const { + dumpStack(llvm::errs()); +} + +//===----------------------------------------------------------------------===// +// Lazily generated map to query the external variables referenced by a Block. +//===----------------------------------------------------------------------===// + +namespace { +class FindBlockDeclRefExprsVals : public StmtVisitor<FindBlockDeclRefExprsVals>{ + BumpVector<const VarDecl*> &BEVals; + BumpVectorContext &BC; + llvm::SmallPtrSet<const VarDecl*, 4> Visited; + llvm::SmallPtrSet<const DeclContext*, 4> IgnoredContexts; +public: + FindBlockDeclRefExprsVals(BumpVector<const VarDecl*> &bevals, + BumpVectorContext &bc) + : BEVals(bevals), BC(bc) {} + + bool IsTrackedDecl(const VarDecl *VD) { + const DeclContext *DC = VD->getDeclContext(); + return IgnoredContexts.count(DC) == 0; + } + + void VisitStmt(Stmt *S) { + for (Stmt::child_range I = S->children(); I; ++I) + if (Stmt *child = *I) + Visit(child); + } + + void VisitDeclRefExpr(DeclRefExpr *DR) { + // Non-local variables are also directly modified. + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (!VD->hasLocalStorage()) { + if (Visited.insert(VD)) + BEVals.push_back(VD, BC); + } + } + } + + void VisitBlockExpr(BlockExpr *BR) { + // Blocks containing blocks can transitively capture more variables. + IgnoredContexts.insert(BR->getBlockDecl()); + Visit(BR->getBlockDecl()->getBody()); + } + + void VisitPseudoObjectExpr(PseudoObjectExpr *PE) { + for (PseudoObjectExpr::semantics_iterator it = PE->semantics_begin(), + et = PE->semantics_end(); it != et; ++it) { + Expr *Semantic = *it; + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Semantic)) + Semantic = OVE->getSourceExpr(); + Visit(Semantic); + } + } +}; +} // end anonymous namespace + +typedef BumpVector<const VarDecl*> DeclVec; + +static DeclVec* LazyInitializeReferencedDecls(const BlockDecl *BD, + void *&Vec, + llvm::BumpPtrAllocator &A) { + if (Vec) + return (DeclVec*) Vec; + + BumpVectorContext BC(A); + DeclVec *BV = (DeclVec*) A.Allocate<DeclVec>(); + new (BV) DeclVec(BC, 10); + + // Go through the capture list. + for (BlockDecl::capture_const_iterator CI = BD->capture_begin(), + CE = BD->capture_end(); CI != CE; ++CI) { + BV->push_back(CI->getVariable(), BC); + } + + // Find the referenced global/static variables. + FindBlockDeclRefExprsVals F(*BV, BC); + F.Visit(BD->getBody()); + + Vec = BV; + return BV; +} + +std::pair<AnalysisDeclContext::referenced_decls_iterator, + AnalysisDeclContext::referenced_decls_iterator> +AnalysisDeclContext::getReferencedBlockVars(const BlockDecl *BD) { + if (!ReferencedBlockVars) + ReferencedBlockVars = new llvm::DenseMap<const BlockDecl*,void*>(); + + DeclVec *V = LazyInitializeReferencedDecls(BD, (*ReferencedBlockVars)[BD], A); + return std::make_pair(V->begin(), V->end()); +} + +ManagedAnalysis *&AnalysisDeclContext::getAnalysisImpl(const void *tag) { + if (!ManagedAnalyses) + ManagedAnalyses = new ManagedAnalysisMap(); + ManagedAnalysisMap *M = (ManagedAnalysisMap*) ManagedAnalyses; + return (*M)[tag]; +} + +//===----------------------------------------------------------------------===// +// Cleanup. +//===----------------------------------------------------------------------===// + +ManagedAnalysis::~ManagedAnalysis() {} + +AnalysisDeclContext::~AnalysisDeclContext() { + delete forcedBlkExprs; + delete ReferencedBlockVars; + // Release the managed analyses. + if (ManagedAnalyses) { + ManagedAnalysisMap *M = (ManagedAnalysisMap*) ManagedAnalyses; + for (ManagedAnalysisMap::iterator I = M->begin(), E = M->end(); I!=E; ++I) + delete I->second; + delete M; + } +} + +AnalysisDeclContextManager::~AnalysisDeclContextManager() { + for (ContextMap::iterator I = Contexts.begin(), E = Contexts.end(); I!=E; ++I) + delete I->second; +} + +LocationContext::~LocationContext() {} + +LocationContextManager::~LocationContextManager() { + clear(); +} + +void LocationContextManager::clear() { + for (llvm::FoldingSet<LocationContext>::iterator I = Contexts.begin(), + E = Contexts.end(); I != E; ) { + LocationContext *LC = &*I; + ++I; + delete LC; + } + + Contexts.clear(); +} + diff --git a/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp new file mode 100644 index 000000000000..4d5c2ee236f9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.cpp @@ -0,0 +1,376 @@ +//== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// BodyFarm is a factory for creating faux implementations for functions/methods +// for analysis purposes. +// +//===----------------------------------------------------------------------===// + +#include "BodyFarm.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Helper creation functions for constructing faux ASTs. +//===----------------------------------------------------------------------===// + +static bool isDispatchBlock(QualType Ty) { + // Is it a block pointer? + const BlockPointerType *BPT = Ty->getAs<BlockPointerType>(); + if (!BPT) + return false; + + // Check if the block pointer type takes no arguments and + // returns void. + const FunctionProtoType *FT = + BPT->getPointeeType()->getAs<FunctionProtoType>(); + if (!FT || !FT->getResultType()->isVoidType() || + FT->getNumArgs() != 0) + return false; + + return true; +} + +namespace { +class ASTMaker { +public: + ASTMaker(ASTContext &C) : C(C) {} + + /// Create a new BinaryOperator representing a simple assignment. + BinaryOperator *makeAssignment(const Expr *LHS, const Expr *RHS, QualType Ty); + + /// Create a new BinaryOperator representing a comparison. + BinaryOperator *makeComparison(const Expr *LHS, const Expr *RHS, + BinaryOperator::Opcode Op); + + /// Create a new compound stmt using the provided statements. + CompoundStmt *makeCompound(ArrayRef<Stmt*>); + + /// Create a new DeclRefExpr for the referenced variable. + DeclRefExpr *makeDeclRefExpr(const VarDecl *D); + + /// Create a new UnaryOperator representing a dereference. + UnaryOperator *makeDereference(const Expr *Arg, QualType Ty); + + /// Create an implicit cast for an integer conversion. + Expr *makeIntegralCast(const Expr *Arg, QualType Ty); + + /// Create an implicit cast to a builtin boolean type. + ImplicitCastExpr *makeIntegralCastToBoolean(const Expr *Arg); + + // Create an implicit cast for lvalue-to-rvaluate conversions. + ImplicitCastExpr *makeLvalueToRvalue(const Expr *Arg, QualType Ty); + + /// Create an Objective-C bool literal. + ObjCBoolLiteralExpr *makeObjCBool(bool Val); + + /// Create a Return statement. + ReturnStmt *makeReturn(const Expr *RetVal); + +private: + ASTContext &C; +}; +} + +BinaryOperator *ASTMaker::makeAssignment(const Expr *LHS, const Expr *RHS, + QualType Ty) { + return new (C) BinaryOperator(const_cast<Expr*>(LHS), const_cast<Expr*>(RHS), + BO_Assign, Ty, VK_RValue, + OK_Ordinary, SourceLocation(), false); +} + +BinaryOperator *ASTMaker::makeComparison(const Expr *LHS, const Expr *RHS, + BinaryOperator::Opcode Op) { + assert(BinaryOperator::isLogicalOp(Op) || + BinaryOperator::isComparisonOp(Op)); + return new (C) BinaryOperator(const_cast<Expr*>(LHS), + const_cast<Expr*>(RHS), + Op, + C.getLogicalOperationType(), + VK_RValue, + OK_Ordinary, SourceLocation(), false); +} + +CompoundStmt *ASTMaker::makeCompound(ArrayRef<Stmt *> Stmts) { + return new (C) CompoundStmt(C, Stmts, SourceLocation(), SourceLocation()); +} + +DeclRefExpr *ASTMaker::makeDeclRefExpr(const VarDecl *D) { + DeclRefExpr *DR = + DeclRefExpr::Create(/* Ctx = */ C, + /* QualifierLoc = */ NestedNameSpecifierLoc(), + /* TemplateKWLoc = */ SourceLocation(), + /* D = */ const_cast<VarDecl*>(D), + /* isEnclosingLocal = */ false, + /* NameLoc = */ SourceLocation(), + /* T = */ D->getType(), + /* VK = */ VK_LValue); + return DR; +} + +UnaryOperator *ASTMaker::makeDereference(const Expr *Arg, QualType Ty) { + return new (C) UnaryOperator(const_cast<Expr*>(Arg), UO_Deref, Ty, + VK_LValue, OK_Ordinary, SourceLocation()); +} + +ImplicitCastExpr *ASTMaker::makeLvalueToRvalue(const Expr *Arg, QualType Ty) { + return ImplicitCastExpr::Create(C, Ty, CK_LValueToRValue, + const_cast<Expr*>(Arg), 0, VK_RValue); +} + +Expr *ASTMaker::makeIntegralCast(const Expr *Arg, QualType Ty) { + if (Arg->getType() == Ty) + return const_cast<Expr*>(Arg); + + return ImplicitCastExpr::Create(C, Ty, CK_IntegralCast, + const_cast<Expr*>(Arg), 0, VK_RValue); +} + +ImplicitCastExpr *ASTMaker::makeIntegralCastToBoolean(const Expr *Arg) { + return ImplicitCastExpr::Create(C, C.BoolTy, CK_IntegralToBoolean, + const_cast<Expr*>(Arg), 0, VK_RValue); +} + +ObjCBoolLiteralExpr *ASTMaker::makeObjCBool(bool Val) { + QualType Ty = C.getBOOLDecl() ? C.getBOOLType() : C.ObjCBuiltinBoolTy; + return new (C) ObjCBoolLiteralExpr(Val, Ty, SourceLocation()); +} + +ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) { + return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal), 0); +} + +//===----------------------------------------------------------------------===// +// Creation functions for faux ASTs. +//===----------------------------------------------------------------------===// + +typedef Stmt *(*FunctionFarmer)(ASTContext &C, const FunctionDecl *D); + +/// Create a fake body for dispatch_once. +static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) { + // Check if we have at least two parameters. + if (D->param_size() != 2) + return 0; + + // Check if the first parameter is a pointer to integer type. + const ParmVarDecl *Predicate = D->getParamDecl(0); + QualType PredicateQPtrTy = Predicate->getType(); + const PointerType *PredicatePtrTy = PredicateQPtrTy->getAs<PointerType>(); + if (!PredicatePtrTy) + return 0; + QualType PredicateTy = PredicatePtrTy->getPointeeType(); + if (!PredicateTy->isIntegerType()) + return 0; + + // Check if the second parameter is the proper block type. + const ParmVarDecl *Block = D->getParamDecl(1); + QualType Ty = Block->getType(); + if (!isDispatchBlock(Ty)) + return 0; + + // Everything checks out. Create a fakse body that checks the predicate, + // sets it, and calls the block. Basically, an AST dump of: + // + // void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block) { + // if (!*predicate) { + // *predicate = 1; + // block(); + // } + // } + + ASTMaker M(C); + + // (1) Create the call. + DeclRefExpr *DR = M.makeDeclRefExpr(Block); + ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); + CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue, + SourceLocation()); + + // (2) Create the assignment to the predicate. + IntegerLiteral *IL = + IntegerLiteral::Create(C, llvm::APInt(C.getTypeSize(C.IntTy), (uint64_t) 1), + C.IntTy, SourceLocation()); + BinaryOperator *B = + M.makeAssignment( + M.makeDereference( + M.makeLvalueToRvalue( + M.makeDeclRefExpr(Predicate), PredicateQPtrTy), + PredicateTy), + M.makeIntegralCast(IL, PredicateTy), + PredicateTy); + + // (3) Create the compound statement. + Stmt *Stmts[2]; + Stmts[0] = B; + Stmts[1] = CE; + CompoundStmt *CS = M.makeCompound(ArrayRef<Stmt*>(Stmts, 2)); + + // (4) Create the 'if' condition. + ImplicitCastExpr *LValToRval = + M.makeLvalueToRvalue( + M.makeDereference( + M.makeLvalueToRvalue( + M.makeDeclRefExpr(Predicate), + PredicateQPtrTy), + PredicateTy), + PredicateTy); + + UnaryOperator *UO = new (C) UnaryOperator(LValToRval, UO_LNot, C.IntTy, + VK_RValue, OK_Ordinary, + SourceLocation()); + + // (5) Create the 'if' statement. + IfStmt *If = new (C) IfStmt(C, SourceLocation(), 0, UO, CS); + return If; +} + +/// Create a fake body for dispatch_sync. +static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) { + // Check if we have at least two parameters. + if (D->param_size() != 2) + return 0; + + // Check if the second parameter is a block. + const ParmVarDecl *PV = D->getParamDecl(1); + QualType Ty = PV->getType(); + if (!isDispatchBlock(Ty)) + return 0; + + // Everything checks out. Create a fake body that just calls the block. + // This is basically just an AST dump of: + // + // void dispatch_sync(dispatch_queue_t queue, void (^block)(void)) { + // block(); + // } + // + ASTMaker M(C); + DeclRefExpr *DR = M.makeDeclRefExpr(PV); + ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty); + CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue, + SourceLocation()); + return CE; +} + +static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D) +{ + // There are exactly 3 arguments. + if (D->param_size() != 3) + return 0; + + // Signature: + // _Bool OSAtomicCompareAndSwapPtr(void *__oldValue, + // void *__newValue, + // void * volatile *__theValue) + // Generate body: + // if (oldValue == *theValue) { + // *theValue = newValue; + // return YES; + // } + // else return NO; + + QualType ResultTy = D->getResultType(); + bool isBoolean = ResultTy->isBooleanType(); + if (!isBoolean && !ResultTy->isIntegralType(C)) + return 0; + + const ParmVarDecl *OldValue = D->getParamDecl(0); + QualType OldValueTy = OldValue->getType(); + + const ParmVarDecl *NewValue = D->getParamDecl(1); + QualType NewValueTy = NewValue->getType(); + + assert(OldValueTy == NewValueTy); + + const ParmVarDecl *TheValue = D->getParamDecl(2); + QualType TheValueTy = TheValue->getType(); + const PointerType *PT = TheValueTy->getAs<PointerType>(); + if (!PT) + return 0; + QualType PointeeTy = PT->getPointeeType(); + + ASTMaker M(C); + // Construct the comparison. + Expr *Comparison = + M.makeComparison( + M.makeLvalueToRvalue(M.makeDeclRefExpr(OldValue), OldValueTy), + M.makeLvalueToRvalue( + M.makeDereference( + M.makeLvalueToRvalue(M.makeDeclRefExpr(TheValue), TheValueTy), + PointeeTy), + PointeeTy), + BO_EQ); + + // Construct the body of the IfStmt. + Stmt *Stmts[2]; + Stmts[0] = + M.makeAssignment( + M.makeDereference( + M.makeLvalueToRvalue(M.makeDeclRefExpr(TheValue), TheValueTy), + PointeeTy), + M.makeLvalueToRvalue(M.makeDeclRefExpr(NewValue), NewValueTy), + NewValueTy); + + Expr *BoolVal = M.makeObjCBool(true); + Expr *RetVal = isBoolean ? M.makeIntegralCastToBoolean(BoolVal) + : M.makeIntegralCast(BoolVal, ResultTy); + Stmts[1] = M.makeReturn(RetVal); + CompoundStmt *Body = M.makeCompound(ArrayRef<Stmt*>(Stmts, 2)); + + // Construct the else clause. + BoolVal = M.makeObjCBool(false); + RetVal = isBoolean ? M.makeIntegralCastToBoolean(BoolVal) + : M.makeIntegralCast(BoolVal, ResultTy); + Stmt *Else = M.makeReturn(RetVal); + + /// Construct the If. + Stmt *If = + new (C) IfStmt(C, SourceLocation(), 0, Comparison, Body, + SourceLocation(), Else); + + return If; +} + +Stmt *BodyFarm::getBody(const FunctionDecl *D) { + D = D->getCanonicalDecl(); + + Optional<Stmt *> &Val = Bodies[D]; + if (Val.hasValue()) + return Val.getValue(); + + Val = 0; + + if (D->getIdentifier() == 0) + return 0; + + StringRef Name = D->getName(); + if (Name.empty()) + return 0; + + FunctionFarmer FF; + + if (Name.startswith("OSAtomicCompareAndSwap") || + Name.startswith("objc_atomicCompareAndSwap")) { + FF = create_OSAtomicCompareAndSwap; + } + else { + FF = llvm::StringSwitch<FunctionFarmer>(Name) + .Case("dispatch_sync", create_dispatch_sync) + .Case("dispatch_once", create_dispatch_once) + .Default(NULL); + } + + if (FF) { Val = FF(C, D); } + return Val.getValue(); +} + diff --git a/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h new file mode 100644 index 000000000000..96f61df40d7f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/BodyFarm.h @@ -0,0 +1,44 @@ +//== BodyFarm.h - Factory for conjuring up fake bodies -------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// BodyFarm is a factory for creating faux implementations for functions/methods +// for analysis purposes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_BODYFARM_H +#define LLVM_CLANG_ANALYSIS_BODYFARM_H + +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" + +namespace clang { + +class ASTContext; +class Decl; +class FunctionDecl; +class Stmt; + +class BodyFarm { +public: + BodyFarm(ASTContext &C) : C(C) {} + + /// Factory method for creating bodies for ordinary functions. + Stmt *getBody(const FunctionDecl *D); + +private: + typedef llvm::DenseMap<const Decl *, Optional<Stmt *> > BodyMap; + + ASTContext &C; + BodyMap Bodies; +}; +} + +#endif diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp new file mode 100644 index 000000000000..8b8c573feade --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/CFG.cpp @@ -0,0 +1,4135 @@ + //===--- CFG.cpp - Classes for representing and building CFGs----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the CFG and CFGBuilder classes for representing and +// building Control-Flow Graphs (CFGs) from ASTs. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/CFG.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/Builtins.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/GraphWriter.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; + +namespace { + +static SourceLocation GetEndLoc(Decl *D) { + if (VarDecl *VD = dyn_cast<VarDecl>(D)) + if (Expr *Ex = VD->getInit()) + return Ex->getSourceRange().getEnd(); + return D->getLocation(); +} + +class CFGBuilder; + +/// The CFG builder uses a recursive algorithm to build the CFG. When +/// we process an expression, sometimes we know that we must add the +/// subexpressions as block-level expressions. For example: +/// +/// exp1 || exp2 +/// +/// When processing the '||' expression, we know that exp1 and exp2 +/// need to be added as block-level expressions, even though they +/// might not normally need to be. AddStmtChoice records this +/// 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 }; + + AddStmtChoice(Kind a_kind = NotAlwaysAdd) : kind(a_kind) {} + + bool alwaysAdd(CFGBuilder &builder, + const Stmt *stmt) const; + + /// Return a copy of this object, except with the 'always-add' bit + /// set as specified. + AddStmtChoice withAlwaysAdd(bool alwaysAdd) const { + return AddStmtChoice(alwaysAdd ? AlwaysAdd : NotAlwaysAdd); + } + +private: + Kind kind; +}; + +/// LocalScope - Node in tree of local scopes created for C++ implicit +/// destructor calls generation. It contains list of automatic variables +/// declared in the scope and link to position in previous scope this scope +/// began in. +/// +/// The process of creating local scopes is as follows: +/// - Init CFGBuilder::ScopePos with invalid position (equivalent for null), +/// - Before processing statements in scope (e.g. CompoundStmt) create +/// LocalScope object using CFGBuilder::ScopePos as link to previous scope +/// and set CFGBuilder::ScopePos to the end of new scope, +/// - On every occurrence of VarDecl increase CFGBuilder::ScopePos if it points +/// at this VarDecl, +/// - For every normal (without jump) end of scope add to CFGBlock destructors +/// for objects in the current scope, +/// - For every jump add to CFGBlock destructors for objects +/// between CFGBuilder::ScopePos and local scope position saved for jump +/// target. Thanks to C++ restrictions on goto jumps we can be sure that +/// jump target position will be on the path to root from CFGBuilder::ScopePos +/// (adding any variable that doesn't need constructor to be called to +/// LocalScope can break this assumption), +/// +class LocalScope { +public: + typedef BumpVector<VarDecl*> AutomaticVarsTy; + + /// 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; + + /// VarIter is guaranteed to be greater then 0 for every valid iterator. + /// Invalid iterator (with null Scope) has VarIter equal to 0. + unsigned VarIter; + + public: + /// Create invalid iterator. Dereferencing invalid iterator is not allowed. + /// Incrementing invalid iterator is allowed and will result in invalid + /// iterator. + const_iterator() + : Scope(NULL), VarIter(0) {} + + /// Create valid iterator. In case when S.Prev is an invalid iterator and + /// I is equal to 0, this will create invalid iterator. + const_iterator(const LocalScope& S, unsigned I) + : Scope(&S), VarIter(I) { + // Iterator to "end" of scope is not allowed. Handle it by going up + // in scopes tree possibly up to invalid iterator in the root. + if (VarIter == 0 && Scope) + *this = Scope->Prev; + } + + VarDecl *const* operator->() const { + 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 { + return *this->operator->(); + } + + const_iterator &operator++() { + if (!Scope) + return *this; + + assert (VarIter != 0 && "Iterator has invalid value of VarIter member"); + --VarIter; + if (VarIter == 0) + *this = Scope->Prev; + return *this; + } + const_iterator operator++(int) { + const_iterator P = *this; + ++*this; + return P; + } + + bool operator==(const const_iterator &rhs) const { + return Scope == rhs.Scope && VarIter == rhs.VarIter; + } + bool operator!=(const const_iterator &rhs) const { + return !(*this == rhs); + } + + LLVM_EXPLICIT operator bool() const { + return *this != const_iterator(); + } + + int distance(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; + +public: + /// Constructs empty scope linked to previous scope in specified place. + LocalScope(BumpVectorContext &ctx, const_iterator P) + : ctx(ctx), Vars(ctx, 4), Prev(P) {} + + /// Begin of scope in direction of CFG building (backwards). + const_iterator begin() const { return const_iterator(*this, Vars.size()); } + + void addVar(VarDecl *VD) { + Vars.push_back(VD, ctx); + } +}; + +/// 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. +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."); + D += F.VarIter; + F = F.Scope->Prev; + } + D += F.VarIter - L.VarIter; + return D; +} + +/// BlockScopePosPair - Structure for specifying position in CFG during its +/// build process. It consists of CFGBlock that specifies position in CFG graph +/// and LocalScope::const_iterator that specifies position in LocalScope graph. +struct BlockScopePosPair { + BlockScopePosPair() : block(0) {} + 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 +/// 'true', 'false', or 'unknown'. This is returned by tryEvaluateBool, +/// and is used by the CFGBuilder to decide if a branch condition +/// can be decided up front during CFG construction. +class TryResult { + int X; +public: + 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; + } +}; + +class reverse_children { + llvm::SmallVector<Stmt *, 12> childrenBuf; + ArrayRef<Stmt*> children; +public: + reverse_children(Stmt *S); + + typedef ArrayRef<Stmt*>::reverse_iterator iterator; + iterator begin() const { return children.rbegin(); } + iterator end() const { return children.rend(); } +}; + + +reverse_children::reverse_children(Stmt *S) { + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + children = CE->getRawSubExprs(); + return; + } + switch (S->getStmtClass()) { + // Note: Fill in this switch with more cases we want to optimize. + case Stmt::InitListExprClass: { + InitListExpr *IE = cast<InitListExpr>(S); + children = llvm::makeArrayRef(reinterpret_cast<Stmt**>(IE->getInits()), + IE->getNumInits()); + return; + } + default: + break; + } + + // Default case for all other statements. + for (Stmt::child_range I = S->children(); I; ++I) { + childrenBuf.push_back(*I); + } + + // This needs to be done *after* childrenBuf has been populated. + children = childrenBuf; +} + +/// 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. +/// +/// Example usage: +/// +/// CFGBuilder builder; +/// CFG* cfg = builder.BuildAST(stmt1); +/// +/// CFG construction is done via a recursive walk of an AST. We actually parse +/// 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; + + ASTContext *Context; + OwningPtr<CFG> cfg; + + CFGBlock *Block; + CFGBlock *Succ; + JumpTarget ContinueJumpTarget; + JumpTarget BreakJumpTarget; + CFGBlock *SwitchTerminatedBlock; + CFGBlock *DefaultCaseBlock; + CFGBlock *TryTerminatedBlock; + + // 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; + 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; + BackpatchBlocksTy BackpatchBlocks; + + // A list of labels whose address has been taken (for indirect gotos). + typedef llvm::SmallPtrSet<LabelDecl*, 5> LabelSetTy; + LabelSetTy AddressTakenLabels; + + bool badCFG; + const CFG::BuildOptions &BuildOpts; + + // State to track for building switch statements. + bool switchExclusivelyCovered; + Expr::EvalResult *switchCond; + + CFG::BuildOptions::ForcedBlkExprs::value_type *cachedEntry; + const Stmt *lastLookup; + + // Caches boolean evaluations of expressions to avoid multiple re-evaluations + // during construction of branches for chained logical operators. + typedef llvm::DenseMap<Expr *, TryResult> CachedBoolEvalsTy; + CachedBoolEvalsTy CachedBoolEvals; + +public: + explicit CFGBuilder(ASTContext *astContext, + const CFG::BuildOptions &buildOpts) + : Context(astContext), cfg(new CFG()), // crew a new CFG + Block(NULL), Succ(NULL), + SwitchTerminatedBlock(NULL), DefaultCaseBlock(NULL), + TryTerminatedBlock(NULL), badCFG(false), BuildOpts(buildOpts), + switchExclusivelyCovered(false), switchCond(0), + cachedEntry(0), lastLookup(0) {} + + // buildCFG - Used by external clients to construct the CFG. + CFG* buildCFG(const Decl *D, Stmt *Statement); + + bool alwaysAdd(const Stmt *stmt); + +private: + // Visitors to walk an AST and construct the CFG. + CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc); + CFGBlock *VisitBinaryOperator(BinaryOperator *B, AddStmtChoice asc); + CFGBlock *VisitBreakStmt(BreakStmt *B); + CFGBlock *VisitCallExpr(CallExpr *C, AddStmtChoice asc); + CFGBlock *VisitCaseStmt(CaseStmt *C); + CFGBlock *VisitChooseExpr(ChooseExpr *C, AddStmtChoice asc); + CFGBlock *VisitCompoundStmt(CompoundStmt *C); + CFGBlock *VisitConditionalOperator(AbstractConditionalOperator *C, + AddStmtChoice asc); + CFGBlock *VisitContinueStmt(ContinueStmt *C); + CFGBlock *VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, + AddStmtChoice asc); + CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S); + CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc); + CFGBlock *VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc); + CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S); + CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, + AddStmtChoice asc); + CFGBlock *VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, + AddStmtChoice asc); + CFGBlock *VisitCXXThrowExpr(CXXThrowExpr *T); + CFGBlock *VisitCXXTryStmt(CXXTryStmt *S); + CFGBlock *VisitDeclStmt(DeclStmt *DS); + CFGBlock *VisitDeclSubExpr(DeclStmt *DS); + CFGBlock *VisitDefaultStmt(DefaultStmt *D); + CFGBlock *VisitDoStmt(DoStmt *D); + CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc); + CFGBlock *VisitForStmt(ForStmt *F); + CFGBlock *VisitGotoStmt(GotoStmt *G); + CFGBlock *VisitIfStmt(IfStmt *I); + CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc); + CFGBlock *VisitIndirectGotoStmt(IndirectGotoStmt *I); + CFGBlock *VisitLabelStmt(LabelStmt *L); + CFGBlock *VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc); + CFGBlock *VisitLogicalOperator(BinaryOperator *B); + std::pair<CFGBlock *, CFGBlock *> VisitLogicalOperator(BinaryOperator *B, + Stmt *Term, + CFGBlock *TrueBlock, + CFGBlock *FalseBlock); + CFGBlock *VisitMemberExpr(MemberExpr *M, AddStmtChoice asc); + CFGBlock *VisitObjCAtCatchStmt(ObjCAtCatchStmt *S); + CFGBlock *VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S); + CFGBlock *VisitObjCAtThrowStmt(ObjCAtThrowStmt *S); + CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S); + CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); + CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); + CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E); + CFGBlock *VisitReturnStmt(ReturnStmt *R); + CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc); + CFGBlock *VisitSwitchStmt(SwitchStmt *S); + CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, + AddStmtChoice asc); + CFGBlock *VisitUnaryOperator(UnaryOperator *U, AddStmtChoice asc); + CFGBlock *VisitWhileStmt(WhileStmt *W); + + CFGBlock *Visit(Stmt *S, AddStmtChoice asc = AddStmtChoice::NotAlwaysAdd); + CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc); + CFGBlock *VisitChildren(Stmt *S); + CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc); + + // Visitors to walk an AST and generate destructors of temporaries in + // full expression. + CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary = false); + CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E); + CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E); + CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(CXXBindTemporaryExpr *E, + bool BindToTemporary); + CFGBlock * + VisitConditionalOperatorForTemporaryDtors(AbstractConditionalOperator *E, + bool BindToTemporary); + + // NYS == Not Yet Supported + CFGBlock *NYS() { + badCFG = true; + return Block; + } + + void autoCreateBlock() { if (!Block) Block = createBlock(); } + CFGBlock *createBlock(bool add_successor = true); + CFGBlock *createNoReturnBlock(); + + CFGBlock *addStmt(Stmt *S) { + return Visit(S, AddStmtChoice::AlwaysAdd); + } + CFGBlock *addInitializer(CXXCtorInitializer *I); + void addAutomaticObjDtors(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S); + void addImplicitDtorsForDestructor(const CXXDestructorDecl *DD); + + // Local scopes creation. + LocalScope* createOrReuseLocalScope(LocalScope* Scope); + + void addLocalScopeForStmt(Stmt *S); + LocalScope* addLocalScopeForDeclStmt(DeclStmt *DS, LocalScope* Scope = NULL); + LocalScope* addLocalScopeForVarDecl(VarDecl *VD, LocalScope* Scope = NULL); + + void addLocalScopeAndDtors(Stmt *S); + + // Interface to CFGBlock - adding CFGElements. + void appendStmt(CFGBlock *B, const Stmt *S) { + if (alwaysAdd(S) && cachedEntry) + cachedEntry->second = B; + + // All block-level expressions should have already been IgnoreParens()ed. + 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 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()); + } + + void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) { + B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext()); + } + + void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, + LocalScope::const_iterator B, LocalScope::const_iterator E); + + void addSuccessor(CFGBlock *B, CFGBlock *S) { + B->addSuccessor(S, cfg->getBumpVectorContext()); + } + + /// Try and evaluate an expression to an integer constant. + bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) { + if (!BuildOpts.PruneTriviallyFalseEdges) + return false; + return !S->isTypeDependent() && + !S->isValueDependent() && + S->EvaluateAsRValue(outResult, *Context); + } + + /// tryEvaluateBool - Try and evaluate the Stmt and return 0 or 1 + /// if we can evaluate to a known value, otherwise return -1. + TryResult tryEvaluateBool(Expr *S) { + if (!BuildOpts.PruneTriviallyFalseEdges || + S->isTypeDependent() || S->isValueDependent()) + return TryResult(); + + if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) { + if (Bop->isLogicalOp()) { + // Check the cache first. + CachedBoolEvalsTy::iterator I = CachedBoolEvals.find(S); + if (I != CachedBoolEvals.end()) + return I->second; // already in map; + + // Retrieve result at first, or the map might be updated. + TryResult Result = evaluateAsBooleanConditionNoCache(S); + CachedBoolEvals[S] = Result; // update or insert + return Result; + } + else { + switch (Bop->getOpcode()) { + default: break; + // For 'x & 0' and 'x * 0', we can determine that + // the value is always false. + case BO_Mul: + case BO_And: { + // If either operand is zero, we know the value + // must be false. + llvm::APSInt IntVal; + if (Bop->getLHS()->EvaluateAsInt(IntVal, *Context)) { + if (IntVal.getBoolValue() == false) { + return TryResult(false); + } + } + if (Bop->getRHS()->EvaluateAsInt(IntVal, *Context)) { + if (IntVal.getBoolValue() == false) { + return TryResult(false); + } + } + } + break; + } + } + } + + return evaluateAsBooleanConditionNoCache(S); + } + + /// \brief Evaluate as boolean \param E without using the cache. + TryResult evaluateAsBooleanConditionNoCache(Expr *E) { + if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(E)) { + if (Bop->isLogicalOp()) { + TryResult LHS = tryEvaluateBool(Bop->getLHS()); + if (LHS.isKnown()) { + // We were able to evaluate the LHS, see if we can get away with not + // evaluating the RHS: 0 && X -> 0, 1 || X -> 1 + if (LHS.isTrue() == (Bop->getOpcode() == BO_LOr)) + return LHS.isTrue(); + + TryResult RHS = tryEvaluateBool(Bop->getRHS()); + if (RHS.isKnown()) { + if (Bop->getOpcode() == BO_LOr) + return LHS.isTrue() || RHS.isTrue(); + else + return LHS.isTrue() && RHS.isTrue(); + } + } else { + TryResult RHS = tryEvaluateBool(Bop->getRHS()); + if (RHS.isKnown()) { + // We can't evaluate the LHS; however, sometimes the result + // is determined by the RHS: X && 0 -> 0, X || 1 -> 1. + if (RHS.isTrue() == (Bop->getOpcode() == BO_LOr)) + return RHS.isTrue(); + } + } + + return TryResult(); + } + } + + bool Result; + if (E->EvaluateAsBooleanCondition(Result, *Context)) + return Result; + + return TryResult(); + } + +}; + +inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder, + const Stmt *stmt) const { + return builder.alwaysAdd(stmt) || kind == AlwaysAdd; +} + +bool CFGBuilder::alwaysAdd(const Stmt *stmt) { + bool shouldAdd = BuildOpts.alwaysAdd(stmt); + + if (!BuildOpts.forcedBlkExprs) + return shouldAdd; + + if (lastLookup == stmt) { + if (cachedEntry) { + assert(cachedEntry->first == stmt); + return true; + } + return shouldAdd; + } + + lastLookup = stmt; + + // Perform the lookup! + CFG::BuildOptions::ForcedBlkExprs *fb = *BuildOpts.forcedBlkExprs; + + if (!fb) { + // No need to update 'cachedEntry', since it will always be null. + assert(cachedEntry == 0); + return shouldAdd; + } + + CFG::BuildOptions::ForcedBlkExprs::iterator itr = fb->find(stmt); + if (itr == fb->end()) { + cachedEntry = 0; + return shouldAdd; + } + + cachedEntry = &*itr; + return true; +} + +// FIXME: Add support for dependent-sized array types in C++? +// Does it even make sense to build a CFG for an uninstantiated template? +static const VariableArrayType *FindVA(const Type *t) { + while (const ArrayType *vt = dyn_cast<ArrayType>(t)) { + if (const VariableArrayType *vat = dyn_cast<VariableArrayType>(vt)) + if (vat->getSizeExpr()) + return vat; + + t = vt->getElementType().getTypePtr(); + } + + return 0; +} + +/// BuildCFG - Constructs a CFG from an AST (a Stmt*). The AST can represent an +/// arbitrary statement. Examples include a single expression or a function +/// body (compound statement). The ownership of the returned CFG is +/// transferred to the caller. If CFG construction fails, this method returns +/// NULL. +CFG* CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { + assert(cfg.get()); + if (!Statement) + return NULL; + + // Create an empty block that will serve as the exit block for the CFG. Since + // this is the first block added to the CFG, it will be implicitly registered + // as the exit block. + Succ = createBlock(); + assert(Succ == &cfg->getExit()); + Block = NULL; // the EXIT block is empty. Create all other blocks lazily. + + if (BuildOpts.AddImplicitDtors) + if (const CXXDestructorDecl *DD = dyn_cast_or_null<CXXDestructorDecl>(D)) + addImplicitDtorsForDestructor(DD); + + // Visit the statements and create the CFG. + CFGBlock *B = addStmt(Statement); + + if (badCFG) + return NULL; + + // For C++ constructor add initializers to CFG. + if (const CXXConstructorDecl *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) { + for (CXXConstructorDecl::init_const_reverse_iterator I = CD->init_rbegin(), + E = CD->init_rend(); I != E; ++I) { + B = addInitializer(*I); + if (badCFG) + return NULL; + } + } + + if (B) + Succ = B; + + // Backpatch the gotos whose label -> block mappings we didn't know when we + // encountered them. + for (BackpatchBlocksTy::iterator I = BackpatchBlocks.begin(), + E = BackpatchBlocks.end(); I != E; ++I ) { + + CFGBlock *B = I->block; + const GotoStmt *G = cast<GotoStmt>(B->getTerminator()); + LabelMapTy::iterator LI = LabelMap.find(G->getLabel()); + + // If there is no target for the goto, then we are looking at an + // incomplete AST. Handle this by not registering a successor. + if (LI == LabelMap.end()) continue; + + JumpTarget JT = LI->second; + prependAutomaticObjDtorsWithTerminator(B, I->scopePosition, + JT.scopePosition); + addSuccessor(B, JT.block); + } + + // Add successors to the Indirect Goto Dispatch block (if we have one). + 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); + + // If there is no target block that contains label, then we are looking + // at an incomplete AST. Handle this by not registering a successor. + if (LI == LabelMap.end()) continue; + + addSuccessor(B, LI->second.block); + } + + // Create an empty entry block that has no predecessors. + cfg->setEntry(createBlock()); + + return cfg.take(); +} + +/// createBlock - Used to lazily create blocks that are connected +/// to the current (global) succcessor. +CFGBlock *CFGBuilder::createBlock(bool add_successor) { + CFGBlock *B = cfg->createBlock(); + if (add_successor && Succ) + addSuccessor(B, Succ); + return B; +} + +/// createNoReturnBlock - Used to create a block is a 'noreturn' point in the +/// CFG. It is *not* connected to the current (global) successor, and instead +/// directly tied to the exit block in order to be reachable. +CFGBlock *CFGBuilder::createNoReturnBlock() { + CFGBlock *B = createBlock(false); + B->setHasNoReturnElement(); + addSuccessor(B, &cfg->getExit()); + return B; +} + +/// addInitializer - Add C++ base or member initializer element to CFG. +CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) { + if (!BuildOpts.AddInitializers) + return Block; + + bool IsReference = false; + bool HasTemporaries = false; + + // Destructors of temporaries in initialization expression should be called + // after initialization finishes. + Expr *Init = I->getInit(); + if (Init) { + if (FieldDecl *FD = I->getAnyMember()) + IsReference = FD->getType()->isReferenceType(); + HasTemporaries = isa<ExprWithCleanups>(Init); + + if (BuildOpts.AddTemporaryDtors && HasTemporaries) { + // Generate destructors for temporaries in initialization expression. + VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(), + IsReference); + } + } + + autoCreateBlock(); + appendInitializer(Block, I); + + if (Init) { + if (HasTemporaries) { + // For expression with temporaries go directly to subexpression to omit + // generating destructors for the second time. + return Visit(cast<ExprWithCleanups>(Init)->getSubExpr()); + } + return Visit(Init); + } + + return Block; +} + +/// \brief Retrieve the type of the temporary object whose lifetime was +/// extended by a local reference with the given initializer. +static QualType getReferenceInitTemporaryType(ASTContext &Context, + const Expr *Init) { + while (true) { + // Skip parentheses. + Init = Init->IgnoreParens(); + + // Skip through cleanups. + if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init)) { + Init = EWC->getSubExpr(); + continue; + } + + // Skip through the temporary-materialization expression. + if (const MaterializeTemporaryExpr *MTE + = dyn_cast<MaterializeTemporaryExpr>(Init)) { + Init = MTE->GetTemporaryExpr(); + continue; + } + + // Skip derived-to-base and no-op casts. + if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) { + if ((CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp) && + Init->getType()->isRecordType()) { + Init = CE->getSubExpr(); + continue; + } + } + + // Skip member accesses into rvalues. + if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) { + if (!ME->isArrow() && ME->getBase()->isRValue()) { + Init = ME->getBase(); + continue; + } + } + + break; + } + + return Init->getType(); +} + +/// addAutomaticObjDtors - Add to current block automatic objects destructors +/// for objects in range of local scope positions. Use S as trigger statement +/// for destructors. +void CFGBuilder::addAutomaticObjDtors(LocalScope::const_iterator B, + LocalScope::const_iterator E, Stmt *S) { + if (!BuildOpts.AddImplicitDtors) + return; + + if (B == E) + return; + + // 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 + // this sequence up and replay them in reverse order when appending onto the + // CFGBlock(s). + SmallVector<VarDecl*, 10> Decls; + Decls.reserve(B.distance(E)); + for (LocalScope::const_iterator I = B; I != E; ++I) + Decls.push_back(*I); + + for (SmallVectorImpl<VarDecl*>::reverse_iterator I = Decls.rbegin(), + E = Decls.rend(); + I != E; ++I) { + // If this destructor is marked as a no-return destructor, we need to + // create a new block for the destructor which does not have as a successor + // anything built thus far: control won't flow out of this block. + QualType Ty = (*I)->getType(); + if (Ty->isReferenceType()) { + Ty = getReferenceInitTemporaryType(*Context, (*I)->getInit()); + } + Ty = Context->getBaseElementType(Ty); + + const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor(); + if (Dtor->isNoReturn()) + Block = createNoReturnBlock(); + else + autoCreateBlock(); + + appendAutomaticObjDtor(Block, *I, S); + } +} + +/// 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"); + const CXXRecordDecl *RD = DD->getParent(); + + // At the end destroy virtual base objects. + for (CXXRecordDecl::base_class_const_iterator VI = RD->vbases_begin(), + VE = RD->vbases_end(); VI != VE; ++VI) { + const CXXRecordDecl *CD = VI->getType()->getAsCXXRecordDecl(); + if (!CD->hasTrivialDestructor()) { + autoCreateBlock(); + appendBaseDtor(Block, VI); + } + } + + // Before virtual bases destroy direct base objects. + for (CXXRecordDecl::base_class_const_iterator BI = RD->bases_begin(), + BE = RD->bases_end(); BI != BE; ++BI) { + if (!BI->isVirtual()) { + const CXXRecordDecl *CD = BI->getType()->getAsCXXRecordDecl(); + if (!CD->hasTrivialDestructor()) { + autoCreateBlock(); + appendBaseDtor(Block, BI); + } + } + } + + // First destroy member objects. + for (CXXRecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); FI != FE; ++FI) { + // Check for constant size array. Set type to array element type. + QualType QT = FI->getType(); + if (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { + if (AT->getSize() == 0) + continue; + QT = AT->getElementType(); + } + + if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) + if (!CD->hasTrivialDestructor()) { + autoCreateBlock(); + appendMemberDtor(Block, *FI); + } + } +} + +/// createOrReuseLocalScope - If Scope is NULL create new LocalScope. Either +/// way return valid LocalScope object. +LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { + if (!Scope) { + llvm::BumpPtrAllocator &alloc = cfg->getAllocator(); + Scope = alloc.Allocate<LocalScope>(); + BumpVectorContext ctx(alloc); + new (Scope) LocalScope(ctx, ScopePos); + } + return Scope; +} + +/// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement +/// that should create implicit scope (e.g. if/else substatements). +void CFGBuilder::addLocalScopeForStmt(Stmt *S) { + if (!BuildOpts.AddImplicitDtors) + return; + + LocalScope *Scope = 0; + + // For compound statement we will be creating explicit scope. + if (CompoundStmt *CS = dyn_cast<CompoundStmt>(S)) { + for (CompoundStmt::body_iterator BI = CS->body_begin(), BE = CS->body_end() + ; BI != BE; ++BI) { + Stmt *SI = (*BI)->stripLabelLikeStatements(); + if (DeclStmt *DS = dyn_cast<DeclStmt>(SI)) + Scope = addLocalScopeForDeclStmt(DS, Scope); + } + return; + } + + // For any other statement scope will be implicit and as such will be + // interesting only for DeclStmt. + if (DeclStmt *DS = dyn_cast<DeclStmt>(S->stripLabelLikeStatements())) + addLocalScopeForDeclStmt(DS); +} + +/// addLocalScopeForDeclStmt - Add LocalScope for declaration statement. Will +/// reuse Scope if not NULL. +LocalScope* CFGBuilder::addLocalScopeForDeclStmt(DeclStmt *DS, + LocalScope* Scope) { + if (!BuildOpts.AddImplicitDtors) + return Scope; + + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end() + ; DI != DE; ++DI) { + if (VarDecl *VD = dyn_cast<VarDecl>(*DI)) + Scope = addLocalScopeForVarDecl(VD, Scope); + } + return Scope; +} + +/// addLocalScopeForVarDecl - Add LocalScope for variable declaration. It will +/// create add scope for automatic objects and temporary objects bound to +/// const reference. Will reuse Scope if not NULL. +LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl *VD, + LocalScope* Scope) { + if (!BuildOpts.AddImplicitDtors) + return Scope; + + // Check if variable is local. + switch (VD->getStorageClass()) { + case SC_None: + case SC_Auto: + case SC_Register: + break; + default: return Scope; + } + + // Check for const references bound to temporary. Set type to pointee. + QualType QT = VD->getType(); + if (QT.getTypePtr()->isReferenceType()) { + // Attempt to determine whether this declaration lifetime-extends a + // temporary. + // + // FIXME: This is incorrect. Non-reference declarations can lifetime-extend + // temporaries, and a single declaration can extend multiple temporaries. + // We should look at the storage duration on each nested + // MaterializeTemporaryExpr instead. + const Expr *Init = VD->getInit(); + if (!Init) + return Scope; + if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Init)) + Init = EWC->getSubExpr(); + if (!isa<MaterializeTemporaryExpr>(Init)) + return Scope; + + // Lifetime-extending a temporary. + QT = getReferenceInitTemporaryType(*Context, Init); + } + + // Check for constant size array. Set type to array element type. + while (const ConstantArrayType *AT = Context->getAsConstantArrayType(QT)) { + if (AT->getSize() == 0) + return Scope; + QT = AT->getElementType(); + } + + // Check if type is a C++ class with non-trivial destructor. + if (const CXXRecordDecl *CD = QT->getAsCXXRecordDecl()) + if (!CD->hasTrivialDestructor()) { + // Add the variable to scope + Scope = createOrReuseLocalScope(Scope); + Scope->addVar(VD); + ScopePos = Scope->begin(); + } + return Scope; +} + +/// addLocalScopeAndDtors - For given statement add local scope for it and +/// add destructors that will cleanup the scope. Will reuse Scope if not NULL. +void CFGBuilder::addLocalScopeAndDtors(Stmt *S) { + if (!BuildOpts.AddImplicitDtors) + return; + + LocalScope::const_iterator scopeBeginPos = ScopePos; + addLocalScopeForStmt(S); + addAutomaticObjDtors(ScopePos, scopeBeginPos, S); +} + +/// prependAutomaticObjDtorsWithTerminator - Prepend destructor CFGElements for +/// variables with automatic storage duration to CFGBlock's elements vector. +/// Elements will be prepended to physical beginning of the vector which +/// happens to be logical end. Use blocks terminator as statement that specifies +/// destructors call site. +/// FIXME: This mechanism for adding automatic destructors doesn't handle +/// no-return destructors properly. +void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk, + LocalScope::const_iterator B, LocalScope::const_iterator E) { + BumpVectorContext &C = cfg->getBumpVectorContext(); + CFGBlock::iterator InsertPos + = Blk->beginAutomaticObjDtorsInsert(Blk->end(), B.distance(E), C); + for (LocalScope::const_iterator I = B; I != E; ++I) + InsertPos = Blk->insertAutomaticObjDtor(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). +CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { + if (!S) { + badCFG = true; + return 0; + } + + if (Expr *E = dyn_cast<Expr>(S)) + S = E->IgnoreParens(); + + switch (S->getStmtClass()) { + default: + return VisitStmt(S, asc); + + case Stmt::AddrLabelExprClass: + return VisitAddrLabelExpr(cast<AddrLabelExpr>(S), asc); + + case Stmt::BinaryConditionalOperatorClass: + return VisitConditionalOperator(cast<BinaryConditionalOperator>(S), asc); + + case Stmt::BinaryOperatorClass: + return VisitBinaryOperator(cast<BinaryOperator>(S), asc); + + case Stmt::BlockExprClass: + return VisitNoRecurse(cast<Expr>(S), asc); + + case Stmt::BreakStmtClass: + return VisitBreakStmt(cast<BreakStmt>(S)); + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::UserDefinedLiteralClass: + return VisitCallExpr(cast<CallExpr>(S), asc); + + case Stmt::CaseStmtClass: + return VisitCaseStmt(cast<CaseStmt>(S)); + + case Stmt::ChooseExprClass: + return VisitChooseExpr(cast<ChooseExpr>(S), asc); + + case Stmt::CompoundStmtClass: + return VisitCompoundStmt(cast<CompoundStmt>(S)); + + case Stmt::ConditionalOperatorClass: + return VisitConditionalOperator(cast<ConditionalOperator>(S), asc); + + case Stmt::ContinueStmtClass: + return VisitContinueStmt(cast<ContinueStmt>(S)); + + case Stmt::CXXCatchStmtClass: + return VisitCXXCatchStmt(cast<CXXCatchStmt>(S)); + + case Stmt::ExprWithCleanupsClass: + return VisitExprWithCleanups(cast<ExprWithCleanups>(S), asc); + + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: + // FIXME: The expression inside a CXXDefaultArgExpr is owned by the + // called function's declaration, not by the caller. If we simply add + // this expression to the CFG, we could end up with the same Expr + // appearing multiple times. + // PR13385 / <rdar://problem/12156507> + // + // It's likewise possible for multiple CXXDefaultInitExprs for the same + // expression to be used in the same function (through aggregate + // initialization). + return VisitStmt(S, asc); + + case Stmt::CXXBindTemporaryExprClass: + return VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), asc); + + case Stmt::CXXConstructExprClass: + return VisitCXXConstructExpr(cast<CXXConstructExpr>(S), asc); + + case Stmt::CXXDeleteExprClass: + return VisitCXXDeleteExpr(cast<CXXDeleteExpr>(S), asc); + + case Stmt::CXXFunctionalCastExprClass: + return VisitCXXFunctionalCastExpr(cast<CXXFunctionalCastExpr>(S), asc); + + case Stmt::CXXTemporaryObjectExprClass: + return VisitCXXTemporaryObjectExpr(cast<CXXTemporaryObjectExpr>(S), asc); + + case Stmt::CXXThrowExprClass: + return VisitCXXThrowExpr(cast<CXXThrowExpr>(S)); + + case Stmt::CXXTryStmtClass: + return VisitCXXTryStmt(cast<CXXTryStmt>(S)); + + case Stmt::CXXForRangeStmtClass: + return VisitCXXForRangeStmt(cast<CXXForRangeStmt>(S)); + + case Stmt::DeclStmtClass: + return VisitDeclStmt(cast<DeclStmt>(S)); + + case Stmt::DefaultStmtClass: + return VisitDefaultStmt(cast<DefaultStmt>(S)); + + case Stmt::DoStmtClass: + return VisitDoStmt(cast<DoStmt>(S)); + + case Stmt::ForStmtClass: + return VisitForStmt(cast<ForStmt>(S)); + + case Stmt::GotoStmtClass: + return VisitGotoStmt(cast<GotoStmt>(S)); + + case Stmt::IfStmtClass: + return VisitIfStmt(cast<IfStmt>(S)); + + case Stmt::ImplicitCastExprClass: + return VisitImplicitCastExpr(cast<ImplicitCastExpr>(S), asc); + + case Stmt::IndirectGotoStmtClass: + return VisitIndirectGotoStmt(cast<IndirectGotoStmt>(S)); + + case Stmt::LabelStmtClass: + return VisitLabelStmt(cast<LabelStmt>(S)); + + case Stmt::LambdaExprClass: + return VisitLambdaExpr(cast<LambdaExpr>(S), asc); + + case Stmt::MemberExprClass: + return VisitMemberExpr(cast<MemberExpr>(S), asc); + + case Stmt::NullStmtClass: + return Block; + + case Stmt::ObjCAtCatchStmtClass: + return VisitObjCAtCatchStmt(cast<ObjCAtCatchStmt>(S)); + + case Stmt::ObjCAutoreleasePoolStmtClass: + return VisitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(S)); + + case Stmt::ObjCAtSynchronizedStmtClass: + return VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S)); + + case Stmt::ObjCAtThrowStmtClass: + return VisitObjCAtThrowStmt(cast<ObjCAtThrowStmt>(S)); + + case Stmt::ObjCAtTryStmtClass: + return VisitObjCAtTryStmt(cast<ObjCAtTryStmt>(S)); + + case Stmt::ObjCForCollectionStmtClass: + return VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S)); + + case Stmt::OpaqueValueExprClass: + return Block; + + case Stmt::PseudoObjectExprClass: + return VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S)); + + case Stmt::ReturnStmtClass: + return VisitReturnStmt(cast<ReturnStmt>(S)); + + case Stmt::UnaryExprOrTypeTraitExprClass: + return VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), + asc); + + case Stmt::StmtExprClass: + return VisitStmtExpr(cast<StmtExpr>(S), asc); + + case Stmt::SwitchStmtClass: + return VisitSwitchStmt(cast<SwitchStmt>(S)); + + case Stmt::UnaryOperatorClass: + return VisitUnaryOperator(cast<UnaryOperator>(S), asc); + + case Stmt::WhileStmtClass: + return VisitWhileStmt(cast<WhileStmt>(S)); + } +} + +CFGBlock *CFGBuilder::VisitStmt(Stmt *S, AddStmtChoice asc) { + if (asc.alwaysAdd(*this, S)) { + autoCreateBlock(); + appendStmt(Block, S); + } + + return VisitChildren(S); +} + +/// VisitChildren - Visit the children of a Stmt. +CFGBlock *CFGBuilder::VisitChildren(Stmt *S) { + CFGBlock *B = Block; + + // Visit the children in their reverse order so that they appear in + // left-to-right (natural) order in the CFG. + reverse_children RChildren(S); + for (reverse_children::iterator I = RChildren.begin(), E = RChildren.end(); + I != E; ++I) { + if (Stmt *Child = *I) + if (CFGBlock *R = Visit(Child)) + B = R; + } + return B; +} + +CFGBlock *CFGBuilder::VisitAddrLabelExpr(AddrLabelExpr *A, + AddStmtChoice asc) { + AddressTakenLabels.insert(A->getLabel()); + + if (asc.alwaysAdd(*this, A)) { + autoCreateBlock(); + appendStmt(Block, A); + } + + return Block; +} + +CFGBlock *CFGBuilder::VisitUnaryOperator(UnaryOperator *U, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, U)) { + autoCreateBlock(); + appendStmt(Block, U); + } + + return Visit(U->getSubExpr(), AddStmtChoice()); +} + +CFGBlock *CFGBuilder::VisitLogicalOperator(BinaryOperator *B) { + CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); + appendStmt(ConfluenceBlock, B); + + if (badCFG) + return 0; + + return VisitLogicalOperator(B, 0, ConfluenceBlock, ConfluenceBlock).first; +} + +std::pair<CFGBlock*, CFGBlock*> +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. + Expr *RHS = B->getRHS()->IgnoreParens(); + CFGBlock *RHSBlock, *ExitBlock; + + do { + if (BinaryOperator *B_RHS = dyn_cast<BinaryOperator>(RHS)) + if (B_RHS->isLogicalOp()) { + llvm::tie(RHSBlock, ExitBlock) = + VisitLogicalOperator(B_RHS, Term, TrueBlock, FalseBlock); + break; + } + + // The RHS is not a nested logical operation. Don't push the terminator + // down further, but instead visit RHS and construct the respective + // pieces of the CFG, and link up the RHSBlock with the terminator + // we have been provided. + ExitBlock = RHSBlock = createBlock(false); + + if (!Term) { + assert(TrueBlock == FalseBlock); + addSuccessor(RHSBlock, TrueBlock); + } + else { + RHSBlock->setTerminator(Term); + TryResult KnownVal = tryEvaluateBool(RHS); + addSuccessor(RHSBlock, KnownVal.isFalse() ? NULL : TrueBlock); + addSuccessor(RHSBlock, KnownVal.isTrue() ? NULL : FalseBlock); + } + + Block = RHSBlock; + RHSBlock = addStmt(RHS); + } + while (false); + + if (badCFG) + return std::make_pair((CFGBlock*)0, (CFGBlock*)0); + + // Generate the blocks for evaluating the LHS. + Expr *LHS = B->getLHS()->IgnoreParens(); + + if (BinaryOperator *B_LHS = dyn_cast<BinaryOperator>(LHS)) + if (B_LHS->isLogicalOp()) { + if (B->getOpcode() == BO_LOr) + FalseBlock = RHSBlock; + else + TrueBlock = RHSBlock; + + // For the LHS, treat 'B' as the terminator that we want to sink + // into the nested branch. The RHS always gets the top-most + // terminator. + return VisitLogicalOperator(B_LHS, B, TrueBlock, FalseBlock); + } + + // Create the block evaluating the LHS. + // This contains the '&&' or '||' as the terminator. + CFGBlock *LHSBlock = createBlock(false); + LHSBlock->setTerminator(B); + + Block = LHSBlock; + CFGBlock *EntryLHSBlock = addStmt(LHS); + + if (badCFG) + return std::make_pair((CFGBlock*)0, (CFGBlock*)0); + + // See if this is a known constant. + TryResult KnownVal = tryEvaluateBool(LHS); + + // Now link the LHSBlock with RHSBlock. + if (B->getOpcode() == BO_LOr) { + addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : TrueBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : RHSBlock); + } else { + assert(B->getOpcode() == BO_LAnd); + addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : FalseBlock); + } + + return std::make_pair(EntryLHSBlock, ExitBlock); +} + + +CFGBlock *CFGBuilder::VisitBinaryOperator(BinaryOperator *B, + AddStmtChoice asc) { + // && or || + if (B->isLogicalOp()) + return VisitLogicalOperator(B); + + if (B->getOpcode() == BO_Comma) { // , + autoCreateBlock(); + appendStmt(Block, B); + addStmt(B->getRHS()); + return addStmt(B->getLHS()); + } + + if (B->isAssignmentOp()) { + if (asc.alwaysAdd(*this, B)) { + autoCreateBlock(); + appendStmt(Block, B); + } + Visit(B->getLHS()); + return Visit(B->getRHS()); + } + + if (asc.alwaysAdd(*this, B)) { + autoCreateBlock(); + appendStmt(Block, B); + } + + CFGBlock *RBlock = Visit(B->getRHS()); + CFGBlock *LBlock = Visit(B->getLHS()); + // If visiting RHS causes us to finish 'Block', e.g. the RHS is a StmtExpr + // containing a DoStmt, and the LHS doesn't create a new block, then we should + // return RBlock. Otherwise we'll incorrectly return NULL. + return (LBlock ? LBlock : RBlock); +} + +CFGBlock *CFGBuilder::VisitNoRecurse(Expr *E, AddStmtChoice asc) { + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + } + return Block; +} + +CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) { + // "break" is a control-flow statement. Thus we stop processing the current + // block. + if (badCFG) + return 0; + + // Now create a new block that ends with the break statement. + Block = createBlock(false); + Block->setTerminator(B); + + // If there is no target for the break, then we are looking at an incomplete + // AST. This means that the CFG cannot be constructed. + if (BreakJumpTarget.block) { + addAutomaticObjDtors(ScopePos, BreakJumpTarget.scopePosition, B); + addSuccessor(Block, BreakJumpTarget.block); + } else + badCFG = true; + + + return Block; +} + +static bool CanThrow(Expr *E, ASTContext &Ctx) { + QualType Ty = E->getType(); + if (Ty->isFunctionPointerType()) + Ty = Ty->getAs<PointerType>()->getPointeeType(); + else if (Ty->isBlockPointerType()) + Ty = Ty->getAs<BlockPointerType>()->getPointeeType(); + + const FunctionType *FT = Ty->getAs<FunctionType>(); + if (FT) { + if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT)) + if (!isUnresolvedExceptionSpec(Proto->getExceptionSpecType()) && + Proto->isNothrow(Ctx)) + return false; + } + return true; +} + +CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { + // Compute the callee type. + QualType calleeType = C->getCallee()->getType(); + if (calleeType == Context->BoundMemberTy) { + QualType boundType = Expr::findBoundMemberType(C->getCallee()); + + // We should only get a null bound type if processing a dependent + // CFG. Recover by assuming nothing. + if (!boundType.isNull()) calleeType = boundType; + } + + // If this is a call to a no-return function, this stops the block here. + bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn(); + + bool AddEHEdge = false; + + // Languages without exceptions are assumed to not throw. + if (Context->getLangOpts().Exceptions) { + if (BuildOpts.AddEHEdges) + AddEHEdge = true; + } + + // If this is a call to a builtin function, it might not actually evaluate + // its arguments. Don't add them to the CFG if this is the case. + bool OmitArguments = false; + + if (FunctionDecl *FD = C->getDirectCallee()) { + if (FD->isNoReturn()) + NoReturn = true; + if (FD->hasAttr<NoThrowAttr>()) + AddEHEdge = false; + if (FD->getBuiltinID() == Builtin::BI__builtin_object_size) + OmitArguments = true; + } + + if (!CanThrow(C->getCallee(), *Context)) + AddEHEdge = false; + + if (OmitArguments) { + assert(!NoReturn && "noreturn calls with unevaluated args not implemented"); + assert(!AddEHEdge && "EH calls with unevaluated args not implemented"); + autoCreateBlock(); + appendStmt(Block, C); + return Visit(C->getCallee()); + } + + if (!NoReturn && !AddEHEdge) { + return VisitStmt(C, asc.withAlwaysAdd(true)); + } + + if (Block) { + Succ = Block; + if (badCFG) + return 0; + } + + if (NoReturn) + Block = createNoReturnBlock(); + else + Block = createBlock(); + + appendStmt(Block, C); + + if (AddEHEdge) { + // Add exceptional edges. + if (TryTerminatedBlock) + addSuccessor(Block, TryTerminatedBlock); + else + addSuccessor(Block, &cfg->getExit()); + } + + return VisitChildren(C); +} + +CFGBlock *CFGBuilder::VisitChooseExpr(ChooseExpr *C, + AddStmtChoice asc) { + CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); + appendStmt(ConfluenceBlock, C); + if (badCFG) + return 0; + + AddStmtChoice alwaysAdd = asc.withAlwaysAdd(true); + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *LHSBlock = Visit(C->getLHS(), alwaysAdd); + if (badCFG) + return 0; + + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *RHSBlock = Visit(C->getRHS(), alwaysAdd); + if (badCFG) + return 0; + + Block = createBlock(false); + // See if this is a known constant. + const TryResult& KnownVal = tryEvaluateBool(C->getCond()); + addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); + addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + Block->setTerminator(C); + return addStmt(C->getCond()); +} + + +CFGBlock *CFGBuilder::VisitCompoundStmt(CompoundStmt *C) { + addLocalScopeAndDtors(C); + CFGBlock *LastBlock = Block; + + for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend(); + I != E; ++I ) { + // If we hit a segment of code just containing ';' (NullStmts), we can + // get a null block back. In such cases, just use the LastBlock + if (CFGBlock *newBlock = addStmt(*I)) + LastBlock = newBlock; + + if (badCFG) + return NULL; + } + + return LastBlock; +} + +CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, + AddStmtChoice asc) { + const BinaryConditionalOperator *BCO = dyn_cast<BinaryConditionalOperator>(C); + const OpaqueValueExpr *opaqueValue = (BCO ? BCO->getOpaqueValue() : NULL); + + // Create the confluence block that will "merge" the results of the ternary + // expression. + CFGBlock *ConfluenceBlock = Block ? Block : createBlock(); + appendStmt(ConfluenceBlock, C); + if (badCFG) + return 0; + + AddStmtChoice alwaysAdd = asc.withAlwaysAdd(true); + + // Create a block for the LHS expression if there is an LHS expression. A + // GCC extension allows LHS to be NULL, causing the condition to be the + // value that is returned instead. + // e.g: x ?: y is shorthand for: x ? x : y; + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *LHSBlock = 0; + const Expr *trueExpr = C->getTrueExpr(); + if (trueExpr != opaqueValue) { + LHSBlock = Visit(C->getTrueExpr(), alwaysAdd); + if (badCFG) + return 0; + Block = NULL; + } + else + LHSBlock = ConfluenceBlock; + + // Create the block for the RHS expression. + Succ = ConfluenceBlock; + CFGBlock *RHSBlock = Visit(C->getFalseExpr(), alwaysAdd); + if (badCFG) + return 0; + + // If the condition is a logical '&&' or '||', build a more accurate CFG. + if (BinaryOperator *Cond = + dyn_cast<BinaryOperator>(C->getCond()->IgnoreParens())) + if (Cond->isLogicalOp()) + return VisitLogicalOperator(Cond, C, LHSBlock, RHSBlock).first; + + // Create the block that will contain the condition. + Block = createBlock(false); + + // See if this is a known constant. + const TryResult& KnownVal = tryEvaluateBool(C->getCond()); + addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); + addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + Block->setTerminator(C); + Expr *condExpr = C->getCond(); + + if (opaqueValue) { + // Run the condition expression if it's not trivially expressed in + // terms of the opaque value (or if there is no opaque value). + if (condExpr != opaqueValue) + addStmt(condExpr); + + // Before that, run the common subexpression if there was one. + // At least one of this or the above will be run. + return addStmt(BCO->getCommon()); + } + + return addStmt(condExpr); +} + +CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { + // Check if the Decl is for an __label__. If so, elide it from the + // CFG entirely. + if (isa<LabelDecl>(*DS->decl_begin())) + return Block; + + // This case also handles static_asserts. + if (DS->isSingleDecl()) + return VisitDeclSubExpr(DS); + + CFGBlock *B = 0; + + // Build an individual DeclStmt for each decl. + for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(), + E = DS->decl_rend(); + I != E; ++I) { + // Get the alignment of the new DeclStmt, padding out to >=8 bytes. + unsigned A = llvm::AlignOf<DeclStmt>::Alignment < 8 + ? 8 : llvm::AlignOf<DeclStmt>::Alignment; + + // Allocate the DeclStmt using the BumpPtrAllocator. It will get + // automatically freed with the CFG. + DeclGroupRef DG(*I); + Decl *D = *I; + void *Mem = cfg->getAllocator().Allocate(sizeof(DeclStmt), A); + DeclStmt *DSNew = new (Mem) DeclStmt(DG, D->getLocation(), GetEndLoc(D)); + cfg->addSyntheticDeclStmt(DSNew, DS); + + // Append the fake DeclStmt to block. + B = VisitDeclSubExpr(DSNew); + } + + return B; +} + +/// VisitDeclSubExpr - Utility method to add block-level expressions for +/// DeclStmts and initializers in them. +CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) { + assert(DS->isSingleDecl() && "Can handle single declarations only."); + VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + + if (!VD) { + // Of everything that can be declared in a DeclStmt, only VarDecls impact + // runtime semantics. + return Block; + } + + bool IsReference = false; + bool HasTemporaries = false; + + // Guard static initializers under a branch. + CFGBlock *blockAfterStaticInit = 0; + + if (BuildOpts.AddStaticInitBranches && VD->isStaticLocal()) { + // For static variables, we need to create a branch to track + // whether or not they are initialized. + if (Block) { + Succ = Block; + Block = 0; + if (badCFG) + return 0; + } + blockAfterStaticInit = Succ; + } + + // Destructors of temporaries in initialization expression should be called + // after initialization finishes. + Expr *Init = VD->getInit(); + if (Init) { + IsReference = VD->getType()->isReferenceType(); + HasTemporaries = isa<ExprWithCleanups>(Init); + + if (BuildOpts.AddTemporaryDtors && HasTemporaries) { + // Generate destructors for temporaries in initialization expression. + VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(), + IsReference); + } + } + + autoCreateBlock(); + appendStmt(Block, DS); + + // Keep track of the last non-null block, as 'Block' can be nulled out + // if the initializer expression is something like a 'while' in a + // statement-expression. + CFGBlock *LastBlock = Block; + + if (Init) { + if (HasTemporaries) { + // For expression with temporaries go directly to subexpression to omit + // generating destructors for the second time. + ExprWithCleanups *EC = cast<ExprWithCleanups>(Init); + if (CFGBlock *newBlock = Visit(EC->getSubExpr())) + LastBlock = newBlock; + } + else { + if (CFGBlock *newBlock = Visit(Init)) + LastBlock = newBlock; + } + } + + // If the type of VD is a VLA, then we must process its size expressions. + for (const VariableArrayType* VA = FindVA(VD->getType().getTypePtr()); + VA != 0; VA = FindVA(VA->getElementType().getTypePtr())) { + if (CFGBlock *newBlock = addStmt(VA->getSizeExpr())) + LastBlock = newBlock; + } + + // Remove variable from local scope. + if (ScopePos && VD == *ScopePos) + ++ScopePos; + + CFGBlock *B = LastBlock; + if (blockAfterStaticInit) { + Succ = B; + Block = createBlock(false); + Block->setTerminator(DS); + addSuccessor(Block, blockAfterStaticInit); + addSuccessor(Block, B); + B = Block; + } + + return B; +} + +CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) { + // 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 + // 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 + // middle of a block, we stop processing that block. That block is then the + // implicit successor for the "then" and "else" clauses. + + // Save local scope position because in case of condition variable ScopePos + // won't be restored when traversing AST. + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + + // Create local scope for possible condition variable. + // Store scope position. Add implicit destructor. + if (VarDecl *VD = I->getConditionVariable()) { + LocalScope::const_iterator BeginScopePos = ScopePos; + addLocalScopeForVarDecl(VD); + addAutomaticObjDtors(ScopePos, BeginScopePos, I); + } + + // The block we were processing is now finished. Make it the successor + // block. + if (Block) { + Succ = Block; + if (badCFG) + return 0; + } + + // Process the false branch. + CFGBlock *ElseBlock = Succ; + + if (Stmt *Else = I->getElse()) { + SaveAndRestore<CFGBlock*> sv(Succ); + + // NULL out Block so that the recursive call to Visit will + // create a new basic block. + Block = NULL; + + // If branch is not a compound statement create implicit scope + // and add destructors. + if (!isa<CompoundStmt>(Else)) + addLocalScopeAndDtors(Else); + + ElseBlock = addStmt(Else); + + if (!ElseBlock) // Can occur when the Else body has all NullStmts. + ElseBlock = sv.get(); + else if (Block) { + if (badCFG) + return 0; + } + } + + // Process the true branch. + CFGBlock *ThenBlock; + { + Stmt *Then = I->getThen(); + assert(Then); + SaveAndRestore<CFGBlock*> sv(Succ); + Block = NULL; + + // If branch is not a compound statement create implicit scope + // and add destructors. + if (!isa<CompoundStmt>(Then)) + addLocalScopeAndDtors(Then); + + ThenBlock = addStmt(Then); + + if (!ThenBlock) { + // We can reach here if the "then" body has all NullStmts. + // Create an empty block so we can distinguish between true and false + // branches in path-sensitive analyses. + ThenBlock = createBlock(false); + addSuccessor(ThenBlock, sv.get()); + } else if (Block) { + if (badCFG) + return 0; + } + } + + // Specially handle "if (expr1 || ...)" and "if (expr1 && ...)" by + // having these handle the actual control-flow jump. Note that + // if we introduce a condition variable, e.g. "if (int x = exp1 || exp2)" + // we resort to the old control-flow behavior. This special handling + // removes infeasible paths from the control-flow graph by having the + // control-flow transfer of '&&' or '||' go directly into the then/else + // blocks directly. + if (!I->getConditionVariable()) + if (BinaryOperator *Cond = + dyn_cast<BinaryOperator>(I->getCond()->IgnoreParens())) + if (Cond->isLogicalOp()) + return VisitLogicalOperator(Cond, I, ThenBlock, ElseBlock).first; + + // Now create a new block containing the if statement. + Block = createBlock(false); + + // Set the terminator of the new block to the If statement. + Block->setTerminator(I); + + // See if this is a known constant. + const TryResult &KnownVal = tryEvaluateBool(I->getCond()); + + // Now add the successors. + addSuccessor(Block, KnownVal.isFalse() ? NULL : ThenBlock); + addSuccessor(Block, KnownVal.isTrue()? NULL : ElseBlock); + + // Add the condition as the last statement in the new block. This may create + // new blocks as the condition may contain control-flow. Any newly created + // blocks will be pointed to be "Block". + CFGBlock *LastBlock = addStmt(I->getCond()); + + // Finally, if the IfStmt contains a condition variable, add both the IfStmt + // and the condition variable initialization to the CFG. + if (VarDecl *VD = I->getConditionVariable()) { + if (Expr *Init = VD->getInit()) { + autoCreateBlock(); + appendStmt(Block, I->getConditionVariableDeclStmt()); + LastBlock = addStmt(Init); + } + } + + return LastBlock; +} + + +CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) { + // 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 + // 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 + // able to report such dead blocks. + + // Create the new block. + Block = createBlock(false); + + addAutomaticObjDtors(ScopePos, LocalScope::const_iterator(), R); + + // If the one of the destructors does not return, we already have the Exit + // block as a successor. + if (!Block->hasNoReturnElement()) + addSuccessor(Block, &cfg->getExit()); + + // Add the return statement to the block. This may create new blocks if R + // contains control-flow (short-circuit operations). + return VisitStmt(R, AddStmtChoice::AlwaysAdd); +} + +CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) { + // Get the block of the labeled statement. Add it to our map. + addStmt(L->getSubStmt()); + CFGBlock *LabelBlock = Block; + + if (!LabelBlock) // This can happen when the body is empty, i.e. + LabelBlock = createBlock(); // scopes that only contains NullStmts. + + assert(LabelMap.find(L->getDecl()) == LabelMap.end() && + "label already in map"); + LabelMap[L->getDecl()] = JumpTarget(LabelBlock, ScopePos); + + // Labels partition blocks, so this is the end of the basic block we were + // processing (L is the block's label). Because this is label (and we have + // already processed the substatement) there is no extra control-flow to worry + // about. + LabelBlock->setLabel(L); + if (badCFG) + return 0; + + // We set Block to NULL to allow lazy creation of a new block (if necessary); + Block = NULL; + + // This block is now the implicit successor of other blocks. + Succ = LabelBlock; + + return LabelBlock; +} + +CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) { + CFGBlock *LastBlock = VisitNoRecurse(E, asc); + for (LambdaExpr::capture_init_iterator it = E->capture_init_begin(), + et = E->capture_init_end(); it != et; ++it) { + if (Expr *Init = *it) { + CFGBlock *Tmp = Visit(Init); + if (Tmp != 0) + LastBlock = Tmp; + } + } + return LastBlock; +} + +CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) { + // Goto is a control-flow statement. Thus we stop processing the current + // block and create a new one. + + Block = createBlock(false); + Block->setTerminator(G); + + // If we already know the mapping to the label block add the successor now. + LabelMapTy::iterator I = LabelMap.find(G->getLabel()); + + if (I == LabelMap.end()) + // We will need to backpatch this block later. + BackpatchBlocks.push_back(JumpSource(Block, ScopePos)); + else { + JumpTarget JT = I->second; + addAutomaticObjDtors(ScopePos, JT.scopePosition, G); + addSuccessor(Block, JT.block); + } + + return Block; +} + +CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { + CFGBlock *LoopSuccessor = NULL; + + // Save local scope position because in case of condition variable ScopePos + // won't be restored when traversing AST. + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + + // Create local scope for init statement and possible condition variable. + // Add destructor for init statement and condition variable. + // Store scope position for continue statement. + if (Stmt *Init = F->getInit()) + addLocalScopeForStmt(Init); + LocalScope::const_iterator LoopBeginScopePos = ScopePos; + + if (VarDecl *VD = F->getConditionVariable()) + addLocalScopeForVarDecl(VD); + LocalScope::const_iterator ContinueScopePos = ScopePos; + + addAutomaticObjDtors(ScopePos, save_scope_pos.get(), F); + + // "for" is a control-flow statement. Thus we stop processing the current + // block. + if (Block) { + if (badCFG) + return 0; + LoopSuccessor = Block; + } else + LoopSuccessor = Succ; + + // Save the current value for the break targets. + // All breaks should go to the code following the loop. + SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); + + CFGBlock *BodyBlock = 0, *TransitionBlock = 0; + + // Now create the loop body. + { + assert(F->getBody()); + + // Save the current values for Block, Succ, continue and break targets. + SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); + SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); + + // Create an empty block to represent the transition block for looping back + // to the head of the loop. If we have increment code, it will + // go in this block as well. + Block = Succ = TransitionBlock = createBlock(false); + TransitionBlock->setLoopTarget(F); + + if (Stmt *I = F->getInc()) { + // Generate increment code in its own basic block. This is the target of + // continue statements. + Succ = addStmt(I); + } + + // Finish up the increment (or empty) block if it hasn't been already. + if (Block) { + assert(Block == Succ); + if (badCFG) + return 0; + Block = 0; + } + + // 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. + ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); + ContinueJumpTarget.block->setLoopTarget(F); + + // Loop body should end with destructor of Condition variable (if any). + addAutomaticObjDtors(ScopePos, LoopBeginScopePos, F); + + // If body is not a compound statement create implicit scope + // and add destructors. + if (!isa<CompoundStmt>(F->getBody())) + addLocalScopeAndDtors(F->getBody()); + + // Now populate the body block, and in the process create new blocks as we + // walk the body of the loop. + BodyBlock = addStmt(F->getBody()); + + if (!BodyBlock) { + // In the case of "for (...;...;...);" we can have a null BodyBlock. + // Use the continue jump target as the proxy for the body. + BodyBlock = ContinueJumpTarget.block; + } + else if (badCFG) + return 0; + } + + // Because of short-circuit evaluation, the condition of the loop can span + // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that + // evaluate the condition. + CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0; + + do { + Expr *C = F->getCond(); + + // Specially handle logical operators, which have a slightly + // more optimal CFG representation. + if (BinaryOperator *Cond = + dyn_cast_or_null<BinaryOperator>(C ? C->IgnoreParens() : 0)) + if (Cond->isLogicalOp()) { + llvm::tie(EntryConditionBlock, ExitConditionBlock) = + VisitLogicalOperator(Cond, F, BodyBlock, LoopSuccessor); + break; + } + + // The default case when not handling logical operators. + EntryConditionBlock = ExitConditionBlock = createBlock(false); + ExitConditionBlock->setTerminator(F); + + // See if this is a known constant. + TryResult KnownVal(true); + + if (C) { + // Now add the actual condition to the condition block. + // Because the condition itself may contain control-flow, new blocks may + // be created. Thus we update "Succ" after adding the condition. + Block = ExitConditionBlock; + EntryConditionBlock = addStmt(C); + + // If this block contains a condition variable, add both the condition + // variable and initializer to the CFG. + if (VarDecl *VD = F->getConditionVariable()) { + if (Expr *Init = VD->getInit()) { + autoCreateBlock(); + appendStmt(Block, F->getConditionVariableDeclStmt()); + EntryConditionBlock = addStmt(Init); + assert(Block == EntryConditionBlock); + } + } + + if (Block && badCFG) + return 0; + + KnownVal = tryEvaluateBool(C); + } + + // Add the loop body entry as a successor to the condition. + addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock); + // Link up the condition block with the code that follows the loop. (the + // false branch). + addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + + } while (false); + + // Link up the loop-back block to the entry condition block. + addSuccessor(TransitionBlock, EntryConditionBlock); + + // The condition block is the implicit successor for any code above the loop. + Succ = EntryConditionBlock; + + // If the loop contains initialization, create a new block for those + // statements. This block can also contain statements that precede the loop. + if (Stmt *I = F->getInit()) { + Block = createBlock(); + return addStmt(I); + } + + // There is no loop initialization. We are thus basically a while loop. + // NULL out Block to force lazy block construction. + Block = NULL; + Succ = EntryConditionBlock; + return EntryConditionBlock; +} + +CFGBlock *CFGBuilder::VisitMemberExpr(MemberExpr *M, AddStmtChoice asc) { + if (asc.alwaysAdd(*this, M)) { + autoCreateBlock(); + appendStmt(Block, M); + } + return Visit(M->getBase()); +} + +CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { + // Objective-C fast enumeration 'for' statements: + // http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC + // + // for ( Type newVariable in collection_expression ) { statements } + // + // becomes: + // + // prologue: + // 1. collection_expression + // T. jump to loop_entry + // loop_entry: + // 1. side-effects of element expression + // 1. ObjCForCollectionStmt [performs binding to newVariable] + // T. ObjCForCollectionStmt TB, FB [jumps to TB if newVariable != nil] + // TB: + // statements + // T. jump to loop_entry + // FB: + // what comes after + // + // and + // + // Type existingItem; + // for ( existingItem in expression ) { statements } + // + // becomes: + // + // 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 = 0; + + if (Block) { + if (badCFG) + return 0; + LoopSuccessor = Block; + Block = 0; + } else + LoopSuccessor = Succ; + + // Build the condition blocks. + CFGBlock *ExitConditionBlock = createBlock(false); + + // Set the terminator for the "exit" condition block. + ExitConditionBlock->setTerminator(S); + + // The last statement in the block should be the ObjCForCollectionStmt, which + // performs the actual binding to 'element' and determines if there are any + // more items in the collection. + appendStmt(ExitConditionBlock, S); + Block = ExitConditionBlock; + + // Walk the 'element' expression to see if there are any side-effects. We + // generate new blocks as necessary. We DON'T add the statement by default to + // the CFG unless it contains control-flow. + CFGBlock *EntryConditionBlock = Visit(S->getElement(), + AddStmtChoice::NotAlwaysAdd); + if (Block) { + if (badCFG) + return 0; + Block = 0; + } + + // The condition block is the implicit successor for the loop body as well as + // any code above the loop. + Succ = EntryConditionBlock; + + // Now create the true branch. + { + // Save the current values for Succ, continue and break targets. + SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); + SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), + save_break(BreakJumpTarget); + + // Add an intermediate block between the BodyBlock and the + // EntryConditionBlock to represent the "loop back" transition, for looping + // back to the head of the loop. + CFGBlock *LoopBackBlock = 0; + Succ = LoopBackBlock = createBlock(); + LoopBackBlock->setLoopTarget(S); + + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); + ContinueJumpTarget = JumpTarget(Succ, ScopePos); + + CFGBlock *BodyBlock = addStmt(S->getBody()); + + if (!BodyBlock) + BodyBlock = ContinueJumpTarget.block; // can happen for "for (X in Y) ;" + else if (Block) { + if (badCFG) + return 0; + } + + // This new body block is a successor to our "exit" condition block. + addSuccessor(ExitConditionBlock, BodyBlock); + } + + // Link up the condition block with the code that follows the loop. + // (the false branch). + addSuccessor(ExitConditionBlock, LoopSuccessor); + + // Now create a prologue block to contain the collection expression. + Block = createBlock(); + return addStmt(S->getCollection()); +} + +CFGBlock *CFGBuilder::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + // Inline the body. + return addStmt(S->getSubStmt()); + // TODO: consider adding cleanups for the end of @autoreleasepool scope. +} + +CFGBlock *CFGBuilder::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S) { + // FIXME: Add locking 'primitives' to CFG for @synchronized. + + // Inline the body. + CFGBlock *SyncBlock = addStmt(S->getSynchBody()); + + // The sync body starts its own basic block. This makes it a little easier + // for diagnostic clients. + if (SyncBlock) { + if (badCFG) + return 0; + + Block = 0; + Succ = SyncBlock; + } + + // Add the @synchronized to the CFG. + autoCreateBlock(); + appendStmt(Block, S); + + // Inline the sync expression. + return addStmt(S->getSynchExpr()); +} + +CFGBlock *CFGBuilder::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { + // FIXME + return NYS(); +} + +CFGBlock *CFGBuilder::VisitPseudoObjectExpr(PseudoObjectExpr *E) { + autoCreateBlock(); + + // Add the PseudoObject as the last thing. + appendStmt(Block, E); + + CFGBlock *lastBlock = Block; + + // Before that, evaluate all of the semantics in order. In + // CFG-land, that means appending them in reverse order. + for (unsigned i = E->getNumSemanticExprs(); i != 0; ) { + Expr *Semantic = E->getSemanticExpr(--i); + + // If the semantic is an opaque value, we're being asked to bind + // it to its source expression. + if (OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Semantic)) + Semantic = OVE->getSourceExpr(); + + if (CFGBlock *B = Visit(Semantic)) + lastBlock = B; + } + + return lastBlock; +} + +CFGBlock *CFGBuilder::VisitWhileStmt(WhileStmt *W) { + CFGBlock *LoopSuccessor = NULL; + + // Save local scope position because in case of condition variable ScopePos + // won't be restored when traversing AST. + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + + // Create local scope for possible condition variable. + // Store scope position for continue statement. + LocalScope::const_iterator LoopBeginScopePos = ScopePos; + if (VarDecl *VD = W->getConditionVariable()) { + addLocalScopeForVarDecl(VD); + addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); + } + + // "while" is a control-flow statement. Thus we stop processing the current + // block. + if (Block) { + if (badCFG) + return 0; + LoopSuccessor = Block; + Block = 0; + } else { + LoopSuccessor = Succ; + } + + CFGBlock *BodyBlock = 0, *TransitionBlock = 0; + + // Process the loop body. + { + assert(W->getBody()); + + // Save the current values for Block, Succ, continue and break targets. + SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); + SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), + save_break(BreakJumpTarget); + + // Create an empty block to represent the transition block for looping back + // to the head of the loop. + Succ = TransitionBlock = createBlock(false); + TransitionBlock->setLoopTarget(W); + ContinueJumpTarget = JumpTarget(Succ, LoopBeginScopePos); + + // All breaks should go to the code following the loop. + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); + + // Loop body should end with destructor of Condition variable (if any). + addAutomaticObjDtors(ScopePos, LoopBeginScopePos, W); + + // If body is not a compound statement create implicit scope + // and add destructors. + if (!isa<CompoundStmt>(W->getBody())) + addLocalScopeAndDtors(W->getBody()); + + // Create the body. The returned block is the entry to the loop body. + BodyBlock = addStmt(W->getBody()); + + if (!BodyBlock) + BodyBlock = ContinueJumpTarget.block; // can happen for "while(...) ;" + else if (Block && badCFG) + return 0; + } + + // Because of short-circuit evaluation, the condition of the loop can span + // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that + // evaluate the condition. + CFGBlock *EntryConditionBlock = 0, *ExitConditionBlock = 0; + + do { + Expr *C = W->getCond(); + + // Specially handle logical operators, which have a slightly + // more optimal CFG representation. + if (BinaryOperator *Cond = dyn_cast<BinaryOperator>(C->IgnoreParens())) + if (Cond->isLogicalOp()) { + llvm::tie(EntryConditionBlock, ExitConditionBlock) = + VisitLogicalOperator(Cond, W, BodyBlock, + LoopSuccessor); + break; + } + + // The default case when not handling logical operators. + ExitConditionBlock = createBlock(false); + ExitConditionBlock->setTerminator(W); + + // Now add the actual condition to the condition block. + // Because the condition itself may contain control-flow, new blocks may + // be created. Thus we update "Succ" after adding the condition. + Block = ExitConditionBlock; + Block = EntryConditionBlock = addStmt(C); + + // If this block contains a condition variable, add both the condition + // variable and initializer to the CFG. + if (VarDecl *VD = W->getConditionVariable()) { + if (Expr *Init = VD->getInit()) { + autoCreateBlock(); + appendStmt(Block, W->getConditionVariableDeclStmt()); + EntryConditionBlock = addStmt(Init); + assert(Block == EntryConditionBlock); + } + } + + if (Block && badCFG) + return 0; + + // See if this is a known constant. + const TryResult& KnownVal = tryEvaluateBool(C); + + // Add the loop body entry as a successor to the condition. + addSuccessor(ExitConditionBlock, KnownVal.isFalse() ? NULL : BodyBlock); + // Link up the condition block with the code that follows the loop. (the + // false branch). + addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + + } while(false); + + // Link up the loop-back block to the entry condition block. + addSuccessor(TransitionBlock, EntryConditionBlock); + + // There can be no more statements in the condition block since we loop back + // to this block. NULL out Block to force lazy creation of another block. + Block = NULL; + + // Return the condition block, which is the dominating block for the loop. + Succ = EntryConditionBlock; + return EntryConditionBlock; +} + + +CFGBlock *CFGBuilder::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) { + // FIXME: For now we pretend that @catch and the code it contains does not + // exit. + return Block; +} + +CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { + // FIXME: This isn't complete. We basically treat @throw like a return + // statement. + + // If we were in the middle of a block we stop processing that block. + if (badCFG) + return 0; + + // Create the new block. + Block = createBlock(false); + + // The Exit block is the only successor. + addSuccessor(Block, &cfg->getExit()); + + // Add the statement to the block. This may create new blocks if S contains + // control-flow (short-circuit operations). + return VisitStmt(S, AddStmtChoice::AlwaysAdd); +} + +CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) { + // If we were in the middle of a block we stop processing that block. + if (badCFG) + return 0; + + // Create the new block. + Block = createBlock(false); + + if (TryTerminatedBlock) + // The current try statement is the only successor. + addSuccessor(Block, TryTerminatedBlock); + else + // otherwise the Exit block is the only successor. + addSuccessor(Block, &cfg->getExit()); + + // Add the statement to the block. This may create new blocks if S contains + // control-flow (short-circuit operations). + return VisitStmt(T, AddStmtChoice::AlwaysAdd); +} + +CFGBlock *CFGBuilder::VisitDoStmt(DoStmt *D) { + CFGBlock *LoopSuccessor = NULL; + + // "do...while" is a control-flow statement. Thus we stop processing the + // current block. + if (Block) { + if (badCFG) + return 0; + LoopSuccessor = Block; + } else + LoopSuccessor = Succ; + + // Because of short-circuit evaluation, the condition of the loop can span + // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that + // evaluate the condition. + CFGBlock *ExitConditionBlock = createBlock(false); + CFGBlock *EntryConditionBlock = ExitConditionBlock; + + // Set the terminator for the "exit" condition block. + ExitConditionBlock->setTerminator(D); + + // Now add the actual condition to the condition block. Because the condition + // itself may contain control-flow, new blocks may be created. + if (Stmt *C = D->getCond()) { + Block = ExitConditionBlock; + EntryConditionBlock = addStmt(C); + if (Block) { + if (badCFG) + return 0; + } + } + + // The condition block is the implicit successor for the loop body. + Succ = EntryConditionBlock; + + // See if this is a known constant. + const TryResult &KnownVal = tryEvaluateBool(D->getCond()); + + // Process the loop body. + CFGBlock *BodyBlock = NULL; + { + assert(D->getBody()); + + // Save the current values for Block, Succ, and continue and break targets + SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); + SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget), + save_break(BreakJumpTarget); + + // All continues within this loop should go to the condition block + ContinueJumpTarget = JumpTarget(EntryConditionBlock, ScopePos); + + // All breaks should go to the code following the loop. + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); + + // NULL out Block to force lazy instantiation of blocks for the body. + Block = NULL; + + // If body is not a compound statement create implicit scope + // and add destructors. + if (!isa<CompoundStmt>(D->getBody())) + addLocalScopeAndDtors(D->getBody()); + + // Create the body. The returned block is the entry to the loop body. + BodyBlock = addStmt(D->getBody()); + + if (!BodyBlock) + BodyBlock = EntryConditionBlock; // can happen for "do ; while(...)" + else if (Block) { + if (badCFG) + return 0; + } + + if (!KnownVal.isFalse()) { + // Add an intermediate block between the BodyBlock and the + // ExitConditionBlock to represent the "loop back" transition. Create an + // empty block to represent the transition block for looping back to the + // head of the loop. + // FIXME: Can we do this more efficiently without adding another block? + Block = NULL; + Succ = BodyBlock; + CFGBlock *LoopBackBlock = createBlock(); + LoopBackBlock->setLoopTarget(D); + + // Add the loop body entry as a successor to the condition. + addSuccessor(ExitConditionBlock, LoopBackBlock); + } + else + addSuccessor(ExitConditionBlock, NULL); + } + + // Link up the condition block with the code that follows the loop. + // (the false branch). + addSuccessor(ExitConditionBlock, KnownVal.isTrue() ? NULL : LoopSuccessor); + + // There can be no more statements in the body block(s) since we loop back to + // the body. NULL out Block to force lazy creation of another block. + Block = NULL; + + // Return the loop body, which is the dominating block for the loop. + Succ = BodyBlock; + return BodyBlock; +} + +CFGBlock *CFGBuilder::VisitContinueStmt(ContinueStmt *C) { + // "continue" is a control-flow statement. Thus we stop processing the + // current block. + if (badCFG) + return 0; + + // Now create a new block that ends with the continue statement. + Block = createBlock(false); + Block->setTerminator(C); + + // If there is no target for the continue, then we are looking at an + // incomplete AST. This means the CFG cannot be constructed. + if (ContinueJumpTarget.block) { + addAutomaticObjDtors(ScopePos, ContinueJumpTarget.scopePosition, C); + addSuccessor(Block, ContinueJumpTarget.block); + } else + badCFG = true; + + return Block; +} + +CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, + AddStmtChoice asc) { + + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + } + + // VLA types have expressions that must be evaluated. + CFGBlock *lastBlock = Block; + + if (E->isArgumentType()) { + for (const VariableArrayType *VA =FindVA(E->getArgumentType().getTypePtr()); + VA != 0; VA = FindVA(VA->getElementType().getTypePtr())) + lastBlock = addStmt(VA->getSizeExpr()); + } + return lastBlock; +} + +/// VisitStmtExpr - Utility method to handle (nested) statement +/// expressions (a GCC extension). +CFGBlock *CFGBuilder::VisitStmtExpr(StmtExpr *SE, AddStmtChoice asc) { + if (asc.alwaysAdd(*this, SE)) { + autoCreateBlock(); + appendStmt(Block, SE); + } + return VisitCompoundStmt(SE->getSubStmt()); +} + +CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { + // "switch" is a control-flow statement. Thus we stop processing the current + // block. + CFGBlock *SwitchSuccessor = NULL; + + // Save local scope position because in case of condition variable ScopePos + // won't be restored when traversing AST. + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + + // Create local scope for possible condition variable. + // Store scope position. Add implicit destructor. + if (VarDecl *VD = Terminator->getConditionVariable()) { + LocalScope::const_iterator SwitchBeginScopePos = ScopePos; + addLocalScopeForVarDecl(VD); + addAutomaticObjDtors(ScopePos, SwitchBeginScopePos, Terminator); + } + + if (Block) { + if (badCFG) + return 0; + SwitchSuccessor = Block; + } else SwitchSuccessor = Succ; + + // Save the current "switch" context. + SaveAndRestore<CFGBlock*> save_switch(SwitchTerminatedBlock), + save_default(DefaultCaseBlock); + SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); + + // Set the "default" case to be the block after the switch statement. If the + // switch statement contains a "default:", this value will be overwritten with + // the block for that code. + DefaultCaseBlock = SwitchSuccessor; + + // Create a new block that will contain the switch statement. + SwitchTerminatedBlock = createBlock(false); + + // Now process the switch body. The code after the switch is the implicit + // successor. + Succ = SwitchSuccessor; + BreakJumpTarget = JumpTarget(SwitchSuccessor, ScopePos); + + // When visiting the body, the case statements should automatically get linked + // up to the switch. We also don't keep a pointer to the body, since all + // control-flow from the switch goes to case/default statements. + assert(Terminator->getBody() && "switch must contain a non-NULL body"); + Block = NULL; + + // For pruning unreachable case statements, save the current state + // for tracking the condition value. + SaveAndRestore<bool> save_switchExclusivelyCovered(switchExclusivelyCovered, + false); + + // Determine if the switch condition can be explicitly evaluated. + assert(Terminator->getCond() && "switch condition must be non-NULL"); + Expr::EvalResult result; + bool b = tryEvaluate(Terminator->getCond(), result); + SaveAndRestore<Expr::EvalResult*> save_switchCond(switchCond, + b ? &result : 0); + + // If body is not a compound statement create implicit scope + // and add destructors. + if (!isa<CompoundStmt>(Terminator->getBody())) + addLocalScopeAndDtors(Terminator->getBody()); + + addStmt(Terminator->getBody()); + if (Block) { + if (badCFG) + return 0; + } + + // If we have no "default:" case, the default transition is to the code + // following the switch body. Moreover, take into account if all the + // 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 + // case statements if the enumeration has no enumerators. + bool SwitchAlwaysHasSuccessor = false; + SwitchAlwaysHasSuccessor |= switchExclusivelyCovered; + SwitchAlwaysHasSuccessor |= Terminator->isAllEnumCasesCovered() && + Terminator->getSwitchCaseList(); + addSuccessor(SwitchTerminatedBlock, + SwitchAlwaysHasSuccessor ? 0 : DefaultCaseBlock); + + // Add the terminator and condition in the switch block. + SwitchTerminatedBlock->setTerminator(Terminator); + Block = SwitchTerminatedBlock; + CFGBlock *LastBlock = addStmt(Terminator->getCond()); + + // Finally, if the SwitchStmt contains a condition variable, add both the + // SwitchStmt and the condition variable initialization to the CFG. + if (VarDecl *VD = Terminator->getConditionVariable()) { + if (Expr *Init = VD->getInit()) { + autoCreateBlock(); + appendStmt(Block, Terminator->getConditionVariableDeclStmt()); + LastBlock = addStmt(Init); + } + } + + return LastBlock; +} + +static bool shouldAddCase(bool &switchExclusivelyCovered, + const Expr::EvalResult *switchCond, + const CaseStmt *CS, + ASTContext &Ctx) { + if (!switchCond) + return true; + + bool addCase = false; + + if (!switchExclusivelyCovered) { + if (switchCond->Val.isInt()) { + // Evaluate the LHS of the case value. + const llvm::APSInt &lhsInt = CS->getLHS()->EvaluateKnownConstInt(Ctx); + const llvm::APSInt &condInt = switchCond->Val.getInt(); + + if (condInt == lhsInt) { + addCase = true; + switchExclusivelyCovered = true; + } + else if (condInt < lhsInt) { + if (const Expr *RHS = CS->getRHS()) { + // Evaluate the RHS of the case value. + const llvm::APSInt &V2 = RHS->EvaluateKnownConstInt(Ctx); + if (V2 <= condInt) { + addCase = true; + switchExclusivelyCovered = true; + } + } + } + } + else + addCase = true; + } + return addCase; +} + +CFGBlock *CFGBuilder::VisitCaseStmt(CaseStmt *CS) { + // CaseStmts are essentially labels, so they are the first statement in a + // block. + CFGBlock *TopBlock = 0, *LastBlock = 0; + + if (Stmt *Sub = CS->getSubStmt()) { + // For deeply nested chains of CaseStmts, instead of doing a recursion + // (which can blow out the stack), manually unroll and create blocks + // along the way. + while (isa<CaseStmt>(Sub)) { + CFGBlock *currentBlock = createBlock(false); + currentBlock->setLabel(CS); + + if (TopBlock) + addSuccessor(LastBlock, currentBlock); + else + TopBlock = currentBlock; + + addSuccessor(SwitchTerminatedBlock, + shouldAddCase(switchExclusivelyCovered, switchCond, + CS, *Context) + ? currentBlock : 0); + + LastBlock = currentBlock; + CS = cast<CaseStmt>(Sub); + Sub = CS->getSubStmt(); + } + + addStmt(Sub); + } + + CFGBlock *CaseBlock = Block; + if (!CaseBlock) + CaseBlock = createBlock(); + + // Cases statements partition blocks, so this is the top of the basic block we + // were processing (the "case XXX:" is the label). + CaseBlock->setLabel(CS); + + if (badCFG) + return 0; + + // Add this block to the list of successors for the block with the switch + // statement. + assert(SwitchTerminatedBlock); + addSuccessor(SwitchTerminatedBlock, + shouldAddCase(switchExclusivelyCovered, switchCond, + CS, *Context) + ? CaseBlock : 0); + + // We set Block to NULL to allow lazy creation of a new block (if necessary) + Block = NULL; + + if (TopBlock) { + addSuccessor(LastBlock, CaseBlock); + Succ = TopBlock; + } else { + // This block is now the implicit successor of other blocks. + Succ = CaseBlock; + } + + return Succ; +} + +CFGBlock *CFGBuilder::VisitDefaultStmt(DefaultStmt *Terminator) { + if (Terminator->getSubStmt()) + addStmt(Terminator->getSubStmt()); + + DefaultCaseBlock = Block; + + if (!DefaultCaseBlock) + DefaultCaseBlock = createBlock(); + + // Default statements partition blocks, so this is the top of the basic block + // we were processing (the "default:" is the label). + DefaultCaseBlock->setLabel(Terminator); + + if (badCFG) + return 0; + + // Unlike case statements, we don't add the default block to the successors + // for the switch statement immediately. This is done when we finish + // processing the switch statement. This allows for the default case + // (including a fall-through to the code after the switch statement) to always + // be the last successor of a switch-terminated block. + + // We set Block to NULL to allow lazy creation of a new block (if necessary) + Block = NULL; + + // This block is now the implicit successor of other blocks. + Succ = DefaultCaseBlock; + + return DefaultCaseBlock; +} + +CFGBlock *CFGBuilder::VisitCXXTryStmt(CXXTryStmt *Terminator) { + // "try"/"catch" is a control-flow statement. Thus we stop processing the + // current block. + CFGBlock *TrySuccessor = NULL; + + if (Block) { + if (badCFG) + return 0; + TrySuccessor = Block; + } else TrySuccessor = Succ; + + CFGBlock *PrevTryTerminatedBlock = 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); + + bool HasCatchAll = false; + for (unsigned h = 0; h <Terminator->getNumHandlers(); ++h) { + // The code after the try is the implicit successor. + Succ = TrySuccessor; + CXXCatchStmt *CS = Terminator->getHandler(h); + if (CS->getExceptionDecl() == 0) { + HasCatchAll = true; + } + Block = NULL; + CFGBlock *CatchBlock = VisitCXXCatchStmt(CS); + if (CatchBlock == 0) + return 0; + // Add this block to the list of successors for the block with the try + // statement. + addSuccessor(NewTryTerminatedBlock, CatchBlock); + } + if (!HasCatchAll) { + if (PrevTryTerminatedBlock) + addSuccessor(NewTryTerminatedBlock, PrevTryTerminatedBlock); + else + addSuccessor(NewTryTerminatedBlock, &cfg->getExit()); + } + + // The code after the try is the implicit successor. + Succ = TrySuccessor; + + // Save the current "try" context. + SaveAndRestore<CFGBlock*> save_try(TryTerminatedBlock, NewTryTerminatedBlock); + cfg->addTryDispatchBlock(TryTerminatedBlock); + + assert(Terminator->getTryBlock() && "try must contain a non-NULL body"); + Block = NULL; + return addStmt(Terminator->getTryBlock()); +} + +CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) { + // CXXCatchStmt 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); + + // Create local scope for possible exception variable. + // Store scope position. Add implicit destructor. + if (VarDecl *VD = CS->getExceptionDecl()) { + LocalScope::const_iterator BeginScopePos = ScopePos; + addLocalScopeForVarDecl(VD); + addAutomaticObjDtors(ScopePos, BeginScopePos, CS); + } + + if (CS->getHandlerBlock()) + addStmt(CS->getHandlerBlock()); + + CFGBlock *CatchBlock = Block; + if (!CatchBlock) + CatchBlock = createBlock(); + + // CXXCatchStmt is more than just a label. They have semantic meaning + // as well, as they implicitly "initialize" the catch variable. Add + // it to the CFG as a CFGElement so that the control-flow of these + // semantics gets captured. + appendStmt(CatchBlock, CS); + + // Also add the CXXCatchStmt as a label, to mirror handling of regular + // labels. + CatchBlock->setLabel(CS); + + // Bail out if the CFG is bad. + if (badCFG) + return 0; + + // We set Block to NULL to allow lazy creation of a new block (if necessary) + Block = NULL; + + return CatchBlock; +} + +CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { + // C++0x for-range statements are specified as [stmt.ranged]: + // + // { + // auto && __range = range-init; + // for ( auto __begin = begin-expr, + // __end = end-expr; + // __begin != __end; + // ++__begin ) { + // for-range-declaration = *__begin; + // statement + // } + // } + + // Save local scope position before the addition of the implicit variables. + SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos); + + // Create local scopes and destructors for range, begin and end variables. + if (Stmt *Range = S->getRangeStmt()) + addLocalScopeForStmt(Range); + if (Stmt *BeginEnd = S->getBeginEndStmt()) + addLocalScopeForStmt(BeginEnd); + addAutomaticObjDtors(ScopePos, save_scope_pos.get(), S); + + LocalScope::const_iterator ContinueScopePos = ScopePos; + + // "for" is a control-flow statement. Thus we stop processing the current + // block. + CFGBlock *LoopSuccessor = NULL; + if (Block) { + if (badCFG) + return 0; + LoopSuccessor = Block; + } else + LoopSuccessor = Succ; + + // Save the current value for the break targets. + // All breaks should go to the code following the loop. + SaveAndRestore<JumpTarget> save_break(BreakJumpTarget); + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); + + // The block for the __begin != __end expression. + CFGBlock *ConditionBlock = createBlock(false); + ConditionBlock->setTerminator(S); + + // Now add the actual condition to the condition block. + if (Expr *C = S->getCond()) { + Block = ConditionBlock; + CFGBlock *BeginConditionBlock = addStmt(C); + if (badCFG) + return 0; + assert(BeginConditionBlock == ConditionBlock && + "condition block in for-range was unexpectedly complex"); + (void)BeginConditionBlock; + } + + // The condition block is the implicit successor for the loop body as well as + // any code above the loop. + Succ = ConditionBlock; + + // See if this is a known constant. + TryResult KnownVal(true); + + if (S->getCond()) + KnownVal = tryEvaluateBool(S->getCond()); + + // Now create the loop body. + { + assert(S->getBody()); + + // Save the current values for Block, Succ, and continue targets. + SaveAndRestore<CFGBlock*> save_Block(Block), save_Succ(Succ); + SaveAndRestore<JumpTarget> save_continue(ContinueJumpTarget); + + // Generate increment code in its own basic block. This is the target of + // continue statements. + Block = 0; + Succ = addStmt(S->getInc()); + ContinueJumpTarget = JumpTarget(Succ, ContinueScopePos); + + // 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. + ContinueJumpTarget.block->setLoopTarget(S); + + // Finish up the increment block and prepare to start the loop body. + assert(Block); + if (badCFG) + return 0; + Block = 0; + + + // Add implicit scope and dtors for loop variable. + addLocalScopeAndDtors(S->getLoopVarStmt()); + + // Populate a new block to contain the loop body and loop variable. + addStmt(S->getBody()); + if (badCFG) + return 0; + CFGBlock *LoopVarStmtBlock = addStmt(S->getLoopVarStmt()); + if (badCFG) + return 0; + + // This new body block is a successor to our condition block. + addSuccessor(ConditionBlock, KnownVal.isFalse() ? 0 : LoopVarStmtBlock); + } + + // Link up the condition block with the code that follows the loop (the + // false branch). + addSuccessor(ConditionBlock, KnownVal.isTrue() ? 0 : LoopSuccessor); + + // Add the initialization statements. + Block = createBlock(); + addStmt(S->getBeginEndStmt()); + return addStmt(S->getRangeStmt()); +} + +CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E, + AddStmtChoice asc) { + if (BuildOpts.AddTemporaryDtors) { + // If adding implicit destructors visit the full expression for adding + // destructors of temporaries. + VisitForTemporaryDtors(E->getSubExpr()); + + // Full expression has to be added as CFGStmt so it will be sequenced + // before destructors of it's temporaries. + asc = asc.withAlwaysAdd(true); + } + return Visit(E->getSubExpr(), asc); +} + +CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + + // We do not want to propagate the AlwaysAdd property. + asc = asc.withAlwaysAdd(false); + } + return Visit(E->getSubExpr(), asc); +} + +CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, + AddStmtChoice asc) { + autoCreateBlock(); + appendStmt(Block, C); + + return VisitChildren(C); +} + + +CFGBlock *CFGBuilder::VisitCXXDeleteExpr(CXXDeleteExpr *DE, + AddStmtChoice asc) { + autoCreateBlock(); + appendStmt(Block, DE); + QualType DTy = DE->getDestroyedType(); + DTy = DTy.getNonReferenceType(); + CXXRecordDecl *RD = Context->getBaseElementType(DTy)->getAsCXXRecordDecl(); + if (RD) { + if (RD->isCompleteDefinition() && !RD->hasTrivialDestructor()) + appendDeleteDtor(Block, RD, DE); + } + + return VisitChildren(DE); +} + +CFGBlock *CFGBuilder::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + // We do not want to propagate the AlwaysAdd property. + asc = asc.withAlwaysAdd(false); + } + return Visit(E->getSubExpr(), asc); +} + +CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C, + AddStmtChoice asc) { + autoCreateBlock(); + appendStmt(Block, C); + return VisitChildren(C); +} + +CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E, + AddStmtChoice asc) { + if (asc.alwaysAdd(*this, E)) { + autoCreateBlock(); + appendStmt(Block, E); + } + return Visit(E->getSubExpr(), AddStmtChoice()); +} + +CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) { + // Lazily create the indirect-goto dispatch block if there isn't one already. + CFGBlock *IBlock = cfg->getIndirectGotoBlock(); + + if (!IBlock) { + IBlock = createBlock(false); + cfg->setIndirectGotoBlock(IBlock); + } + + // IndirectGoto is a control-flow statement. Thus we stop processing the + // current block and create a new one. + if (badCFG) + return 0; + + Block = createBlock(false); + Block->setTerminator(I); + addSuccessor(Block, IBlock); + return addStmt(I->getTarget()); +} + +CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary) { + assert(BuildOpts.AddImplicitDtors && BuildOpts.AddTemporaryDtors); + +tryAgain: + if (!E) { + badCFG = true; + return NULL; + } + switch (E->getStmtClass()) { + default: + return VisitChildrenForTemporaryDtors(E); + + case Stmt::BinaryOperatorClass: + return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E)); + + case Stmt::CXXBindTemporaryExprClass: + return VisitCXXBindTemporaryExprForTemporaryDtors( + cast<CXXBindTemporaryExpr>(E), BindToTemporary); + + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + return VisitConditionalOperatorForTemporaryDtors( + cast<AbstractConditionalOperator>(E), BindToTemporary); + + case Stmt::ImplicitCastExprClass: + // For implicit cast we want BindToTemporary to be passed further. + E = cast<CastExpr>(E)->getSubExpr(); + goto tryAgain; + + case Stmt::ParenExprClass: + E = cast<ParenExpr>(E)->getSubExpr(); + goto tryAgain; + + case Stmt::MaterializeTemporaryExprClass: + E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(); + goto tryAgain; + } +} + +CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) { + // When visiting children for destructors we want to visit them in reverse + // order that they will appear in the CFG. Because the CFG is built + // bottom-up, this means we visit them in their natural order, which + // reverses them in the CFG. + CFGBlock *B = Block; + for (Stmt::child_range I = E->children(); I; ++I) { + if (Stmt *Child = *I) + if (CFGBlock *R = VisitForTemporaryDtors(Child)) + B = R; + } + return B; +} + +CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E) { + if (E->isLogicalOp()) { + // Destructors for temporaries in LHS expression should be called after + // those for RHS expression. Even if this will unnecessarily create a block, + // this block will be used at least by the full expression. + autoCreateBlock(); + CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getLHS()); + if (badCFG) + return NULL; + + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); + + if (RHSBlock) { + if (badCFG) + return NULL; + + // If RHS expression did produce destructors we need to connect created + // blocks to CFG in same manner as for binary operator itself. + CFGBlock *LHSBlock = createBlock(false); + LHSBlock->setTerminator(CFGTerminator(E, true)); + + // For binary operator LHS block is before RHS in list of predecessors + // of ConfluenceBlock. + std::reverse(ConfluenceBlock->pred_begin(), + ConfluenceBlock->pred_end()); + + // See if this is a known constant. + TryResult KnownVal = tryEvaluateBool(E->getLHS()); + if (KnownVal.isKnown() && (E->getOpcode() == BO_LOr)) + KnownVal.negate(); + + // Link LHSBlock with RHSBlock exactly the same way as for binary operator + // itself. + if (E->getOpcode() == BO_LOr) { + addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); + addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + } else { + assert (E->getOpcode() == BO_LAnd); + addSuccessor(LHSBlock, KnownVal.isFalse() ? NULL : RHSBlock); + addSuccessor(LHSBlock, KnownVal.isTrue() ? NULL : ConfluenceBlock); + } + + Block = LHSBlock; + return LHSBlock; + } + + Block = ConfluenceBlock; + return ConfluenceBlock; + } + + if (E->isAssignmentOp()) { + // For assignment operator (=) LHS expression is visited + // before RHS expression. For destructors visit them in reverse order. + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); + CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS()); + return LHSBlock ? LHSBlock : RHSBlock; + } + + // For any other binary operator RHS expression is visited before + // LHS expression (order of children). For destructors visit them in reverse + // order. + CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS()); + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS()); + return RHSBlock ? RHSBlock : LHSBlock; +} + +CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors( + CXXBindTemporaryExpr *E, bool BindToTemporary) { + // First add destructors for temporaries in subexpression. + CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr()); + if (!BindToTemporary) { + // If lifetime of temporary is not prolonged (by assigning to constant + // reference) add destructor for it. + + // If the destructor is marked as a no-return destructor, we need to create + // a new block for the destructor which does not have as a successor + // anything built thus far. Control won't flow out of this block. + const CXXDestructorDecl *Dtor = E->getTemporary()->getDestructor(); + if (Dtor->isNoReturn()) + Block = createNoReturnBlock(); + else + autoCreateBlock(); + + appendTemporaryDtor(Block, E); + B = Block; + } + return B; +} + +CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors( + AbstractConditionalOperator *E, bool BindToTemporary) { + // First add destructors for condition expression. Even if this will + // unnecessarily create a block, this block will be used at least by the full + // expression. + autoCreateBlock(); + CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getCond()); + if (badCFG) + return NULL; + if (BinaryConditionalOperator *BCO + = dyn_cast<BinaryConditionalOperator>(E)) { + ConfluenceBlock = VisitForTemporaryDtors(BCO->getCommon()); + if (badCFG) + return NULL; + } + + // Try to add block with destructors for LHS expression. + CFGBlock *LHSBlock = NULL; + Succ = ConfluenceBlock; + Block = NULL; + LHSBlock = VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary); + if (badCFG) + return NULL; + + // Try to add block with destructors for RHS expression; + Succ = ConfluenceBlock; + Block = NULL; + CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getFalseExpr(), + BindToTemporary); + if (badCFG) + return NULL; + + if (!RHSBlock && !LHSBlock) { + // If neither LHS nor RHS expression had temporaries to destroy don't create + // more blocks. + Block = ConfluenceBlock; + return Block; + } + + Block = createBlock(false); + Block->setTerminator(CFGTerminator(E, true)); + + // See if this is a known constant. + const TryResult &KnownVal = tryEvaluateBool(E->getCond()); + + if (LHSBlock) { + addSuccessor(Block, KnownVal.isFalse() ? NULL : LHSBlock); + } else if (KnownVal.isFalse()) { + addSuccessor(Block, NULL); + } else { + addSuccessor(Block, ConfluenceBlock); + std::reverse(ConfluenceBlock->pred_begin(), ConfluenceBlock->pred_end()); + } + + if (!RHSBlock) + RHSBlock = ConfluenceBlock; + addSuccessor(Block, KnownVal.isTrue() ? NULL : RHSBlock); + + 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. +CFGBlock *CFG::createBlock() { + bool first_block = begin() == end(); + + // Create the block. + CFGBlock *Mem = getAllocator().Allocate<CFGBlock>(); + new (Mem) CFGBlock(NumBlockIDs++, BlkBVC, this); + Blocks.push_back(Mem, BlkBVC); + + // If this is the first block, set it as the Entry and Exit. + if (first_block) + Entry = Exit = &back(); + + // Return the block. + return &back(); +} + +/// buildCFG - Constructs a CFG from an AST. Ownership of the returned +/// CFG is returned to the caller. +CFG* CFG::buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, + const BuildOptions &BO) { + CFGBuilder Builder(C, BO); + return Builder.buildCFG(D, Statement); +} + +const CXXDestructorDecl * +CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { + switch (getKind()) { + case CFGElement::Statement: + case CFGElement::Initializer: + llvm_unreachable("getDestructorDecl should only be used with " + "ImplicitDtors"); + case CFGElement::AutomaticObjectDtor: { + const VarDecl *var = castAs<CFGAutomaticObjDtor>().getVarDecl(); + QualType ty = var->getType(); + ty = ty.getNonReferenceType(); + while (const ArrayType *arrayType = astContext.getAsArrayType(ty)) { + ty = arrayType->getElementType(); + } + const RecordType *recordType = ty->getAs<RecordType>(); + const CXXRecordDecl *classDecl = + cast<CXXRecordDecl>(recordType->getDecl()); + return classDecl->getDestructor(); + } + case CFGElement::DeleteDtor: { + const CXXDeleteExpr *DE = castAs<CFGDeleteDtor>().getDeleteExpr(); + QualType DTy = DE->getDestroyedType(); + DTy = DTy.getNonReferenceType(); + const CXXRecordDecl *classDecl = + astContext.getBaseElementType(DTy)->getAsCXXRecordDecl(); + return classDecl->getDestructor(); + } + case CFGElement::TemporaryDtor: { + const CXXBindTemporaryExpr *bindExpr = + castAs<CFGTemporaryDtor>().getBindTemporaryExpr(); + const CXXTemporary *temp = bindExpr->getTemporary(); + return temp->getDestructor(); + } + case CFGElement::BaseDtor: + case CFGElement::MemberDtor: + + // Not yet supported. + return 0; + } + llvm_unreachable("getKind() returned bogus value"); +} + +bool CFGImplicitDtor::isNoReturn(ASTContext &astContext) const { + if (const CXXDestructorDecl *DD = getDestructorDecl(astContext)) + return DD->isNoReturn(); + return false; +} + +//===----------------------------------------------------------------------===// +// Filtered walking of the CFG. +//===----------------------------------------------------------------------===// + +bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F, + const CFGBlock *From, const CFGBlock *To) { + + if (To && F.IgnoreDefaultsWithCoveredEnums) { + // If the 'To' has no label or is labeled but the label isn't a + // CaseStmt then filter this edge. + if (const SwitchStmt *S = + dyn_cast_or_null<SwitchStmt>(From->getTerminator().getStmt())) { + if (S->isAllEnumCasesCovered()) { + const Stmt *L = To->getLabel(); + if (!L || !isa<CaseStmt>(L)) + return true; + } + } + } + + return false; +} + +//===----------------------------------------------------------------------===// +// CFG pretty printing +//===----------------------------------------------------------------------===// + +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; + StmtMapTy StmtMap; + DeclMapTy DeclMap; + signed currentBlock; + unsigned currStmt; + const LangOptions &LangOpts; +public: + + StmtPrinterHelper(const CFG* cfg, const LangOptions &LO) + : currentBlock(0), currStmt(0), 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() ; + BI != BEnd; ++BI, ++j ) { + if (Optional<CFGStmt> SE = BI->getAs<CFGStmt>()) { + const Stmt *stmt= SE->getStmt(); + std::pair<unsigned, unsigned> P((*I)->getBlockID(), j); + StmtMap[stmt] = P; + + switch (stmt->getStmtClass()) { + case Stmt::DeclStmtClass: + DeclMap[cast<DeclStmt>(stmt)->getSingleDecl()] = P; + break; + case Stmt::IfStmtClass: { + const VarDecl *var = cast<IfStmt>(stmt)->getConditionVariable(); + if (var) + DeclMap[var] = P; + break; + } + case Stmt::ForStmtClass: { + const VarDecl *var = cast<ForStmt>(stmt)->getConditionVariable(); + if (var) + DeclMap[var] = P; + break; + } + case Stmt::WhileStmtClass: { + const VarDecl *var = + cast<WhileStmt>(stmt)->getConditionVariable(); + if (var) + DeclMap[var] = P; + break; + } + case Stmt::SwitchStmtClass: { + const VarDecl *var = + cast<SwitchStmt>(stmt)->getConditionVariable(); + if (var) + DeclMap[var] = P; + break; + } + case Stmt::CXXCatchStmtClass: { + const VarDecl *var = + cast<CXXCatchStmt>(stmt)->getExceptionDecl(); + if (var) + DeclMap[var] = P; + break; + } + default: + break; + } + } + } + } + } + + + virtual ~StmtPrinterHelper() {} + + const LangOptions &getLangOpts() const { return LangOpts; } + void setBlockID(signed i) { currentBlock = i; } + void setStmtID(unsigned i) { currStmt = i; } + + virtual bool handledStmt(Stmt *S, raw_ostream &OS) { + StmtMapTy::iterator I = StmtMap.find(S); + + if (I == StmtMap.end()) + return false; + + if (currentBlock >= 0 && I->second.first == (unsigned) currentBlock + && I->second.second == currStmt) { + return false; + } + + OS << "[B" << I->second.first << "." << I->second.second << "]"; + return true; + } + + bool handleDecl(const Decl *D, raw_ostream &OS) { + DeclMapTy::iterator I = DeclMap.find(D); + + if (I == DeclMap.end()) + return false; + + if (currentBlock >= 0 && I->second.first == (unsigned) currentBlock + && I->second.second == currStmt) { + return false; + } + + OS << "[B" << I->second.first << "." << I->second.second << "]"; + return true; + } +}; +} // end anonymous namespace + + +namespace { +class CFGBlockTerminatorPrint + : 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) {} + + void VisitIfStmt(IfStmt *I) { + OS << "if "; + I->getCond()->printPretty(OS,Helper,Policy); + } + + // Default case. + void VisitStmt(Stmt *Terminator) { + Terminator->printPretty(OS, Helper, Policy); + } + + void VisitDeclStmt(DeclStmt *DS) { + VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + OS << "static init " << VD->getName(); + } + + void VisitForStmt(ForStmt *F) { + OS << "for (" ; + if (F->getInit()) + OS << "..."; + OS << "; "; + if (Stmt *C = F->getCond()) + C->printPretty(OS, Helper, Policy); + OS << "; "; + if (F->getInc()) + OS << "..."; + OS << ")"; + } + + void VisitWhileStmt(WhileStmt *W) { + OS << "while " ; + if (Stmt *C = W->getCond()) + C->printPretty(OS, Helper, Policy); + } + + void VisitDoStmt(DoStmt *D) { + OS << "do ... while "; + if (Stmt *C = D->getCond()) + C->printPretty(OS, Helper, Policy); + } + + void VisitSwitchStmt(SwitchStmt *Terminator) { + OS << "switch "; + Terminator->getCond()->printPretty(OS, Helper, Policy); + } + + void VisitCXXTryStmt(CXXTryStmt *CS) { + OS << "try ..."; + } + + void VisitAbstractConditionalOperator(AbstractConditionalOperator* C) { + C->getCond()->printPretty(OS, Helper, Policy); + OS << " ? ... : ..."; + } + + void VisitChooseExpr(ChooseExpr *C) { + OS << "__builtin_choose_expr( "; + C->getCond()->printPretty(OS, Helper, Policy); + OS << " )"; + } + + void VisitIndirectGotoStmt(IndirectGotoStmt *I) { + OS << "goto *"; + I->getTarget()->printPretty(OS, Helper, Policy); + } + + void VisitBinaryOperator(BinaryOperator* B) { + if (!B->isLogicalOp()) { + VisitExpr(B); + return; + } + + B->getLHS()->printPretty(OS, Helper, Policy); + + switch (B->getOpcode()) { + case BO_LOr: + OS << " || ..."; + return; + case BO_LAnd: + OS << " && ..."; + return; + default: + llvm_unreachable("Invalid logical operator."); + } + } + + void VisitExpr(Expr *E) { + E->printPretty(OS, Helper, Policy); + } +}; +} // end anonymous namespace + +static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper, + const CFGElement &E) { + if (Optional<CFGStmt> CS = E.getAs<CFGStmt>()) { + const Stmt *S = CS->getStmt(); + + // special printing for statement-expressions. + if (const StmtExpr *SE = dyn_cast<StmtExpr>(S)) { + const CompoundStmt *Sub = SE->getSubStmt(); + + if (Sub->children()) { + OS << "({ ... ; "; + Helper.handledStmt(*SE->getSubStmt()->body_rbegin(),OS); + OS << " })\n"; + return; + } + } + // special printing for comma expressions. + if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { + if (B->getOpcode() == BO_Comma) { + OS << "... , "; + Helper.handledStmt(B->getRHS(),OS); + OS << '\n'; + return; + } + } + S->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); + + if (isa<CXXOperatorCallExpr>(S)) { + OS << " (OperatorCall)"; + } + else if (isa<CXXBindTemporaryExpr>(S)) { + OS << " (BindTemporary)"; + } + else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) { + OS << " (CXXConstructExpr, " << CCE->getType().getAsString() << ")"; + } + else if (const CastExpr *CE = dyn_cast<CastExpr>(S)) { + OS << " (" << CE->getStmtClassName() << ", " + << CE->getCastKindName() + << ", " << CE->getType().getAsString() + << ")"; + } + + // 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()) + OS << I->getBaseClass()->getAsCXXRecordDecl()->getName(); + else if (I->isDelegatingInitializer()) + OS << I->getTypeSourceInfo()->getType()->getAsCXXRecordDecl()->getName(); + else OS << I->getAnyMember()->getName(); + + OS << "("; + if (Expr *IE = I->getInit()) + IE->printPretty(OS, &Helper, PrintingPolicy(Helper.getLangOpts())); + OS << ")"; + + if (I->isBaseInitializer()) + OS << " (Base initializer)\n"; + 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(); + Helper.handleDecl(VD, OS); + + const Type* T = VD->getType().getTypePtr(); + if (const ReferenceType* RT = T->getAs<ReferenceType>()) + T = RT->getPointeeType().getTypePtr(); + T = T->getBaseElementTypeUnsafe(); + + OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; + OS << " (Implicit destructor)\n"; + + } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) { + const CXXRecordDecl *RD = DE->getCXXRecordDecl(); + if (!RD) + return; + CXXDeleteExpr *DelExpr = + const_cast<CXXDeleteExpr*>(DE->getDeleteExpr()); + Helper.handledStmt(cast<Stmt>(DelExpr->getArgument()), OS); + OS << "->~" << RD->getName().str() << "()"; + OS << " (Implicit destructor)\n"; + } else if (Optional<CFGBaseDtor> BE = E.getAs<CFGBaseDtor>()) { + 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 << "~"; + BT->getType().print(OS, PrintingPolicy(Helper.getLangOpts())); + OS << "() (Temporary object destructor)\n"; + } +} + +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. + if (ShowColors) + OS.changeColor(raw_ostream::YELLOW, true); + + OS << "\n [B" << B.getBlockID(); + + if (&B == &cfg->getEntry()) + OS << " (ENTRY)]\n"; + else if (&B == &cfg->getExit()) + OS << " (EXIT)]\n"; + else if (&B == cfg->getIndirectGotoBlock()) + OS << " (INDIRECT GOTO DISPATCH)]\n"; + else + OS << "]\n"; + + if (ShowColors) + OS.resetColor(); + + // Print the label of this block. + if (Stmt *Label = const_cast<Stmt*>(B.getLabel())) { + + if (print_edges) + OS << " "; + + if (LabelStmt *L = dyn_cast<LabelStmt>(Label)) + OS << L->getName(); + else if (CaseStmt *C = dyn_cast<CaseStmt>(Label)) { + OS << "case "; + C->getLHS()->printPretty(OS, &Helper, + PrintingPolicy(Helper.getLangOpts())); + if (C->getRHS()) { + OS << " ... "; + C->getRHS()->printPretty(OS, &Helper, + PrintingPolicy(Helper.getLangOpts())); + } + } else if (isa<DefaultStmt>(Label)) + OS << "default"; + else if (CXXCatchStmt *CS = dyn_cast<CXXCatchStmt>(Label)) { + OS << "catch ("; + if (CS->getExceptionDecl()) + CS->getExceptionDecl()->print(OS, PrintingPolicy(Helper.getLangOpts()), + 0); + else + OS << "..."; + OS << ")"; + + } else + llvm_unreachable("Invalid label statement in CFGBlock."); + + OS << ":\n"; + } + + // Iterate through the statements in the block and print them. + unsigned j = 1; + + 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 << " "; + + OS << llvm::format("%3d", j) << ": "; + + Helper.setStmtID(j); + + print_elem(OS, Helper, *I); + } + + // Print the terminator of this block. + if (B.getTerminator()) { + if (ShowColors) + OS.changeColor(raw_ostream::GREEN); + + OS << " T: "; + + Helper.setBlockID(-1); + + PrintingPolicy PP(Helper.getLangOpts()); + CFGBlockTerminatorPrint TPrinter(OS, &Helper, PP); + TPrinter.Visit(const_cast<Stmt*>(B.getTerminator().getStmt())); + OS << '\n'; + + if (ShowColors) + OS.resetColor(); + } + + if (print_edges) { + // Print the predecessors of this block. + if (!B.pred_empty()) { + const raw_ostream::Colors Color = raw_ostream::BLUE; + if (ShowColors) + OS.changeColor(Color); + OS << " Preds " ; + if (ShowColors) + OS.resetColor(); + OS << '(' << B.pred_size() << "):"; + unsigned i = 0; + + if (ShowColors) + OS.changeColor(Color); + + for (CFGBlock::const_pred_iterator I = B.pred_begin(), E = B.pred_end(); + I != E; ++I, ++i) { + + if (i % 10 == 8) + OS << "\n "; + + OS << " B" << (*I)->getBlockID(); + } + + if (ShowColors) + OS.resetColor(); + + OS << '\n'; + } + + // Print the successors of this block. + if (!B.succ_empty()) { + const raw_ostream::Colors Color = raw_ostream::MAGENTA; + if (ShowColors) + OS.changeColor(Color); + OS << " Succs "; + if (ShowColors) + OS.resetColor(); + OS << '(' << B.succ_size() << "):"; + unsigned i = 0; + + if (ShowColors) + OS.changeColor(Color); + + for (CFGBlock::const_succ_iterator I = B.succ_begin(), E = B.succ_end(); + I != E; ++I, ++i) { + + if (i % 10 == 8) + OS << "\n "; + + if (*I) + OS << " B" << (*I)->getBlockID(); + else + OS << " NULL"; + } + + if (ShowColors) + OS.resetColor(); + OS << '\n'; + } + } +} + + +/// 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); +} + +/// print - A simple pretty printer of a CFG that outputs to an ostream. +void CFG::print(raw_ostream &OS, const LangOptions &LO, bool ShowColors) const { + StmtPrinterHelper Helper(this, LO); + + // Print the entry block. + print_block(OS, this, getEntry(), Helper, true, ShowColors); + + // Iterate through the CFGBlocks and print them one by one. + for (const_iterator I = Blocks.begin(), E = Blocks.end() ; I != E ; ++I) { + // Skip the entry block, because we already printed it. + if (&(**I) == &getEntry() || &(**I) == &getExit()) + continue; + + print_block(OS, this, **I, Helper, true, ShowColors); + } + + // Print the exit block. + print_block(OS, this, getExit(), Helper, true, ShowColors); + OS << '\n'; + OS.flush(); +} + +/// dump - A simply pretty printer of a CFGBlock that outputs to stderr. +void CFGBlock::dump(const CFG* cfg, const LangOptions &LO, + bool ShowColors) const { + print(llvm::errs(), cfg, LO, ShowColors); +} + +/// print - A simple pretty printer of a CFGBlock that outputs to an ostream. +/// Generally this will only be called from CFG::print. +void CFGBlock::print(raw_ostream &OS, const CFG* cfg, + const LangOptions &LO, bool ShowColors) const { + StmtPrinterHelper Helper(cfg, LO); + print_block(OS, cfg, *this, Helper, true, ShowColors); + OS << '\n'; +} + +/// printTerminator - A simple pretty printer of the terminator of a CFGBlock. +void CFGBlock::printTerminator(raw_ostream &OS, + const LangOptions &LO) const { + CFGBlockTerminatorPrint TPrinter(OS, NULL, PrintingPolicy(LO)); + TPrinter.Visit(const_cast<Stmt*>(getTerminator().getStmt())); +} + +Stmt *CFGBlock::getTerminatorCondition() { + Stmt *Terminator = this->Terminator; + if (!Terminator) + return NULL; + + Expr *E = NULL; + + switch (Terminator->getStmtClass()) { + default: + break; + + case Stmt::CXXForRangeStmtClass: + E = cast<CXXForRangeStmt>(Terminator)->getCond(); + break; + + case Stmt::ForStmtClass: + E = cast<ForStmt>(Terminator)->getCond(); + break; + + case Stmt::WhileStmtClass: + E = cast<WhileStmt>(Terminator)->getCond(); + break; + + case Stmt::DoStmtClass: + E = cast<DoStmt>(Terminator)->getCond(); + break; + + case Stmt::IfStmtClass: + E = cast<IfStmt>(Terminator)->getCond(); + break; + + case Stmt::ChooseExprClass: + E = cast<ChooseExpr>(Terminator)->getCond(); + break; + + case Stmt::IndirectGotoStmtClass: + E = cast<IndirectGotoStmt>(Terminator)->getTarget(); + break; + + case Stmt::SwitchStmtClass: + E = cast<SwitchStmt>(Terminator)->getCond(); + break; + + case Stmt::BinaryConditionalOperatorClass: + E = cast<BinaryConditionalOperator>(Terminator)->getCond(); + break; + + case Stmt::ConditionalOperatorClass: + E = cast<ConditionalOperator>(Terminator)->getCond(); + break; + + case Stmt::BinaryOperatorClass: // '&&' and '||' + E = cast<BinaryOperator>(Terminator)->getLHS(); + break; + + case Stmt::ObjCForCollectionStmtClass: + return Terminator; + } + + return E ? E->IgnoreParens() : NULL; +} + +//===----------------------------------------------------------------------===// +// CFG Graphviz Visualization +//===----------------------------------------------------------------------===// + + +#ifndef NDEBUG +static StmtPrinterHelper* GraphHelper; +#endif + +void CFG::viewCFG(const LangOptions &LO) const { +#ifndef NDEBUG + StmtPrinterHelper H(this, LO); + GraphHelper = &H; + llvm::ViewGraph(this,"CFG"); + GraphHelper = NULL; +#endif +} + +namespace llvm { +template<> +struct DOTGraphTraits<const CFG*> : public DefaultDOTGraphTraits { + + 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); + print_block(Out,Graph, *Node, *GraphHelper, false, false); + std::string& OutStr = Out.str(); + + if (OutStr[0] == '\n') OutStr.erase(OutStr.begin()); + + // Process string output to make it nicer... + for (unsigned i = 0; i != OutStr.length(); ++i) + if (OutStr[i] == '\n') { // Left justify + OutStr[i] = '\\'; + OutStr.insert(OutStr.begin()+i+1, 'l'); + } + + return OutStr; +#else + return ""; +#endif + } +}; +} // end namespace llvm diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp new file mode 100644 index 000000000000..492e66fe8172 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/CFGReachabilityAnalysis.cpp @@ -0,0 +1,75 @@ +//==- CFGReachabilityAnalysis.cpp - Basic reachability analysis --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a flow-sensitive, (mostly) path-insensitive reachability +// analysis based on Clang's CFGs. Clients can query if a given basic block +// is reachable within the CFG. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallVector.h" +#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h" +#include "clang/Analysis/CFG.h" + +using namespace clang; + +CFGReverseBlockReachabilityAnalysis::CFGReverseBlockReachabilityAnalysis(const CFG &cfg) + : analyzed(cfg.getNumBlockIDs(), false) {} + +bool CFGReverseBlockReachabilityAnalysis::isReachable(const CFGBlock *Src, + const CFGBlock *Dst) { + + const unsigned DstBlockID = Dst->getBlockID(); + + // If we haven't analyzed the destination node, run the analysis now + if (!analyzed[DstBlockID]) { + mapReachability(Dst); + analyzed[DstBlockID] = true; + } + + // Return the cached result + return reachable[DstBlockID][Src->getBlockID()]; +} + +// Maps reachability to a common node by walking the predecessors of the +// destination node. +void CFGReverseBlockReachabilityAnalysis::mapReachability(const CFGBlock *Dst) { + SmallVector<const CFGBlock *, 11> worklist; + llvm::BitVector visited(analyzed.size()); + + ReachableSet &DstReachability = reachable[Dst->getBlockID()]; + DstReachability.resize(analyzed.size(), false); + + // Start searching from the destination node, since we commonly will perform + // multiple queries relating to a destination node. + worklist.push_back(Dst); + bool firstRun = true; + + while (!worklist.empty()) { + const CFGBlock *block = worklist.pop_back_val(); + + if (visited[block->getBlockID()]) + continue; + visited[block->getBlockID()] = true; + + // Update reachability information for this node -> Dst + if (!firstRun) { + // Don't insert Dst -> Dst unless it was a predecessor of itself + DstReachability[block->getBlockID()] = true; + } + else + firstRun = false; + + // Add the predecessors to the worklist. + for (CFGBlock::const_pred_iterator i = block->pred_begin(), + e = block->pred_end(); i != e; ++i) { + worklist.push_back(*i); + } + } +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp b/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp new file mode 100644 index 000000000000..87c2f5bdc130 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/CFGStmtMap.cpp @@ -0,0 +1,91 @@ +//===--- CFGStmtMap.h - Map from Stmt* to CFGBlock* -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the CFGStmtMap class, which defines a mapping from +// Stmt* to CFGBlock* +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseMap.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" + +using namespace clang; + +typedef llvm::DenseMap<const Stmt*, CFGBlock*> SMap; +static SMap *AsMap(void *m) { return (SMap*) m; } + +CFGStmtMap::~CFGStmtMap() { delete AsMap(M); } + +CFGBlock *CFGStmtMap::getBlock(Stmt *S) { + SMap *SM = AsMap(M); + Stmt *X = S; + + // If 'S' isn't in the map, walk the ParentMap to see if one of its ancestors + // is in the map. + while (X) { + SMap::iterator I = SM->find(X); + if (I != SM->end()) { + CFGBlock *B = I->second; + // Memoize this lookup. + if (X != S) + (*SM)[X] = B; + return B; + } + + X = PM->getParentIgnoreParens(X); + } + + return 0; +} + +static void Accumulate(SMap &SM, CFGBlock *B) { + // First walk the block-level expressions. + for (CFGBlock::iterator I = B->begin(), E = B->end(); I != E; ++I) { + const CFGElement &CE = *I; + Optional<CFGStmt> CS = CE.getAs<CFGStmt>(); + if (!CS) + continue; + + CFGBlock *&Entry = SM[CS->getStmt()]; + // If 'Entry' is already initialized (e.g., a terminator was already), + // skip. + if (Entry) + continue; + + Entry = B; + + } + + // Look at the label of the block. + if (Stmt *Label = B->getLabel()) + SM[Label] = B; + + // Finally, look at the terminator. If the terminator was already added + // because it is a block-level expression in another block, overwrite + // that mapping. + if (Stmt *Term = B->getTerminator()) + SM[Term] = B; +} + +CFGStmtMap *CFGStmtMap::Build(CFG *C, ParentMap *PM) { + if (!C || !PM) + return 0; + + SMap *SM = new SMap(); + + // Walk all blocks, accumulating the block-level expressions, labels, + // and terminators. + for (CFG::iterator I = C->begin(), E = C->end(); I != E; ++I) + Accumulate(*SM, *I); + + return new CFGStmtMap(PM, SM); +} + diff --git a/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp b/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp new file mode 100644 index 000000000000..33870158b384 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/CallGraph.cpp @@ -0,0 +1,237 @@ +//== CallGraph.cpp - AST-based Call graph ----------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the AST-based CallGraph. +// +//===----------------------------------------------------------------------===// +#define DEBUG_TYPE "CallGraph" + +#include "clang/Analysis/CallGraph.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/GraphWriter.h" + +using namespace clang; + +STATISTIC(NumObjCCallEdges, "Number of Objective-C method call edges"); +STATISTIC(NumBlockCallEdges, "Number of block call edges"); + +namespace { +/// A helper class, which walks the AST and locates all the call sites in the +/// given function body. +class CGBuilder : public StmtVisitor<CGBuilder> { + CallGraph *G; + CallGraphNode *CallerNode; + +public: + CGBuilder(CallGraph *g, CallGraphNode *N) + : G(g), CallerNode(N) {} + + void VisitStmt(Stmt *S) { VisitChildren(S); } + + Decl *getDeclFromCall(CallExpr *CE) { + if (FunctionDecl *CalleeDecl = CE->getDirectCallee()) + return CalleeDecl; + + // Simple detection of a call through a block. + Expr *CEE = CE->getCallee()->IgnoreParenImpCasts(); + if (BlockExpr *Block = dyn_cast<BlockExpr>(CEE)) { + NumBlockCallEdges++; + return Block->getBlockDecl(); + } + + return 0; + } + + void addCalledDecl(Decl *D) { + if (G->includeInGraph(D)) { + CallGraphNode *CalleeNode = G->getOrInsertNode(D); + CallerNode->addCallee(CalleeNode, G); + } + } + + void VisitCallExpr(CallExpr *CE) { + if (Decl *D = getDeclFromCall(CE)) + addCalledDecl(D); + } + + // Adds may-call edges for the ObjC message sends. + void VisitObjCMessageExpr(ObjCMessageExpr *ME) { + if (ObjCInterfaceDecl *IDecl = ME->getReceiverInterface()) { + Selector Sel = ME->getSelector(); + + // Find the callee definition within the same translation unit. + Decl *D = 0; + if (ME->isInstanceMessage()) + D = IDecl->lookupPrivateMethod(Sel); + else + D = IDecl->lookupPrivateClassMethod(Sel); + if (D) { + addCalledDecl(D); + NumObjCCallEdges++; + } + } + } + + void VisitChildren(Stmt *S) { + for (Stmt::child_range I = S->children(); I; ++I) + if (*I) + static_cast<CGBuilder*>(this)->Visit(*I); + } +}; + +} // end anonymous namespace + +void CallGraph::addNodesForBlocks(DeclContext *D) { + if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) + addNodeForDecl(BD, true); + + for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); + I!=E; ++I) + if (DeclContext *DC = dyn_cast<DeclContext>(*I)) + addNodesForBlocks(DC); +} + +CallGraph::CallGraph() { + Root = getOrInsertNode(0); +} + +CallGraph::~CallGraph() { + if (!FunctionMap.empty()) { + for (FunctionMapTy::iterator I = FunctionMap.begin(), E = FunctionMap.end(); + I != E; ++I) + delete I->second; + FunctionMap.clear(); + } +} + +bool CallGraph::includeInGraph(const Decl *D) { + assert(D); + if (!D->getBody()) + return false; + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + // We skip function template definitions, as their semantics is + // only determined when they are instantiated. + if (!FD->isThisDeclarationADefinition() || + FD->isDependentContext()) + return false; + + IdentifierInfo *II = FD->getIdentifier(); + if (II && II->getName().startswith("__inline")) + return false; + } + + if (const ObjCMethodDecl *ID = dyn_cast<ObjCMethodDecl>(D)) { + if (!ID->isThisDeclarationADefinition()) + return false; + } + + return true; +} + +void CallGraph::addNodeForDecl(Decl* D, bool IsGlobal) { + assert(D); + + // Allocate a new node, mark it as root, and process it's calls. + CallGraphNode *Node = getOrInsertNode(D); + + // Process all the calls by this function as well. + CGBuilder builder(this, Node); + if (Stmt *Body = D->getBody()) + builder.Visit(Body); +} + +CallGraphNode *CallGraph::getNode(const Decl *F) const { + FunctionMapTy::const_iterator I = FunctionMap.find(F); + if (I == FunctionMap.end()) return 0; + return I->second; +} + +CallGraphNode *CallGraph::getOrInsertNode(Decl *F) { + CallGraphNode *&Node = FunctionMap[F]; + if (Node) + return Node; + + Node = new CallGraphNode(F); + // Make Root node a parent of all functions to make sure all are reachable. + if (F != 0) + Root->addCallee(Node, this); + return Node; +} + +void CallGraph::print(raw_ostream &OS) const { + OS << " --- Call graph Dump --- \n"; + + // We are going to print the graph in reverse post order, partially, to make + // sure the output is deterministic. + llvm::ReversePostOrderTraversal<const clang::CallGraph*> RPOT(this); + for (llvm::ReversePostOrderTraversal<const clang::CallGraph*>::rpo_iterator + I = RPOT.begin(), E = RPOT.end(); I != E; ++I) { + const CallGraphNode *N = *I; + + OS << " Function: "; + if (N == Root) + OS << "< root >"; + else + N->print(OS); + + OS << " calls: "; + for (CallGraphNode::const_iterator CI = N->begin(), + CE = N->end(); CI != CE; ++CI) { + assert(*CI != Root && "No one can call the root node."); + (*CI)->print(OS); + OS << " "; + } + OS << '\n'; + } + OS.flush(); +} + +void CallGraph::dump() const { + print(llvm::errs()); +} + +void CallGraph::viewGraph() const { + llvm::ViewGraph(this, "CallGraph"); +} + +void CallGraphNode::print(raw_ostream &os) const { + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(FD)) + return ND->printName(os); + os << "< >"; +} + +void CallGraphNode::dump() const { + print(llvm::errs()); +} + +namespace llvm { + +template <> +struct DOTGraphTraits<const CallGraph*> : public DefaultDOTGraphTraits { + + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + + static std::string getNodeLabel(const CallGraphNode *Node, + const CallGraph *CG) { + if (CG->getRoot() == Node) { + return "< root >"; + } + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Node->getDecl())) + return ND->getNameAsString(); + else + return "< >"; + } + +}; +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/CocoaConventions.cpp b/contrib/llvm/tools/clang/lib/Analysis/CocoaConventions.cpp new file mode 100644 index 000000000000..0db3cac58b56 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/CocoaConventions.cpp @@ -0,0 +1,140 @@ +//===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements cocoa naming convention analysis. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "clang/Basic/CharInfo.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace ento; + +bool cocoa::isRefType(QualType RetTy, StringRef Prefix, + StringRef Name) { + // Recursively walk the typedef stack, allowing typedefs of reference types. + while (const TypedefType *TD = dyn_cast<TypedefType>(RetTy.getTypePtr())) { + StringRef TDName = TD->getDecl()->getIdentifier()->getName(); + if (TDName.startswith(Prefix) && TDName.endswith("Ref")) + return true; + // XPC unfortunately uses CF-style function names, but aren't CF types. + if (TDName.startswith("xpc_")) + return false; + RetTy = TD->getDecl()->getUnderlyingType(); + } + + if (Name.empty()) + return false; + + // Is the type void*? + const PointerType* PT = RetTy->getAs<PointerType>(); + if (!(PT->getPointeeType().getUnqualifiedType()->isVoidType())) + return false; + + // Does the name start with the prefix? + return Name.startswith(Prefix); +} + +bool coreFoundation::isCFObjectRef(QualType T) { + return cocoa::isRefType(T, "CF") || // Core Foundation. + cocoa::isRefType(T, "CG") || // Core Graphics. + cocoa::isRefType(T, "DADisk") || // Disk Arbitration API. + cocoa::isRefType(T, "DADissenter") || + cocoa::isRefType(T, "DASessionRef"); +} + + +bool cocoa::isCocoaObjectRef(QualType Ty) { + if (!Ty->isObjCObjectPointerType()) + return false; + + const ObjCObjectPointerType *PT = Ty->getAs<ObjCObjectPointerType>(); + + // Can be true for objects with the 'NSObject' attribute. + if (!PT) + return true; + + // We assume that id<..>, id, Class, and Class<..> all represent tracked + // objects. + if (PT->isObjCIdType() || PT->isObjCQualifiedIdType() || + PT->isObjCClassType() || PT->isObjCQualifiedClassType()) + return true; + + // Does the interface subclass NSObject? + // FIXME: We can memoize here if this gets too expensive. + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + + // Assume that anything declared with a forward declaration and no + // @interface subclasses NSObject. + if (!ID->hasDefinition()) + return true; + + for ( ; ID ; ID = ID->getSuperClass()) + if (ID->getIdentifier()->getName() == "NSObject") + return true; + + return false; +} + +bool coreFoundation::followsCreateRule(const FunctionDecl *fn) { + // For now, *just* base this on the function name, not on anything else. + + const IdentifierInfo *ident = fn->getIdentifier(); + if (!ident) return false; + StringRef functionName = ident->getName(); + + StringRef::iterator it = functionName.begin(); + StringRef::iterator start = it; + StringRef::iterator endI = functionName.end(); + + while (true) { + // Scan for the start of 'create' or 'copy'. + for ( ; it != endI ; ++it) { + // Search for the first character. It can either be 'C' or 'c'. + char ch = *it; + if (ch == 'C' || ch == 'c') { + // Make sure this isn't something like 'recreate' or 'Scopy'. + if (ch == 'c' && it != start && isLetter(*(it - 1))) + continue; + + ++it; + break; + } + } + + // Did we hit the end of the string? If so, we didn't find a match. + if (it == endI) + return false; + + // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase + // character. + StringRef suffix = functionName.substr(it - start); + if (suffix.startswith("reate")) { + it += 5; + } + else if (suffix.startswith("opy")) { + it += 3; + } else { + // Keep scanning. + continue; + } + + if (it == endI || !isLowercase(*it)) + return true; + + // If we matched a lowercase character, it isn't the end of the + // word. Keep scanning. + } +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp b/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp new file mode 100644 index 000000000000..b33c8d8930f8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/Consumed.cpp @@ -0,0 +1,1521 @@ +//===- Consumed.cpp --------------------------------------------*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A intra-procedural analysis for checking consumed properties. This is based, +// in part, on research on linear types. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/Analyses/Consumed.h" +#include "clang/Basic/OperatorKinds.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" + +// TODO: Adjust states of args to constructors in the same way that arguments to +// function calls are handled. +// TODO: Use information from tests in for- and while-loop conditional. +// TODO: Add notes about the actual and expected state for +// TODO: Correctly identify unreachable blocks when chaining boolean operators. +// TODO: Adjust the parser and AttributesList class to support lists of +// identifiers. +// TODO: Warn about unreachable code. +// TODO: Switch to using a bitmap to track unreachable blocks. +// TODO: Handle variable definitions, e.g. bool valid = x.isValid(); +// if (valid) ...; (Deferred) +// TODO: Take notes on state transitions to provide better warning messages. +// (Deferred) +// TODO: Test nested conditionals: A) Checking the same value multiple times, +// and 2) Checking different values. (Deferred) + +using namespace clang; +using namespace consumed; + +// Key method definition +ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {} + +static SourceLocation getFirstStmtLoc(const CFGBlock *Block) { + // Find the source location of the first statement in the block, if the block + // is not empty. + for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end(); + BI != BE; ++BI) { + if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) + return CS->getStmt()->getLocStart(); + } + + // Block is empty. + // If we have one successor, return the first statement in that block + if (Block->succ_size() == 1 && *Block->succ_begin()) + return getFirstStmtLoc(*Block->succ_begin()); + + return SourceLocation(); +} + +static SourceLocation getLastStmtLoc(const CFGBlock *Block) { + // Find the source location of the last statement in the block, if the block + // is not empty. + if (const Stmt *StmtNode = Block->getTerminator()) { + return StmtNode->getLocStart(); + } else { + for (CFGBlock::const_reverse_iterator BI = Block->rbegin(), + BE = Block->rend(); BI != BE; ++BI) { + if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) + return CS->getStmt()->getLocStart(); + } + } + + // If we have one successor, return the first statement in that block + SourceLocation Loc; + if (Block->succ_size() == 1 && *Block->succ_begin()) + Loc = getFirstStmtLoc(*Block->succ_begin()); + if (Loc.isValid()) + return Loc; + + // If we have one predecessor, return the last statement in that block + if (Block->pred_size() == 1 && *Block->pred_begin()) + return getLastStmtLoc(*Block->pred_begin()); + + return Loc; +} + +static ConsumedState invertConsumedUnconsumed(ConsumedState State) { + switch (State) { + case CS_Unconsumed: + return CS_Consumed; + case CS_Consumed: + return CS_Unconsumed; + case CS_None: + return CS_None; + case CS_Unknown: + return CS_Unknown; + } + llvm_unreachable("invalid enum"); +} + +static bool isCallableInState(const CallableWhenAttr *CWAttr, + ConsumedState State) { + + CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(), + E = CWAttr->callableState_end(); + + for (; I != E; ++I) { + + ConsumedState MappedAttrState = CS_None; + + switch (*I) { + case CallableWhenAttr::Unknown: + MappedAttrState = CS_Unknown; + break; + + case CallableWhenAttr::Unconsumed: + MappedAttrState = CS_Unconsumed; + break; + + case CallableWhenAttr::Consumed: + MappedAttrState = CS_Consumed; + break; + } + + if (MappedAttrState == State) + return true; + } + + return false; +} + +static bool isConsumableType(const QualType &QT) { + if (QT->isPointerType() || QT->isReferenceType()) + return false; + + if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl()) + return RD->hasAttr<ConsumableAttr>(); + + return false; +} + +static bool isKnownState(ConsumedState State) { + switch (State) { + case CS_Unconsumed: + case CS_Consumed: + return true; + case CS_None: + case CS_Unknown: + return false; + } + llvm_unreachable("invalid enum"); +} + +static bool isRValueRefish(QualType ParamType) { + return ParamType->isRValueReferenceType() || + (ParamType->isLValueReferenceType() && + !cast<LValueReferenceType>( + ParamType.getCanonicalType())->isSpelledAsLValue()); +} + +static bool isTestingFunction(const FunctionDecl *FunDecl) { + return FunDecl->hasAttr<TestTypestateAttr>(); +} + +static bool isValueType(QualType ParamType) { + return !(ParamType->isPointerType() || ParamType->isReferenceType()); +} + +static ConsumedState mapConsumableAttrState(const QualType QT) { + assert(isConsumableType(QT)); + + const ConsumableAttr *CAttr = + QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>(); + + switch (CAttr->getDefaultState()) { + case ConsumableAttr::Unknown: + return CS_Unknown; + case ConsumableAttr::Unconsumed: + return CS_Unconsumed; + case ConsumableAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid enum"); +} + +static ConsumedState +mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) { + switch (PTAttr->getParamState()) { + case ParamTypestateAttr::Unknown: + return CS_Unknown; + case ParamTypestateAttr::Unconsumed: + return CS_Unconsumed; + case ParamTypestateAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid_enum"); +} + +static ConsumedState +mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) { + switch (RTSAttr->getState()) { + case ReturnTypestateAttr::Unknown: + return CS_Unknown; + case ReturnTypestateAttr::Unconsumed: + return CS_Unconsumed; + case ReturnTypestateAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid enum"); +} + +static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) { + switch (STAttr->getNewState()) { + case SetTypestateAttr::Unknown: + return CS_Unknown; + case SetTypestateAttr::Unconsumed: + return CS_Unconsumed; + case SetTypestateAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid_enum"); +} + +static StringRef stateToString(ConsumedState State) { + switch (State) { + case consumed::CS_None: + return "none"; + + case consumed::CS_Unknown: + return "unknown"; + + case consumed::CS_Unconsumed: + return "unconsumed"; + + case consumed::CS_Consumed: + return "consumed"; + } + llvm_unreachable("invalid enum"); +} + +static ConsumedState testsFor(const FunctionDecl *FunDecl) { + assert(isTestingFunction(FunDecl)); + switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) { + case TestTypestateAttr::Unconsumed: + return CS_Unconsumed; + case TestTypestateAttr::Consumed: + return CS_Consumed; + } + llvm_unreachable("invalid enum"); +} + +namespace { +struct VarTestResult { + const VarDecl *Var; + ConsumedState TestsFor; +}; +} // end anonymous::VarTestResult + +namespace clang { +namespace consumed { + +enum EffectiveOp { + EO_And, + EO_Or +}; + +class PropagationInfo { + enum { + IT_None, + IT_State, + IT_VarTest, + IT_BinTest, + IT_Var, + IT_Tmp + } InfoType; + + struct BinTestTy { + const BinaryOperator *Source; + EffectiveOp EOp; + VarTestResult LTest; + VarTestResult RTest; + }; + + union { + ConsumedState State; + VarTestResult VarTest; + const VarDecl *Var; + const CXXBindTemporaryExpr *Tmp; + BinTestTy BinTest; + }; + +public: + PropagationInfo() : InfoType(IT_None) {} + + PropagationInfo(const VarTestResult &VarTest) + : InfoType(IT_VarTest), VarTest(VarTest) {} + + PropagationInfo(const VarDecl *Var, ConsumedState TestsFor) + : InfoType(IT_VarTest) { + + VarTest.Var = Var; + VarTest.TestsFor = TestsFor; + } + + PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp, + const VarTestResult <est, const VarTestResult &RTest) + : InfoType(IT_BinTest) { + + BinTest.Source = Source; + BinTest.EOp = EOp; + BinTest.LTest = LTest; + BinTest.RTest = RTest; + } + + PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp, + const VarDecl *LVar, ConsumedState LTestsFor, + const VarDecl *RVar, ConsumedState RTestsFor) + : InfoType(IT_BinTest) { + + BinTest.Source = Source; + BinTest.EOp = EOp; + BinTest.LTest.Var = LVar; + BinTest.LTest.TestsFor = LTestsFor; + BinTest.RTest.Var = RVar; + BinTest.RTest.TestsFor = RTestsFor; + } + + PropagationInfo(ConsumedState State) + : InfoType(IT_State), State(State) {} + + PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {} + PropagationInfo(const CXXBindTemporaryExpr *Tmp) + : InfoType(IT_Tmp), Tmp(Tmp) {} + + const ConsumedState & getState() const { + assert(InfoType == IT_State); + return State; + } + + const VarTestResult & getVarTest() const { + assert(InfoType == IT_VarTest); + return VarTest; + } + + const VarTestResult & getLTest() const { + assert(InfoType == IT_BinTest); + return BinTest.LTest; + } + + const VarTestResult & getRTest() const { + assert(InfoType == IT_BinTest); + return BinTest.RTest; + } + + const VarDecl * getVar() const { + assert(InfoType == IT_Var); + return Var; + } + + const CXXBindTemporaryExpr * getTmp() const { + assert(InfoType == IT_Tmp); + return Tmp; + } + + ConsumedState getAsState(const ConsumedStateMap *StateMap) const { + assert(isVar() || isTmp() || isState()); + + if (isVar()) + return StateMap->getState(Var); + else if (isTmp()) + return StateMap->getState(Tmp); + else if (isState()) + return State; + else + return CS_None; + } + + EffectiveOp testEffectiveOp() const { + assert(InfoType == IT_BinTest); + return BinTest.EOp; + } + + const BinaryOperator * testSourceNode() const { + assert(InfoType == IT_BinTest); + return BinTest.Source; + } + + inline bool isValid() const { return InfoType != IT_None; } + inline bool isState() const { return InfoType == IT_State; } + inline bool isVarTest() const { return InfoType == IT_VarTest; } + inline bool isBinTest() const { return InfoType == IT_BinTest; } + inline bool isVar() const { return InfoType == IT_Var; } + inline bool isTmp() const { return InfoType == IT_Tmp; } + + bool isTest() const { + return InfoType == IT_VarTest || InfoType == IT_BinTest; + } + + bool isPointerToValue() const { + return InfoType == IT_Var || InfoType == IT_Tmp; + } + + PropagationInfo invertTest() const { + assert(InfoType == IT_VarTest || InfoType == IT_BinTest); + + if (InfoType == IT_VarTest) { + return PropagationInfo(VarTest.Var, + invertConsumedUnconsumed(VarTest.TestsFor)); + + } else if (InfoType == IT_BinTest) { + return PropagationInfo(BinTest.Source, + BinTest.EOp == EO_And ? EO_Or : EO_And, + BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor), + BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor)); + } else { + return PropagationInfo(); + } + } +}; + +static inline void +setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo, + ConsumedState State) { + + assert(PInfo.isVar() || PInfo.isTmp()); + + if (PInfo.isVar()) + StateMap->setState(PInfo.getVar(), State); + else + StateMap->setState(PInfo.getTmp(), State); +} + +class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { + + typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType; + typedef std::pair<const Stmt *, PropagationInfo> PairType; + typedef MapType::iterator InfoEntry; + typedef MapType::const_iterator ConstInfoEntry; + + AnalysisDeclContext &AC; + ConsumedAnalyzer &Analyzer; + ConsumedStateMap *StateMap; + MapType PropagationMap; + void forwardInfo(const Stmt *From, const Stmt *To); + bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl); + void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun, + QualType ReturnType); + +public: + void checkCallability(const PropagationInfo &PInfo, + const FunctionDecl *FunDecl, + SourceLocation BlameLoc); + + void VisitBinaryOperator(const BinaryOperator *BinOp); + void VisitCallExpr(const CallExpr *Call); + void VisitCastExpr(const CastExpr *Cast); + void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp); + void VisitCXXConstructExpr(const CXXConstructExpr *Call); + void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call); + void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call); + void VisitDeclRefExpr(const DeclRefExpr *DeclRef); + void VisitDeclStmt(const DeclStmt *DelcS); + void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp); + void VisitMemberExpr(const MemberExpr *MExpr); + void VisitParmVarDecl(const ParmVarDecl *Param); + void VisitReturnStmt(const ReturnStmt *Ret); + void VisitUnaryOperator(const UnaryOperator *UOp); + void VisitVarDecl(const VarDecl *Var); + + ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer, + ConsumedStateMap *StateMap) + : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {} + + PropagationInfo getInfo(const Stmt *StmtNode) const { + ConstInfoEntry Entry = PropagationMap.find(StmtNode); + + if (Entry != PropagationMap.end()) + return Entry->second; + else + return PropagationInfo(); + } + + void reset(ConsumedStateMap *NewStateMap) { + StateMap = NewStateMap; + } +}; + +void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, + const FunctionDecl *FunDecl, + SourceLocation BlameLoc) { + assert(!PInfo.isTest()); + + if (!FunDecl->hasAttr<CallableWhenAttr>()) + return; + + const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>(); + + if (PInfo.isVar()) { + ConsumedState VarState = StateMap->getState(PInfo.getVar()); + + if (VarState == CS_None || isCallableInState(CWAttr, VarState)) + return; + + Analyzer.WarningsHandler.warnUseInInvalidState( + FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(), + stateToString(VarState), BlameLoc); + + } else { + ConsumedState TmpState = PInfo.getAsState(StateMap); + + if (TmpState == CS_None || isCallableInState(CWAttr, TmpState)) + return; + + Analyzer.WarningsHandler.warnUseOfTempInInvalidState( + FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc); + } +} + +void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) { + InfoEntry Entry = PropagationMap.find(From); + + if (Entry != PropagationMap.end()) + PropagationMap.insert(PairType(To, Entry->second)); +} + +bool ConsumedStmtVisitor::isLikeMoveAssignment( + const CXXMethodDecl *MethodDecl) { + + return MethodDecl->isMoveAssignmentOperator() || + (MethodDecl->getOverloadedOperator() == OO_Equal && + MethodDecl->getNumParams() == 1 && + MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType()); +} + +void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call, + const FunctionDecl *Fun, + QualType ReturnType) { + if (isConsumableType(ReturnType)) { + + ConsumedState ReturnState; + + if (Fun->hasAttr<ReturnTypestateAttr>()) + ReturnState = mapReturnTypestateAttrState( + Fun->getAttr<ReturnTypestateAttr>()); + else + ReturnState = mapConsumableAttrState(ReturnType); + + PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState))); + } +} + +void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { + switch (BinOp->getOpcode()) { + case BO_LAnd: + case BO_LOr : { + InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()), + REntry = PropagationMap.find(BinOp->getRHS()); + + VarTestResult LTest, RTest; + + if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) { + LTest = LEntry->second.getVarTest(); + + } else { + LTest.Var = NULL; + LTest.TestsFor = CS_None; + } + + if (REntry != PropagationMap.end() && REntry->second.isVarTest()) { + RTest = REntry->second.getVarTest(); + + } else { + RTest.Var = NULL; + RTest.TestsFor = CS_None; + } + + if (!(LTest.Var == NULL && RTest.Var == NULL)) + PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp, + static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest))); + + break; + } + + case BO_PtrMemD: + case BO_PtrMemI: + forwardInfo(BinOp->getLHS(), BinOp); + break; + + default: + break; + } +} + +void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) { + if (const FunctionDecl *FunDecl = + dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) { + + // Special case for the std::move function. + // TODO: Make this more specific. (Deferred) + if (FunDecl->getNameAsString() == "move") { + forwardInfo(Call->getArg(0), Call); + return; + } + + unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams(); + + for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) { + const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset); + QualType ParamType = Param->getType(); + + InfoEntry Entry = PropagationMap.find(Call->getArg(Index)); + + if (Entry == PropagationMap.end() || Entry->second.isTest()) + continue; + + PropagationInfo PInfo = Entry->second; + + // Check that the parameter is in the correct state. + + if (Param->hasAttr<ParamTypestateAttr>()) { + ConsumedState ParamState = PInfo.getAsState(StateMap); + + ConsumedState ExpectedState = + mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>()); + + if (ParamState != ExpectedState) + Analyzer.WarningsHandler.warnParamTypestateMismatch( + Call->getArg(Index - Offset)->getExprLoc(), + stateToString(ExpectedState), stateToString(ParamState)); + } + + if (!(Entry->second.isVar() || Entry->second.isTmp())) + continue; + + // Adjust state on the caller side. + + if (isRValueRefish(ParamType)) { + setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); + + } else if (Param->hasAttr<ReturnTypestateAttr>()) { + setStateForVarOrTmp(StateMap, PInfo, + mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>())); + + } else if (!isValueType(ParamType) && + !ParamType->getPointeeType().isConstQualified()) { + + setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown); + } + } + + QualType RetType = FunDecl->getCallResultType(); + if (RetType->isReferenceType()) + RetType = RetType->getPointeeType(); + + propagateReturnType(Call, FunDecl, RetType); + } +} + +void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { + forwardInfo(Cast->getSubExpr(), Cast); +} + +void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr( + const CXXBindTemporaryExpr *Temp) { + + InfoEntry Entry = PropagationMap.find(Temp->getSubExpr()); + + if (Entry != PropagationMap.end() && !Entry->second.isTest()) { + StateMap->setState(Temp, Entry->second.getAsState(StateMap)); + PropagationMap.insert(PairType(Temp, PropagationInfo(Temp))); + } +} + +void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { + CXXConstructorDecl *Constructor = Call->getConstructor(); + + ASTContext &CurrContext = AC.getASTContext(); + QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType(); + + if (!isConsumableType(ThisType)) + return; + + // FIXME: What should happen if someone annotates the move constructor? + if (Constructor->hasAttr<ReturnTypestateAttr>()) { + // TODO: Adjust state of args appropriately. + + ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>(); + ConsumedState RetState = mapReturnTypestateAttrState(RTAttr); + PropagationMap.insert(PairType(Call, PropagationInfo(RetState))); + + } else if (Constructor->isDefaultConstructor()) { + + PropagationMap.insert(PairType(Call, + PropagationInfo(consumed::CS_Consumed))); + + } else if (Constructor->isMoveConstructor()) { + + InfoEntry Entry = PropagationMap.find(Call->getArg(0)); + + if (Entry != PropagationMap.end()) { + PropagationInfo PInfo = Entry->second; + + if (PInfo.isVar()) { + const VarDecl* Var = PInfo.getVar(); + + PropagationMap.insert(PairType(Call, + PropagationInfo(StateMap->getState(Var)))); + + StateMap->setState(Var, consumed::CS_Consumed); + + } else if (PInfo.isTmp()) { + const CXXBindTemporaryExpr *Tmp = PInfo.getTmp(); + + PropagationMap.insert(PairType(Call, + PropagationInfo(StateMap->getState(Tmp)))); + + StateMap->setState(Tmp, consumed::CS_Consumed); + + } else { + PropagationMap.insert(PairType(Call, PInfo)); + } + } + } else if (Constructor->isCopyConstructor()) { + forwardInfo(Call->getArg(0), Call); + + } else { + // TODO: Adjust state of args appropriately. + + ConsumedState RetState = mapConsumableAttrState(ThisType); + PropagationMap.insert(PairType(Call, PropagationInfo(RetState))); + } +} + +void ConsumedStmtVisitor::VisitCXXMemberCallExpr( + const CXXMemberCallExpr *Call) { + + VisitCallExpr(Call); + + InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens()); + + if (Entry != PropagationMap.end()) { + PropagationInfo PInfo = Entry->second; + const CXXMethodDecl *MethodDecl = Call->getMethodDecl(); + + checkCallability(PInfo, MethodDecl, Call->getExprLoc()); + + if (PInfo.isVar()) { + if (isTestingFunction(MethodDecl)) + PropagationMap.insert(PairType(Call, + PropagationInfo(PInfo.getVar(), testsFor(MethodDecl)))); + else if (MethodDecl->hasAttr<SetTypestateAttr>()) + StateMap->setState(PInfo.getVar(), + mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>())); + } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) { + StateMap->setState(PInfo.getTmp(), + mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>())); + } + } +} + +void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( + const CXXOperatorCallExpr *Call) { + + const FunctionDecl *FunDecl = + dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee()); + + if (!FunDecl) return; + + if (isa<CXXMethodDecl>(FunDecl) && + isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) { + + InfoEntry LEntry = PropagationMap.find(Call->getArg(0)); + InfoEntry REntry = PropagationMap.find(Call->getArg(1)); + + PropagationInfo LPInfo, RPInfo; + + if (LEntry != PropagationMap.end() && + REntry != PropagationMap.end()) { + + LPInfo = LEntry->second; + RPInfo = REntry->second; + + if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) { + setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap)); + PropagationMap.insert(PairType(Call, LPInfo)); + setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed); + + } else if (RPInfo.isState()) { + setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState()); + PropagationMap.insert(PairType(Call, LPInfo)); + + } else { + setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed); + } + + } else if (LEntry != PropagationMap.end() && + REntry == PropagationMap.end()) { + + LPInfo = LEntry->second; + + assert(!LPInfo.isTest()); + + if (LPInfo.isPointerToValue()) { + setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown); + PropagationMap.insert(PairType(Call, LPInfo)); + + } else { + PropagationMap.insert(PairType(Call, + PropagationInfo(consumed::CS_Unknown))); + } + + } else if (LEntry == PropagationMap.end() && + REntry != PropagationMap.end()) { + + RPInfo = REntry->second; + + if (RPInfo.isPointerToValue()) + setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed); + } + + } else { + + VisitCallExpr(Call); + + InfoEntry Entry = PropagationMap.find(Call->getArg(0)); + + if (Entry != PropagationMap.end()) { + PropagationInfo PInfo = Entry->second; + + checkCallability(PInfo, FunDecl, Call->getExprLoc()); + + if (PInfo.isVar()) { + if (isTestingFunction(FunDecl)) + PropagationMap.insert(PairType(Call, + PropagationInfo(PInfo.getVar(), testsFor(FunDecl)))); + else if (FunDecl->hasAttr<SetTypestateAttr>()) + StateMap->setState(PInfo.getVar(), + mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>())); + + } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) { + StateMap->setState(PInfo.getTmp(), + mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>())); + } + } + } +} + +void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) { + if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) + if (StateMap->getState(Var) != consumed::CS_None) + PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var))); +} + +void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) { + for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(), + DE = DeclS->decl_end(); DI != DE; ++DI) { + + if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI)); + } + + if (DeclS->isSingleDecl()) + if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl())) + PropagationMap.insert(PairType(DeclS, PropagationInfo(Var))); +} + +void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr( + const MaterializeTemporaryExpr *Temp) { + + forwardInfo(Temp->GetTemporaryExpr(), Temp); +} + +void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) { + forwardInfo(MExpr->getBase(), MExpr); +} + + +void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) { + QualType ParamType = Param->getType(); + ConsumedState ParamState = consumed::CS_None; + + if (Param->hasAttr<ParamTypestateAttr>()) { + const ParamTypestateAttr *PTAttr = Param->getAttr<ParamTypestateAttr>(); + ParamState = mapParamTypestateAttrState(PTAttr); + + } else if (isConsumableType(ParamType)) { + ParamState = mapConsumableAttrState(ParamType); + + } else if (isRValueRefish(ParamType) && + isConsumableType(ParamType->getPointeeType())) { + + ParamState = mapConsumableAttrState(ParamType->getPointeeType()); + + } else if (ParamType->isReferenceType() && + isConsumableType(ParamType->getPointeeType())) { + ParamState = consumed::CS_Unknown; + } + + if (ParamState != CS_None) + StateMap->setState(Param, ParamState); +} + +void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) { + ConsumedState ExpectedState = Analyzer.getExpectedReturnState(); + + if (ExpectedState != CS_None) { + InfoEntry Entry = PropagationMap.find(Ret->getRetValue()); + + if (Entry != PropagationMap.end()) { + ConsumedState RetState = Entry->second.getAsState(StateMap); + + if (RetState != ExpectedState) + Analyzer.WarningsHandler.warnReturnTypestateMismatch( + Ret->getReturnLoc(), stateToString(ExpectedState), + stateToString(RetState)); + } + } + + StateMap->checkParamsForReturnTypestate(Ret->getLocStart(), + Analyzer.WarningsHandler); +} + +void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { + InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens()); + if (Entry == PropagationMap.end()) return; + + switch (UOp->getOpcode()) { + case UO_AddrOf: + PropagationMap.insert(PairType(UOp, Entry->second)); + break; + + case UO_LNot: + if (Entry->second.isTest()) + PropagationMap.insert(PairType(UOp, Entry->second.invertTest())); + break; + + default: + break; + } +} + +// TODO: See if I need to check for reference types here. +void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) { + if (isConsumableType(Var->getType())) { + if (Var->hasInit()) { + MapType::iterator VIT = PropagationMap.find( + Var->getInit()->IgnoreImplicit()); + if (VIT != PropagationMap.end()) { + PropagationInfo PInfo = VIT->second; + ConsumedState St = PInfo.getAsState(StateMap); + + if (St != consumed::CS_None) { + StateMap->setState(Var, St); + return; + } + } + } + // Otherwise + StateMap->setState(Var, consumed::CS_Unknown); + } +} +}} // end clang::consumed::ConsumedStmtVisitor + +namespace clang { +namespace consumed { + +void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test, + ConsumedStateMap *ThenStates, + ConsumedStateMap *ElseStates) { + + ConsumedState VarState = ThenStates->getState(Test.Var); + + if (VarState == CS_Unknown) { + ThenStates->setState(Test.Var, Test.TestsFor); + ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); + + } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) { + ThenStates->markUnreachable(); + + } else if (VarState == Test.TestsFor) { + ElseStates->markUnreachable(); + } +} + +void splitVarStateForIfBinOp(const PropagationInfo &PInfo, + ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) { + + const VarTestResult <est = PInfo.getLTest(), + &RTest = PInfo.getRTest(); + + ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None, + RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None; + + if (LTest.Var) { + if (PInfo.testEffectiveOp() == EO_And) { + if (LState == CS_Unknown) { + ThenStates->setState(LTest.Var, LTest.TestsFor); + + } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) { + ThenStates->markUnreachable(); + + } else if (LState == LTest.TestsFor && isKnownState(RState)) { + if (RState == RTest.TestsFor) + ElseStates->markUnreachable(); + else + ThenStates->markUnreachable(); + } + + } else { + if (LState == CS_Unknown) { + ElseStates->setState(LTest.Var, + invertConsumedUnconsumed(LTest.TestsFor)); + + } else if (LState == LTest.TestsFor) { + ElseStates->markUnreachable(); + + } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) && + isKnownState(RState)) { + + if (RState == RTest.TestsFor) + ElseStates->markUnreachable(); + else + ThenStates->markUnreachable(); + } + } + } + + if (RTest.Var) { + if (PInfo.testEffectiveOp() == EO_And) { + if (RState == CS_Unknown) + ThenStates->setState(RTest.Var, RTest.TestsFor); + else if (RState == invertConsumedUnconsumed(RTest.TestsFor)) + ThenStates->markUnreachable(); + + } else { + if (RState == CS_Unknown) + ElseStates->setState(RTest.Var, + invertConsumedUnconsumed(RTest.TestsFor)); + else if (RState == RTest.TestsFor) + ElseStates->markUnreachable(); + } + } +} + +bool ConsumedBlockInfo::allBackEdgesVisited(const CFGBlock *CurrBlock, + const CFGBlock *TargetBlock) { + + assert(CurrBlock && "Block pointer must not be NULL"); + assert(TargetBlock && "TargetBlock pointer must not be NULL"); + + unsigned int CurrBlockOrder = VisitOrder[CurrBlock->getBlockID()]; + for (CFGBlock::const_pred_iterator PI = TargetBlock->pred_begin(), + PE = TargetBlock->pred_end(); PI != PE; ++PI) { + if (*PI && CurrBlockOrder < VisitOrder[(*PI)->getBlockID()] ) + return false; + } + return true; +} + +void ConsumedBlockInfo::addInfo(const CFGBlock *Block, + ConsumedStateMap *StateMap, + bool &AlreadyOwned) { + + assert(Block && "Block pointer must not be NULL"); + + ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()]; + + if (Entry) { + Entry->intersect(StateMap); + + } else if (AlreadyOwned) { + StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap); + + } else { + StateMapsArray[Block->getBlockID()] = StateMap; + AlreadyOwned = true; + } +} + +void ConsumedBlockInfo::addInfo(const CFGBlock *Block, + ConsumedStateMap *StateMap) { + + assert(Block != NULL && "Block pointer must not be NULL"); + + ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()]; + + if (Entry) { + Entry->intersect(StateMap); + delete StateMap; + + } else { + StateMapsArray[Block->getBlockID()] = StateMap; + } +} + +ConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) { + assert(Block && "Block pointer must not be NULL"); + assert(StateMapsArray[Block->getBlockID()] && "Block has no block info"); + + return StateMapsArray[Block->getBlockID()]; +} + +void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) { + unsigned int BlockID = Block->getBlockID(); + delete StateMapsArray[BlockID]; + StateMapsArray[BlockID] = NULL; +} + +ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) { + assert(Block && "Block pointer must not be NULL"); + + ConsumedStateMap *StateMap = StateMapsArray[Block->getBlockID()]; + if (isBackEdgeTarget(Block)) { + return new ConsumedStateMap(*StateMap); + } else { + StateMapsArray[Block->getBlockID()] = NULL; + return StateMap; + } +} + +bool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) { + assert(From && "From block must not be NULL"); + assert(To && "From block must not be NULL"); + + return VisitOrder[From->getBlockID()] > VisitOrder[To->getBlockID()]; +} + +bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) { + assert(Block != NULL && "Block pointer must not be NULL"); + + // Anything with less than two predecessors can't be the target of a back + // edge. + if (Block->pred_size() < 2) + return false; + + unsigned int BlockVisitOrder = VisitOrder[Block->getBlockID()]; + for (CFGBlock::const_pred_iterator PI = Block->pred_begin(), + PE = Block->pred_end(); PI != PE; ++PI) { + if (*PI && BlockVisitOrder < VisitOrder[(*PI)->getBlockID()]) + return true; + } + return false; +} + +void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc, + ConsumedWarningsHandlerBase &WarningsHandler) const { + + ConsumedState ExpectedState; + + for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end(); + DMI != DME; ++DMI) { + + if (isa<ParmVarDecl>(DMI->first)) { + const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first); + + if (!Param->hasAttr<ReturnTypestateAttr>()) continue; + + ExpectedState = + mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()); + + if (DMI->second != ExpectedState) { + WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc, + Param->getNameAsString(), stateToString(ExpectedState), + stateToString(DMI->second)); + } + } + } +} + +void ConsumedStateMap::clearTemporaries() { + TmpMap.clear(); +} + +ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const { + VarMapType::const_iterator Entry = VarMap.find(Var); + + if (Entry != VarMap.end()) + return Entry->second; + + return CS_None; +} + +ConsumedState +ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const { + TmpMapType::const_iterator Entry = TmpMap.find(Tmp); + + if (Entry != TmpMap.end()) + return Entry->second; + + return CS_None; +} + +void ConsumedStateMap::intersect(const ConsumedStateMap *Other) { + ConsumedState LocalState; + + if (this->From && this->From == Other->From && !Other->Reachable) { + this->markUnreachable(); + return; + } + + for (VarMapType::const_iterator DMI = Other->VarMap.begin(), + DME = Other->VarMap.end(); DMI != DME; ++DMI) { + + LocalState = this->getState(DMI->first); + + if (LocalState == CS_None) + continue; + + if (LocalState != DMI->second) + VarMap[DMI->first] = CS_Unknown; + } +} + +void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead, + const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates, + ConsumedWarningsHandlerBase &WarningsHandler) { + + ConsumedState LocalState; + SourceLocation BlameLoc = getLastStmtLoc(LoopBack); + + for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(), + DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) { + + LocalState = this->getState(DMI->first); + + if (LocalState == CS_None) + continue; + + if (LocalState != DMI->second) { + VarMap[DMI->first] = CS_Unknown; + WarningsHandler.warnLoopStateMismatch( + BlameLoc, DMI->first->getNameAsString()); + } + } +} + +void ConsumedStateMap::markUnreachable() { + this->Reachable = false; + VarMap.clear(); + TmpMap.clear(); +} + +void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) { + VarMap[Var] = State; +} + +void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp, + ConsumedState State) { + TmpMap[Tmp] = State; +} + +void ConsumedStateMap::remove(const VarDecl *Var) { + VarMap.erase(Var); +} + +bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const { + for (VarMapType::const_iterator DMI = Other->VarMap.begin(), + DME = Other->VarMap.end(); DMI != DME; ++DMI) { + + if (this->getState(DMI->first) != DMI->second) + return true; + } + + return false; +} + +void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC, + const FunctionDecl *D) { + QualType ReturnType; + if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { + ASTContext &CurrContext = AC.getASTContext(); + ReturnType = Constructor->getThisType(CurrContext)->getPointeeType(); + } else + ReturnType = D->getCallResultType(); + + if (D->hasAttr<ReturnTypestateAttr>()) { + const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>(); + + const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); + if (!RD || !RD->hasAttr<ConsumableAttr>()) { + // FIXME: This should be removed when template instantiation propagates + // attributes at template specialization definition, not + // declaration. When it is removed the test needs to be enabled + // in SemaDeclAttr.cpp. + WarningsHandler.warnReturnTypestateForUnconsumableType( + RTSAttr->getLocation(), ReturnType.getAsString()); + ExpectedReturnState = CS_None; + } else + ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr); + } else if (isConsumableType(ReturnType)) + ExpectedReturnState = mapConsumableAttrState(ReturnType); + else + ExpectedReturnState = CS_None; +} + +bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, + const ConsumedStmtVisitor &Visitor) { + + OwningPtr<ConsumedStateMap> FalseStates(new ConsumedStateMap(*CurrStates)); + PropagationInfo PInfo; + + if (const IfStmt *IfNode = + dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) { + + const Stmt *Cond = IfNode->getCond(); + + PInfo = Visitor.getInfo(Cond); + if (!PInfo.isValid() && isa<BinaryOperator>(Cond)) + PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS()); + + if (PInfo.isVarTest()) { + CurrStates->setSource(Cond); + FalseStates->setSource(Cond); + splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates, + FalseStates.get()); + + } else if (PInfo.isBinTest()) { + CurrStates->setSource(PInfo.testSourceNode()); + FalseStates->setSource(PInfo.testSourceNode()); + splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates.get()); + + } else { + return false; + } + + } else if (const BinaryOperator *BinOp = + dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) { + + PInfo = Visitor.getInfo(BinOp->getLHS()); + if (!PInfo.isVarTest()) { + if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) { + PInfo = Visitor.getInfo(BinOp->getRHS()); + + if (!PInfo.isVarTest()) + return false; + + } else { + return false; + } + } + + CurrStates->setSource(BinOp); + FalseStates->setSource(BinOp); + + const VarTestResult &Test = PInfo.getVarTest(); + ConsumedState VarState = CurrStates->getState(Test.Var); + + if (BinOp->getOpcode() == BO_LAnd) { + if (VarState == CS_Unknown) + CurrStates->setState(Test.Var, Test.TestsFor); + else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) + CurrStates->markUnreachable(); + + } else if (BinOp->getOpcode() == BO_LOr) { + if (VarState == CS_Unknown) + FalseStates->setState(Test.Var, + invertConsumedUnconsumed(Test.TestsFor)); + else if (VarState == Test.TestsFor) + FalseStates->markUnreachable(); + } + + } else { + return false; + } + + CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(); + + if (*SI) + BlockInfo.addInfo(*SI, CurrStates); + else + delete CurrStates; + + if (*++SI) + BlockInfo.addInfo(*SI, FalseStates.take()); + + CurrStates = NULL; + return true; +} + +void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { + const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl()); + if (!D) + return; + + CFG *CFGraph = AC.getCFG(); + if (!CFGraph) + return; + + determineExpectedReturnState(AC, D); + + PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>(); + // AC.getCFG()->viewCFG(LangOptions()); + + BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph); + + CurrStates = new ConsumedStateMap(); + ConsumedStmtVisitor Visitor(AC, *this, CurrStates); + + // Add all trackable parameters to the state map. + for (FunctionDecl::param_const_iterator PI = D->param_begin(), + PE = D->param_end(); PI != PE; ++PI) { + Visitor.VisitParmVarDecl(*PI); + } + + // Visit all of the function's basic blocks. + for (PostOrderCFGView::iterator I = SortedGraph->begin(), + E = SortedGraph->end(); I != E; ++I) { + + const CFGBlock *CurrBlock = *I; + + if (CurrStates == NULL) + CurrStates = BlockInfo.getInfo(CurrBlock); + + if (!CurrStates) { + continue; + + } else if (!CurrStates->isReachable()) { + delete CurrStates; + CurrStates = NULL; + continue; + } + + Visitor.reset(CurrStates); + + // Visit all of the basic block's statements. + for (CFGBlock::const_iterator BI = CurrBlock->begin(), + BE = CurrBlock->end(); BI != BE; ++BI) { + + switch (BI->getKind()) { + case CFGElement::Statement: + Visitor.Visit(BI->castAs<CFGStmt>().getStmt()); + break; + + case CFGElement::TemporaryDtor: { + const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>(); + const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr(); + + Visitor.checkCallability(PropagationInfo(BTE), + DTor.getDestructorDecl(AC.getASTContext()), + BTE->getExprLoc()); + break; + } + + case CFGElement::AutomaticObjectDtor: { + const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>(); + SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd(); + const VarDecl *Var = DTor.getVarDecl(); + + Visitor.checkCallability(PropagationInfo(Var), + DTor.getDestructorDecl(AC.getASTContext()), + Loc); + break; + } + + default: + break; + } + } + + CurrStates->clearTemporaries(); + + // TODO: Handle other forms of branching with precision, including while- + // and for-loops. (Deferred) + if (!splitState(CurrBlock, Visitor)) { + CurrStates->setSource(NULL); + + if (CurrBlock->succ_size() > 1 || + (CurrBlock->succ_size() == 1 && + (*CurrBlock->succ_begin())->pred_size() > 1)) { + + bool OwnershipTaken = false; + + for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), + SE = CurrBlock->succ_end(); SI != SE; ++SI) { + + if (*SI == NULL) continue; + + if (BlockInfo.isBackEdge(CurrBlock, *SI)) { + BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(*SI, CurrBlock, + CurrStates, + WarningsHandler); + + if (BlockInfo.allBackEdgesVisited(*SI, CurrBlock)) + BlockInfo.discardInfo(*SI); + } else { + BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken); + } + } + + if (!OwnershipTaken) + delete CurrStates; + + CurrStates = NULL; + } + } + + if (CurrBlock == &AC.getCFG()->getExit() && + D->getCallResultType()->isVoidType()) + CurrStates->checkParamsForReturnTypestate(D->getLocation(), + WarningsHandler); + } // End of block iterator. + + // Delete the last existing state map. + delete CurrStates; + + WarningsHandler.emitDiagnostics(); +} +}} // end namespace clang::consumed diff --git a/contrib/llvm/tools/clang/lib/Analysis/Dominators.cpp b/contrib/llvm/tools/clang/lib/Analysis/Dominators.cpp new file mode 100644 index 000000000000..0e02c6d7174a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/Dominators.cpp @@ -0,0 +1,14 @@ +//=- Dominators.cpp - Implementation of dominators tree for Clang CFG C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/Dominators.h" + +using namespace clang; + +void DominatorTree::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp b/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp new file mode 100644 index 000000000000..07431ac08c5c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/FormatString.cpp @@ -0,0 +1,858 @@ +// FormatString.cpp - Common stuff for handling printf/scanf formats -*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Shared details for processing format strings of printf and scanf +// (and friends). +// +//===----------------------------------------------------------------------===// + +#include "FormatStringParsing.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetInfo.h" + +using clang::analyze_format_string::ArgType; +using clang::analyze_format_string::FormatStringHandler; +using clang::analyze_format_string::FormatSpecifier; +using clang::analyze_format_string::LengthModifier; +using clang::analyze_format_string::OptionalAmount; +using clang::analyze_format_string::PositionContext; +using clang::analyze_format_string::ConversionSpecifier; +using namespace clang; + +// Key function to FormatStringHandler. +FormatStringHandler::~FormatStringHandler() {} + +//===----------------------------------------------------------------------===// +// Functions for parsing format strings components in both printf and +// scanf format strings. +//===----------------------------------------------------------------------===// + +OptionalAmount +clang::analyze_format_string::ParseAmount(const char *&Beg, const char *E) { + const char *I = Beg; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + unsigned accumulator = 0; + bool hasDigits = false; + + for ( ; I != E; ++I) { + char c = *I; + if (c >= '0' && c <= '9') { + hasDigits = true; + accumulator = (accumulator * 10) + (c - '0'); + continue; + } + + if (hasDigits) + return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg, + false); + + break; + } + + return OptionalAmount(); +} + +OptionalAmount +clang::analyze_format_string::ParseNonPositionAmount(const char *&Beg, + const char *E, + unsigned &argIndex) { + if (*Beg == '*') { + ++Beg; + return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); + } + + return ParseAmount(Beg, E); +} + +OptionalAmount +clang::analyze_format_string::ParsePositionAmount(FormatStringHandler &H, + const char *Start, + const char *&Beg, + const char *E, + PositionContext p) { + if (*Beg == '*') { + const char *I = Beg + 1; + const OptionalAmount &Amt = ParseAmount(I, E); + + if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { + H.HandleInvalidPosition(Beg, I - Beg, p); + return OptionalAmount(false); + } + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return OptionalAmount(false); + } + + assert(Amt.getHowSpecified() == OptionalAmount::Constant); + + if (*I == '$') { + // Handle positional arguments + + // Special case: '*0$', since this is an easy mistake. + if (Amt.getConstantAmount() == 0) { + H.HandleZeroPosition(Beg, I - Beg + 1); + return OptionalAmount(false); + } + + const char *Tmp = Beg; + Beg = ++I; + + return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1, + Tmp, 0, true); + } + + H.HandleInvalidPosition(Beg, I - Beg, p); + return OptionalAmount(false); + } + + return ParseAmount(Beg, E); +} + + +bool +clang::analyze_format_string::ParseFieldWidth(FormatStringHandler &H, + FormatSpecifier &CS, + const char *Start, + const char *&Beg, const char *E, + unsigned *argIndex) { + // FIXME: Support negative field widths. + if (argIndex) { + CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex)); + } + else { + const OptionalAmount Amt = + ParsePositionAmount(H, Start, Beg, E, + analyze_format_string::FieldWidthPos); + + if (Amt.isInvalid()) + return true; + CS.setFieldWidth(Amt); + } + return false; +} + +bool +clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, + FormatSpecifier &FS, + const char *Start, + const char *&Beg, + const char *E) { + const char *I = Beg; + + const OptionalAmount &Amt = ParseAmount(I, E); + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') { + // Warn that positional arguments are non-standard. + H.HandlePosition(Start, I - Start); + + // Special case: '%0$', since this is an easy mistake. + if (Amt.getConstantAmount() == 0) { + H.HandleZeroPosition(Start, I - Start); + return true; + } + + FS.setArgIndex(Amt.getConstantAmount() - 1); + FS.setUsesPositionalArg(); + // Update the caller's pointer if we decided to consume + // these characters. + Beg = I; + return false; + } + + return false; +} + +bool +clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, + const char *&I, + const char *E, + const LangOptions &LO, + bool IsScanf) { + LengthModifier::Kind lmKind = LengthModifier::None; + const char *lmPosition = I; + switch (*I) { + default: + return false; + case 'h': + ++I; + lmKind = (I != E && *I == 'h') ? (++I, LengthModifier::AsChar) + : LengthModifier::AsShort; + break; + case 'l': + ++I; + lmKind = (I != E && *I == 'l') ? (++I, LengthModifier::AsLongLong) + : LengthModifier::AsLong; + break; + case 'j': lmKind = LengthModifier::AsIntMax; ++I; break; + case 'z': lmKind = LengthModifier::AsSizeT; ++I; break; + case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break; + case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break; + case 'q': lmKind = LengthModifier::AsQuad; ++I; break; + case 'a': + if (IsScanf && !LO.C99 && !LO.CPlusPlus11) { + // For scanf in C90, look at the next character to see if this should + // be parsed as the GNU extension 'a' length modifier. If not, this + // will be parsed as a conversion specifier. + ++I; + if (I != E && (*I == 's' || *I == 'S' || *I == '[')) { + lmKind = LengthModifier::AsAllocate; + break; + } + --I; + } + return false; + case 'm': + if (IsScanf) { + lmKind = LengthModifier::AsMAllocate; + ++I; + break; + } + return false; + // printf: AsInt64, AsInt32, AsInt3264 + // scanf: AsInt64 + case 'I': + if (I + 1 != E && I + 2 != E) { + if (I[1] == '6' && I[2] == '4') { + I += 3; + lmKind = LengthModifier::AsInt64; + break; + } + if (IsScanf) + return false; + + if (I[1] == '3' && I[2] == '2') { + I += 3; + lmKind = LengthModifier::AsInt32; + break; + } + } + ++I; + lmKind = LengthModifier::AsInt3264; + break; + } + LengthModifier lm(lmPosition, lmKind); + FS.setLengthModifier(lm); + return true; +} + +//===----------------------------------------------------------------------===// +// Methods on ArgType. +//===----------------------------------------------------------------------===// + +bool ArgType::matchesType(ASTContext &C, QualType argTy) const { + if (Ptr) { + // It has to be a pointer. + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + + // We cannot write through a const qualified pointer. + if (PT->getPointeeType().isConstQualified()) + return false; + + argTy = PT->getPointeeType(); + } + + switch (K) { + case InvalidTy: + llvm_unreachable("ArgType must be valid"); + + case UnknownTy: + return true; + + case AnyCharTy: { + if (const EnumType *ETy = argTy->getAs<EnumType>()) + argTy = ETy->getDecl()->getIntegerType(); + + if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::UChar: + case BuiltinType::Char_U: + return true; + } + return false; + } + + case SpecificTy: { + if (const EnumType *ETy = argTy->getAs<EnumType>()) + argTy = ETy->getDecl()->getIntegerType(); + argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + + if (T == argTy) + return true; + // Check for "compatible types". + if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::Char_U: + case BuiltinType::UChar: + return T == C.UnsignedCharTy || T == C.SignedCharTy; + case BuiltinType::Short: + return T == C.UnsignedShortTy; + case BuiltinType::UShort: + return T == C.ShortTy; + case BuiltinType::Int: + return T == C.UnsignedIntTy; + case BuiltinType::UInt: + return T == C.IntTy; + case BuiltinType::Long: + return T == C.UnsignedLongTy; + case BuiltinType::ULong: + return T == C.LongTy; + case BuiltinType::LongLong: + return T == C.UnsignedLongLongTy; + case BuiltinType::ULongLong: + return T == C.LongLongTy; + } + return false; + } + + case CStrTy: { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + QualType pointeeTy = PT->getPointeeType(); + if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Void: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + return true; + default: + break; + } + + return false; + } + + case WCStrTy: { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + QualType pointeeTy = + C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType(); + return pointeeTy == C.getWideCharType(); + } + + case WIntTy: { + + QualType PromoArg = + argTy->isPromotableIntegerType() + ? C.getPromotedIntegerType(argTy) : argTy; + + QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType(); + PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType(); + + // If the promoted argument is the corresponding signed type of the + // wint_t type, then it should match. + if (PromoArg->hasSignedIntegerRepresentation() && + C.getCorrespondingUnsignedType(PromoArg) == WInt) + return true; + + return WInt == PromoArg; + } + + case CPointerTy: + return argTy->isPointerType() || argTy->isObjCObjectPointerType() || + argTy->isBlockPointerType() || argTy->isNullPtrType(); + + case ObjCPointerTy: { + if (argTy->getAs<ObjCObjectPointerType>() || + argTy->getAs<BlockPointerType>()) + return true; + + // Handle implicit toll-free bridging. + if (const PointerType *PT = argTy->getAs<PointerType>()) { + // Things such as CFTypeRef are really just opaque pointers + // to C structs representing CF types that can often be bridged + // to Objective-C objects. Since the compiler doesn't know which + // structs can be toll-free bridged, we just accept them all. + QualType pointee = PT->getPointeeType(); + if (pointee->getAsStructureType() || pointee->isVoidType()) + return true; + } + return false; + } + } + + llvm_unreachable("Invalid ArgType Kind!"); +} + +QualType ArgType::getRepresentativeType(ASTContext &C) const { + QualType Res; + switch (K) { + case InvalidTy: + llvm_unreachable("No representative type for Invalid ArgType"); + case UnknownTy: + llvm_unreachable("No representative type for Unknown ArgType"); + case AnyCharTy: + Res = C.CharTy; + break; + case SpecificTy: + Res = T; + break; + case CStrTy: + Res = C.getPointerType(C.CharTy); + break; + case WCStrTy: + Res = C.getPointerType(C.getWideCharType()); + break; + case ObjCPointerTy: + Res = C.ObjCBuiltinIdTy; + break; + case CPointerTy: + Res = C.VoidPtrTy; + break; + case WIntTy: { + Res = C.getWIntType(); + break; + } + } + + if (Ptr) + Res = C.getPointerType(Res); + return Res; +} + +std::string ArgType::getRepresentativeTypeName(ASTContext &C) const { + std::string S = getRepresentativeType(C).getAsString(); + + std::string Alias; + if (Name) { + // Use a specific name for this type, e.g. "size_t". + Alias = Name; + if (Ptr) { + // If ArgType is actually a pointer to T, append an asterisk. + Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *"; + } + // If Alias is the same as the underlying type, e.g. wchar_t, then drop it. + if (S == Alias) + Alias.clear(); + } + + if (!Alias.empty()) + return std::string("'") + Alias + "' (aka '" + S + "')"; + return std::string("'") + S + "'"; +} + + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +ArgType +analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const { + return Ctx.IntTy; +} + +//===----------------------------------------------------------------------===// +// Methods on LengthModifier. +//===----------------------------------------------------------------------===// + +const char * +analyze_format_string::LengthModifier::toString() const { + switch (kind) { + case AsChar: + return "hh"; + case AsShort: + return "h"; + case AsLong: // or AsWideChar + return "l"; + case AsLongLong: + return "ll"; + case AsQuad: + return "q"; + case AsIntMax: + return "j"; + case AsSizeT: + return "z"; + case AsPtrDiff: + return "t"; + case AsInt32: + return "I32"; + case AsInt3264: + return "I"; + case AsInt64: + return "I64"; + case AsLongDouble: + return "L"; + case AsAllocate: + return "a"; + case AsMAllocate: + return "m"; + case None: + return ""; + } + return NULL; +} + +//===----------------------------------------------------------------------===// +// Methods on ConversionSpecifier. +//===----------------------------------------------------------------------===// + +const char *ConversionSpecifier::toString() const { + switch (kind) { + case dArg: return "d"; + case DArg: return "D"; + case iArg: return "i"; + case oArg: return "o"; + case OArg: return "O"; + case uArg: return "u"; + case UArg: return "U"; + case xArg: return "x"; + case XArg: return "X"; + case fArg: return "f"; + case FArg: return "F"; + case eArg: return "e"; + case EArg: return "E"; + case gArg: return "g"; + case GArg: return "G"; + case aArg: return "a"; + case AArg: return "A"; + case cArg: return "c"; + case sArg: return "s"; + case pArg: return "p"; + case nArg: return "n"; + case PercentArg: return "%"; + case ScanListArg: return "["; + case InvalidSpecifier: return NULL; + + // POSIX unicode extensions. + case CArg: return "C"; + case SArg: return "S"; + + // Objective-C specific specifiers. + case ObjCObjArg: return "@"; + + // FreeBSD specific specifiers. + case FreeBSDbArg: return "b"; + case FreeBSDDArg: return "D"; + case FreeBSDrArg: return "r"; + + // GlibC specific specifiers. + case PrintErrno: return "m"; + } + return NULL; +} + +Optional<ConversionSpecifier> +ConversionSpecifier::getStandardSpecifier() const { + ConversionSpecifier::Kind NewKind; + + switch (getKind()) { + default: + return None; + case DArg: + NewKind = dArg; + break; + case UArg: + NewKind = uArg; + break; + case OArg: + NewKind = oArg; + break; + } + + ConversionSpecifier FixedCS(*this); + FixedCS.setKind(NewKind); + return FixedCS; +} + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +void OptionalAmount::toString(raw_ostream &os) const { + switch (hs) { + case Invalid: + case NotSpecified: + return; + case Arg: + if (UsesDotPrefix) + os << "."; + if (usesPositionalArg()) + os << "*" << getPositionalArgIndex() << "$"; + else + os << "*"; + break; + case Constant: + if (UsesDotPrefix) + os << "."; + os << amt; + break; + } +} + +bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const { + switch (LM.getKind()) { + case LengthModifier::None: + return true; + + // Handle most integer flags + case LengthModifier::AsChar: + case LengthModifier::AsShort: + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + case LengthModifier::AsIntMax: + case LengthModifier::AsSizeT: + case LengthModifier::AsPtrDiff: + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + case ConversionSpecifier::nArg: + case ConversionSpecifier::FreeBSDrArg: + return true; + default: + return false; + } + + // Handle 'l' flag + case LengthModifier::AsLong: + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + case ConversionSpecifier::nArg: + case ConversionSpecifier::cArg: + case ConversionSpecifier::sArg: + case ConversionSpecifier::FreeBSDrArg: + case ConversionSpecifier::ScanListArg: + return true; + default: + return false; + } + + case LengthModifier::AsLongDouble: + switch (CS.getKind()) { + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + return true; + // GNU libc extension. + case ConversionSpecifier::dArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + return !Target.getTriple().isOSDarwin() && + !Target.getTriple().isOSWindows(); + default: + return false; + } + + case LengthModifier::AsAllocate: + switch (CS.getKind()) { + case ConversionSpecifier::sArg: + case ConversionSpecifier::SArg: + case ConversionSpecifier::ScanListArg: + return true; + default: + return false; + } + + case LengthModifier::AsMAllocate: + switch (CS.getKind()) { + case ConversionSpecifier::cArg: + case ConversionSpecifier::CArg: + case ConversionSpecifier::sArg: + case ConversionSpecifier::SArg: + case ConversionSpecifier::ScanListArg: + return true; + default: + return false; + } + case LengthModifier::AsInt32: + case LengthModifier::AsInt3264: + case LengthModifier::AsInt64: + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + return Target.getTriple().isOSMSVCRT(); + default: + return false; + } + } + llvm_unreachable("Invalid LengthModifier Kind!"); +} + +bool FormatSpecifier::hasStandardLengthModifier() const { + switch (LM.getKind()) { + case LengthModifier::None: + case LengthModifier::AsChar: + case LengthModifier::AsShort: + case LengthModifier::AsLong: + case LengthModifier::AsLongLong: + case LengthModifier::AsIntMax: + case LengthModifier::AsSizeT: + case LengthModifier::AsPtrDiff: + case LengthModifier::AsLongDouble: + return true; + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + case LengthModifier::AsQuad: + case LengthModifier::AsInt32: + case LengthModifier::AsInt3264: + case LengthModifier::AsInt64: + return false; + } + llvm_unreachable("Invalid LengthModifier Kind!"); +} + +bool FormatSpecifier::hasStandardConversionSpecifier(const LangOptions &LangOpt) const { + switch (CS.getKind()) { + case ConversionSpecifier::cArg: + case ConversionSpecifier::dArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::sArg: + case ConversionSpecifier::pArg: + case ConversionSpecifier::nArg: + case ConversionSpecifier::ObjCObjArg: + case ConversionSpecifier::ScanListArg: + case ConversionSpecifier::PercentArg: + return true; + case ConversionSpecifier::CArg: + case ConversionSpecifier::SArg: + return LangOpt.ObjC1 || LangOpt.ObjC2; + case ConversionSpecifier::InvalidSpecifier: + case ConversionSpecifier::FreeBSDbArg: + case ConversionSpecifier::FreeBSDDArg: + case ConversionSpecifier::FreeBSDrArg: + case ConversionSpecifier::PrintErrno: + case ConversionSpecifier::DArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::UArg: + return false; + } + llvm_unreachable("Invalid ConversionSpecifier Kind!"); +} + +bool FormatSpecifier::hasStandardLengthConversionCombination() const { + if (LM.getKind() == LengthModifier::AsLongDouble) { + switch(CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + return false; + default: + return true; + } + } + return true; +} + +Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const { + if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) { + if (LM.getKind() == LengthModifier::AsLongDouble || + LM.getKind() == LengthModifier::AsQuad) { + LengthModifier FixedLM(LM); + FixedLM.setKind(LengthModifier::AsLongLong); + return FixedLM; + } + } + + return None; +} + +bool FormatSpecifier::namedTypeToLengthModifier(QualType QT, + LengthModifier &LM) { + assert(isa<TypedefType>(QT) && "Expected a TypedefType"); + const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl(); + + for (;;) { + const IdentifierInfo *Identifier = Typedef->getIdentifier(); + if (Identifier->getName() == "size_t") { + LM.setKind(LengthModifier::AsSizeT); + return true; + } else if (Identifier->getName() == "ssize_t") { + // Not C99, but common in Unix. + LM.setKind(LengthModifier::AsSizeT); + return true; + } else if (Identifier->getName() == "intmax_t") { + LM.setKind(LengthModifier::AsIntMax); + return true; + } else if (Identifier->getName() == "uintmax_t") { + LM.setKind(LengthModifier::AsIntMax); + return true; + } else if (Identifier->getName() == "ptrdiff_t") { + LM.setKind(LengthModifier::AsPtrDiff); + return true; + } + + QualType T = Typedef->getUnderlyingType(); + if (!isa<TypedefType>(T)) + break; + + Typedef = cast<TypedefType>(T)->getDecl(); + } + return false; +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h b/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h new file mode 100644 index 000000000000..6b251230136a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/FormatStringParsing.h @@ -0,0 +1,74 @@ +#ifndef LLVM_CLANG_FORMAT_PARSING_H +#define LLVM_CLANG_FORMAT_PARSING_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/Analyses/FormatString.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +class LangOptions; + +template <typename T> +class UpdateOnReturn { + T &ValueToUpdate; + const T &ValueToCopy; +public: + UpdateOnReturn(T &valueToUpdate, const T &valueToCopy) + : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {} + + ~UpdateOnReturn() { + ValueToUpdate = ValueToCopy; + } +}; + +namespace analyze_format_string { + +OptionalAmount ParseAmount(const char *&Beg, const char *E); +OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E, + unsigned &argIndex); + +OptionalAmount ParsePositionAmount(FormatStringHandler &H, + const char *Start, const char *&Beg, + const char *E, PositionContext p); + +bool ParseFieldWidth(FormatStringHandler &H, + FormatSpecifier &CS, + const char *Start, const char *&Beg, const char *E, + unsigned *argIndex); + +bool ParseArgPosition(FormatStringHandler &H, + FormatSpecifier &CS, const char *Start, + const char *&Beg, const char *E); + +/// Returns true if a LengthModifier was parsed and installed in the +/// FormatSpecifier& argument, and false otherwise. +bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E, + const LangOptions &LO, bool IsScanf = false); + +template <typename T> class SpecifierResult { + T FS; + const char *Start; + bool Stop; +public: + SpecifierResult(bool stop = false) + : Start(0), Stop(stop) {} + SpecifierResult(const char *start, + const T &fs) + : FS(fs), Start(start), Stop(false) {} + + const char *getStart() const { return Start; } + bool shouldStop() const { return Stop; } + bool hasValue() const { return Start != 0; } + const T &getValue() const { + assert(hasValue()); + return FS; + } + const T &getValue() { return FS; } +}; + +} // end analyze_format_string namespace +} // end clang namespace + +#endif diff --git a/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp b/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp new file mode 100644 index 000000000000..9e5ec557bca2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/LiveVariables.cpp @@ -0,0 +1,637 @@ +//=- LiveVariables.cpp - Live Variable Analysis for Source CFGs ----------*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Live Variables analysis for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <vector> + +using namespace clang; + +namespace { + +class DataflowWorklist { + SmallVector<const CFGBlock *, 20> worklist; + llvm::BitVector enqueuedBlocks; + PostOrderCFGView *POV; +public: + DataflowWorklist(const CFG &cfg, AnalysisDeclContext &Ctx) + : enqueuedBlocks(cfg.getNumBlockIDs()), + POV(Ctx.getAnalysis<PostOrderCFGView>()) {} + + void enqueueBlock(const CFGBlock *block); + void enqueueSuccessors(const CFGBlock *block); + void enqueuePredecessors(const CFGBlock *block); + + const CFGBlock *dequeue(); + + void sortWorklist(); +}; + +} + +void DataflowWorklist::enqueueBlock(const clang::CFGBlock *block) { + if (block && !enqueuedBlocks[block->getBlockID()]) { + enqueuedBlocks[block->getBlockID()] = true; + worklist.push_back(block); + } +} + +void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) { + const unsigned OldWorklistSize = worklist.size(); + for (CFGBlock::const_succ_iterator I = block->succ_begin(), + E = block->succ_end(); I != E; ++I) { + enqueueBlock(*I); + } + + if (OldWorklistSize == 0 || OldWorklistSize == worklist.size()) + return; + + sortWorklist(); +} + +void DataflowWorklist::enqueuePredecessors(const clang::CFGBlock *block) { + const unsigned OldWorklistSize = worklist.size(); + for (CFGBlock::const_pred_iterator I = block->pred_begin(), + E = block->pred_end(); I != E; ++I) { + enqueueBlock(*I); + } + + if (OldWorklistSize == 0 || OldWorklistSize == worklist.size()) + return; + + sortWorklist(); +} + +void DataflowWorklist::sortWorklist() { + std::sort(worklist.begin(), worklist.end(), POV->getComparator()); +} + +const CFGBlock *DataflowWorklist::dequeue() { + if (worklist.empty()) + return 0; + const CFGBlock *b = worklist.pop_back_val(); + enqueuedBlocks[b->getBlockID()] = false; + return b; +} + +namespace { +class LiveVariablesImpl { +public: + AnalysisDeclContext &analysisContext; + std::vector<LiveVariables::LivenessValues> cfgBlockValues; + llvm::ImmutableSet<const Stmt *>::Factory SSetFact; + llvm::ImmutableSet<const VarDecl *>::Factory DSetFact; + llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksEndToLiveness; + llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksBeginToLiveness; + llvm::DenseMap<const Stmt *, LiveVariables::LivenessValues> stmtsToLiveness; + llvm::DenseMap<const DeclRefExpr *, unsigned> inAssignment; + const bool killAtAssign; + + LiveVariables::LivenessValues + merge(LiveVariables::LivenessValues valsA, + LiveVariables::LivenessValues valsB); + + LiveVariables::LivenessValues runOnBlock(const CFGBlock *block, + LiveVariables::LivenessValues val, + LiveVariables::Observer *obs = 0); + + void dumpBlockLiveness(const SourceManager& M); + + LiveVariablesImpl(AnalysisDeclContext &ac, bool KillAtAssign) + : analysisContext(ac), + SSetFact(false), // Do not canonicalize ImmutableSets by default. + DSetFact(false), // This is a *major* performance win. + killAtAssign(KillAtAssign) {} +}; +} + +static LiveVariablesImpl &getImpl(void *x) { + return *((LiveVariablesImpl *) x); +} + +//===----------------------------------------------------------------------===// +// Operations and queries on LivenessValues. +//===----------------------------------------------------------------------===// + +bool LiveVariables::LivenessValues::isLive(const Stmt *S) const { + return liveStmts.contains(S); +} + +bool LiveVariables::LivenessValues::isLive(const VarDecl *D) const { + return liveDecls.contains(D); +} + +namespace { + template <typename SET> + SET mergeSets(SET A, SET B) { + if (A.isEmpty()) + return B; + + for (typename SET::iterator it = B.begin(), ei = B.end(); it != ei; ++it) { + A = A.add(*it); + } + return A; + } +} + +void LiveVariables::Observer::anchor() { } + +LiveVariables::LivenessValues +LiveVariablesImpl::merge(LiveVariables::LivenessValues valsA, + LiveVariables::LivenessValues valsB) { + + llvm::ImmutableSetRef<const Stmt *> + SSetRefA(valsA.liveStmts.getRootWithoutRetain(), SSetFact.getTreeFactory()), + SSetRefB(valsB.liveStmts.getRootWithoutRetain(), SSetFact.getTreeFactory()); + + + llvm::ImmutableSetRef<const VarDecl *> + DSetRefA(valsA.liveDecls.getRootWithoutRetain(), DSetFact.getTreeFactory()), + DSetRefB(valsB.liveDecls.getRootWithoutRetain(), DSetFact.getTreeFactory()); + + + SSetRefA = mergeSets(SSetRefA, SSetRefB); + DSetRefA = mergeSets(DSetRefA, DSetRefB); + + // asImmutableSet() canonicalizes the tree, allowing us to do an easy + // comparison afterwards. + return LiveVariables::LivenessValues(SSetRefA.asImmutableSet(), + DSetRefA.asImmutableSet()); +} + +bool LiveVariables::LivenessValues::equals(const LivenessValues &V) const { + return liveStmts == V.liveStmts && liveDecls == V.liveDecls; +} + +//===----------------------------------------------------------------------===// +// Query methods. +//===----------------------------------------------------------------------===// + +static bool isAlwaysAlive(const VarDecl *D) { + return D->hasGlobalStorage(); +} + +bool LiveVariables::isLive(const CFGBlock *B, const VarDecl *D) { + return isAlwaysAlive(D) || getImpl(impl).blocksEndToLiveness[B].isLive(D); +} + +bool LiveVariables::isLive(const Stmt *S, const VarDecl *D) { + return isAlwaysAlive(D) || getImpl(impl).stmtsToLiveness[S].isLive(D); +} + +bool LiveVariables::isLive(const Stmt *Loc, const Stmt *S) { + return getImpl(impl).stmtsToLiveness[Loc].isLive(S); +} + +//===----------------------------------------------------------------------===// +// Dataflow computation. +//===----------------------------------------------------------------------===// + +namespace { +class TransferFunctions : public StmtVisitor<TransferFunctions> { + LiveVariablesImpl &LV; + LiveVariables::LivenessValues &val; + LiveVariables::Observer *observer; + const CFGBlock *currentBlock; +public: + TransferFunctions(LiveVariablesImpl &im, + LiveVariables::LivenessValues &Val, + LiveVariables::Observer *Observer, + const CFGBlock *CurrentBlock) + : LV(im), val(Val), observer(Observer), currentBlock(CurrentBlock) {} + + void VisitBinaryOperator(BinaryOperator *BO); + void VisitBlockExpr(BlockExpr *BE); + void VisitDeclRefExpr(DeclRefExpr *DR); + void VisitDeclStmt(DeclStmt *DS); + void VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS); + void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE); + void VisitUnaryOperator(UnaryOperator *UO); + void Visit(Stmt *S); +}; +} + +static const VariableArrayType *FindVA(QualType Ty) { + const Type *ty = Ty.getTypePtr(); + while (const ArrayType *VT = dyn_cast<ArrayType>(ty)) { + if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(VT)) + if (VAT->getSizeExpr()) + return VAT; + + ty = VT->getElementType().getTypePtr(); + } + + return 0; +} + +static const Stmt *LookThroughStmt(const Stmt *S) { + while (S) { + if (const Expr *Ex = dyn_cast<Expr>(S)) + S = Ex->IgnoreParens(); + if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S)) { + S = EWC->getSubExpr(); + continue; + } + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) { + S = OVE->getSourceExpr(); + continue; + } + break; + } + return S; +} + +static void AddLiveStmt(llvm::ImmutableSet<const Stmt *> &Set, + llvm::ImmutableSet<const Stmt *>::Factory &F, + const Stmt *S) { + Set = F.add(Set, LookThroughStmt(S)); +} + +void TransferFunctions::Visit(Stmt *S) { + if (observer) + observer->observeStmt(S, currentBlock, val); + + StmtVisitor<TransferFunctions>::Visit(S); + + if (isa<Expr>(S)) { + val.liveStmts = LV.SSetFact.remove(val.liveStmts, S); + } + + // Mark all children expressions live. + + switch (S->getStmtClass()) { + default: + break; + case Stmt::StmtExprClass: { + // For statement expressions, look through the compound statement. + S = cast<StmtExpr>(S)->getSubStmt(); + break; + } + case Stmt::CXXMemberCallExprClass: { + // Include the implicit "this" pointer as being live. + CXXMemberCallExpr *CE = cast<CXXMemberCallExpr>(S); + if (Expr *ImplicitObj = CE->getImplicitObjectArgument()) { + AddLiveStmt(val.liveStmts, LV.SSetFact, ImplicitObj); + } + break; + } + case Stmt::ObjCMessageExprClass: { + // In calls to super, include the implicit "self" pointer as being live. + ObjCMessageExpr *CE = cast<ObjCMessageExpr>(S); + if (CE->getReceiverKind() == ObjCMessageExpr::SuperInstance) + val.liveDecls = LV.DSetFact.add(val.liveDecls, + LV.analysisContext.getSelfDecl()); + break; + } + case Stmt::DeclStmtClass: { + const DeclStmt *DS = cast<DeclStmt>(S); + if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) { + for (const VariableArrayType* VA = FindVA(VD->getType()); + VA != 0; VA = FindVA(VA->getElementType())) { + AddLiveStmt(val.liveStmts, LV.SSetFact, VA->getSizeExpr()); + } + } + break; + } + case Stmt::PseudoObjectExprClass: { + // A pseudo-object operation only directly consumes its result + // expression. + Expr *child = cast<PseudoObjectExpr>(S)->getResultExpr(); + if (!child) return; + if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(child)) + child = OV->getSourceExpr(); + child = child->IgnoreParens(); + val.liveStmts = LV.SSetFact.add(val.liveStmts, child); + return; + } + + // FIXME: These cases eventually shouldn't be needed. + case Stmt::ExprWithCleanupsClass: { + S = cast<ExprWithCleanups>(S)->getSubExpr(); + break; + } + case Stmt::CXXBindTemporaryExprClass: { + S = cast<CXXBindTemporaryExpr>(S)->getSubExpr(); + break; + } + case Stmt::UnaryExprOrTypeTraitExprClass: { + // No need to unconditionally visit subexpressions. + return; + } + } + + for (Stmt::child_iterator it = S->child_begin(), ei = S->child_end(); + it != ei; ++it) { + if (Stmt *child = *it) + AddLiveStmt(val.liveStmts, LV.SSetFact, child); + } +} + +void TransferFunctions::VisitBinaryOperator(BinaryOperator *B) { + if (B->isAssignmentOp()) { + if (!LV.killAtAssign) + return; + + // Assigning to a variable? + Expr *LHS = B->getLHS()->IgnoreParens(); + + if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + // Assignments to references don't kill the ref's address + if (VD->getType()->isReferenceType()) + return; + + if (!isAlwaysAlive(VD)) { + // The variable is now dead. + val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); + } + + if (observer) + observer->observerKill(DR); + } + } +} + +void TransferFunctions::VisitBlockExpr(BlockExpr *BE) { + AnalysisDeclContext::referenced_decls_iterator I, E; + llvm::tie(I, E) = + LV.analysisContext.getReferencedBlockVars(BE->getBlockDecl()); + for ( ; I != E ; ++I) { + const VarDecl *VD = *I; + if (isAlwaysAlive(VD)) + continue; + val.liveDecls = LV.DSetFact.add(val.liveDecls, VD); + } +} + +void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) { + if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl())) + if (!isAlwaysAlive(D) && LV.inAssignment.find(DR) == LV.inAssignment.end()) + val.liveDecls = LV.DSetFact.add(val.liveDecls, D); +} + +void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { + for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) + if (VarDecl *VD = dyn_cast<VarDecl>(*DI)) { + if (!isAlwaysAlive(VD)) + val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); + } +} + +void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS) { + // Kill the iteration variable. + DeclRefExpr *DR = 0; + const VarDecl *VD = 0; + + Stmt *element = OS->getElement(); + if (DeclStmt *DS = dyn_cast<DeclStmt>(element)) { + VD = cast<VarDecl>(DS->getSingleDecl()); + } + else if ((DR = dyn_cast<DeclRefExpr>(cast<Expr>(element)->IgnoreParens()))) { + VD = cast<VarDecl>(DR->getDecl()); + } + + if (VD) { + val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); + if (observer && DR) + observer->observerKill(DR); + } +} + +void TransferFunctions:: +VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE) +{ + // While sizeof(var) doesn't technically extend the liveness of 'var', it + // does extent the liveness of metadata if 'var' is a VariableArrayType. + // We handle that special case here. + if (UE->getKind() != UETT_SizeOf || UE->isArgumentType()) + return; + + const Expr *subEx = UE->getArgumentExpr(); + if (subEx->getType()->isVariableArrayType()) { + assert(subEx->isLValue()); + val.liveStmts = LV.SSetFact.add(val.liveStmts, subEx->IgnoreParens()); + } +} + +void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) { + // Treat ++/-- as a kill. + // Note we don't actually have to do anything if we don't have an observer, + // since a ++/-- acts as both a kill and a "use". + if (!observer) + return; + + switch (UO->getOpcode()) { + default: + return; + case UO_PostInc: + case UO_PostDec: + case UO_PreInc: + case UO_PreDec: + break; + } + + if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreParens())) + if (isa<VarDecl>(DR->getDecl())) { + // Treat ++/-- as a kill. + observer->observerKill(DR); + } +} + +LiveVariables::LivenessValues +LiveVariablesImpl::runOnBlock(const CFGBlock *block, + LiveVariables::LivenessValues val, + LiveVariables::Observer *obs) { + + TransferFunctions TF(*this, val, obs, block); + + // Visit the terminator (if any). + if (const Stmt *term = block->getTerminator()) + TF.Visit(const_cast<Stmt*>(term)); + + // Apply the transfer function for all Stmts in the block. + for (CFGBlock::const_reverse_iterator it = block->rbegin(), + ei = block->rend(); it != ei; ++it) { + const CFGElement &elem = *it; + + if (Optional<CFGAutomaticObjDtor> Dtor = + elem.getAs<CFGAutomaticObjDtor>()) { + val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl()); + continue; + } + + if (!elem.getAs<CFGStmt>()) + continue; + + const Stmt *S = elem.castAs<CFGStmt>().getStmt(); + TF.Visit(const_cast<Stmt*>(S)); + stmtsToLiveness[S] = val; + } + return val; +} + +void LiveVariables::runOnAllBlocks(LiveVariables::Observer &obs) { + const CFG *cfg = getImpl(impl).analysisContext.getCFG(); + for (CFG::const_iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) + getImpl(impl).runOnBlock(*it, getImpl(impl).blocksEndToLiveness[*it], &obs); +} + +LiveVariables::LiveVariables(void *im) : impl(im) {} + +LiveVariables::~LiveVariables() { + delete (LiveVariablesImpl*) impl; +} + +LiveVariables * +LiveVariables::computeLiveness(AnalysisDeclContext &AC, + bool killAtAssign) { + + // No CFG? Bail out. + CFG *cfg = AC.getCFG(); + if (!cfg) + return 0; + + // The analysis currently has scalability issues for very large CFGs. + // Bail out if it looks too large. + if (cfg->getNumBlockIDs() > 300000) + return 0; + + LiveVariablesImpl *LV = new LiveVariablesImpl(AC, killAtAssign); + + // Construct the dataflow worklist. Enqueue the exit block as the + // start of the analysis. + DataflowWorklist worklist(*cfg, AC); + llvm::BitVector everAnalyzedBlock(cfg->getNumBlockIDs()); + + // FIXME: we should enqueue using post order. + for (CFG::const_iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) { + const CFGBlock *block = *it; + worklist.enqueueBlock(block); + + // FIXME: Scan for DeclRefExprs using in the LHS of an assignment. + // We need to do this because we lack context in the reverse analysis + // to determine if a DeclRefExpr appears in such a context, and thus + // doesn't constitute a "use". + if (killAtAssign) + for (CFGBlock::const_iterator bi = block->begin(), be = block->end(); + bi != be; ++bi) { + if (Optional<CFGStmt> cs = bi->getAs<CFGStmt>()) { + if (const BinaryOperator *BO = + dyn_cast<BinaryOperator>(cs->getStmt())) { + if (BO->getOpcode() == BO_Assign) { + if (const DeclRefExpr *DR = + dyn_cast<DeclRefExpr>(BO->getLHS()->IgnoreParens())) { + LV->inAssignment[DR] = 1; + } + } + } + } + } + } + + worklist.sortWorklist(); + + while (const CFGBlock *block = worklist.dequeue()) { + // Determine if the block's end value has changed. If not, we + // have nothing left to do for this block. + LivenessValues &prevVal = LV->blocksEndToLiveness[block]; + + // Merge the values of all successor blocks. + LivenessValues val; + for (CFGBlock::const_succ_iterator it = block->succ_begin(), + ei = block->succ_end(); it != ei; ++it) { + if (const CFGBlock *succ = *it) { + val = LV->merge(val, LV->blocksBeginToLiveness[succ]); + } + } + + if (!everAnalyzedBlock[block->getBlockID()]) + everAnalyzedBlock[block->getBlockID()] = true; + else if (prevVal.equals(val)) + continue; + + prevVal = val; + + // Update the dataflow value for the start of this block. + LV->blocksBeginToLiveness[block] = LV->runOnBlock(block, val); + + // Enqueue the value to the predecessors. + worklist.enqueuePredecessors(block); + } + + return new LiveVariables(LV); +} + +static bool compare_entries(const CFGBlock *A, const CFGBlock *B) { + return A->getBlockID() < B->getBlockID(); +} + +static bool compare_vd_entries(const Decl *A, const Decl *B) { + SourceLocation ALoc = A->getLocStart(); + SourceLocation BLoc = B->getLocStart(); + return ALoc.getRawEncoding() < BLoc.getRawEncoding(); +} + +void LiveVariables::dumpBlockLiveness(const SourceManager &M) { + getImpl(impl).dumpBlockLiveness(M); +} + +void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) { + std::vector<const CFGBlock *> vec; + for (llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues>::iterator + it = blocksEndToLiveness.begin(), ei = blocksEndToLiveness.end(); + it != ei; ++it) { + vec.push_back(it->first); + } + std::sort(vec.begin(), vec.end(), compare_entries); + + std::vector<const VarDecl*> declVec; + + for (std::vector<const CFGBlock *>::iterator + it = vec.begin(), ei = vec.end(); it != ei; ++it) { + llvm::errs() << "\n[ B" << (*it)->getBlockID() + << " (live variables at block exit) ]\n"; + + LiveVariables::LivenessValues vals = blocksEndToLiveness[*it]; + declVec.clear(); + + for (llvm::ImmutableSet<const VarDecl *>::iterator si = + vals.liveDecls.begin(), + se = vals.liveDecls.end(); si != se; ++si) { + declVec.push_back(*si); + } + + std::sort(declVec.begin(), declVec.end(), compare_vd_entries); + + for (std::vector<const VarDecl*>::iterator di = declVec.begin(), + de = declVec.end(); di != de; ++di) { + llvm::errs() << " " << (*di)->getDeclName().getAsString() + << " <"; + (*di)->getLocation().dump(M); + llvm::errs() << ">\n"; + } + } + llvm::errs() << "\n"; +} + +const void *LiveVariables::getTag() { static int x; return &x; } +const void *RelaxedLiveVariables::getTag() { static int x; return &x; } diff --git a/contrib/llvm/tools/clang/lib/Analysis/ObjCNoReturn.cpp b/contrib/llvm/tools/clang/lib/Analysis/ObjCNoReturn.cpp new file mode 100644 index 000000000000..52d844bf9dd8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ObjCNoReturn.cpp @@ -0,0 +1,67 @@ +//= ObjCNoReturn.cpp - Handling of Cocoa APIs known not to return --*- C++ -*--- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements special handling of recognizing ObjC API hooks that +// do not return but aren't marked as such in API headers. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" + +using namespace clang; + +static bool isSubclass(const ObjCInterfaceDecl *Class, IdentifierInfo *II) { + if (!Class) + return false; + if (Class->getIdentifier() == II) + return true; + return isSubclass(Class->getSuperClass(), II); +} + +ObjCNoReturn::ObjCNoReturn(ASTContext &C) + : RaiseSel(GetNullarySelector("raise", C)), + NSExceptionII(&C.Idents.get("NSException")) +{ + // Generate selectors. + SmallVector<IdentifierInfo*, 3> II; + + // raise:format: + II.push_back(&C.Idents.get("raise")); + II.push_back(&C.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[0] = + C.Selectors.getSelector(II.size(), &II[0]); + + // raise:format:arguments: + II.push_back(&C.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[1] = + C.Selectors.getSelector(II.size(), &II[0]); +} + + +bool ObjCNoReturn::isImplicitNoReturn(const ObjCMessageExpr *ME) { + Selector S = ME->getSelector(); + + if (ME->isInstanceMessage()) { + // Check for the "raise" message. + return S == RaiseSel; + } + + if (const ObjCInterfaceDecl *ID = ME->getReceiverInterface()) { + if (isSubclass(ID, NSExceptionII)) { + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) { + if (S == NSExceptionInstanceRaiseSelectors[i]) + return true; + } + } + } + + return false; +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp b/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp new file mode 100644 index 000000000000..cfd66f7aa1f2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/PostOrderCFGView.cpp @@ -0,0 +1,49 @@ +//===- PostOrderCFGView.cpp - Post order view of CFG blocks -------*- C++ --*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements post order view of the blocks in a CFG. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/PostOrderCFGView.h" + +using namespace clang; + +void PostOrderCFGView::anchor() { } + +PostOrderCFGView::PostOrderCFGView(const CFG *cfg) { + Blocks.reserve(cfg->getNumBlockIDs()); + CFGBlockSet BSet(cfg); + + for (po_iterator I = po_iterator::begin(cfg, BSet), + E = po_iterator::end(cfg, BSet); I != E; ++I) { + BlockOrder[*I] = Blocks.size() + 1; + Blocks.push_back(*I); + } +} + +PostOrderCFGView *PostOrderCFGView::create(AnalysisDeclContext &ctx) { + const CFG *cfg = ctx.getCFG(); + if (!cfg) + return 0; + return new PostOrderCFGView(cfg); +} + +const void *PostOrderCFGView::getTag() { static int x; return &x; } + +bool PostOrderCFGView::BlockOrderCompare::operator()(const CFGBlock *b1, + const CFGBlock *b2) const { + PostOrderCFGView::BlockOrderTy::const_iterator b1It = POV.BlockOrder.find(b1); + PostOrderCFGView::BlockOrderTy::const_iterator b2It = POV.BlockOrder.find(b2); + + unsigned b1V = (b1It == POV.BlockOrder.end()) ? 0 : b1It->second; + unsigned b2V = (b2It == POV.BlockOrder.end()) ? 0 : b2It->second; + return b1V > b2V; +} + diff --git a/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp b/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp new file mode 100644 index 000000000000..400bbf86b612 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/PrintfFormatString.cpp @@ -0,0 +1,804 @@ +//== PrintfFormatString.cpp - Analysis of printf format strings --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Handling of format string in printf and friends. The structure of format +// strings for fprintf() are described in C99 7.19.6.1. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/FormatString.h" +#include "FormatStringParsing.h" +#include "clang/Basic/TargetInfo.h" + +using clang::analyze_format_string::ArgType; +using clang::analyze_format_string::FormatStringHandler; +using clang::analyze_format_string::LengthModifier; +using clang::analyze_format_string::OptionalAmount; +using clang::analyze_format_string::ConversionSpecifier; +using clang::analyze_printf::PrintfSpecifier; + +using namespace clang; + +typedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier> + PrintfSpecifierResult; + +//===----------------------------------------------------------------------===// +// Methods for parsing format strings. +//===----------------------------------------------------------------------===// + +using analyze_format_string::ParseNonPositionAmount; + +static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, + const char *Start, const char *&Beg, const char *E, + unsigned *argIndex) { + if (argIndex) { + FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex)); + } else { + const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E, + analyze_format_string::PrecisionPos); + if (Amt.isInvalid()) + return true; + FS.setPrecision(Amt); + } + return false; +} + +static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, + const char *&Beg, + const char *E, + unsigned &argIndex, + const LangOptions &LO, + const TargetInfo &Target) { + + using namespace clang::analyze_format_string; + using namespace clang::analyze_printf; + + const char *I = Beg; + const char *Start = 0; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + // Look for a '%' character that indicates the start of a format specifier. + for ( ; I != E ; ++I) { + char c = *I; + if (c == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + if (c == '%') { + Start = I++; // Record the start of the format specifier. + break; + } + } + + // No format specifier found? + if (!Start) + return false; + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + PrintfSpecifier FS; + if (ParseArgPosition(H, FS, Start, I, E)) + return true; + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + // Look for flags (if any). + bool hasMore = true; + for ( ; I != E; ++I) { + switch (*I) { + default: hasMore = false; break; + case '\'': + // FIXME: POSIX specific. Always accept? + FS.setHasThousandsGrouping(I); + break; + case '-': FS.setIsLeftJustified(I); break; + case '+': FS.setHasPlusPrefix(I); break; + case ' ': FS.setHasSpacePrefix(I); break; + case '#': FS.setHasAlternativeForm(I); break; + case '0': FS.setHasLeadingZeros(I); break; + } + if (!hasMore) + break; + } + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + // Look for the field width (if any). + if (ParseFieldWidth(H, FS, Start, I, E, + FS.usesPositionalArg() ? 0 : &argIndex)) + return true; + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + // Look for the precision (if any). + if (*I == '.') { + ++I; + if (I == E) { + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + if (ParsePrecision(H, FS, Start, I, E, + FS.usesPositionalArg() ? 0 : &argIndex)) + return true; + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + } + + // Look for the length modifier. + if (ParseLengthModifier(FS, I, E, LO) && I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + if (*I == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + + // Finally, look for the conversion specifier. + const char *conversionPosition = I++; + ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; + switch (*conversionPosition) { + default: + break; + // C99: 7.19.6.1 (section 8). + case '%': k = ConversionSpecifier::PercentArg; break; + case 'A': k = ConversionSpecifier::AArg; break; + case 'E': k = ConversionSpecifier::EArg; break; + case 'F': k = ConversionSpecifier::FArg; break; + case 'G': k = ConversionSpecifier::GArg; break; + case 'X': k = ConversionSpecifier::XArg; break; + case 'a': k = ConversionSpecifier::aArg; break; + case 'c': k = ConversionSpecifier::cArg; break; + case 'd': k = ConversionSpecifier::dArg; break; + case 'e': k = ConversionSpecifier::eArg; break; + case 'f': k = ConversionSpecifier::fArg; break; + case 'g': k = ConversionSpecifier::gArg; break; + case 'i': k = ConversionSpecifier::iArg; break; + case 'n': k = ConversionSpecifier::nArg; break; + case 'o': k = ConversionSpecifier::oArg; break; + case 'p': k = ConversionSpecifier::pArg; break; + case 's': k = ConversionSpecifier::sArg; break; + case 'u': k = ConversionSpecifier::uArg; break; + case 'x': k = ConversionSpecifier::xArg; break; + // POSIX specific. + case 'C': k = ConversionSpecifier::CArg; break; + case 'S': k = ConversionSpecifier::SArg; break; + // Objective-C. + case '@': k = ConversionSpecifier::ObjCObjArg; break; + // Glibc specific. + case 'm': k = ConversionSpecifier::PrintErrno; break; + // FreeBSD format extensions + case 'b': + if (LO.FormatExtensions) + k = ConversionSpecifier::FreeBSDbArg; // int followed by char * + break; + case 'r': + if (LO.FormatExtensions) + k = ConversionSpecifier::FreeBSDrArg; + break; + case 'y': + if (LO.FormatExtensions) + k = ConversionSpecifier::iArg; + break; + // Apple-specific + case 'D': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::DArg; + else if (LO.FormatExtensions) + k = ConversionSpecifier::FreeBSDDArg; // u_char * followed by char * + break; + case 'O': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::OArg; + break; + case 'U': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::UArg; + break; + } + PrintfConversionSpecifier CS(conversionPosition, k); + FS.setConversionSpecifier(CS); + if (CS.consumesDataArgument() && !FS.usesPositionalArg()) + FS.setArgIndex(argIndex++); + // FreeBSD extension + if (k == ConversionSpecifier::FreeBSDbArg || + k == ConversionSpecifier::FreeBSDDArg) + argIndex++; + + if (k == ConversionSpecifier::InvalidSpecifier) { + // Assume the conversion takes one argument. + return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, I - Start); + } + return PrintfSpecifierResult(Start, FS); +} + +bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, + const char *I, + const char *E, + const LangOptions &LO, + const TargetInfo &Target) { + + unsigned argIndex = 0; + + // Keep looking for a format specifier until we have exhausted the string. + while (I != E) { + const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex, + LO, Target); + // Did a fail-stop error of any kind occur when parsing the specifier? + // If so, don't do any more processing. + if (FSR.shouldStop()) + return true; + // Did we exhaust the string or encounter an error that + // we can recover from? + if (!FSR.hasValue()) + continue; + // We have a format specifier. Pass it to the callback. + if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(), + I - FSR.getStart())) + return true; + } + assert(I == E && "Format string not exhausted"); + return false; +} + +//===----------------------------------------------------------------------===// +// Methods on PrintfSpecifier. +//===----------------------------------------------------------------------===// + +ArgType PrintfSpecifier::getArgType(ASTContext &Ctx, + bool IsObjCLiteral) const { + const PrintfConversionSpecifier &CS = getConversionSpecifier(); + + if (!CS.consumesDataArgument()) + return ArgType::Invalid(); + + if (CS.getKind() == ConversionSpecifier::cArg) + switch (LM.getKind()) { + case LengthModifier::None: return Ctx.IntTy; + case LengthModifier::AsLong: + return ArgType(ArgType::WIntTy, "wint_t"); + default: + return ArgType::Invalid(); + } + + if (CS.isIntArg()) + switch (LM.getKind()) { + case LengthModifier::AsLongDouble: + // GNU extension. + return Ctx.LongLongTy; + case LengthModifier::None: + return Ctx.IntTy; + case LengthModifier::AsInt32: + return ArgType(Ctx.IntTy, "__int32"); + case LengthModifier::AsChar: return ArgType::AnyCharTy; + case LengthModifier::AsShort: return Ctx.ShortTy; + case LengthModifier::AsLong: return Ctx.LongTy; + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return Ctx.LongLongTy; + case LengthModifier::AsInt64: + return ArgType(Ctx.LongLongTy, "__int64"); + case LengthModifier::AsIntMax: + return ArgType(Ctx.getIntMaxType(), "intmax_t"); + case LengthModifier::AsSizeT: + // FIXME: How to get the corresponding signed version of size_t? + return ArgType(); + case LengthModifier::AsInt3264: + return Ctx.getTargetInfo().getTriple().isArch64Bit() + ? ArgType(Ctx.LongLongTy, "__int64") + : ArgType(Ctx.IntTy, "__int32"); + case LengthModifier::AsPtrDiff: + return ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"); + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + return ArgType::Invalid(); + } + + if (CS.isUIntArg()) + switch (LM.getKind()) { + case LengthModifier::AsLongDouble: + // GNU extension. + return Ctx.UnsignedLongLongTy; + case LengthModifier::None: + return Ctx.UnsignedIntTy; + case LengthModifier::AsInt32: + return ArgType(Ctx.UnsignedIntTy, "unsigned __int32"); + case LengthModifier::AsChar: return Ctx.UnsignedCharTy; + case LengthModifier::AsShort: return Ctx.UnsignedShortTy; + case LengthModifier::AsLong: return Ctx.UnsignedLongTy; + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return Ctx.UnsignedLongLongTy; + case LengthModifier::AsInt64: + return ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64"); + case LengthModifier::AsIntMax: + return ArgType(Ctx.getUIntMaxType(), "uintmax_t"); + case LengthModifier::AsSizeT: + return ArgType(Ctx.getSizeType(), "size_t"); + case LengthModifier::AsInt3264: + return Ctx.getTargetInfo().getTriple().isArch64Bit() + ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64") + : ArgType(Ctx.UnsignedIntTy, "unsigned __int32"); + case LengthModifier::AsPtrDiff: + // FIXME: How to get the corresponding unsigned + // version of ptrdiff_t? + return ArgType(); + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + return ArgType::Invalid(); + } + + if (CS.isDoubleArg()) { + if (LM.getKind() == LengthModifier::AsLongDouble) + return Ctx.LongDoubleTy; + return Ctx.DoubleTy; + } + + if (CS.getKind() == ConversionSpecifier::nArg) { + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.IntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(Ctx.SignedCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.ShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.LongTy); + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsIntMax: + return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); + case LengthModifier::AsSizeT: + return ArgType(); // FIXME: ssize_t + case LengthModifier::AsPtrDiff: + return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); + case LengthModifier::AsLongDouble: + return ArgType(); // FIXME: Is this a known extension? + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + case LengthModifier::AsInt32: + case LengthModifier::AsInt3264: + case LengthModifier::AsInt64: + return ArgType::Invalid(); + } + } + + switch (CS.getKind()) { + case ConversionSpecifier::sArg: + if (LM.getKind() == LengthModifier::AsWideChar) { + if (IsObjCLiteral) + return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()), + "const unichar *"); + return ArgType(ArgType::WCStrTy, "wchar_t *"); + } + return ArgType::CStrTy; + case ConversionSpecifier::SArg: + if (IsObjCLiteral) + return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()), + "const unichar *"); + return ArgType(ArgType::WCStrTy, "wchar_t *"); + case ConversionSpecifier::CArg: + if (IsObjCLiteral) + return ArgType(Ctx.UnsignedShortTy, "unichar"); + return ArgType(Ctx.WideCharTy, "wchar_t"); + case ConversionSpecifier::pArg: + return ArgType::CPointerTy; + case ConversionSpecifier::ObjCObjArg: + return ArgType::ObjCPointerTy; + default: + break; + } + + // FIXME: Handle other cases. + return ArgType(); +} + +bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, + ASTContext &Ctx, bool IsObjCLiteral) { + // %n is different from other conversion specifiers; don't try to fix it. + if (CS.getKind() == ConversionSpecifier::nArg) + return false; + + // Handle Objective-C objects first. Note that while the '%@' specifier will + // not warn for structure pointer or void pointer arguments (because that's + // how CoreFoundation objects are implemented), we only show a fixit for '%@' + // if we know it's an object (block, id, class, or __attribute__((NSObject))). + if (QT->isObjCRetainableType()) { + if (!IsObjCLiteral) + return false; + + CS.setKind(ConversionSpecifier::ObjCObjArg); + + // Disable irrelevant flags + HasThousandsGrouping = false; + HasPlusPrefix = false; + HasSpacePrefix = false; + HasAlternativeForm = false; + HasLeadingZeroes = false; + Precision.setHowSpecified(OptionalAmount::NotSpecified); + LM.setKind(LengthModifier::None); + + return true; + } + + // Handle strings next (char *, wchar_t *) + if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) { + CS.setKind(ConversionSpecifier::sArg); + + // Disable irrelevant flags + HasAlternativeForm = 0; + HasLeadingZeroes = 0; + + // Set the long length modifier for wide characters + if (QT->getPointeeType()->isWideCharType()) + LM.setKind(LengthModifier::AsWideChar); + else + LM.setKind(LengthModifier::None); + + return true; + } + + // If it's an enum, get its underlying type. + if (const EnumType *ETy = QT->getAs<EnumType>()) + QT = ETy->getDecl()->getIntegerType(); + + // We can only work with builtin types. + const BuiltinType *BT = QT->getAs<BuiltinType>(); + if (!BT) + return false; + + // Set length modifier + switch (BT->getKind()) { + case BuiltinType::Bool: + case BuiltinType::WChar_U: + case BuiltinType::WChar_S: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::UInt128: + case BuiltinType::Int128: + case BuiltinType::Half: + // Various types which are non-trivial to correct. + return false; + +#define SIGNED_TYPE(Id, SingletonId) +#define UNSIGNED_TYPE(Id, SingletonId) +#define FLOATING_TYPE(Id, SingletonId) +#define BUILTIN_TYPE(Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/AST/BuiltinTypes.def" + // Misc other stuff which doesn't make sense here. + return false; + + case BuiltinType::UInt: + case BuiltinType::Int: + case BuiltinType::Float: + case BuiltinType::Double: + LM.setKind(LengthModifier::None); + break; + + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + LM.setKind(LengthModifier::AsChar); + break; + + case BuiltinType::Short: + case BuiltinType::UShort: + LM.setKind(LengthModifier::AsShort); + break; + + case BuiltinType::Long: + case BuiltinType::ULong: + LM.setKind(LengthModifier::AsLong); + break; + + case BuiltinType::LongLong: + case BuiltinType::ULongLong: + LM.setKind(LengthModifier::AsLongLong); + break; + + case BuiltinType::LongDouble: + LM.setKind(LengthModifier::AsLongDouble); + break; + } + + // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. + if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11)) + namedTypeToLengthModifier(QT, LM); + + // If fixing the length modifier was enough, we might be done. + if (hasValidLengthModifier(Ctx.getTargetInfo())) { + // If we're going to offer a fix anyway, make sure the sign matches. + switch (CS.getKind()) { + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + if (QT->isSignedIntegerType()) + CS.setKind(clang::analyze_format_string::ConversionSpecifier::dArg); + break; + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + if (QT->isUnsignedIntegerType() && !HasPlusPrefix) + CS.setKind(clang::analyze_format_string::ConversionSpecifier::uArg); + break; + default: + // Other specifiers do not have signed/unsigned variants. + break; + } + + const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral); + if (ATR.isValid() && ATR.matchesType(Ctx, QT)) + return true; + } + + // Set conversion specifier and disable any flags which do not apply to it. + // Let typedefs to char fall through to int, as %c is silly for uint8_t. + if (!isa<TypedefType>(QT) && QT->isCharType()) { + CS.setKind(ConversionSpecifier::cArg); + LM.setKind(LengthModifier::None); + Precision.setHowSpecified(OptionalAmount::NotSpecified); + HasAlternativeForm = 0; + HasLeadingZeroes = 0; + HasPlusPrefix = 0; + } + // Test for Floating type first as LongDouble can pass isUnsignedIntegerType + else if (QT->isRealFloatingType()) { + CS.setKind(ConversionSpecifier::fArg); + } + else if (QT->isSignedIntegerType()) { + CS.setKind(ConversionSpecifier::dArg); + HasAlternativeForm = 0; + } + else if (QT->isUnsignedIntegerType()) { + CS.setKind(ConversionSpecifier::uArg); + HasAlternativeForm = 0; + HasPlusPrefix = 0; + } else { + llvm_unreachable("Unexpected type"); + } + + return true; +} + +void PrintfSpecifier::toString(raw_ostream &os) const { + // Whilst some features have no defined order, we are using the order + // appearing in the C99 standard (ISO/IEC 9899:1999 (E) 7.19.6.1) + os << "%"; + + // Positional args + if (usesPositionalArg()) { + os << getPositionalArgIndex() << "$"; + } + + // Conversion flags + if (IsLeftJustified) os << "-"; + if (HasPlusPrefix) os << "+"; + if (HasSpacePrefix) os << " "; + if (HasAlternativeForm) os << "#"; + if (HasLeadingZeroes) os << "0"; + + // Minimum field width + FieldWidth.toString(os); + // Precision + Precision.toString(os); + // Length modifier + os << LM.toString(); + // Conversion specifier + os << CS.toString(); +} + +bool PrintfSpecifier::hasValidPlusPrefix() const { + if (!HasPlusPrefix) + return true; + + // The plus prefix only makes sense for signed conversions + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::FreeBSDrArg: + return true; + + default: + return false; + } +} + +bool PrintfSpecifier::hasValidAlternativeForm() const { + if (!HasAlternativeForm) + return true; + + // Alternate form flag only valid with the oxXaAeEfFgG conversions + switch (CS.getKind()) { + case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + case ConversionSpecifier::FreeBSDrArg: + return true; + + default: + return false; + } +} + +bool PrintfSpecifier::hasValidLeadingZeros() const { + if (!HasLeadingZeroes) + return true; + + // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + return true; + + default: + return false; + } +} + +bool PrintfSpecifier::hasValidSpacePrefix() const { + if (!HasSpacePrefix) + return true; + + // The space prefix only makes sense for signed conversions + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + return true; + + default: + return false; + } +} + +bool PrintfSpecifier::hasValidLeftJustified() const { + if (!IsLeftJustified) + return true; + + // The left justified flag is valid for all conversions except n + switch (CS.getKind()) { + case ConversionSpecifier::nArg: + return false; + + default: + return true; + } +} + +bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const { + if (!HasThousandsGrouping) + return true; + + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + return true; + default: + return false; + } +} + +bool PrintfSpecifier::hasValidPrecision() const { + if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) + return true; + + // Precision is only valid with the diouxXaAeEfFgGs conversions + switch (CS.getKind()) { + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + case ConversionSpecifier::sArg: + return true; + + default: + return false; + } +} +bool PrintfSpecifier::hasValidFieldWidth() const { + if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified) + return true; + + // The field width is valid for all conversions except n + switch (CS.getKind()) { + case ConversionSpecifier::nArg: + return false; + + default: + return true; + } +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp b/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp new file mode 100644 index 000000000000..7d67e8a91c8a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ProgramPoint.cpp @@ -0,0 +1,51 @@ +//==- ProgramPoint.cpp - Program Points for Path-Sensitive Analysis -*- C++ -*-/ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface ProgramPoint, which identifies a +// distinct location in a function. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/ProgramPoint.h" + +using namespace clang; + +ProgramPointTag::~ProgramPointTag() {} + +ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K, + const LocationContext *LC, + const ProgramPointTag *tag){ + switch (K) { + default: + llvm_unreachable("Unhandled ProgramPoint kind"); + case ProgramPoint::PreStmtKind: + return PreStmt(S, LC, tag); + case ProgramPoint::PostStmtKind: + return PostStmt(S, LC, tag); + case ProgramPoint::PreLoadKind: + return PreLoad(S, LC, tag); + case ProgramPoint::PostLoadKind: + return PostLoad(S, LC, tag); + case ProgramPoint::PreStoreKind: + return PreStore(S, LC, tag); + case ProgramPoint::PostLValueKind: + return PostLValue(S, LC, tag); + case ProgramPoint::PostStmtPurgeDeadSymbolsKind: + return PostStmtPurgeDeadSymbols(S, LC, tag); + case ProgramPoint::PreStmtPurgeDeadSymbolsKind: + return PreStmtPurgeDeadSymbols(S, LC, tag); + } +} + +SimpleProgramPointTag::SimpleProgramPointTag(StringRef description) + : desc(description) {} + +StringRef SimpleProgramPointTag::getTagDescription() const { + return desc; +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp b/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp new file mode 100644 index 000000000000..5d659ce5851f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/PseudoConstantAnalysis.cpp @@ -0,0 +1,228 @@ +//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file tracks the usage of variables in a Decl body to see if they are +// never written to, implying that they constant. This is useful in static +// analysis to see if a developer might have intended a variable to be const. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Stmt.h" +#include "llvm/ADT/SmallPtrSet.h" +#include <deque> + +using namespace clang; + +// The number of ValueDecls we want to keep track of by default (per-function) +#define VARDECL_SET_SIZE 256 +typedef llvm::SmallPtrSet<const VarDecl*, VARDECL_SET_SIZE> VarDeclSet; + +PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) : + DeclBody(DeclBody), Analyzed(false) { + NonConstantsImpl = new VarDeclSet; + UsedVarsImpl = new VarDeclSet; +} + +PseudoConstantAnalysis::~PseudoConstantAnalysis() { + delete (VarDeclSet*)NonConstantsImpl; + delete (VarDeclSet*)UsedVarsImpl; +} + +// Returns true if the given ValueDecl is never written to in the given DeclBody +bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) { + // Only local and static variables can be pseudoconstants + if (!VD->hasLocalStorage() && !VD->isStaticLocal()) + return false; + + if (!Analyzed) { + RunAnalysis(); + Analyzed = true; + } + + VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; + + return !NonConstants->count(VD); +} + +// Returns true if the variable was used (self assignments don't count) +bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) { + if (!Analyzed) { + RunAnalysis(); + Analyzed = true; + } + + VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; + + return UsedVars->count(VD); +} + +// Returns a Decl from a (Block)DeclRefExpr (if any) +const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) { + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + return DR->getDecl(); + else + return 0; +} + +void PseudoConstantAnalysis::RunAnalysis() { + std::deque<const Stmt *> WorkList; + VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; + VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; + + // Start with the top level statement of the function + WorkList.push_back(DeclBody); + + while (!WorkList.empty()) { + const Stmt *Head = WorkList.front(); + WorkList.pop_front(); + + if (const Expr *Ex = dyn_cast<Expr>(Head)) + Head = Ex->IgnoreParenCasts(); + + switch (Head->getStmtClass()) { + // Case 1: Assignment operators modifying VarDecls + case Stmt::BinaryOperatorClass: { + const BinaryOperator *BO = cast<BinaryOperator>(Head); + // Look for a Decl on the LHS + const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts()); + if (!LHSDecl) + break; + + // We found a binary operator with a DeclRefExpr on the LHS. We now check + // for any of the assignment operators, implying that this Decl is being + // written to. + switch (BO->getOpcode()) { + // Self-assignments don't count as use of a variable + case BO_Assign: { + // Look for a DeclRef on the RHS + const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts()); + + // If the Decls match, we have self-assignment + if (LHSDecl == RHSDecl) + // Do not visit the children + continue; + + } + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_ShlAssign: + case BO_ShrAssign: { + const VarDecl *VD = dyn_cast<VarDecl>(LHSDecl); + // The DeclRefExpr is being assigned to - mark it as non-constant + if (VD) + NonConstants->insert(VD); + break; + } + + default: + break; + } + break; + } + + // Case 2: Pre/post increment/decrement and address of + case Stmt::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(Head); + + // Look for a DeclRef in the subexpression + const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts()); + if (!D) + break; + + // We found a unary operator with a DeclRef as a subexpression. We now + // check for any of the increment/decrement operators, as well as + // addressOf. + switch (UO->getOpcode()) { + case UO_PostDec: + case UO_PostInc: + case UO_PreDec: + case UO_PreInc: + // The DeclRef is being changed - mark it as non-constant + case UO_AddrOf: { + // If we are taking the address of the DeclRefExpr, assume it is + // non-constant. + const VarDecl *VD = dyn_cast<VarDecl>(D); + if (VD) + NonConstants->insert(VD); + break; + } + + default: + break; + } + break; + } + + // Case 3: Reference Declarations + case Stmt::DeclStmtClass: { + const DeclStmt *DS = cast<DeclStmt>(Head); + // Iterate over each decl and see if any of them contain reference decls + for (DeclStmt::const_decl_iterator I = DS->decl_begin(), + E = DS->decl_end(); I != E; ++I) { + // We only care about VarDecls + const VarDecl *VD = dyn_cast<VarDecl>(*I); + if (!VD) + continue; + + // We found a VarDecl; make sure it is a reference type + if (!VD->getType().getTypePtr()->isReferenceType()) + continue; + + // Try to find a Decl in the initializer + const Decl *D = getDecl(VD->getInit()->IgnoreParenCasts()); + if (!D) + break; + + // If the reference is to another var, add the var to the non-constant + // list + if (const VarDecl *RefVD = dyn_cast<VarDecl>(D)) { + NonConstants->insert(RefVD); + continue; + } + } + break; + } + + // Case 4: Variable references + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast<DeclRefExpr>(Head); + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + // Add the Decl to the used list + UsedVars->insert(VD); + continue; + } + break; + } + + // Case 5: Block expressions + case Stmt::BlockExprClass: { + const BlockExpr *B = cast<BlockExpr>(Head); + // Add the body of the block to the list + WorkList.push_back(B->getBody()); + continue; + } + + default: + break; + } // switch (head->getStmtClass()) + + // Add all substatements to the worklist + for (Stmt::const_child_range I = Head->children(); I; ++I) + if (*I) + WorkList.push_back(*I); + } // while (!WorkList.empty()) +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp b/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp new file mode 100644 index 000000000000..a2d19c050829 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ReachableCode.cpp @@ -0,0 +1,334 @@ +//=- ReachableCodePathInsensitive.cpp ---------------------------*- C++ --*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a flow-sensitive, path-insensitive analysis of +// determining reachable blocks within a CFG. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/ReachableCode.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; + +namespace { +class DeadCodeScan { + llvm::BitVector Visited; + llvm::BitVector &Reachable; + SmallVector<const CFGBlock *, 10> WorkList; + + typedef SmallVector<std::pair<const CFGBlock *, const Stmt *>, 12> + DeferredLocsTy; + + DeferredLocsTy DeferredLocs; + +public: + DeadCodeScan(llvm::BitVector &reachable) + : Visited(reachable.size()), + Reachable(reachable) {} + + void enqueue(const CFGBlock *block); + unsigned scanBackwards(const CFGBlock *Start, + clang::reachable_code::Callback &CB); + + bool isDeadCodeRoot(const CFGBlock *Block); + + const Stmt *findDeadCode(const CFGBlock *Block); + + void reportDeadCode(const Stmt *S, + clang::reachable_code::Callback &CB); +}; +} + +void DeadCodeScan::enqueue(const CFGBlock *block) { + unsigned blockID = block->getBlockID(); + if (Reachable[blockID] || Visited[blockID]) + return; + Visited[blockID] = true; + WorkList.push_back(block); +} + +bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { + bool isDeadRoot = true; + + for (CFGBlock::const_pred_iterator I = Block->pred_begin(), + E = Block->pred_end(); I != E; ++I) { + if (const CFGBlock *PredBlock = *I) { + unsigned blockID = PredBlock->getBlockID(); + if (Visited[blockID]) { + isDeadRoot = false; + continue; + } + if (!Reachable[blockID]) { + isDeadRoot = false; + Visited[blockID] = true; + WorkList.push_back(PredBlock); + continue; + } + } + } + + return isDeadRoot; +} + +static bool isValidDeadStmt(const Stmt *S) { + if (S->getLocStart().isInvalid()) + return false; + if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) + return BO->getOpcode() != BO_Comma; + return true; +} + +const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) { + for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I!=E; ++I) + if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) { + const Stmt *S = CS->getStmt(); + if (isValidDeadStmt(S)) + return S; + } + + if (CFGTerminator T = Block->getTerminator()) { + const Stmt *S = T.getStmt(); + if (isValidDeadStmt(S)) + return S; + } + + return 0; +} + +static int SrcCmp(const std::pair<const CFGBlock *, const Stmt *> *p1, + const std::pair<const CFGBlock *, const Stmt *> *p2) { + if (p1->second->getLocStart() < p2->second->getLocStart()) + return -1; + if (p2->second->getLocStart() < p1->second->getLocStart()) + return 1; + return 0; +} + +unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, + clang::reachable_code::Callback &CB) { + + unsigned count = 0; + enqueue(Start); + + while (!WorkList.empty()) { + const CFGBlock *Block = WorkList.pop_back_val(); + + // It is possible that this block has been marked reachable after + // it was enqueued. + if (Reachable[Block->getBlockID()]) + continue; + + // Look for any dead code within the block. + const Stmt *S = findDeadCode(Block); + + if (!S) { + // No dead code. Possibly an empty block. Look at dead predecessors. + for (CFGBlock::const_pred_iterator I = Block->pred_begin(), + E = Block->pred_end(); I != E; ++I) { + if (const CFGBlock *predBlock = *I) + enqueue(predBlock); + } + continue; + } + + // Specially handle macro-expanded code. + if (S->getLocStart().isMacroID()) { + count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable); + continue; + } + + if (isDeadCodeRoot(Block)) { + reportDeadCode(S, CB); + count += clang::reachable_code::ScanReachableFromBlock(Block, Reachable); + } + else { + // Record this statement as the possibly best location in a + // strongly-connected component of dead code for emitting a + // warning. + DeferredLocs.push_back(std::make_pair(Block, S)); + } + } + + // If we didn't find a dead root, then report the dead code with the + // earliest location. + if (!DeferredLocs.empty()) { + llvm::array_pod_sort(DeferredLocs.begin(), DeferredLocs.end(), SrcCmp); + for (DeferredLocsTy::iterator I = DeferredLocs.begin(), + E = DeferredLocs.end(); I != E; ++I) { + const CFGBlock *block = I->first; + if (Reachable[block->getBlockID()]) + continue; + reportDeadCode(I->second, CB); + count += clang::reachable_code::ScanReachableFromBlock(block, Reachable); + } + } + + return count; +} + +static SourceLocation GetUnreachableLoc(const Stmt *S, + SourceRange &R1, + SourceRange &R2) { + R1 = R2 = SourceRange(); + + if (const Expr *Ex = dyn_cast<Expr>(S)) + S = Ex->IgnoreParenImpCasts(); + + switch (S->getStmtClass()) { + case Expr::BinaryOperatorClass: { + const BinaryOperator *BO = cast<BinaryOperator>(S); + return BO->getOperatorLoc(); + } + case Expr::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(S); + R1 = UO->getSubExpr()->getSourceRange(); + return UO->getOperatorLoc(); + } + case Expr::CompoundAssignOperatorClass: { + const CompoundAssignOperator *CAO = cast<CompoundAssignOperator>(S); + R1 = CAO->getLHS()->getSourceRange(); + R2 = CAO->getRHS()->getSourceRange(); + return CAO->getOperatorLoc(); + } + case Expr::BinaryConditionalOperatorClass: + case Expr::ConditionalOperatorClass: { + const AbstractConditionalOperator *CO = + cast<AbstractConditionalOperator>(S); + return CO->getQuestionLoc(); + } + case Expr::MemberExprClass: { + const MemberExpr *ME = cast<MemberExpr>(S); + R1 = ME->getSourceRange(); + return ME->getMemberLoc(); + } + case Expr::ArraySubscriptExprClass: { + const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(S); + R1 = ASE->getLHS()->getSourceRange(); + R2 = ASE->getRHS()->getSourceRange(); + return ASE->getRBracketLoc(); + } + case Expr::CStyleCastExprClass: { + const CStyleCastExpr *CSC = cast<CStyleCastExpr>(S); + R1 = CSC->getSubExpr()->getSourceRange(); + return CSC->getLParenLoc(); + } + case Expr::CXXFunctionalCastExprClass: { + const CXXFunctionalCastExpr *CE = cast <CXXFunctionalCastExpr>(S); + R1 = CE->getSubExpr()->getSourceRange(); + return CE->getLocStart(); + } + case Stmt::CXXTryStmtClass: { + return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc(); + } + case Expr::ObjCBridgedCastExprClass: { + const ObjCBridgedCastExpr *CSC = cast<ObjCBridgedCastExpr>(S); + R1 = CSC->getSubExpr()->getSourceRange(); + return CSC->getLParenLoc(); + } + default: ; + } + R1 = S->getSourceRange(); + return S->getLocStart(); +} + +void DeadCodeScan::reportDeadCode(const Stmt *S, + clang::reachable_code::Callback &CB) { + SourceRange R1, R2; + SourceLocation Loc = GetUnreachableLoc(S, R1, R2); + CB.HandleUnreachable(Loc, R1, R2); +} + +namespace clang { namespace reachable_code { + +void Callback::anchor() { } + +unsigned ScanReachableFromBlock(const CFGBlock *Start, + llvm::BitVector &Reachable) { + unsigned count = 0; + + // Prep work queue + SmallVector<const CFGBlock*, 32> WL; + + // The entry block may have already been marked reachable + // by the caller. + if (!Reachable[Start->getBlockID()]) { + ++count; + Reachable[Start->getBlockID()] = true; + } + + WL.push_back(Start); + + // Find the reachable blocks from 'Start'. + while (!WL.empty()) { + const CFGBlock *item = WL.pop_back_val(); + + // Look at the successors and mark then reachable. + for (CFGBlock::const_succ_iterator I = item->succ_begin(), + E = item->succ_end(); I != E; ++I) + if (const CFGBlock *B = *I) { + unsigned blockID = B->getBlockID(); + if (!Reachable[blockID]) { + Reachable.set(blockID); + WL.push_back(B); + ++count; + } + } + } + return count; +} + +void FindUnreachableCode(AnalysisDeclContext &AC, Callback &CB) { + CFG *cfg = AC.getCFG(); + if (!cfg) + return; + + // Scan for reachable blocks from the entrance of the CFG. + // If there are no unreachable blocks, we're done. + llvm::BitVector reachable(cfg->getNumBlockIDs()); + unsigned numReachable = ScanReachableFromBlock(&cfg->getEntry(), reachable); + if (numReachable == cfg->getNumBlockIDs()) + return; + + // If there aren't explicit EH edges, we should include the 'try' dispatch + // blocks as roots. + if (!AC.getCFGBuildOptions().AddEHEdges) { + for (CFG::try_block_iterator I = cfg->try_blocks_begin(), + E = cfg->try_blocks_end() ; I != E; ++I) { + numReachable += ScanReachableFromBlock(*I, reachable); + } + if (numReachable == cfg->getNumBlockIDs()) + return; + } + + // There are some unreachable blocks. We need to find the root blocks that + // contain code that should be considered unreachable. + for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { + const CFGBlock *block = *I; + // A block may have been marked reachable during this loop. + if (reachable[block->getBlockID()]) + continue; + + DeadCodeScan DS(reachable); + numReachable += DS.scanBackwards(block, CB); + + if (numReachable == cfg->getNumBlockIDs()) + return; + } +} + +}} // end namespace clang::reachable_code diff --git a/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp b/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp new file mode 100644 index 000000000000..f5ce84fe3615 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ScanfFormatString.cpp @@ -0,0 +1,522 @@ +//= ScanfFormatString.cpp - Analysis of printf format strings --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Handling of format string in scanf and friends. The structure of format +// strings for fscanf() are described in C99 7.19.6.2. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/FormatString.h" +#include "FormatStringParsing.h" +#include "clang/Basic/TargetInfo.h" + +using clang::analyze_format_string::ArgType; +using clang::analyze_format_string::FormatStringHandler; +using clang::analyze_format_string::LengthModifier; +using clang::analyze_format_string::OptionalAmount; +using clang::analyze_format_string::ConversionSpecifier; +using clang::analyze_scanf::ScanfConversionSpecifier; +using clang::analyze_scanf::ScanfSpecifier; +using clang::UpdateOnReturn; +using namespace clang; + +typedef clang::analyze_format_string::SpecifierResult<ScanfSpecifier> + ScanfSpecifierResult; + +static bool ParseScanList(FormatStringHandler &H, + ScanfConversionSpecifier &CS, + const char *&Beg, const char *E) { + const char *I = Beg; + const char *start = I - 1; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + // No more characters? + if (I == E) { + H.HandleIncompleteScanList(start, I); + return true; + } + + // Special case: ']' is the first character. + if (*I == ']') { + if (++I == E) { + H.HandleIncompleteScanList(start, I - 1); + return true; + } + } + + // Look for a ']' character which denotes the end of the scan list. + while (*I != ']') { + if (++I == E) { + H.HandleIncompleteScanList(start, I - 1); + return true; + } + } + + CS.setEndScanList(I); + return false; +} + +// FIXME: Much of this is copy-paste from ParsePrintfSpecifier. +// We can possibly refactor. +static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, + const char *&Beg, + const char *E, + unsigned &argIndex, + const LangOptions &LO, + const TargetInfo &Target) { + + using namespace clang::analyze_scanf; + const char *I = Beg; + const char *Start = 0; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + // Look for a '%' character that indicates the start of a format specifier. + for ( ; I != E ; ++I) { + char c = *I; + if (c == '\0') { + // Detect spurious null characters, which are likely errors. + H.HandleNullChar(I); + return true; + } + if (c == '%') { + Start = I++; // Record the start of the format specifier. + break; + } + } + + // No format specifier found? + if (!Start) + return false; + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + ScanfSpecifier FS; + if (ParseArgPosition(H, FS, Start, I, E)) + return true; + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + // Look for '*' flag if it is present. + if (*I == '*') { + FS.setSuppressAssignment(I); + if (++I == E) { + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + } + + // Look for the field width (if any). Unlike printf, this is either + // a fixed integer or isn't present. + const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount(I, E); + if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) { + assert(Amt.getHowSpecified() == OptionalAmount::Constant); + FS.setFieldWidth(Amt); + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + } + + // Look for the length modifier. + if (ParseLengthModifier(FS, I, E, LO, /*scanf=*/true) && I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + // Detect spurious null characters, which are likely errors. + if (*I == '\0') { + H.HandleNullChar(I); + return true; + } + + // Finally, look for the conversion specifier. + const char *conversionPosition = I++; + ScanfConversionSpecifier::Kind k = ScanfConversionSpecifier::InvalidSpecifier; + switch (*conversionPosition) { + default: + break; + case '%': k = ConversionSpecifier::PercentArg; break; + case 'A': k = ConversionSpecifier::AArg; break; + case 'E': k = ConversionSpecifier::EArg; break; + case 'F': k = ConversionSpecifier::FArg; break; + case 'G': k = ConversionSpecifier::GArg; break; + case 'X': k = ConversionSpecifier::XArg; break; + case 'a': k = ConversionSpecifier::aArg; break; + case 'd': k = ConversionSpecifier::dArg; break; + case 'e': k = ConversionSpecifier::eArg; break; + case 'f': k = ConversionSpecifier::fArg; break; + case 'g': k = ConversionSpecifier::gArg; break; + case 'i': k = ConversionSpecifier::iArg; break; + case 'n': k = ConversionSpecifier::nArg; break; + case 'c': k = ConversionSpecifier::cArg; break; + case 'C': k = ConversionSpecifier::CArg; break; + case 'S': k = ConversionSpecifier::SArg; break; + case '[': k = ConversionSpecifier::ScanListArg; break; + case 'u': k = ConversionSpecifier::uArg; break; + case 'x': k = ConversionSpecifier::xArg; break; + case 'o': k = ConversionSpecifier::oArg; break; + case 's': k = ConversionSpecifier::sArg; break; + case 'p': k = ConversionSpecifier::pArg; break; + // Apple extensions + // Apple-specific + case 'D': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::DArg; + break; + case 'O': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::OArg; + break; + case 'U': + if (Target.getTriple().isOSDarwin()) + k = ConversionSpecifier::UArg; + break; + } + ScanfConversionSpecifier CS(conversionPosition, k); + if (k == ScanfConversionSpecifier::ScanListArg) { + if (ParseScanList(H, CS, I, E)) + return true; + } + FS.setConversionSpecifier(CS); + if (CS.consumesDataArgument() && !FS.getSuppressAssignment() + && !FS.usesPositionalArg()) + FS.setArgIndex(argIndex++); + + // FIXME: '%' and '*' doesn't make sense. Issue a warning. + // FIXME: 'ConsumedSoFar' and '*' doesn't make sense. + + if (k == ScanfConversionSpecifier::InvalidSpecifier) { + // Assume the conversion takes one argument. + return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, I - Beg); + } + return ScanfSpecifierResult(Start, FS); +} + +ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { + const ScanfConversionSpecifier &CS = getConversionSpecifier(); + + if (!CS.consumesDataArgument()) + return ArgType::Invalid(); + + switch(CS.getKind()) { + // Signed int. + case ConversionSpecifier::dArg: + case ConversionSpecifier::DArg: + case ConversionSpecifier::iArg: + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.IntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(ArgType::AnyCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.ShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.LongTy); + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsInt64: + return ArgType::PtrTo(ArgType(Ctx.LongLongTy, "__int64")); + case LengthModifier::AsIntMax: + return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); + case LengthModifier::AsSizeT: + // FIXME: ssize_t. + return ArgType(); + case LengthModifier::AsPtrDiff: + return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); + case LengthModifier::AsLongDouble: + // GNU extension. + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + case LengthModifier::AsInt32: + case LengthModifier::AsInt3264: + return ArgType::Invalid(); + } + + // Unsigned int. + case ConversionSpecifier::oArg: + case ConversionSpecifier::OArg: + case ConversionSpecifier::uArg: + case ConversionSpecifier::UArg: + case ConversionSpecifier::xArg: + case ConversionSpecifier::XArg: + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.UnsignedIntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(Ctx.UnsignedCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.UnsignedShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.UnsignedLongTy); + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return ArgType::PtrTo(Ctx.UnsignedLongLongTy); + case LengthModifier::AsInt64: + return ArgType::PtrTo(ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64")); + case LengthModifier::AsIntMax: + return ArgType::PtrTo(ArgType(Ctx.getUIntMaxType(), "uintmax_t")); + case LengthModifier::AsSizeT: + return ArgType::PtrTo(ArgType(Ctx.getSizeType(), "size_t")); + case LengthModifier::AsPtrDiff: + // FIXME: Unsigned version of ptrdiff_t? + return ArgType(); + case LengthModifier::AsLongDouble: + // GNU extension. + return ArgType::PtrTo(Ctx.UnsignedLongLongTy); + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + case LengthModifier::AsInt32: + case LengthModifier::AsInt3264: + return ArgType::Invalid(); + } + + // Float. + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.FloatTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.DoubleTy); + case LengthModifier::AsLongDouble: + return ArgType::PtrTo(Ctx.LongDoubleTy); + default: + return ArgType::Invalid(); + } + + // Char, string and scanlist. + case ConversionSpecifier::cArg: + case ConversionSpecifier::sArg: + case ConversionSpecifier::ScanListArg: + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(ArgType::AnyCharTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(ArgType(Ctx.getWideCharType(), "wchar_t")); + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + return ArgType::PtrTo(ArgType::CStrTy); + default: + return ArgType::Invalid(); + } + case ConversionSpecifier::CArg: + case ConversionSpecifier::SArg: + // FIXME: Mac OS X specific? + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(ArgType(Ctx.getWideCharType(), "wchar_t")); + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + return ArgType::PtrTo(ArgType(ArgType::WCStrTy, "wchar_t *")); + default: + return ArgType::Invalid(); + } + + // Pointer. + case ConversionSpecifier::pArg: + return ArgType::PtrTo(ArgType::CPointerTy); + + // Write-back. + case ConversionSpecifier::nArg: + switch (LM.getKind()) { + case LengthModifier::None: + return ArgType::PtrTo(Ctx.IntTy); + case LengthModifier::AsChar: + return ArgType::PtrTo(Ctx.SignedCharTy); + case LengthModifier::AsShort: + return ArgType::PtrTo(Ctx.ShortTy); + case LengthModifier::AsLong: + return ArgType::PtrTo(Ctx.LongTy); + case LengthModifier::AsLongLong: + case LengthModifier::AsQuad: + return ArgType::PtrTo(Ctx.LongLongTy); + case LengthModifier::AsInt64: + return ArgType::PtrTo(ArgType(Ctx.LongLongTy, "__int64")); + case LengthModifier::AsIntMax: + return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t")); + case LengthModifier::AsSizeT: + return ArgType(); // FIXME: ssize_t + case LengthModifier::AsPtrDiff: + return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t")); + case LengthModifier::AsLongDouble: + return ArgType(); // FIXME: Is this a known extension? + case LengthModifier::AsAllocate: + case LengthModifier::AsMAllocate: + case LengthModifier::AsInt32: + case LengthModifier::AsInt3264: + return ArgType::Invalid(); + } + + default: + break; + } + + return ArgType(); +} + +bool ScanfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, + ASTContext &Ctx) { + if (!QT->isPointerType()) + return false; + + // %n is different from other conversion specifiers; don't try to fix it. + if (CS.getKind() == ConversionSpecifier::nArg) + return false; + + QualType PT = QT->getPointeeType(); + + // If it's an enum, get its underlying type. + if (const EnumType *ETy = QT->getAs<EnumType>()) + QT = ETy->getDecl()->getIntegerType(); + + const BuiltinType *BT = PT->getAs<BuiltinType>(); + if (!BT) + return false; + + // Pointer to a character. + if (PT->isAnyCharacterType()) { + CS.setKind(ConversionSpecifier::sArg); + if (PT->isWideCharType()) + LM.setKind(LengthModifier::AsWideChar); + else + LM.setKind(LengthModifier::None); + return true; + } + + // Figure out the length modifier. + switch (BT->getKind()) { + // no modifier + case BuiltinType::UInt: + case BuiltinType::Int: + case BuiltinType::Float: + LM.setKind(LengthModifier::None); + break; + + // hh + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + LM.setKind(LengthModifier::AsChar); + break; + + // h + case BuiltinType::Short: + case BuiltinType::UShort: + LM.setKind(LengthModifier::AsShort); + break; + + // l + case BuiltinType::Long: + case BuiltinType::ULong: + case BuiltinType::Double: + LM.setKind(LengthModifier::AsLong); + break; + + // ll + case BuiltinType::LongLong: + case BuiltinType::ULongLong: + LM.setKind(LengthModifier::AsLongLong); + break; + + // L + case BuiltinType::LongDouble: + LM.setKind(LengthModifier::AsLongDouble); + break; + + // Don't know. + default: + return false; + } + + // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. + if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus11)) + namedTypeToLengthModifier(PT, LM); + + // If fixing the length modifier was enough, we are done. + if (hasValidLengthModifier(Ctx.getTargetInfo())) { + const analyze_scanf::ArgType &AT = getArgType(Ctx); + if (AT.isValid() && AT.matchesType(Ctx, QT)) + return true; + } + + // Figure out the conversion specifier. + if (PT->isRealFloatingType()) + CS.setKind(ConversionSpecifier::fArg); + else if (PT->isSignedIntegerType()) + CS.setKind(ConversionSpecifier::dArg); + else if (PT->isUnsignedIntegerType()) + CS.setKind(ConversionSpecifier::uArg); + else + llvm_unreachable("Unexpected type"); + + return true; +} + +void ScanfSpecifier::toString(raw_ostream &os) const { + os << "%"; + + if (usesPositionalArg()) + os << getPositionalArgIndex() << "$"; + if (SuppressAssignment) + os << "*"; + + FieldWidth.toString(os); + os << LM.toString(); + os << CS.toString(); +} + +bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, + const char *I, + const char *E, + const LangOptions &LO, + const TargetInfo &Target) { + + unsigned argIndex = 0; + + // Keep looking for a format specifier until we have exhausted the string. + while (I != E) { + const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex, + LO, Target); + // Did a fail-stop error of any kind occur when parsing the specifier? + // If so, don't do any more processing. + if (FSR.shouldStop()) + return true; + // Did we exhaust the string or encounter an error that + // we can recover from? + if (!FSR.hasValue()) + continue; + // We have a format specifier. Pass it to the callback. + if (!H.HandleScanfSpecifier(FSR.getValue(), FSR.getStart(), + I - FSR.getStart())) { + return true; + } + } + assert(I == E && "Format string not exhausted"); + return false; +} diff --git a/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp new file mode 100644 index 000000000000..6e0e1732bae9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/ThreadSafety.cpp @@ -0,0 +1,2655 @@ +//===- ThreadSafety.cpp ----------------------------------------*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A intra-procedural analysis for thread safety (e.g. deadlocks and race +// conditions), based off of an annotation system. +// +// See http://clang.llvm.org/docs/LanguageExtensions.html#thread-safety-annotation-checking +// for more information. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/ThreadSafety.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Basic/OperatorKinds.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <utility> +#include <vector> + +using namespace clang; +using namespace thread_safety; + +// Key method definition +ThreadSafetyHandler::~ThreadSafetyHandler() {} + +namespace { + +/// SExpr implements a simple expression language that is used to store, +/// compare, and pretty-print C++ expressions. Unlike a clang Expr, a SExpr +/// does not capture surface syntax, and it does not distinguish between +/// C++ concepts, like pointers and references, that have no real semantic +/// differences. This simplicity allows SExprs to be meaningfully compared, +/// e.g. +/// (x) = x +/// (*this).foo = this->foo +/// *&a = a +/// +/// Thread-safety analysis works by comparing lock expressions. Within the +/// body of a function, an expression such as "x->foo->bar.mu" will resolve to +/// a particular mutex object at run-time. Subsequent occurrences of the same +/// expression (where "same" means syntactic equality) will refer to the same +/// run-time object if three conditions hold: +/// (1) Local variables in the expression, such as "x" have not changed. +/// (2) Values on the heap that affect the expression have not changed. +/// (3) The expression involves only pure function calls. +/// +/// The current implementation assumes, but does not verify, that multiple uses +/// of the same lock expression satisfies these criteria. +class SExpr { +private: + enum ExprOp { + EOP_Nop, ///< No-op + EOP_Wildcard, ///< Matches anything. + EOP_Universal, ///< Universal lock. + EOP_This, ///< This keyword. + EOP_NVar, ///< Named variable. + EOP_LVar, ///< Local variable. + EOP_Dot, ///< Field access + EOP_Call, ///< Function call + EOP_MCall, ///< Method call + EOP_Index, ///< Array index + EOP_Unary, ///< Unary operation + EOP_Binary, ///< Binary operation + EOP_Unknown ///< Catchall for everything else + }; + + + class SExprNode { + private: + unsigned char Op; ///< Opcode of the root node + unsigned char Flags; ///< Additional opcode-specific data + unsigned short Sz; ///< Number of child nodes + const void* Data; ///< Additional opcode-specific data + + public: + SExprNode(ExprOp O, unsigned F, const void* D) + : Op(static_cast<unsigned char>(O)), + Flags(static_cast<unsigned char>(F)), Sz(1), Data(D) + { } + + unsigned size() const { return Sz; } + void setSize(unsigned S) { Sz = S; } + + ExprOp kind() const { return static_cast<ExprOp>(Op); } + + const NamedDecl* getNamedDecl() const { + assert(Op == EOP_NVar || Op == EOP_LVar || Op == EOP_Dot); + return reinterpret_cast<const NamedDecl*>(Data); + } + + const NamedDecl* getFunctionDecl() const { + assert(Op == EOP_Call || Op == EOP_MCall); + return reinterpret_cast<const NamedDecl*>(Data); + } + + bool isArrow() const { return Op == EOP_Dot && Flags == 1; } + void setArrow(bool A) { Flags = A ? 1 : 0; } + + unsigned arity() const { + switch (Op) { + case EOP_Nop: return 0; + case EOP_Wildcard: return 0; + case EOP_Universal: return 0; + case EOP_NVar: return 0; + case EOP_LVar: return 0; + case EOP_This: return 0; + case EOP_Dot: return 1; + case EOP_Call: return Flags+1; // First arg is function. + case EOP_MCall: return Flags+1; // First arg is implicit obj. + case EOP_Index: return 2; + case EOP_Unary: return 1; + case EOP_Binary: return 2; + case EOP_Unknown: return Flags; + } + return 0; + } + + bool operator==(const SExprNode& Other) const { + // Ignore flags and size -- they don't matter. + return (Op == Other.Op && + Data == Other.Data); + } + + bool operator!=(const SExprNode& Other) const { + return !(*this == Other); + } + + bool matches(const SExprNode& Other) const { + return (*this == Other) || + (Op == EOP_Wildcard) || + (Other.Op == EOP_Wildcard); + } + }; + + + /// \brief Encapsulates the lexical context of a function call. The lexical + /// context includes the arguments to the call, including the implicit object + /// argument. When an attribute containing a mutex expression is attached to + /// a method, the expression may refer to formal parameters of the method. + /// Actual arguments must be substituted for formal parameters to derive + /// the appropriate mutex expression in the lexical context where the function + /// is called. PrevCtx holds the context in which the arguments themselves + /// should be evaluated; multiple calling contexts can be chained together + /// by the lock_returned attribute. + struct CallingContext { + const NamedDecl* AttrDecl; // The decl to which the attribute is attached. + const Expr* SelfArg; // Implicit object argument -- e.g. 'this' + bool SelfArrow; // is Self referred to with -> or .? + unsigned NumArgs; // Number of funArgs + const Expr* const* FunArgs; // Function arguments + CallingContext* PrevCtx; // The previous context; or 0 if none. + + CallingContext(const NamedDecl *D = 0, const Expr *S = 0, + unsigned N = 0, const Expr* const *A = 0, + CallingContext *P = 0) + : AttrDecl(D), SelfArg(S), SelfArrow(false), + NumArgs(N), FunArgs(A), PrevCtx(P) + { } + }; + + typedef SmallVector<SExprNode, 4> NodeVector; + +private: + // A SExpr is a list of SExprNodes in prefix order. The Size field allows + // the list to be traversed as a tree. + NodeVector NodeVec; + +private: + unsigned makeNop() { + NodeVec.push_back(SExprNode(EOP_Nop, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeWildcard() { + NodeVec.push_back(SExprNode(EOP_Wildcard, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeUniversal() { + NodeVec.push_back(SExprNode(EOP_Universal, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeNamedVar(const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_NVar, 0, D)); + return NodeVec.size()-1; + } + + unsigned makeLocalVar(const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_LVar, 0, D)); + return NodeVec.size()-1; + } + + unsigned makeThis() { + NodeVec.push_back(SExprNode(EOP_This, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeDot(const NamedDecl *D, bool Arrow) { + NodeVec.push_back(SExprNode(EOP_Dot, Arrow ? 1 : 0, D)); + return NodeVec.size()-1; + } + + unsigned makeCall(unsigned NumArgs, const NamedDecl *D) { + NodeVec.push_back(SExprNode(EOP_Call, NumArgs, D)); + return NodeVec.size()-1; + } + + // Grab the very first declaration of virtual method D + const CXXMethodDecl* getFirstVirtualDecl(const CXXMethodDecl *D) { + while (true) { + D = D->getCanonicalDecl(); + CXXMethodDecl::method_iterator I = D->begin_overridden_methods(), + E = D->end_overridden_methods(); + if (I == E) + return D; // Method does not override anything + D = *I; // FIXME: this does not work with multiple inheritance. + } + return 0; + } + + unsigned makeMCall(unsigned NumArgs, const CXXMethodDecl *D) { + NodeVec.push_back(SExprNode(EOP_MCall, NumArgs, getFirstVirtualDecl(D))); + return NodeVec.size()-1; + } + + unsigned makeIndex() { + NodeVec.push_back(SExprNode(EOP_Index, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeUnary() { + NodeVec.push_back(SExprNode(EOP_Unary, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeBinary() { + NodeVec.push_back(SExprNode(EOP_Binary, 0, 0)); + return NodeVec.size()-1; + } + + unsigned makeUnknown(unsigned Arity) { + NodeVec.push_back(SExprNode(EOP_Unknown, Arity, 0)); + return NodeVec.size()-1; + } + + inline bool isCalleeArrow(const Expr *E) { + const MemberExpr *ME = dyn_cast<MemberExpr>(E->IgnoreParenCasts()); + return ME ? ME->isArrow() : false; + } + + /// Build an SExpr from the given C++ expression. + /// Recursive function that terminates on DeclRefExpr. + /// Note: this function merely creates a SExpr; it does not check to + /// ensure that the original expression is a valid mutex expression. + /// + /// NDeref returns the number of Derefence and AddressOf operations + /// preceeding the Expr; this is used to decide whether to pretty-print + /// SExprs with . or ->. + unsigned buildSExpr(const Expr *Exp, CallingContext* CallCtx, + int* NDeref = 0) { + if (!Exp) + return 0; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Exp)) { + const NamedDecl *ND = cast<NamedDecl>(DRE->getDecl()->getCanonicalDecl()); + const ParmVarDecl *PV = dyn_cast_or_null<ParmVarDecl>(ND); + if (PV) { + const FunctionDecl *FD = + cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl(); + unsigned i = PV->getFunctionScopeIndex(); + + if (CallCtx && CallCtx->FunArgs && + FD == CallCtx->AttrDecl->getCanonicalDecl()) { + // Substitute call arguments for references to function parameters + assert(i < CallCtx->NumArgs); + return buildSExpr(CallCtx->FunArgs[i], CallCtx->PrevCtx, NDeref); + } + // Map the param back to the param of the original function declaration. + makeNamedVar(FD->getParamDecl(i)); + return 1; + } + // Not a function parameter -- just store the reference. + makeNamedVar(ND); + return 1; + } else if (isa<CXXThisExpr>(Exp)) { + // Substitute parent for 'this' + if (CallCtx && CallCtx->SelfArg) { + if (!CallCtx->SelfArrow && NDeref) + // 'this' is a pointer, but self is not, so need to take address. + --(*NDeref); + return buildSExpr(CallCtx->SelfArg, CallCtx->PrevCtx, NDeref); + } + else { + makeThis(); + return 1; + } + } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { + const NamedDecl *ND = ME->getMemberDecl(); + int ImplicitDeref = ME->isArrow() ? 1 : 0; + unsigned Root = makeDot(ND, false); + unsigned Sz = buildSExpr(ME->getBase(), CallCtx, &ImplicitDeref); + NodeVec[Root].setArrow(ImplicitDeref > 0); + NodeVec[Root].setSize(Sz + 1); + return Sz + 1; + } else if (const CXXMemberCallExpr *CMCE = dyn_cast<CXXMemberCallExpr>(Exp)) { + // When calling a function with a lock_returned attribute, replace + // the function call with the expression in lock_returned. + const CXXMethodDecl *MD = CMCE->getMethodDecl()->getMostRecentDecl(); + if (LockReturnedAttr* At = MD->getAttr<LockReturnedAttr>()) { + CallingContext LRCallCtx(CMCE->getMethodDecl()); + LRCallCtx.SelfArg = CMCE->getImplicitObjectArgument(); + LRCallCtx.SelfArrow = isCalleeArrow(CMCE->getCallee()); + LRCallCtx.NumArgs = CMCE->getNumArgs(); + LRCallCtx.FunArgs = CMCE->getArgs(); + LRCallCtx.PrevCtx = CallCtx; + return buildSExpr(At->getArg(), &LRCallCtx); + } + // Hack to treat smart pointers and iterators as pointers; + // ignore any method named get(). + if (CMCE->getMethodDecl()->getNameAsString() == "get" && + CMCE->getNumArgs() == 0) { + if (NDeref && isCalleeArrow(CMCE->getCallee())) + ++(*NDeref); + return buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx, NDeref); + } + unsigned NumCallArgs = CMCE->getNumArgs(); + unsigned Root = makeMCall(NumCallArgs, CMCE->getMethodDecl()); + unsigned Sz = buildSExpr(CMCE->getImplicitObjectArgument(), CallCtx); + const Expr* const* CallArgs = CMCE->getArgs(); + for (unsigned i = 0; i < NumCallArgs; ++i) { + Sz += buildSExpr(CallArgs[i], CallCtx); + } + NodeVec[Root].setSize(Sz + 1); + return Sz + 1; + } else if (const CallExpr *CE = dyn_cast<CallExpr>(Exp)) { + const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl(); + if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) { + CallingContext LRCallCtx(CE->getDirectCallee()); + LRCallCtx.NumArgs = CE->getNumArgs(); + LRCallCtx.FunArgs = CE->getArgs(); + LRCallCtx.PrevCtx = CallCtx; + return buildSExpr(At->getArg(), &LRCallCtx); + } + // Treat smart pointers and iterators as pointers; + // ignore the * and -> operators. + if (const CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(CE)) { + OverloadedOperatorKind k = OE->getOperator(); + if (k == OO_Star) { + if (NDeref) ++(*NDeref); + return buildSExpr(OE->getArg(0), CallCtx, NDeref); + } + else if (k == OO_Arrow) { + return buildSExpr(OE->getArg(0), CallCtx, NDeref); + } + } + unsigned NumCallArgs = CE->getNumArgs(); + unsigned Root = makeCall(NumCallArgs, 0); + unsigned Sz = buildSExpr(CE->getCallee(), CallCtx); + const Expr* const* CallArgs = CE->getArgs(); + for (unsigned i = 0; i < NumCallArgs; ++i) { + Sz += buildSExpr(CallArgs[i], CallCtx); + } + NodeVec[Root].setSize(Sz+1); + return Sz+1; + } else if (const BinaryOperator *BOE = dyn_cast<BinaryOperator>(Exp)) { + unsigned Root = makeBinary(); + unsigned Sz = buildSExpr(BOE->getLHS(), CallCtx); + Sz += buildSExpr(BOE->getRHS(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; + } else if (const UnaryOperator *UOE = dyn_cast<UnaryOperator>(Exp)) { + // Ignore & and * operators -- they're no-ops. + // However, we try to figure out whether the expression is a pointer, + // so we can use . and -> appropriately in error messages. + if (UOE->getOpcode() == UO_Deref) { + if (NDeref) ++(*NDeref); + return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref); + } + if (UOE->getOpcode() == UO_AddrOf) { + if (DeclRefExpr* DRE = dyn_cast<DeclRefExpr>(UOE->getSubExpr())) { + if (DRE->getDecl()->isCXXInstanceMember()) { + // This is a pointer-to-member expression, e.g. &MyClass::mu_. + // We interpret this syntax specially, as a wildcard. + unsigned Root = makeDot(DRE->getDecl(), false); + makeWildcard(); + NodeVec[Root].setSize(2); + return 2; + } + } + if (NDeref) --(*NDeref); + return buildSExpr(UOE->getSubExpr(), CallCtx, NDeref); + } + unsigned Root = makeUnary(); + unsigned Sz = buildSExpr(UOE->getSubExpr(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; + } else if (const ArraySubscriptExpr *ASE = + dyn_cast<ArraySubscriptExpr>(Exp)) { + unsigned Root = makeIndex(); + unsigned Sz = buildSExpr(ASE->getBase(), CallCtx); + Sz += buildSExpr(ASE->getIdx(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; + } else if (const AbstractConditionalOperator *CE = + dyn_cast<AbstractConditionalOperator>(Exp)) { + unsigned Root = makeUnknown(3); + unsigned Sz = buildSExpr(CE->getCond(), CallCtx); + Sz += buildSExpr(CE->getTrueExpr(), CallCtx); + Sz += buildSExpr(CE->getFalseExpr(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; + } else if (const ChooseExpr *CE = dyn_cast<ChooseExpr>(Exp)) { + unsigned Root = makeUnknown(3); + unsigned Sz = buildSExpr(CE->getCond(), CallCtx); + Sz += buildSExpr(CE->getLHS(), CallCtx); + Sz += buildSExpr(CE->getRHS(), CallCtx); + NodeVec[Root].setSize(Sz); + return Sz; + } else if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) { + return buildSExpr(CE->getSubExpr(), CallCtx, NDeref); + } else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { + return buildSExpr(PE->getSubExpr(), CallCtx, NDeref); + } else if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Exp)) { + return buildSExpr(EWC->getSubExpr(), CallCtx, NDeref); + } else if (const CXXBindTemporaryExpr *E = dyn_cast<CXXBindTemporaryExpr>(Exp)) { + return buildSExpr(E->getSubExpr(), CallCtx, NDeref); + } else if (isa<CharacterLiteral>(Exp) || + isa<CXXNullPtrLiteralExpr>(Exp) || + isa<GNUNullExpr>(Exp) || + isa<CXXBoolLiteralExpr>(Exp) || + isa<FloatingLiteral>(Exp) || + isa<ImaginaryLiteral>(Exp) || + isa<IntegerLiteral>(Exp) || + isa<StringLiteral>(Exp) || + isa<ObjCStringLiteral>(Exp)) { + makeNop(); + return 1; // FIXME: Ignore literals for now + } else { + makeNop(); + return 1; // Ignore. FIXME: mark as invalid expression? + } + } + + /// \brief Construct a SExpr from an expression. + /// \param MutexExp The original mutex expression within an attribute + /// \param DeclExp An expression involving the Decl on which the attribute + /// occurs. + /// \param D The declaration to which the lock/unlock attribute is attached. + void buildSExprFromExpr(const Expr *MutexExp, const Expr *DeclExp, + const NamedDecl *D, VarDecl *SelfDecl = 0) { + CallingContext CallCtx(D); + + if (MutexExp) { + if (const StringLiteral* SLit = dyn_cast<StringLiteral>(MutexExp)) { + if (SLit->getString() == StringRef("*")) + // The "*" expr is a universal lock, which essentially turns off + // checks until it is removed from the lockset. + makeUniversal(); + else + // Ignore other string literals for now. + makeNop(); + return; + } + } + + // If we are processing a raw attribute expression, with no substitutions. + if (DeclExp == 0) { + buildSExpr(MutexExp, 0); + return; + } + + // Examine DeclExp to find SelfArg and FunArgs, which are used to substitute + // for formal parameters when we call buildMutexID later. + if (const MemberExpr *ME = dyn_cast<MemberExpr>(DeclExp)) { + CallCtx.SelfArg = ME->getBase(); + CallCtx.SelfArrow = ME->isArrow(); + } else if (const CXXMemberCallExpr *CE = + dyn_cast<CXXMemberCallExpr>(DeclExp)) { + CallCtx.SelfArg = CE->getImplicitObjectArgument(); + CallCtx.SelfArrow = isCalleeArrow(CE->getCallee()); + CallCtx.NumArgs = CE->getNumArgs(); + CallCtx.FunArgs = CE->getArgs(); + } else if (const CallExpr *CE = dyn_cast<CallExpr>(DeclExp)) { + CallCtx.NumArgs = CE->getNumArgs(); + CallCtx.FunArgs = CE->getArgs(); + } else if (const CXXConstructExpr *CE = + dyn_cast<CXXConstructExpr>(DeclExp)) { + CallCtx.SelfArg = 0; // Will be set below + CallCtx.NumArgs = CE->getNumArgs(); + CallCtx.FunArgs = CE->getArgs(); + } else if (D && isa<CXXDestructorDecl>(D)) { + // There's no such thing as a "destructor call" in the AST. + CallCtx.SelfArg = DeclExp; + } + + // Hack to handle constructors, where self cannot be recovered from + // the expression. + if (SelfDecl && !CallCtx.SelfArg) { + DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue, + SelfDecl->getLocation()); + CallCtx.SelfArg = &SelfDRE; + + // If the attribute has no arguments, then assume the argument is "this". + if (MutexExp == 0) + buildSExpr(CallCtx.SelfArg, 0); + else // For most attributes. + buildSExpr(MutexExp, &CallCtx); + return; + } + + // If the attribute has no arguments, then assume the argument is "this". + if (MutexExp == 0) + buildSExpr(CallCtx.SelfArg, 0); + else // For most attributes. + buildSExpr(MutexExp, &CallCtx); + } + + /// \brief Get index of next sibling of node i. + unsigned getNextSibling(unsigned i) const { + return i + NodeVec[i].size(); + } + +public: + explicit SExpr(clang::Decl::EmptyShell e) { NodeVec.clear(); } + + /// \param MutexExp The original mutex expression within an attribute + /// \param DeclExp An expression involving the Decl on which the attribute + /// occurs. + /// \param D The declaration to which the lock/unlock attribute is attached. + /// Caller must check isValid() after construction. + SExpr(const Expr* MutexExp, const Expr *DeclExp, const NamedDecl* D, + VarDecl *SelfDecl=0) { + buildSExprFromExpr(MutexExp, DeclExp, D, SelfDecl); + } + + /// Return true if this is a valid decl sequence. + /// Caller must call this by hand after construction to handle errors. + bool isValid() const { + return !NodeVec.empty(); + } + + bool shouldIgnore() const { + // Nop is a mutex that we have decided to deliberately ignore. + assert(NodeVec.size() > 0 && "Invalid Mutex"); + return NodeVec[0].kind() == EOP_Nop; + } + + bool isUniversal() const { + assert(NodeVec.size() > 0 && "Invalid Mutex"); + return NodeVec[0].kind() == EOP_Universal; + } + + /// Issue a warning about an invalid lock expression + static void warnInvalidLock(ThreadSafetyHandler &Handler, + const Expr *MutexExp, + const Expr *DeclExp, const NamedDecl* D) { + SourceLocation Loc; + if (DeclExp) + Loc = DeclExp->getExprLoc(); + + // FIXME: add a note about the attribute location in MutexExp or D + if (Loc.isValid()) + Handler.handleInvalidLockExp(Loc); + } + + bool operator==(const SExpr &other) const { + return NodeVec == other.NodeVec; + } + + bool operator!=(const SExpr &other) const { + return !(*this == other); + } + + bool matches(const SExpr &Other, unsigned i = 0, unsigned j = 0) const { + if (NodeVec[i].matches(Other.NodeVec[j])) { + unsigned ni = NodeVec[i].arity(); + unsigned nj = Other.NodeVec[j].arity(); + unsigned n = (ni < nj) ? ni : nj; + bool Result = true; + unsigned ci = i+1; // first child of i + unsigned cj = j+1; // first child of j + for (unsigned k = 0; k < n; + ++k, ci=getNextSibling(ci), cj = Other.getNextSibling(cj)) { + Result = Result && matches(Other, ci, cj); + } + return Result; + } + return false; + } + + // A partial match between a.mu and b.mu returns true a and b have the same + // type (and thus mu refers to the same mutex declaration), regardless of + // whether a and b are different objects or not. + bool partiallyMatches(const SExpr &Other) const { + if (NodeVec[0].kind() == EOP_Dot) + return NodeVec[0].matches(Other.NodeVec[0]); + return false; + } + + /// \brief Pretty print a lock expression for use in error messages. + std::string toString(unsigned i = 0) const { + assert(isValid()); + if (i >= NodeVec.size()) + return ""; + + const SExprNode* N = &NodeVec[i]; + switch (N->kind()) { + case EOP_Nop: + return "_"; + case EOP_Wildcard: + return "(?)"; + case EOP_Universal: + return "*"; + case EOP_This: + return "this"; + case EOP_NVar: + case EOP_LVar: { + return N->getNamedDecl()->getNameAsString(); + } + case EOP_Dot: { + if (NodeVec[i+1].kind() == EOP_Wildcard) { + std::string S = "&"; + S += N->getNamedDecl()->getQualifiedNameAsString(); + return S; + } + std::string FieldName = N->getNamedDecl()->getNameAsString(); + if (NodeVec[i+1].kind() == EOP_This) + return FieldName; + + std::string S = toString(i+1); + if (N->isArrow()) + return S + "->" + FieldName; + else + return S + "." + FieldName; + } + case EOP_Call: { + std::string S = toString(i+1) + "("; + unsigned NumArgs = N->arity()-1; + unsigned ci = getNextSibling(i+1); + for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) { + S += toString(ci); + if (k+1 < NumArgs) S += ","; + } + S += ")"; + return S; + } + case EOP_MCall: { + std::string S = ""; + if (NodeVec[i+1].kind() != EOP_This) + S = toString(i+1) + "."; + if (const NamedDecl *D = N->getFunctionDecl()) + S += D->getNameAsString() + "("; + else + S += "#("; + unsigned NumArgs = N->arity()-1; + unsigned ci = getNextSibling(i+1); + for (unsigned k=0; k<NumArgs; ++k, ci = getNextSibling(ci)) { + S += toString(ci); + if (k+1 < NumArgs) S += ","; + } + S += ")"; + return S; + } + case EOP_Index: { + std::string S1 = toString(i+1); + std::string S2 = toString(i+1 + NodeVec[i+1].size()); + return S1 + "[" + S2 + "]"; + } + case EOP_Unary: { + std::string S = toString(i+1); + return "#" + S; + } + case EOP_Binary: { + std::string S1 = toString(i+1); + std::string S2 = toString(i+1 + NodeVec[i+1].size()); + return "(" + S1 + "#" + S2 + ")"; + } + case EOP_Unknown: { + unsigned NumChildren = N->arity(); + if (NumChildren == 0) + return "(...)"; + std::string S = "("; + unsigned ci = i+1; + for (unsigned j = 0; j < NumChildren; ++j, ci = getNextSibling(ci)) { + S += toString(ci); + if (j+1 < NumChildren) S += "#"; + } + S += ")"; + return S; + } + } + return ""; + } +}; + + + +/// \brief A short list of SExprs +class MutexIDList : public SmallVector<SExpr, 3> { +public: + /// \brief Return true if the list contains the specified SExpr + /// Performs a linear search, because these lists are almost always very small. + bool contains(const SExpr& M) { + for (iterator I=begin(),E=end(); I != E; ++I) + if ((*I) == M) return true; + return false; + } + + /// \brief Push M onto list, bud discard duplicates + void push_back_nodup(const SExpr& M) { + if (!contains(M)) push_back(M); + } +}; + + + +/// \brief This is a helper class that stores info about the most recent +/// accquire of a Lock. +/// +/// The main body of the analysis maps MutexIDs to LockDatas. +struct LockData { + SourceLocation AcquireLoc; + + /// \brief LKind stores whether a lock is held shared or exclusively. + /// Note that this analysis does not currently support either re-entrant + /// locking or lock "upgrading" and "downgrading" between exclusive and + /// shared. + /// + /// FIXME: add support for re-entrant locking and lock up/downgrading + LockKind LKind; + bool Asserted; // for asserted locks + bool Managed; // for ScopedLockable objects + SExpr UnderlyingMutex; // for ScopedLockable objects + + LockData(SourceLocation AcquireLoc, LockKind LKind, bool M=false, + bool Asrt=false) + : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(Asrt), Managed(M), + UnderlyingMutex(Decl::EmptyShell()) + {} + + LockData(SourceLocation AcquireLoc, LockKind LKind, const SExpr &Mu) + : AcquireLoc(AcquireLoc), LKind(LKind), Asserted(false), Managed(false), + UnderlyingMutex(Mu) + {} + + bool operator==(const LockData &other) const { + return AcquireLoc == other.AcquireLoc && LKind == other.LKind; + } + + bool operator!=(const LockData &other) const { + return !(*this == other); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(AcquireLoc.getRawEncoding()); + ID.AddInteger(LKind); + } + + bool isAtLeast(LockKind LK) { + return (LK == LK_Shared) || (LKind == LK_Exclusive); + } +}; + + +/// \brief A FactEntry stores a single fact that is known at a particular point +/// in the program execution. Currently, this is information regarding a lock +/// that is held at that point. +struct FactEntry { + SExpr MutID; + LockData LDat; + + FactEntry(const SExpr& M, const LockData& L) + : MutID(M), LDat(L) + { } +}; + + +typedef unsigned short FactID; + +/// \brief FactManager manages the memory for all facts that are created during +/// the analysis of a single routine. +class FactManager { +private: + std::vector<FactEntry> Facts; + +public: + FactID newLock(const SExpr& M, const LockData& L) { + Facts.push_back(FactEntry(M,L)); + return static_cast<unsigned short>(Facts.size() - 1); + } + + const FactEntry& operator[](FactID F) const { return Facts[F]; } + FactEntry& operator[](FactID F) { return Facts[F]; } +}; + + +/// \brief A FactSet is the set of facts that are known to be true at a +/// particular program point. FactSets must be small, because they are +/// frequently copied, and are thus implemented as a set of indices into a +/// table maintained by a FactManager. A typical FactSet only holds 1 or 2 +/// locks, so we can get away with doing a linear search for lookup. Note +/// that a hashtable or map is inappropriate in this case, because lookups +/// may involve partial pattern matches, rather than exact matches. +class FactSet { +private: + typedef SmallVector<FactID, 4> FactVec; + + FactVec FactIDs; + +public: + typedef FactVec::iterator iterator; + typedef FactVec::const_iterator const_iterator; + + iterator begin() { return FactIDs.begin(); } + const_iterator begin() const { return FactIDs.begin(); } + + iterator end() { return FactIDs.end(); } + const_iterator end() const { return FactIDs.end(); } + + bool isEmpty() const { return FactIDs.size() == 0; } + + FactID addLock(FactManager& FM, const SExpr& M, const LockData& L) { + FactID F = FM.newLock(M, L); + FactIDs.push_back(F); + return F; + } + + bool removeLock(FactManager& FM, const SExpr& M) { + unsigned n = FactIDs.size(); + if (n == 0) + return false; + + for (unsigned i = 0; i < n-1; ++i) { + if (FM[FactIDs[i]].MutID.matches(M)) { + FactIDs[i] = FactIDs[n-1]; + FactIDs.pop_back(); + return true; + } + } + if (FM[FactIDs[n-1]].MutID.matches(M)) { + FactIDs.pop_back(); + return true; + } + return false; + } + + // Returns an iterator + iterator findLockIter(FactManager &FM, const SExpr &M) { + for (iterator I = begin(), E = end(); I != E; ++I) { + const SExpr &Exp = FM[*I].MutID; + if (Exp.matches(M)) + return I; + } + return end(); + } + + LockData* findLock(FactManager &FM, const SExpr &M) const { + for (const_iterator I = begin(), E = end(); I != E; ++I) { + const SExpr &Exp = FM[*I].MutID; + if (Exp.matches(M)) + return &FM[*I].LDat; + } + return 0; + } + + LockData* findLockUniv(FactManager &FM, const SExpr &M) const { + for (const_iterator I = begin(), E = end(); I != E; ++I) { + const SExpr &Exp = FM[*I].MutID; + if (Exp.matches(M) || Exp.isUniversal()) + return &FM[*I].LDat; + } + return 0; + } + + FactEntry* findPartialMatch(FactManager &FM, const SExpr &M) const { + for (const_iterator I=begin(), E=end(); I != E; ++I) { + const SExpr& Exp = FM[*I].MutID; + if (Exp.partiallyMatches(M)) return &FM[*I]; + } + return 0; + } +}; + + + +/// A Lockset maps each SExpr (defined above) to information about how it has +/// been locked. +typedef llvm::ImmutableMap<SExpr, LockData> Lockset; +typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext; + +class LocalVariableMap; + +/// A side (entry or exit) of a CFG node. +enum CFGBlockSide { CBS_Entry, CBS_Exit }; + +/// CFGBlockInfo is a struct which contains all the information that is +/// maintained for each block in the CFG. See LocalVariableMap for more +/// information about the contexts. +struct CFGBlockInfo { + FactSet EntrySet; // Lockset held at entry to block + FactSet ExitSet; // Lockset held at exit from block + LocalVarContext EntryContext; // Context held at entry to block + LocalVarContext ExitContext; // Context held at exit from block + SourceLocation EntryLoc; // Location of first statement in block + SourceLocation ExitLoc; // Location of last statement in block. + unsigned EntryIndex; // Used to replay contexts later + bool Reachable; // Is this block reachable? + + const FactSet &getSet(CFGBlockSide Side) const { + return Side == CBS_Entry ? EntrySet : ExitSet; + } + SourceLocation getLocation(CFGBlockSide Side) const { + return Side == CBS_Entry ? EntryLoc : ExitLoc; + } + +private: + CFGBlockInfo(LocalVarContext EmptyCtx) + : EntryContext(EmptyCtx), ExitContext(EmptyCtx), Reachable(false) + { } + +public: + static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M); +}; + + + +// A LocalVariableMap maintains a map from local variables to their currently +// valid definitions. It provides SSA-like functionality when traversing the +// CFG. Like SSA, each definition or assignment to a variable is assigned a +// unique name (an integer), which acts as the SSA name for that definition. +// The total set of names is shared among all CFG basic blocks. +// Unlike SSA, we do not rewrite expressions to replace local variables declrefs +// with their SSA-names. Instead, we compute a Context for each point in the +// code, which maps local variables to the appropriate SSA-name. This map +// changes with each assignment. +// +// The map is computed in a single pass over the CFG. Subsequent analyses can +// then query the map to find the appropriate Context for a statement, and use +// that Context to look up the definitions of variables. +class LocalVariableMap { +public: + typedef LocalVarContext Context; + + /// A VarDefinition consists of an expression, representing the value of the + /// variable, along with the context in which that expression should be + /// interpreted. A reference VarDefinition does not itself contain this + /// information, but instead contains a pointer to a previous VarDefinition. + struct VarDefinition { + public: + friend class LocalVariableMap; + + const NamedDecl *Dec; // The original declaration for this variable. + const Expr *Exp; // The expression for this variable, OR + unsigned Ref; // Reference to another VarDefinition + Context Ctx; // The map with which Exp should be interpreted. + + bool isReference() { return !Exp; } + + private: + // Create ordinary variable definition + VarDefinition(const NamedDecl *D, const Expr *E, Context C) + : Dec(D), Exp(E), Ref(0), Ctx(C) + { } + + // Create reference to previous definition + VarDefinition(const NamedDecl *D, unsigned R, Context C) + : Dec(D), Exp(0), Ref(R), Ctx(C) + { } + }; + +private: + Context::Factory ContextFactory; + std::vector<VarDefinition> VarDefinitions; + std::vector<unsigned> CtxIndices; + std::vector<std::pair<Stmt*, Context> > SavedContexts; + +public: + LocalVariableMap() { + // index 0 is a placeholder for undefined variables (aka phi-nodes). + VarDefinitions.push_back(VarDefinition(0, 0u, getEmptyContext())); + } + + /// Look up a definition, within the given context. + const VarDefinition* lookup(const NamedDecl *D, Context Ctx) { + const unsigned *i = Ctx.lookup(D); + if (!i) + return 0; + assert(*i < VarDefinitions.size()); + return &VarDefinitions[*i]; + } + + /// Look up the definition for D within the given context. Returns + /// NULL if the expression is not statically known. If successful, also + /// modifies Ctx to hold the context of the return Expr. + const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) { + const unsigned *P = Ctx.lookup(D); + if (!P) + return 0; + + unsigned i = *P; + while (i > 0) { + if (VarDefinitions[i].Exp) { + Ctx = VarDefinitions[i].Ctx; + return VarDefinitions[i].Exp; + } + i = VarDefinitions[i].Ref; + } + return 0; + } + + Context getEmptyContext() { return ContextFactory.getEmptyMap(); } + + /// Return the next context after processing S. This function is used by + /// clients of the class to get the appropriate context when traversing the + /// CFG. It must be called for every assignment or DeclStmt. + Context getNextContext(unsigned &CtxIndex, Stmt *S, Context C) { + if (SavedContexts[CtxIndex+1].first == S) { + CtxIndex++; + Context Result = SavedContexts[CtxIndex].second; + return Result; + } + return C; + } + + void dumpVarDefinitionName(unsigned i) { + if (i == 0) { + llvm::errs() << "Undefined"; + return; + } + const NamedDecl *Dec = VarDefinitions[i].Dec; + if (!Dec) { + llvm::errs() << "<<NULL>>"; + return; + } + Dec->printName(llvm::errs()); + llvm::errs() << "." << i << " " << ((const void*) Dec); + } + + /// Dumps an ASCII representation of the variable map to llvm::errs() + void dump() { + for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) { + const Expr *Exp = VarDefinitions[i].Exp; + unsigned Ref = VarDefinitions[i].Ref; + + dumpVarDefinitionName(i); + llvm::errs() << " = "; + if (Exp) Exp->dump(); + else { + dumpVarDefinitionName(Ref); + llvm::errs() << "\n"; + } + } + } + + /// Dumps an ASCII representation of a Context to llvm::errs() + void dumpContext(Context C) { + for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) { + const NamedDecl *D = I.getKey(); + D->printName(llvm::errs()); + const unsigned *i = C.lookup(D); + llvm::errs() << " -> "; + dumpVarDefinitionName(*i); + llvm::errs() << "\n"; + } + } + + /// Builds the variable map. + void traverseCFG(CFG *CFGraph, PostOrderCFGView *SortedGraph, + std::vector<CFGBlockInfo> &BlockInfo); + +protected: + // Get the current context index + unsigned getContextIndex() { return SavedContexts.size()-1; } + + // Save the current context for later replay + void saveContext(Stmt *S, Context C) { + SavedContexts.push_back(std::make_pair(S,C)); + } + + // Adds a new definition to the given context, and returns a new context. + // This method should be called when declaring a new variable. + Context addDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) { + assert(!Ctx.contains(D)); + unsigned newID = VarDefinitions.size(); + Context NewCtx = ContextFactory.add(Ctx, D, newID); + VarDefinitions.push_back(VarDefinition(D, Exp, Ctx)); + return NewCtx; + } + + // Add a new reference to an existing definition. + Context addReference(const NamedDecl *D, unsigned i, Context Ctx) { + unsigned newID = VarDefinitions.size(); + Context NewCtx = ContextFactory.add(Ctx, D, newID); + VarDefinitions.push_back(VarDefinition(D, i, Ctx)); + return NewCtx; + } + + // Updates a definition only if that definition is already in the map. + // This method should be called when assigning to an existing variable. + Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) { + if (Ctx.contains(D)) { + unsigned newID = VarDefinitions.size(); + Context NewCtx = ContextFactory.remove(Ctx, D); + NewCtx = ContextFactory.add(NewCtx, D, newID); + VarDefinitions.push_back(VarDefinition(D, Exp, Ctx)); + return NewCtx; + } + return Ctx; + } + + // Removes a definition from the context, but keeps the variable name + // as a valid variable. The index 0 is a placeholder for cleared definitions. + Context clearDefinition(const NamedDecl *D, Context Ctx) { + Context NewCtx = Ctx; + if (NewCtx.contains(D)) { + NewCtx = ContextFactory.remove(NewCtx, D); + NewCtx = ContextFactory.add(NewCtx, D, 0); + } + return NewCtx; + } + + // Remove a definition entirely frmo the context. + Context removeDefinition(const NamedDecl *D, Context Ctx) { + Context NewCtx = Ctx; + if (NewCtx.contains(D)) { + NewCtx = ContextFactory.remove(NewCtx, D); + } + return NewCtx; + } + + Context intersectContexts(Context C1, Context C2); + Context createReferenceContext(Context C); + void intersectBackEdge(Context C1, Context C2); + + friend class VarMapBuilder; +}; + + +// This has to be defined after LocalVariableMap. +CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) { + return CFGBlockInfo(M.getEmptyContext()); +} + + +/// Visitor which builds a LocalVariableMap +class VarMapBuilder : public StmtVisitor<VarMapBuilder> { +public: + LocalVariableMap* VMap; + LocalVariableMap::Context Ctx; + + VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C) + : VMap(VM), Ctx(C) {} + + void VisitDeclStmt(DeclStmt *S); + void VisitBinaryOperator(BinaryOperator *BO); +}; + + +// Add new local variables to the variable map +void VarMapBuilder::VisitDeclStmt(DeclStmt *S) { + bool modifiedCtx = false; + DeclGroupRef DGrp = S->getDeclGroup(); + for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { + if (VarDecl *VD = dyn_cast_or_null<VarDecl>(*I)) { + Expr *E = VD->getInit(); + + // Add local variables with trivial type to the variable map + QualType T = VD->getType(); + if (T.isTrivialType(VD->getASTContext())) { + Ctx = VMap->addDefinition(VD, E, Ctx); + modifiedCtx = true; + } + } + } + if (modifiedCtx) + VMap->saveContext(S, Ctx); +} + +// Update local variable definitions in variable map +void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) { + if (!BO->isAssignmentOp()) + return; + + Expr *LHSExp = BO->getLHS()->IgnoreParenCasts(); + + // Update the variable map and current context. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHSExp)) { + ValueDecl *VDec = DRE->getDecl(); + if (Ctx.lookup(VDec)) { + if (BO->getOpcode() == BO_Assign) + Ctx = VMap->updateDefinition(VDec, BO->getRHS(), Ctx); + else + // FIXME -- handle compound assignment operators + Ctx = VMap->clearDefinition(VDec, Ctx); + VMap->saveContext(BO, Ctx); + } + } +} + + +// Computes the intersection of two contexts. The intersection is the +// set of variables which have the same definition in both contexts; +// variables with different definitions are discarded. +LocalVariableMap::Context +LocalVariableMap::intersectContexts(Context C1, Context C2) { + Context Result = C1; + for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { + const NamedDecl *Dec = I.getKey(); + unsigned i1 = I.getData(); + const unsigned *i2 = C2.lookup(Dec); + if (!i2) // variable doesn't exist on second path + Result = removeDefinition(Dec, Result); + else if (*i2 != i1) // variable exists, but has different definition + Result = clearDefinition(Dec, Result); + } + return Result; +} + +// For every variable in C, create a new variable that refers to the +// definition in C. Return a new context that contains these new variables. +// (We use this for a naive implementation of SSA on loop back-edges.) +LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) { + Context Result = getEmptyContext(); + for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) { + const NamedDecl *Dec = I.getKey(); + unsigned i = I.getData(); + Result = addReference(Dec, i, Result); + } + return Result; +} + +// This routine also takes the intersection of C1 and C2, but it does so by +// altering the VarDefinitions. C1 must be the result of an earlier call to +// createReferenceContext. +void LocalVariableMap::intersectBackEdge(Context C1, Context C2) { + for (Context::iterator I = C1.begin(), E = C1.end(); I != E; ++I) { + const NamedDecl *Dec = I.getKey(); + unsigned i1 = I.getData(); + VarDefinition *VDef = &VarDefinitions[i1]; + assert(VDef->isReference()); + + const unsigned *i2 = C2.lookup(Dec); + if (!i2 || (*i2 != i1)) + VDef->Ref = 0; // Mark this variable as undefined + } +} + + +// Traverse the CFG in topological order, so all predecessors of a block +// (excluding back-edges) are visited before the block itself. At +// each point in the code, we calculate a Context, which holds the set of +// variable definitions which are visible at that point in execution. +// Visible variables are mapped to their definitions using an array that +// contains all definitions. +// +// At join points in the CFG, the set is computed as the intersection of +// the incoming sets along each edge, E.g. +// +// { Context | VarDefinitions } +// int x = 0; { x -> x1 | x1 = 0 } +// int y = 0; { x -> x1, y -> y1 | y1 = 0, x1 = 0 } +// if (b) x = 1; { x -> x2, y -> y1 | x2 = 1, y1 = 0, ... } +// else x = 2; { x -> x3, y -> y1 | x3 = 2, x2 = 1, ... } +// ... { y -> y1 (x is unknown) | x3 = 2, x2 = 1, ... } +// +// This is essentially a simpler and more naive version of the standard SSA +// algorithm. Those definitions that remain in the intersection are from blocks +// that strictly dominate the current block. We do not bother to insert proper +// phi nodes, because they are not used in our analysis; instead, wherever +// a phi node would be required, we simply remove that definition from the +// context (E.g. x above). +// +// The initial traversal does not capture back-edges, so those need to be +// handled on a separate pass. Whenever the first pass encounters an +// incoming back edge, it duplicates the context, creating new definitions +// that refer back to the originals. (These correspond to places where SSA +// might have to insert a phi node.) On the second pass, these definitions are +// set to NULL if the variable has changed on the back-edge (i.e. a phi +// node was actually required.) E.g. +// +// { Context | VarDefinitions } +// int x = 0, y = 0; { x -> x1, y -> y1 | y1 = 0, x1 = 0 } +// while (b) { x -> x2, y -> y1 | [1st:] x2=x1; [2nd:] x2=NULL; } +// x = x+1; { x -> x3, y -> y1 | x3 = x2 + 1, ... } +// ... { y -> y1 | x3 = 2, x2 = 1, ... } +// +void LocalVariableMap::traverseCFG(CFG *CFGraph, + PostOrderCFGView *SortedGraph, + std::vector<CFGBlockInfo> &BlockInfo) { + PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph); + + CtxIndices.resize(CFGraph->getNumBlockIDs()); + + for (PostOrderCFGView::iterator I = SortedGraph->begin(), + E = SortedGraph->end(); I!= E; ++I) { + const CFGBlock *CurrBlock = *I; + int CurrBlockID = CurrBlock->getBlockID(); + CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; + + VisitedBlocks.insert(CurrBlock); + + // Calculate the entry context for the current block + bool HasBackEdges = false; + bool CtxInit = true; + for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), + PE = CurrBlock->pred_end(); PI != PE; ++PI) { + // if *PI -> CurrBlock is a back edge, so skip it + if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) { + HasBackEdges = true; + continue; + } + + int PrevBlockID = (*PI)->getBlockID(); + CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; + + if (CtxInit) { + CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext; + CtxInit = false; + } + else { + CurrBlockInfo->EntryContext = + intersectContexts(CurrBlockInfo->EntryContext, + PrevBlockInfo->ExitContext); + } + } + + // Duplicate the context if we have back-edges, so we can call + // intersectBackEdges later. + if (HasBackEdges) + CurrBlockInfo->EntryContext = + createReferenceContext(CurrBlockInfo->EntryContext); + + // Create a starting context index for the current block + saveContext(0, CurrBlockInfo->EntryContext); + CurrBlockInfo->EntryIndex = getContextIndex(); + + // Visit all the statements in the basic block. + VarMapBuilder VMapBuilder(this, CurrBlockInfo->EntryContext); + for (CFGBlock::const_iterator BI = CurrBlock->begin(), + BE = CurrBlock->end(); BI != BE; ++BI) { + switch (BI->getKind()) { + case CFGElement::Statement: { + CFGStmt CS = BI->castAs<CFGStmt>(); + VMapBuilder.Visit(const_cast<Stmt*>(CS.getStmt())); + break; + } + default: + break; + } + } + CurrBlockInfo->ExitContext = VMapBuilder.Ctx; + + // Mark variables on back edges as "unknown" if they've been changed. + for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), + SE = CurrBlock->succ_end(); SI != SE; ++SI) { + // if CurrBlock -> *SI is *not* a back edge + if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) + continue; + + CFGBlock *FirstLoopBlock = *SI; + Context LoopBegin = BlockInfo[FirstLoopBlock->getBlockID()].EntryContext; + Context LoopEnd = CurrBlockInfo->ExitContext; + intersectBackEdge(LoopBegin, LoopEnd); + } + } + + // Put an extra entry at the end of the indexed context array + unsigned exitID = CFGraph->getExit().getBlockID(); + saveContext(0, BlockInfo[exitID].ExitContext); +} + +/// Find the appropriate source locations to use when producing diagnostics for +/// each block in the CFG. +static void findBlockLocations(CFG *CFGraph, + PostOrderCFGView *SortedGraph, + std::vector<CFGBlockInfo> &BlockInfo) { + for (PostOrderCFGView::iterator I = SortedGraph->begin(), + E = SortedGraph->end(); I!= E; ++I) { + const CFGBlock *CurrBlock = *I; + CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()]; + + // Find the source location of the last statement in the block, if the + // block is not empty. + if (const Stmt *S = CurrBlock->getTerminator()) { + CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getLocStart(); + } else { + for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(), + BE = CurrBlock->rend(); BI != BE; ++BI) { + // FIXME: Handle other CFGElement kinds. + if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) { + CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart(); + break; + } + } + } + + if (!CurrBlockInfo->ExitLoc.isInvalid()) { + // This block contains at least one statement. Find the source location + // of the first statement in the block. + for (CFGBlock::const_iterator BI = CurrBlock->begin(), + BE = CurrBlock->end(); BI != BE; ++BI) { + // FIXME: Handle other CFGElement kinds. + if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) { + CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart(); + break; + } + } + } else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() && + CurrBlock != &CFGraph->getExit()) { + // The block is empty, and has a single predecessor. Use its exit + // location. + CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = + BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc; + } + } +} + +/// \brief Class which implements the core thread safety analysis routines. +class ThreadSafetyAnalyzer { + friend class BuildLockset; + + ThreadSafetyHandler &Handler; + LocalVariableMap LocalVarMap; + FactManager FactMan; + std::vector<CFGBlockInfo> BlockInfo; + +public: + ThreadSafetyAnalyzer(ThreadSafetyHandler &H) : Handler(H) {} + + void addLock(FactSet &FSet, const SExpr &Mutex, const LockData &LDat); + void removeLock(FactSet &FSet, const SExpr &Mutex, + SourceLocation UnlockLoc, bool FullyRemove=false); + + template <typename AttrType> + void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, + const NamedDecl *D, VarDecl *SelfDecl=0); + + template <class AttrType> + void getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, Expr *Exp, + const NamedDecl *D, + const CFGBlock *PredBlock, const CFGBlock *CurrBlock, + Expr *BrE, bool Neg); + + const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C, + bool &Negate); + + void getEdgeLockset(FactSet &Result, const FactSet &ExitSet, + const CFGBlock* PredBlock, + const CFGBlock *CurrBlock); + + void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2, + SourceLocation JoinLoc, + LockErrorKind LEK1, LockErrorKind LEK2, + bool Modify=true); + + void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2, + SourceLocation JoinLoc, LockErrorKind LEK1, + bool Modify=true) { + intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify); + } + + void runAnalysis(AnalysisDeclContext &AC); +}; + + +/// \brief Add a new lock to the lockset, warning if the lock is already there. +/// \param Mutex -- the Mutex expression for the lock +/// \param LDat -- the LockData for the lock +void ThreadSafetyAnalyzer::addLock(FactSet &FSet, const SExpr &Mutex, + const LockData &LDat) { + // FIXME: deal with acquired before/after annotations. + // FIXME: Don't always warn when we have support for reentrant locks. + if (Mutex.shouldIgnore()) + return; + + if (FSet.findLock(FactMan, Mutex)) { + if (!LDat.Asserted) + Handler.handleDoubleLock(Mutex.toString(), LDat.AcquireLoc); + } else { + FSet.addLock(FactMan, Mutex, LDat); + } +} + + +/// \brief Remove a lock from the lockset, warning if the lock is not there. +/// \param Mutex The lock expression corresponding to the lock to be removed +/// \param UnlockLoc The source location of the unlock (only used in error msg) +void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, + const SExpr &Mutex, + SourceLocation UnlockLoc, + bool FullyRemove) { + if (Mutex.shouldIgnore()) + return; + + const LockData *LDat = FSet.findLock(FactMan, Mutex); + if (!LDat) { + Handler.handleUnmatchedUnlock(Mutex.toString(), UnlockLoc); + return; + } + + if (LDat->UnderlyingMutex.isValid()) { + // This is scoped lockable object, which manages the real mutex. + if (FullyRemove) { + // We're destroying the managing object. + // Remove the underlying mutex if it exists; but don't warn. + if (FSet.findLock(FactMan, LDat->UnderlyingMutex)) + FSet.removeLock(FactMan, LDat->UnderlyingMutex); + } else { + // We're releasing the underlying mutex, but not destroying the + // managing object. Warn on dual release. + if (!FSet.findLock(FactMan, LDat->UnderlyingMutex)) { + Handler.handleUnmatchedUnlock(LDat->UnderlyingMutex.toString(), + UnlockLoc); + } + FSet.removeLock(FactMan, LDat->UnderlyingMutex); + return; + } + } + FSet.removeLock(FactMan, Mutex); +} + + +/// \brief Extract the list of mutexIDs from the attribute on an expression, +/// and push them onto Mtxs, discarding any duplicates. +template <typename AttrType> +void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, + Expr *Exp, const NamedDecl *D, + VarDecl *SelfDecl) { + typedef typename AttrType::args_iterator iterator_type; + + if (Attr->args_size() == 0) { + // The mutex held is the "this" object. + SExpr Mu(0, Exp, D, SelfDecl); + if (!Mu.isValid()) + SExpr::warnInvalidLock(Handler, 0, Exp, D); + else + Mtxs.push_back_nodup(Mu); + return; + } + + for (iterator_type I=Attr->args_begin(), E=Attr->args_end(); I != E; ++I) { + SExpr Mu(*I, Exp, D, SelfDecl); + if (!Mu.isValid()) + SExpr::warnInvalidLock(Handler, *I, Exp, D); + else + Mtxs.push_back_nodup(Mu); + } +} + + +/// \brief Extract the list of mutexIDs from a trylock attribute. If the +/// trylock applies to the given edge, then push them onto Mtxs, discarding +/// any duplicates. +template <class AttrType> +void ThreadSafetyAnalyzer::getMutexIDs(MutexIDList &Mtxs, AttrType *Attr, + Expr *Exp, const NamedDecl *D, + const CFGBlock *PredBlock, + const CFGBlock *CurrBlock, + Expr *BrE, bool Neg) { + // Find out which branch has the lock + bool branch = 0; + if (CXXBoolLiteralExpr *BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE)) { + branch = BLE->getValue(); + } + else if (IntegerLiteral *ILE = dyn_cast_or_null<IntegerLiteral>(BrE)) { + branch = ILE->getValue().getBoolValue(); + } + int branchnum = branch ? 0 : 1; + if (Neg) branchnum = !branchnum; + + // If we've taken the trylock branch, then add the lock + int i = 0; + for (CFGBlock::const_succ_iterator SI = PredBlock->succ_begin(), + SE = PredBlock->succ_end(); SI != SE && i < 2; ++SI, ++i) { + if (*SI == CurrBlock && i == branchnum) { + getMutexIDs(Mtxs, Attr, Exp, D); + } + } +} + + +bool getStaticBooleanValue(Expr* E, bool& TCond) { + if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E)) { + TCond = false; + return true; + } else if (CXXBoolLiteralExpr *BLE = dyn_cast<CXXBoolLiteralExpr>(E)) { + TCond = BLE->getValue(); + return true; + } else if (IntegerLiteral *ILE = dyn_cast<IntegerLiteral>(E)) { + TCond = ILE->getValue().getBoolValue(); + return true; + } else if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) { + return getStaticBooleanValue(CE->getSubExpr(), TCond); + } + return false; +} + + +// If Cond can be traced back to a function call, return the call expression. +// The negate variable should be called with false, and will be set to true +// if the function call is negated, e.g. if (!mu.tryLock(...)) +const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond, + LocalVarContext C, + bool &Negate) { + if (!Cond) + return 0; + + if (const CallExpr *CallExp = dyn_cast<CallExpr>(Cond)) { + return CallExp; + } + else if (const ParenExpr *PE = dyn_cast<ParenExpr>(Cond)) { + return getTrylockCallExpr(PE->getSubExpr(), C, Negate); + } + else if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Cond)) { + return getTrylockCallExpr(CE->getSubExpr(), C, Negate); + } + else if (const ExprWithCleanups* EWC = dyn_cast<ExprWithCleanups>(Cond)) { + return getTrylockCallExpr(EWC->getSubExpr(), C, Negate); + } + else if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Cond)) { + const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C); + return getTrylockCallExpr(E, C, Negate); + } + else if (const UnaryOperator *UOP = dyn_cast<UnaryOperator>(Cond)) { + if (UOP->getOpcode() == UO_LNot) { + Negate = !Negate; + return getTrylockCallExpr(UOP->getSubExpr(), C, Negate); + } + return 0; + } + else if (const BinaryOperator *BOP = dyn_cast<BinaryOperator>(Cond)) { + if (BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) { + if (BOP->getOpcode() == BO_NE) + Negate = !Negate; + + bool TCond = false; + if (getStaticBooleanValue(BOP->getRHS(), TCond)) { + if (!TCond) Negate = !Negate; + return getTrylockCallExpr(BOP->getLHS(), C, Negate); + } + TCond = false; + if (getStaticBooleanValue(BOP->getLHS(), TCond)) { + if (!TCond) Negate = !Negate; + return getTrylockCallExpr(BOP->getRHS(), C, Negate); + } + return 0; + } + if (BOP->getOpcode() == BO_LAnd) { + // LHS must have been evaluated in a different block. + return getTrylockCallExpr(BOP->getRHS(), C, Negate); + } + if (BOP->getOpcode() == BO_LOr) { + return getTrylockCallExpr(BOP->getRHS(), C, Negate); + } + return 0; + } + return 0; +} + + +/// \brief Find the lockset that holds on the edge between PredBlock +/// and CurrBlock. The edge set is the exit set of PredBlock (passed +/// as the ExitSet parameter) plus any trylocks, which are conditionally held. +void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result, + const FactSet &ExitSet, + const CFGBlock *PredBlock, + const CFGBlock *CurrBlock) { + Result = ExitSet; + + const Stmt *Cond = PredBlock->getTerminatorCondition(); + if (!Cond) + return; + + bool Negate = false; + const CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->getBlockID()]; + const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext; + + CallExpr *Exp = + const_cast<CallExpr*>(getTrylockCallExpr(Cond, LVarCtx, Negate)); + if (!Exp) + return; + + NamedDecl *FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl()); + if(!FunDecl || !FunDecl->hasAttrs()) + return; + + MutexIDList ExclusiveLocksToAdd; + MutexIDList SharedLocksToAdd; + + // If the condition is a call to a Trylock function, then grab the attributes + AttrVec &ArgAttrs = FunDecl->getAttrs(); + for (unsigned i = 0; i < ArgAttrs.size(); ++i) { + Attr *Attr = ArgAttrs[i]; + switch (Attr->getKind()) { + case attr::ExclusiveTrylockFunction: { + ExclusiveTrylockFunctionAttr *A = + cast<ExclusiveTrylockFunctionAttr>(Attr); + getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, + PredBlock, CurrBlock, A->getSuccessValue(), Negate); + break; + } + case attr::SharedTrylockFunction: { + SharedTrylockFunctionAttr *A = + cast<SharedTrylockFunctionAttr>(Attr); + getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, + PredBlock, CurrBlock, A->getSuccessValue(), Negate); + break; + } + default: + break; + } + } + + // Add and remove locks. + SourceLocation Loc = Exp->getExprLoc(); + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + addLock(Result, ExclusiveLocksToAdd[i], + LockData(Loc, LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + addLock(Result, SharedLocksToAdd[i], + LockData(Loc, LK_Shared)); + } +} + + +/// \brief We use this class to visit different types of expressions in +/// CFGBlocks, and build up the lockset. +/// An expression may cause us to add or remove locks from the lockset, or else +/// output error messages related to missing locks. +/// FIXME: In future, we may be able to not inherit from a visitor. +class BuildLockset : public StmtVisitor<BuildLockset> { + friend class ThreadSafetyAnalyzer; + + ThreadSafetyAnalyzer *Analyzer; + FactSet FSet; + LocalVariableMap::Context LVarCtx; + unsigned CtxIndex; + + // Helper functions + const ValueDecl *getValueDecl(const Expr *Exp); + + void warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, AccessKind AK, + Expr *MutexExp, ProtectedOperationKind POK); + void warnIfMutexHeld(const NamedDecl *D, const Expr *Exp, Expr *MutexExp); + + void checkAccess(const Expr *Exp, AccessKind AK); + void checkPtAccess(const Expr *Exp, AccessKind AK); + + void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = 0); + +public: + BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info) + : StmtVisitor<BuildLockset>(), + Analyzer(Anlzr), + FSet(Info.EntrySet), + LVarCtx(Info.EntryContext), + CtxIndex(Info.EntryIndex) + {} + + void VisitUnaryOperator(UnaryOperator *UO); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitCastExpr(CastExpr *CE); + void VisitCallExpr(CallExpr *Exp); + void VisitCXXConstructExpr(CXXConstructExpr *Exp); + void VisitDeclStmt(DeclStmt *S); +}; + + +/// \brief Gets the value decl pointer from DeclRefExprs or MemberExprs +const ValueDecl *BuildLockset::getValueDecl(const Expr *Exp) { + if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(Exp)) + return getValueDecl(CE->getSubExpr()); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Exp)) + return DR->getDecl(); + + if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) + return ME->getMemberDecl(); + + return 0; +} + +/// \brief Warn if the LSet does not contain a lock sufficient to protect access +/// of at least the passed in AccessKind. +void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp, + AccessKind AK, Expr *MutexExp, + ProtectedOperationKind POK) { + LockKind LK = getLockKindFromAccessKind(AK); + + SExpr Mutex(MutexExp, Exp, D); + if (!Mutex.isValid()) { + SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); + return; + } else if (Mutex.shouldIgnore()) { + return; + } + + LockData* LDat = FSet.findLockUniv(Analyzer->FactMan, Mutex); + bool NoError = true; + if (!LDat) { + // No exact match found. Look for a partial match. + FactEntry* FEntry = FSet.findPartialMatch(Analyzer->FactMan, Mutex); + if (FEntry) { + // Warn that there's no precise match. + LDat = &FEntry->LDat; + std::string PartMatchStr = FEntry->MutID.toString(); + StringRef PartMatchName(PartMatchStr); + Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, + Exp->getExprLoc(), &PartMatchName); + } else { + // Warn that there's no match at all. + Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, + Exp->getExprLoc()); + } + NoError = false; + } + // Make sure the mutex we found is the right kind. + if (NoError && LDat && !LDat->isAtLeast(LK)) + Analyzer->Handler.handleMutexNotHeld(D, POK, Mutex.toString(), LK, + Exp->getExprLoc()); +} + +/// \brief Warn if the LSet contains the given lock. +void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr* Exp, + Expr *MutexExp) { + SExpr Mutex(MutexExp, Exp, D); + if (!Mutex.isValid()) { + SExpr::warnInvalidLock(Analyzer->Handler, MutexExp, Exp, D); + return; + } + + LockData* LDat = FSet.findLock(Analyzer->FactMan, Mutex); + if (LDat) { + std::string DeclName = D->getNameAsString(); + StringRef DeclNameSR (DeclName); + Analyzer->Handler.handleFunExcludesLock(DeclNameSR, Mutex.toString(), + Exp->getExprLoc()); + } +} + + +/// \brief Checks guarded_by and pt_guarded_by attributes. +/// Whenever we identify an access (read or write) to a DeclRefExpr that is +/// marked with guarded_by, we must ensure the appropriate mutexes are held. +/// Similarly, we check if the access is to an expression that dereferences +/// a pointer marked with pt_guarded_by. +void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK) { + Exp = Exp->IgnoreParenCasts(); + + if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Exp)) { + // For dereferences + if (UO->getOpcode() == clang::UO_Deref) + checkPtAccess(UO->getSubExpr(), AK); + return; + } + + if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(Exp)) { + if (Analyzer->Handler.issueBetaWarnings()) { + checkPtAccess(AE->getLHS(), AK); + return; + } + } + + if (const MemberExpr *ME = dyn_cast<MemberExpr>(Exp)) { + if (ME->isArrow()) + checkPtAccess(ME->getBase(), AK); + else + checkAccess(ME->getBase(), AK); + } + + const ValueDecl *D = getValueDecl(Exp); + if (!D || !D->hasAttrs()) + return; + + if (D->getAttr<GuardedVarAttr>() && FSet.isEmpty()) + Analyzer->Handler.handleNoMutexHeld(D, POK_VarAccess, AK, + Exp->getExprLoc()); + + const AttrVec &ArgAttrs = D->getAttrs(); + for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) + if (GuardedByAttr *GBAttr = dyn_cast<GuardedByAttr>(ArgAttrs[i])) + warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarAccess); +} + +/// \brief Checks pt_guarded_by and pt_guarded_var attributes. +void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK) { + if (Analyzer->Handler.issueBetaWarnings()) { + while (true) { + if (const ParenExpr *PE = dyn_cast<ParenExpr>(Exp)) { + Exp = PE->getSubExpr(); + continue; + } + if (const CastExpr *CE = dyn_cast<CastExpr>(Exp)) { + if (CE->getCastKind() == CK_ArrayToPointerDecay) { + // If it's an actual array, and not a pointer, then it's elements + // are protected by GUARDED_BY, not PT_GUARDED_BY; + checkAccess(CE->getSubExpr(), AK); + return; + } + Exp = CE->getSubExpr(); + continue; + } + break; + } + } + else + Exp = Exp->IgnoreParenCasts(); + + const ValueDecl *D = getValueDecl(Exp); + if (!D || !D->hasAttrs()) + return; + + if (D->getAttr<PtGuardedVarAttr>() && FSet.isEmpty()) + Analyzer->Handler.handleNoMutexHeld(D, POK_VarDereference, AK, + Exp->getExprLoc()); + + const AttrVec &ArgAttrs = D->getAttrs(); + for (unsigned i = 0, Size = ArgAttrs.size(); i < Size; ++i) + if (PtGuardedByAttr *GBAttr = dyn_cast<PtGuardedByAttr>(ArgAttrs[i])) + warnIfMutexNotHeld(D, Exp, AK, GBAttr->getArg(), POK_VarDereference); +} + + +/// \brief Process a function call, method call, constructor call, +/// or destructor call. This involves looking at the attributes on the +/// corresponding function/method/constructor/destructor, issuing warnings, +/// and updating the locksets accordingly. +/// +/// FIXME: For classes annotated with one of the guarded annotations, we need +/// to treat const method calls as reads and non-const method calls as writes, +/// and check that the appropriate locks are held. Non-const method calls with +/// the same signature as const method calls can be also treated as reads. +/// +void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) { + SourceLocation Loc = Exp->getExprLoc(); + const AttrVec &ArgAttrs = D->getAttrs(); + MutexIDList ExclusiveLocksToAdd; + MutexIDList SharedLocksToAdd; + MutexIDList LocksToRemove; + + for(unsigned i = 0; i < ArgAttrs.size(); ++i) { + Attr *At = const_cast<Attr*>(ArgAttrs[i]); + switch (At->getKind()) { + // When we encounter an exclusive lock function, we need to add the lock + // to our lockset with kind exclusive. + case attr::ExclusiveLockFunction: { + ExclusiveLockFunctionAttr *A = cast<ExclusiveLockFunctionAttr>(At); + Analyzer->getMutexIDs(ExclusiveLocksToAdd, A, Exp, D, VD); + break; + } + + // When we encounter a shared lock function, we need to add the lock + // to our lockset with kind shared. + case attr::SharedLockFunction: { + SharedLockFunctionAttr *A = cast<SharedLockFunctionAttr>(At); + Analyzer->getMutexIDs(SharedLocksToAdd, A, Exp, D, VD); + break; + } + + // An assert will add a lock to the lockset, but will not generate + // a warning if it is already there, and will not generate a warning + // if it is not removed. + case attr::AssertExclusiveLock: { + AssertExclusiveLockAttr *A = cast<AssertExclusiveLockAttr>(At); + + MutexIDList AssertLocks; + Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD); + for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) { + Analyzer->addLock(FSet, AssertLocks[i], + LockData(Loc, LK_Exclusive, false, true)); + } + break; + } + case attr::AssertSharedLock: { + AssertSharedLockAttr *A = cast<AssertSharedLockAttr>(At); + + MutexIDList AssertLocks; + Analyzer->getMutexIDs(AssertLocks, A, Exp, D, VD); + for (unsigned i=0,n=AssertLocks.size(); i<n; ++i) { + Analyzer->addLock(FSet, AssertLocks[i], + LockData(Loc, LK_Shared, false, true)); + } + break; + } + + // When we encounter an unlock function, we need to remove unlocked + // mutexes from the lockset, and flag a warning if they are not there. + case attr::UnlockFunction: { + UnlockFunctionAttr *A = cast<UnlockFunctionAttr>(At); + Analyzer->getMutexIDs(LocksToRemove, A, Exp, D, VD); + break; + } + + case attr::ExclusiveLocksRequired: { + ExclusiveLocksRequiredAttr *A = cast<ExclusiveLocksRequiredAttr>(At); + + for (ExclusiveLocksRequiredAttr::args_iterator + I = A->args_begin(), E = A->args_end(); I != E; ++I) + warnIfMutexNotHeld(D, Exp, AK_Written, *I, POK_FunctionCall); + break; + } + + case attr::SharedLocksRequired: { + SharedLocksRequiredAttr *A = cast<SharedLocksRequiredAttr>(At); + + for (SharedLocksRequiredAttr::args_iterator I = A->args_begin(), + E = A->args_end(); I != E; ++I) + warnIfMutexNotHeld(D, Exp, AK_Read, *I, POK_FunctionCall); + break; + } + + case attr::LocksExcluded: { + LocksExcludedAttr *A = cast<LocksExcludedAttr>(At); + + for (LocksExcludedAttr::args_iterator I = A->args_begin(), + E = A->args_end(); I != E; ++I) { + warnIfMutexHeld(D, Exp, *I); + } + break; + } + + // Ignore other (non thread-safety) attributes + default: + break; + } + } + + // Figure out if we're calling the constructor of scoped lockable class + bool isScopedVar = false; + if (VD) { + if (const CXXConstructorDecl *CD = dyn_cast<const CXXConstructorDecl>(D)) { + const CXXRecordDecl* PD = CD->getParent(); + if (PD && PD->getAttr<ScopedLockableAttr>()) + isScopedVar = true; + } + } + + // Add locks. + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, ExclusiveLocksToAdd[i], + LockData(Loc, LK_Exclusive, isScopedVar)); + } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, SharedLocksToAdd[i], + LockData(Loc, LK_Shared, isScopedVar)); + } + + // Add the managing object as a dummy mutex, mapped to the underlying mutex. + // FIXME -- this doesn't work if we acquire multiple locks. + if (isScopedVar) { + SourceLocation MLoc = VD->getLocation(); + DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation()); + SExpr SMutex(&DRE, 0, 0); + + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Exclusive, + ExclusiveLocksToAdd[i])); + } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + Analyzer->addLock(FSet, SMutex, LockData(MLoc, LK_Shared, + SharedLocksToAdd[i])); + } + } + + // Remove locks. + // FIXME -- should only fully remove if the attribute refers to 'this'. + bool Dtor = isa<CXXDestructorDecl>(D); + for (unsigned i=0,n=LocksToRemove.size(); i<n; ++i) { + Analyzer->removeLock(FSet, LocksToRemove[i], Loc, Dtor); + } +} + + +/// \brief For unary operations which read and write a variable, we need to +/// check whether we hold any required mutexes. Reads are checked in +/// VisitCastExpr. +void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) { + switch (UO->getOpcode()) { + case clang::UO_PostDec: + case clang::UO_PostInc: + case clang::UO_PreDec: + case clang::UO_PreInc: { + checkAccess(UO->getSubExpr(), AK_Written); + break; + } + default: + break; + } +} + +/// For binary operations which assign to a variable (writes), we need to check +/// whether we hold any required mutexes. +/// FIXME: Deal with non-primitive types. +void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) { + if (!BO->isAssignmentOp()) + return; + + // adjust the context + LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx); + + checkAccess(BO->getLHS(), AK_Written); +} + + +/// Whenever we do an LValue to Rvalue cast, we are reading a variable and +/// need to ensure we hold any required mutexes. +/// FIXME: Deal with non-primitive types. +void BuildLockset::VisitCastExpr(CastExpr *CE) { + if (CE->getCastKind() != CK_LValueToRValue) + return; + checkAccess(CE->getSubExpr(), AK_Read); +} + + +void BuildLockset::VisitCallExpr(CallExpr *Exp) { + if (CXXMemberCallExpr *CE = dyn_cast<CXXMemberCallExpr>(Exp)) { + MemberExpr *ME = dyn_cast<MemberExpr>(CE->getCallee()); + // ME can be null when calling a method pointer + CXXMethodDecl *MD = CE->getMethodDecl(); + + if (ME && MD) { + if (ME->isArrow()) { + if (MD->isConst()) { + checkPtAccess(CE->getImplicitObjectArgument(), AK_Read); + } else { // FIXME -- should be AK_Written + checkPtAccess(CE->getImplicitObjectArgument(), AK_Read); + } + } else { + if (MD->isConst()) + checkAccess(CE->getImplicitObjectArgument(), AK_Read); + else // FIXME -- should be AK_Written + checkAccess(CE->getImplicitObjectArgument(), AK_Read); + } + } + } else if (CXXOperatorCallExpr *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) { + switch (OE->getOperator()) { + case OO_Equal: { + const Expr *Target = OE->getArg(0); + const Expr *Source = OE->getArg(1); + checkAccess(Target, AK_Written); + checkAccess(Source, AK_Read); + break; + } + case OO_Star: + case OO_Arrow: + case OO_Subscript: { + if (Analyzer->Handler.issueBetaWarnings()) { + const Expr *Obj = OE->getArg(0); + checkAccess(Obj, AK_Read); + checkPtAccess(Obj, AK_Read); + } + break; + } + default: { + const Expr *Obj = OE->getArg(0); + checkAccess(Obj, AK_Read); + break; + } + } + } + NamedDecl *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl()); + if(!D || !D->hasAttrs()) + return; + handleCall(Exp, D); +} + +void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) { + const CXXConstructorDecl *D = Exp->getConstructor(); + if (D && D->isCopyConstructor()) { + const Expr* Source = Exp->getArg(0); + checkAccess(Source, AK_Read); + } + // FIXME -- only handles constructors in DeclStmt below. +} + +void BuildLockset::VisitDeclStmt(DeclStmt *S) { + // adjust the context + LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx); + + DeclGroupRef DGrp = S->getDeclGroup(); + for (DeclGroupRef::iterator I = DGrp.begin(), E = DGrp.end(); I != E; ++I) { + Decl *D = *I; + if (VarDecl *VD = dyn_cast_or_null<VarDecl>(D)) { + Expr *E = VD->getInit(); + // handle constructors that involve temporaries + if (ExprWithCleanups *EWC = dyn_cast_or_null<ExprWithCleanups>(E)) + E = EWC->getSubExpr(); + + if (CXXConstructExpr *CE = dyn_cast_or_null<CXXConstructExpr>(E)) { + NamedDecl *CtorD = dyn_cast_or_null<NamedDecl>(CE->getConstructor()); + if (!CtorD || !CtorD->hasAttrs()) + return; + handleCall(CE, CtorD, VD); + } + } + } +} + + + +/// \brief Compute the intersection of two locksets and issue warnings for any +/// locks in the symmetric difference. +/// +/// This function is used at a merge point in the CFG when comparing the lockset +/// of each branch being merged. For example, given the following sequence: +/// A; if () then B; else C; D; we need to check that the lockset after B and C +/// are the same. In the event of a difference, we use the intersection of these +/// two locksets at the start of D. +/// +/// \param FSet1 The first lockset. +/// \param FSet2 The second lockset. +/// \param JoinLoc The location of the join point for error reporting +/// \param LEK1 The error message to report if a mutex is missing from LSet1 +/// \param LEK2 The error message to report if a mutex is missing from Lset2 +void ThreadSafetyAnalyzer::intersectAndWarn(FactSet &FSet1, + const FactSet &FSet2, + SourceLocation JoinLoc, + LockErrorKind LEK1, + LockErrorKind LEK2, + bool Modify) { + FactSet FSet1Orig = FSet1; + + // Find locks in FSet2 that conflict or are not in FSet1, and warn. + for (FactSet::const_iterator I = FSet2.begin(), E = FSet2.end(); + I != E; ++I) { + const SExpr &FSet2Mutex = FactMan[*I].MutID; + const LockData &LDat2 = FactMan[*I].LDat; + FactSet::iterator I1 = FSet1.findLockIter(FactMan, FSet2Mutex); + + if (I1 != FSet1.end()) { + const LockData* LDat1 = &FactMan[*I1].LDat; + if (LDat1->LKind != LDat2.LKind) { + Handler.handleExclusiveAndShared(FSet2Mutex.toString(), + LDat2.AcquireLoc, + LDat1->AcquireLoc); + if (Modify && LDat1->LKind != LK_Exclusive) { + // Take the exclusive lock, which is the one in FSet2. + *I1 = *I; + } + } + else if (LDat1->Asserted && !LDat2.Asserted) { + // The non-asserted lock in FSet2 is the one we want to track. + *I1 = *I; + } + } else { + if (LDat2.UnderlyingMutex.isValid()) { + if (FSet2.findLock(FactMan, LDat2.UnderlyingMutex)) { + // If this is a scoped lock that manages another mutex, and if the + // underlying mutex is still held, then warn about the underlying + // mutex. + Handler.handleMutexHeldEndOfScope(LDat2.UnderlyingMutex.toString(), + LDat2.AcquireLoc, + JoinLoc, LEK1); + } + } + else if (!LDat2.Managed && !FSet2Mutex.isUniversal() && !LDat2.Asserted) + Handler.handleMutexHeldEndOfScope(FSet2Mutex.toString(), + LDat2.AcquireLoc, + JoinLoc, LEK1); + } + } + + // Find locks in FSet1 that are not in FSet2, and remove them. + for (FactSet::const_iterator I = FSet1Orig.begin(), E = FSet1Orig.end(); + I != E; ++I) { + const SExpr &FSet1Mutex = FactMan[*I].MutID; + const LockData &LDat1 = FactMan[*I].LDat; + + if (!FSet2.findLock(FactMan, FSet1Mutex)) { + if (LDat1.UnderlyingMutex.isValid()) { + if (FSet1Orig.findLock(FactMan, LDat1.UnderlyingMutex)) { + // If this is a scoped lock that manages another mutex, and if the + // underlying mutex is still held, then warn about the underlying + // mutex. + Handler.handleMutexHeldEndOfScope(LDat1.UnderlyingMutex.toString(), + LDat1.AcquireLoc, + JoinLoc, LEK1); + } + } + else if (!LDat1.Managed && !FSet1Mutex.isUniversal() && !LDat1.Asserted) + Handler.handleMutexHeldEndOfScope(FSet1Mutex.toString(), + LDat1.AcquireLoc, + JoinLoc, LEK2); + if (Modify) + FSet1.removeLock(FactMan, FSet1Mutex); + } + } +} + + +// Return true if block B never continues to its successors. +inline bool neverReturns(const CFGBlock* B) { + if (B->hasNoReturnElement()) + return true; + if (B->empty()) + return false; + + CFGElement Last = B->back(); + if (Optional<CFGStmt> S = Last.getAs<CFGStmt>()) { + if (isa<CXXThrowExpr>(S->getStmt())) + return true; + } + return false; +} + + +/// \brief Check a function's CFG for thread-safety violations. +/// +/// We traverse the blocks in the CFG, compute the set of mutexes that are held +/// at the end of each block, and issue warnings for thread safety violations. +/// Each block in the CFG is traversed exactly once. +void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) { + CFG *CFGraph = AC.getCFG(); + if (!CFGraph) return; + const NamedDecl *D = dyn_cast_or_null<NamedDecl>(AC.getDecl()); + + // AC.dumpCFG(true); + + if (!D) + return; // Ignore anonymous functions for now. + if (D->getAttr<NoThreadSafetyAnalysisAttr>()) + return; + // FIXME: Do something a bit more intelligent inside constructor and + // destructor code. Constructors and destructors must assume unique access + // to 'this', so checks on member variable access is disabled, but we should + // still enable checks on other objects. + if (isa<CXXConstructorDecl>(D)) + return; // Don't check inside constructors. + if (isa<CXXDestructorDecl>(D)) + return; // Don't check inside destructors. + + BlockInfo.resize(CFGraph->getNumBlockIDs(), + CFGBlockInfo::getEmptyBlockInfo(LocalVarMap)); + + // We need to explore the CFG via a "topological" ordering. + // That way, we will be guaranteed to have information about required + // predecessor locksets when exploring a new block. + PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>(); + PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph); + + // Mark entry block as reachable + BlockInfo[CFGraph->getEntry().getBlockID()].Reachable = true; + + // Compute SSA names for local variables + LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo); + + // Fill in source locations for all CFGBlocks. + findBlockLocations(CFGraph, SortedGraph, BlockInfo); + + MutexIDList ExclusiveLocksAcquired; + MutexIDList SharedLocksAcquired; + MutexIDList LocksReleased; + + // Add locks from exclusive_locks_required and shared_locks_required + // to initial lockset. Also turn off checking for lock and unlock functions. + // FIXME: is there a more intelligent way to check lock/unlock functions? + if (!SortedGraph->empty() && D->hasAttrs()) { + const CFGBlock *FirstBlock = *SortedGraph->begin(); + FactSet &InitialLockset = BlockInfo[FirstBlock->getBlockID()].EntrySet; + const AttrVec &ArgAttrs = D->getAttrs(); + + MutexIDList ExclusiveLocksToAdd; + MutexIDList SharedLocksToAdd; + + SourceLocation Loc = D->getLocation(); + for (unsigned i = 0; i < ArgAttrs.size(); ++i) { + Attr *Attr = ArgAttrs[i]; + Loc = Attr->getLocation(); + if (ExclusiveLocksRequiredAttr *A + = dyn_cast<ExclusiveLocksRequiredAttr>(Attr)) { + getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); + } else if (SharedLocksRequiredAttr *A + = dyn_cast<SharedLocksRequiredAttr>(Attr)) { + getMutexIDs(SharedLocksToAdd, A, (Expr*) 0, D); + } else if (UnlockFunctionAttr *A = dyn_cast<UnlockFunctionAttr>(Attr)) { + // UNLOCK_FUNCTION() is used to hide the underlying lock implementation. + // We must ignore such methods. + if (A->args_size() == 0) + return; + // FIXME -- deal with exclusive vs. shared unlock functions? + getMutexIDs(ExclusiveLocksToAdd, A, (Expr*) 0, D); + getMutexIDs(LocksReleased, A, (Expr*) 0, D); + } else if (ExclusiveLockFunctionAttr *A + = dyn_cast<ExclusiveLockFunctionAttr>(Attr)) { + if (A->args_size() == 0) + return; + getMutexIDs(ExclusiveLocksAcquired, A, (Expr*) 0, D); + } else if (SharedLockFunctionAttr *A + = dyn_cast<SharedLockFunctionAttr>(Attr)) { + if (A->args_size() == 0) + return; + getMutexIDs(SharedLocksAcquired, A, (Expr*) 0, D); + } else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) { + // Don't try to check trylock functions for now + return; + } else if (isa<SharedTrylockFunctionAttr>(Attr)) { + // Don't try to check trylock functions for now + return; + } + } + + // FIXME -- Loc can be wrong here. + for (unsigned i=0,n=ExclusiveLocksToAdd.size(); i<n; ++i) { + addLock(InitialLockset, ExclusiveLocksToAdd[i], + LockData(Loc, LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksToAdd.size(); i<n; ++i) { + addLock(InitialLockset, SharedLocksToAdd[i], + LockData(Loc, LK_Shared)); + } + } + + for (PostOrderCFGView::iterator I = SortedGraph->begin(), + E = SortedGraph->end(); I!= E; ++I) { + const CFGBlock *CurrBlock = *I; + int CurrBlockID = CurrBlock->getBlockID(); + CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID]; + + // Use the default initial lockset in case there are no predecessors. + VisitedBlocks.insert(CurrBlock); + + // Iterate through the predecessor blocks and warn if the lockset for all + // predecessors is not the same. We take the entry lockset of the current + // block to be the intersection of all previous locksets. + // FIXME: By keeping the intersection, we may output more errors in future + // for a lock which is not in the intersection, but was in the union. We + // may want to also keep the union in future. As an example, let's say + // the intersection contains Mutex L, and the union contains L and M. + // Later we unlock M. At this point, we would output an error because we + // never locked M; although the real error is probably that we forgot to + // lock M on all code paths. Conversely, let's say that later we lock M. + // In this case, we should compare against the intersection instead of the + // union because the real error is probably that we forgot to unlock M on + // all code paths. + bool LocksetInitialized = false; + SmallVector<CFGBlock *, 8> SpecialBlocks; + for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(), + PE = CurrBlock->pred_end(); PI != PE; ++PI) { + + // if *PI -> CurrBlock is a back edge + if (*PI == 0 || !VisitedBlocks.alreadySet(*PI)) + continue; + + int PrevBlockID = (*PI)->getBlockID(); + CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; + + // Ignore edges from blocks that can't return. + if (neverReturns(*PI) || !PrevBlockInfo->Reachable) + continue; + + // Okay, we can reach this block from the entry. + CurrBlockInfo->Reachable = true; + + // If the previous block ended in a 'continue' or 'break' statement, then + // a difference in locksets is probably due to a bug in that block, rather + // than in some other predecessor. In that case, keep the other + // predecessor's lockset. + if (const Stmt *Terminator = (*PI)->getTerminator()) { + if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) { + SpecialBlocks.push_back(*PI); + continue; + } + } + + FactSet PrevLockset; + getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock); + + if (!LocksetInitialized) { + CurrBlockInfo->EntrySet = PrevLockset; + LocksetInitialized = true; + } else { + intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset, + CurrBlockInfo->EntryLoc, + LEK_LockedSomePredecessors); + } + } + + // Skip rest of block if it's not reachable. + if (!CurrBlockInfo->Reachable) + continue; + + // Process continue and break blocks. Assume that the lockset for the + // resulting block is unaffected by any discrepancies in them. + for (unsigned SpecialI = 0, SpecialN = SpecialBlocks.size(); + SpecialI < SpecialN; ++SpecialI) { + CFGBlock *PrevBlock = SpecialBlocks[SpecialI]; + int PrevBlockID = PrevBlock->getBlockID(); + CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID]; + + if (!LocksetInitialized) { + CurrBlockInfo->EntrySet = PrevBlockInfo->ExitSet; + LocksetInitialized = true; + } else { + // Determine whether this edge is a loop terminator for diagnostic + // purposes. FIXME: A 'break' statement might be a loop terminator, but + // it might also be part of a switch. Also, a subsequent destructor + // might add to the lockset, in which case the real issue might be a + // double lock on the other path. + const Stmt *Terminator = PrevBlock->getTerminator(); + bool IsLoop = Terminator && isa<ContinueStmt>(Terminator); + + FactSet PrevLockset; + getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, + PrevBlock, CurrBlock); + + // Do not update EntrySet. + intersectAndWarn(CurrBlockInfo->EntrySet, PrevLockset, + PrevBlockInfo->ExitLoc, + IsLoop ? LEK_LockedSomeLoopIterations + : LEK_LockedSomePredecessors, + false); + } + } + + BuildLockset LocksetBuilder(this, *CurrBlockInfo); + + // Visit all the statements in the basic block. + for (CFGBlock::const_iterator BI = CurrBlock->begin(), + BE = CurrBlock->end(); BI != BE; ++BI) { + switch (BI->getKind()) { + case CFGElement::Statement: { + CFGStmt CS = BI->castAs<CFGStmt>(); + LocksetBuilder.Visit(const_cast<Stmt*>(CS.getStmt())); + break; + } + // Ignore BaseDtor, MemberDtor, and TemporaryDtor for now. + case CFGElement::AutomaticObjectDtor: { + CFGAutomaticObjDtor AD = BI->castAs<CFGAutomaticObjDtor>(); + CXXDestructorDecl *DD = const_cast<CXXDestructorDecl *>( + AD.getDestructorDecl(AC.getASTContext())); + if (!DD->hasAttrs()) + break; + + // Create a dummy expression, + VarDecl *VD = const_cast<VarDecl*>(AD.getVarDecl()); + DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, + AD.getTriggerStmt()->getLocEnd()); + LocksetBuilder.handleCall(&DRE, DD); + break; + } + default: + break; + } + } + CurrBlockInfo->ExitSet = LocksetBuilder.FSet; + + // For every back edge from CurrBlock (the end of the loop) to another block + // (FirstLoopBlock) we need to check that the Lockset of Block is equal to + // the one held at the beginning of FirstLoopBlock. We can look up the + // Lockset held at the beginning of FirstLoopBlock in the EntryLockSets map. + for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), + SE = CurrBlock->succ_end(); SI != SE; ++SI) { + + // if CurrBlock -> *SI is *not* a back edge + if (*SI == 0 || !VisitedBlocks.alreadySet(*SI)) + continue; + + CFGBlock *FirstLoopBlock = *SI; + CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->getBlockID()]; + CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID]; + intersectAndWarn(LoopEnd->ExitSet, PreLoop->EntrySet, + PreLoop->EntryLoc, + LEK_LockedSomeLoopIterations, + false); + } + } + + CFGBlockInfo *Initial = &BlockInfo[CFGraph->getEntry().getBlockID()]; + CFGBlockInfo *Final = &BlockInfo[CFGraph->getExit().getBlockID()]; + + // Skip the final check if the exit block is unreachable. + if (!Final->Reachable) + return; + + // By default, we expect all locks held on entry to be held on exit. + FactSet ExpectedExitSet = Initial->EntrySet; + + // Adjust the expected exit set by adding or removing locks, as declared + // by *-LOCK_FUNCTION and UNLOCK_FUNCTION. The intersect below will then + // issue the appropriate warning. + // FIXME: the location here is not quite right. + for (unsigned i=0,n=ExclusiveLocksAcquired.size(); i<n; ++i) { + ExpectedExitSet.addLock(FactMan, ExclusiveLocksAcquired[i], + LockData(D->getLocation(), LK_Exclusive)); + } + for (unsigned i=0,n=SharedLocksAcquired.size(); i<n; ++i) { + ExpectedExitSet.addLock(FactMan, SharedLocksAcquired[i], + LockData(D->getLocation(), LK_Shared)); + } + for (unsigned i=0,n=LocksReleased.size(); i<n; ++i) { + ExpectedExitSet.removeLock(FactMan, LocksReleased[i]); + } + + // FIXME: Should we call this function for all blocks which exit the function? + intersectAndWarn(ExpectedExitSet, Final->ExitSet, + Final->ExitLoc, + LEK_LockedAtEndOfFunction, + LEK_NotLockedAtEndOfFunction, + false); +} + +} // end anonymous namespace + + +namespace clang { +namespace thread_safety { + +/// \brief Check a function's CFG for thread-safety violations. +/// +/// We traverse the blocks in the CFG, compute the set of mutexes that are held +/// at the end of each block, and issue warnings for thread safety violations. +/// Each block in the CFG is traversed exactly once. +void runThreadSafetyAnalysis(AnalysisDeclContext &AC, + ThreadSafetyHandler &Handler) { + ThreadSafetyAnalyzer Analyzer(Handler); + Analyzer.runAnalysis(AC); +} + +/// \brief Helper function that returns a LockKind required for the given level +/// of access. +LockKind getLockKindFromAccessKind(AccessKind AK) { + switch (AK) { + case AK_Read : + return LK_Shared; + case AK_Written : + return LK_Exclusive; + } + llvm_unreachable("Unknown AccessKind"); +} + +}} // end namespace clang::thread_safety diff --git a/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp b/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp new file mode 100644 index 000000000000..332c02cf1852 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp @@ -0,0 +1,864 @@ +//==- UninitializedValues.cpp - Find Uninitialized Values -------*- C++ --*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements uninitialized values analysis for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/Analyses/PostOrderCFGView.h" +#include "clang/Analysis/Analyses/UninitializedValues.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PackedVector.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/SaveAndRestore.h" +#include <utility> + +using namespace clang; + +#define DEBUG_LOGGING 0 + +static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) { + if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() && + !vd->isExceptionVariable() && + vd->getDeclContext() == dc) { + QualType ty = vd->getType(); + return ty->isScalarType() || ty->isVectorType(); + } + return false; +} + +//------------------------------------------------------------------------====// +// DeclToIndex: a mapping from Decls we track to value indices. +//====------------------------------------------------------------------------// + +namespace { +class DeclToIndex { + llvm::DenseMap<const VarDecl *, unsigned> map; +public: + DeclToIndex() {} + + /// Compute the actual mapping from declarations to bits. + void computeMap(const DeclContext &dc); + + /// Return the number of declarations in the map. + unsigned size() const { return map.size(); } + + /// Returns the bit vector index for a given declaration. + Optional<unsigned> getValueIndex(const VarDecl *d) const; +}; +} + +void DeclToIndex::computeMap(const DeclContext &dc) { + unsigned count = 0; + DeclContext::specific_decl_iterator<VarDecl> I(dc.decls_begin()), + E(dc.decls_end()); + for ( ; I != E; ++I) { + const VarDecl *vd = *I; + if (isTrackedVar(vd, &dc)) + map[vd] = count++; + } +} + +Optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const { + llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d); + if (I == map.end()) + return None; + return I->second; +} + +//------------------------------------------------------------------------====// +// CFGBlockValues: dataflow values for CFG blocks. +//====------------------------------------------------------------------------// + +// These values are defined in such a way that a merge can be done using +// a bitwise OR. +enum Value { Unknown = 0x0, /* 00 */ + Initialized = 0x1, /* 01 */ + Uninitialized = 0x2, /* 10 */ + MayUninitialized = 0x3 /* 11 */ }; + +static bool isUninitialized(const Value v) { + return v >= Uninitialized; +} +static bool isAlwaysUninit(const Value v) { + return v == Uninitialized; +} + +namespace { + +typedef llvm::PackedVector<Value, 2, llvm::SmallBitVector> ValueVector; + +class CFGBlockValues { + const CFG &cfg; + SmallVector<ValueVector, 8> vals; + ValueVector scratch; + DeclToIndex declToIndex; +public: + CFGBlockValues(const CFG &cfg); + + unsigned getNumEntries() const { return declToIndex.size(); } + + void computeSetOfDeclarations(const DeclContext &dc); + ValueVector &getValueVector(const CFGBlock *block) { + return vals[block->getBlockID()]; + } + + void setAllScratchValues(Value V); + void mergeIntoScratch(ValueVector const &source, bool isFirst); + bool updateValueVectorWithScratch(const CFGBlock *block); + + bool hasNoDeclarations() const { + return declToIndex.size() == 0; + } + + void resetScratch(); + + ValueVector::reference operator[](const VarDecl *vd); + + Value getValue(const CFGBlock *block, const CFGBlock *dstBlock, + const VarDecl *vd) { + const Optional<unsigned> &idx = declToIndex.getValueIndex(vd); + assert(idx.hasValue()); + return getValueVector(block)[idx.getValue()]; + } +}; +} // end anonymous namespace + +CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {} + +void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) { + declToIndex.computeMap(dc); + unsigned decls = declToIndex.size(); + scratch.resize(decls); + unsigned n = cfg.getNumBlockIDs(); + if (!n) + return; + vals.resize(n); + for (unsigned i = 0; i < n; ++i) + vals[i].resize(decls); +} + +#if DEBUG_LOGGING +static void printVector(const CFGBlock *block, ValueVector &bv, + unsigned num) { + llvm::errs() << block->getBlockID() << " :"; + for (unsigned i = 0; i < bv.size(); ++i) { + llvm::errs() << ' ' << bv[i]; + } + llvm::errs() << " : " << num << '\n'; +} +#endif + +void CFGBlockValues::setAllScratchValues(Value V) { + for (unsigned I = 0, E = scratch.size(); I != E; ++I) + scratch[I] = V; +} + +void CFGBlockValues::mergeIntoScratch(ValueVector const &source, + bool isFirst) { + if (isFirst) + scratch = source; + else + scratch |= source; +} + +bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) { + ValueVector &dst = getValueVector(block); + bool changed = (dst != scratch); + if (changed) + dst = scratch; +#if DEBUG_LOGGING + printVector(block, scratch, 0); +#endif + return changed; +} + +void CFGBlockValues::resetScratch() { + scratch.reset(); +} + +ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) { + const Optional<unsigned> &idx = declToIndex.getValueIndex(vd); + assert(idx.hasValue()); + return scratch[idx.getValue()]; +} + +//------------------------------------------------------------------------====// +// Worklist: worklist for dataflow analysis. +//====------------------------------------------------------------------------// + +namespace { +class DataflowWorklist { + PostOrderCFGView::iterator PO_I, PO_E; + SmallVector<const CFGBlock *, 20> worklist; + llvm::BitVector enqueuedBlocks; +public: + DataflowWorklist(const CFG &cfg, PostOrderCFGView &view) + : PO_I(view.begin()), PO_E(view.end()), + enqueuedBlocks(cfg.getNumBlockIDs(), true) { + // Treat the first block as already analyzed. + if (PO_I != PO_E) { + assert(*PO_I == &cfg.getEntry()); + enqueuedBlocks[(*PO_I)->getBlockID()] = false; + ++PO_I; + } + } + + void enqueueSuccessors(const CFGBlock *block); + const CFGBlock *dequeue(); +}; +} + +void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) { + for (CFGBlock::const_succ_iterator I = block->succ_begin(), + E = block->succ_end(); I != E; ++I) { + const CFGBlock *Successor = *I; + if (!Successor || enqueuedBlocks[Successor->getBlockID()]) + continue; + worklist.push_back(Successor); + enqueuedBlocks[Successor->getBlockID()] = true; + } +} + +const CFGBlock *DataflowWorklist::dequeue() { + const CFGBlock *B = 0; + + // First dequeue from the worklist. This can represent + // updates along backedges that we want propagated as quickly as possible. + if (!worklist.empty()) + B = worklist.pop_back_val(); + + // Next dequeue from the initial reverse post order. This is the + // theoretical ideal in the presence of no back edges. + else if (PO_I != PO_E) { + B = *PO_I; + ++PO_I; + } + else { + return 0; + } + + assert(enqueuedBlocks[B->getBlockID()] == true); + enqueuedBlocks[B->getBlockID()] = false; + return B; +} + +//------------------------------------------------------------------------====// +// Classification of DeclRefExprs as use or initialization. +//====------------------------------------------------------------------------// + +namespace { +class FindVarResult { + const VarDecl *vd; + const DeclRefExpr *dr; +public: + FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {} + + const DeclRefExpr *getDeclRefExpr() const { return dr; } + const VarDecl *getDecl() const { return vd; } +}; + +static const Expr *stripCasts(ASTContext &C, const Expr *Ex) { + while (Ex) { + Ex = Ex->IgnoreParenNoopCasts(C); + if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + if (CE->getCastKind() == CK_LValueBitCast) { + Ex = CE->getSubExpr(); + continue; + } + } + break; + } + return Ex; +} + +/// If E is an expression comprising a reference to a single variable, find that +/// variable. +static FindVarResult findVar(const Expr *E, const DeclContext *DC) { + if (const DeclRefExpr *DRE = + dyn_cast<DeclRefExpr>(stripCasts(DC->getParentASTContext(), E))) + if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) + if (isTrackedVar(VD, DC)) + return FindVarResult(VD, DRE); + return FindVarResult(0, 0); +} + +/// \brief Classify each DeclRefExpr as an initialization or a use. Any +/// DeclRefExpr which isn't explicitly classified will be assumed to have +/// escaped the analysis and will be treated as an initialization. +class ClassifyRefs : public StmtVisitor<ClassifyRefs> { +public: + enum Class { + Init, + Use, + SelfInit, + Ignore + }; + +private: + const DeclContext *DC; + llvm::DenseMap<const DeclRefExpr*, Class> Classification; + + bool isTrackedVar(const VarDecl *VD) const { + return ::isTrackedVar(VD, DC); + } + + void classify(const Expr *E, Class C); + +public: + ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {} + + void VisitDeclStmt(DeclStmt *DS); + void VisitUnaryOperator(UnaryOperator *UO); + void VisitBinaryOperator(BinaryOperator *BO); + void VisitCallExpr(CallExpr *CE); + void VisitCastExpr(CastExpr *CE); + + void operator()(Stmt *S) { Visit(S); } + + Class get(const DeclRefExpr *DRE) const { + llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I + = Classification.find(DRE); + if (I != Classification.end()) + return I->second; + + const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()); + if (!VD || !isTrackedVar(VD)) + return Ignore; + + return Init; + } +}; +} + +static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) { + if (Expr *Init = VD->getInit()) { + const DeclRefExpr *DRE + = dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init)); + if (DRE && DRE->getDecl() == VD) + return DRE; + } + return 0; +} + +void ClassifyRefs::classify(const Expr *E, Class C) { + // The result of a ?: could also be an lvalue. + E = E->IgnoreParens(); + if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) { + const Expr *TrueExpr = CO->getTrueExpr(); + if (!isa<OpaqueValueExpr>(TrueExpr)) + classify(TrueExpr, C); + classify(CO->getFalseExpr(), C); + return; + } + + FindVarResult Var = findVar(E, DC); + if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) + Classification[DRE] = std::max(Classification[DRE], C); +} + +void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) { + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + VarDecl *VD = dyn_cast<VarDecl>(*DI); + if (VD && isTrackedVar(VD)) + if (const DeclRefExpr *DRE = getSelfInitExpr(VD)) + Classification[DRE] = SelfInit; + } +} + +void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) { + // Ignore the evaluation of a DeclRefExpr on the LHS of an assignment. If this + // is not a compound-assignment, we will treat it as initializing the variable + // when TransferFunctions visits it. A compound-assignment does not affect + // whether a variable is uninitialized, and there's no point counting it as a + // use. + if (BO->isCompoundAssignmentOp()) + classify(BO->getLHS(), Use); + else if (BO->getOpcode() == BO_Assign) + classify(BO->getLHS(), Ignore); +} + +void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) { + // Increment and decrement are uses despite there being no lvalue-to-rvalue + // conversion. + if (UO->isIncrementDecrementOp()) + classify(UO->getSubExpr(), Use); +} + +void ClassifyRefs::VisitCallExpr(CallExpr *CE) { + // If a value is passed by const reference to a function, we should not assume + // that it is initialized by the call, and we conservatively do not assume + // that it is used. + for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) + if ((*I)->getType().isConstQualified() && (*I)->isGLValue()) + classify(*I, Ignore); +} + +void ClassifyRefs::VisitCastExpr(CastExpr *CE) { + if (CE->getCastKind() == CK_LValueToRValue) + classify(CE->getSubExpr(), Use); + else if (CStyleCastExpr *CSE = dyn_cast<CStyleCastExpr>(CE)) { + if (CSE->getType()->isVoidType()) { + // Squelch any detected load of an uninitialized value if + // we cast it to void. + // e.g. (void) x; + classify(CSE->getSubExpr(), Ignore); + } + } +} + +//------------------------------------------------------------------------====// +// Transfer function for uninitialized values analysis. +//====------------------------------------------------------------------------// + +namespace { +class TransferFunctions : public StmtVisitor<TransferFunctions> { + CFGBlockValues &vals; + const CFG &cfg; + const CFGBlock *block; + AnalysisDeclContext ∾ + const ClassifyRefs &classification; + ObjCNoReturn objCNoRet; + UninitVariablesHandler &handler; + +public: + TransferFunctions(CFGBlockValues &vals, const CFG &cfg, + const CFGBlock *block, AnalysisDeclContext &ac, + const ClassifyRefs &classification, + UninitVariablesHandler &handler) + : vals(vals), cfg(cfg), block(block), ac(ac), + classification(classification), objCNoRet(ac.getASTContext()), + handler(handler) {} + + void reportUse(const Expr *ex, const VarDecl *vd); + + void VisitBinaryOperator(BinaryOperator *bo); + void VisitBlockExpr(BlockExpr *be); + void VisitCallExpr(CallExpr *ce); + void VisitDeclRefExpr(DeclRefExpr *dr); + void VisitDeclStmt(DeclStmt *ds); + void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS); + void VisitObjCMessageExpr(ObjCMessageExpr *ME); + + bool isTrackedVar(const VarDecl *vd) { + return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl())); + } + + FindVarResult findVar(const Expr *ex) { + return ::findVar(ex, cast<DeclContext>(ac.getDecl())); + } + + UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) { + UninitUse Use(ex, isAlwaysUninit(v)); + + assert(isUninitialized(v)); + if (Use.getKind() == UninitUse::Always) + return Use; + + // If an edge which leads unconditionally to this use did not initialize + // the variable, we can say something stronger than 'may be uninitialized': + // we can say 'either it's used uninitialized or you have dead code'. + // + // We track the number of successors of a node which have been visited, and + // visit a node once we have visited all of its successors. Only edges where + // the variable might still be uninitialized are followed. Since a variable + // can't transfer from being initialized to being uninitialized, this will + // trace out the subgraph which inevitably leads to the use and does not + // initialize the variable. We do not want to skip past loops, since their + // non-termination might be correlated with the initialization condition. + // + // For example: + // + // void f(bool a, bool b) { + // block1: int n; + // if (a) { + // block2: if (b) + // block3: n = 1; + // block4: } else if (b) { + // block5: while (!a) { + // block6: do_work(&a); + // n = 2; + // } + // } + // block7: if (a) + // block8: g(); + // block9: return n; + // } + // + // Starting from the maybe-uninitialized use in block 9: + // * Block 7 is not visited because we have only visited one of its two + // successors. + // * Block 8 is visited because we've visited its only successor. + // From block 8: + // * Block 7 is visited because we've now visited both of its successors. + // From block 7: + // * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all + // of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively). + // * Block 3 is not visited because it initializes 'n'. + // Now the algorithm terminates, having visited blocks 7 and 8, and having + // found the frontier is blocks 2, 4, and 5. + // + // 'n' is definitely uninitialized for two edges into block 7 (from blocks 2 + // and 4), so we report that any time either of those edges is taken (in + // each case when 'b == false'), 'n' is used uninitialized. + SmallVector<const CFGBlock*, 32> Queue; + SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0); + Queue.push_back(block); + // Specify that we've already visited all successors of the starting block. + // This has the dual purpose of ensuring we never add it to the queue, and + // of marking it as not being a candidate element of the frontier. + SuccsVisited[block->getBlockID()] = block->succ_size(); + while (!Queue.empty()) { + const CFGBlock *B = Queue.pop_back_val(); + + // If the use is always reached from the entry block, make a note of that. + if (B == &cfg.getEntry()) + Use.setUninitAfterCall(); + + for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end(); + I != E; ++I) { + const CFGBlock *Pred = *I; + Value AtPredExit = vals.getValue(Pred, B, vd); + if (AtPredExit == Initialized) + // This block initializes the variable. + continue; + if (AtPredExit == MayUninitialized && + vals.getValue(B, 0, vd) == Uninitialized) { + // This block declares the variable (uninitialized), and is reachable + // from a block that initializes the variable. We can't guarantee to + // give an earlier location for the diagnostic (and it appears that + // this code is intended to be reachable) so give a diagnostic here + // and go no further down this path. + Use.setUninitAfterDecl(); + continue; + } + + unsigned &SV = SuccsVisited[Pred->getBlockID()]; + if (!SV) { + // When visiting the first successor of a block, mark all NULL + // successors as having been visited. + for (CFGBlock::const_succ_iterator SI = Pred->succ_begin(), + SE = Pred->succ_end(); + SI != SE; ++SI) + if (!*SI) + ++SV; + } + + if (++SV == Pred->succ_size()) + // All paths from this block lead to the use and don't initialize the + // variable. + Queue.push_back(Pred); + } + } + + // Scan the frontier, looking for blocks where the variable was + // uninitialized. + for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) { + const CFGBlock *Block = *BI; + unsigned BlockID = Block->getBlockID(); + const Stmt *Term = Block->getTerminator(); + if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() && + Term) { + // This block inevitably leads to the use. If we have an edge from here + // to a post-dominator block, and the variable is uninitialized on that + // edge, we have found a bug. + for (CFGBlock::const_succ_iterator I = Block->succ_begin(), + E = Block->succ_end(); I != E; ++I) { + const CFGBlock *Succ = *I; + if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size() && + vals.getValue(Block, Succ, vd) == Uninitialized) { + // Switch cases are a special case: report the label to the caller + // as the 'terminator', not the switch statement itself. Suppress + // situations where no label matched: we can't be sure that's + // possible. + if (isa<SwitchStmt>(Term)) { + const Stmt *Label = Succ->getLabel(); + if (!Label || !isa<SwitchCase>(Label)) + // Might not be possible. + continue; + UninitUse::Branch Branch; + Branch.Terminator = Label; + Branch.Output = 0; // Ignored. + Use.addUninitBranch(Branch); + } else { + UninitUse::Branch Branch; + Branch.Terminator = Term; + Branch.Output = I - Block->succ_begin(); + Use.addUninitBranch(Branch); + } + } + } + } + } + + return Use; + } +}; +} + +void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) { + Value v = vals[vd]; + if (isUninitialized(v)) + handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v)); +} + +void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) { + // This represents an initialization of the 'element' value. + if (DeclStmt *DS = dyn_cast<DeclStmt>(FS->getElement())) { + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); + if (isTrackedVar(VD)) + vals[VD] = Initialized; + } +} + +void TransferFunctions::VisitBlockExpr(BlockExpr *be) { + const BlockDecl *bd = be->getBlockDecl(); + for (BlockDecl::capture_const_iterator i = bd->capture_begin(), + e = bd->capture_end() ; i != e; ++i) { + const VarDecl *vd = i->getVariable(); + if (!isTrackedVar(vd)) + continue; + if (i->isByRef()) { + vals[vd] = Initialized; + continue; + } + reportUse(be, vd); + } +} + +void TransferFunctions::VisitCallExpr(CallExpr *ce) { + if (Decl *Callee = ce->getCalleeDecl()) { + if (Callee->hasAttr<ReturnsTwiceAttr>()) { + // After a call to a function like setjmp or vfork, any variable which is + // initialized anywhere within this function may now be initialized. For + // now, just assume such a call initializes all variables. FIXME: Only + // mark variables as initialized if they have an initializer which is + // reachable from here. + vals.setAllScratchValues(Initialized); + } + else if (Callee->hasAttr<AnalyzerNoReturnAttr>()) { + // Functions labeled like "analyzer_noreturn" are often used to denote + // "panic" functions that in special debug situations can still return, + // but for the most part should not be treated as returning. This is a + // useful annotation borrowed from the static analyzer that is useful for + // suppressing branch-specific false positives when we call one of these + // functions but keep pretending the path continues (when in reality the + // user doesn't care). + vals.setAllScratchValues(Unknown); + } + } +} + +void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { + switch (classification.get(dr)) { + case ClassifyRefs::Ignore: + break; + case ClassifyRefs::Use: + reportUse(dr, cast<VarDecl>(dr->getDecl())); + break; + case ClassifyRefs::Init: + vals[cast<VarDecl>(dr->getDecl())] = Initialized; + break; + case ClassifyRefs::SelfInit: + handler.handleSelfInit(cast<VarDecl>(dr->getDecl())); + break; + } +} + +void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) { + if (BO->getOpcode() == BO_Assign) { + FindVarResult Var = findVar(BO->getLHS()); + if (const VarDecl *VD = Var.getDecl()) + vals[VD] = Initialized; + } +} + +void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + VarDecl *VD = dyn_cast<VarDecl>(*DI); + if (VD && isTrackedVar(VD)) { + if (getSelfInitExpr(VD)) { + // If the initializer consists solely of a reference to itself, we + // explicitly mark the variable as uninitialized. This allows code + // like the following: + // + // int x = x; + // + // to deliberately leave a variable uninitialized. Different analysis + // clients can detect this pattern and adjust their reporting + // appropriately, but we need to continue to analyze subsequent uses + // of the variable. + vals[VD] = Uninitialized; + } else if (VD->getInit()) { + // Treat the new variable as initialized. + vals[VD] = Initialized; + } else { + // No initializer: the variable is now uninitialized. This matters + // for cases like: + // while (...) { + // int n; + // use(n); + // n = 0; + // } + // FIXME: Mark the variable as uninitialized whenever its scope is + // left, since its scope could be re-entered by a jump over the + // declaration. + vals[VD] = Uninitialized; + } + } + } +} + +void TransferFunctions::VisitObjCMessageExpr(ObjCMessageExpr *ME) { + // If the Objective-C message expression is an implicit no-return that + // is not modeled in the CFG, set the tracked dataflow values to Unknown. + if (objCNoRet.isImplicitNoReturn(ME)) { + vals.setAllScratchValues(Unknown); + } +} + +//------------------------------------------------------------------------====// +// High-level "driver" logic for uninitialized values analysis. +//====------------------------------------------------------------------------// + +static bool runOnBlock(const CFGBlock *block, const CFG &cfg, + AnalysisDeclContext &ac, CFGBlockValues &vals, + const ClassifyRefs &classification, + llvm::BitVector &wasAnalyzed, + UninitVariablesHandler &handler) { + wasAnalyzed[block->getBlockID()] = true; + vals.resetScratch(); + // Merge in values of predecessor blocks. + bool isFirst = true; + for (CFGBlock::const_pred_iterator I = block->pred_begin(), + E = block->pred_end(); I != E; ++I) { + const CFGBlock *pred = *I; + if (wasAnalyzed[pred->getBlockID()]) { + vals.mergeIntoScratch(vals.getValueVector(pred), isFirst); + isFirst = false; + } + } + // Apply the transfer function. + TransferFunctions tf(vals, cfg, block, ac, classification, handler); + for (CFGBlock::const_iterator I = block->begin(), E = block->end(); + I != E; ++I) { + if (Optional<CFGStmt> cs = I->getAs<CFGStmt>()) + tf.Visit(const_cast<Stmt*>(cs->getStmt())); + } + return vals.updateValueVectorWithScratch(block); +} + +/// PruneBlocksHandler is a special UninitVariablesHandler that is used +/// to detect when a CFGBlock has any *potential* use of an uninitialized +/// variable. It is mainly used to prune out work during the final +/// reporting pass. +namespace { +struct PruneBlocksHandler : public UninitVariablesHandler { + PruneBlocksHandler(unsigned numBlocks) + : hadUse(numBlocks, false), hadAnyUse(false), + currentBlock(0) {} + + virtual ~PruneBlocksHandler() {} + + /// Records if a CFGBlock had a potential use of an uninitialized variable. + llvm::BitVector hadUse; + + /// Records if any CFGBlock had a potential use of an uninitialized variable. + bool hadAnyUse; + + /// The current block to scribble use information. + unsigned currentBlock; + + virtual void handleUseOfUninitVariable(const VarDecl *vd, + const UninitUse &use) { + hadUse[currentBlock] = true; + hadAnyUse = true; + } + + /// Called when the uninitialized variable analysis detects the + /// idiom 'int x = x'. All other uses of 'x' within the initializer + /// are handled by handleUseOfUninitVariable. + virtual void handleSelfInit(const VarDecl *vd) { + hadUse[currentBlock] = true; + hadAnyUse = true; + } +}; +} + +void clang::runUninitializedVariablesAnalysis( + const DeclContext &dc, + const CFG &cfg, + AnalysisDeclContext &ac, + UninitVariablesHandler &handler, + UninitVariablesAnalysisStats &stats) { + CFGBlockValues vals(cfg); + vals.computeSetOfDeclarations(dc); + if (vals.hasNoDeclarations()) + return; + + stats.NumVariablesAnalyzed = vals.getNumEntries(); + + // Precompute which expressions are uses and which are initializations. + ClassifyRefs classification(ac); + cfg.VisitBlockStmts(classification); + + // Mark all variables uninitialized at the entry. + const CFGBlock &entry = cfg.getEntry(); + ValueVector &vec = vals.getValueVector(&entry); + const unsigned n = vals.getNumEntries(); + for (unsigned j = 0; j < n ; ++j) { + vec[j] = Uninitialized; + } + + // Proceed with the workist. + DataflowWorklist worklist(cfg, *ac.getAnalysis<PostOrderCFGView>()); + llvm::BitVector previouslyVisited(cfg.getNumBlockIDs()); + worklist.enqueueSuccessors(&cfg.getEntry()); + llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false); + wasAnalyzed[cfg.getEntry().getBlockID()] = true; + PruneBlocksHandler PBH(cfg.getNumBlockIDs()); + + while (const CFGBlock *block = worklist.dequeue()) { + PBH.currentBlock = block->getBlockID(); + + // Did the block change? + bool changed = runOnBlock(block, cfg, ac, vals, + classification, wasAnalyzed, PBH); + ++stats.NumBlockVisits; + if (changed || !previouslyVisited[block->getBlockID()]) + worklist.enqueueSuccessors(block); + previouslyVisited[block->getBlockID()] = true; + } + + if (!PBH.hadAnyUse) + return; + + // Run through the blocks one more time, and report uninitialized variables. + for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) { + const CFGBlock *block = *BI; + if (PBH.hadUse[block->getBlockID()]) { + runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, handler); + ++stats.NumBlockVisits; + } + } +} + +UninitVariablesHandler::~UninitVariablesHandler() {} |