[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