This patch adds some RTL simplifications for SMUL_HIGHPART and
UMUL_HIGHPART.  Currently, simplify_binary_operation_1 knows
that highpart multiplication by zero is zero.  This patch
optimizes unsigned highpart multiplication (UMUL_HIGHPART) by
1 to zero, and signed highpart multiplication (SMUL_HIGHPART)
by one to be the repeated signbit, x < 0 ? -1 : 0, i.e.
x >> PRECISION-1.  Additionally, UMUL_HIGHPART by a power
of two is transformed into a logical right shift, and
SMUL_HIGHPART by a positive power of two is transformed into
an arithmetic right shift.

This patch has been tested on x86_64-pc-linux-gnu with make
bootstrap and make -k check (to confirm it does no harm) and
on a cross-compiler to nvptx-none, with make and make -k check
with no new failures (to confirm it benefits targets with a
highpart multiplication instruction/pattern).

Ok for mainline?


2026-09-22  Roger Sayle  <[email protected]>

gcc/ChangeLog
        * simplify_rtx.cc (simplify_binary_operation_1) <SMUL_HIGHPART>:
        Add transformations for multiplications by one and powers to two.
        <UMUL_HIGHPART>: Likewise.

gcc/testsuite/ChangeLog
        * gcc.target/nvptx/mul-hi-2.c: New test case.


Thanks in advance,
Roger
--

diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 38c8abf..285826a 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -5082,11 +5082,53 @@ simplify_ashift:
       return 0;
 
     case SMUL_HIGHPART:
+      /* Simplify x h* 0 to 0, if possible.  */
+      if (trueop1 == CONST0_RTX (mode)
+         && !side_effects_p (op0))
+       return op1;
+      /* Simplify x h* 1 to sign(x).  */
+      if (trueop1 == const1_rtx
+         && is_a <scalar_int_mode> (mode, &int_mode))
+       {
+         HOST_WIDE_INT bits = GET_MODE_PRECISION (int_mode) - 1;
+         return simplify_gen_binary (ASHIFTRT, mode, op0,
+                                     gen_int_shift_amount (mode, bits));
+       }
+      /* Simplify SMUL_HIGHPART by power of two as ASHIFRT.  */
+      if (CONST_INT_P (trueop1)
+         && INTVAL (trueop1) > 1
+         && is_a <scalar_int_mode> (mode, &int_mode))
+       {
+         HOST_WIDE_INT prec = GET_MODE_PRECISION (int_mode);
+         val = wi::exact_log2 (INTVAL (trueop1));
+         if (val > 0 && val < prec)
+           return simplify_gen_binary (ASHIFTRT, mode, op0,
+                                       gen_int_shift_amount (mode,
+                                                             prec - val));
+       }
+      return 0;
+
     case UMUL_HIGHPART:
-      /* Simplify x * 0 to 0, if possible.  */
+      /* Simplify x h* 0 to 0, if possible.  */
       if (trueop1 == CONST0_RTX (mode)
          && !side_effects_p (op0))
        return op1;
+      /* Simplify x h* 1 to 0.  */
+      if (trueop1 == CONST1_RTX (mode)
+         && !side_effects_p (op1))
+       return CONST0_RTX (mode);
+      /* Simplify UMUL_HIGHPART by power of two as LSHIFRT.  */
+      if (CONST_INT_P (trueop1)
+         && UINTVAL (trueop1) > 1
+         && is_a <scalar_int_mode> (mode, &int_mode))
+       {
+         HOST_WIDE_INT prec = GET_MODE_PRECISION (int_mode);
+         val = wi::exact_log2 (UINTVAL (trueop1));
+         if (val > 0 && val < prec)
+           return simplify_gen_binary (LSHIFTRT, mode, op0,
+                                       gen_int_shift_amount (mode,
+                                                             prec - val));
+       }
       return 0;
 
     case SS_DIV:
diff --git a/gcc/testsuite/gcc.target/nvptx/mul-hi-2.c 
b/gcc/testsuite/gcc.target/nvptx/mul-hi-2.c
new file mode 100644
index 0000000..2f7b578
--- /dev/null
+++ b/gcc/testsuite/gcc.target/nvptx/mul-hi-2.c
@@ -0,0 +1,37 @@
+/* { dg-do assemble } */
+/* { dg-options "-O2 -save-temps" } */
+
+int smul_hi(int x, int y)
+{
+    return ((long)x*(long)y)>>32;
+}
+
+int s2(int x)
+{
+    return smul_hi(x,2);
+}
+
+int s4(int x)
+{
+    return smul_hi(x,4);
+}
+
+unsigned int umul_hi(unsigned int x, unsigned int y)
+{
+    return ((unsigned long)x * (unsigned long)y) >> 32;
+}
+
+unsigned int u2(unsigned int x)
+{
+    return umul_hi(x,2);
+}
+
+unsigned int u4(unsigned int x)
+{
+    return umul_hi(x,4);
+}
+
+/* { dg-final { scan-assembler-times "mul.hi.s32" 1 } } */
+/* { dg-final { scan-assembler-times "shr.s32" 2 } } */
+/* { dg-final { scan-assembler-times "mul.hi.u32" 1 } } */
+/* { dg-final { scan-assembler-times "shr.u32" 2 } } */

Reply via email to