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: over gcc.c-torture/compile at -O2 an unconditional
version changes no object at all on Cortex-A7, A9, A53 or M4, and over a sweep
of 17 constants it costs Cortex-A7 8 bytes while saving arm1176jzf-s 16.
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/compile, gcc.c-torture/execute, gcc.dg/torture and
gcc.target/arm (5771 objects) at -O2 -marm -mcpu=arm1176jzf-s, nine objects
change: .text falls by 52 bytes, eight shrink and one grows.  The grower is
gcc.target/arm/pr85434.c, by 12 bytes, where the cheaper multiply lets two
divisibility tests take the modular-inverse form: one instruction less each,
four more literal-pool words.  At -Os the same sources are byte-identical.

Tested on arm-none-eabi against a pristine build of the same commit: arm.exp,
mve.exp and simd.exp on the default board and on an arm-linux-gnueabihf-like
board, and arm.exp on Cortex-M0.  No test changes status on any of the three.

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]>
---
Changes in v2:

 - the new arm returned true without costing operand 0, so for any caller
   other than the synth_mult query a multiply with a non-trivial operand 0
   came out free.  It now costs XEXP (x, 0), as the SMUL[TB][TB] arm above it
   does.  No output changes on any of the corpora above -- it is a
   correctness fix, not a tuning one.
 - drop the comment on the new hunk; the field name says it.
 - the corpus, tune and testsuite numbers above are re-measured on trunk
   2cb78d1a0e5, against a pristine build of that commit.
 - mult-cost-9e.c uses arm_arch_v6k_arm and -mtune, so any multilib runs it.

OK for trunk?

 gcc/config/arm/arm-protos.h                 |  3 +
 gcc/config/arm/arm.cc                       | 78 ++++++++++++++-------
 gcc/testsuite/gcc.target/arm/mult-cost-9e.c | 27 +++++++
 3 files changed, 84 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..d5b90fe8736 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,12 @@ arm_rtx_costs_internal (rtx x, enum rtx_code code, 
enum rtx_code outer_code,
            }
          if (speed_p)
            *cost += extra_cost->mult[0].simple;
+         if (CONST_INT_P (XEXP (x, 1)) && current_tune->mult_const_as_imm)
+           {
+             *cost += (COSTS_N_INSNS (1)
+                       + rtx_cost (XEXP (x, 0), mode, MULT, 0, speed_p));
+             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..58a9d01d0b4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/mult-cost-9e.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v6k_arm_ok } */
+/* { dg-options "-O2 -mtune=arm1176jzf-s" } */
+/* { dg-add-options arm_arch_v6k_arm } */
+
+/* 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