[llvm] 862e35e - [InstCombine] preserve signbit semantics of NAN with fold to fabs

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 28 07:29:42 PST 2022


Author: Sanjay Patel
Date: 2022-12-28T10:28:23-05:00
New Revision: 862e35e25a68502433da0a8d0819448ff5745339

URL: https://github.com/llvm/llvm-project/commit/862e35e25a68502433da0a8d0819448ff5745339
DIFF: https://github.com/llvm/llvm-project/commit/862e35e25a68502433da0a8d0819448ff5745339.diff

LOG: [InstCombine] preserve signbit semantics of NAN with fold to fabs

As discussed in issue #59279, we want fneg/fabs to conform to the
IEEE-754 spec for signbit operations - quoting from section 5.5.1
of IEEE-754-2008:
"negate(x) copies a floating-point operand x to a destination in
the same format, reversing the sign bit"
"abs(x) copies a floating-point operand x to a destination in the
same format, setting the sign bit to 0 (positive)"
"The operations treat floating-point numbers and NaNs alike."

So we gate this transform with "nnan" in addition to "nsz":
(X > 0.0) ? X : -X --> fabs(X)

Without that restriction, we could have for example:
(+NaN > 0.0) ? +NaN : -NaN --> -NaN
(because an ordered compare with NaN is always false)
That would be different than fabs(+NaN) --> +NaN.

More fabs/fneg patterns demonstrated here:
https://godbolt.org/z/h8ecc659d
(without any FMF, these are correct independently of this patch -
no fabs should be created)

