[DAGCombiner] Fold fmin/fmax of NaN
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 9 Sep 2020 20:35:02 +0000 (22:35 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Wed, 9 Sep 2020 21:53:32 +0000 (23:53 +0200)
fminnum(X, NaN) is X, fminimum(X, NaN) is NaN. This mirrors the
behavior of existing InstSimplify folds.

This is expected to improve the reduction lowerings in D87391,
which use NaN as a neutral element.

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

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
llvm/test/CodeGen/ARM/fminmax-folds.ll
llvm/test/CodeGen/X86/fmaxnum.ll
llvm/test/CodeGen/X86/fminnum.ll

index c714358..eaa7044 100644 (file)
@@ -14040,7 +14040,8 @@ SDValue DAGCombiner::visitFNEG(SDNode *N) {
 }
 
 static SDValue visitFMinMax(SelectionDAG &DAG, SDNode *N,
-                            APFloat (*Op)(const APFloat &, const APFloat &)) {
+                            APFloat (*Op)(const APFloat &, const APFloat &),
+                            bool PropagatesNaN) {
   SDValue N0 = N->getOperand(0);
   SDValue N1 = N->getOperand(1);
   EVT VT = N->getValueType(0);
@@ -14058,23 +14059,30 @@ static SDValue visitFMinMax(SelectionDAG &DAG, SDNode *N,
       !isConstantFPBuildVectorOrConstantFP(N1))
     return DAG.getNode(N->getOpcode(), SDLoc(N), VT, N1, N0);
 
+  // minnum(X, nan) -> X
+  // maxnum(X, nan) -> X
+  // minimum(X, nan) -> nan
+  // maximum(X, nan) -> nan
+  if (N1CFP && N1CFP->isNaN())
+    return PropagatesNaN ? N->getOperand(1) : N->getOperand(0);
+
   return SDValue();
 }
 
 SDValue DAGCombiner::visitFMINNUM(SDNode *N) {
-  return visitFMinMax(DAG, N, minnum);
+  return visitFMinMax(DAG, N, minnum, /* PropagatesNaN */ false);
 }
 
 SDValue DAGCombiner::visitFMAXNUM(SDNode *N) {
-  return visitFMinMax(DAG, N, maxnum);
+  return visitFMinMax(DAG, N, maxnum, /* PropagatesNaN */ false);
 }
 
 SDValue DAGCombiner::visitFMINIMUM(SDNode *N) {
-  return visitFMinMax(DAG, N, minimum);
+  return visitFMinMax(DAG, N, minimum, /* PropagatesNaN */ true);
 }
 
 SDValue DAGCombiner::visitFMAXIMUM(SDNode *N) {
-  return visitFMinMax(DAG, N, maximum);
+  return visitFMinMax(DAG, N, maximum, /* PropagatesNaN */ true);
 }
 
 SDValue DAGCombiner::visitFABS(SDNode *N) {
index 807c0a8..35fdcd1 100644 (file)
@@ -9,15 +9,7 @@ declare float @llvm.maximum.f32(float, float)
 define float @test_minnum_const_nan(float %x) {
 ; CHECK-LABEL: test_minnum_const_nan:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    vldr s0, .LCPI0_0
-; CHECK-NEXT:    vmov s2, r0
-; CHECK-NEXT:    vminnm.f32 s0, s2, s0
-; CHECK-NEXT:    vmov r0, s0
 ; CHECK-NEXT:    bx lr
-; CHECK-NEXT:    .p2align 2
-; CHECK-NEXT:  @ %bb.1:
-; CHECK-NEXT:  .LCPI0_0:
-; CHECK-NEXT:    .long 0x7ff80000 @ float NaN
   %r = call float @llvm.minnum.f32(float %x, float 0x7fff000000000000)
   ret float %r
 }
@@ -25,15 +17,7 @@ define float @test_minnum_const_nan(float %x) {
 define float @test_maxnum_const_nan(float %x) {
 ; CHECK-LABEL: test_maxnum_const_nan:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    vldr s0, .LCPI1_0
-; CHECK-NEXT:    vmov s2, r0
-; CHECK-NEXT:    vmaxnm.f32 s0, s2, s0
-; CHECK-NEXT:    vmov r0, s0
 ; CHECK-NEXT:    bx lr
-; CHECK-NEXT:    .p2align 2
-; CHECK-NEXT:  @ %bb.1:
-; CHECK-NEXT:  .LCPI1_0:
-; CHECK-NEXT:    .long 0x7ff80000 @ float NaN
   %r = call float @llvm.maxnum.f32(float %x, float 0x7fff000000000000)
   ret float %r
 }
@@ -41,15 +25,9 @@ define float @test_maxnum_const_nan(float %x) {
 define float @test_maximum_const_nan(float %x) {
 ; CHECK-LABEL: test_maximum_const_nan:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    vldr s0, .LCPI2_0
-; CHECK-NEXT:    vmov s2, r0
-; CHECK-NEXT:    vmax.f32 d0, d1, d0
-; CHECK-NEXT:    vmov r0, s0
+; CHECK-NEXT:    movw r0, #0
+; CHECK-NEXT:    movt r0, #32760
 ; CHECK-NEXT:    bx lr
-; CHECK-NEXT:    .p2align 2
-; CHECK-NEXT:  @ %bb.1:
-; CHECK-NEXT:  .LCPI2_0:
-; CHECK-NEXT:    .long 0x7ff80000 @ float NaN
   %r = call float @llvm.maximum.f32(float %x, float 0x7fff000000000000)
   ret float %r
 }
@@ -57,15 +35,9 @@ define float @test_maximum_const_nan(float %x) {
 define float @test_minimum_const_nan(float %x) {
 ; CHECK-LABEL: test_minimum_const_nan:
 ; CHECK:       @ %bb.0:
-; CHECK-NEXT:    vldr s0, .LCPI3_0
-; CHECK-NEXT:    vmov s2, r0
-; CHECK-NEXT:    vmin.f32 d0, d1, d0
-; CHECK-NEXT:    vmov r0, s0
+; CHECK-NEXT:    movw r0, #0
+; CHECK-NEXT:    movt r0, #32760
 ; CHECK-NEXT:    bx lr
-; CHECK-NEXT:    .p2align 2
-; CHECK-NEXT:  @ %bb.1:
-; CHECK-NEXT:  .LCPI3_0:
-; CHECK-NEXT:    .long 0x7ff80000 @ float NaN
   %r = call float @llvm.minimum.f32(float %x, float 0x7fff000000000000)
   ret float %r
 }
index 41256ba..fd5b638 100644 (file)
@@ -610,35 +610,9 @@ define float @test_maxnum_const_op2(float %x) {
 }
 
 define float @test_maxnum_const_nan(float %x) {
-; SSE-LABEL: test_maxnum_const_nan:
-; SSE:       # %bb.0:
-; SSE-NEXT:    movss {{.*#+}} xmm2 = mem[0],zero,zero,zero
-; SSE-NEXT:    movaps %xmm0, %xmm1
-; SSE-NEXT:    cmpunordss %xmm0, %xmm1
-; SSE-NEXT:    movaps %xmm1, %xmm3
-; SSE-NEXT:    andps %xmm2, %xmm3
-; SSE-NEXT:    maxss %xmm0, %xmm2
-; SSE-NEXT:    andnps %xmm2, %xmm1
-; SSE-NEXT:    orps %xmm3, %xmm1
-; SSE-NEXT:    movaps %xmm1, %xmm0
-; SSE-NEXT:    retq
-;
-; AVX1-LABEL: test_maxnum_const_nan:
-; AVX1:       # %bb.0:
-; AVX1-NEXT:    vmovss {{.*#+}} xmm1 = mem[0],zero,zero,zero
-; AVX1-NEXT:    vmaxss %xmm0, %xmm1, %xmm2
-; AVX1-NEXT:    vcmpunordss %xmm0, %xmm0, %xmm0
-; AVX1-NEXT:    vblendvps %xmm0, %xmm1, %xmm2, %xmm0
-; AVX1-NEXT:    retq
-;
-; AVX512-LABEL: test_maxnum_const_nan:
-; AVX512:       # %bb.0:
-; AVX512-NEXT:    vmovss {{.*#+}} xmm2 = mem[0],zero,zero,zero
-; AVX512-NEXT:    vmaxss %xmm0, %xmm2, %xmm1
-; AVX512-NEXT:    vcmpunordss %xmm0, %xmm0, %k1
-; AVX512-NEXT:    vmovss %xmm2, %xmm1, %xmm1 {%k1}
-; AVX512-NEXT:    vmovaps %xmm1, %xmm0
-; AVX512-NEXT:    retq
+; CHECK-LABEL: test_maxnum_const_nan:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    retq
   %r = call float @llvm.maxnum.f32(float %x, float 0x7fff000000000000)
   ret float %r
 }
index 373920c..dc1b8ca 100644 (file)
@@ -610,35 +610,9 @@ define float @test_minnum_const_op2(float %x) {
 }
 
 define float @test_minnum_const_nan(float %x) {
-; SSE-LABEL: test_minnum_const_nan:
-; SSE:       # %bb.0:
-; SSE-NEXT:    movss {{.*#+}} xmm2 = mem[0],zero,zero,zero
-; SSE-NEXT:    movaps %xmm0, %xmm1
-; SSE-NEXT:    cmpunordss %xmm0, %xmm1
-; SSE-NEXT:    movaps %xmm1, %xmm3
-; SSE-NEXT:    andps %xmm2, %xmm3
-; SSE-NEXT:    minss %xmm0, %xmm2
-; SSE-NEXT:    andnps %xmm2, %xmm1
-; SSE-NEXT:    orps %xmm3, %xmm1
-; SSE-NEXT:    movaps %xmm1, %xmm0
-; SSE-NEXT:    retq
-;
-; AVX1-LABEL: test_minnum_const_nan:
-; AVX1:       # %bb.0:
-; AVX1-NEXT:    vmovss {{.*#+}} xmm1 = mem[0],zero,zero,zero
-; AVX1-NEXT:    vminss %xmm0, %xmm1, %xmm2
-; AVX1-NEXT:    vcmpunordss %xmm0, %xmm0, %xmm0
-; AVX1-NEXT:    vblendvps %xmm0, %xmm1, %xmm2, %xmm0
-; AVX1-NEXT:    retq
-;
-; AVX512-LABEL: test_minnum_const_nan:
-; AVX512:       # %bb.0:
-; AVX512-NEXT:    vmovss {{.*#+}} xmm2 = mem[0],zero,zero,zero
-; AVX512-NEXT:    vminss %xmm0, %xmm2, %xmm1
-; AVX512-NEXT:    vcmpunordss %xmm0, %xmm0, %k1
-; AVX512-NEXT:    vmovss %xmm2, %xmm1, %xmm1 {%k1}
-; AVX512-NEXT:    vmovaps %xmm1, %xmm0
-; AVX512-NEXT:    retq
+; CHECK-LABEL: test_minnum_const_nan:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    retq
   %r = call float @llvm.minnum.f32(float %x, float 0x7fff000000000000)
   ret float %r
 }