]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
backport: re PR middle-end/36578 (cast to long double not taken into account when...
authorJanis Johnson <janis187@us.ibm.com>
Wed, 25 Feb 2009 22:08:55 +0000 (22:08 +0000)
committerJanis Johnson <janis@gcc.gnu.org>
Wed, 25 Feb 2009 22:08:55 +0000 (22:08 +0000)
2009-02-25  Janis Johnson  <janis187@us.ibm.com>

gcc/
Backport from mainline:
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.

gcc/testsuite/
Backport from mainline:
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.

From-SVN: r144436

gcc/ChangeLog
gcc/convert.c
gcc/real.c
gcc/real.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/dfp/convert-bfp-13.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/convert-bfp-14.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/convert-dfp-fold-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/dfp/pr39034.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr36578-1.c [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr36578-2.c [new file with mode: 0644]

index ede8715f3e81df432072765e1ce191599baa84f3..0f5e583b6b198daef8678a57de896dd7bdfd9392 100644 (file)
@@ -1,3 +1,18 @@
+2009-02-25  Janis Johnson  <janis187@us.ibm.com>
+
+       Backport from mainline:
+       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.
+
 2009-02-21  Uros Bizjak  <ubizjak@gmail.com>
 
        Backport from mainline:
index f82604e775a5bd1338b341deecdbd84227697e08..a9a22c52c53d52677a80f5d24456606624fbd45f 100644 (file)
@@ -261,18 +261,22 @@ convert_to_real (tree type, tree expr)
             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
@@ -290,7 +294,32 @@ convert_to_real (tree type, tree expr)
                    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)),
index cbfd22c79f789429faa6340f4a55ef466d4f5d68..902a9b88f920f8d2a805eee3e22df066d8d4b182 100644 (file)
@@ -1274,6 +1274,31 @@ exact_real_inverse (enum machine_mode mode, REAL_VALUE_TYPE *r)
   *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_nans >= tfmt->has_nans
+         && ifmt->has_inf >= tfmt->has_inf
+         && ifmt->has_signed_zero >= tfmt->has_signed_zero);
+}
 \f
 /* Render R as an integer.  */
 
index 381ac5ef213191e6d251618484253e3ac811d33f..7cb1ac1395a234da92e1a63caf3d47ff4f5fd7f3 100644 (file)
@@ -413,6 +413,11 @@ extern rtx const_double_from_real_value (REAL_VALUE_TYPE, enum machine_mode);
 /* 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 b5883b10b04a68d60563c2d63f48bab5c2a2c4d9..68a7e295ec17445eed137a30e75c64456b1f460b 100644 (file)
@@ -1,3 +1,13 @@
+2009-02-25  Janis Johnson  <janis187@us.ibm.com>
+
+       Backport from mainline:
+       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.
+
 2009-02-25  H.J. Lu  <hongjiu.lu@intel.com>
 
        Backport from mainline:
diff --git a/gcc/testsuite/gcc.dg/dfp/convert-bfp-13.c b/gcc/testsuite/gcc.dg/dfp/convert-bfp-13.c
new file mode 100644 (file)
index 0000000..91a5b49
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.dg/dfp/convert-bfp-14.c b/gcc/testsuite/gcc.dg/dfp/convert-bfp-14.c
new file mode 100644 (file)
index 0000000..a1312d2
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.dg/dfp/convert-dfp-fold-2.c b/gcc/testsuite/gcc.dg/dfp/convert-dfp-fold-2.c
new file mode 100644 (file)
index 0000000..9f09279
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.dg/dfp/pr39034.c b/gcc/testsuite/gcc.dg/dfp/pr39034.c
new file mode 100644 (file)
index 0000000..9f92d3a
--- /dev/null
@@ -0,0 +1,110 @@
+/* { dg-do run } */
+/* { dg-options "-std=gnu99 -O" } */
+
+/* DFP TR 24732 == WG14 / N1176, N1312 */
+
+extern void abort (void);
+int failures = 0;
+
+#ifdef DBG
+#include <stdio.h>
+#define FAILURE(MSG) { printf ("line %d: %s\n", __LINE__, MSG); failures++; }
+#else
+#define FAILURE(MSG) failures++;
+#endif
+
+
+/* Test runtime computations.  */
+
+void
+runtime32 (void)
+{
+  volatile float v1 = 28.f, v2 = 3.f, v3 = 9.f, v4 = 31.f, v5 = 3.f, v6 = 10.f;
+  float      b32 = (float)((v1/v2-v3) - (v4/v5-v6));
+  _Decimal32 d32 = (float)((v1/v2-v3) - (v4/v5-v6));
+
+  if (b32)
+    FAILURE ("runtime: b32 should be zero")
+  if (d32)
+    FAILURE ("runtime: d32 should be zero")
+}
+
+void
+runtime64 (void)
+{
+  volatile double v1 = 28., v2 = 3., v3 = 9., v4 = 31., v5 = 3., v6 = 10.;
+  double     b64 = (double)((v1/v2-v3) - (v4/v5-v6));
+  _Decimal64 d64 = (double)((v1/v2-v3) - (v4/v5-v6));
+
+  if (b64)
+    FAILURE ("runtime: b64 should be zero")
+  if (d64)
+    FAILURE ("runtime: d64 should be zero")
+}
+
+void
+runtime128 (void)
+{
+  volatile long double v1 = 28.l, v2 = 3.l, v3 = 9.l,
+                       v4 = 31.l, v5 = 3.l, v6 = 10.l;
+  long double b128 = (long double)((v1/v2-v3) - (v4/v5-v6));
+  _Decimal128 d128 = (long double)((v1/v2-v3) - (v4/v5-v6));
+
+  if (b128)
+    FAILURE ("runtime: b128 should be zero")
+  if (d128)
+    FAILURE ("runtime: d128 should be zero")
+}
+
+/* Test constant folding.  */
+
+void
+fold32 (void)
+{
+  double     d32 = (float)((28.f/3.f-9.f) - (31.f/3.f-10.f));
+  _Decimal32 b32 = (float)((28.f/3.f-9.f) - (31.f/3.f-10.f));
+
+  if (b32)
+    FAILURE ("fold: b32 should be zero")
+  if (d32)
+    FAILURE ("fold: d32 should be zero")
+}
+
+void
+fold64 (void)
+{
+  double     b64 = (double)((28./3.-9.) - (31./3.-10.));
+  _Decimal64 d64 = (double)((28./3.-9.) - (31./3.-10.));
+
+  if (b64)
+    FAILURE ("fold: b64 should be zero")
+  if (d64)
+    FAILURE ("fold: d64 should be zero")
+}
+
+void
+fold128 (void)
+{
+  long double b128 = (long double)((28./3.-9.) - (31./3.-10.));
+  _Decimal128 d128 = (long double)((28./3.-9.) - (31./3.-10.));
+
+  if (b128)
+    FAILURE ("fold: b128 should be zero")
+  if (d128)
+    FAILURE ("fold: d128 should be zero")
+}
+
+int
+main ()
+{
+  runtime32 ();
+  runtime64 ();
+  runtime128 ();
+  fold32 ();
+  fold64 ();
+  fold128 ();
+
+  if (failures != 0)
+    abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr36578-1.c b/gcc/testsuite/gcc.target/i386/pr36578-1.c
new file mode 100644 (file)
index 0000000..cae0d70
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr36578-2.c b/gcc/testsuite/gcc.target/i386/pr36578-2.c
new file mode 100644 (file)
index 0000000..19143cf
--- /dev/null
@@ -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);
+}