[InstCombine] fma x, y, 0 -> fmul x, y
authorDavid Green <david.green@arm.com>
Tue, 30 Jun 2020 17:40:04 +0000 (18:40 +0100)
committerDavid Green <david.green@arm.com>
Tue, 30 Jun 2020 18:56:37 +0000 (19:56 +0100)
If the addend of the fma is zero, common sense would suggest that we can
convert fma x, y, 0.0 to fmul x, y. This comes up with some user code
that was expecting the first fma in an unrolled loop to simplify to a
fmul.

Floating point often does not follow naive common sense though. Alive
suggests that this should be guarded by nsz (as fadd -0.0, 0.0 = 0.0).
fma x, y, -0.0 is always valid.

Differential Revision: https://reviews.llvm.org/D82778

llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
llvm/test/Transforms/InstCombine/fma.ll

index 5c576bf..ad06020 100644 (file)
@@ -2379,6 +2379,14 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
       return FAdd;
     }
 
+    // fma x, y, 0 -> fmul x, y
+    // This is always valid for -0.0, but requires nsz for +0.0 as
+    // -0.0 + 0.0 = 0.0, which would not be the same as the fmul on its own.
+    if (match(II->getArgOperand(2), m_NegZeroFP()) ||
+        (match(II->getArgOperand(2), m_PosZeroFP()) &&
+         II->getFastMathFlags().noSignedZeros()))
+      return BinaryOperator::CreateFMulFMF(Src0, Src1, II);
+
     break;
   }
   case Intrinsic::copysign: {
index a619d77..6d9f8ea 100644 (file)
@@ -372,7 +372,7 @@ define float @fma_x_y_0(float %x, float %y) {
 
 define float @fma_x_y_0_nsz(float %x, float %y) {
 ; CHECK-LABEL: @fma_x_y_0_nsz(
-; CHECK-NEXT:    [[FMA:%.*]] = call nsz float @llvm.fma.f32(float [[X:%.*]], float [[Y:%.*]], float 0.000000e+00)
+; CHECK-NEXT:    [[FMA:%.*]] = fmul nsz float [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    ret float [[FMA]]
 ;
   %fma = call nsz float @llvm.fma.f32(float %x, float %y, float 0.0)
@@ -390,7 +390,7 @@ define <8 x half> @fma_x_y_0_v(<8 x half> %x, <8 x half> %y) {
 
 define <8 x half> @fma_x_y_0_nsz_v(<8 x half> %x, <8 x half> %y) {
 ; CHECK-LABEL: @fma_x_y_0_nsz_v(
-; CHECK-NEXT:    [[FMA:%.*]] = call nsz <8 x half> @llvm.fma.v8f16(<8 x half> [[X:%.*]], <8 x half> [[Y:%.*]], <8 x half> zeroinitializer)
+; CHECK-NEXT:    [[FMA:%.*]] = fmul nsz <8 x half> [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    ret <8 x half> [[FMA]]
 ;
   %fma = call nsz <8 x half> @llvm.fma.v8f16(<8 x half> %x, <8 x half> %y, <8 x half> zeroinitializer)
@@ -408,7 +408,7 @@ define float @fmuladd_x_y_0(float %x, float %y) {
 
 define float @fmuladd_x_y_0_nsz(float %x, float %y) {
 ; CHECK-LABEL: @fmuladd_x_y_0_nsz(
-; CHECK-NEXT:    [[FMA:%.*]] = call nsz float @llvm.fmuladd.f32(float [[X:%.*]], float [[Y:%.*]], float 0.000000e+00)
+; CHECK-NEXT:    [[FMA:%.*]] = fmul nsz float [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    ret float [[FMA]]
 ;
   %fma = call nsz float @llvm.fmuladd.f32(float %x, float %y, float 0.0)
@@ -417,7 +417,7 @@ define float @fmuladd_x_y_0_nsz(float %x, float %y) {
 
 define float @fma_x_y_m0(float %x, float %y) {
 ; CHECK-LABEL: @fma_x_y_m0(
-; CHECK-NEXT:    [[FMA:%.*]] = call float @llvm.fma.f32(float [[X:%.*]], float [[Y:%.*]], float -0.000000e+00)
+; CHECK-NEXT:    [[FMA:%.*]] = fmul float [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    ret float [[FMA]]
 ;
   %fma = call float @llvm.fma.f32(float %x, float %y, float -0.0)
@@ -426,7 +426,7 @@ define float @fma_x_y_m0(float %x, float %y) {
 
 define <8 x half> @fma_x_y_m0_v(<8 x half> %x, <8 x half> %y) {
 ; CHECK-LABEL: @fma_x_y_m0_v(
-; CHECK-NEXT:    [[FMA:%.*]] = call <8 x half> @llvm.fma.v8f16(<8 x half> [[X:%.*]], <8 x half> [[Y:%.*]], <8 x half> <half 0xH8000, half 0xH8000, half 0xH8000, half 0xH8000, half 0xH8000, half 0xH8000, half 0xH8000, half 0xH8000>)
+; CHECK-NEXT:    [[FMA:%.*]] = fmul <8 x half> [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    ret <8 x half> [[FMA]]
 ;
   %fma = call <8 x half> @llvm.fma.v8f16(<8 x half> %x, <8 x half> %y, <8 x half> <half -0.0, half -0.0, half -0.0, half -0.0, half -0.0, half -0.0, half -0.0, half -0.0>)
@@ -435,7 +435,7 @@ define <8 x half> @fma_x_y_m0_v(<8 x half> %x, <8 x half> %y) {
 
 define float @fmuladd_x_y_m0(float %x, float %y) {
 ; CHECK-LABEL: @fmuladd_x_y_m0(
-; CHECK-NEXT:    [[FMA:%.*]] = call float @llvm.fmuladd.f32(float [[X:%.*]], float [[Y:%.*]], float -0.000000e+00)
+; CHECK-NEXT:    [[FMA:%.*]] = fmul float [[X:%.*]], [[Y:%.*]]
 ; CHECK-NEXT:    ret float [[FMA]]
 ;
   %fma = call float @llvm.fmuladd.f32(float %x, float %y, float -0.0)