]> granicus.if.org Git - clang/commitdiff
[analyzer] Implement "do not inline large functions many times"
authorAnna Zaks <ganna@apple.com>
Mon, 17 Dec 2012 20:08:51 +0000 (20:08 +0000)
committerAnna Zaks <ganna@apple.com>
Mon, 17 Dec 2012 20:08:51 +0000 (20:08 +0000)
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

include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
include/clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
test/Analysis/analyzer-config.c
test/Analysis/analyzer-config.cpp

index 9cbce17ca864f6f13ba22c8d14b6298e33aae058..02bb036a0a9e47261aa211224369cf6419b5b104 100644 (file)
@@ -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;
index 9e2505fe60d4310d9ecd6db6d8d8c6d62ff8fce4..546cec568f960ce9fbc267a7859a2ff27e7777e5 100644 (file)
@@ -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);
index 8ecbac009d3e8eafdd14285c21f4f39e25ce2594..b993804afeadb358b08b9288574e171b8b58ec24 100644 (file)
@@ -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);
 }
index d654bb5f7073d34c0df9166f81cd387a9c6706ce..7c1c26e8f77f87face662a2f340f43714ddf235a 100644 (file)
@@ -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;
 }
 
index 990f5784b42bed538613ef7e716d6deaa887a583..d156a6ec3b5305ddfd101ad8ec78f232b4c584bb 100644 (file)
@@ -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
index fb142669b428e05856877496f25d5b687b85c9b8..b3a8b34d49528f87565699c4087487f614edab1a 100644 (file)
@@ -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