From cbeef5588624b930da980b32d36978a915568965 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Mon, 2 Jul 2012 19:28:09 +0000 Subject: [PATCH] [analyzer] Use CallEvent for inlining and call default-evaluation. llvm-svn: 159560 --- .../StaticAnalyzer/Core/CheckerManager.h | 10 +- .../StaticAnalyzer/Core/PathSensitive/Calls.h | 21 +- .../Core/PathSensitive/ExprEngine.h | 9 +- clang/lib/StaticAnalyzer/Core/Calls.cpp | 21 +- .../StaticAnalyzer/Core/CheckerManager.cpp | 24 +- .../Core/ExprEngineCallAndReturn.cpp | 206 +++++++++--------- 6 files changed, 147 insertions(+), 144 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h index eef82fe84830..689b13d99d56 100644 --- a/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/clang/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -33,6 +33,7 @@ namespace ento { class AnalysisManager; class BugReporter; class CheckerContext; + class SimpleCall; class ObjCMethodCall; class SVal; class ExplodedNode; @@ -44,12 +45,6 @@ namespace ento { class MemRegion; class SymbolReaper; -class GraphExpander { -public: - virtual ~GraphExpander(); - virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) = 0; -}; - template class CheckerFn; template (getSVal(CE->getCallee()).getAsRegion())); + : SimpleCall(CE, St, LCtx, CE_Block) {} + + /// \brief Returns the region associated with this instance of the block. + /// + /// This may be NULL if the block's origin is unknown. + const BlockDataRegion *getBlockRegion() const; + + /// \brief Gets the declaration of the block. + /// + /// This is not an override of getDecl() because AnyFunctionCall has already + /// assumed that it's a FunctionDecl. + const BlockDecl *getBlockDecl() const { + const BlockDataRegion *BR = getBlockRegion(); + if (!BR) + return 0; + return BR->getDecl(); } static bool classof(const CallEvent *CA) { return CA->getKind() == CE_Block; } - -private: - const BlockDataRegion *getBlockRegion() const; }; /// \brief Represents a call to a C++ constructor. diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index bddc23cacd5b..c8a7e8102a0b 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -42,6 +42,7 @@ namespace ento { class AnalysisManager; class CallEvent; +class SimpleCall; class ObjCMethodCall; class ExprEngine : public SubEngine { @@ -468,6 +469,11 @@ public: void evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, const Expr *StoreE, ExplodedNode *Pred, ProgramStateRef St, SVal TargetLV, SVal Val, const ProgramPointTag *tag = 0); + + void evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const SimpleCall &Call); + void defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call); private: void evalLoadCommon(ExplodedNodeSet &Dst, const Expr *NodeEx, /* Eventually will be a CFGStmt */ @@ -488,7 +494,8 @@ private: const ProgramPointTag *tag, bool isLoad); bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred); - bool InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExplodedNode *Pred); + bool inlineCall(ExplodedNodeSet &Dst, const CallEvent &Call, + ExplodedNode *Pred); bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC); }; diff --git a/clang/lib/StaticAnalyzer/Core/Calls.cpp b/clang/lib/StaticAnalyzer/Core/Calls.cpp index 4fec757e0eec..35ddfc965f10 100644 --- a/clang/lib/StaticAnalyzer/Core/Calls.cpp +++ b/clang/lib/StaticAnalyzer/Core/Calls.cpp @@ -307,23 +307,34 @@ const BlockDataRegion *BlockCall::getBlockRegion() const { const Expr *Callee = getOriginExpr()->getCallee(); const MemRegion *DataReg = getSVal(Callee).getAsRegion(); - return cast(DataReg); + return dyn_cast_or_null(DataReg); } CallEvent::param_iterator BlockCall::param_begin() const { - return getBlockRegion()->getDecl()->param_begin(); + const BlockDecl *D = getBlockDecl(); + if (!D) + return 0; + return D->param_begin(); } CallEvent::param_iterator BlockCall::param_end() const { - return getBlockRegion()->getDecl()->param_end(); + const BlockDecl *D = getBlockDecl(); + if (!D) + return 0; + return D->param_end(); } void BlockCall::addExtraInvalidatedRegions(RegionList &Regions) const { - Regions.push_back(getBlockRegion()); + // FIXME: This also needs to invalidate captured globals. + if (const MemRegion *R = getBlockRegion()) + Regions.push_back(R); } QualType BlockCall::getDeclaredResultType() const { - QualType BlockTy = getBlockRegion()->getCodeRegion()->getLocationType(); + const BlockDataRegion *BR = getBlockRegion(); + if (!BR) + return QualType(); + QualType BlockTy = BR->getCodeRegion()->getLocationType(); return cast(BlockTy->getPointeeType())->getResultType(); } diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 5cb1b49c1500..e4209e50b3a5 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -461,16 +461,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, /// Only one checker will evaluate the call. void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, const ExplodedNodeSet &Src, - const CallExpr *CE, - ExprEngine &Eng, - GraphExpander *defaultEval) { - if (EvalCallCheckers.empty() && - InlineCallCheckers.empty() && - defaultEval == 0) { - Dst.insert(Src); - return; - } - + const SimpleCall &Call, + ExprEngine &Eng) { + const CallExpr *CE = Call.getOriginExpr(); for (ExplodedNodeSet::iterator NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) { @@ -533,12 +526,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, } // If none of the checkers evaluated the call, ask ExprEngine to handle it. - if (!anyEvaluated) { - if (defaultEval) - defaultEval->expandGraph(Dst, Pred); - else - Dst.insert(Pred); - } + if (!anyEvaluated) + Eng.defaultEvalCall(Dst, Pred, Call); } } @@ -678,6 +667,3 @@ CheckerManager::~CheckerManager() { for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i) CheckerDtors[i](); } - -// Anchor for the vtable. -GraphExpander::~GraphExpander() { } diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 0cbf4829ab4f..d2d2f53eaf6f 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -207,6 +207,10 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) { // Determine if we should inline the call. bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { + // FIXME: default constructors don't have bodies. + if (!D->hasBody()) + return false; + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const CFG *CalleeCFG = CalleeADC->getCFG(); @@ -235,52 +239,47 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { return false; } + // Do not inline constructors until we can model destructors. + // This is unfortunate, but basically necessary for smart pointers and such. + if (isa(D)) + return false; + return true; } -bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, - const CallExpr *CE, +bool ExprEngine::inlineCall(ExplodedNodeSet &Dst, + const CallEvent &Call, ExplodedNode *Pred) { if (!getAnalysisManager().shouldInlineCall()) return false; - // if (!shouldInlineCallExpr(CE, this)) - // return false; - const StackFrameContext *CallerSFC = Pred->getLocationContext()->getCurrentStackFrame(); - ProgramStateRef state = Pred->getState(); - const Expr *Callee = CE->getCallee(); - SVal CalleeVal = state->getSVal(Callee, Pred->getLocationContext()); - const Decl *D = 0; + const Decl *D = Call.getDecl(); const LocationContext *ParentOfCallee = 0; - - if (const FunctionDecl *FD = CalleeVal.getAsFunctionDecl()) { - if (!FD->hasBody(FD)) + + switch (Call.getKind()) { + case CE_Function: + case CE_CXXConstructor: + case CE_CXXMember: + // These are always at least possible to inline. + break; + case CE_Block: { + const BlockDataRegion *BR = cast(Call).getBlockRegion(); + if (!BR) return false; - - switch (CE->getStmtClass()) { - default: - break; - case Stmt::CXXMemberCallExprClass: - case Stmt::CallExprClass: { - D = FD; - break; - - } - } - } else if (const BlockDataRegion *BR = - dyn_cast_or_null(CalleeVal.getAsRegion())) { - assert(CE->getStmtClass() == Stmt::CallExprClass); - const BlockDecl *BD = BR->getDecl(); - D = BD; - AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(BD); + D = BR->getDecl(); + AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, - BD, + cast(D), BR); - } else { - // This is case we don't handle yet. + break; + } + case CE_ObjCMessage: + case CE_ObjCPropertyAccess: + // These always use dynamic dispatch; enabling inlining means assuming + // that a particular method will be called at runtime. return false; } @@ -290,16 +289,19 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, if (!ParentOfCallee) ParentOfCallee = CallerSFC; + const Expr *CallE = Call.getOriginExpr(); + assert(CallE && "It is not yet possible to have calls without statements"); + // Construct a new stack frame for the callee. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(ParentOfCallee, CE, + CalleeADC->getStackFrame(ParentOfCallee, CallE, currentBuilderContext->getBlock(), currentStmtIdx); - CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); + CallEnter Loc(CallE, CalleeSFC, Pred->getLocationContext()); bool isNew; - if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { + if (ExplodedNode *N = G.getNode(Loc, Pred->getState(), false, &isNew)) { N->addPredecessor(Pred, G); if (isNew) Engine.getWorkList()->enqueue(N); @@ -307,13 +309,13 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, return true; } -static ProgramStateRef getReplayWithoutInliningState(ExplodedNode *&N, - const CallExpr *CE) { +static ProgramStateRef getInlineFailedState(ExplodedNode *&N, + const Stmt *CallE) { void *ReplayState = N->getState()->get(); if (!ReplayState) return 0; - const CallExpr *ReplayCE = reinterpret_cast(ReplayState); - if (CE == ReplayCE) { + const Stmt *ReplayCallE = reinterpret_cast(ReplayState); + if (CallE == ReplayCallE) { return N->getState()->remove(); } return 0; @@ -324,83 +326,75 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, // Perform the previsit of the CallExpr. ExplodedNodeSet dstPreVisit; getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this); - - // Now evaluate the call itself. - class DefaultEval : public GraphExpander { - ExprEngine &Eng; - const CallExpr *CE; - public: - - DefaultEval(ExprEngine &eng, const CallExpr *ce) - : Eng(eng), CE(ce) {} - virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) { - ProgramStateRef state = getReplayWithoutInliningState(Pred, CE); + // Get the callee kind. + const CXXMemberCallExpr *MemberCE = dyn_cast(CE); + bool IsBlock = (MemberCE ? false + : CE->getCallee()->getType()->isBlockPointerType()); - // First, try to inline the call. - if (state == 0 && Eng.InlineCall(Dst, CE, Pred)) - return; - - // First handle the return value. - StmtNodeBuilder Bldr(Pred, Dst, *Eng.currentBuilderContext); - - // Get the callee. - const Expr *Callee = CE->getCallee()->IgnoreParens(); - if (state == 0) - state = Pred->getState(); - SVal L = state->getSVal(Callee, Pred->getLocationContext()); - - // Figure out the result type. We do this dance to handle references. - // FIXME: This doesn't handle C++ methods, blocks, etc. - QualType ResultTy; - if (const FunctionDecl *FD = L.getAsFunctionDecl()) - ResultTy = FD->getResultType(); - else - ResultTy = CE->getType(); - - if (CE->isGLValue()) - ResultTy = Eng.getContext().getPointerType(ResultTy); - - // Conjure a symbol value to use as the result. - SValBuilder &SVB = Eng.getSValBuilder(); - unsigned Count = Eng.currentBuilderContext->getCurrentBlockCount(); - const LocationContext *LCtx = Pred->getLocationContext(); - SVal RetVal = SVB.getConjuredSymbolVal(0, CE, LCtx, ResultTy, Count); - - // Generate a new state with the return value set. - state = state->BindExpr(CE, LCtx, RetVal); - - // Invalidate the arguments. - if (const CXXMemberCallExpr *MemberCE = dyn_cast(CE)) { - CXXMemberCall Call(MemberCE, state, LCtx); - state = Call.invalidateRegions(Count); - } else if (isa(L.getAsRegion())) { - BlockCall Call(CE, state, LCtx); - state = Call.invalidateRegions(Count); - } else { - FunctionCall Call(CE, state, LCtx); - state = Call.invalidateRegions(Count); - } - - // And make the result node. - Bldr.generateNode(CE, Pred, state); - } - }; - - // Finally, evaluate the function call. We try each of the checkers + // Evaluate the function call. We try each of the checkers // to see if the can evaluate the function call. ExplodedNodeSet dstCallEvaluated; - DefaultEval defEval(*this, CE); - getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, - dstPreVisit, - CE, *this, &defEval); - + for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + + // Evaluate the call. + if (MemberCE) + evalCall(dstCallEvaluated, *I, CXXMemberCall(MemberCE, State, LCtx)); + else if (IsBlock) + evalCall(dstCallEvaluated, *I, BlockCall(CE, State, LCtx)); + else + evalCall(dstCallEvaluated, *I, FunctionCall(CE, State, LCtx)); + } + // Finally, perform the post-condition check of the CallExpr and store // the created nodes in 'Dst'. + // Note that if the call was inlined, dstCallEvaluated will be empty. + // The post-CallExpr check will occur in processCallExit. getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, *this); } +void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const SimpleCall &Call) { + getCheckerManager().runCheckersForEvalCall(Dst, Pred, Call, *this); +} + +void ExprEngine::defaultEvalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call) { + // Try to inline the call. + ProgramStateRef state = 0; + const Expr *E = Call.getOriginExpr(); + if (E) { + state = getInlineFailedState(Pred, E); + if (state == 0 && inlineCall(Dst, Call, Pred)) + return; + } + + // If we can't inline it, handle the return value and invalidate the regions. + StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext); + + // Invalidate any regions touched by the call. + unsigned Count = currentBuilderContext->getCurrentBlockCount(); + if (state == 0) + state = Pred->getState(); + state = Call.invalidateRegions(Count, state); + + // Conjure a symbol value to use as the result. + assert(Call.getOriginExpr() && "Must have an expression to bind the result"); + QualType ResultTy = Call.getResultType(); + SValBuilder &SVB = getSValBuilder(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal RetVal = SVB.getConjuredSymbolVal(0, Call.getOriginExpr(), LCtx, + ResultTy, Count); + + // And make the result node. + state = state->BindExpr(Call.getOriginExpr(), LCtx, RetVal); + Bldr.generateNode(Call.getOriginExpr(), Pred, state); +} + void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, ExplodedNodeSet &Dst) {