The code change is a one-liner, but we have lots of tests diffs
because there are many variations of the basic pattern.

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

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
    llvm/test/Transforms/InstCombine/fabs.ll
    llvm/test/Transforms/InstCombine/fneg-fabs.ll
    llvm/test/Transforms/InstCombine/fneg.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 6b4e63eb64a33..ddf5882f901df 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -2634,7 +2634,11 @@ static Instruction *foldSelectWithFCmpToFabs(SelectInst &SI,
     // when 'Swap' is true:
     // fold (X > +/-0.0) ? X : -X or (X >= +/-0.0) ? X : -X to fabs(X)
     // fold (X < +/-0.0) ? X : -X or (X <= +/-0.0) ? X : -X to -fabs(X)
-    if (!SI.hasNoSignedZeros())
+    //
+    // Note: We require "nnan" for this fold because fcmp ignores the signbit
+    //       of NAN, but IEEE-754 specifies the signbit of NAN values with
+    //       fneg/fabs operations.
+    if (!SI.hasNoSignedZeros() || !SI.hasNoNaNs())
       return nullptr;
 
     if (Swap)

diff  --git a/llvm/test/Transforms/InstCombine/fabs.ll b/llvm/test/Transforms/InstCombine/fabs.ll
index 06f9b4f07c1b1..06ae2a737997b 100644
--- a/llvm/test/Transforms/InstCombine/fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fabs.ll
@@ -349,10 +349,15 @@ define fp128 @select_fcmp_ogt_zero(fp128 %x) {
   ret fp128 %fabs
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define float @select_nsz_fcmp_ogt_fneg(float %a) {
 ; CHECK-LABEL: @select_nsz_fcmp_ogt_fneg(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz float @llvm.fabs.f32(float [[A:%.*]])
-; CHECK-NEXT:    ret float [[TMP1]]
+; CHECK-NEXT:    [[FNEG:%.*]] = fneg float [[A:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt float [[A]], 0.000000e+00
+; CHECK-NEXT:    [[R:%.*]] = select nsz i1 [[CMP]], float [[A]], float [[FNEG]]
+; CHECK-NEXT:    ret float [[R]]
 ;
   %fneg = fneg float %a
   %cmp = fcmp ogt float %a, %fneg
@@ -445,12 +450,15 @@ define half @select_fcmp_nnan_oge_negzero(half %x) {
   ret half %fabs
 }
 
-; X < 0.0 ? -X : X --> fabs(X)
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
 
 define double @select_nsz_fcmp_olt_zero_unary_fneg(double %x) {
 ; CHECK-LABEL: @select_nsz_fcmp_olt_zero_unary_fneg(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    ret double [[TMP1]]
+; CHECK-NEXT:    [[LTZERO:%.*]] = fcmp olt double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[FABS:%.*]] = select nsz i1 [[LTZERO]], double [[NEGX]], double [[X]]
+; CHECK-NEXT:    ret double [[FABS]]
 ;
   %ltzero = fcmp olt double %x, 0.0
   %negx = fneg double %x
@@ -458,6 +466,8 @@ define double @select_nsz_fcmp_olt_zero_unary_fneg(double %x) {
   ret double %fabs
 }
 
+; X < 0.0 ? -X : X --> fabs(X)
+
 define double @select_nsz_nnan_fcmp_olt_zero_unary_fneg(double %x) {
 ; CHECK-LABEL: @select_nsz_nnan_fcmp_olt_zero_unary_fneg(
 ; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz double @llvm.fabs.f64(double [[X:%.*]])
@@ -743,12 +753,15 @@ define float @select_fcmp_nnan_nsz_ule_negzero_unary_fneg(float %x) {
   ret float %fabs
 }
 
-; X > 0.0 ? X : (-X) --> fabs(X)
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
 
 define <2 x float> @select_nsz_fcmp_ogt_zero_unary_fneg(<2 x float> %x) {
 ; CHECK-LABEL: @select_nsz_fcmp_ogt_zero_unary_fneg(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz <2 x float> @llvm.fabs.v2f32(<2 x float> [[X:%.*]])
-; CHECK-NEXT:    ret <2 x float> [[TMP1]]
+; CHECK-NEXT:    [[GTZERO:%.*]] = fcmp ogt <2 x float> [[X:%.*]], zeroinitializer
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg <2 x float> [[X]]
+; CHECK-NEXT:    [[FABS:%.*]] = select nsz <2 x i1> [[GTZERO]], <2 x float> [[X]], <2 x float> [[NEGX]]
+; CHECK-NEXT:    ret <2 x float> [[FABS]]
 ;
   %gtzero = fcmp ogt <2 x float> %x, zeroinitializer
   %negx = fneg <2 x float> %x
@@ -756,6 +769,8 @@ define <2 x float> @select_nsz_fcmp_ogt_zero_unary_fneg(<2 x float> %x) {
   ret <2 x float> %fabs
 }
 
+; X > 0.0 ? X : (-X) --> fabs(X)
+
 define <2 x float> @select_nsz_nnan_fcmp_ogt_zero_unary_fneg(<2 x float> %x) {
 ; CHECK-LABEL: @select_nsz_nnan_fcmp_ogt_zero_unary_fneg(
 ; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz <2 x float> @llvm.fabs.v2f32(<2 x float> [[X:%.*]])

diff  --git a/llvm/test/Transforms/InstCombine/fneg-fabs.ll b/llvm/test/Transforms/InstCombine/fneg-fabs.ll
index 67edc1d4b7043..fdcdfd123eefa 100644
--- a/llvm/test/Transforms/InstCombine/fneg-fabs.ll
+++ b/llvm/test/Transforms/InstCombine/fneg-fabs.ll
@@ -44,10 +44,14 @@ define double @select_nsz_nnan_nfabs_lt_fmfProp(double %x) {
 
 ; Tests with various predicate types.
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_ult(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_ult(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ult double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[X]], double [[NEGX]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp ult double %x, 0.000000e+00
@@ -68,10 +72,14 @@ define double @select_nsz_nnan_nfabs_ult(double %x) {
   ret double %sel
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_ole(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_ole(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ole double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[X]], double [[NEGX]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp ole double %x, 0.000000e+00
@@ -92,10 +100,14 @@ define double @select_nsz_nnan_nfabs_ole(double %x) {
   ret double %sel
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_ule(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_ule(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ule double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[X]], double [[NEGX]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp ule double %x, 0.000000e+00
@@ -157,11 +169,14 @@ define double @select_nsz_nnan_nfabs_gt_fmfProp(double %x) {
   ret double %sel
 }
 
-; Tests with various predicate types.
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_ogt(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_ogt(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ogt double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[NEGX]], double [[X]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp ogt double %x, 0.000000e+00
@@ -170,6 +185,8 @@ define double @select_nsz_nfabs_ogt(double %x) {
   ret double %sel
 }
 
+; Tests with various predicate types.
+
 define double @select_nsz_nnan_nfabs_ogt(double %x) {
 ; CHECK-LABEL: @select_nsz_nnan_nfabs_ogt(
 ; CHECK-NEXT:    [[TMP1:%.*]] = call nnan nsz double @llvm.fabs.f64(double [[X:%.*]])
@@ -182,10 +199,14 @@ define double @select_nsz_nnan_nfabs_ogt(double %x) {
   ret double %sel
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_ugt(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_ugt(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp ugt double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[NEGX]], double [[X]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp ugt double %x, 0.000000e+00
@@ -206,10 +227,14 @@ define double @select_nsz_nnan_nfabs_ugt(double %x) {
   ret double %sel
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_oge(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_oge(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp oge double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[NEGX]], double [[X]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp oge double %x, 0.000000e+00
@@ -230,10 +255,14 @@ define double @select_nsz_nnan_nfabs_oge(double %x) {
   ret double %sel
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define double @select_nsz_nfabs_uge(double %x) {
 ; CHECK-LABEL: @select_nsz_nfabs_uge(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz double @llvm.fabs.f64(double [[X:%.*]])
-; CHECK-NEXT:    [[SEL:%.*]] = fneg nsz double [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp uge double [[X:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[NEGX:%.*]] = fneg double [[X]]
+; CHECK-NEXT:    [[SEL:%.*]] = select nsz i1 [[CMP]], double [[NEGX]], double [[X]]
 ; CHECK-NEXT:    ret double [[SEL]]
 ;
   %cmp = fcmp uge double %x, 0.000000e+00

diff  --git a/llvm/test/Transforms/InstCombine/fneg.ll b/llvm/test/Transforms/InstCombine/fneg.ll
index 00d8539029a25..24f70770c590a 100644
--- a/llvm/test/Transforms/InstCombine/fneg.ll
+++ b/llvm/test/Transforms/InstCombine/fneg.ll
@@ -742,10 +742,14 @@ define float @fnabs_1(float %a) {
   ret float %fneg1
 }
 
+; This is not fabs because that could produce a 
diff erent signbit for a NAN input.
+; PR59279
+
 define float @fnabs_2_nsz(float %a) {
 ; CHECK-LABEL: @fnabs_2_nsz(
-; CHECK-NEXT:    [[TMP1:%.*]] = call nsz float @llvm.fabs.f32(float [[A:%.*]])
-; CHECK-NEXT:    [[FNEG1:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    [[CMP:%.*]] = fcmp olt float [[A:%.*]], 0.000000e+00
+; CHECK-NEXT:    [[A_NEG:%.*]] = fneg float [[A]]
+; CHECK-NEXT:    [[FNEG1:%.*]] = select nsz i1 [[CMP]], float [[A]], float [[A_NEG]]
 ; CHECK-NEXT:    ret float [[FNEG1]]
 ;
   %fneg = fneg float %a


        


More information about the llvm-commits mailing list