The arm backend implements neither register-allocator cost hook added by
r15-7895-gb191e8bdecf, so IRA charges nothing for the frame a spill slot
needs.  Price that sub sp, add sp as a core register move, as aarch64 does
with regmove_cost->GP2GP, and a callee-saved register at the memory cost
of its save or restore alone, since a push or pop adjusts SP as it
transfers.  The transfer is not discounted: a cheaper price lets values
that cross cold calls take every free callee-saved register before
long-lived loop values are allocated.  Thumb-1 keeps the default for
r8-r11, which PUSH and POP cannot name.  -Os code is unchanged: IRA does
not consult TARGET_CALLEE_SAVE_COST there and the frame cost is zero.

vmlinux .text, github.com/raspberrypi/linux f5a99b953, arm-none-eabi
crosses built from the same base, SOURCE_DATE_EPOCH and KBUILD_BUILD_*
pinned:

  bcmrpi_defconfig  ARMv6 -O2  10879804 -> 10842296  -37508 B (-0.34%)
  bcm2709_defconfig ARMv7 -O2  11395700 -> 11352180  -43520 B (-0.38%)

On gcc.c-torture/execute under qemu, -marm -march=armv6zk executes 1.3%
fewer instructions over the 60 tests whose code changes (5 more, 23
fewer, worst +0.03%); Cortex-M33 is flat.

Regression tested on arm-none-eabi against a pristine compiler built
from the same base: arm.exp, simd.exp, mve.exp, acle.exp, cmse.exp and
advsimd-intrinsics.exp with the default options and with -mthumb
-march=armv7-a -mfloat-abi=hard -mfpu=neon-fp-armv8 -mtune=cortex-a9,
gcc.c-torture/compile.exp, and gcc.c-torture/execute.exp at -O2 under
qemu for -marm -march=armv6zk and -mthumb -mcpu=cortex-m4; no new
failures.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * config/arm/arm.cc (arm_callee_save_cost): New function.
        (arm_frame_allocation_cost): New function.
        (TARGET_CALLEE_SAVE_COST): Define.
        (TARGET_FRAME_ALLOCATION_COST): Define.

gcc/testsuite/ChangeLog:

        * gcc.target/arm/callee-save-cost-1.c: New test.
        * gcc.target/arm/callee-save-cost-mprofile-1.c: New test.
        * gcc.target/arm/callee-save-cost-vfp-1.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
Changes in v3:

v2 priced a register joining an existing push or pop at nregs, the number
of words, where the hook returns a cost on the memory-move scale.  At -O2
every callee-saved register then looked almost free, and values crossing
cold calls took them before long-lived loop values were allocated.  In
PiTubeDirect's 6502 interpreter (lib6502.c, M6502_run) a loop-invariant
address lost its register and was reloaded from the literal pool at 274
more sites in the dispatch loop: 4.2% more instructions executed under
qemu.

v3 prices the save and restore at the memory cost and keeps only the
frame allocation that a push or pop avoids.  The 6502, Z80 and RV32
emulator cores now compile identically to trunk.  Kernel .text drops by
0.34% (ARMv6) and 0.38% (ARMv7) instead of 1.81% and 1.87%.  The
dlstp-compile-asm-2.c change and cross_call_live_si are gone: v3 no longer
changes those allocations.  The tests pin the -mtune their architecture
selects and accept the (PLT) suffix.

OK for trunk?

 gcc/config/arm/arm.cc                         | 39 +++++++++++++++++
 .../gcc.target/arm/callee-save-cost-1.c       | 25 +++++++++++
 .../arm/callee-save-cost-mprofile-1.c         | 42 +++++++++++++++++++
 .../gcc.target/arm/callee-save-cost-vfp-1.c   | 42 +++++++++++++++++++
 4 files changed, 148 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/arm/callee-save-cost-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/callee-save-cost-mprofile-1.c
 create mode 100644 gcc/testsuite/gcc.target/arm/callee-save-cost-vfp-1.c

diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 33b64995442..ffcef9634d0 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -187,6 +187,9 @@ static int arm_insn_cost (rtx_insn *, bool);
 static int arm_address_cost (rtx, machine_mode, addr_space_t, bool);
 static int arm_register_move_cost (machine_mode, reg_class_t, reg_class_t);
 static int arm_memory_move_cost (machine_mode, reg_class_t, bool);
+static int arm_callee_save_cost (spill_cost_type, unsigned int, machine_mode,
+                                unsigned int, int, const HARD_REG_SET &, bool);
+static int arm_frame_allocation_cost (frame_cost_type, const HARD_REG_SET &);
 static void emit_constant_insn (rtx cond, rtx pattern);
 static rtx_insn *emit_set_insn (rtx, rtx);
 static void arm_add_cfa_adjust_cfa_note (rtx, int, rtx, rtx);
@@ -474,6 +477,12 @@ static const scoped_attribute_specs *const 
arm_attribute_table[] =
 #undef TARGET_MEMORY_MOVE_COST
 #define TARGET_MEMORY_MOVE_COST arm_memory_move_cost
 
+#undef TARGET_CALLEE_SAVE_COST
+#define TARGET_CALLEE_SAVE_COST arm_callee_save_cost
+
+#undef TARGET_FRAME_ALLOCATION_COST
+#define TARGET_FRAME_ALLOCATION_COST arm_frame_allocation_cost
+
 #undef TARGET_ENCODE_SECTION_INFO
 #define TARGET_ENCODE_SECTION_INFO  arm_encode_section_info
 
@@ -12677,6 +12686,36 @@ arm_memory_move_cost (machine_mode mode, reg_class_t 
rclass,
     }
 }
 
+/* Implement TARGET_CALLEE_SAVE_COST.  */
+
+static int
+arm_callee_save_cost (spill_cost_type spill_type, unsigned int regno,
+                     machine_mode mode, unsigned int nregs, int mem_cost,
+                     const HARD_REG_SET &allocated_callee_regs,
+                     bool existing_spills_p)
+{
+  /* Only r0-r7 and LR may be named in a Thumb-1 PUSH or POP reglist.  */
+  if (TARGET_THUMB1 && regno > LAST_LO_REGNUM)
+    return default_callee_save_cost (spill_type, regno, mode, nregs, mem_cost,
+                                    allocated_callee_regs, existing_spills_p);
+
+  /* The push and the pop adjust SP as they transfer, so the save needs no
+     frame allocation.  */
+  return mem_cost;
+}
+
+/* Implement TARGET_FRAME_ALLOCATION_COST.  Price the sub sp, add sp that a
+   spill slot needs as a core register move.  */
+
+static int
+arm_frame_allocation_cost (frame_cost_type, const HARD_REG_SET &)
+{
+  if (optimize_function_for_size_p (cfun))
+    return 0;
+
+  return arm_register_move_cost (word_mode, GENERAL_REGS, GENERAL_REGS);
+}
+
 /* Vectorizer cost model implementation.  */
 
 /* Implement targetm.vectorize.builtin_vectorization_cost.  */
