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 Andrew
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.
This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32} with
no new failures. Ok for mainline?
2026-03-28 Roger Sayle <[email protected]>
gcc/ChangeLog
PR middle-end/122871
* expmed.cc (synth_mult): Handle doubleword left shifts by
BITS_PER_MODE bits or more, for scalar modes.
* optabs.cc (expand_doubleword_mult): Avoid generating multiply
instructions by immediate constants 0, 1 or 2.
Roger
--
diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index d57ea78d6b1..a75e406376a 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 e813cf9b215..829967ff9af 100644
--- a/gcc/optabs.cc
+++ b/gcc/optabs.cc
@@ -902,8 +902,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;
@@ -931,8 +939,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;
@@ -949,7 +965,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