forked from OSchip/llvm-project
[analyzer] Cache whether a function is generally inlineable.
Certain properties of a function can determine ahead of time whether or not the function is inlineable, such as its kind, its signature, or its location. We can cache this value in the FunctionSummaries map to avoid rechecking these static properties for every call. Note that the analyzer may still decide not to inline a specific call to a function because of the particular dynamic properties of the call along the current path. No intended functionality change. llvm-svn: 178515
This commit is contained in:
parent
33a1063cab
commit
19440f58e9
|
@ -14,35 +14,42 @@
|
||||||
#ifndef LLVM_CLANG_GR_FUNCTIONSUMMARY_H
|
#ifndef LLVM_CLANG_GR_FUNCTIONSUMMARY_H
|
||||||
#define LLVM_CLANG_GR_FUNCTIONSUMMARY_H
|
#define LLVM_CLANG_GR_FUNCTIONSUMMARY_H
|
||||||
|
|
||||||
#include "clang/AST/Decl.h"
|
#include "clang/Basic/LLVM.h"
|
||||||
#include "llvm/ADT/SmallBitVector.h"
|
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "llvm/ADT/SmallBitVector.h"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
|
class Decl;
|
||||||
|
|
||||||
namespace ento {
|
namespace ento {
|
||||||
typedef std::deque<Decl*> SetOfDecls;
|
typedef std::deque<Decl*> SetOfDecls;
|
||||||
typedef llvm::DenseSet<const Decl*> SetOfConstDecls;
|
typedef llvm::DenseSet<const Decl*> SetOfConstDecls;
|
||||||
|
|
||||||
class FunctionSummariesTy {
|
class FunctionSummariesTy {
|
||||||
struct FunctionSummary {
|
class FunctionSummary {
|
||||||
|
public:
|
||||||
/// Marks the IDs of the basic blocks visited during the analyzes.
|
/// Marks the IDs of the basic blocks visited during the analyzes.
|
||||||
llvm::SmallBitVector VisitedBasicBlocks;
|
llvm::SmallBitVector VisitedBasicBlocks;
|
||||||
|
|
||||||
/// Total number of blocks in the function.
|
/// Total number of blocks in the function.
|
||||||
unsigned TotalBasicBlocks : 31;
|
unsigned TotalBasicBlocks : 30;
|
||||||
|
|
||||||
/// True if this function has reached a max block count while inlined from
|
/// True if this function has been checked against the rules for which
|
||||||
/// at least one call site.
|
/// functions may be inlined.
|
||||||
unsigned MayReachMaxBlockCount : 1;
|
unsigned InlineChecked : 1;
|
||||||
|
|
||||||
|
/// True if this function may be inlined.
|
||||||
|
unsigned MayInline : 1;
|
||||||
|
|
||||||
/// The number of times the function has been inlined.
|
/// The number of times the function has been inlined.
|
||||||
unsigned TimesInlined : 32;
|
unsigned TimesInlined : 32;
|
||||||
|
|
||||||
FunctionSummary() :
|
FunctionSummary() :
|
||||||
TotalBasicBlocks(0),
|
TotalBasicBlocks(0),
|
||||||
MayReachMaxBlockCount(0),
|
InlineChecked(0),
|
||||||
TimesInlined(0) {}
|
TimesInlined(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,16 +68,27 @@ public:
|
||||||
return I;
|
return I;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markReachedMaxBlockCount(const Decl* D) {
|
void markMayInline(const Decl *D) {
|
||||||
MapTy::iterator I = findOrInsertSummary(D);
|
MapTy::iterator I = findOrInsertSummary(D);
|
||||||
I->second.MayReachMaxBlockCount = 1;
|
I->second.InlineChecked = 1;
|
||||||
|
I->second.MayInline = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasReachedMaxBlockCount(const Decl* D) {
|
void markShouldNotInline(const Decl *D) {
|
||||||
MapTy::const_iterator I = Map.find(D);
|
MapTy::iterator I = findOrInsertSummary(D);
|
||||||
if (I != Map.end())
|
I->second.InlineChecked = 1;
|
||||||
return I->second.MayReachMaxBlockCount;
|
I->second.MayInline = 0;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
void markReachedMaxBlockCount(const Decl *D) {
|
||||||
|
markShouldNotInline(D);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<bool> mayInline(const Decl *D) {
|
||||||
|
MapTy::const_iterator I = Map.find(D);
|
||||||
|
if (I != Map.end() && I->second.InlineChecked)
|
||||||
|
return I->second.MayInline;
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
void markVisitedBasicBlock(unsigned ID, const Decl* D, unsigned TotalIDs) {
|
void markVisitedBasicBlock(unsigned ID, const Decl* D, unsigned TotalIDs) {
|
||||||
|
|
|
@ -578,9 +578,15 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
|
||||||
Bldr.generateNode(Call.getProgramPoint(), State, Pred);
|
Bldr.generateNode(Call.getProgramPoint(), State, Pred);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool shouldInlineCallKind(const CallEvent &Call,
|
enum CallInlinePolicy {
|
||||||
const ExplodedNode *Pred,
|
CIP_Allowed,
|
||||||
AnalyzerOptions &Opts) {
|
CIP_DisallowedOnce,
|
||||||
|
CIP_DisallowedAlways
|
||||||
|
};
|
||||||
|
|
||||||
|
static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
|
||||||
|
const ExplodedNode *Pred,
|
||||||
|
AnalyzerOptions &Opts) {
|
||||||
const LocationContext *CurLC = Pred->getLocationContext();
|
const LocationContext *CurLC = Pred->getLocationContext();
|
||||||
const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
|
const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
|
||||||
switch (Call.getKind()) {
|
switch (Call.getKind()) {
|
||||||
|
@ -590,18 +596,18 @@ static bool shouldInlineCallKind(const CallEvent &Call,
|
||||||
case CE_CXXMember:
|
case CE_CXXMember:
|
||||||
case CE_CXXMemberOperator:
|
case CE_CXXMemberOperator:
|
||||||
if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions))
|
if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions))
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
break;
|
break;
|
||||||
case CE_CXXConstructor: {
|
case CE_CXXConstructor: {
|
||||||
if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors))
|
if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors))
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
|
|
||||||
const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
|
const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
|
||||||
|
|
||||||
// FIXME: We don't handle constructors or destructors for arrays properly.
|
// FIXME: We don't handle constructors or destructors for arrays properly.
|
||||||
const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion();
|
const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion();
|
||||||
if (Target && isa<ElementRegion>(Target))
|
if (Target && isa<ElementRegion>(Target))
|
||||||
return false;
|
return CIP_DisallowedOnce;
|
||||||
|
|
||||||
// FIXME: This is a hack. We don't use the correct region for a new
|
// FIXME: This is a hack. We don't use the correct region for a new
|
||||||
// expression, so if we inline the constructor its result will just be
|
// expression, so if we inline the constructor its result will just be
|
||||||
|
@ -610,7 +616,7 @@ static bool shouldInlineCallKind(const CallEvent &Call,
|
||||||
const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
|
const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
|
||||||
if (const Stmt *Parent = CurLC->getParentMap().getParent(CtorExpr))
|
if (const Stmt *Parent = CurLC->getParentMap().getParent(CtorExpr))
|
||||||
if (isa<CXXNewExpr>(Parent))
|
if (isa<CXXNewExpr>(Parent))
|
||||||
return false;
|
return CIP_DisallowedOnce;
|
||||||
|
|
||||||
// Inlining constructors requires including initializers in the CFG.
|
// Inlining constructors requires including initializers in the CFG.
|
||||||
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
|
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
|
||||||
|
@ -624,19 +630,19 @@ static bool shouldInlineCallKind(const CallEvent &Call,
|
||||||
// For other types, only inline constructors if destructor inlining is
|
// For other types, only inline constructors if destructor inlining is
|
||||||
// also enabled.
|
// also enabled.
|
||||||
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
|
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
|
|
||||||
// FIXME: This is a hack. We don't handle temporary destructors
|
// FIXME: This is a hack. We don't handle temporary destructors
|
||||||
// right now, so we shouldn't inline their constructors.
|
// right now, so we shouldn't inline their constructors.
|
||||||
if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
|
if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
|
||||||
if (!Target || !isa<DeclRegion>(Target))
|
if (!Target || !isa<DeclRegion>(Target))
|
||||||
return false;
|
return CIP_DisallowedOnce;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CE_CXXDestructor: {
|
case CE_CXXDestructor: {
|
||||||
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
|
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
|
|
||||||
// Inlining destructors requires building the CFG correctly.
|
// Inlining destructors requires building the CFG correctly.
|
||||||
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
|
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
|
||||||
|
@ -648,22 +654,71 @@ static bool shouldInlineCallKind(const CallEvent &Call,
|
||||||
// FIXME: We don't handle constructors or destructors for arrays properly.
|
// FIXME: We don't handle constructors or destructors for arrays properly.
|
||||||
const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion();
|
const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion();
|
||||||
if (Target && isa<ElementRegion>(Target))
|
if (Target && isa<ElementRegion>(Target))
|
||||||
return false;
|
return CIP_DisallowedOnce;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CE_CXXAllocator:
|
case CE_CXXAllocator:
|
||||||
// Do not inline allocators until we model deallocators.
|
// Do not inline allocators until we model deallocators.
|
||||||
// This is unfortunate, but basically necessary for smart pointers and such.
|
// This is unfortunate, but basically necessary for smart pointers and such.
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
case CE_ObjCMessage:
|
case CE_ObjCMessage:
|
||||||
if (!Opts.mayInlineObjCMethod())
|
if (!Opts.mayInlineObjCMethod())
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
if (!(Opts.getIPAMode() == IPAK_DynamicDispatch ||
|
if (!(Opts.getIPAMode() == IPAK_DynamicDispatch ||
|
||||||
Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate))
|
Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate))
|
||||||
return false;
|
return CIP_DisallowedAlways;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return CIP_Allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the function in \p CalleeADC may be inlined in general.
|
||||||
|
///
|
||||||
|
/// This checks static properties of the function, such as its signature and
|
||||||
|
/// CFG, to determine whether the analyzer should ever consider inlining it,
|
||||||
|
/// in any context.
|
||||||
|
static bool mayInlineDecl(const CallEvent &Call, AnalysisDeclContext *CalleeADC,
|
||||||
|
AnalyzerOptions &Opts) {
|
||||||
|
const Decl *D = CalleeADC->getDecl();
|
||||||
|
|
||||||
|
// FIXME: Do not inline variadic calls.
|
||||||
|
if (Call.isVariadic())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check certain C++-related inlining policies.
|
||||||
|
ASTContext &Ctx = D->getASTContext();
|
||||||
|
if (Ctx.getLangOpts().CPlusPlus) {
|
||||||
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
||||||
|
// Conditionally control the inlining of template functions.
|
||||||
|
if (!Opts.mayInlineTemplateFunctions())
|
||||||
|
if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Conditionally control the inlining of C++ standard library functions.
|
||||||
|
if (!Opts.mayInlineCXXStandardLibrary())
|
||||||
|
if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation()))
|
||||||
|
if (IsInStdNamespace(FD))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is possible that the CFG cannot be constructed.
|
||||||
|
// Be safe, and check if the CalleeCFG is valid.
|
||||||
|
const CFG *CalleeCFG = CalleeADC->getCFG();
|
||||||
|
if (!CalleeCFG)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Do not inline large functions.
|
||||||
|
if (CalleeCFG->getNumBlockIDs() > Opts.getMaxInlinableSize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// It is possible that the live variables analysis cannot be
|
||||||
|
// run. If so, bail out.
|
||||||
|
if (!CalleeADC->getAnalysis<RelaxedLiveVariables>())
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,15 +741,37 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
|
||||||
if (!AMgr.shouldInlineCall())
|
if (!AMgr.shouldInlineCall())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if we should inline a call based on its kind.
|
// Check if this function has been marked as non-inlinable.
|
||||||
if (!shouldInlineCallKind(Call, Pred, Opts))
|
Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D);
|
||||||
return false;
|
if (MayInline.hasValue()) {
|
||||||
|
if (!MayInline.getValue())
|
||||||
|
return false;
|
||||||
|
|
||||||
// It is possible that the CFG cannot be constructed.
|
} else {
|
||||||
// Be safe, and check if the CalleeCFG is valid.
|
// We haven't actually checked the static properties of this function yet.
|
||||||
const CFG *CalleeCFG = CalleeADC->getCFG();
|
// Do that now, and record our decision in the function summaries.
|
||||||
if (!CalleeCFG)
|
if (mayInlineDecl(Call, CalleeADC, Opts)) {
|
||||||
|
Engine.FunctionSummaries->markMayInline(D);
|
||||||
|
} else {
|
||||||
|
Engine.FunctionSummaries->markShouldNotInline(D);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we should inline a call based on its kind.
|
||||||
|
// FIXME: this checks both static and dynamic properties of the call, which
|
||||||
|
// means we're redoing a bit of work that could be cached in the function
|
||||||
|
// summary.
|
||||||
|
CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts);
|
||||||
|
if (CIP != CIP_Allowed) {
|
||||||
|
if (CIP == CIP_DisallowedAlways) {
|
||||||
|
assert(!MayInline.hasValue() || MayInline.getValue());
|
||||||
|
Engine.FunctionSummaries->markShouldNotInline(D);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CFG *CalleeCFG = CalleeADC->getCFG();
|
||||||
|
|
||||||
// Do not inline if recursive or we've reached max stack frame count.
|
// Do not inline if recursive or we've reached max stack frame count.
|
||||||
bool IsRecursive = false;
|
bool IsRecursive = false;
|
||||||
|
@ -705,39 +782,6 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
|
||||||
|| IsRecursive))
|
|| IsRecursive))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Do not inline if it took too long to inline previously.
|
|
||||||
if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Or if the function is too big.
|
|
||||||
if (CalleeCFG->getNumBlockIDs() > Opts.getMaxInlinableSize())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Do not inline variadic calls (for now).
|
|
||||||
if (Call.isVariadic())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check our template policy.
|
|
||||||
if (getContext().getLangOpts().CPlusPlus) {
|
|
||||||
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
||||||
// Conditionally allow the inlining of template functions.
|
|
||||||
if (!Opts.mayInlineTemplateFunctions())
|
|
||||||
if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Conditionally allow the inlining of C++ standard library functions.
|
|
||||||
if (!Opts.mayInlineCXXStandardLibrary())
|
|
||||||
if (getContext().getSourceManager().isInSystemHeader(FD->getLocation()))
|
|
||||||
if (IsInStdNamespace(FD))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is possible that the live variables analysis cannot be
|
|
||||||
// run. If so, bail out.
|
|
||||||
if (!CalleeADC->getAnalysis<RelaxedLiveVariables>())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Do not inline large functions too many times.
|
// Do not inline large functions too many times.
|
||||||
if ((Engine.FunctionSummaries->getNumTimesInlined(D) >
|
if ((Engine.FunctionSummaries->getNumTimesInlined(D) >
|
||||||
Opts.getMaxTimesInlineLarge()) &&
|
Opts.getMaxTimesInlineLarge()) &&
|
||||||
|
|
Loading…
Reference in New Issue