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
+}

Reply via email to