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

Reply via email to