diff mbox

PR67218: Fix invalid removal of FP cast

Message ID 87bne4bpjs.fsf@e105548-lin.cambridge.arm.com
State New
Headers show

Commit Message

Richard Sandiford Aug. 18, 2015, 3:10 p.m. UTC
simplify_unary_operation_1 has:

      /*  (float_truncate (float x)) is (float x)  */
      if ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
	 && (flag_unsafe_math_optimizations
	     || (SCALAR_FLOAT_MODE_P (GET_MODE (op))
		 && ((unsigned)significand_size (GET_MODE (op))
		     >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
			 - num_sign_bit_copies (XEXP (op, 0),
						GET_MODE (XEXP (op, 0))))))))
       return simplify_gen_unary (GET_CODE (op), mode,
				  XEXP (op, 0),
				  GET_MODE (XEXP (op, 0)));

where the !flag_unsafe_math_optimizations alternative is trying to check
that the int->float conversion is exact.  Using num_sign_bit_copies is
only correct for signed inputs though; for UNSIGNED_FLOAT we need to
check whether the upper bits are zero.

Also, for both cases we can discount known trailing zeros.  It might not
be a particularly exciting thing to check on its own, but since we're
calling nonzero_bits for UNSIGNED_FLOAT anyway...  It does mean calling
nonzero_bits as well as num_sign_bit_copies for FLOAT, but this is very
rarely executed code.

I noticed this while trying to generalise some of the simplify-rtx.c
patterns so that they applied to vectors as well as scalars.  (Dave's
recent GET_MODE_INNER changes make this easier and cheaper to do.)
The patch does that here too for completeness.  I think any code
in simplify-rtx.c that handles GET_MODE_PRECISION for scalars only
is suspect.

Tested on x86_64-linux-gnu and aarch64-linux-gnu.  OK to install?

This is a gcc 6 regression so no backport is needed.

Thanks,
Richard


gcc/
	PR rtl-optimization/67218
	* simplify-rtx.c (exact_int_to_float_conversion_p): New function.
	(simplify_unary_operation_1): Use it.

gcc/testsuite/
	PR rtl-optimization/67218
	* gcc.c-torture/execute/ieee/pr67218.c,
	gcc.target/aarch64/fcvt_int_float_double1.c,
	gcc.target/aarch64/fcvt_int_float_double2.c,
	gcc.target/aarch64/fcvt_int_float_double3.c,
	gcc.target/aarch64/fcvt_int_float_double4.c,
	gcc.target/aarch64/fcvt_uint_float_double1.c,
	gcc.target/aarch64/fcvt_uint_float_double2.c,
	gcc.target/aarch64/fcvt_uint_float_double3.c,
	gcc.target/aarch64/fcvt_uint_float_double4.c: New tests.

Comments

