arm_rtx_costs_internal returns false for a SImode (mult reg CONST_INT), so
the rtl-cost recursion charges the constant's materialization on top of
mult[0].simple.  expand_mult sizes synth_mult's budget from that cost, so a
constant that needs a literal pool raises the budget by that materialization
cost, and a long fully serial shift-add chain then beats a single MUL.  The
chain never materializes the constant, and in a loop the MUL's constant load
is hoisted, so that extra budget buys nothing.

Add a tune_params field for charging the constant as a cheap immediate and
set it on the 9e tune only: an unconditional version saved nothing on any
other tune and grew one object by 8 bytes on Cortex-A7, A9, A53 and M4.
x * 16777619 now becomes one MUL rather than a five-deep add-shift chain,
while 1-3 insn multiplies such as x * 7 and x * 100 are still synthesized.

Over gcc.c-torture/execute and gcc.dg/torture (3466 objects) at -O2
-mcpu=arm1176jzf-s, five objects change, .text shrinks by 44 bytes and none
grows.  Tested on arm-none-eabi with gcc.target/arm/arm.exp: no
pre-existing test changes status.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * config/arm/arm-protos.h (struct tune_params): Add
        mult_const_as_imm.
        * config/arm/arm.cc (arm_slowmul_tune, arm_fastmul_tune)
        (arm_strongarm_tune, arm_xscale_tune, arm_9e_tune)
        (arm_marvell_pj4_tune, arm_v6t2_tune, arm_cortex_tune)
        (arm_cortex_a8_tune, arm_cortex_a7_tune, arm_cortex_a15_tune)
        (arm_cortex_a35_tune, arm_cortex_a53_tune, arm_cortex_a57_tune)
        (arm_exynosm1_tune, arm_xgene1_tune, arm_cortex_a5_tune)
        (arm_cortex_a9_tune, arm_cortex_a12_tune, arm_cortex_a73_tune)
        (arm_v7m_tune, arm_cortex_m7_tune, arm_v6m_tune)
        (arm_fa726te_tune): Initialize mult_const_as_imm.
        (arm_rtx_costs_internal): Charge the constant operand of a SImode
        multiply as a cheap immediate when the tune requests it.

gcc/testsuite/ChangeLog:

        * gcc.target/arm/mult-cost-9e.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
 gcc/config/arm/arm-protos.h                 |  3 +
 gcc/config/arm/arm.cc                       | 79 ++++++++++++++-------
 gcc/testsuite/gcc.target/arm/mult-cost-9e.c | 29 ++++++++
 3 files changed, 87 insertions(+), 24 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/arm/mult-cost-9e.c

diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index b607cdb3fcc..ad07427b112 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -385,6 +385,9 @@ struct tune_params
   /* Depth of scheduling queue to check for L2 autoprefetcher.  */
   enum {SCHED_AUTOPREF_OFF, SCHED_AUTOPREF_RANK, SCHED_AUTOPREF_FULL}
     sched_autopref: 2;
