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
This commit is contained in:
Joseph Myers 2008-10-29 17:05:42 +00:00 committed by Joseph Myers
parent 669eeb28ea
commit 20ded7a68b
10 changed files with 186 additions and 5 deletions

View File

@ -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,

View File

@ -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)),

View File

@ -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. */

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}