From 20ded7a68b10f9c2d1aaa94e89df494bf0ce41a0 Mon Sep 17 00:00:00 2001 From: Joseph Myers Date: Wed, 29 Oct 2008 17:05:42 +0000 Subject: [PATCH] re PR middle-end/36578 (cast to long double not taken into account when result stored to a double) 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. testsuite: * 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: r141432 --- gcc/ChangeLog | 12 ++++++ gcc/convert.c | 39 ++++++++++++++++--- gcc/real.c | 29 ++++++++++++++ gcc/real.h | 5 +++ gcc/testsuite/ChangeLog | 7 ++++ gcc/testsuite/gcc.dg/dfp/convert-bfp-13.c | 20 ++++++++++ gcc/testsuite/gcc.dg/dfp/convert-bfp-14.c | 17 ++++++++ gcc/testsuite/gcc.dg/dfp/convert-dfp-fold-2.c | 17 ++++++++ gcc/testsuite/gcc.target/i386/pr36578-1.c | 22 +++++++++++ gcc/testsuite/gcc.target/i386/pr36578-2.c | 23 +++++++++++ 10 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/dfp/convert-bfp-13.c create mode 100644 gcc/testsuite/gcc.dg/dfp/convert-bfp-14.c create mode 100644 gcc/testsuite/gcc.dg/dfp/convert-dfp-fold-2.c create mode 100644 gcc/testsuite/gcc.target/i386/pr36578-1.c create mode 100644 gcc/testsuite/gcc.target/i386/pr36578-2.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2a0f5ecd9a4..1270ebc5f28 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2008-10-29 Joseph Myers + + 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 * config/bfin/bfin-protos.h (WA_05000257, WA_05000283, WA_05000315, diff --git a/gcc/convert.c b/gcc/convert.c index 24617728298..1a462e7626f 100644 --- a/gcc/convert.c +++ b/gcc/convert.c @@ -263,18 +263,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 @@ -292,7 +296,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)), diff --git a/gcc/real.c b/gcc/real.c index dc6d7483e99..c5a16a867e1 100644 --- a/gcc/real.c +++ b/gcc/real.c @@ -1266,6 +1266,35 @@ 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_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. */ diff --git a/gcc/real.h b/gcc/real.h index 44e79c36e17..5a686536608 100644 --- a/gcc/real.h +++ b/gcc/real.h @@ -438,6 +438,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); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 070840147c8..6e3f6f2123e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2008-10-29 Joseph Myers + + 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 PR middle-end/37870 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 index 00000000000..91a5b494884 --- /dev/null +++ b/gcc/testsuite/gcc.dg/dfp/convert-bfp-13.c @@ -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 index 00000000000..a1312d2a0e6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/dfp/convert-bfp-14.c @@ -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 index 00000000000..9f0927963a1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/dfp/convert-dfp-fold-2.c @@ -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.target/i386/pr36578-1.c b/gcc/testsuite/gcc.target/i386/pr36578-1.c new file mode 100644 index 00000000000..cae0d708837 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr36578-1.c @@ -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 index 00000000000..19143cfe5ba --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr36578-2.c @@ -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); +}