diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-06-01 20:04:04 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-06-01 20:04:04 +0000 |
commit | 7fa9b4f258636d89342eda28f21a986c8ac353b1 (patch) | |
tree | d7b3c4df21fc87a3f8eb202703db8f322d73aa5d /lib/StaticAnalyzer | |
parent | 784ae8e5c6b557e2395991c6008293660f5afe66 (diff) |
static analyzer: add inlining support for directly called blocks.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157833 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp | 99 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/MemRegion.cpp | 39 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ProgramState.cpp | 10 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/RegionStore.cpp | 60 |
4 files changed, 153 insertions, 55 deletions
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 5604a33ae9..cab812ffdc 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -103,7 +103,12 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { const StackFrameContext *calleeCtx = CEBNode->getLocationContext()->getCurrentStackFrame(); - const LocationContext *callerCtx = calleeCtx->getParent(); + + // The parent context might not be a stack frame, so make sure we + // look up the first enclosing stack frame. + const StackFrameContext *callerCtx = + calleeCtx->getParent()->getCurrentStackFrame(); + const Stmt *CE = calleeCtx->getCallSite(); ProgramStateRef state = CEBNode->getState(); // Find the last statement in the function and the corresponding basic block. @@ -199,8 +204,8 @@ static unsigned getNumberStackFrames(const LocationContext *LCtx) { } // Determine if we should inline the call. -bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); +bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const CFG *CalleeCFG = CalleeADC->getCFG(); // It is possible that the CFG cannot be constructed. @@ -212,7 +217,7 @@ bool ExprEngine::shouldInlineDecl(const FunctionDecl *FD, ExplodedNode *Pred) { == AMgr.InlineMaxStackDepth) return false; - if (Engine.FunctionSummaries->hasReachedMaxBlockCount(FD)) + if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D)) return false; if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize) @@ -231,10 +236,7 @@ static bool shouldInlineCallExpr(const CallExpr *CE, ExprEngine *E) { if (const PointerType *PT = callee->getAs<PointerType>()) FT = dyn_cast<FunctionProtoType>(PT->getPointeeType()); else if (const BlockPointerType *BT = callee->getAs<BlockPointerType>()) { - // FIXME: inline blocks. - // FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); - (void) BT; - return false; + FT = dyn_cast<FunctionProtoType>(BT->getPointeeType()); } // If we have no prototype, assume the function is okay. if (!FT) @@ -250,41 +252,64 @@ bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, if (!shouldInlineCallExpr(CE, this)) return false; + const StackFrameContext *CallerSFC = + Pred->getLocationContext()->getCurrentStackFrame(); + ProgramStateRef state = Pred->getState(); const Expr *Callee = CE->getCallee(); - const FunctionDecl *FD = - state->getSVal(Callee, Pred->getLocationContext()).getAsFunctionDecl(); - if (!FD || !FD->hasBody(FD)) - return false; + SVal CalleeVal = state->getSVal(Callee, Pred->getLocationContext()); + const Decl *D = 0; + const LocationContext *ParentOfCallee = 0; - switch (CE->getStmtClass()) { - default: - // FIXME: Handle C++. - break; - case Stmt::CallExprClass: { - if (!shouldInlineDecl(FD, Pred)) - return false; - - // Construct a new stack frame for the callee. - AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD); - const StackFrameContext *CallerSFC = - Pred->getLocationContext()->getCurrentStackFrame(); - const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(CallerSFC, CE, - currentBuilderContext->getBlock(), - currentStmtIdx); - - CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); - bool isNew; - if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { - N->addPredecessor(Pred, G); - if (isNew) - Engine.getWorkList()->enqueue(N); + if (const FunctionDecl *FD = CalleeVal.getAsFunctionDecl()) { + if (!FD->hasBody(FD)) + return false; + + switch (CE->getStmtClass()) { + default: + // FIXME: Handle C++. + break; + case Stmt::CallExprClass: { + D = FD; + break; + } - return true; } + } 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); + ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, + BD, + BR); + } else { + // This is case we don't handle yet. + return false; } - return false; + + if (!D || !shouldInlineDecl(D, Pred)) + return false; + + if (!ParentOfCallee) + ParentOfCallee = CallerSFC; + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(ParentOfCallee, CE, + currentBuilderContext->getBlock(), + currentStmtIdx); + + CallEnter Loc(CE, CalleeSFC, Pred->getLocationContext()); + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, state, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } + return true; } static bool isPointerToConst(const ParmVarDecl *ParamDecl) { diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index e7c57ede88..dc309a0e42 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -643,6 +643,37 @@ MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){ return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion()); } +/// Look through a chain of LocationContexts to either find the +/// StackFrameContext that matches a DeclContext, or find a VarRegion +/// for a variable captured by a block. +static llvm::PointerUnion<const StackFrameContext *, const VarRegion *> +getStackOrCaptureRegionForDeclContext(const LocationContext *LC, + const DeclContext *DC, + const VarDecl *VD) { + while (LC) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) { + if (cast<DeclContext>(SFC->getDecl()) == DC) + return SFC; + } + if (const BlockInvocationContext *BC = + dyn_cast<BlockInvocationContext>(LC)) { + const BlockDataRegion *BR = + static_cast<const BlockDataRegion*>(BC->getContextData()); + // FIXME: This can be made more efficient. + for (BlockDataRegion::referenced_vars_iterator + I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); I != E; ++I) { + if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion())) + if (VR->getDecl() == VD) + return cast<VarRegion>(I.getCapturedRegion()); + } + } + + LC = LC->getParent(); + } + return (const StackFrameContext*)0; +} + const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const LocationContext *LC) { const MemRegion *sReg = 0; @@ -675,7 +706,13 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, // FIXME: Once we implement scope handling, we will need to properly lookup // 'D' to the proper LocationContext. const DeclContext *DC = D->getDeclContext(); - const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); + llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V = + getStackOrCaptureRegionForDeclContext(LC, DC, D); + + if (V.is<const VarRegion*>()) + return V.get<const VarRegion*>(); + + const StackFrameContext *STC = V.get<const StackFrameContext*>(); if (!STC) sReg = getUnknownRegion(); diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index b9cfa27808..a666de0c5c 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -568,6 +568,16 @@ bool ScanReachableSymbols::scan(const MemRegion *R) { if (!scan(SR->getSuperRegion())) return false; + // Regions captured by a block are also implicitly reachable. + if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + for ( ; I != E; ++I) { + if (!scan(I.getCapturedRegion())) + return false; + } + } + // Now look at the binding to this region (if any). if (!scan(state->getSValAsScalarOrLoc(R))) return false; diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index acc0ec4dcf..3a932bc7e2 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -394,6 +394,12 @@ public: // Part of public interface to class. const LocationContext *callerCtx, const StackFrameContext *calleeCtx); + StoreRef enterStackFrame(ProgramStateRef state, + const FunctionDecl *FD, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx); + + //===------------------------------------------------------------------===// // Region "extents". //===------------------------------------------------------------------===// @@ -1944,8 +1950,18 @@ void removeDeadBindingsWorker::VisitBinding(SVal V) { } // If V is a region, then add it to the worklist. - if (const MemRegion *R = V.getAsRegion()) + if (const MemRegion *R = V.getAsRegion()) { AddToWorkList(R); + + // All regions captured by a block are also live. + if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); + for ( ; I != E; ++I) + AddToWorkList(I.getCapturedRegion()); + } + } + // Update the set of live symbols. for (SymExpr::symbol_iterator SI = V.symbol_begin(), SE = V.symbol_end(); @@ -1964,21 +1980,6 @@ void removeDeadBindingsWorker::VisitBindingKey(BindingKey K) { // should continue to track that symbol. if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(R)) SymReaper.markLive(SymR->getSymbol()); - - // For BlockDataRegions, enqueue the VarRegions for variables marked - // with __block (passed-by-reference). - // via BlockDeclRefExprs. - if (const BlockDataRegion *BD = dyn_cast<BlockDataRegion>(R)) { - for (BlockDataRegion::referenced_vars_iterator - RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); - RI != RE; ++RI) { - if ((*RI)->getDecl()->getAttr<BlocksAttr>()) - AddToWorkList(*RI); - } - - // No possible data bindings on a BlockDataRegion. - return; - } } // Visit the data binding for K. @@ -2045,12 +2046,37 @@ StoreRef RegionStoreManager::removeDeadBindings(Store store, return StoreRef(B.getRootWithoutRetain(), *this); } +StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, + const LocationContext *callerCtx, + const StackFrameContext *calleeCtx) +{ + const Decl *D = calleeCtx->getDecl(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + return enterStackFrame(state, FD, callerCtx, calleeCtx); + + // FIXME: when we handle more cases, this will need to be expanded. + + const BlockDecl *BD = cast<BlockDecl>(D); + BlockDecl::param_const_iterator PI = BD->param_begin(), + PE = BD->param_end(); + StoreRef store = StoreRef(state->getStore(), *this); + const CallExpr *CE = cast<CallExpr>(calleeCtx->getCallSite()); + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + for (; AI != AE && PI != PE; ++AI, ++PI) { + SVal ArgVal = state->getSVal(*AI, callerCtx); + store = Bind(store.getStore(), + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI, calleeCtx)), + ArgVal); + } + + return store; +} StoreRef RegionStoreManager::enterStackFrame(ProgramStateRef state, + const FunctionDecl *FD, const LocationContext *callerCtx, const StackFrameContext *calleeCtx) { - FunctionDecl const *FD = cast<FunctionDecl>(calleeCtx->getDecl()); FunctionDecl::param_const_iterator PI = FD->param_begin(), PE = FD->param_end(); StoreRef store = StoreRef(state->getStore(), *this); |