[analyzer] Bound the size of the functions being inlined + provide
command line options for inlining tuning.

This adds the option for stack depth bound as well as function size
bound. 

+ minor doxygenification

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151930 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index 9d44280..e7a0ad3 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -81,7 +81,11 @@
 def analyzer_viz_egraph_ubigraph : Flag<"-analyzer-viz-egraph-ubigraph">,
   HelpText<"Display exploded graph using Ubigraph">;
 def analyzer_inline_call : Flag<"-analyzer-inline-call">,
-  HelpText<"Experimental transfer function inlining callees when its definition is available.">;
+  HelpText<"Experimental: Inline callees when their definitions are available">;
+def analyzer_inline_max_stack_depth : Separate<"-analyzer-inline-max-stack-depth">,
+  HelpText<"Bound on stack depth while inlining (4 by default)">;
+def analyzer_inline_max_function_size : Separate<"-analyzer-inline-max-function-size">,
+  HelpText<"Bound on the number of basic blocks in an inlined function (10 by default)">;
 def analyzer_max_nodes : Separate<"-analyzer-max-nodes">,
   HelpText<"The maximum number of nodes the analyzer can generate (150000 default, 0 = no limit)">;
 def analyzer_max_loop : Separate<"-analyzer-max-loop">,
diff --git a/include/clang/Frontend/AnalyzerOptions.h b/include/clang/Frontend/AnalyzerOptions.h
index 8a1f4f0..a39dc25 100644
--- a/include/clang/Frontend/AnalyzerOptions.h
+++ b/include/clang/Frontend/AnalyzerOptions.h
@@ -85,6 +85,8 @@
   unsigned CFGAddInitializers : 1;
   unsigned EagerlyTrimEGraph : 1;
   unsigned PrintStats : 1;
+  unsigned InlineMaxStackDepth;
+  unsigned InlineMaxFunctionSize;
 
 public:
   AnalyzerOptions() {
@@ -106,6 +108,9 @@
     CFGAddInitializers = 0;
     EagerlyTrimEGraph = 0;
     PrintStats = 0;
+    // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls).
+    InlineMaxStackDepth = 5;
+    InlineMaxFunctionSize = 10;
   }
 };
 
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
index 3a9a125..39020c1 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h
@@ -53,29 +53,40 @@
 
   enum AnalysisScope { ScopeTU, ScopeDecl } AScope;
 
-  // The maximum number of exploded nodes the analyzer will generate.
+  /// \brief The maximum number of exploded nodes the analyzer will generate.
   unsigned MaxNodes;
 
-  // The maximum number of times the analyzer visit a block.
+  /// \brief The maximum number of times the analyzer visits a block.
   unsigned MaxVisit;
 
   bool VisualizeEGDot;
   bool VisualizeEGUbi;
   AnalysisPurgeMode PurgeDead;
 
-  /// EargerlyAssume - A flag indicating how the engine should handle
-  //   expressions such as: 'x = (y != 0)'.  When this flag is true then
-  //   the subexpression 'y != 0' will be eagerly assumed to be true or false,
-  //   thus evaluating it to the integers 0 or 1 respectively.  The upside
-  //   is that this can increase analysis precision until we have a better way
-  //   to lazily evaluate such logic.  The downside is that it eagerly
-  //   bifurcates paths.
+  /// \brief The flag regulates if we should eagerly assume evaluations of
+  /// conditionals, thus, bifurcating the path.
+  ///
+  /// EagerlyAssume - A flag indicating how the engine should handle
+  ///   expressions such as: 'x = (y != 0)'.  When this flag is true then
+  ///   the subexpression 'y != 0' will be eagerly assumed to be true or false,
+  ///   thus evaluating it to the integers 0 or 1 respectively.  The upside
+  ///   is that this can increase analysis precision until we have a better way
+  ///   to lazily evaluate such logic.  The downside is that it eagerly
+  ///   bifurcates paths.
   bool EagerlyAssume;
   bool TrimGraph;
   bool InlineCall;
   bool EagerlyTrimEGraph;
 
 public:
+  // Settings for inlining tuning.
+
+  /// \brief The inlining stack depth limit.
+  unsigned InlineMaxStackDepth;
+  /// \brief The max number of basic blocks in a function being inlined.
+  unsigned InlineMaxFunctionSize;
+
+public:
   AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, 
                   const LangOptions &lang, PathDiagnosticConsumer *pd,
                   StoreManagerCreator storemgr,
