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

commit a4bbbe808a0bc6d7c6abad41af85b0ab8494fde8
Author: Alexandre Oliva <[email protected]>
Date:   Wed Nov 19 21:22:49 2025 -0300

    ira: stabilize allocation across word size

Diff:
---
 gcc/ira-color.cc | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 165 insertions(+), 1 deletion(-)

diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc
index fa2ea61cadf3..f7b143553929 100644
--- a/gcc/ira-color.cc
+++ b/gcc/ira-color.cc
@@ -217,7 +217,171 @@ struct allocno_hard_regs_hasher : nofree_ptr_hash 
<allocno_hard_regs>
 inline hashval_t
 allocno_hard_regs_hasher::hash (const allocno_hard_regs *hv)
 {
-  return iterative_hash (&hv->set, sizeof (HARD_REG_SET), 0);
+  /* Normalize the HARD_REG_SET to byte-little-endian 32-bit words, for stable
+     hashing and register allocation.  Prefer little endian because, on
+     little-endian hosts, we can just take a prefix of HARD_REG_SET if the
+     trailing 32-bit word is unused.  */
+#if FIRST_PSEUDO_REGISTER <= HOST_BITS_PER_WIDEST_FAST_INT
+  auto *const elts = &hv->set.elts;
+  const size_t ns = 1;
+#else
+  auto &elts = hv->set.elts;
+  const size_t ns = ARRAY_SIZE (elts);
+#endif
+  /* If there are excess bits in the set, take the next bit, otherwise
+     assume it to be zero.  */
+  bool xbit = ((FIRST_PSEUDO_REGISTER
+               < ns * sizeof (*elts) * CHAR_BIT)
+              ? TEST_HARD_REG_BIT (hv->set, FIRST_PSEUDO_REGISTER)
+              : 0);
+
+  if (sizeof (uint32_t) * CHAR_BIT == 32
+      && (HOST_BITS_PER_WIDEST_FAST_INT % 32) == 0)
+    {
+      typedef uint32_t T;
+      auto bswp = [](T v) {
+       return (0
+               | ((v & 0xff000000) >> 24)
+               | ((v & 0x00ff0000) >>  8)
+               | ((v & 0x0000ff00) <<  8)
+               | ((v & 0x000000ff) << 24)
+               );
+      };
+      const size_t t = sizeof (T);
+      const size_t s = t * CHAR_BIT;
+      const size_t n = (FIRST_PSEUDO_REGISTER + s - 1) / s;
+#ifndef WORDS_BIGENDIAN
+# define WORDS_BIGENDIAN 0
+#endif
+      if (!WORDS_BIGENDIAN)
+       /* When host is little endian, we can just take a prefix of
+          HARD_REG_SET, dropping any trailing unused 32-bit words.  On 64-bit
+          hosts, compiling for the same target, we may get an extra half
+          64-bit word due to the choice of a larger elts size, and that would
+          affect hashing.  */
+       return iterative_hash (elts, n * t, 0);
+      else
+       {
+         const size_t m = HOST_BITS_PER_WIDEST_FAST_INT / s;
+         const size_t l = ns * m - n;
+         T normal[n];
+         /* First normalize all ELTS that are fully used.  */
+         size_t k = 0;
+         for (size_t i = 0; i < ns - (l != 0); i++)
+           {
+             auto elt = elts[i];
+             for (size_t j = 0; j < m; j++, k++)
+               {
+                 T v = elt;
+                 (elt >>= (s - 1)) >>= 1;
+                 T w = bswp (v);
+                 normal[k] = w;
+               }
+             gcc_checking_assert (!elt);
+           }
+         /* If there's a partially-used ELTS member at the end, normalize it
+            as well.  */
+         if (l != 0)
+           {
+             const size_t i = ns - (l> 0);
+             auto elt = elts[i];
+             for (size_t j = 0; j < l; j++, k++)
+               {
+                 T v = elt;
+                 (elt >>= (s - 1)) >>= 1;
+                 T w = bswp (v);
+                 normal[k] = w;
+               }
+             /* Negated sets may have left-overs half-words that are -1.  */
+             gcc_checking_assert (!xbit
+                                  ? !elt
+                                  : elt == ((~(elt * 0)
+                                             << (l * s))
+                                            >> (l * s)));
+           }
+         gcc_checking_assert (k == n);
+         return iterative_hash (normal, n * t, 0);
+       }
+    }
+  else if ((HOST_BITS_PER_WIDEST_FAST_INT % 8) == 0)
+    {
+      typedef unsigned char T;
+      const size_t t = 1;
+      const size_t s = t * 8;
+      /* Aim for similarity with 32-bit little-endian words WRT hashing, so
+        compute a total size that's a multiple of 32 bits.  */
+      const size_t n = (FIRST_PSEUDO_REGISTER + 4 * s - 1) / (4 * s) * 4;
+      const size_t m = HOST_BITS_PER_WIDEST_FAST_INT / s;
+      const size_t l = ns * m - n;
+      T normal[n];
+      /* First normalize all ELTS that are fully used.  */
+      size_t k = 0;
+      for (size_t i = 0; i < ns - (l > 0); i++)
+       {
+         auto elt = elts[i];
+         for (size_t j = 0; j < m; j++, k++)
+           {
+             T v = elt;
+             (elt >>= (s - 1)) >>= 1;
+             T w = v;
+             normal[k] = w;
+           }
+         gcc_checking_assert (!elt);
+       }
+      /* If there's a partially-used ELTS member at the end, normalize it
+        as well.  */
+      if (l != 0)
+       {
+         const size_t i = ns - (l > 0);
+         auto elt = elts[i];
+         for (size_t j = 0; j < l; j++, k++)
+           {
+             T v = elt;
+             (elt >>= (s - 1)) >>= 1;
+             T w = v;
+             normal[k] = w;
+           }
+         /* Negated sets may have left-overs half-words that are -1.  */
+         gcc_checking_assert (!xbit
+                              ? !elt
+                              : elt == ((~(elt * 0)
+                                         << (l * s))
+                                        >> (l * s)));
+       }
+      gcc_checking_assert (k == n);
+      return iterative_hash (normal, n * t, 0);
+    }
+  else
+    {
+      /* Cover unusual architectures.  */
+      typedef unsigned char T;
+      const size_t t = 1;
+      const size_t s = t * 8;
+      /* Aim for similarity with 32-bit little-endian words WRT hashing, so
+        compute a total size that's a multiple of 32 bits.  */
+      const size_t n = (FIRST_PSEUDO_REGISTER + 4 * s - 1) / (4 * s) * 4;
+      const size_t nb = n * s;
+      T normal[n] = {};
+      size_t k = 0;
+      for (size_t i = 0; i < FIRST_PSEUDO_REGISTER; k = ++i)
+       if (TEST_HARD_REG_BIT (hv->set, i))
+         normal[i/s] |= (1 << (i % s));
+       else
+         normal[i/s] &= ~(1 << (i % s));
+      if (k < nb)
+       {
+         /* If there are excess bits in the set, take the next bit, otherwise
+            assume it to be zero.  */
+         T bit = xbit;
+         for (size_t i = k; k < nb; k = ++i)
+           if (bit)
+             normal[i/s] |= 1 << (i % s);
+           else
+             normal[i/s] &= ~(1 << (i % s));
+       }
+      gcc_checking_assert (k == nb);
+      return iterative_hash (normal, n * t, 0);
+    }
 }
 
 /* Compares allocno hard registers V1 and V2.  */

Reply via email to