https://gcc.gnu.org/g:1a06a37611e3b27889c595a17df13f6d27202a95
commit r17-383-g1a06a37611e3b27889c595a17df13f6d27202a95 Author: Roger Sayle <[email protected]> Date: Thu May 7 18:46:37 2026 +0100 PR middle-end/122871: Doubleword multiplication improvements This patch resolves PR middle-end/122871 by improving RTL expansion of doubleword multiplications. The main change is to synth_mult adding support for the case where the constant being multiplied has BITS_PER_WORD or more trailing zeros. The shift_cost tables in expmed are only parameterized for shifts less than BITS_PER_WORD, so doubleword shifts by more than this can't use the usual code path. This patch teaches synth_mult that for scalar doubleword multiplications, a doubleword shift by more than BITS_PER_WORD typically requires two instructions; one to set the result lowpart to zero, and the other a wordmode shift to calculate the result highpart. For the testcase given in the PR: long long ashll_fn (long long a) { long long c; c = a << 33; c += a; return c; } GCC for arm-linux-gnueabihf currently generates with -O2: ashll_fn: lsl r2, r1, #11 lsl ip, r0, #11 subs ip, ip, r0 orr r2, r2, r0, lsr #21 sbc r2, r2, r1 lsl r3, ip, #11 lsl r2, r2, #11 adds r3, r3, r0 orr r2, r2, ip, lsr #21 adc r1, r1, r2 lsl r2, r1, #11 lsl r0, r3, #11 adds r0, r3, r0 orr r2, r2, r3, lsr #21 adc r1, r1, r2 bx lr with this patch, we instead generate: ashll_fn: add r1, r1, r0, lsl #1 bx lr Additionally, this patch includes a clean-up (identified by A. Pinski) to prevent RTL expansion of doubleword multiplications from initially emitting multiply instructions by immediate constants 0, 1 or 2. These dubious multiplications eventually get tidied up by later RTL optimization passes, but being sensible during RTL expansion both speeds up the compiler and reduces unnecessary memory usage. 2026-05-07 Roger Sayle <[email protected]> gcc/ChangeLog PR middle-end/122871 * expmed.cc (synth_mult): Handle doubleword left shifts by BITS_PER_WORD bits or more, for scalar modes. * optabs.cc (expand_doubleword_mult): Avoid generating multiply instructions by immediate constants 0, 1 or 2. gcc/testsuite/ChangeLog PR middle-end/122871 * gcc.target/arm/muldi-1.c: New test case. Diff: --- gcc/expmed.cc | 22 ++++++++++++++++++++++ gcc/optabs.cc | 28 +++++++++++++++++++++++----- gcc/testsuite/gcc.target/arm/muldi-1.c | 20 ++++++++++++++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/gcc/expmed.cc b/gcc/expmed.cc index d57ea78d6b1b..a75e406376a8 100644 --- a/gcc/expmed.cc +++ b/gcc/expmed.cc @@ -2962,6 +2962,28 @@ synth_mult (struct algorithm *alg_out, unsigned HOST_WIDE_INT t, } } } + else if (GET_MODE_BITSIZE (imode) == 2 * BITS_PER_WORD + && imode == mode) + { + q = t >> m; + int op1_cost = shift_cost (speed, mode, m - BITS_PER_WORD); + int op2_cost = zero_cost (speed); + op_latency = MAX (op1_cost, op2_cost); + op_cost = op1_cost + op2_cost; + + new_limit.cost = best_cost.cost - op_cost; + new_limit.latency = best_cost.latency - op_latency; + synth_mult (alg_in, q, &new_limit, mode); + alg_in->cost.cost += op_cost; + alg_in->cost.latency += op_latency; + if (CHEAPER_MULT_COST (&alg_in->cost, &best_cost)) + { + best_cost = alg_in->cost; + std::swap (alg_in, best_alg); + best_alg->log[best_alg->ops] = m; + best_alg->op[best_alg->ops] = alg_shift; + } + } if (cache_hit) goto done; } diff --git a/gcc/optabs.cc b/gcc/optabs.cc index 111b9be99139..d17fbcf12ed6 100644 --- a/gcc/optabs.cc +++ b/gcc/optabs.cc @@ -907,8 +907,16 @@ expand_doubleword_mult (machine_mode mode, rtx op0, rtx op1, rtx target, return NULL_RTX; } - adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low, - NULL_RTX, 0, OPTAB_DIRECT); + if (op1_low == const1_rtx) + adjust = op0_high; + else if (op1_low == const0_rtx) + adjust = const0_rtx; + else if (op1_low == const2_rtx) + adjust = expand_binop (word_mode, add_optab, op0_high, op0_high, + NULL_RTX, 0, OPTAB_DIRECT); + else + adjust = expand_binop (word_mode, smul_optab, op0_high, op1_low, + NULL_RTX, 0, OPTAB_DIRECT); if (!adjust) return NULL_RTX; @@ -936,8 +944,16 @@ expand_doubleword_mult (machine_mode mode, rtx op0, rtx op1, rtx target, return NULL_RTX; } - temp = expand_binop (word_mode, smul_optab, op1_high, op0_low, - NULL_RTX, 0, OPTAB_DIRECT); + if (op1_high == const1_rtx) + temp = op0_low; + else if (op1_high == const0_rtx) + temp = const0_rtx; + else if (op1_high == const2_rtx) + temp = expand_binop (word_mode, add_optab, op0_low, op0_low, + NULL_RTX, 0, OPTAB_DIRECT); + else + temp = expand_binop (word_mode, smul_optab, op0_low, op1_high, + NULL_RTX, 0, OPTAB_DIRECT); if (!temp) return NULL_RTX; @@ -954,7 +970,9 @@ expand_doubleword_mult (machine_mode mode, rtx op0, rtx op1, rtx target, if (GET_MODE (op0_low) == VOIDmode && GET_MODE (op1_low) == VOIDmode) op0_low = force_reg (word_mode, op0_low); - if (umulp) + if (op1_low == const1_rtx) + product = convert_modes (mode, word_mode, op0_low, umulp); + else if (umulp) product = expand_binop (mode, umul_widen_optab, op0_low, op1_low, target, 1, OPTAB_DIRECT); else diff --git a/gcc/testsuite/gcc.target/arm/muldi-1.c b/gcc/testsuite/gcc.target/arm/muldi-1.c new file mode 100644 index 000000000000..cc3f3b9f3259 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/muldi-1.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +long long ashll_fn (long long a) +{ + long long c; + + c = a << 33; + c += a; + return c; +} + +/* { dg-final { scan-assembler "add\tr1, r1, r0, lsl #1" } } */ +/* { dg-final { scan-assembler-not "adc" } } */ +/* { dg-final { scan-assembler-not "adds" } } */ +/* { dg-final { scan-assembler-not "lsr" } } */ +/* { dg-final { scan-assembler-not "orr" } } */ +/* { dg-final { scan-assembler-not "sbc" } } */ +/* { dg-final { scan-assembler-not "subs" } } */ +
