Message ID | alpine.DEB.2.22.394.2208232352390.392053@digraph.polyomino.org.uk |
---|---|
State | New |
Headers | show |
Series | tree.cc: Fix optimization of DFP default initialization | expand |
> Am 24.08.2022 um 01:54 schrieb Joseph Myers <joseph@codesourcery.com>: > > 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. OK to commit? Ok Thanks, Richard > 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. > > diff --git a/gcc/testsuite/gcc.dg/torture/dfp-default-init-1.c b/gcc/testsuite/gcc.dg/torture/dfp-default-init-1.c > new file mode 100644 > index 00000000000..f893ddb52b9 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/dfp-default-init-1.c > @@ -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); > +} > diff --git a/gcc/testsuite/gcc.dg/torture/dfp-default-init-2.c b/gcc/testsuite/gcc.dg/torture/dfp-default-init-2.c > new file mode 100644 > index 00000000000..30f850be2ee > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/dfp-default-init-2.c > @@ -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" > diff --git a/gcc/testsuite/gcc.dg/torture/dfp-default-init-3.c b/gcc/testsuite/gcc.dg/torture/dfp-default-init-3.c > new file mode 100644 > index 00000000000..cdf73508c76 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/torture/dfp-default-init-3.c > @@ -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" > diff --git a/gcc/tree.cc b/gcc/tree.cc > index fed1434d141..007c9325b17 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -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 (); > } > > -- > Joseph S. Myers > joseph@codesourcery.com
diff --git a/gcc/testsuite/gcc.dg/torture/dfp-default-init-1.c b/gcc/testsuite/gcc.dg/torture/dfp-default-init-1.c new file mode 100644 index 00000000000..f893ddb52b9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/dfp-default-init-1.c @@ -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); +} diff --git a/gcc/testsuite/gcc.dg/torture/dfp-default-init-2.c b/gcc/testsuite/gcc.dg/torture/dfp-default-init-2.c new file mode 100644 index 00000000000..30f850be2ee --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/dfp-default-init-2.c @@ -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" diff --git a/gcc/testsuite/gcc.dg/torture/dfp-default-init-3.c b/gcc/testsuite/gcc.dg/torture/dfp-default-init-3.c new file mode 100644 index 00000000000..cdf73508c76 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/dfp-default-init-3.c @@ -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" diff --git a/gcc/tree.cc b/gcc/tree.cc index fed1434d141..007c9325b17 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -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 (); }