Fix convert_to_real bugs (PR 36578 etc.)

Joseph S. Myers joseph@codesourcery.com
Wed Oct 29 19:10:00 GMT 2008


On Wed, 29 Oct 2008, Richard Guenther wrote:

> this conditional should be better split out into a predicate in
> real.[ch]?  The patch
> is ok with such a change.

This is the version I have committed.

Index: real.c
===================================================================
--- real.c	(revision 141431)
+++ real.c	(working copy)
@@ -1266,6 +1266,35 @@
   *r = u;
   return true;
 }
+
+/* Return true if arithmetic on values in IMODE that were promoted
+   from values in TMODE is equivalent to direct arithmetic on values
+   in TMODE.  */
+
+bool
+real_can_shorten_arithmetic (enum machine_mode imode, enum machine_mode tmode)
+{
+  const struct real_format *tfmt, *ifmt;
+  tfmt = REAL_MODE_FORMAT (tmode);
+  ifmt = REAL_MODE_FORMAT (imode);
+  /* These conditions are conservative rather than trying to catch the
+     exact boundary conditions; the main case to allow is IEEE float
+     and double.  */
+  return (ifmt->b == tfmt->b
+	  && ifmt->p > 2 * tfmt->p
+	  && ifmt->emin < 2 * tfmt->emin - tfmt->p - 2
+	  && ifmt->emin < tfmt->emin - tfmt->emax - tfmt->p - 2
+	  && ifmt->emax > 2 * tfmt->emax + 2
+	  && ifmt->emax > tfmt->emax - tfmt->emin + tfmt->p + 2
+	  && ifmt->round_towards_zero == tfmt->round_towards_zero
+	  && (ifmt->has_sign_dependent_rounding
+	      == tfmt->has_sign_dependent_rounding)
+	  && ifmt->has_nans >= tfmt->has_nans
+	  && ifmt->has_inf >= tfmt->has_inf
+	  && ifmt->has_signed_zero >= tfmt->has_signed_zero
+	  && !MODE_COMPOSITE_P (tmode)
+	  && !MODE_COMPOSITE_P (imode));
+}
 
 /* Render R as an integer.  */
 
Index: real.h
===================================================================
--- real.h	(revision 141431)
+++ real.h	(working copy)
@@ -438,6 +438,11 @@
 /* Replace R by 1/R in the given machine mode, if the result is exact.  */
 extern bool exact_real_inverse (enum machine_mode, REAL_VALUE_TYPE *);
 
