From: Anna Zaks Date: Mon, 17 Dec 2012 20:08:51 +0000 (+0000) Subject: [analyzer] Implement "do not inline large functions many times" X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7959671d456c916706a5f61af609d8f1fc95decf;p=clang [analyzer] Implement "do not inline large functions many times" performance heuristic After inlining a function with more than 13 basic blocks 32 times, we are not going to inline it anymore. The idea is that inlining large functions leads to drastic performance implications. Since the function has already been inlined, we know that we've analyzed it in many contexts. The following metrics are used: - Large function is a function with more than 13 basic blocks (we should switch to another metric, like cyclomatic complexity) - We consider that we've inlined a function many times if it's been inlined 32 times. This number is configurable with -analyzer-config max-times-inline-large=xx This heuristic addresses a performance regression introduced with inlining on one benchmark. The analyzer on this benchmark became 60 times slower with inlining turned on. The heuristic allows us to analyze it in 24% of the time. The performance improvements on the other benchmarks I've tested with are much lower - under 10%, which is expected. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@170361 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index 9cbce17ca8..02bb036a0a 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -193,6 +193,9 @@ private: /// \sa getGraphTrimInterval llvm::Optional GraphTrimInterval; + /// \sa getMaxTimesInlineLarge + llvm::Optional 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 9e2505fe60..546cec568f 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 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 8ecbac009d..b993804afe 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 d654bb5f70..7c1c26e8f7 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(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()) 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 990f5784b4..d156a6ec3b 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 fb142669b4..b3a8b34d49 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