diff mbox

[PATCHv5,PR,57371] Remove useless floating point casts in comparisons

Message ID b02a8e22-fb7d-5306-c431-59054280019a@gmail.com
State New
Headers show

Commit Message

Yury Gribov July 30, 2017, 7:25 a.m. UTC
Hi all,

This is an updated version of patch in 
https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00924.html . It prevents 
optimization in presense of sNaNs (and qNaNs when comparison operator is 
 > >= < <=) to preserve FP exceptions.

I've fixed some stylistic issues mentioned in previous review.

Rebased, bootstrapped and regtested on x64. Ok for trunk?

-Y
2017-07-30  Yury Gribov  <tetra2005@gmail.com>

	PR tree-optimization/57371

gcc/
	* match.pd: New pattern.

gcc/testsuite/
	* testsuite/c-c++-common/pr57371-1.c: New test.
	* testsuite/c-c++-common/pr57371-2.c: New test.
	* testsuite/c-c++-common/pr57371-3.c: New test.
	* testsuite/c-c++-common/pr57371-4.c: New test.
	* testsuite/gcc.dg/pr57371-5.c: New test.

Comments

Richard Biener Aug. 4, 2017, 9:10 a.m. UTC | #1
On Sun, Jul 30, 2017 at 9:25 AM, Yury Gribov
<tetra2005.patches@gmail.com> wrote:
> Hi all,
>
> This is an updated version of patch in
> https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00924.html . It prevents
> optimization in presense of sNaNs (and qNaNs when comparison operator is >
>>= < <=) to preserve FP exceptions.
>
> I've fixed some stylistic issues mentioned in previous review.
>
> Rebased, bootstrapped and regtested on x64. Ok for trunk?

Ok.

Thanks,
Richard.

> -Y
diff mbox

Patch

diff -rupN gcc/gcc/match.pd gcc-57371/gcc/match.pd
--- gcc/gcc/match.pd	2017-07-18 22:21:16.000000000 +0200
+++ gcc-57371/gcc/match.pd	2017-07-28 05:00:05.000000000 +0200
@@ -2805,6 +2805,80 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       (if (! HONOR_NANS (@0))
 	(cmp @0 @1))))))
 
