Fold fptrunc(add (fpextend x), (fpextend y)) -> add(x,y), as GCC does.
authorChris Lattner <sabre@nondot.org>
Sun, 27 Jan 2008 05:29:54 +0000 (05:29 +0000)
committerChris Lattner <sabre@nondot.org>
Sun, 27 Jan 2008 05:29:54 +0000 (05:29 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@46406 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Transforms/Scalar/InstructionCombining.cpp
test/Transforms/InstCombine/fpextend.ll [new file with mode: 0644]

index d17035c161ee59b3505cee059a1dca58ee254c04..49f1c45f395beceb01b55a8cf3be39069783aa92 100644 (file)
@@ -203,7 +203,7 @@ namespace {
     Instruction *visitTrunc(TruncInst &CI);
     Instruction *visitZExt(ZExtInst &CI);
     Instruction *visitSExt(SExtInst &CI);
-    Instruction *visitFPTrunc(CastInst &CI);
+    Instruction *visitFPTrunc(FPTruncInst &CI);
     Instruction *visitFPExt(CastInst &CI);
     Instruction *visitFPToUI(CastInst &CI);
     Instruction *visitFPToSI(CastInst &CI);
@@ -7141,8 +7141,80 @@ Instruction *InstCombiner::visitSExt(SExtInst &CI) {
   return 0;
 }
 
-Instruction *InstCombiner::visitFPTrunc(CastInst &CI) {
-  return commonCastTransforms(CI);
+/// FitsInFPType - Return a Constant* for the specified FP constant if it fits
+/// in the specified FP type without changing its value.
+static Constant *FitsInFPType(ConstantFP *CFP, const Type *FPTy, 
+                              const fltSemantics &Sem) {
+  APFloat F = CFP->getValueAPF();
+  if (F.convert(Sem, APFloat::rmNearestTiesToEven) == APFloat::opOK)
+    return ConstantFP::get(FPTy, F);
+  return 0;
+}
+
+/// LookThroughFPExtensions - If this is an fp extension instruction, look
+/// through it until we get the source value.
+static Value *LookThroughFPExtensions(Value *V) {
+  if (Instruction *I = dyn_cast<Instruction>(V))
+    if (I->getOpcode() == Instruction::FPExt)
+      return LookThroughFPExtensions(I->getOperand(0));
+  
+  // If this value is a constant, return the constant in the smallest FP type
+  // that can accurately represent it.  This allows us to turn
+  // (float)((double)X+2.0) into x+2.0f.
+  if (ConstantFP *CFP = dyn_cast<ConstantFP>(V)) {
+    if (CFP->getType() == Type::PPC_FP128Ty)
+      return V;  // No constant folding of this.
+    // See if the value can be truncated to float and then reextended.
+    if (Value *V = FitsInFPType(CFP, Type::FloatTy, APFloat::IEEEsingle))
+      return V;
+    if (CFP->getType() == Type::DoubleTy)
+      return V;  // Won't shrink.
+    if (Value *V = FitsInFPType(CFP, Type::DoubleTy, APFloat::IEEEdouble))
+      return V;
+    // Don't try to shrink to various long double types.
+  }
+  
+  return V;
+}
+
+Instruction *InstCombiner::visitFPTrunc(FPTruncInst &CI) {
+  if (Instruction *I = commonCastTransforms(CI))
+    return I;
+  
+  // If we have fptrunc(add (fpextend x), (fpextend y)), where x and y are
+  // smaller than the destination type, we can eliminate the truncate by doing
+  // the add as the smaller type.  This applies to add/sub/mul/div as well as
+  // many builtins (sqrt, etc).
+  BinaryOperator *OpI = dyn_cast<BinaryOperator>(CI.getOperand(0));
+  if (OpI && OpI->hasOneUse()) {
+    switch (OpI->getOpcode()) {
+    default: break;
+    case Instruction::Add:
+    case Instruction::Sub:
+    case Instruction::Mul:
+    case Instruction::FDiv:
+    case Instruction::FRem:
+      const Type *SrcTy = OpI->getType();
+      Value *LHSTrunc = LookThroughFPExtensions(OpI->getOperand(0));
+      Value *RHSTrunc = LookThroughFPExtensions(OpI->getOperand(1));
+      if (LHSTrunc->getType() != SrcTy && 
+          RHSTrunc->getType() != SrcTy) {
+        unsigned DstSize = CI.getType()->getPrimitiveSizeInBits();
+        // If the source types were both smaller than the destination type of
+        // the cast, do this xform.
+        if (LHSTrunc->getType()->getPrimitiveSizeInBits() <= DstSize &&
+            RHSTrunc->getType()->getPrimitiveSizeInBits() <= DstSize) {
+          LHSTrunc = InsertCastBefore(Instruction::FPExt, LHSTrunc,
+                                      CI.getType(), CI);
+          RHSTrunc = InsertCastBefore(Instruction::FPExt, RHSTrunc,
+                                      CI.getType(), CI);
+          return BinaryOperator::create(OpI->getOpcode(), LHSTrunc, RHSTrunc);
+        }
+      }
+      break;  
+    }
+  }
+  return 0;
 }
 
 Instruction *InstCombiner::visitFPExt(CastInst &CI) {
diff --git a/test/Transforms/InstCombine/fpextend.ll b/test/Transforms/InstCombine/fpextend.ll
new file mode 100644 (file)
index 0000000..5971080
--- /dev/null
@@ -0,0 +1,36 @@
+; RUN: llvm-as < %s | opt -instcombine | llvm-dis | not grep fpext
+@X = external global float 
+@Y = external global float
+
+define void @test() nounwind  {
+entry:
+       %tmp = load float* @X, align 4          ; <float> [#uses=1]
+       %tmp1 = fpext float %tmp to double              ; <double> [#uses=1]
+       %tmp3 = add double %tmp1, 0.000000e+00          ; <double> [#uses=1]
+       %tmp34 = fptrunc double %tmp3 to float          ; <float> [#uses=1]
+       store float %tmp34, float* @X, align 4
+       ret void
+}
+
+define void @test3() nounwind  {
+entry:
+       %tmp = load float* @X, align 4          ; <float> [#uses=1]
+       %tmp1 = fpext float %tmp to double              ; <double> [#uses=1]
+       %tmp2 = load float* @Y, align 4         ; <float> [#uses=1]
+       %tmp23 = fpext float %tmp2 to double            ; <double> [#uses=1]
+       %tmp5 = fdiv double %tmp1, %tmp23               ; <double> [#uses=1]
+       %tmp56 = fptrunc double %tmp5 to float          ; <float> [#uses=1]
+       store float %tmp56, float* @X, align 4
+       ret void
+}
+
+define void @test4() nounwind  {
+entry:
+       %tmp = load float* @X, align 4          ; <float> [#uses=1]
+       %tmp1 = fpext float %tmp to double              ; <double> [#uses=1]
+       %tmp2 = sub double -0.000000e+00, %tmp1         ; <double> [#uses=1]
+       %tmp34 = fptrunc double %tmp2 to float          ; <float> [#uses=1]
+       store float %tmp34, float* @X, align 4
+       ret void
+}
+