[InstCombine] canonicalize fcmp+select to fabs
authorSanjay Patel <spatel@rotateright.com>
Mon, 19 Mar 2018 15:14:30 +0000 (15:14 +0000)
committerSanjay Patel <spatel@rotateright.com>
Mon, 19 Mar 2018 15:14:30 +0000 (15:14 +0000)
This is complicated by -0.0 and nan. This is based on the DAG patterns
as shown in D44091. I'm hoping that we can just remove those DAG folds
and always rely on IR canonicalization to handle the matching to fabs.

We would still need to delete the broken code from DAGCombiner to fix
PR36600:
https://bugs.llvm.org/show_bug.cgi?id=36600

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

llvm-svn: 327858

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
llvm/test/Transforms/InstCombine/fabs.ll

index 1f89ca6..ff682ac 100644 (file)
@@ -1569,7 +1569,37 @@ Instruction *InstCombiner::visitSelectInst(SelectInst &SI) {
 
       // NOTE: if we wanted to, this is where to detect MIN/MAX
     }
-    // NOTE: if we wanted to, this is where to detect ABS
+
+    // Canonicalize select with fcmp to fabs(). -0.0 makes this tricky. We need
+    // fast-math-flags (nsz) or fsub with +0.0 (not fneg) for this to work. We
+    // also require nnan because we do not want to unintentionally change the
+    // sign of a NaN value.
+    Value *X = FCI->getOperand(0);
+    FCmpInst::Predicate Pred = FCI->getPredicate();
+    if (match(FCI->getOperand(1), m_AnyZeroFP()) && FCI->hasNoNaNs()) {
+      // (X <= +/-0.0) ? (0.0 - X) : X --> fabs(X)
+      // (X >  +/-0.0) ? X : (0.0 - X) --> fabs(X)
+      if ((X == FalseVal && match(TrueVal, m_FSub(m_Zero(), m_Specific(X))) &&
+          Pred == FCmpInst::FCMP_OLE) ||
+          (X == TrueVal && match(FalseVal, m_FSub(m_Zero(), m_Specific(X))) &&
+          Pred == FCmpInst::FCMP_OGT)) {
+        Value *Fabs = Builder.CreateIntrinsic(Intrinsic::fabs, { X }, FCI);
+        return replaceInstUsesWith(SI, Fabs);
+      }
+      // With nsz:
+      // (X <  +/-0.0) ? -X : X --> fabs(X)
+      // (X <= +/-0.0) ? -X : X --> fabs(X)
+      // (X >  +/-0.0) ? X : -X --> fabs(X)
+      // (X >= +/-0.0) ? X : -X --> fabs(X)
+      if (FCI->hasNoSignedZeros() &&
+          ((X == FalseVal && match(TrueVal, m_FNeg(m_Specific(X))) &&
+            (Pred == FCmpInst::FCMP_OLT || Pred == FCmpInst::FCMP_OLE)) ||
+           (X == TrueVal && match(FalseVal, m_FNeg(m_Specific(X))) &&
+            (Pred == FCmpInst::FCMP_OGT || Pred == FCmpInst::FCMP_OGE)))) {
+        Value *Fabs = Builder.CreateIntrinsic(Intrinsic::fabs, { X }, FCI);
+        return replaceInstUsesWith(SI, Fabs);
+      }
+    }
   }
 
   // See if we are selecting two values based on a comparison of the two values.
index 023e2a6..87ab0bd 100644 (file)
@@ -246,15 +246,29 @@ define double @multi_use_fabs_fpext(float %x) {
   ret double %fabs
 }
 
-; X <= 0.0 ? (0.0 - X) : X --> fabs(X)
+; Negative test for the fabs folds below: we require nnan, so
+; we won't always clear the sign bit of a NaN value.
 
 define double @select_fcmp_ole_zero(double %x) {
 ; CHECK-LABEL: @select_fcmp_ole_zero(
-; CHECK-NEXT:    [[LEZERO:%.*]] = fcmp nnan ole double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[LEZERO:%.*]] = fcmp ole double [[X:%.*]], 0.000000e+00
 ; CHECK-NEXT:    [[NEGX:%.*]] = fsub double 0.000000e+00, [[X]]
 ; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[LEZERO]], double [[NEGX]], double [[X]]
 ; CHECK-NEXT:    ret double [[FABS]]
 ;