+/* Return true if arithmetic on values in IMODE that were promoted
+   from values in TMODE is equivalent to direct arithmetic on values
+   in TMODE.  */
+bool real_can_shorten_arithmetic (enum machine_mode, enum machine_mode);
+
 /* In tree.c: wrap up a REAL_VALUE_TYPE in a tree node.  */
 extern tree build_real (tree, REAL_VALUE_TYPE);
 
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 141431)
+++ ChangeLog	(working copy)
@@ -1,3 +1,15 @@
+2008-10-29  Joseph Myers  <joseph@codesourcery.com>
+
+	PR middle-end/36578
+	* convert.c (convert_to_real): Do not optimize conversions of
+	binary arithmetic operations between binary and decimal
+	floating-point types.  Consider mode of target type in determining
+	decimal type for arithmetic.  Unless
+	flag_unsafe_math_optimizations, do not optimize binary conversions
+	where this may change rounding behavior.
+	* real.c (real_can_shorten_arithmetic): New.
+	* real.h (real_can_shorten_arithmetic): Declare.
+
 2008-10-29  Bernd Schmidt  <bernd.schmidt@analog.com>
 	
 	* config/bfin/bfin-protos.h (WA_05000257, WA_05000283, WA_05000315,
Index: testsuite/gcc.target/i386/pr36578-2.c
===================================================================
--- testsuite/gcc.target/i386/pr36578-2.c	(revision 0)
+++ testsuite/gcc.target/i386/pr36578-2.c	(revision 0)
@@ -0,0 +1,23 @@
+/* Test for unsafe floating-point conversions.  */
+/* { dg-do run } */
+/* { dg-options "-msse2 -mfpmath=sse" } */
+
+#include "sse2-check.h"
+
+extern void abort (void);
+extern void exit (int);
+extern int printf(const char *, ...);
+
+volatile double d1 = 0x1.000001p0;
+volatile double d2 = 0x1p-54;
+volatile float f = 0x1.000002p0f;
+volatile float f2;
+
+static void
+sse2_test (void)
+{
+  f2 = (float)((long double)d1 + (long double)d2);
+  if (f != f2)
+    abort ();
+  exit (0);
+}
Index: testsuite/gcc.target/i386/pr36578-1.c
===================================================================
--- testsuite/gcc.target/i386/pr36578-1.c	(revision 0)
+++ testsuite/gcc.target/i386/pr36578-1.c	(revision 0)
@@ -0,0 +1,22 @@
+/* Test for unsafe floating-point conversions.  PR 36578.  */
+/* { dg-do run } */
+/* { dg-options "-msse2 -mfpmath=sse" } */
+
+#include "sse2-check.h"
+
+extern void abort (void);
+extern void exit (int);
+extern int printf(const char *, ...);
+
+volatile double d1 = 1.0;
+volatile double d2 = 0x1.00001p-53;
+volatile double d3;
+
+static void
+sse2_test (void)
+{
+  d3 = (double)((long double)d1 + (long double)d2);
+  if (d3 != d1)
+    abort ();
+  exit (0);
+}
Index: testsuite/gcc.dg/dfp/convert-bfp-13.c
===================================================================
--- testsuite/gcc.dg/dfp/convert-bfp-13.c	(revision 0)
+++ testsuite/gcc.dg/dfp/convert-bfp-13.c	(revision 0)
@@ -0,0 +1,20 @@
+/* Test for bug where fold changed binary operation to decimal
+   depending on typedefs.  */
+/* { dg-options "-std=gnu99" } */
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double d = 1.2345675;
+
+typedef const volatile _Decimal32 d32;
+
+int
+main (void)
+{
+  _Decimal32 a = (d * d);
+  d32 b = (d * d);
+  if (a != b)
+    abort ();
+  exit (0);
+}
Index: testsuite/gcc.dg/dfp/convert-dfp-fold-2.c
===================================================================
--- testsuite/gcc.dg/dfp/convert-dfp-fold-2.c	(revision 0)
+++ testsuite/gcc.dg/dfp/convert-dfp-fold-2.c	(revision 0)
@@ -0,0 +1,17 @@
+/* Test for bug where fold narrowed decimal floating-point
+   operations.  */
+/* { dg-options "-std=gnu99" } */
+
+extern void abort (void);
+extern void exit (int);
+
+volatile _Decimal32 f = 1.23456DF;
+volatile _Decimal64 d = 1.23456DD;
+
+int
+main (void)
+{
+  if ((_Decimal128)((_Decimal64)f * (_Decimal64)f) != (_Decimal128)(d * d))
+    abort ();
+  exit (0);
+}
Index: testsuite/gcc.dg/dfp/convert-bfp-14.c
===================================================================
--- testsuite/gcc.dg/dfp/convert-bfp-14.c	(revision 0)
+++ testsuite/gcc.dg/dfp/convert-bfp-14.c	(revision 0)
@@ -0,0 +1,17 @@
+/* Test for bug where fold narrowed decimal floating-point
+   operations.  */
+/* { dg-options "-std=gnu99" } */
+
+extern void abort (void);
+extern void exit (int);
+
+volatile _Decimal32 f = 1.23456DF;
+volatile _Decimal64 d = 1.23456DD;
+
+int
+main (void)
+{
+  if ((double)((_Decimal64)f * (_Decimal64)f) != (double)(d * d))
+    abort ();
+  exit (0);
+}
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog	(revision 141431)
+++ testsuite/ChangeLog	(working copy)
@@ -1,3 +1,10 @@
+2008-10-29  Joseph Myers  <joseph@codesourcery.com>
+
+	PR middle-end/36578
+	* gcc.dg/dfp/convert-bfp-13.c, gcc.dg/dfp/convert-bfp-14.c,
+	gcc.dg/dfp/convert-dfp-fold-2.c, gcc.target/i386/pr36578-1.c,
+	gcc.target/i386/pr36578-2.c: New tests.
+
 2008-10-29  Jakub Jelinek  <jakub@redhat.com>
 
 	PR middle-end/37870
Index: convert.c
===================================================================
--- convert.c	(revision 141431)
+++ convert.c	(working copy)
@@ -263,18 +263,22 @@
 	     tree arg1 = strip_float_extensions (TREE_OPERAND (expr, 1));
 
 	     if (FLOAT_TYPE_P (TREE_TYPE (arg0))
-		 && FLOAT_TYPE_P (TREE_TYPE (arg1)))
+		 && FLOAT_TYPE_P (TREE_TYPE (arg1))
+		 && DECIMAL_FLOAT_TYPE_P (itype) == DECIMAL_FLOAT_TYPE_P (type))
 	       {
 		  tree newtype = type;
 
 		  if (TYPE_MODE (TREE_TYPE (arg0)) == SDmode
-		      || TYPE_MODE (TREE_TYPE (arg1)) == SDmode)
+		      || TYPE_MODE (TREE_TYPE (arg1)) == SDmode
+		      || TYPE_MODE (type) == SDmode)
 		    newtype = dfloat32_type_node;
 		  if (TYPE_MODE (TREE_TYPE (arg0)) == DDmode
-		      || TYPE_MODE (TREE_TYPE (arg1)) == DDmode)
+		      || TYPE_MODE (TREE_TYPE (arg1)) == DDmode
+		      || TYPE_MODE (type) == DDmode)
 		    newtype = dfloat64_type_node;
 		  if (TYPE_MODE (TREE_TYPE (arg0)) == TDmode
-		      || TYPE_MODE (TREE_TYPE (arg1)) == TDmode)
+		      || TYPE_MODE (TREE_TYPE (arg1)) == TDmode
+		      || TYPE_MODE (type) == TDmode)
                     newtype = dfloat128_type_node;
 		  if (newtype == dfloat32_type_node
 		      || newtype == dfloat64_type_node
@@ -292,7 +296,32 @@
 		    newtype = TREE_TYPE (arg0);
 		  if (TYPE_PRECISION (TREE_TYPE (arg1)) > TYPE_PRECISION (newtype))
 		    newtype = TREE_TYPE (arg1);
-		  if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype))
+		  /* Sometimes this transformation is safe (cannot
+		     change results through affecting double rounding
+		     cases) and sometimes it is not.  If NEWTYPE is
+		     wider than TYPE, e.g. (float)((long double)double
+		     + (long double)double) converted to
+		     (float)(double + double), the transformation is
+		     unsafe regardless of the details of the types
+		     involved; double rounding can arise if the result
+		     of NEWTYPE arithmetic is a NEWTYPE value half way
+		     between two representable TYPE values but the
+		     exact value is sufficiently different (in the
+		     right direction) for this difference to be
+		     visible in ITYPE arithmetic.  If NEWTYPE is the
+		     same as TYPE, however, the transformation may be
+		     safe depending on the types involved: it is safe
+		     if the ITYPE has strictly more than twice as many
+		     mantissa bits as TYPE, can represent infinities
+		     and NaNs if the TYPE can, and has sufficient
+		     exponent range for the product or ratio of two
+		     values representable in the TYPE to be within the
+		     range of normal values of ITYPE.  */
+		  if (TYPE_PRECISION (newtype) < TYPE_PRECISION (itype)
+		      && (flag_unsafe_math_optimizations
+			  || (TYPE_PRECISION (newtype) == TYPE_PRECISION (type)
+			      && real_can_shorten_arithmetic (TYPE_MODE (itype),
+							      TYPE_MODE (type)))))
 		    {
 		      expr = build2 (TREE_CODE (expr), newtype,
 				     fold (convert_to_real (newtype, arg0)),

-- 
Joseph S. Myers
joseph@codesourcery.com



More information about the Gcc-patches mailing list