From: Anna Zaks Date: Mon, 10 Sep 2012 22:37:19 +0000 (+0000) Subject: [analyzer] Add ipa-always-inline-size option (with 3 as the default). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7229d0011766c174beffe6a846d78f448f845b39;p=clang [analyzer] Add ipa-always-inline-size option (with 3 as the default). 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 --- diff --git a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h index eb3f8e4271..72e614d3e7 100644 --- a/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h +++ b/include/clang/StaticAnalyzer/Core/AnalyzerOptions.h @@ -18,9 +18,9 @@ #include #include #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 InlineTemplateFunctions; + + // Cache of the "ipa-always-inline-size" setting. + // \sa getAlwaysInlineSize + llvm::Optional 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; diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 29e2783476..5cbbb8d462 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -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 &MutableOption = + const_cast &>(AlwaysInlineSize); + MutableOption = getOptionAsInteger("ipa-always-inline-size", DefaultSize); + } + + return AlwaysInlineSize.getValue(); +} diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 9bf63c5b96..a4e1eb2f4c 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -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(LCtx)) - ++count; + if (const StackFrameContext *SFC = dyn_cast(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 index 0000000000..ef604c2128 --- /dev/null +++ b/test/Analysis/inlining/test-always-inline-size-option.c @@ -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(); +}