On Thu, 2026-11-12 at 11:06 +0530, Surya Kumari Jangala wrote:
> 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
s390 makes use of a macro IRA_HARD_REGNO_ADD_COST_MULTIPLIER which does
similar to what is being tried to achieve here.
Is there a reason to not use that?
> 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.
> 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" } */
>
I think r13 is fixed by default
> /* 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
> │
The pipe characters seem extra with blank spaces as well. Please
correct the comment format.
This test would now fix all other registers leaving r2 as the only
allocate-able register.
A test for the new behaviour can be added, where we force r2 to be the
last register that is allocated. For example in a loop, where
previously r2 was being allocated, but with this patch it is not.
Thanks,
Avinash
> + allocation when not fixed (as in pcrel mode). */
>
> extern long bar (long, long, long, long, long, long, long, long *);
>