On 13/11/26 11:07 AM, Avinash Jayakar wrote:
> 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?

The issue with using this macro is that once we define it, then the code
in ira_tune_allocno_costs() which is protected by this macro gets exercised
but we don't want that to happen for powerpc.

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

The original intent of this testcase is to check that r2 is available for 
allocation
when pcrel is enabled. With r2 being deprioritized, it gets allocated only
after all the non-volatile registers. So, we have to make all the non-volatile
registers fixed.

I can write another testcase (this same one actually without the -ffixed's)
to confirm that r2 is not allocated.

-Surya

> 
> 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