diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 9cbce17ca864f6f13ba22c8d14b6298e33aae058..02bb036a0a9e47261aa211224369cf6419b5b104 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -193,6 +193,9 @@ private: /// \sa getGraphTrimInterval llvm::Optional<unsigned> GraphTrimInterval; + /// \sa getMaxTimesInlineLarge + llvm::Optional<unsigned> MaxTimesInlineLarge; + /// Interprets an option's string value as a boolean. /// /// Accepts the strings "true" and "false". @@ -276,6 +279,11 @@ public: /// node reclamation, set the option to "0". unsigned getGraphTrimInterval(); + /// Returns the maximum times a large function could be inlined. + /// + /// This is controlled by the 'max-times-inline-large' config option. + unsigned getMaxTimesInlineLarge(); + public: AnalyzerOptions() : CXXMemberInliningMode() { AnalysisStoreOpt = RegionStoreModel; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h index 9e2505fe60d4310d9ecd6db6d8d8c6d62ff8fce4..546cec568f960ce9fbc267a7859a2ff27e7777e5 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h @@ -37,10 +37,14 @@ class FunctionSummariesTy { /// Marks the IDs of the basic blocks visited during the analyzes. llvm::BitVector VisitedBasicBlocks; + /// The number of times the function has been inlined. + unsigned TimesInlined; + FunctionSummary() : MayReachMaxBlockCount(false), TotalBasicBlocks(0), - VisitedBasicBlocks(0) {} + VisitedBasicBlocks(0), + TimesInlined(0) {} }; typedef llvm::DenseMap<const Decl*, FunctionSummary*> MapTy; @@ -84,11 +88,23 @@ public: unsigned getNumVisitedBasicBlocks(const Decl* D) { MapTy::const_iterator I = Map.find(D); - if (I != Map.end()) - return I->second->VisitedBasicBlocks.count(); + if (I != Map.end()) + return I->second->VisitedBasicBlocks.count(); return 0; } + unsigned getNumTimesInlined(const Decl* D) { + MapTy::const_iterator I = Map.find(D); + if (I != Map.end()) + return I->second->TimesInlined; + return 0; + } + + void bumpNumTimesInlined(const Decl* D) { + MapTy::iterator I = findOrInsertSummary(D); + I->second->TimesInlined++; + } + /// Get the percentage of the reachable blocks. unsigned getPercentBlocksReachable(const Decl *D) { MapTy::const_iterator I = Map.find(D); diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 8ecbac009d3e8eafdd14285c21f4f39e25ce2594..b993804afeadb358b08b9288574e171b8b58ec24 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -133,6 +133,12 @@ unsigned AnalyzerOptions::getGraphTrimInterval() { return GraphTrimInterval.getValue(); } +unsigned AnalyzerOptions::getMaxTimesInlineLarge() { + if (!MaxTimesInlineLarge.hasValue()) + MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32); + return MaxTimesInlineLarge.getValue(); +} + bool AnalyzerOptions::shouldSynthesizeBodies() { return getBooleanOption("faux-bodies", true); } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index d654bb5f7073d34c0df9166f81cd387a9c6706ce..7c1c26e8f77f87face662a2f340f43714ddf235a 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -33,6 +33,9 @@ STATISTIC(NumOfDynamicDispatchPathSplits, STATISTIC(NumInlinedCalls, "The # of times we inlined a call"); +STATISTIC(NumReachedInlineCountMax, + "The # of times we reached inline count maximum"); + void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); @@ -415,12 +418,12 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { if (getContext().getLangOpts().CPlusPlus) { if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { // Conditionally allow the inlining of template functions. - if (!getAnalysisManager().options.mayInlineTemplateFunctions()) + if (!AMgr.options.mayInlineTemplateFunctions()) if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) return false; // Conditionally allow the inlining of C++ standard library functions. - if (!getAnalysisManager().options.mayInlineCXXStandardLibrary()) + if (!AMgr.options.mayInlineCXXStandardLibrary()) if (getContext().getSourceManager().isInSystemHeader(FD->getLocation())) if (IsInStdNamespace(FD)) return false; @@ -432,6 +435,14 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) { if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) return false; + if (Engine.FunctionSummaries->getNumTimesInlined(D) > + AMgr.options.getMaxTimesInlineLarge() && + CalleeCFG->getNumBlockIDs() > 13) { + NumReachedInlineCountMax++; + return false; + } + Engine.FunctionSummaries->bumpNumTimesInlined(D); + return true; } diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index 990f5784b42bed538613ef7e716d6deaa887a583..d156a6ec3b5305ddfd101ad8ec78f232b4c584bb 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -9,5 +9,6 @@ void foo() { bar(); } // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: ipa-always-inline-size = 3 +// CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 4 +// CHECK-NEXT: num-entries = 5 diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index fb142669b428e05856877496f25d5b687b85c9b8..b3a8b34d49528f87565699c4087487f614edab1a 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -18,5 +18,6 @@ public: // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 // CHECK-NEXT: ipa-always-inline-size = 3 +// CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 7 +// CHECK-NEXT: num-entries = 8