]> granicus.if.org Git - clang/commitdiff
[analyzer] Add ipa-always-inline-size option (with 3 as the default).
authorAnna Zaks <ganna@apple.com>
Mon, 10 Sep 2012 22:37:19 +0000 (22:37 +0000)
committerAnna Zaks <ganna@apple.com>
Mon, 10 Sep 2012 22:37:19 +0000 (22:37 +0000)
The option allows to always inline very small functions, whose size (in
number of basic blocks) is set using -analyzer-config
ipa-always-inline-size option.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163558 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
test/Analysis/inlining/test-always-inline-size-option.c [new file with mode: 0644]

index eb3f8e4271d3bee39a8aa55ea3aed57851234473..72e614d3e769e9118fc7937f614ff148f99da2a9 100644 (file)
@@ -18,9 +18,9 @@
 #include <string>
 #include <vector>
 #include "clang/Basic/LLVM.h"
-#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringMap.h"
 
 namespace clang {
 class ASTConsumer;
@@ -177,6 +177,10 @@ private:
   
   /// \sa mayInlineTemplateFunctions
   llvm::Optional<bool> InlineTemplateFunctions;
+
+  // Cache of the "ipa-always-inline-size" setting.
+  // \sa getAlwaysInlineSize
+  llvm::Optional<unsigned> AlwaysInlineSize;
   
   /// Interprets an option's string value as a boolean.
   ///
@@ -184,6 +188,9 @@ private:
   /// If an option value is not provided, returns the given \p DefaultVal.
   bool getBooleanOption(StringRef Name, bool DefaultVal) const;
 
+  /// Interprets an option's string value as an integer value.
+  int getOptionAsInteger(llvm::StringRef Name, int DefaultVal) const;
+
 public:
   /// Returns the option controlling which C++ member functions will be
   /// considered for inlining.
@@ -213,6 +220,12 @@ public:
   /// accepts the values "true" and "false".
   bool mayInlineTemplateFunctions() const;
 
+  // Returns the size of the functions (in basic blocks), which should be
+  // considered to be small enough to always inline.
+  //
+  // This is controlled by "ipa-always-inline-size" analyzer-config option.
+  unsigned getAlwaysInlineSize() const;
+
 public:
   AnalyzerOptions() : CXXMemberInliningMode() {
     AnalysisStoreOpt = RegionStoreModel;
index 29e27834763bb2dc7b4e3dd59f4e51381c8cfc13..5cbbb8d46285fed9d54142b5cf6954d63ff1902a 100644 (file)
@@ -16,6 +16,7 @@
 #include "llvm/ADT/StringSwitch.h"
 
 using namespace clang;
+using namespace llvm;
 
 bool
 AnalyzerOptions::mayInlineCXXMemberFunction(CXXInlineableMemberKind K) const {
@@ -80,3 +81,26 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() const {
   
   return *InlineTemplateFunctions;
 }
+
+int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal) const {
+  std::string OptStr = Config.lookup(Name);
+  if (OptStr.empty())
+    return DefaultVal;
+
+  int Res = DefaultVal;
+  assert(StringRef(OptStr).getAsInteger(10, Res) == false &&
+         "analyzer-config option should be numeric.");
+
+  return Res;
+}
+
+unsigned AnalyzerOptions::getAlwaysInlineSize() const {
+  if (!AlwaysInlineSize.hasValue()) {
+    unsigned DefaultSize = 3;
+    Optional<unsigned> &MutableOption =
+      const_cast<Optional<unsigned> &>(AlwaysInlineSize);
+    MutableOption = getOptionAsInteger("ipa-always-inline-size", DefaultSize);
+  }
+
+  return AlwaysInlineSize.getValue();
+}
index 9bf63c5b9610203a074523f21541902e4b4a6090..a4e1eb2f4ccb86b14bced5a76bbbcb1d047900f4 100644 (file)
@@ -247,14 +247,18 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
   }
 }
 
-static unsigned getNumberStackFrames(const LocationContext *LCtx) {
-  unsigned count = 0;
+static void examineStackFrames(const Decl *D, const LocationContext *LCtx,
+                               bool &IsRecursive, unsigned &StackDepth) {
+  IsRecursive = false;
+  StackDepth = 0;
   while (LCtx) {
-    if (isa<StackFrameContext>(LCtx))
-      ++count;
+    if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LCtx)) {
+      ++StackDepth;
+      if (SFC->getDecl() == D)
+        IsRecursive = true;
+    }
     LCtx = LCtx->getParent();
   }
-  return count;  
 }
 
 static bool IsInStdNamespace(const FunctionDecl *FD) {
@@ -282,8 +286,12 @@ bool ExprEngine::shouldInlineDecl(const Decl *D, ExplodedNode *Pred) {
   if (!CalleeCFG)
     return false;
 
-  if (getNumberStackFrames(Pred->getLocationContext())
-        == AMgr.options.InlineMaxStackDepth)
+  bool IsRecursive = false;
+  unsigned StackDepth = 0;
+  examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth);
+  if ((StackDepth >= AMgr.options.InlineMaxStackDepth) &&
+       ((CalleeCFG->getNumBlockIDs() > AMgr.options.getAlwaysInlineSize())
+         || IsRecursive))
     return false;
 
   if (Engine.FunctionSummaries->hasReachedMaxBlockCount(D))
diff --git a/test/Analysis/inlining/test-always-inline-size-option.c b/test/Analysis/inlining/test-always-inline-size-option.c
new file mode 100644 (file)
index 0000000..ef604c2
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-inline-max-stack-depth=3 -analyzer-config ipa-always-inline-size=3 -verify %s
+
+void clang_analyzer_eval(int);
+int nested5() {
+  return 0;
+}
+int nested4() {
+  return nested5();
+}
+int nested3() {
+  return nested4();
+}
+int nested2() {
+  return nested3();
+}
+int nested1() {
+  return nested2();
+}
+
+void testNested() {
+  clang_analyzer_eval(nested1() == 0); // expected-warning{{TRUE}}
+}
+
+// Make sure we terminate a recursive path.
+int recursive() {
+  return recursive();
+}
+int callRecursive() {
+  return recursive();
+}