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