summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Transforms/Scalar/TailRecursionElimination.cpp30
-rw-r--r--test/Transforms/TailCallElim/inf-recursion.ll26
2 files changed, 46 insertions, 10 deletions
diff --git a/lib/Transforms/Scalar/TailRecursionElimination.cpp b/lib/Transforms/Scalar/TailRecursionElimination.cpp
index 667e4d9a95..a29047c8af 100644
--- a/lib/Transforms/Scalar/TailRecursionElimination.cpp
+++ b/lib/Transforms/Scalar/TailRecursionElimination.cpp
@@ -59,6 +59,8 @@
#include "llvm/Instructions.h"
#include "llvm/Pass.h"
#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/InlineCost.h"
+#include "llvm/Support/CallSite.h"
#include "llvm/Support/CFG.h"
#include "llvm/ADT/Statistic.h"
using namespace llvm;
@@ -328,15 +330,6 @@ bool TailCallElim::ProcessReturningBlock(ReturnInst *Ret, BasicBlock *&OldEntry,
if (&BB->front() == Ret) // Make sure there is something before the ret...
return false;
- // If the return is in the entry block, then making this transformation would
- // turn infinite recursion into an infinite loop. This transformation is ok
- // in theory, but breaks some code like:
- // double fabs(double f) { return __builtin_fabs(f); } // a 'fabs' call
- // disable this xform in this case, because the code generator will lower the
- // call to fabs into inline code.
- if (BB == &F->getEntryBlock())
- return false;
-
// Scan backwards from the return, checking to see if there is a tail call in
// this block. If so, set CI to it.
CallInst *CI;
@@ -356,6 +349,25 @@ bool TailCallElim::ProcessReturningBlock(ReturnInst *Ret, BasicBlock *&OldEntry,
if (CI->isTailCall() && CannotTailCallElimCallsMarkedTail)
return false;
+ // As a special case, detect code like this:
+ // double fabs(double f) { return __builtin_fabs(f); } // a 'fabs' call
+ // and disable this xform in this case, because the code generator will
+ // lower the call to fabs into inline code.
+ if (BB == &F->getEntryBlock() &&
+ &BB->front() == CI && &*++BB->begin() == Ret &&
+ callIsSmall(F)) {
+ // A single-block function with just a call and a return. Check that
+ // the arguments match.
+ CallSite::arg_iterator I = CallSite(CI).arg_begin(),
+ E = CallSite(CI).arg_end();
+ Function::arg_iterator FI = F->arg_begin(),
+ FE = F->arg_end();
+ for (; I != E && FI != FE; ++I, ++FI)
+ if (*I != &*FI) break;
+ if (I == E && FI == FE)
+ return false;
+ }
+
// If we are introducing accumulator recursion to eliminate associative
// operations after the call instruction, this variable contains the initial
// value for the accumulator. If this value is set, we actually perform
diff --git a/test/Transforms/TailCallElim/inf-recursion.ll b/test/Transforms/TailCallElim/inf-recursion.ll
index a5f246d36c..e4ac9283ae 100644
--- a/test/Transforms/TailCallElim/inf-recursion.ll
+++ b/test/Transforms/TailCallElim/inf-recursion.ll
@@ -1,6 +1,10 @@
-; RUN: opt < %s -tailcallelim -S | grep call
+; RUN: opt < %s -tailcallelim -S | FileCheck %s
+
; Don't turn this into an infinite loop, this is probably the implementation
; of fabs and we expect the codegen to lower fabs.
+; CHECK: @fabs(double %f)
+; CHECK: call
+; CHECK: ret
define double @fabs(double %f) {
entry:
@@ -8,3 +12,23 @@ entry:
ret double %tmp2
}
+; Do turn other calls into infinite loops though.
+
+; CHECK: define double @foo
+; CHECK-NOT: call
+; CHECK: }
+define double @foo(double %f) {
+ %t= call double @foo(double %f)
+ ret double %t
+}
+
+; CHECK: define float @fabsf
+; CHECK-NOT: call
+; CHECK: }
+define float @fabsf(float %f) {
+ %t= call float @fabsf(float 2.0)
+ ret float %t
+}
+
+declare float @fabsf(float %f)
+declare x86_fp80 @fabsl(x86_fp80 %f)