Jeff Law Aug. 18, 2015, 4:55 p.m. UTC | #1
On 08/18/2015 09:10 AM, Richard Sandiford wrote:
> simplify_unary_operation_1 has:
>
>        /*  (float_truncate (float x)) is (float x)  */
>        if ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
> 	 && (flag_unsafe_math_optimizations
> 	     || (SCALAR_FLOAT_MODE_P (GET_MODE (op))
> 		 && ((unsigned)significand_size (GET_MODE (op))
> 		     >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
> 			 - num_sign_bit_copies (XEXP (op, 0),
> 						GET_MODE (XEXP (op, 0))))))))
>         return simplify_gen_unary (GET_CODE (op), mode,
> 				  XEXP (op, 0),
> 				  GET_MODE (XEXP (op, 0)));
>
> where the !flag_unsafe_math_optimizations alternative is trying to check
> that the int->float conversion is exact.  Using num_sign_bit_copies is
> only correct for signed inputs though; for UNSIGNED_FLOAT we need to
> check whether the upper bits are zero.
>
> Also, for both cases we can discount known trailing zeros.  It might not
> be a particularly exciting thing to check on its own, but since we're
> calling nonzero_bits for UNSIGNED_FLOAT anyway...  It does mean calling
> nonzero_bits as well as num_sign_bit_copies for FLOAT, but this is very
> rarely executed code.
>
> I noticed this while trying to generalise some of the simplify-rtx.c
> patterns so that they applied to vectors as well as scalars.  (Dave's
> recent GET_MODE_INNER changes make this easier and cheaper to do.)
> The patch does that here too for completeness.  I think any code
> in simplify-rtx.c that handles GET_MODE_PRECISION for scalars only
> is suspect.
>
> Tested on x86_64-linux-gnu and aarch64-linux-gnu.  OK to install?
>
> This is a gcc 6 regression so no backport is needed.
>
> Thanks,
> Richard
>
>
> gcc/
> 	PR rtl-optimization/67218
> 	* simplify-rtx.c (exact_int_to_float_conversion_p): New function.
> 	(simplify_unary_operation_1): Use it.
>
> gcc/testsuite/
> 	PR rtl-optimization/67218
> 	* gcc.c-torture/execute/ieee/pr67218.c,
> 	gcc.target/aarch64/fcvt_int_float_double1.c,
> 	gcc.target/aarch64/fcvt_int_float_double2.c,
> 	gcc.target/aarch64/fcvt_int_float_double3.c,
> 	gcc.target/aarch64/fcvt_int_float_double4.c,
> 	gcc.target/aarch64/fcvt_uint_float_double1.c,
> 	gcc.target/aarch64/fcvt_uint_float_double2.c,
> 	gcc.target/aarch64/fcvt_uint_float_double3.c,
> 	gcc.target/aarch64/fcvt_uint_float_double4.c: New tests.
OK.  Thanks!

Jeff
diff mbox

Patch

diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c
index f9352a1..8d86e57 100644
--- a/gcc/simplify-rtx.c
+++ b/gcc/simplify-rtx.c
@@ -829,6 +829,32 @@  simplify_unary_operation (enum rtx_code code, machine_mode mode,
   return simplify_unary_operation_1 (code, mode, op);
 }
 
+/* Return true if FLOAT or UNSIGNED_FLOAT operation OP is known
+   to be exact.  */
+
+static bool
+exact_int_to_float_conversion_p (const_rtx op)
+{
+  int out_bits = significand_size (GET_MODE_INNER (GET_MODE (op)));
+  machine_mode op0_mode = GET_MODE (XEXP (op, 0));
+  /* Constants shouldn't reach here.  */
+  gcc_assert (op0_mode != VOIDmode);
+  int in_prec = GET_MODE_UNIT_PRECISION (op0_mode);
+  int in_bits = in_prec;
+  if (HWI_COMPUTABLE_MODE_P (op0_mode))
+    {
+      unsigned HOST_WIDE_INT nonzero = nonzero_bits (XEXP (op, 0), op0_mode);
+      if (GET_CODE (op) == FLOAT)
+	in_bits -= num_sign_bit_copies (XEXP (op, 0), op0_mode);
+      else if (GET_CODE (op) == UNSIGNED_FLOAT)
+	in_bits = wi::min_precision (wi::uhwi (nonzero, in_prec), UNSIGNED);
+      else
+	gcc_unreachable ();
+      in_bits -= wi::ctz (wi::uhwi (nonzero, in_prec));
+    }
+  return in_bits <= out_bits;
+}
+
 /* Perform some simplifications we can do even if the operands
    aren't constant.  */
 static rtx
@@ -1190,11 +1216,7 @@  simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
       /*  (float_truncate (float x)) is (float x)  */
       if ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
 	  && (flag_unsafe_math_optimizations
-	      || (SCALAR_FLOAT_MODE_P (GET_MODE (op))
-		  && ((unsigned)significand_size (GET_MODE (op))
-		      >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
-			  - num_sign_bit_copies (XEXP (op, 0),
-						 GET_MODE (XEXP (op, 0))))))))
+	      || exact_int_to_float_conversion_p (op)))
 	return simplify_gen_unary (GET_CODE (op), mode,
 				   XEXP (op, 0),
 				   GET_MODE (XEXP (op, 0)));
