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