https://gcc.gnu.org/g:ea9ea72e448e391d4be781b74956a0190f93afc8

commit r16-1185-gea9ea72e448e391d4be781b74956a0190f93afc8
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Jun 5 15:47:19 2025 +0200

    real: Fix up real_from_integer [PR120547]
    
    The function has 2 problems, one is _BitInt specific and the other is
    most likely also reproduceable only with it.
    
    The first issue is that I've missed updating the function for _BitInt,
    maxbitlen as MAX_BITSIZE_MODE_ANY_INT + HOST_BITS_PER_WIDE_INT
    obviously isn't guaranteed to be larger than any integral type we might
    want to convert at compile time from wide_int to REAL_VALUE_FORMAT.
    Just using len instead of it works fine, at least when used after
    HOST_BITS_PER_WIDE_INT is added to it and it is truncated to multiples
    of HOST_BITS_PER_WIDE_INT.
    
    The other bug is that if the value has too many significant bits (formerly
    maxbitlen - cnt_l_z, now len - cnt_l_z), the code just shifts it right and
    adds the shift count to the future exponent.  That isn't correct for
    rounding as the testcase attempts to show, the internal real format has more
    bits than any precision in supported format, but we still need to
    distinguish bewtween values exactly half way between representable floating
    point values (those should be rounded to even) and the case when we've
    shifted away some non-zero bits, so the value was tiny bit larger than half
    way and then we should round up.
    
    The patch uses something like e.g. soft-fp uses in these cases, right shift
    with sticky bit in the least significant bit.
    
    2025-06-05  Jakub Jelinek  <ja...@redhat.com>
    
            PR middle-end/120547
            * real.cc (real_from_integer): Remove maxbitlen variable, use
            len instead of that.  When shifting right, or in 1 if any of the
            shifted away bits are non-zero.  Formatting fix.
    
            * gcc.dg/bitint-123.c: New test.

Diff:
---
 gcc/real.cc                       | 27 +++++++++++++++------------
 gcc/testsuite/gcc.dg/bitint-123.c | 26 ++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/gcc/real.cc b/gcc/real.cc
index b64bad0e9b27..95a933220b5d 100644
--- a/gcc/real.cc
+++ b/gcc/real.cc
@@ -2230,7 +2230,6 @@ real_from_integer (REAL_VALUE_TYPE *r, format_helper fmt,
     {
       unsigned int len = val_in.get_precision ();
       int i, j, e = 0;
-      int maxbitlen = MAX_BITSIZE_MODE_ANY_INT + HOST_BITS_PER_WIDE_INT;
       const unsigned int realmax = (SIGNIFICAND_BITS / HOST_BITS_PER_WIDE_INT
                                    * HOST_BITS_PER_WIDE_INT);
 
@@ -2238,12 +2237,6 @@ real_from_integer (REAL_VALUE_TYPE *r, format_helper fmt,
       r->cl = rvc_normal;
       r->sign = wi::neg_p (val_in, sgn);
 
-      /* We have to ensure we can negate the largest negative number.  */
-      wide_int val = wide_int::from (val_in, maxbitlen, sgn);
-
-      if (r->sign)
-       val = -val;
-
       /* Ensure a multiple of HOST_BITS_PER_WIDE_INT, ceiling, as elt
         won't work with precisions that are not a multiple of
         HOST_BITS_PER_WIDE_INT.  */
@@ -2252,7 +2245,13 @@ real_from_integer (REAL_VALUE_TYPE *r, format_helper fmt,
       /* Ensure we can represent the largest negative number.  */
       len += 1;
 
-      len = len/HOST_BITS_PER_WIDE_INT * HOST_BITS_PER_WIDE_INT;
+      len = len / HOST_BITS_PER_WIDE_INT * HOST_BITS_PER_WIDE_INT;
+
+      /* We have to ensure we can negate the largest negative number.  */
+      wide_int val = wide_int::from (val_in, len, sgn);
+
+      if (r->sign)
+       val = -val;
 
       /* Cap the size to the size allowed by real.h.  */
       if (len > realmax)
@@ -2260,14 +2259,18 @@ real_from_integer (REAL_VALUE_TYPE *r, format_helper 
fmt,
          HOST_WIDE_INT cnt_l_z;
          cnt_l_z = wi::clz (val);
 
-         if (maxbitlen - cnt_l_z > realmax)
+         if (len - cnt_l_z > realmax)
            {
-             e = maxbitlen - cnt_l_z - realmax;
+             e = len - cnt_l_z - realmax;
 
              /* This value is too large, we must shift it right to
                 preserve all the bits we can, and then bump the
-                exponent up by that amount.  */
-             val = wi::lrshift (val, e);
+                exponent up by that amount, but or in 1 if any of
+                the shifted out bits are non-zero.  */
+             if (wide_int::from (val, e, UNSIGNED) != 0)
+               val = wi::set_bit (wi::lrshift (val, e), 0);
+             else
+               val = wi::lrshift (val, e);
            }
          len = realmax;
        }
diff --git a/gcc/testsuite/gcc.dg/bitint-123.c 
b/gcc/testsuite/gcc.dg/bitint-123.c
new file mode 100644
index 000000000000..4d019a98fdfc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/bitint-123.c
@@ -0,0 +1,26 @@
+/* PR middle-end/120547 */
+/* { dg-do run { target bitint } } */
+/* { dg-options "-O2" } */
+/* { dg-add-options float64 } */
+/* { dg-require-effective-target float64 } */
+
+#define CHECK(x, y) \
+  if ((_Float64) x != (_Float64) y                             \
+      || (_Float64) (x + 1) != (_Float64) (y + 1))             \
+    __builtin_abort ()
+
+int
+main ()
+{
+  unsigned long long a = 0x20000000000001ULL << 7;
+  volatile unsigned long long b = a;
+  CHECK (a, b);
+#if __BITINT_MAXWIDTH__ >= 4096
+  unsigned _BitInt(4096) c = ((unsigned _BitInt(4096)) 0x20000000000001ULL) << 
253;
+  volatile unsigned _BitInt(4096) d = c;
+  CHECK (c, d);
+  unsigned _BitInt(4096) e = ((unsigned _BitInt(4096)) 0x20000000000001ULL) << 
931;
+  volatile unsigned _BitInt(4096) f = e;
+  CHECK (e, f);
+#endif
+}

Reply via email to