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

Reply via email to