[llvm] [RISCV][P-ext] Select scalar asub/asubu and mulhr/mulhru/mulhrsu on RV32 (PR #201540)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 4 02:52:39 PDT 2026


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

The truncate combine only formed these nodes for packed vectors; extend it to scalar i32 on RV32 and add the matching isel patterns.

>From be7e741208610f15d9cbc72c246c688f49663436 Mon Sep 17 00:00:00 2001
From: SiHuaN <liyongtai at iscas.ac.cn>
Date: Thu, 4 Jun 2026 17:47:32 +0800
Subject: [PATCH] [RISCV][P-ext] Select scalar asub/asubu and
 mulhr/mulhru/mulhrsu on RV32

The truncate combine only formed these nodes for packed vectors; extend it
to scalar i32 on RV32 and add the matching isel patterns.
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 41 ++++-------
 llvm/lib/Target/RISCV/RISCVInstrInfoP.td    |  7 ++
 llvm/test/CodeGen/RISCV/rv32p.ll            | 82 +++++++++++++++++++++
 3 files changed, 104 insertions(+), 26 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 8931cf03b7a4f..865df70a3a5fa 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -16665,7 +16665,8 @@ static SDValue combineTruncSelectToSMaxUSat(SDNode *N, SelectionDAG &DAG) {
   return DAG.getNode(ISD::TRUNCATE, DL, VT, Min);
 }
 
-// Handle P extension truncate patterns:
+// Handle P extension truncate patterns, both on packed vectors and on scalar
+// i32 (the RV32-only asub/asubu and mulhr* instructions):
 // ASUB/ASUBU: (trunc (srl (sub ([s|z]ext a), ([s|z]ext b)), 1))
 // MULHSU: (trunc (srl (mul (sext a), (zext b)), EltBits))
 // MULHR*: (trunc (srl (add (mul (sext a), (zext b)), round_const), EltBits))
@@ -16677,21 +16678,10 @@ static SDValue combinePExtTruncate(SDNode *N, SelectionDAG &DAG,
     return SDValue();
 
   if (VT != MVT::v4i16 && VT != MVT::v2i16 && VT != MVT::v8i8 &&
-      VT != MVT::v4i8 && VT != MVT::v2i32)
+      VT != MVT::v4i8 && VT != MVT::v2i32 && VT != MVT::i32)
     return SDValue();
 
-  // Check if shift amount is a splat constant
-  SDValue ShAmt = N0.getOperand(1);
-  if (ShAmt.getOpcode() != ISD::BUILD_VECTOR)
-    return SDValue();
-
-  BuildVectorSDNode *BV = dyn_cast<BuildVectorSDNode>(ShAmt.getNode());
-  if (!BV)
-    return SDValue();
-  SDValue Splat = BV->getSplatValue();
-  if (!Splat)
-    return SDValue();
-  ConstantSDNode *C = dyn_cast<ConstantSDNode>(Splat);
+  ConstantSDNode *C = isConstOrConstSplat(N0.getOperand(1));
   if (!C)
     return SDValue();
 
@@ -16702,17 +16692,11 @@ static SDValue combinePExtTruncate(SDNode *N, SelectionDAG &DAG,
   // Check for rounding pattern: (add (mul ...), round_const)
   bool IsRounding = false;
   if (Op.getOpcode() == ISD::ADD && (EltBits == 16 || EltBits == 32)) {
-    SDValue AddRHS = Op.getOperand(1);
-    if (auto *RndBV = dyn_cast<BuildVectorSDNode>(AddRHS.getNode())) {
-      if (auto *RndC =
-              dyn_cast_or_null<ConstantSDNode>(RndBV->getSplatValue())) {
-        uint64_t ExpectedRnd = 1ULL << (EltBits - 1);
-        if (RndC->getZExtValue() == ExpectedRnd &&
-            Op.getOperand(0).getOpcode() == ISD::MUL) {
-          Op = Op.getOperand(0);
-          IsRounding = true;
-        }
-      }
+    ConstantSDNode *RndC = isConstOrConstSplat(Op.getOperand(1));
+    if (RndC && RndC->getZExtValue() == (1ULL << (EltBits - 1)) &&
+        Op.getOperand(0).getOpcode() == ISD::MUL) {
+      Op = Op.getOperand(0);
+      IsRounding = true;
     }
   }
 
@@ -16772,6 +16756,9 @@ static SDValue combinePExtTruncate(SDNode *N, SelectionDAG &DAG,
         return SDValue();
       }
     } else {
+      // Scalar mulhsu is handled elsewhere, only match the packed MULHSU here.
+      if (!VT.isVector())
+        return SDValue();
       if ((LHSIsSExt && RHSIsZExt) || (LHSIsZExt && RHSIsSExt)) {
         Opc = RISCVISD::MULHSU;
         // commuted case
@@ -16791,7 +16778,9 @@ static SDValue performTRUNCATECombine(SDNode *N, SelectionDAG &DAG,
   SDValue N0 = N->getOperand(0);
   EVT VT = N->getValueType(0);
 
-  if (VT.isFixedLengthVector() && Subtarget.hasStdExtP())
+  // P truncate patterns: packed vectors, plus RV32-only scalar i32.
+  if (Subtarget.hasStdExtP() &&
+      (VT.isFixedLengthVector() || (VT == MVT::i32 && !Subtarget.is64Bit())))
     return combinePExtTruncate(N, DAG, Subtarget);
 
   // Pre-promote (i1 (truncate (srl X, Y))) on RV64 with Zbs without zero
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
index 4c07d054c2983..f11698f2a9e8f 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
@@ -2075,6 +2075,13 @@ let append Predicates = [IsRV32] in {
   // 32-bit averaging patterns
   def : PatGprGpr<avgfloors, AADD, i32>;
   def : PatGprGpr<avgflooru, AADDU, i32>;
+  def : PatGprGpr<riscv_asub, ASUB, i32>;
+  def : PatGprGpr<riscv_asubu, ASUBU, i32>;
+
+  // 32-bit multiply high rounding patterns
+  def : PatGprGpr<riscv_mulhr, MULHR, i32>;
+  def : PatGprGpr<riscv_mulhru, MULHRU, i32>;
+  def : PatGprGpr<riscv_mulhrsu, MULHRSU, i32>;
 
   // Halfword multiply patterns where one operand is a sext.h or zext.h and
   // the other is a sext.h or zext.h or is known to be sign/zero-extended. We
diff --git a/llvm/test/CodeGen/RISCV/rv32p.ll b/llvm/test/CodeGen/RISCV/rv32p.ll
index e2f6fd7e0192d..a96881fd49133 100644
--- a/llvm/test/CodeGen/RISCV/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/rv32p.ll
@@ -882,6 +882,88 @@ define i32 @aaddu2_i32(i32 %a, i32 %b) {
   ret i32 %res
 }
 
+define i32 @asub_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: asub_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    asub a0, a0, a1
+; CHECK-NEXT:    ret
+  %ext.a = sext i32 %a to i64
+  %ext.b = sext i32 %b to i64
+  %sub = sub i64 %ext.a, %ext.b
+  %shift = ashr i64 %sub, 1
+  %res = trunc i64 %shift to i32
+  ret i32 %res
+}
+
+define i32 @asubu_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: asubu_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    asubu a0, a0, a1
+; CHECK-NEXT:    ret
+  %ext.a = zext i32 %a to i64
+  %ext.b = zext i32 %b to i64
+  %sub = sub i64 %ext.a, %ext.b
+  %shift = lshr i64 %sub, 1
+  %res = trunc i64 %shift to i32
+  ret i32 %res
+}
+
+define i32 @mulhr_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: mulhr_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mulhr a0, a0, a1
+; CHECK-NEXT:    ret
+  %ext.a = sext i32 %a to i64
+  %ext.b = sext i32 %b to i64
+  %mul = mul i64 %ext.a, %ext.b
+  %add = add i64 %mul, 2147483648
+  %shift = lshr i64 %add, 32
+  %res = trunc i64 %shift to i32
+  ret i32 %res
+}
+
+define i32 @mulhru_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: mulhru_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mulhru a0, a0, a1
+; CHECK-NEXT:    ret
+  %ext.a = zext i32 %a to i64
+  %ext.b = zext i32 %b to i64
+  %mul = mul i64 %ext.a, %ext.b
+  %add = add i64 %mul, 2147483648
+  %shift = lshr i64 %add, 32
+  %res = trunc i64 %shift to i32
+  ret i32 %res
+}
+
+define i32 @mulhrsu_i32(i32 %a, i32 %b) {
+; CHECK-LABEL: mulhrsu_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mulhrsu a0, a0, a1
+; CHECK-NEXT:    ret
+  %ext.a = sext i32 %a to i64
+  %ext.b = zext i32 %b to i64
+  %mul = mul i64 %ext.a, %ext.b
+  %add = add i64 %mul, 2147483648
+  %shift = lshr i64 %add, 32
+  %res = trunc i64 %shift to i32
+  ret i32 %res
+}
+
+define i32 @mulhrsu_i32_commuted(i32 %a, i32 %b) {
+; CHECK-LABEL: mulhrsu_i32_commuted:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    mulhrsu a0, a1, a0
+; CHECK-NEXT:    ret
+  %ext.a = zext i32 %a to i64
+  %ext.b = sext i32 %b to i64
+  %mul = mul i64 %ext.a, %ext.b
+  %add = add i64 %mul, 2147483648
+  %shift = lshr i64 %add, 32
+  %res = trunc i64 %shift to i32
+  ret i32 %res
+}
+
 define i64 @wmul_i32(i32 %x, i32 %y) {
 ; CHECK-LABEL: wmul_i32:
 ; CHECK:       # %bb.0:



More information about the llvm-commits mailing list