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 *);
>  

Reply via email to