[analyzer] Inline C++ operator new when c++-inline-allocators is turned on.

This will let us stage in the modeling of operator new. The -analyzer-config
opton 'c++-inline-allocators' is currently off by default.

Patch by Karthik Bhat!

llvm-svn: 201122
This commit is contained in:
Jordan Rose 2014-02-11 02:21:06 +00:00
parent 5a69dda9b0
commit 8b808d64af
8 changed files with 67 additions and 6 deletions

View File

@ -199,6 +199,9 @@ private:
/// \sa mayInlineTemplateFunctions
Optional<bool> InlineTemplateFunctions;
/// \sa mayInlineCXXAllocator
Optional<bool> InlineCXXAllocator;
/// \sa mayInlineCXXContainerCtorsAndDtors
Optional<bool> InlineCXXContainerCtorsAndDtors;
@ -291,6 +294,12 @@ public:
/// accepts the values "true" and "false".
bool mayInlineTemplateFunctions();
/// Returns whether or not allocator call may be considered for inlining.
///
/// This is controlled by the 'c++-allocator-inlining' config option, which
/// accepts the values "true" and "false".
bool mayInlineCXXAllocator();
/// Returns whether or not constructors and destructors of C++ container
/// objects may be considered for inlining.
///

View File

@ -421,6 +421,10 @@ public:
const Stmt *S, bool IsBaseDtor,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
void VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNode *Pred,
ExplodedNodeSet &Dst);
void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst);

View File

@ -134,6 +134,12 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() {
/*Default=*/true);
}
bool AnalyzerOptions::mayInlineCXXAllocator() {
return getBooleanOption(InlineCXXAllocator,
"c++-allocator-inlining",
/*Default=*/false);
}
bool AnalyzerOptions::mayInlineCXXContainerCtorsAndDtors() {
return getBooleanOption(InlineCXXContainerCtorsAndDtors,
"c++-container-inlining",

View File

@ -532,6 +532,11 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
return;
}
if ((*Block)[Idx].getKind() == CFGElement::NewAllocator) {
WList->enqueue(N, Block, Idx+1);
return;
}
// At this point, we know we're processing a normal statement.
CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>();
PostStmt Loc(CS.getStmt(), N->getLocationContext());

View File

@ -553,12 +553,20 @@ void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
ExplodedNode *Pred) {
//TODO: Implement VisitCXXNewAllocatorCall
ExplodedNodeSet Dst;
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
const LocationContext *LCtx = Pred->getLocationContext();
PostImplicitCall PP(NE->getOperatorNew(), NE->getLocStart(), LCtx);
Bldr.generateNode(PP, Pred->getState(), Pred);
AnalysisManager &AMgr = getAnalysisManager();
AnalyzerOptions &Opts = AMgr.options;
// TODO: We're not evaluating allocators for all cases just yet as
// we're not handling the return value correctly, which causes false
// positives when the alpha.cplusplus.NewDeleteLeaks check is on.
if (Opts.mayInlineCXXAllocator())
VisitCXXNewAllocatorCall(NE, Pred, Dst);
else {
NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
const LocationContext *LCtx = Pred->getLocationContext();
PostImplicitCall PP(NE->getOperatorNew(), NE->getLocStart(), LCtx);
Bldr.generateNode(PP, Pred->getState(), Pred);
}
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
}

View File

@ -329,6 +329,32 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
*Call, *this);
}
void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
ProgramStateRef State = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
CNE->getStartLoc(),
"Error evaluating New Allocator Call");
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXAllocatorCall> Call =
CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
ExplodedNodeSet DstPreCall;
getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
*Call, *this);
ExplodedNodeSet DstInvalidated;
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx);
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
defaultEvalCall(Bldr, *I, *Call);
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
*Call, *this);
}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
// FIXME: Much of this should eventually migrate to CXXAllocatorCall.

View File

@ -664,6 +664,8 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
break;
}
case CE_CXXAllocator:
if (Opts.mayInlineCXXAllocator())
break;
// Do not inline allocators until we model deallocators.
// This is unfortunate, but basically necessary for smart pointers and such.
return CIP_DisallowedAlways;

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config ipa=inlining -verify %s
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config ipa=inlining -analyzer-config c++-allocator-inlining=true -verify %s
void clang_analyzer_eval(bool);
void clang_analyzer_checkInlined(bool);
@ -9,6 +9,7 @@ extern "C" void *malloc(size_t);
// This is the standard placement new.
inline void* operator new(size_t, void* __p) throw()
{
clang_analyzer_checkInlined(true);// expected-warning{{TRUE}}
return __p;
}