[llvm] [RISCV][P-ext] Select scalar mulhr/mulhru/mulhrsu for RV32 v2i32 (PR #215938)

via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 12 20:28:43 PDT 2026


https://github.com/sihuan created https://github.com/llvm/llvm-project/pull/215938

The `combinePExtTruncate` DAGCombine bailed out on RV32 for both
`v4i16` and `v2i32` 64-bit packed types. The `v4i16` case is correct
(no paired rounding multiply-high for 16-bit lanes), but `v2i32` was
incorrectly excluded.

RV32 provides scalar `mulhr`/`mulhru`/`mulhrsu` instructions. Since
`v2i32` on RV32 is a GPRPair, splitting into two scalar operations in
the combine — while the widening multiply shape is still visible —
reuses those instructions directly.

The non-rounding forms (`mulh`/`mulhu`/`mulhsu`) already scalarize
correctly through the generic legalizer, so only the rounding case
needs explicit handling here.

>From 20d999b11f2554472a7b8070d342c79a3ff554d5 Mon Sep 17 00:00:00 2001
From: SiHuaN <liyongtai at iscas.ac.cn>
Date: Thu, 23 Jul 2026 08:33:30 +0000
Subject: [PATCH] [RISCV][P-ext] Select scalar mulhr/mulhru/mulhrsu for RV32
 v2i32

RV32 has no paired rounding multiply-high instruction, so the packed
v2i32 pattern previously fell back to a wide multiply sequence. Split it
into two i32 lanes in the truncate combine and reuse the scalar
mulhr/mulhru/mulhrsu instructions. The non-rounding forms already
scalarize to mulh/mulhu/mulhsu, so only the rounding case is handled
here.
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 17 ++++++++++-
 llvm/test/CodeGen/RISCV/rvp-simd-64.ll      | 32 ++++++---------------
 2 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index b060542bf0bf4..03c000b86b2e6 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -17884,7 +17884,7 @@ static SDValue combinePExtTruncate(SDNode *N, SelectionDAG &DAG,
     // MULH*/MULHR*: shift amount must be element size, only for i16/i32
     if (ShAmtVal != EltBits || (EltBits != 16 && EltBits != 32))
       return SDValue();
-    if (!Subtarget.is64Bit() && (VT == MVT::v2i32 || VT == MVT::v4i16))
+    if (!Subtarget.is64Bit() && VT == MVT::v4i16)
       return SDValue();
     if (IsRounding) {
       if (LHSIsSExt && RHSIsSExt) {
@@ -17911,6 +17911,21 @@ static SDValue combinePExtTruncate(SDNode *N, SelectionDAG &DAG,
       } else
         return SDValue();
     }
+
+    // RV32 has scalar rounded multiply-high instructions, but no paired form.
+    // Split v2i32 while the widening multiply shape is still visible.
+    if (!Subtarget.is64Bit() && VT == MVT::v2i32) {
+      if (!IsRounding)
+        return SDValue();
+      SDLoc DL(N);
+      SDValue ALo = DAG.getExtractVectorElt(DL, MVT::i32, A, 0);
+      SDValue AHi = DAG.getExtractVectorElt(DL, MVT::i32, A, 1);
+      SDValue BLo = DAG.getExtractVectorElt(DL, MVT::i32, B, 0);
+      SDValue BHi = DAG.getExtractVectorElt(DL, MVT::i32, B, 1);
+      SDValue Lo = DAG.getNode(Opc, DL, MVT::i32, ALo, BLo);
+      SDValue Hi = DAG.getNode(Opc, DL, MVT::i32, AHi, BHi);
+      return DAG.getNode(ISD::BUILD_VECTOR, DL, VT, Lo, Hi);
+    }
     break;
   }
 
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
index 0ea81f0cef552..490634197f29b 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
@@ -2769,12 +2769,8 @@ define <4 x i16> @test_pmulhr_h(<4 x i16> %a, <4 x i16> %b) {
 define <2 x i32> @test_pmulhr_w(<2 x i32> %a, <2 x i32> %b) {
 ; RV32-LABEL: test_pmulhr_w:
 ; RV32:       # %bb.0:
-; RV32-NEXT:    wmul a4, a0, a2
-; RV32-NEXT:    wmul a0, a1, a3
-; RV32-NEXT:    lui a2, 524288
-; RV32-NEXT:    waddau a0, a2, zero
-; RV32-NEXT:    waddau a4, a2, zero
-; RV32-NEXT:    mv a0, a5
+; RV32-NEXT:    mulhr a1, a1, a3
+; RV32-NEXT:    mulhr a0, a0, a2
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: test_pmulhr_w:
@@ -2814,12 +2810,8 @@ define <4 x i16> @test_pmulhru_h(<4 x i16> %a, <4 x i16> %b) {
 define <2 x i32> @test_pmulhru_w(<2 x i32> %a, <2 x i32> %b) {
 ; RV32-LABEL: test_pmulhru_w:
 ; RV32:       # %bb.0:
-; RV32-NEXT:    wmulu a4, a0, a2
-; RV32-NEXT:    wmulu a0, a1, a3
-; RV32-NEXT:    lui a2, 524288
-; RV32-NEXT:    waddau a0, a2, zero
-; RV32-NEXT:    waddau a4, a2, zero
-; RV32-NEXT:    mv a0, a5
+; RV32-NEXT:    mulhru a1, a1, a3
+; RV32-NEXT:    mulhru a0, a0, a2
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: test_pmulhru_w:
@@ -2879,12 +2871,8 @@ define <4 x i16> @test_pmulhrsu_h_commuted(<4 x i16> %a, <4 x i16> %b) {
 define <2 x i32> @test_pmulhrsu_w(<2 x i32> %a, <2 x i32> %b) {
 ; RV32-LABEL: test_pmulhrsu_w:
 ; RV32:       # %bb.0:
-; RV32-NEXT:    wmulsu a4, a0, a2
-; RV32-NEXT:    wmulsu a0, a1, a3
-; RV32-NEXT:    lui a2, 524288
-; RV32-NEXT:    waddau a0, a2, zero
-; RV32-NEXT:    waddau a4, a2, zero
-; RV32-NEXT:    mv a0, a5
+; RV32-NEXT:    mulhrsu a1, a1, a3
+; RV32-NEXT:    mulhrsu a0, a0, a2
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: test_pmulhrsu_w:
@@ -2903,12 +2891,8 @@ define <2 x i32> @test_pmulhrsu_w(<2 x i32> %a, <2 x i32> %b) {
 define <2 x i32> @test_pmulhrsu_w_commuted(<2 x i32> %a, <2 x i32> %b) {
 ; RV32-LABEL: test_pmulhrsu_w_commuted:
 ; RV32:       # %bb.0:
-; RV32-NEXT:    wmulsu a4, a2, a0
-; RV32-NEXT:    wmulsu a0, a3, a1
-; RV32-NEXT:    lui a2, 524288
-; RV32-NEXT:    waddau a0, a2, zero
-; RV32-NEXT:    waddau a4, a2, zero
-; RV32-NEXT:    mv a0, a5
+; RV32-NEXT:    mulhrsu a1, a3, a1
+; RV32-NEXT:    mulhrsu a0, a2, a0
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: test_pmulhrsu_w_commuted:



More information about the llvm-commits mailing list