[gcc(refs/users/aoliva/heads/testme)] ira: stabilize allocation across word size

Alexandre Oliva aoliva@gcc.gnu.org
Thu Nov 20 07:30:42 GMT 2025


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

commit a4bbbe808a0bc6d7c6abad41af85b0ab8494fde8
Author: Alexandre Oliva <oliva@gnu.org>
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.  */


More information about the Gcc-cvs mailing list