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. */
