tree.cc: Fix optimization of DFP default initialization

When an object of decimal floating-point type is default-initialized,
GCC is inconsistent about whether it is given the all-zero-bits
representation (zero with the least quantum exponent) or whether it
acts like a conversion of integer 0 to the DFP type (zero with quantum
exponent 0).  In particular, the representation stored in memory can
have all zero bits, but optimization of access to the same object
based on its known constant value can then produce zero with quantum
exponent 0 instead.

C2x leaves the quantum exponent for default initialization
implementation-defined, but that doesn't allow such inconsistency in
the interpretation of a single object.  All zero bits seems most
appropriate; change build_real to special-case dconst0 the same way
other constants are special-cased and ensure that the correct zero for
the type is generated.

Bootstrapped with no regressions for x86_64-pc-linux-gnu.

gcc/
	* tree.cc (build_real): Give DFP dconst0 the minimum quantum
	exponent for the type.

gcc/testsuite/
	* gcc.dg/torture/dfp-default-init-1.c,
	gcc.dg/torture/dfp-default-init-2.c,
	gcc.dg/torture/dfp-default-init-3.c: New tests.
This commit is contained in:
Joseph Myers 2022-08-24 14:10:25 +00:00
parent 6d1f144b3e
commit 02de9d26b1
4 changed files with 140 additions and 2 deletions

View file

@ -0,0 +1,113 @@
/* Test that default-initialized DFP values consistently have the least quantum
exponent. */
/* { dg-do run } */
/* { dg-require-effective-target dfp } */
extern void exit (int);
extern void abort (void);
void *memset (void *, int, __SIZE_TYPE__);
int memcmp (const void *, const void *, __SIZE_TYPE__);
#ifndef TYPE
#define TYPE _Decimal32
#endif
#ifndef ZEROFP
#define ZEROFP 0e-101DF
#endif
TYPE zero_int = 0;
TYPE zero_fp = ZEROFP;
TYPE default_init;
TYPE zero_bytes;
TYPE x;
struct s { TYPE a, b; };
struct s s_default_init;
struct s s_empty_init = {};
struct s s_first_int = { 0 };
struct s s_both_int = { 0, 0 };
struct s sx;
const TYPE a_default_init[10];
const TYPE a_empty_init[10] = {};
const TYPE a_first_int[10] = { 0 };
const TYPE a_two_int[10] = { 0, 0 };
#define CHECK_ZERO_BYTES(expr) \
do \
{ \
if (memcmp (expr, &zero_bytes, sizeof zero_bytes) != 0) \
abort (); \
TYPE tmp = *expr; \
if (memcmp (&tmp, &zero_bytes, sizeof zero_bytes) != 0) \
abort (); \
} \
while (0)
#define CHECK_INT_BYTES(expr) \
do \
{ \
if (memcmp (expr, &zero_int, sizeof zero_int) != 0) \
abort (); \
TYPE tmp = *expr; \
if (memcmp (&tmp, &zero_int, sizeof zero_int) != 0) \
abort (); \
} \
while (0)
int
main (void)
{
memset (&zero_bytes, 0, sizeof zero_bytes);
if (memcmp (&zero_bytes, &zero_int, sizeof zero_int) == 0)
abort ();
CHECK_ZERO_BYTES (&zero_fp);
CHECK_ZERO_BYTES (&default_init);
CHECK_ZERO_BYTES (&s_default_init.a);
CHECK_ZERO_BYTES (&s_default_init.b);
CHECK_ZERO_BYTES (&s_empty_init.a);
CHECK_ZERO_BYTES (&s_empty_init.b);
CHECK_INT_BYTES (&s_first_int.a);
CHECK_ZERO_BYTES (&s_first_int.b);
CHECK_INT_BYTES (&s_both_int.a);
CHECK_INT_BYTES (&s_both_int.b);
CHECK_ZERO_BYTES (&a_default_init[0]);
CHECK_ZERO_BYTES (&a_default_init[1]);
CHECK_ZERO_BYTES (&a_default_init[2]);
CHECK_ZERO_BYTES (&a_default_init[9]);
CHECK_ZERO_BYTES (&a_empty_init[0]);
CHECK_ZERO_BYTES (&a_empty_init[1]);
CHECK_ZERO_BYTES (&a_empty_init[2]);
CHECK_ZERO_BYTES (&a_empty_init[9]);
CHECK_INT_BYTES (&a_first_int[0]);
CHECK_ZERO_BYTES (&a_first_int[1]);
CHECK_ZERO_BYTES (&a_first_int[2]);
CHECK_ZERO_BYTES (&a_first_int[9]);
CHECK_INT_BYTES (&a_two_int[0]);
CHECK_INT_BYTES (&a_two_int[1]);
CHECK_ZERO_BYTES (&a_two_int[2]);
CHECK_ZERO_BYTES (&a_two_int[9]);
struct s s2 = {};
CHECK_ZERO_BYTES (&s2.a);
CHECK_ZERO_BYTES (&s2.b);
struct s s3 = { 0 };
CHECK_INT_BYTES (&s3.a);
CHECK_ZERO_BYTES (&s3.b);
struct s s4 = { 0, 0 };
CHECK_INT_BYTES (&s4.a);
CHECK_INT_BYTES (&s4.b);
struct s s5 = { 0 };
sx = s5;
CHECK_INT_BYTES (&sx.a);
CHECK_ZERO_BYTES (&sx.b);
x = default_init;
CHECK_ZERO_BYTES (&x);
x = zero_int;
CHECK_INT_BYTES (&x);
x = s_default_init.a;
CHECK_ZERO_BYTES (&x);
x = s_default_init.b;
CHECK_ZERO_BYTES (&x);
exit (0);
}

View file

@ -0,0 +1,8 @@
/* Test that default-initialized DFP values consistently have the least quantum
exponent. */
/* { dg-do run } */
/* { dg-require-effective-target dfp } */
#define TYPE _Decimal64
#define ZEROFP 0e-398DD
#include "dfp-default-init-1.c"

View file

@ -0,0 +1,8 @@
/* Test that default-initialized DFP values consistently have the least quantum
exponent. */
/* { dg-do run } */
/* { dg-require-effective-target dfp } */
#define TYPE _Decimal128
#define ZEROFP 0e-6176DL
#include "dfp-default-init-1.c"

View file

@ -2385,12 +2385,12 @@ build_real (tree type, REAL_VALUE_TYPE d)
tree v;
int overflow = 0;
/* dconst{1,2,m1,half} are used in various places in
/* dconst{0,1,2,m1,half} are used in various places in
the middle-end and optimizers, allow them here
even for decimal floating point types as an exception
by converting them to decimal. */
if (DECIMAL_FLOAT_MODE_P (TYPE_MODE (type))
&& d.cl == rvc_normal
&& (d.cl == rvc_normal || d.cl == rvc_zero)
&& !d.decimal)
{
if (memcmp (&d, &dconst1, sizeof (d)) == 0)
@ -2401,6 +2401,15 @@ build_real (tree type, REAL_VALUE_TYPE d)
decimal_real_from_string (&d, "-1");
else if (memcmp (&d, &dconsthalf, sizeof (d)) == 0)
decimal_real_from_string (&d, "0.5");
else if (memcmp (&d, &dconst0, sizeof (d)) == 0)
{
/* Make sure to give zero the minimum quantum exponent for
the type (which corresponds to all bits zero). */
const struct real_format *fmt = REAL_MODE_FORMAT (TYPE_MODE (type));
char buf[16];
sprintf (buf, "0e%d", fmt->emin - fmt->p);
decimal_real_from_string (&d, buf);
}
else
gcc_unreachable ();
}