[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