Hi!

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.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk/15/14?

2025-06-04  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.

--- gcc/real.cc.jj      2025-04-08 14:08:59.847162199 +0200
+++ gcc/real.cc 2025-06-04 18:17:04.018080378 +0200
@@ -2230,7 +2230,6 @@ real_from_integer (REAL_VALUE_TYPE *r, f
     {
       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, f
       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, f
       /* 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, f
          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;
        }
--- gcc/testsuite/gcc.dg/bitint-123.c.jj        2025-06-04 18:29:29.487456324 
+0200
+++ gcc/testsuite/gcc.dg/bitint-123.c   2025-06-04 18:31:13.941077762 +0200
@@ -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
+}

        Jakub

Reply via email to