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