forked from OSchip/llvm-project
[analyzer] Use CallEvent for inlining and call default-evaluation.
llvm-svn: 159560
This commit is contained in:
parent
547060b30b
commit
cbeef55886
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() { }
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
Loading…
Reference in New Issue