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" } } */
+

Reply via email to