@@ -1227,11 +1249,7 @@  simplify_unary_operation_1 (enum rtx_code code, machine_mode mode, rtx op)
           */
       if (GET_CODE (op) == FLOAT_EXTEND
 	  || ((GET_CODE (op) == FLOAT || GET_CODE (op) == UNSIGNED_FLOAT)
-	      && SCALAR_FLOAT_MODE_P (GET_MODE (op))
-	      && ((unsigned)significand_size (GET_MODE (op))
-		  >= (GET_MODE_PRECISION (GET_MODE (XEXP (op, 0)))
-		      - num_sign_bit_copies (XEXP (op, 0),
-					     GET_MODE (XEXP (op, 0)))))))
+	      && exact_int_to_float_conversion_p (op)))
 	return simplify_gen_unary (GET_CODE (op), mode,
 				   XEXP (op, 0),
 				   GET_MODE (XEXP (op, 0)));
diff --git a/gcc/testsuite/gcc.c-torture/execute/ieee/pr67218.c b/gcc/testsuite/gcc.c-torture/execute/ieee/pr67218.c
new file mode 100644
index 0000000..2a1260a
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/ieee/pr67218.c
@@ -0,0 +1,15 @@ 
+extern void abort (void) __attribute__ ((noreturn));
+
+double __attribute__ ((noinline, noclone))
+foo (unsigned int x)
+{
+  return (double) (float) (x | 0xffff0000);
+}
+
+int
+main ()
+{
+  if (foo (1) != 0x1.fffep31)
+    abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double1.c b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double1.c
new file mode 100644
index 0000000..e555b2c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double1.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (int x)
+{
+  return (double) (float) (x | (int) 0xff000000);
+}
+
+/* { dg-final { scan-assembler {\tscvtf\td0, w[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double2.c b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double2.c
new file mode 100644
index 0000000..7791b22
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double2.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (int x)
+{
+  return (double) (float) (x | (int) 0xfe000000);
+}
+
+/* { dg-final { scan-assembler {\tscvtf\ts[0-9]*, w[0-9]*} } } */
+/* { dg-final { scan-assembler {\tfcvt\td0, s[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double3.c b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double3.c
new file mode 100644
index 0000000..c647742
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double3.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (int x)
+{
+  return (double) (float) ((x & -16) | (int) 0xf0000000);
+}
+
+/* { dg-final { scan-assembler {\tscvtf\td0, w[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double4.c b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double4.c
new file mode 100644
index 0000000..88a5242
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_int_float_double4.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (int x)
+{
+  return (double) (float) ((x & -16) | (int) 0xfe00000);
+}
+
+/* { dg-final { scan-assembler {\tscvtf\ts[0-9]*, w[0-9]*} } } */
+/* { dg-final { scan-assembler {\tfcvt\td0, s[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double1.c b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double1.c
new file mode 100644
index 0000000..c1c4920
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double1.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (unsigned int x)
+{
+  return (double) (float) (x & 0xffffff);
+}
+
+/* { dg-final { scan-assembler {\t[su]cvtf\td0, w[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double2.c b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double2.c
new file mode 100644
index 0000000..334aae8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double2.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (unsigned int x)
+{
+  return (double) (float) (x & 0x1ffffff);
+}
+
+/* { dg-final { scan-assembler {\t[su]cvtf\ts[0-9]*, w[0-9]*} } } */
+/* { dg-final { scan-assembler {\tfcvt\td0, s[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double3.c b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double3.c
new file mode 100644
index 0000000..deb4578
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double3.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (unsigned int x)
+{
+  return (double) (float) (x & 0xffffff00);
+}
+
+/* { dg-final { scan-assembler {\tucvtf\td0, w[0-9]*} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double4.c b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double4.c
new file mode 100644
index 0000000..8f0955c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fcvt_uint_float_double4.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+double
+foo (unsigned int x)
+{
+  return (double) (float) (x & 0xffffff80);
+}
+
+/* { dg-final { scan-assembler {\tucvtf\ts[0-9]*, w[0-9]*} } } */
+/* { dg-final { scan-assembler {\tfcvt\td0, s[0-9]*} } } */