https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126952

Roger Sayle <roger at nextmovesoftware dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
   Last reconfirmed|                            |2026-10-08
     Ever confirmed|0                           |1
             Status|UNCONFIRMED                 |NEW
                 CC|                            |roger at nextmovesoftware dot 
com

--- Comment #7 from Roger Sayle <roger at nextmovesoftware dot com> ---
Firstly, thanks to Andrea for pointing out that my patch posted at:
https://gcc.gnu.org/pipermail/gcc-patches/2026-October/733683.html
was associated with PR middle-end/126952.

It's interesting that the bisection points to jump threading and that this is
believed to be a false positive warning, as whilst developing/reducing the
patch above (to the minimal change that would restore bootstrap), that I
initially diagnosed the tree cause being elsewhere.

I believe that a minimal reproducer of the true cause is:

    wide_int x;
    wide_int y;
    x = y;

Obviously, the above uses an uninitialized value (as would be the case if the
type was int), but for the current implementation of wide_int it also invokes
undefined behavior, possibly overwriting memory, corrupting the stack and/or
segfaulting.

This is because wide_int effectively contains three fields; val, len and
precision, and the default constructor only initializes one of them:
inline wide_int_storage::wide_int_storage () : precision (0) {}

Remembering this is you then study the implementation of wi::copy:
template <typename T1, typename T2>
inline void
wi::copy (T1 &x, const T2 &y)
{
  unsigned int len = y.get_len ();
  HOST_WIDE_INT *xval = x.write_val (len);
  const HOST_WIDE_INT *yval = y.get_val ();
  unsigned int i = 0;
  do
    xval[i] = yval[i];
  while (++i < len);
  /* For widest_int write_val is called with an exact value, not
     upper bound for len, so nothing is needed further.  */
  if (!wi::int_traits <T1>::needs_write_val_arg)
    x.set_len (len, y.is_sign_extended);
}


You'll notice that it never checks "precision" and instead overwrites "len"
words at wherever write_val decides to point to.  Boom goes the dynamite.


So my first attempt at a patch looked like:

diff --git a/gcc/wide-int.h b/gcc/wide-int.h
index 9c30f320b91..2ff028e571b 100644
--- a/gcc/wide-int.h
+++ b/gcc/wide-int.h
@@ -706,7 +706,7 @@ namespace wi
   class storage_ref
   {
   public:
-    storage_ref () {}
+    storage_ref () : val (0), len (0), precision (0) {}
     storage_ref (const HOST_WIDE_INT *, unsigned int, unsigned int);

     const HOST_WIDE_INT *val;
@@ -1174,7 +1174,7 @@ namespace wi
   };
 }

-inline wide_int_storage::wide_int_storage () : precision (0) {}
+inline wide_int_storage::wide_int_storage () : len (0), precision (0) {}

 /* Initialize the storage from integer X, in its natural precision.
    Note that we do not allow integers with host-dependent precision
@@ -1356,7 +1356,7 @@ private:
   unsigned int len;

 public:
-  fixed_wide_int_storage () = default;
+  fixed_wide_int_storage () : len (0) {}
   template <typename T>
   fixed_wide_int_storage (const T &);


Alas I was worried that this potentially had a compile-time impact, with
an additional write to memory for each wide_int construction that didn't
specify an initial value.  Hence, the proposed fix was to avoid having
(and copying) any uninitialized wide_int in this PR's initial description.

Of course, correctly diagnosing that copying an uninitialized wide_int should
generate a "may be used uninitialized" error, is heavily dependent of inlining,
loop unrolling, jump threading etc.  So Richard's bisected patch is actually
doing a better job, and my posted patch is indeed hiding/avoiding the true
cause.

I'd be curious if folks agree with my analysis.  I wouldn't be surprised if it
turns out that its the "while (i++ < len)" backedge in wi::copy that is now
threaded differently and allows the "used uninitialized" to correctly identify
the problem (which may have been silenced by the earlier UB writes to memory).

Reply via email to