@@ -87,7 +98,9 @@
                   bool eager, bool trim,
                   bool inlinecall, bool useUnoptimizedCFG,
                   bool addImplicitDtors, bool addInitializers,
-                  bool eagerlyTrimEGraph);
+                  bool eagerlyTrimEGraph,
+                  unsigned inlineMaxStack,
+                  unsigned inlineMaxFunctionSize);
 
   /// Construct a clone of the given AnalysisManager with the given ASTContext
   /// and DiagnosticsEngine.
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index b0c0302..14cdefb 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1047,6 +1047,12 @@
   if (Args.hasArg(OPT_analyzer_inline_call))
     Opts.InlineCall = 1;
   Opts.PrintStats = Args.hasArg(OPT_analyzer_stats);
+  Opts.InlineMaxStackDepth =
+    Args.getLastArgIntValue(OPT_analyzer_inline_max_stack_depth,
+                            Opts.InlineMaxStackDepth, Diags);
+  Opts.InlineMaxFunctionSize =
+    Args.getLastArgIntValue(OPT_analyzer_inline_max_function_size,
+                            Opts.InlineMaxFunctionSize, Diags);
 
   Opts.CheckersControlList.clear();
   for (arg_iterator it = Args.filtered_begin(OPT_analyzer_checker,
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index af18eff..00701c9 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -29,7 +29,9 @@
                                  bool eager, bool trim,
                                  bool inlinecall, bool useUnoptimizedCFG,
                                  bool addImplicitDtors, bool addInitializers,
-                                 bool eagerlyTrimEGraph)
+                                 bool eagerlyTrimEGraph,
+                                 unsigned inlineMaxStack,
+                                 unsigned inlineMaxFunctionSize)
   : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
     Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd),
     CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
@@ -37,7 +39,9 @@
     AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
     VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
     EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall),
-    EagerlyTrimEGraph(eagerlyTrimEGraph)
+    EagerlyTrimEGraph(eagerlyTrimEGraph),
+    InlineMaxStackDepth(inlineMaxStack),
+    InlineMaxFunctionSize(inlineMaxFunctionSize)
 {
   AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
 }
@@ -62,7 +66,9 @@
     EagerlyAssume(ParentAM.EagerlyAssume),
     TrimGraph(ParentAM.TrimGraph),
     InlineCall(ParentAM.InlineCall),
-    EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph)
+    EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph),
+    InlineMaxStackDepth(ParentAM.InlineMaxStackDepth),
+    InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize)
 {
   AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
 }
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index c570d1b..bb4ea99 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -142,13 +142,16 @@
       // FIXME: Handle C++.
       break;
     case Stmt::CallExprClass: {
-      // Cap the stack depth at 4 calls (5 stack frames, base + 4 calls).
-      // These heuristics are a WIP.
-      if (getNumberStackFrames(Pred->getLocationContext()) == 5)
+      if (getNumberStackFrames(Pred->getLocationContext())
+            == AMgr.InlineMaxStackDepth)
         return false;
-      
-      // Construct a new stack frame for the callee.
+
       AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(FD);
+      const CFG *CalleeCFG = CalleeADC->getCFG();
+      if (CalleeCFG->getNumBlockIDs() > AMgr.InlineMaxFunctionSize)
+        return false;
+
+      // Construct a new stack frame for the callee.
       const StackFrameContext *CallerSFC =
       Pred->getLocationContext()->getCurrentStackFrame();
       const StackFrameContext *CalleeSFC =
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 074ad23..f8b6832 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -157,7 +157,9 @@
                                   Opts.TrimGraph, Opts.InlineCall,
                                   Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors,
                                   Opts.CFGAddInitializers,
-                                  Opts.EagerlyTrimEGraph));
+                                  Opts.EagerlyTrimEGraph,
+                                  Opts.InlineMaxStackDepth,
+                                  Opts.InlineMaxFunctionSize));
     if (Opts.PrintStats)
       llvm::EnableStatistics();
   }
diff --git a/test/Analysis/malloc-interprocedural.c b/test/Analysis/malloc-interprocedural.c
index 374c4a3..8a42f39 100644
--- a/test/Analysis/malloc-interprocedural.c
+++ b/test/Analysis/malloc-interprocedural.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=unix.Malloc -analyzer-inline-call -analyzer-store=region -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=unix.Malloc -analyzer-inline-call -analyzer-inline-max-stack-depth 5 -analyzer-inline-max-function-size 6 -analyzer-store=region -verify %s
 
 #include "system-header-simulator.h"