+/* Optimize various special cases of (FTYPE) N CMP CST.  */
+(for cmp  (lt le eq ne ge gt)
+     icmp (le le eq ne ge ge)
+ (simplify
+  (cmp (float @0) REAL_CST@1)
+   (if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (@1))
+	&& ! DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@1)))
+    (with
+     {
+       tree itype = TREE_TYPE (@0);
+       signop isign = TYPE_SIGN (itype);
+       format_helper fmt (REAL_MODE_FORMAT (TYPE_MODE (TREE_TYPE (@1))));
+       const REAL_VALUE_TYPE *cst = TREE_REAL_CST_PTR (@1);
+       /* Be careful to preserve any potential exceptions due to
+	  NaNs.  qNaNs are ok in == or != context.
+	  TODO: relax under -fno-trapping-math or
+	  -fno-signaling-nans.  */
+       bool exception_p
+         = real_isnan (cst) && (cst->signalling
+				|| (cmp != EQ_EXPR || cmp != NE_EXPR));
+       /* INT?_MIN is power-of-two so it takes
+	  only one mantissa bit.  */
+       bool signed_p = isign == SIGNED;
+       bool itype_fits_ftype_p
+	 = TYPE_PRECISION (itype) - signed_p <= significand_size (fmt);
+     }
+     /* TODO: allow non-fitting itype and SNaNs when
+	-fno-trapping-math.  */
+     (if (itype_fits_ftype_p && ! exception_p)
+      (with
+       {
+	 REAL_VALUE_TYPE imin, imax;
+	 real_from_integer (&imin, fmt, wi::min_value (itype), isign);
+	 real_from_integer (&imax, fmt, wi::max_value (itype), isign);
+
+	 REAL_VALUE_TYPE icst;
+	 if (cmp == GT_EXPR || cmp == GE_EXPR)
+	   real_ceil (&icst, fmt, cst);
+	 else if (cmp == LT_EXPR || cmp == LE_EXPR)
+	   real_floor (&icst, fmt, cst);
+	 else
+	   real_trunc (&icst, fmt, cst);
+
+	 bool cst_int_p = real_identical (&icst, cst);
+
+	 bool overflow_p = false;
+	 wide_int icst_val
+	   = real_to_integer (&icst, &overflow_p, TYPE_PRECISION (itype));
+       }
+       (switch
+	/* Optimize cases when CST is outside of ITYPE's range.  */
+	(if (real_compare (LT_EXPR, cst, &imin))
+	 { constant_boolean_node (cmp == GT_EXPR || cmp == GE_EXPR || cmp == NE_EXPR,
+				  type); })
+	(if (real_compare (GT_EXPR, cst, &imax))
+	 { constant_boolean_node (cmp == LT_EXPR || cmp == LE_EXPR || cmp == NE_EXPR,
+				  type); })
+	/* Remove cast if CST is an integer representable by ITYPE.  */
+	(if (cst_int_p)
+	 (cmp @0 { gcc_assert (!overflow_p);
+		   wide_int_to_tree (itype, icst_val); })
+	)
+	/* When CST is fractional, optimize
+	    (FTYPE) N == CST -> 0
+	    (FTYPE) N != CST -> 1.  */
+	(if (cmp == EQ_EXPR || cmp == NE_EXPR)
+	 { constant_boolean_node (cmp == NE_EXPR, type); }) 
+	/* Otherwise replace with sensible integer constant.  */
+	(with
+	 {
+	   gcc_checking_assert (!overflow_p);
+	 }
+	 (icmp @0 { wide_int_to_tree (itype, icst_val); })))))))))
+
 /* Fold A /[ex] B CMP C to A CMP B * C.  */
 (for cmp (eq ne)
  (simplify
diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-1.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-1.c
--- gcc/gcc/testsuite/c-c++-common/pr57371-1.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-1.c	2017-07-28 05:00:05.000000000 +0200
@@ -0,0 +1,341 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-original" } */
+
+#include <limits.h>
+
+/* Original testcase from PR.  */
+
+int foo1 (short x) {
+  return (double) x != 0;
+  /* { dg-final { scan-tree-dump "return (<retval> = )?x != 0" "original" } } */
+}
+
+int foo2 (short x) {
+  return (float) x != 0;
+  /* { dg-final { scan-tree-dump "return (<retval> = )?x != 0" "original" } } */
+}
+
+int foo3 (int x) {
+  return (double) x != 0;
+  /* { dg-final { scan-tree-dump "return (<retval> = )?x != 0" "original" } } */
+}
+
+/* Tests when RHS is within range of integer type.  */
+
+void in_range (unsigned short x)
+{
+  {
+    volatile int in_range_1;
+    in_range_1 = (float) x > 100.0f;
+    /* { dg-final { scan-tree-dump "in_range_1 = x > 100" "original" } } */
+  }
+
+  {
+    volatile int in_range_2;
+    in_range_2 = (float) x < 100.0f;
+    /* { dg-final { scan-tree-dump "in_range_2 = x <= 99" "original" } } */
+  }
+
+  {
+    volatile int in_range_3;
+    in_range_3 = (float) x > 100.5f;
+    /* { dg-final { scan-tree-dump "in_range_3 = x (>= 101|> 100)" "original" } } */
+  }
+
+  {
+    volatile int in_range_4;
+    in_range_4 = (float) x < 100.5f;
+    /* { dg-final { scan-tree-dump "in_range_4 = x <= 100" "original" } } */
+  }
+
+  {
+    volatile int in_range_5;
+    in_range_5 = (float) x == 100.0f;
+    /* { dg-final { scan-tree-dump "in_range_5 = x == 100" "original" } } */
+  }
+
+  {
+    volatile int in_range_6;
+    in_range_6 = (float) x != 100.0f;
+    /* { dg-final { scan-tree-dump "in_range_6 = x != 100" "original" } } */
+  }
+
+  {
+    volatile int in_range_7;
+    in_range_7 = (float) x == 100.5f;
+    /* { dg-final { scan-tree-dump "in_range_7 = 0" "original" } } */
+  }
+
+  {
+    volatile int in_range_8;
+    in_range_8 = (float) x != 100.5f;
+    /* { dg-final { scan-tree-dump "in_range_8 = 1" "original" } } */
+  }
+}
+
+/* Tests for cases where RHS is out of range of integer type.  */
+
+void out_range (unsigned short x)
+{
+  {
+    volatile int out_range_1;
+    out_range_1 = (float) x > -100.5f;
+    /* { dg-final { scan-tree-dump "out_range_1 = 1" "original" } } */
+  }
+
+  {
+    volatile int out_range_2;
+    out_range_2 = (float) x >= -100.5f;
+    /* { dg-final { scan-tree-dump "out_range_2 = 1" "original" } } */
+  }
+
+  {
+    volatile int out_range_3;
+    out_range_3 = (float) x < -100.5f;
+    /* { dg-final { scan-tree-dump "out_range_3 = 0" "original" } } */
+  }
+
+  {
+    volatile int out_range_4;
+    out_range_4 = (float) x <= -100.5f;
+    /* { dg-final { scan-tree-dump "out_range_4 = 0" "original" } } */
+  }
+
+  {
+    volatile int out_range_5;
+    out_range_5 = (float) x == -100.5f;
+    /* { dg-final { scan-tree-dump "out_range_5 = 0" "original" } } */
+  }
+
+  {
+    volatile int out_range_6;
+    out_range_6 = (float) x != -100.5f;
+    /* { dg-final { scan-tree-dump "out_range_6 = 1" "original" } } */
+  }
+}
+
+/* Tests when RHS is at boundary of integer type.  */
+
+void lo_bounds (unsigned short x)
+{
+  {
+    volatile int lo_bounds_1;
+    lo_bounds_1 = (float) x > 0x0;
+    /* { dg-final { scan-tree-dump "lo_bounds_1 = x (>|!=) 0" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_2;
+    lo_bounds_2 = (float) x >= 0x0;
+    /* { dg-final { scan-tree-dump "lo_bounds_2 = 1" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_3;
+    lo_bounds_3 = (float) x < 0x0;
+    /* { dg-final { scan-tree-dump "lo_bounds_3 = 0" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_4;
+    lo_bounds_4 = (float) x <= 0x0;
+    /* { dg-final { scan-tree-dump "lo_bounds_4 = x (<=|==) 0" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_5;
+    lo_bounds_5 = (float) x > 0x0 - 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_5 = 1" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_6;
+    lo_bounds_6 = (float) x >= 0x0 - 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_6 = 1" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_7;
+    lo_bounds_7 = (float) x < 0x0 - 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_7 = 0" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_8;
+    lo_bounds_8 = (float) x <= 0x0 - 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_8 = 0" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_9;
+    lo_bounds_9 = (float) x > 0x0 + 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_9 = x (>= 1|!= 0)" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_10;
+    lo_bounds_10 = (float) x >= 0x0 + 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_10 = x (>= 1|!= 0)" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_11;
+    lo_bounds_11 = (float) x < 0x0 + 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_11 = x (<=|==) 0" "original" } } */
+  }
+
+  {
+    volatile int lo_bounds_12;
+    lo_bounds_12 = (float) x <= 0x0 + 0.5f;
+    /* { dg-final { scan-tree-dump "lo_bounds_12 = x (<=|==) 0" "original" } } */
+  }
+}
+
+void hi_bounds (unsigned short x)
+{
+  {
+    volatile int hi_bounds_1;
+    hi_bounds_1 = (float) x > USHRT_MAX;
+    /* { dg-final { scan-tree-dump "hi_bounds_1 = 0" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_2;
+    hi_bounds_2 = (float) x >= USHRT_MAX;
+    /* { dg-final { scan-tree-dump "hi_bounds_2 = x (>=|==) 65535" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_3;
+    hi_bounds_3 = (float) x < USHRT_MAX;
+    /* { dg-final { scan-tree-dump "hi_bounds_3 = x (<|!=) 65535" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_4;
+    hi_bounds_4 = (float) x <= USHRT_MAX;
+    /* { dg-final { scan-tree-dump "hi_bounds_4 = 1" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_5;
+    hi_bounds_5 = (float) x > USHRT_MAX - 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_5 = x (>=|==) 65535" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_6;
+    hi_bounds_6 = (float) x >= USHRT_MAX - 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_6 = x (>=|==) 65535" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_7;
+    hi_bounds_7 = (float) x < USHRT_MAX - 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_7 = x (<= 65534|!= 65535)" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_8;
+    hi_bounds_8 = (float) x <= USHRT_MAX - 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_8 = x (<= 65534|!= 65535)" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_9;
+    hi_bounds_9 = (float) x > USHRT_MAX + 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_9 = 0" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_10;
+    hi_bounds_10 = (float) x >= USHRT_MAX + 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_10 = 0" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_11;
+    hi_bounds_11 = (float) x < USHRT_MAX + 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_11 = 1" "original" } } */
+  }
+
+  {
+    volatile int hi_bounds_12;
+    hi_bounds_12 = (float) x <= USHRT_MAX + 0.5f;
+    /* { dg-final { scan-tree-dump "hi_bounds_12 = 1" "original" } } */
+  }
+}
+
+/* Tests with non-finite float consts.  */
+
+void nonfinite (unsigned short x)
+{
+#define INFINITY __builtin_inff ()
+
+  {
+    volatile int nonfinite_1;
+    nonfinite_1 = (float) x > INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_1 = 0" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_2;
+    nonfinite_2 = (float) x >= INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_2 = 0" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_3;
+    nonfinite_3 = (float) x < INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_3 = 1" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_4;
+    nonfinite_4 = (float) x <= INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_4 = 1" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_5;
+    nonfinite_5 = (float) x > -INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_5 = 1" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_6;
+    nonfinite_6 = (float) x >= -INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_6 = 1" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_7;
+    nonfinite_7 = (float) x < -INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_7 = 0" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_8;
+    nonfinite_8 = (float) x <= -INFINITY;
+    /* { dg-final { scan-tree-dump "nonfinite_8 = 0" "original" } } */
+  }
+
+#define QNAN __builtin_nanf ("0")
+
+  /* Even for qNaNs, only == and != are quiet.  */
+
+  {
+    volatile int nonfinite_9;
+    nonfinite_9 = (float) x == QNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_9 = 0" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_10;
+    nonfinite_10 = (float) x != QNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_10 = 1" "original" } } */
+  }
+}
+
+/* { dg-final { scan-tree-dump-not "\\(float\\)" "original" } } */
+/* { dg-final { scan-tree-dump-not "\\(double\\)" "original" } } */
diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-2.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-2.c
--- gcc/gcc/testsuite/c-c++-common/pr57371-2.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-2.c	2017-07-28 05:00:05.000000000 +0200
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-optimized" } */
+
+/* We can not get rid of comparison in tests below because of
+   potential inexact exception.
+
+   TODO: enable when -fno-trapping-math.  */
+
+int foo1(int x) {
+  return (float) x != 0;
+  /* { dg-final { scan-tree-dump "\\(float\\)" "optimized" } } */
+}
+
+int foo2(long long x) {
+  /* { dg-final { scan-tree-dump "\\(double\\)" "optimized" } } */
+  return (double) x != 0;
+}
diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-3.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-3.c
--- gcc/gcc/testsuite/c-c++-common/pr57371-3.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-3.c	2017-07-28 05:00:05.000000000 +0200
@@ -0,0 +1,13 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-optimized" } */
+/* { dg-require-effective-target int128 } */
+
+/* We can not get rid of comparison in tests below because of
+   potential overflow exception.
+
+   TODO: enable when -fno-trapping-math.  */
+
+int foo(__int128_t x) {
+  /* { dg-final { scan-tree-dump "\\(float\\)" "optimized" } } */
+  return (float) x != 0;
+}
diff -rupN gcc/gcc/testsuite/c-c++-common/pr57371-4.c gcc-57371/gcc/testsuite/c-c++-common/pr57371-4.c
--- gcc/gcc/testsuite/c-c++-common/pr57371-4.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-57371/gcc/testsuite/c-c++-common/pr57371-4.c	2017-07-28 05:00:05.000000000 +0200
@@ -0,0 +1,72 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O -fsignaling-nans -fdump-tree-original" } */
+
+/* We can not get rid of comparison in tests below because of
+   pending NaN exceptions.
+
+   TODO: avoid under -fno-trapping-math.  */
+
+#define QNAN __builtin_nanf ("0")
+#define SNAN __builtin_nansf ("0")
+
+void nonfinite(unsigned short x) {
+  {
+    volatile int nonfinite_1;
+    nonfinite_1 = (float) x > QNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_1 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_2;
+    nonfinite_2 = (float) x >= QNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_2 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_3;
+    nonfinite_3 = (float) x < QNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_3 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_4;
+    nonfinite_4 = (float) x <= QNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_4 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_5;
+    nonfinite_5 = (float) x > SNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_5 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_6;
+    nonfinite_6 = (float) x >= SNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_6 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_7;
+    nonfinite_7 = (float) x < SNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_7 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_8;
+    nonfinite_8 = (float) x <= SNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_8 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_9;
+    nonfinite_9 = (float) x == SNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_9 = \\(float\\)" "original" } } */
+  }
+
+  {
+    volatile int nonfinite_10;
+    nonfinite_10 = (float) x != SNAN;
+    /* { dg-final { scan-tree-dump "nonfinite_10 = \\(float\\)" "original" } } */
+  }
+}
diff -rupN gcc/gcc/testsuite/gcc.dg/pr57371-5.c gcc-57371/gcc/testsuite/gcc.dg/pr57371-5.c
--- gcc/gcc/testsuite/gcc.dg/pr57371-5.c	1970-01-01 01:00:00.000000000 +0100
+++ gcc-57371/gcc/testsuite/gcc.dg/pr57371-5.c	2017-07-28 05:00:05.000000000 +0200
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-optimized" } */
+/* { dg-require-effective-target dfp } */
+
+/* We do not support DFPs.  */
+
+int foo(short x) {
+  /* { dg-final { scan-tree-dump "\\(_Decimal32\\)" "optimized" } } */
+  return (_Decimal32) x != 0;
+}