diff options
author | Jordan Rose <jordan_rose@apple.com> | 2012-07-02 19:28:09 +0000 |
---|---|---|
committer | Jordan Rose <jordan_rose@apple.com> | 2012-07-02 19:28:09 +0000 |
commit | 69f87c956b3ac2b80124fd9604af012e1061473a (patch) | |
tree | a137e8b3bb536d633c81c6b3c31ec63a855baa27 /lib/StaticAnalyzer | |
parent | de507eaf3cb54d3cb234dc14499c10ab3373d15f (diff) |
[analyzer] Use CallEvent for inlining and call default-evaluation.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@159560 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Core/Calls.cpp | 21 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CheckerManager.cpp | 24 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 208 |
3 files changed, 122 insertions, 131 deletions
diff --git a/lib/StaticAnalyzer/Core/Calls.cpp b/lib/StaticAnalyzer/Core/Calls.cpp index 4fec757e0e..35ddfc965f 100644 --- a/lib/StaticAnalyzer/Core/Calls.cpp +++ b/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<BlockDataRegion>(DataReg); + return dyn_cast_or_null<BlockDataRegion>(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<FunctionType>(BlockTy->getPointeeType())->getResultType(); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 5cb1b49c15..e4209e50b3 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/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/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 0cbf4829ab..d2d2f53eaf 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/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<CXXConstructorDecl>(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<BlockCall>(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<BlockDataRegion>(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<BlockDecl>(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<ReplayWithoutInlining>(); if (!ReplayState) return 0; - const CallExpr *ReplayCE = reinterpret_cast<const CallExpr*>(ReplayState); - if (CE == ReplayCE) { + const Stmt *ReplayCallE = reinterpret_cast<const Stmt *>(ReplayState); + if (CallE == ReplayCallE) { return N->getState()->remove<ReplayWithoutInlining>(); } 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); - - // 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<CXXMemberCallExpr>(CE)) { - CXXMemberCall Call(MemberCE, state, LCtx); - state = Call.invalidateRegions(Count); - } else if (isa<BlockDataRegion>(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 + // Get the callee kind. + const CXXMemberCallExpr *MemberCE = dyn_cast<CXXMemberCallExpr>(CE); + bool IsBlock = (MemberCE ? false + : CE->getCallee()->getType()->isBlockPointerType()); + + // 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) { |