+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:
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
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)),
*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. */
/* 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);
+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:
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* { 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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}