[analyzer] Use CallEvent for inlining and call default-evaluation.

llvm-svn: 159560
This commit is contained in:
Jordan Rose 2012-07-02 19:28:09 +00:00
parent 547060b30b
commit cbeef55886
6 changed files with 147 additions and 144 deletions

View File

@ -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 <typename T> class CheckerFn;
template <typename RET, typename P1, typename P2, typename P3, typename P4,
@ -303,8 +298,7 @@ public:
/// \brief Run checkers for evaluating a call.
void runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallExpr *CE, ExprEngine &Eng,
GraphExpander *defaultEval = 0);
const SimpleCall &CE, ExprEngine &Eng);
/// \brief Run checkers for the entire Translation Unit.
void runCheckersOnEndOfTranslationUnit(const TranslationUnitDecl *TU,

View File

@ -278,16 +278,27 @@ protected:
public:
BlockCall(const CallExpr *CE, ProgramStateRef St,
const LocationContext *LCtx)
: SimpleCall(CE, St, LCtx, CE_Block) {
assert(isa<BlockDataRegion>(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.

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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() { }

View File

@ -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);
// Get the callee kind.
const CXXMemberCallExpr *MemberCE = dyn_cast<CXXMemberCallExpr>(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<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
// 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) {