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