https://gcc.gnu.org/g:f3002d664d1137844c714645a841a48ab57d0eaa
commit r16-1555-gf3002d664d1137844c714645a841a48ab57d0eaa Author: Jakub Jelinek <ja...@redhat.com> Date: Wed Jun 18 08:07:22 2025 +0200 dfp, real: Fix up FLOAT_EXPR/FIX_TRUNC_EXPR constant folding between dfp and large _BitInt [PR120631] The following testcase shows that while at runtime we handle conversions between _Decimal{64,128} and large _BitInt correctly, at compile time we mishandle them in both directions, in one direction we end up in ICE in decimal_from_integer callee because the char buffer is too short for the needed number of decimal digits, in the conversion of dfp to large _BitInt we return 0 in the wide_int. The following patch fixes the ICE by using larger buffer (XALLOCAVEC allocated, it will be never larger than 65536 / 3 bytes) in the larger _BitInt case, and the other direction by setting exponent to exp % 19 and instead multiplying the result by needed powers of 10^19 (10^19 chosen as largest power of ten that can fit into UHWI). 2025-06-18 Jakub Jelinek <ja...@redhat.com> PR middle-end/120631 * real.cc (decimal_from_integer): Add digits argument, if larger than 256, use XALLOCAVEC allocated buffer. (real_from_integer): Pass val_in's precision divided by 3 to decimal_from_integer. * dfp.cc (decimal_real_to_integer): For precision > 128 if finite and exponent is large, decrease exponent and multiply resulting wide_int by powers of 10^19. * gcc.dg/dfp/bitint-9.c: New test. Diff: --- gcc/dfp.cc | 49 ++++++++++++++++++++++++++++++++++++- gcc/real.cc | 21 ++++++++++++---- gcc/testsuite/gcc.dg/dfp/bitint-9.c | 29 ++++++++++++++++++++++ 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/gcc/dfp.cc b/gcc/dfp.cc index 5c2bf1ae0686..4011a95db4af 100644 --- a/gcc/dfp.cc +++ b/gcc/dfp.cc @@ -619,11 +619,21 @@ decimal_real_to_integer (const REAL_VALUE_TYPE *r, bool *fail, int precision) decNumber dn, dn2, dn3; REAL_VALUE_TYPE to; char string[256]; + int scale = 0; decContextDefault (&set, DEC_INIT_DECIMAL128); set.traps = 0; set.round = DEC_ROUND_DOWN; decimal128ToNumber ((const decimal128 *) r->sig, &dn); + if (precision > 128 && decNumberIsFinite (&dn) && dn.exponent > 19) + { + /* libdecNumber doesn't really handle too large integers. + So when precision is large and exponent as well, trim the + exponent and adjust the resulting wide_int by multiplying + it multiple times with 10^19. */ + scale = dn.exponent / 19; + dn.exponent %= 19; + } decNumberToIntegralValue (&dn2, &dn, &set); decNumberZero (&dn3); @@ -633,7 +643,44 @@ decimal_real_to_integer (const REAL_VALUE_TYPE *r, bool *fail, int precision) function. */ decNumberToString (&dn, string); real_from_string (&to, string); - return real_to_integer (&to, fail, precision); + bool failp = false; + wide_int w = real_to_integer (&to, &failp, precision); + if (failp) + *fail = true; + if (scale && !failp) + { + wide_int wm = wi::uhwi (HOST_WIDE_INT_UC (10000000000000000000), + w.get_precision ()); + bool isneg = wi::neg_p (w); + if (isneg) + w = -w; + enum wi::overflow_type ovf = wi::OVF_NONE; + do + { + if (scale & 1) + { + w = wi::umul (w, wm, &ovf); + if (ovf) + break; + } + scale >>= 1; + if (!scale) + break; + wm = wi::umul (wm, wm, &ovf); + } + while (!ovf); + if (ovf) + { + *fail = true; + if (isneg) + return wi::set_bit_in_zero (precision - 1, precision); + else + return ~wi::set_bit_in_zero (precision - 1, precision); + } + if (isneg) + w = -w; + } + return w; } /* Perform the decimal floating point operation described by CODE. diff --git a/gcc/real.cc b/gcc/real.cc index 95a933220b5d..1f987d48889f 100644 --- a/gcc/real.cc +++ b/gcc/real.cc @@ -101,7 +101,7 @@ static int do_compare (const REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *, int); static void do_fix_trunc (REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *); static unsigned long rtd_divmod (REAL_VALUE_TYPE *, REAL_VALUE_TYPE *); -static void decimal_from_integer (REAL_VALUE_TYPE *); +static void decimal_from_integer (REAL_VALUE_TYPE *, int); static void decimal_integer_string (char *, const REAL_VALUE_TYPE *, size_t); @@ -2309,7 +2309,9 @@ real_from_integer (REAL_VALUE_TYPE *r, format_helper fmt, } if (fmt.decimal_p ()) - decimal_from_integer (r); + /* We need at most one decimal digits for each 3 bits of input + precision. */ + decimal_from_integer (r, val_in.get_precision () / 3); if (fmt) real_convert (r, fmt, r); } @@ -2364,12 +2366,21 @@ decimal_integer_string (char *str, const REAL_VALUE_TYPE *r_orig, /* Convert a real with an integral value to decimal float. */ static void -decimal_from_integer (REAL_VALUE_TYPE *r) +decimal_from_integer (REAL_VALUE_TYPE *r, int digits) { char str[256]; - decimal_integer_string (str, r, sizeof (str) - 1); - decimal_real_from_string (r, str); + if (digits <= 256) + { + decimal_integer_string (str, r, sizeof (str) - 1); + decimal_real_from_string (r, str); + } + else + { + char *s = XALLOCAVEC (char, digits); + decimal_integer_string (s, r, digits - 1); + decimal_real_from_string (r, s); + } } /* Returns 10**2**N. */ diff --git a/gcc/testsuite/gcc.dg/dfp/bitint-9.c b/gcc/testsuite/gcc.dg/dfp/bitint-9.c new file mode 100644 index 000000000000..72155a012475 --- /dev/null +++ b/gcc/testsuite/gcc.dg/dfp/bitint-9.c @@ -0,0 +1,29 @@ +/* PR middle-end/120631 */ +/* { dg-do run { target bitint } } */ +/* { dg-options "-O2" } */ + +#if __BITINT_MAXWIDTH__ >= 2048 +_Decimal128 a = 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dl; +_BitInt(2048) b = 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb; +_Decimal64 c = 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dd; +_BitInt(1536) d = 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb; +#endif + +int +main () +{ +#if __BITINT_MAXWIDTH__ >= 2048 + if (a != b || (_BitInt(2048)) a != b || c != d || (_BitInt(1536)) c != d) + __builtin_abort (); + _Decimal128 e = 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dl; + _BitInt(2048) f = 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb; + _Decimal128 g = 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb; + _BitInt(2048) h = 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dl; + _Decimal64 i = 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dd; + _BitInt(1536) j = 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb; + _Decimal64 k = 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb; + _BitInt(1536) l = 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dd; + if (e != g || f != h || i != k || j != l) + __builtin_abort (); +#endif +}