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 @@ -90,6 +90,12 @@ cl::desc("Compute the full inline cost of a call site even when the cost " "exceeds the threshold.")); +static cl::opt RecursiveCallerSearchDepth( + "inline-recursion-search-depth", cl::Hidden, cl::init(1), cl::ZeroOrMore, + cl::desc("Check if the caller is recursive, up to the level specified " + "with this flag. Default is 1, which means checking " + "self-recursion only.")); + namespace { class CallAnalyzer : public InstVisitor { @@ -1758,14 +1764,24 @@ return true; Function *Caller = Call.getFunction(); - // Check if the caller function is recursive itself. - for (User *U : Caller->users()) { - CallBase *Call = dyn_cast(U); - if (Call && Call->getFunction() == Caller) { - IsCallerRecursive = true; - break; + std::function RecursionFindingHelper; + RecursionFindingHelper = [&RecursionFindingHelper](Function *A, Function *B, + unsigned Depth) -> bool { + if (Depth == 0) + return false; + for (User *U : A->users()) { + CallBase *Call = dyn_cast(U); + if (!Call) + continue; + Function *CSF = Call->getFunction(); + if (CSF == B || RecursionFindingHelper(CSF, B, Depth - 1)) + return true; } - } + return false; + }; + // Check if the caller function is recursive. + IsCallerRecursive = + RecursionFindingHelper(Caller, Caller, RecursiveCallerSearchDepth); // Populate our simplified values by mapping from function arguments to call // arguments with known important simplifications. diff --git a/llvm/test/Transforms/Inline/recursive.ll b/llvm/test/Transforms/Inline/recursive.ll --- a/llvm/test/Transforms/Inline/recursive.ll +++ b/llvm/test/Transforms/Inline/recursive.ll @@ -3,6 +3,10 @@ ; ; RUN: opt -inline -S < %s | FileCheck %s ; RUN: opt -passes='cgscc(inline)' -S < %s | FileCheck %s +; RUN: opt -inline -S -inline-recursion-search-depth=2 < %s | FileCheck --check-prefix=CHECK-INDIRECT %s +; RUN: opt -passes='cgscc(inline)' -S -inline-recursion-search-depth=2 < %s | FileCheck --check-prefix=CHECK-INDIRECT %s +; RUN: opt -inline -S < %s | FileCheck --check-prefix=CHECK-INDIRECT-NOFLAG %s +; RUN: opt -passes='cgscc(inline)' -S < %s | FileCheck --check-prefix=CHECK-INDIRECT-NOFLAG %s define i32 @large_stack_callee(i32 %param) { ; CHECK-LABEL: define i32 @large_stack_callee( @@ -36,6 +40,43 @@ ret i32 4 } +define i32 @recursive_callee(i32 %v) { +entry: + %c = call i32 @large_stack_indirect_recursive_caller(i32 %v) + %r = sub i32 %c, 1 + ret i32 %r +} + +; Test an indirectly recursive function which calls another function with a +; large stack. If '-inline-recursion-search-depth' is set to greater than 2, +; inliner should refuse to inline the large stack allocation into an indirectly +; recursive frame. + +define i32 @large_stack_indirect_recursive_caller(i32 %param) #0 { +; CHECK-INDIRECT-LABEL: define i32 @large_stack_indirect_recursive_caller( +; CHECK-INDIRECT-NOFLAG-LABEL: define i32 @large_stack_indirect_recursive_caller( +entry: +; CHECK-INDIRECT-NEXT: entry: +; CHECK-INDIRECT-NOFLAG-NEXT: entry: +; CHECK-INDIRECT-NOT: alloca +; CHECK-INDIRECT-NOFLAG: alloca + %t = call i32 @foo(i32 %param) + %cmp = icmp eq i32 %t, -1 + br i1 %cmp, label %exit, label %cont + +cont: + %r = call i32 @recursive_callee(i32 %t) +; CHECK-INDIRECT: call i32 @large_stack_indirect_recursive_caller + %f = call i32 @large_stack_callee(i32 %r) +; CHECK-INDIRECT: call i32 @large_stack_callee +; CHECK-INDIRECT-NOFLAG-NOT: call i32 @large_stack_callee + br label %exit + +exit: + ret i32 4 +} + + declare void @bar(i8* %in) declare i32 @foo(i32 %param) @@ -71,3 +112,5 @@ %n.load = load i32, i32* %castback ret i32 %n.load } + +attributes #0 = {noinline}