+  /* Cost the constant operand of a multiply as an immediate.  */
+  enum {MULT_CONST_AS_IMM_FALSE, MULT_CONST_AS_IMM_TRUE}
+    mult_const_as_imm: 1;
 };
 
 /* Smash multiple fusion operations into a type that can be used for an
diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index 33b64995442..784e1136962 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -1897,7 +1897,8 @@ const struct tune_params arm_slowmul_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_fastmul_tune =
@@ -1919,7 +1920,8 @@ const struct tune_params arm_fastmul_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 /* StrongARM has early execution of branches, so a sequence that is worth
@@ -1944,7 +1946,8 @@ const struct tune_params arm_strongarm_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_xscale_tune =
@@ -1966,7 +1969,8 @@ const struct tune_params arm_xscale_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_9e_tune =
@@ -1988,7 +1992,8 @@ const struct tune_params arm_9e_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_TRUE
 };
 
 const struct tune_params arm_marvell_pj4_tune =
@@ -2010,7 +2015,8 @@ const struct tune_params arm_marvell_pj4_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_v6t2_tune =
@@ -2032,7 +2038,8 @@ const struct tune_params arm_v6t2_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 
@@ -2056,7 +2063,8 @@ const struct tune_params arm_cortex_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a8_tune =
@@ -2078,7 +2086,8 @@ const struct tune_params arm_cortex_a8_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a7_tune =
@@ -2100,7 +2109,8 @@ const struct tune_params arm_cortex_a7_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a15_tune =
@@ -2122,7 +2132,8 @@ const struct tune_params arm_cortex_a15_tune =
   tune_params::DISPARAGE_FLAGS_ALL,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_FULL
+  tune_params::SCHED_AUTOPREF_FULL,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a35_tune =
@@ -2144,7 +2155,8 @@ const struct tune_params arm_cortex_a35_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   FUSE_OPS (tune_params::FUSE_MOVW_MOVT),
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a53_tune =
@@ -2166,7 +2178,8 @@ const struct tune_params arm_cortex_a53_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   FUSE_OPS (tune_params::FUSE_MOVW_MOVT | tune_params::FUSE_AES_AESMC),
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a57_tune =
@@ -2188,7 +2201,8 @@ const struct tune_params arm_cortex_a57_tune =
   tune_params::DISPARAGE_FLAGS_ALL,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   FUSE_OPS (tune_params::FUSE_MOVW_MOVT | tune_params::FUSE_AES_AESMC),
-  tune_params::SCHED_AUTOPREF_FULL
+  tune_params::SCHED_AUTOPREF_FULL,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_exynosm1_tune =
@@ -2210,7 +2224,8 @@ const struct tune_params arm_exynosm1_tune =
   tune_params::DISPARAGE_FLAGS_ALL,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_xgene1_tune =
@@ -2232,7 +2247,8 @@ const struct tune_params arm_xgene1_tune =
   tune_params::DISPARAGE_FLAGS_ALL,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 /* Branches can be dual-issued on Cortex-A5, so conditional execution is
@@ -2257,7 +2273,8 @@ const struct tune_params arm_cortex_a5_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a9_tune =
@@ -2279,7 +2296,8 @@ const struct tune_params arm_cortex_a9_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a12_tune =
@@ -2301,7 +2319,8 @@ const struct tune_params arm_cortex_a12_tune =
   tune_params::DISPARAGE_FLAGS_ALL,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   FUSE_OPS (tune_params::FUSE_MOVW_MOVT),
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_cortex_a73_tune =
@@ -2323,7 +2342,8 @@ const struct tune_params arm_cortex_a73_tune =
   tune_params::DISPARAGE_FLAGS_ALL,
   tune_params::PREF_NEON_STRINGOPS_TRUE,
   FUSE_OPS (tune_params::FUSE_AES_AESMC | tune_params::FUSE_MOVW_MOVT),
-  tune_params::SCHED_AUTOPREF_FULL
+  tune_params::SCHED_AUTOPREF_FULL,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 /* armv7m tuning.  On Cortex-M4 cores for example, MOVW/MOVT take a single
@@ -2352,7 +2372,8 @@ const struct tune_params arm_v7m_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 /* Cortex-M7 tuning.  */
@@ -2376,7 +2397,8 @@ const struct tune_params arm_cortex_m7_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 /* The arm_v6m_tune is duplicated from arm_cortex_tune, rather than
@@ -2401,7 +2423,8 @@ const struct tune_params arm_v6m_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 const struct tune_params arm_fa726te_tune =
@@ -2423,7 +2446,8 @@ const struct tune_params arm_fa726te_tune =
   tune_params::DISPARAGE_FLAGS_NEITHER,
   tune_params::PREF_NEON_STRINGOPS_FALSE,
   tune_params::FUSE_NOTHING,
-  tune_params::SCHED_AUTOPREF_OFF
+  tune_params::SCHED_AUTOPREF_OFF,
+  tune_params::MULT_CONST_AS_IMM_FALSE
 };
 
 /* Auto-generated CPU, FPU and architecture tables.  */
@@ -11502,6 +11526,13 @@ arm_rtx_costs_internal (rtx x, enum rtx_code code, 
enum rtx_code outer_code,
            }
          if (speed_p)
            *cost += extra_cost->mult[0].simple;
+         /* Cost the constant as an immediate so that it does not inflate
+            synth_mult's budget for this multiply.  */
+         if (CONST_INT_P (XEXP (x, 1)) && current_tune->mult_const_as_imm)
+           {
+             *cost += COSTS_N_INSNS (1);
+             return true;
+           }
          return false;
        }
 
diff --git a/gcc/testsuite/gcc.target/arm/mult-cost-9e.c 
b/gcc/testsuite/gcc.target/arm/mult-cost-9e.c
new file mode 100644
index 00000000000..7afe4f62436
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/mult-cost-9e.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arm_ok } */
+/* { dg-skip-if "avoid conflicting multilib options" { *-*-* } { "-mthumb" } { 
"" } } */
+/* { dg-skip-if "do not override -mcpu" { *-*-* } { "-mcpu=*" } { 
"-mcpu=arm1176jzf-s" } } */
+/* { dg-skip-if "incompatible options" { arm*-*-* } { "-march=*" } { "" } } */
+/* { dg-options "-O2 -marm -mcpu=arm1176jzf-s" } */
+
+/* A costly constant must use MUL; short shift-add chains stay synthesized.  */
+
+unsigned
+fnv (unsigned x)
+{
+  return x * 16777619u;
+}
+
+unsigned
+by7 (unsigned x)
+{
+  return x * 7u;
+}
+
+unsigned
+by100 (unsigned x)
+{
+  return x * 100u;
+}
+
+/* { dg-final { scan-assembler-times "\\smul\\s" 1 } } */
+/* { dg-final { scan-assembler-times "\\srsb\\s" 1 } } */
-- 
2.55.0

Reply via email to