+  %lezero = fcmp ole double %x, 0.0
+  %negx = fsub double 0.0, %x
+  %fabs = select i1 %lezero, double %negx, double %x
+  ret double %fabs
+}
+
+; X <= 0.0 ? (0.0 - X) : X --> fabs(X)
+
+define double @select_fcmp_nnan_ole_zero(double %x) {
+; CHECK-LABEL: @select_fcmp_nnan_ole_zero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    ret double [[TMP1]]
+;
   %lezero = fcmp nnan ole double %x, 0.0
   %negx = fsub double 0.0, %x
   %fabs = select i1 %lezero, double %negx, double %x
@@ -263,12 +277,10 @@ define double @select_fcmp_ole_zero(double %x) {
 
 ; X <= -0.0 ? (0.0 - X) : X --> fabs(X)
 
-define float @select_fcmp_ole_negzero(float %x) {
-; CHECK-LABEL: @select_fcmp_ole_negzero(
-; CHECK-NEXT:    [[LEZERO:%.*]] = fcmp nnan ole float [[X:%.*]], -0.000000e+00
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub float 0.000000e+00, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[LEZERO]], float [[NEGX]], float [[X]]
-; CHECK-NEXT:    ret float [[FABS]]
+define float @select_fcmp_nnan_ole_negzero(float %x) {
+; CHECK-LABEL: @select_fcmp_nnan_ole_negzero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT:    ret float [[TMP1]]
 ;
   %lezero = fcmp nnan ole float %x, -0.0
   %negx = fsub float 0.0, %x
@@ -278,12 +290,10 @@ define float @select_fcmp_ole_negzero(float %x) {
 
 ; X > 0.0 ? X : (0.0 - X) --> fabs(X)
 
-define fp128 @select_fcmp_ogt_zero(fp128 %x) {
-; CHECK-LABEL: @select_fcmp_ogt_zero(
-; CHECK-NEXT:    [[GTZERO:%.*]] = fcmp nnan ogt fp128 [[X:%.*]], 0xL00000000000000000000000000000000
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub fp128 0xL00000000000000000000000000000000, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[GTZERO]], fp128 [[X]], fp128 [[NEGX]]
-; CHECK-NEXT:    ret fp128 [[FABS]]
+define fp128 @select_fcmp_nnan_ogt_zero(fp128 %x) {
+; CHECK-LABEL: @select_fcmp_nnan_ogt_zero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan fp128 @llvm.fabs.f128(fp128 [[X:%.*]])
+; CHECK-NEXT:    ret fp128 [[TMP1]]
 ;
   %gtzero = fcmp nnan ogt fp128 %x, zeroinitializer
   %negx = fsub fp128 zeroinitializer, %x
@@ -293,12 +303,10 @@ define fp128 @select_fcmp_ogt_zero(fp128 %x) {
 
 ; X > -0.0 ? X : (0.0 - X) --> fabs(X)
 
-define half @select_fcmp_ogt_negzero(half %x) {
-; CHECK-LABEL: @select_fcmp_ogt_negzero(
-; CHECK-NEXT:    [[GTZERO:%.*]] = fcmp nnan ogt half [[X:%.*]], 0xH8000
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub half 0xH0000, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[GTZERO]], half [[X]], half [[NEGX]]
-; CHECK-NEXT:    ret half [[FABS]]
+define half @select_fcmp_nnan_ogt_negzero(half %x) {
+; CHECK-LABEL: @select_fcmp_nnan_ogt_negzero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan half @llvm.fabs.f16(half [[X:%.*]])
+; CHECK-NEXT:    ret half [[TMP1]]
 ;
   %gtzero = fcmp nnan ogt half %x, -0.0
   %negx = fsub half 0.0, %x
@@ -308,12 +316,10 @@ define half @select_fcmp_ogt_negzero(half %x) {
 
 ; X < 0.0 ? -X : X --> fabs(X)
 
-define double @select_fcmp_nsz_olt_zero(double %x) {
-; CHECK-LABEL: @select_fcmp_nsz_olt_zero(
-; CHECK-NEXT:    [[LTZERO:%.*]] = fcmp nnan nsz olt double [[X:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub double -0.000000e+00, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[LTZERO]], double [[NEGX]], double [[X]]
-; CHECK-NEXT:    ret double [[FABS]]
+define double @select_fcmp_nnan_nsz_olt_zero(double %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_olt_zero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    ret double [[TMP1]]
 ;
   %ltzero = fcmp nnan nsz olt double %x, 0.0
   %negx = fsub double -0.0, %x
@@ -323,12 +329,10 @@ define double @select_fcmp_nsz_olt_zero(double %x) {
 
 ; X < -0.0 ? -X : X --> fabs(X)
 
-define float @select_fcmp_nsz_olt_negzero(float %x) {
-; CHECK-LABEL: @select_fcmp_nsz_olt_negzero(
-; CHECK-NEXT:    [[LTZERO:%.*]] = fcmp nnan ninf nsz olt float [[X:%.*]], -0.000000e+00
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub float -0.000000e+00, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[LTZERO]], float [[NEGX]], float [[X]]
-; CHECK-NEXT:    ret float [[FABS]]
+define float @select_fcmp_nnan_nsz_olt_negzero(float %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_olt_negzero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan ninf nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT:    ret float [[TMP1]]
 ;
   %ltzero = fcmp nnan nsz ninf olt float %x, -0.0
   %negx = fsub float -0.0, %x
@@ -338,12 +342,10 @@ define float @select_fcmp_nsz_olt_negzero(float %x) {
 
 ; X <= 0.0 ? -X : X --> fabs(X)
 
-define double @select_fcmp_nsz_ole_zero(double %x) {
-; CHECK-LABEL: @select_fcmp_nsz_ole_zero(
-; CHECK-NEXT:    [[LEZERO:%.*]] = fcmp fast ole double [[X:%.*]], 0.000000e+00
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub double -0.000000e+00, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[LEZERO]], double [[NEGX]], double [[X]]
-; CHECK-NEXT:    ret double [[FABS]]
+define double @select_fcmp_nnan_nsz_ole_zero(double %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_ole_zero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call fast double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT:    ret double [[TMP1]]
 ;
   %lezero = fcmp fast ole double %x, 0.0
   %negx = fsub double -0.0, %x
@@ -353,12 +355,10 @@ define double @select_fcmp_nsz_ole_zero(double %x) {
 
 ; X <= -0.0 ? -X : X --> fabs(X)
 
-define float @select_fcmp_nsz_ole_negzero(float %x) {
-; CHECK-LABEL: @select_fcmp_nsz_ole_negzero(
-; CHECK-NEXT:    [[LEZERO:%.*]] = fcmp nnan nsz ole float [[X:%.*]], -0.000000e+00
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub float -0.000000e+00, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[LEZERO]], float [[NEGX]], float [[X]]
-; CHECK-NEXT:    ret float [[FABS]]
+define float @select_fcmp_nnan_nsz_ole_negzero(float %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_ole_negzero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT:    ret float [[TMP1]]
 ;
   %lezero = fcmp nnan nsz ole float %x, -0.0
   %negx = fsub float -0.0, %x
@@ -368,12 +368,10 @@ define float @select_fcmp_nsz_ole_negzero(float %x) {
 
 ; X > 0.0 ? X : (0.0 - X) --> fabs(X)
 
-define <2 x float> @select_fcmp_nsz_ogt_zero(<2 x float> %x) {
-; CHECK-LABEL: @select_fcmp_nsz_ogt_zero(
-; CHECK-NEXT:    [[GTZERO:%.*]] = fcmp nnan nsz arcp ogt <2 x float> [[X:%.*]], zeroinitializer
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub <2 x float> <float -0.000000e+00, float -0.000000e+00>, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select <2 x i1> [[GTZERO]], <2 x float> [[X]], <2 x float> [[NEGX]]
-; CHECK-NEXT:    ret <2 x float> [[FABS]]
+define <2 x float> @select_fcmp_nnan_nsz_ogt_zero(<2 x float> %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_ogt_zero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz arcp <2 x float> @llvm.fabs.v2f32(<2 x float> [[X:%.*]])
+; CHECK-NEXT:    ret <2 x float> [[TMP1]]
 ;
   %gtzero = fcmp nnan nsz arcp ogt <2 x float> %x, zeroinitializer
   %negx = fsub <2 x float> <float -0.0, float -0.0>, %x
@@ -383,12 +381,10 @@ define <2 x float> @select_fcmp_nsz_ogt_zero(<2 x float> %x) {
 
 ; X > -0.0 ? X : (0.0 - X) --> fabs(X)
 
-define half @select_fcmp_nsz_ogt_negzero(half %x) {
-; CHECK-LABEL: @select_fcmp_nsz_ogt_negzero(
-; CHECK-NEXT:    [[GTZERO:%.*]] = fcmp fast ogt half [[X:%.*]], 0xH8000
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub half 0xH0000, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[GTZERO]], half [[X]], half [[NEGX]]
-; CHECK-NEXT:    ret half [[FABS]]
+define half @select_fcmp_nnan_nsz_ogt_negzero(half %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_ogt_negzero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call fast half @llvm.fabs.f16(half [[X:%.*]])
+; CHECK-NEXT:    ret half [[TMP1]]
 ;
   %gtzero = fcmp fast ogt half %x, -0.0
   %negx = fsub half 0.0, %x
@@ -398,12 +394,10 @@ define half @select_fcmp_nsz_ogt_negzero(half %x) {
 
 ; X > 0.0 ? X : (0.0 - X) --> fabs(X)
 
-define <2 x double> @select_fcmp_nsz_oge_zero(<2 x double> %x) {
-; CHECK-LABEL: @select_fcmp_nsz_oge_zero(
-; CHECK-NEXT:    [[GEZERO:%.*]] = fcmp reassoc nnan nsz oge <2 x double> [[X:%.*]], zeroinitializer
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub <2 x double> <double -0.000000e+00, double -0.000000e+00>, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select <2 x i1> [[GEZERO]], <2 x double> [[X]], <2 x double> [[NEGX]]
-; CHECK-NEXT:    ret <2 x double> [[FABS]]
+define <2 x double> @select_fcmp_nnan_nsz_oge_zero(<2 x double> %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_oge_zero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call reassoc nnan nsz <2 x double> @llvm.fabs.v2f64(<2 x double> [[X:%.*]])
+; CHECK-NEXT:    ret <2 x double> [[TMP1]]
 ;
   %gezero = fcmp nnan nsz reassoc oge <2 x double> %x, zeroinitializer
   %negx = fsub <2 x double> <double -0.0, double -0.0>, %x
@@ -413,12 +407,10 @@ define <2 x double> @select_fcmp_nsz_oge_zero(<2 x double> %x) {
 
 ; X > -0.0 ? X : (0.0 - X) --> fabs(X)
 
-define half @select_fcmp_nsz_oge_negzero(half %x) {
-; CHECK-LABEL: @select_fcmp_nsz_oge_negzero(
-; CHECK-NEXT:    [[GEZERO:%.*]] = fcmp nnan nsz oge half [[X:%.*]], 0xH8000
-; CHECK-NEXT:    [[NEGX:%.*]] = fsub half 0xH8000, [[X]]
-; CHECK-NEXT:    [[FABS:%.*]] = select i1 [[GEZERO]], half [[X]], half [[NEGX]]
-; CHECK-NEXT:    ret half [[FABS]]
+define half @select_fcmp_nnan_nsz_oge_negzero(half %x) {
+; CHECK-LABEL: @select_fcmp_nnan_nsz_oge_negzero(
+; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz half @llvm.fabs.f16(half [[X:%.*]])
+; CHECK-NEXT:    ret half [[TMP1]]
 ;
   %gezero = fcmp nnan nsz oge half %x, -0.0
   %negx = fsub half -0.0, %x