[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