https://gcc.gnu.org/g:aedbb777b4e1547d489b78d8a873c52225053c83
commit aedbb777b4e1547d489b78d8a873c52225053c83 Author: Alexandre Oliva <[email protected]> Date: Thu Nov 20 22:57:41 2025 -0300 ira: sort allocno_hard_regs by regset Using hashes of data structures for tie breaking makes sorting dependent on type sizes, padding, and endianness, i.e., unstable across different hosts. ira-color.cc's allocno_hard_regs_compare does that, and it causes different register allocations to be chosen for the same target depending on the host. That's undesirable. Compare the HARD_REG_SETs directly instead, looking for the lowest-numbered difference register to use as the tie breaker for the cost compare. With a hardware implementation of ctz, this is likely faster than the hash used to break ties before. for gcc/ChangeLog PR rtl-optimization/122767 * ira-color.cc (allocno_hard_regs_compare): Break ties using... * hard-reg-set.h (hard_reg_set_first_diff): ... this. New HARD_REG_SET API entry point. Diff: --- gcc/hard-reg-set.h | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/ira-color.cc | 6 +++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/gcc/hard-reg-set.h b/gcc/hard-reg-set.h index 0d03aed5128f..e33c8e334d39 100644 --- a/gcc/hard-reg-set.h +++ b/gcc/hard-reg-set.h @@ -197,6 +197,28 @@ hard_reg_set_popcount (const_hard_reg_set x) return popcount_hwi (x); } +/* Return 0 if there aren't any differences between X and Y after the first + SKIP registers, or 1 + the register number of the lowest-numbered + difference, negated if it's set in Y. The return value is suitable for + qsort. */ +inline int +hard_reg_set_first_diff (const_hard_reg_set x, const_hard_reg_set y, + unsigned skip) +{ + if (skip >= UHOST_BITS_PER_WIDE_INT) + return 0; + const HARD_REG_ELT_TYPE full_mask = -1; + HARD_REG_ELT_TYPE mask = full_mask << skip; + HARD_REG_ELT_TYPE dif = (x ^ y) & mask; + if (dif == 0) + return 0; + int bit = ctz_hwi (dif); + int regp1 = bit + 1; + if (y & (HARD_CONST (1) << bit)) + return -regp1; + return regp1; +} + #else inline void @@ -269,6 +291,34 @@ hard_reg_set_popcount (const_hard_reg_set x) count += popcount_hwi (x.elts[i]); return count; } + +/* Return 0 if there aren't any differences between X and Y after the first + SKIP registers, or 1 + the register number of the lowest-numbered + difference, negated if it's set in Y. The return value is suitable for + qsort. */ +inline int +hard_reg_set_first_diff (const_hard_reg_set x, const_hard_reg_set y, + unsigned skip) +{ + const HARD_REG_ELT_TYPE full_mask = -1; + HARD_REG_ELT_TYPE mask = full_mask << (skip % UHOST_BITS_PER_WIDE_INT); + for (unsigned int i = skip / UHOST_BITS_PER_WIDE_INT; + i < ARRAY_SIZE (x.elts); ++i) + { + HARD_REG_ELT_TYPE dif = (x.elts[i] ^ y.elts[i]) & mask; + if (dif == 0) + { + mask = full_mask; + continue; + } + int bit = ctz_hwi (dif); + int regp1 = bit + 1 + i * UHOST_BITS_PER_WIDE_INT; + if (y.elts[i] & (HARD_CONST (1) << bit)) + return -regp1; + return regp1; + } + return 0; +} #endif /* Iterator for hard register sets. */ diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc index fa2ea61cadf3..4ee2a65e2911 100644 --- a/gcc/ira-color.cc +++ b/gcc/ira-color.cc @@ -310,7 +310,11 @@ allocno_hard_regs_compare (const void *v1p, const void *v2p) return 1; else if (hv2->cost < hv1->cost) return -1; - return SORTGT (allocno_hard_regs_hasher::hash(hv2), allocno_hard_regs_hasher::hash(hv1)); + + /* Break ties using the HARD_REG_SETs themselves. Avoid influencing sorting + by such host features as word size and alignment, looking for the + lowest-numbered hard register difference. */ + return hard_reg_set_first_diff (hv1->set, hv2->set, 0); }
