On 12/11/26 11:34 AM, Richard Biener wrote:
>
>
>> Am 12.11.2026 um 06:38 schrieb Surya Kumari Jangala <[email protected]>:
>>
>> rs6000: De-prioritize r2 register allocation on PowerPC with pcrel
>>
>> On PowerPC with PC-relative addressing (pcrel), r2 is not fixed and
>> becomes available for general register allocation.
>>
>> This patch introduces a new target hook TARGET_ALLOCNO_HARD_REG_COST
>> that allows targets to add cost penalties to specific registers, causing
>> register allocator to deprioritize them without making them completely
>> unavailable (as fixed_regs would).
>>
>> For PowerPC, this hook adds a penalty to r2 that exceeds callee-save
>> costs, ensuring r2 is chosen only after all other general-purpose
>> registers (both caller-saved and callee-saved) have been exhausted.
>
> Isn’t that what reg-alloc-order is for?
On PowerPC, we don't define HONOR_REG_ALLOC_ORDER - similar to other platforms
like aarch64, which explicitly documents in gcc/config/aarch64/aarch64.cc:
"IRA's model of register save/restore costs is much more sophisticated
than the model that a simple ordering could provide. We leave
HONOR_REG_ALLOC_ORDER undefined so that we can get the full benefit of
IRA's model."
The new hook provides a way to influence register allocation through costs
rather than ordering, which is more aligned with how IRA works. This allows
us to maintain the benefits of IRA's sophisticated analysis while still
achieving the goal of deprioritizing r2.
-Surya
>
>> The penalty is only applied when r2 is not fixed (i.e., in pcrel mode).
>>
>> gcc/
>> * target.def (allocno_hard_reg_cost): New target hook.
>> * doc/tm.texi: Regenerate.
>> * doc/tm.texi.in: Add TARGET_ALLOCNO_HARD_REG_COST reference.
>> * targhooks.cc (default_allocno_hard_reg_cost): New function.
>> * targhooks.h (default_allocno_hard_reg_cost): Declare.
>> * ira-color.cc (assign_hard_reg): Call targetm.allocno_hard_reg_cost to
>> add register-specific cost penalties.
>> (improve_allocation): Likewise.
>> * config/rs6000/rs6000.cc (rs6000_allocno_hard_reg_cost): New function.
>> (TARGET_ALLOCNO_HARD_REG_COST): Define.
>>
>> gcc/testsuite/
>> * gcc.target/powerpc/pr110320-1.c: Update dg-options. Update comment.
>> ---
>> gcc/config/rs6000/rs6000.cc | 24 +++++++++++++++++++
>> gcc/doc/tm.texi | 18 ++++++++++++++
>> gcc/doc/tm.texi.in | 2 ++
>> gcc/ira-color.cc | 22 +++++++++++++++++
>> gcc/target.def | 20 ++++++++++++++++
>> gcc/targhooks.cc | 6 +++++
>> gcc/targhooks.h | 1 +
>> gcc/testsuite/gcc.target/powerpc/pr110320-1.c | 8 +++----
>> 8 files changed, 97 insertions(+), 4 deletions(-)
>>
>> diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
>> index 9581d122a9d..c8025cba97b 100644
>> --- a/gcc/config/rs6000/rs6000.cc
>> +++ b/gcc/config/rs6000/rs6000.cc
>> @@ -1122,6 +1122,7 @@ static tree rs6000_handle_struct_attribute (tree *,
>> tree, tree, int, bool *);
>> static tree rs6000_builtin_vectorized_libmass (combined_fn, tree, tree);
>> static void rs6000_emit_set_long_const (rtx, HOST_WIDE_INT, int * = nullptr);
>> static int rs6000_memory_move_cost (machine_mode, reg_class_t, bool);
>> +static int rs6000_allocno_hard_reg_cost (int, machine_mode, int);
>> static bool rs6000_debug_rtx_costs (rtx, machine_mode, int, int, int *,
>> bool);
>> static int rs6000_debug_address_cost (rtx, machine_mode, addr_space_t,
>> bool);
>> @@ -1495,6 +1496,8 @@ static const scoped_attribute_specs *const
>> rs6000_attribute_table[] =
>> #define TARGET_REGISTER_MOVE_COST rs6000_register_move_cost
>> #undef TARGET_MEMORY_MOVE_COST
>> #define TARGET_MEMORY_MOVE_COST rs6000_memory_move_cost
>> +#undef TARGET_ALLOCNO_HARD_REG_COST
>> +#define TARGET_ALLOCNO_HARD_REG_COST rs6000_allocno_hard_reg_cost
>> #undef TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS
>> #define TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS \
>> rs6000_ira_change_pseudo_allocno_class
>> @@ -23105,6 +23108,27 @@ rs6000_memory_move_cost (machine_mode mode,
>> reg_class_t rclass,
>> return ret;
>> }
>>
>> +/* Implement TARGET_ALLOCNO_HARD_REG_COST.
>> +
>> + Penalise r2 (TOC register) so the IRA cost-based allocator chooses
>> + it last among all GPRs, after both caller-saved and callee-saved
>> + registers. The penalty uses the same memory-move cost basis as the
>> + callee-save cost computation in assign_hard_reg, multiplied by 2 to
>> + guarantee it strictly exceeds any callee-save cost even when the
>> + allocno's usage frequency equals the function entry/exit frequency. */
>> +
>> +static int
>> +rs6000_allocno_hard_reg_cost (int hard_regno, machine_mode mode, int freq)
>> +{
>> + if (fixed_regs[2] || hard_regno != 2)
>> + return 0;
>> +
>> + reg_class_t rclass = REGNO_REG_CLASS (hard_regno);
>> + return ((ira_memory_move_cost[mode][rclass][0]
>> + + ira_memory_move_cost[mode][rclass][1])
>> + * freq * 2);
>> +}
>> +
>> /* Implement TARGET_IRA_CHANGE_PSEUDO_ALLOCNO_CLASS.
>>
>> The register allocator chooses GEN_OR_VSX_REGS for the allocno
>> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
>> index 65cf4c640e7..b5b7d79278b 100644
>> --- a/gcc/doc/tm.texi
>> +++ b/gcc/doc/tm.texi
>> @@ -7169,6 +7169,24 @@ allocator has not yet committed to using both
>> strategies.
>> The default implementation returns 0.
>> @end deftypefn
>>
>> +@deftypefn {Target Hook} int TARGET_ALLOCNO_HARD_REG_COST (int
>> @var{hard_regno}, machine_mode @var{mode}, int @var{freq})
>> +Return an additional cost penalty for the IRA register allocator to apply
>> +to hard register @var{hard_regno} when costing an allocno. This hook allows
>> +targets to deprioritize specific registers in the allocation order without
>> +making them unavailable (as @code{fixed_regs} would).
>> +@var{mode} is the mode of the allocno and @var{freq} is its usage
>> +frequency (@code{ALLOCNO_FREQ}).
>> +
>> +Use this hook when a register should remain available for allocation but
>> +be chosen only as a last resort.
>> +
>> +The cost value should be chosen to achieve the desired prioritization
>> relative
>> +to other allocation costs in the target. For instance, to deprioritize a
>> +register below all other general-purpose registers including callee-saved
>> +registers, the penalty should exceed typical callee-save costs.
>> +Return 0 (the default) to apply no penalty.
>> +@end deftypefn
>> +
>> @defmac BRANCH_COST (@var{speed_p}, @var{predictable_p})
>> A C expression for the cost of a branch instruction. A value of 1 is
>> the default; other values are interpreted relative to that. Parameter
>> diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
>> index 6b8203c5bed..039b5909d5f 100644
>> --- a/gcc/doc/tm.texi.in
>> +++ b/gcc/doc/tm.texi.in
>> @@ -4634,6 +4634,8 @@ These macros are obsolete, new ports should use the
>> target hook
>>
>> @hook TARGET_FRAME_ALLOCATION_COST
>>
>> +@hook TARGET_ALLOCNO_HARD_REG_COST
>> +
>> @defmac BRANCH_COST (@var{speed_p}, @var{predictable_p})
>> A C expression for the cost of a branch instruction. A value of 1 is
>> the default; other values are interpreted relative to that. Parameter
>> diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc
>> index 866d5b6bec3..807bc19f1cf 100644
>> --- a/gcc/ira-color.cc
>> +++ b/gcc/ira-color.cc
>> @@ -2292,6 +2292,19 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
>> cost += spill_cost;
>> full_cost += spill_cost;
>> }
>> +
>> + /* Allow the target to add an unconditional cost penalty to
>> + specific hard registers. */
>> + {
>> + int add_cost = targetm.allocno_hard_reg_cost (hard_regno, mode,
>> + ALLOCNO_FREQ (a));
>> + if (add_cost != 0)
>> + {
>> + cost += add_cost;
>> + full_cost += add_cost;
>> + }
>> + }
>> +
>> if (min_cost > cost)
>> min_cost = cost;
>> if (min_full_cost > full_cost)
>> @@ -3345,6 +3358,15 @@ improve_allocation (void)
>> costs[hregno] += add_cost;
>> }
>>
>> + /* Allow the target to add an unconditional cost penalty to
>> + specific hard registers. */
>> + {
>> + int add_cost = targetm.allocno_hard_reg_cost (hregno, mode,
>> + ALLOCNO_FREQ (a));
>> + if (add_cost != 0)
>> + costs[hregno] += add_cost;
>> + }
>> +
>> costs[hregno] -= base_cost;
>> if (costs[hregno] < 0)
>> try_p = true;
>> diff --git a/gcc/target.def b/gcc/target.def
>> index 877bd166b23..ac5053fb6dd 100644
>> --- a/gcc/target.def
>> +++ b/gcc/target.def
>> @@ -4004,6 +4004,26 @@ The default implementation returns 0.",
>> int, (frame_cost_type cost_type, const HARD_REG_SET &allocated_callee_regs),
>> default_frame_allocation_cost)
>>
>> +DEFHOOK
>> +(allocno_hard_reg_cost,
>> + "Return an additional cost penalty for the IRA register allocator to
>> apply\n\
>> +to hard register @var{hard_regno} when costing an allocno. This hook
>> allows\n\
>> +targets to deprioritize specific registers in the allocation order
>> without\n\
>> +making them unavailable (as @code{fixed_regs} would).\n\
>> +@var{mode} is the mode of the allocno and @var{freq} is its usage\n\
>> +frequency (@code{ALLOCNO_FREQ}).\n\
>> +\n\
>> +Use this hook when a register should remain available for allocation but\n\
>> +be chosen only as a last resort.\n\
>> +\n\
>> +The cost value should be chosen to achieve the desired prioritization
>> relative\n\
>> +to other allocation costs in the target. For instance, to deprioritize a\n\
>> +register below all other general-purpose registers including callee-saved\n\
>> +registers, the penalty should exceed typical callee-save costs.\n\
>> +Return 0 (the default) to apply no penalty.",
>> + int, (int hard_regno, machine_mode mode, int freq),
>> + default_allocno_hard_reg_cost)
>> +
>> DEFHOOK
>> (use_by_pieces_infrastructure_p,
>> "GCC will attempt several strategies when asked to copy between\n\
>> diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
>> index 727a0f2ba32..f7f53a942ea 100644
>> --- a/gcc/targhooks.cc
>> +++ b/gcc/targhooks.cc
>> @@ -2167,6 +2167,12 @@ default_frame_allocation_cost (frame_cost_type, const
>> HARD_REG_SET &)
>> return 0;
>> }
>>
>> +int
>> +default_allocno_hard_reg_cost (int, machine_mode, int)
>> +{
>> + return 0;
>> +}
>> +
>> /* The default implementation of TARGET_SLOW_UNALIGNED_ACCESS. */
>>
>> bool
>> diff --git a/gcc/targhooks.h b/gcc/targhooks.h
>> index d5f4548fc7c..b6ccf8bc248 100644
>> --- a/gcc/targhooks.h
>> +++ b/gcc/targhooks.h
>> @@ -245,6 +245,7 @@ extern int default_callee_save_cost (spill_cost_type,
>> unsigned int,
>> const HARD_REG_SET &, bool);
>> extern int default_frame_allocation_cost (frame_cost_type,
>> const HARD_REG_SET &);
>> +extern int default_allocno_hard_reg_cost (int, machine_mode, int);
>> extern bool default_slow_unaligned_access (machine_mode, unsigned int);
>> extern HOST_WIDE_INT default_estimated_poly_value (poly_int64,
>> poly_value_estimate_kind);
>> diff --git a/gcc/testsuite/gcc.target/powerpc/pr110320-1.c
>> b/gcc/testsuite/gcc.target/powerpc/pr110320-1.c
>> index a87b37c52be..34ea8c2de26 100644
>> --- a/gcc/testsuite/gcc.target/powerpc/pr110320-1.c
>> +++ b/gcc/testsuite/gcc.target/powerpc/pr110320-1.c
>> @@ -1,15 +1,15 @@
>> /* PR target/110320 */
>> /* { dg-require-effective-target powerpc_elfv2 } */
>> /* { dg-require-effective-target powerpc_pcrel } */
>> -/* { dg-options "-O2 -mdejagnu-cpu=power10 -ffixed-r0 -ffixed-r11
>> -ffixed-r12" } */
>> +/* { dg-options "-O2 -mdejagnu-cpu=power10 -ffixed-r0 -ffixed-r11
>> -ffixed-r12 -ffixed-r13 -ffixed-r14 -ffixed-r15 -ffixed-r16 -ffixed-r17
>> -ffixed-r18 -ffixed-r19 -ffixed-r20 -ffixed-r21 -ffixed-r22 -ffixed-r23
>> -ffixed-r24 -ffixed-r25 -ffixed-r26 -ffixed-r27 -ffixed-r28 -ffixed-r29
>> -ffixed-r30 -ffixed-r31" } */
>>
>> /* Ensure we use r2 as a normal volatile register for the code below.
>> The test case ensures all of the parameter registers r3 - r10 are used
>> and needed after we compute the expression "x + y" which requires a
>> temporary. The -ffixed-r* options disallow using the other volatile
>> - registers r0, r11 and r12. That leaves RA to choose from r2 and the more
>> - expensive non-volatile registers for the temporary to be assigned to, and
>> - RA will always chooses the cheaper volatile r2 register. */
>> + registers r0, r11 and r12, as well as all callee-saved registers
>> r13-r31. │
>> + That leaves RA to choose r2, demonstrating that r2 is available for
>> │
>> + allocation when not fixed (as in pcrel mode). */
>>
>> extern long bar (long, long, long, long, long, long, long, long *);
>>
>> --
>> 2.52.0
>>