diff --git a/gcc/testsuite/gcc.target/arm/callee-save-cost-1.c 
b/gcc/testsuite/gcc.target/arm/callee-save-cost-1.c
new file mode 100644
index 00000000000..61671ccb5f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/callee-save-cost-1.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v7a_arm_ok } */
+/* { dg-options "-O2 -mtune=cortex-a53" } */
+/* { dg-add-options arm_arch_v7a_arm } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+extern int g (int);
+
+/*
+** cross_call_live:
+**     push\t{r4, r5, r6, lr}
+**     mov\tr4, r0
+**     add\tr0, r0, r1
+**     mov\tr5, r1
+**     bl\tg(?:\(PLT\))?
+**     add\tr0, r4, r0
+**     sub\tr0, r0, r5
+**     pop\t{r4, r5, r6, pc}
+*/
+int
+cross_call_live (int a, int b)
+{
+  int t = g (a + b);
+  return t + a - b;
+}
diff --git a/gcc/testsuite/gcc.target/arm/callee-save-cost-mprofile-1.c 
b/gcc/testsuite/gcc.target/arm/callee-save-cost-mprofile-1.c
new file mode 100644
index 00000000000..e4d72e61b62
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/callee-save-cost-mprofile-1.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v7em_hard_ok } */
+/* { dg-options "-O2 -mtune=cortex-m4" } */
+/* { dg-add-options arm_arch_v7em_hard } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+extern float fwork (float);
+
+/*
+** cross_call_live_sf:
+**     push\t{r3, lr}
+**     vpush.64\t{d8, d9, d10}
+**     vmov.f32\ts18, s1
+**     vmov.f32\ts17, s2
+**     vmov.f32\ts20, s3
+**     vmov.f32\ts19, s4
+**     vmov.f32\ts21, s0
+**     bl\tfwork(?:\(PLT\))?
+**     vmov.f32\ts16, s0
+**     vmov.f32\ts0, s18
+**     bl\tfwork(?:\(PLT\))?
+**     vmov.f32\ts15, s0
+**     vmov.f32\ts0, s17
+**     vadd.f32\ts16, s16, s15
+**     bl\tfwork(?:\(PLT\))?
+**     vadd.f32\ts0, s16, s0
+**     vadd.f32\ts0, s0, s21
+**     vadd.f32\ts0, s0, s18
+**     vadd.f32\ts0, s0, s17
+**     vadd.f32\ts0, s0, s20
+**     vadd.f32\ts0, s0, s19
+**     vldm\tsp!, {d8-d10}
+**     pop\t{r3, pc}
+*/
+float
+cross_call_live_sf (float a, float b, float c, float d, float e)
+{
+  float r = fwork (a);
+  r += fwork (b);
+  r += fwork (c);
+  return r + a + b + c + d + e;
+}
diff --git a/gcc/testsuite/gcc.target/arm/callee-save-cost-vfp-1.c 
b/gcc/testsuite/gcc.target/arm/callee-save-cost-vfp-1.c
new file mode 100644
index 00000000000..9d16f7a579e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/callee-save-cost-vfp-1.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v7a_fp_hard_ok } */
+/* { dg-options "-O2 -marm -mtune=cortex-a53" } */
+/* { dg-add-options arm_arch_v7a_fp_hard } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+extern double g (double);
+
+/*
+** cross_call_live_df:
+**     push\t{r4, lr}
+**     vpush.64\t{d8, d9, d10, d11, d12, d13}
+**     vmov.f64\td10, d1
+**     vmov.f64\td9, d2
+**     vmov.f64\td13, d3
+**     vmov.f64\td11, d4
+**     vmov.f64\td12, d0
+**     bl\tg(?:\(PLT\))?
+**     vmov.f64\td8, d0
+**     vmov.f64\td0, d10
+**     bl\tg(?:\(PLT\))?
+**     vmov.f64\td7, d0
+**     vmov.f64\td0, d9
+**     vadd.f64\td8, d8, d7
+**     bl\tg(?:\(PLT\))?
+**     vadd.f64\td0, d8, d0
+**     vadd.f64\td0, d0, d12
+**     vadd.f64\td0, d0, d10
+**     vadd.f64\td0, d0, d9
+**     vadd.f64\td0, d0, d13
+**     vadd.f64\td0, d0, d11
+**     vldm\tsp!, {d8-d13}
+**     pop\t{r4, pc}
+*/
+double
+cross_call_live_df (double a, double b, double c, double d, double e)
+{
+  double r = g (a);
+  r += g (b);
+  r += g (c);
+  return r + a + b + c + d + e;
+}
-- 
2.55.0

Reply via email to