aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2012-06-01 20:04:04 +0000
committerTed Kremenek <kremenek@apple.com>2012-06-01 20:04:04 +0000
commit7fa9b4f258636d89342eda28f21a986c8ac353b1 (patch)
treed7b3c4df21fc87a3f8eb202703db8f322d73aa5d /lib/StaticAnalyzer
parent784ae8e5c6b557e2395991c6008293660f5afe66 (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.cpp99
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp39
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp10
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp60
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);