diff --git a/llvm/include/llvm/Analysis/InlineCost.h b/llvm/include/llvm/Analysis/InlineCost.h --- a/llvm/include/llvm/Analysis/InlineCost.h +++ b/llvm/include/llvm/Analysis/InlineCost.h @@ -58,6 +58,8 @@ const char FunctionInlineCostMultiplierAttributeName[] = "function-inline-cost-multiplier"; + +const char MaxInlineStackSizeAttributeName[] = "inline-max-stacksize"; } // namespace InlineConstants // The cost-benefit pair computed by cost-benefit analysis. diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -161,12 +161,21 @@ cl::desc("Disables evaluation of GetElementPtr with constant operands")); namespace llvm { +Optional getStringFnAttrAsInt(const Attribute &Attr) { + if (Attr.isValid()) { + int AttrValue = 0; + if (!Attr.getValueAsString().getAsInteger(10, AttrValue)) + return AttrValue; + } + return None; +} + Optional getStringFnAttrAsInt(CallBase &CB, StringRef AttrKind) { - Attribute Attr = CB.getFnAttr(AttrKind); - int AttrValue; - if (Attr.getValueAsString().getAsInteger(10, AttrValue)) - return None; - return AttrValue; + return getStringFnAttrAsInt(CB.getFnAttr(AttrKind)); +} + +Optional getStringFnAttrAsInt(Function *F, StringRef AttrKind) { + return getStringFnAttrAsInt(F->getFnAttribute(AttrKind)); } namespace InlineConstants { @@ -2713,7 +2722,13 @@ // If the callee's stack size exceeds the user-specified threshold, // do not let it be inlined. - if (AllocatedSize > StackSizeThreshold) + // The command line option overrides a limit set in the function attributes. + size_t FinalStackSizeThreshold = StackSizeThreshold; + if (!StackSizeThreshold.getNumOccurrences()) + if (Optional AttrMaxStackSize = getStringFnAttrAsInt( + Caller, InlineConstants::MaxInlineStackSizeAttributeName)) + FinalStackSizeThreshold = *AttrMaxStackSize; + if (AllocatedSize > FinalStackSizeThreshold) return InlineResult::failure("stacksize"); return finalizeAnalysis(); diff --git a/llvm/test/Transforms/Inline/inline-stacksize.ll b/llvm/test/Transforms/Inline/inline-stacksize.ll --- a/llvm/test/Transforms/Inline/inline-stacksize.ll +++ b/llvm/test/Transforms/Inline/inline-stacksize.ll @@ -12,22 +12,44 @@ ret i32 %3 } -define i32 @bar() { +define i32 @barNoAttr() { %1 = call i32 @foo() ret i32 %1 -; ALL: define {{.*}}@bar +; ALL: define {{.*}}@barNoAttr ; ALL-NOT: define ; UNLIMITED-NOT: call {{.*}}@foo ; LIMITED: call {{.*}}@foo } ; Check that, under the imposed limit, baz() inlines bar(), but not foo(). -define i32 @baz() { - %1 = call i32 @bar() +define i32 @bazNoAttr() { + %1 = call i32 @barNoAttr() ret i32 %1 ; ALL: define {{.*}}@baz -; UNLIMITED-NOT: call {{.*}}@bar +; UNLIMITED-NOT: call {{.*}}@barNoAttr ; UNLIMITED-NOT: call {{.*}}@foo -; LIMITED-NOT: call {{.*}}@bar +; LIMITED-NOT: call {{.*}}@barNoAttr ; LIMITED: call {{.*}}@foo } + +; Check that the function attribute prevents inlining of foo(). +define i32 @barAttr() #0 { + %1 = call i32 @foo() + ret i32 %1 +; ALL: define {{.*}}@barAttr +; ALL-NOT: define +; ALL: call {{.*}}@foo +} + +; Check that the commandline option overrides the function attribute. +define i32 @bazAttr() #1 { + %1 = call i32 @barAttr() + ret i32 %1 +; ALL: define {{.*}}@bazAttr +; UNLIMITED-NOT: call {{.*}}@barAttr +; UNLIMITED-NOT: call {{.*}}@foo +; LIMITED: call {{.*}}@foo +} + +attributes #0 = { "inline-max-stacksize"="256" } +attributes #1 = { "inline-max-stacksize"="512" }