[clang] [llvm] [RISCV][P-ext] Add scalar multiply high intrinsics (PR #225561)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 05:09:04 PDT 2026
https://github.com/StarryCSF updated https://github.com/llvm/llvm-project/pull/225561
>From ca6adfc22355847165f70e75749855b43079befb Mon Sep 17 00:00:00 2001
From: "ZhiQiang.Fan" <571253675 at QQ.com>
Date: Tue, 22 Sep 2026 15:13:14 +0800
Subject: [PATCH 1/2] [RISCV][P-ext] Add scalar multiply high intrinsics
---
clang/include/clang/Basic/BuiltinsRISCV.td | 8 ++
clang/lib/CodeGen/TargetBuiltins/RISCV.cpp | 32 +++++++
clang/lib/Headers/riscv_packed_simd.h | 14 +++
clang/test/CodeGen/RISCV/rvp-intrinsics.c | 96 +++++++++++++++++++
.../riscv_packed_simd.c | 43 +++++++++
llvm/include/llvm/IR/IntrinsicsRISCV.td | 12 +++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 75 +++++++++++++++
llvm/test/CodeGen/RISCV/rvp-simd-32.ll | 85 ++++++++++++++++
8 files changed, 365 insertions(+)
diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td
index ee840e45a65ba..a02c8ff47f1ff 100644
--- a/clang/include/clang/Basic/BuiltinsRISCV.td
+++ b/clang/include/clang/Basic/BuiltinsRISCV.td
@@ -247,6 +247,14 @@ def pmerge_i16x4 : RISCVBuiltin<"_Vector<4, short>(_Vector<4, short>, _Vector<4,
def pmerge_u32x2 : RISCVBuiltin<"_Vector<2, unsigned int>(_Vector<2, unsigned int>, _Vector<2, unsigned int>, _Vector<2, unsigned int>)">;
def pmerge_i32x2 : RISCVBuiltin<"_Vector<2, int>(_Vector<2, int>, _Vector<2, int>, _Vector<2, unsigned int>)">;
+// Scalar Multiply High
+def mulh_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">;
+def mulhr_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">;
+def mulhu_u32 : RISCVBuiltin<"uint32_t(uint32_t, uint32_t)">;
+def mulhru_u32 : RISCVBuiltin<"uint32_t(uint32_t, uint32_t)">;
+def mulhsu_i32 : RISCVBuiltin<"int32_t(int32_t, uint32_t)">;
+def mulhrsu_i32 : RISCVBuiltin<"int32_t(int32_t, uint32_t)">;
+
// Packed Multiply High (32-bit)
def pmulh_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Vector<2, short>)">;
def pmulhr_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Vector<2, short>)">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
index f99a05ce673aa..edff5d6689550 100644
--- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
@@ -1531,6 +1531,38 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
break;
}
+ // Scalar Multiply High
+ case RISCV::BI__builtin_riscv_mulh_i32:
+ case RISCV::BI__builtin_riscv_mulhr_i32:
+ case RISCV::BI__builtin_riscv_mulhu_u32:
+ case RISCV::BI__builtin_riscv_mulhru_u32:
+ case RISCV::BI__builtin_riscv_mulhsu_i32:
+ case RISCV::BI__builtin_riscv_mulhrsu_i32: {
+ switch (BuiltinID) {
+ default:
+ llvm_unreachable("unexpected builtin ID");
+ case RISCV::BI__builtin_riscv_mulh_i32:
+ ID = Intrinsic::riscv_mulh_i32;
+ break;
+ case RISCV::BI__builtin_riscv_mulhr_i32:
+ ID = Intrinsic::riscv_mulhr_i32;
+ break;
+ case RISCV::BI__builtin_riscv_mulhu_u32:
+ ID = Intrinsic::riscv_mulhu_u32;
+ break;
+ case RISCV::BI__builtin_riscv_mulhru_u32:
+ ID = Intrinsic::riscv_mulhru_u32;
+ break;
+ case RISCV::BI__builtin_riscv_mulhsu_i32:
+ ID = Intrinsic::riscv_mulhsu_i32;
+ break;
+ case RISCV::BI__builtin_riscv_mulhrsu_i32:
+ ID = Intrinsic::riscv_mulhrsu_i32;
+ break;
+ }
+ break;
+ }
+
// Packed Multiplication with Horizontal Addition
case RISCV::BI__builtin_riscv_pm4add_i8x4:
case RISCV::BI__builtin_riscv_pm4add_i8x8:
diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h
index db6d0d37c2e8a..dacd9600d9bd2 100644
--- a/clang/lib/Headers/riscv_packed_simd.h
+++ b/clang/lib/Headers/riscv_packed_simd.h
@@ -400,6 +400,20 @@ __packed_pabs(abs_u32, int32_t, uint32_t)
__packed_pabs(abs_u64, int64_t, uint64_t)
#endif
+/* Scalar Multiply High */
+#define __packed_mulh_builtin(name, res_ty, ty1, ty2) \
+ static __inline__ res_ty __DEFAULT_FN_ATTRS __riscv_##name(ty1 __rs1, \
+ ty2 __rs2) { \
+ return __builtin_riscv_##name(__rs1, __rs2); \
+ }
+__packed_mulh_builtin(mulh_i32, int32_t, int32_t, int32_t)
+__packed_mulh_builtin(mulhr_i32, int32_t, int32_t, int32_t)
+__packed_mulh_builtin(mulhu_u32, uint32_t, uint32_t, uint32_t)
+__packed_mulh_builtin(mulhru_u32, uint32_t, uint32_t, uint32_t)
+__packed_mulh_builtin(mulhsu_i32, int32_t, int32_t, uint32_t)
+__packed_mulh_builtin(mulhrsu_i32, int32_t, int32_t, uint32_t)
+#undef __packed_mulh_builtin
+
/* Packed Splat (32-bit) */
__packed_splat(pmv_s_u8x4, uint8x4_t, uint8_t, __packed_splat4)
__packed_splat(pmv_s_i8x4, int8x4_t, int8_t, __packed_splat4)
diff --git a/clang/test/CodeGen/RISCV/rvp-intrinsics.c b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
index c6721dbeb5db8..fd4aff5f23e84 100644
--- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c
+++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
@@ -134,6 +134,102 @@ uint64_t test_abs_u64(int64_t a) {
}
#endif
+// RV32-LABEL: define dso_local i32 @test_mulh_i32(
+// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulh.i32(i32 [[A]], i32 [[B]])
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local signext i32 @test_mulh_i32(
+// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulh.i32(i32 [[A]], i32 [[B]])
+// RV64-NEXT: ret i32 [[TMP0]]
+//
+int32_t test_mulh_i32(int32_t a, int32_t b) {
+ return __riscv_mulh_i32(a, b);
+}
+
+// RV32-LABEL: define dso_local i32 @test_mulhr_i32(
+// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhr.i32(i32 [[A]], i32 [[B]])
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local signext i32 @test_mulhr_i32(
+// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhr.i32(i32 [[A]], i32 [[B]])
+// RV64-NEXT: ret i32 [[TMP0]]
+//
+int32_t test_mulhr_i32(int32_t a, int32_t b) {
+ return __riscv_mulhr_i32(a, b);
+}
+
+// RV32-LABEL: define dso_local i32 @test_mulhu_u32(
+// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhu.u32(i32 [[A]], i32 [[B]])
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local signext i32 @test_mulhu_u32(
+// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhu.u32(i32 [[A]], i32 [[B]])
+// RV64-NEXT: ret i32 [[TMP0]]
+//
+uint32_t test_mulhu_u32(uint32_t a, uint32_t b) {
+ return __riscv_mulhu_u32(a, b);
+}
+
+// RV32-LABEL: define dso_local i32 @test_mulhru_u32(
+// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhru.u32(i32 [[A]], i32 [[B]])
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local signext i32 @test_mulhru_u32(
+// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhru.u32(i32 [[A]], i32 [[B]])
+// RV64-NEXT: ret i32 [[TMP0]]
+//
+uint32_t test_mulhru_u32(uint32_t a, uint32_t b) {
+ return __riscv_mulhru_u32(a, b);
+}
+
+// RV32-LABEL: define dso_local i32 @test_mulhsu_i32(
+// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhsu.i32(i32 [[A]], i32 [[B]])
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local signext i32 @test_mulhsu_i32(
+// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhsu.i32(i32 [[A]], i32 [[B]])
+// RV64-NEXT: ret i32 [[TMP0]]
+//
+int32_t test_mulhsu_i32(int32_t a, uint32_t b) {
+ return __riscv_mulhsu_i32(a, b);
+}
+
+// RV32-LABEL: define dso_local i32 @test_mulhrsu_i32(
+// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhrsu.i32(i32 [[A]], i32 [[B]])
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local signext i32 @test_mulhrsu_i32(
+// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulhrsu.i32(i32 [[A]], i32 [[B]])
+// RV64-NEXT: ret i32 [[TMP0]]
+//
+int32_t test_mulhrsu_i32(int32_t a, uint32_t b) {
+ return __riscv_mulhrsu_i32(a, b);
+}
+
/* Packed Splat (32-bit) */
// RV32-LABEL: define dso_local i32 @test_pmv_s_u8x4(
diff --git a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
index 14e64c3c3584b..5b9d03df5c72d 100644
--- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
+++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
@@ -2753,6 +2753,49 @@ int32x2_t test_pmerge_mvmn_i32x2(int32x2_t rs2, int32x2_t rs1, uint32x2_t rd) {
return __riscv_pmerge_i32x2(rs1, rs2, rd);
}
+// Scalar multiply high
+// CHECK-LABEL: test_mulh_i32:
+// RV32: mulh
+// RV64: pmulh.w
+int32_t test_mulh_i32(int32_t rs1, int32_t rs2) {
+ return __riscv_mulh_i32(rs1, rs2);
+}
+
+// CHECK-LABEL: test_mulhr_i32:
+// RV32: mulhr
+// RV64: pmulhr.w
+int32_t test_mulhr_i32(int32_t rs1, int32_t rs2) {
+ return __riscv_mulhr_i32(rs1, rs2);
+}
+
+// CHECK-LABEL: test_mulhu_u32:
+// RV32: mulhu
+// RV64: pmulhu.w
+uint32_t test_mulhu_u32(uint32_t rs1, uint32_t rs2) {
+ return __riscv_mulhu_u32(rs1, rs2);
+}
+
+// CHECK-LABEL: test_mulhru_u32:
+// RV32: mulhru
+// RV64: pmulhru.w
+uint32_t test_mulhru_u32(uint32_t rs1, uint32_t rs2) {
+ return __riscv_mulhru_u32(rs1, rs2);
+}
+
+// CHECK-LABEL: test_mulhsu_i32:
+// RV32: mulhsu
+// RV64: pmulhsu.w
+int32_t test_mulhsu_i32(int32_t rs1, uint32_t rs2) {
+ return __riscv_mulhsu_i32(rs1, rs2);
+}
+
+// CHECK-LABEL: test_mulhrsu_i32:
+// RV32: mulhrsu
+// RV64: pmulhrsu.w
+int32_t test_mulhrsu_i32(int32_t rs1, uint32_t rs2) {
+ return __riscv_mulhrsu_i32(rs1, rs2);
+}
+
// CHECK-LABEL: test_pmulh_i16x2:
// CHECK: pmulh.h
int16x2_t test_pmulh_i16x2(int16x2_t rs1, int16x2_t rs2) {
diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td
index 09399b0ea3f36..5ff4859ec5930 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -2118,6 +2118,18 @@ class RVPBinaryIntrinsic
def int_riscv_pmulhsu : RVPBinaryIntrinsic;
def int_riscv_pmulhrsu : RVPBinaryIntrinsic;
+ // Scalar Multiply High. On RV32 these map to the M-extension mulh
+ // instructions and the P-extension mulhr instructions.
+ class RVPScalarMulHighIntrinsic
+ : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty],
+ [IntrNoMem, IntrSpeculatable]>;
+ def int_riscv_mulh_i32 : RVPScalarMulHighIntrinsic;
+ def int_riscv_mulhr_i32 : RVPScalarMulHighIntrinsic;
+ def int_riscv_mulhu_u32 : RVPScalarMulHighIntrinsic;
+ def int_riscv_mulhru_u32 : RVPScalarMulHighIntrinsic;
+ def int_riscv_mulhsu_i32 : RVPScalarMulHighIntrinsic;
+ def int_riscv_mulhrsu_i32 : RVPScalarMulHighIntrinsic;
+
// Packed Multiply High Accumulate.
def int_riscv_pmhacc : RVPTernaryIntrinsic;
def int_riscv_pmhracc : RVPTernaryIntrinsic;
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index f17d9e90bfb73..d2835fd042d05 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -13177,6 +13177,39 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
return DAG.getNode(Opc, DL, VT, Op.getOperand(1), Op.getOperand(2));
}
+ case Intrinsic::riscv_mulh_i32:
+ case Intrinsic::riscv_mulhr_i32:
+ case Intrinsic::riscv_mulhu_u32:
+ case Intrinsic::riscv_mulhru_u32:
+ case Intrinsic::riscv_mulhsu_i32:
+ case Intrinsic::riscv_mulhrsu_i32: {
+ // RV32 maps the non-rounding forms onto the M extension and the rounding
+ // forms onto the scalar P instructions. RV64 goes via ReplaceNodeResults.
+ unsigned Opc;
+ switch (IntNo) {
+ default:
+ llvm_unreachable("unexpected multiply high intrinsic");
+ case Intrinsic::riscv_mulh_i32:
+ Opc = ISD::MULHS;
+ break;
+ case Intrinsic::riscv_mulhu_u32:
+ Opc = ISD::MULHU;
+ break;
+ case Intrinsic::riscv_mulhsu_i32:
+ Opc = RISCVISD::MULHSU;
+ break;
+ case Intrinsic::riscv_mulhr_i32:
+ Opc = RISCVISD::MULHR;
+ break;
+ case Intrinsic::riscv_mulhru_u32:
+ Opc = RISCVISD::MULHRU;
+ break;
+ case Intrinsic::riscv_mulhrsu_i32:
+ Opc = RISCVISD::MULHRSU;
+ break;
+ }
+ return DAG.getNode(Opc, DL, MVT::i32, Op.getOperand(1), Op.getOperand(2));
+ }
case Intrinsic::riscv_pmhacc:
case Intrinsic::riscv_pmhracc:
case Intrinsic::riscv_pmhaccu:
@@ -17590,6 +17623,48 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
Results.push_back(DAG.getExtractSubvector(DL, VT, Res, 0));
return;
}
+ case Intrinsic::riscv_mulh_i32:
+ case Intrinsic::riscv_mulhr_i32:
+ case Intrinsic::riscv_mulhu_u32:
+ case Intrinsic::riscv_mulhru_u32:
+ case Intrinsic::riscv_mulhsu_i32:
+ case Intrinsic::riscv_mulhrsu_i32: {
+ // RV64 has no scalar mulh instructions; reuse the packed pmulh.w
+ // family on the low words, whose element 0 is the scalar product.
+ MVT VT = N->getSimpleValueType(0);
+ if (!Subtarget.is64Bit() || VT != MVT::i32)
+ return;
+ unsigned Opc;
+ switch (IntNo) {
+ default:
+ llvm_unreachable("unexpected multiply high intrinsic");
+ case Intrinsic::riscv_mulh_i32:
+ Opc = ISD::MULHS;
+ break;
+ case Intrinsic::riscv_mulhu_u32:
+ Opc = ISD::MULHU;
+ break;
+ case Intrinsic::riscv_mulhsu_i32:
+ Opc = RISCVISD::MULHSU;
+ break;
+ case Intrinsic::riscv_mulhr_i32:
+ Opc = RISCVISD::MULHR;
+ break;
+ case Intrinsic::riscv_mulhru_u32:
+ Opc = RISCVISD::MULHRU;
+ break;
+ case Intrinsic::riscv_mulhrsu_i32:
+ Opc = RISCVISD::MULHRSU;
+ break;
+ }
+ SDValue Rd =
+ DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2i32, N->getOperand(1));
+ SDValue Rs =
+ DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2i32, N->getOperand(2));
+ SDValue Res = DAG.getNode(Opc, DL, MVT::v2i32, Rd, Rs);
+ Results.push_back(DAG.getExtractVectorElt(DL, MVT::i32, Res, 0));
+ return;
+ }
case Intrinsic::riscv_pnclipp:
case Intrinsic::riscv_pnclipup: {
bool IsSigned = IntNo == Intrinsic::riscv_pnclipp;
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
index e9e04eb6b5ebe..cb8c4dfacb524 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
@@ -3827,3 +3827,88 @@ define i32 @test_maccsu_h11_i32(i32 %rd, <2 x i16> %a, <2 x i16> %b) {
%r = call i32 @llvm.riscv.maccsu.11.i32.v2i16(i32 %rd, <2 x i16> %a, <2 x i16> %b)
ret i32 %r
}
+
+; Scalar multiply high
+define i32 @test_mulh_i32(i32 %rs1, i32 %rs2) {
+; RV32-LABEL: test_mulh_i32:
+; RV32: # %bb.0:
+; RV32-NEXT: mulh a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_mulh_i32:
+; RV64: # %bb.0:
+; RV64-NEXT: pmulh.w a0, a0, a1
+; RV64-NEXT: ret
+ %res = call i32 @llvm.riscv.mulh.i32(i32 %rs1, i32 %rs2)
+ ret i32 %res
+}
+
+define i32 @test_mulhr_i32(i32 %rs1, i32 %rs2) {
+; RV32-LABEL: test_mulhr_i32:
+; RV32: # %bb.0:
+; RV32-NEXT: mulhr a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_mulhr_i32:
+; RV64: # %bb.0:
+; RV64-NEXT: pmulhr.w a0, a0, a1
+; RV64-NEXT: ret
+ %res = call i32 @llvm.riscv.mulhr.i32(i32 %rs1, i32 %rs2)
+ ret i32 %res
+}
+
+define i32 @test_mulhu_u32(i32 %rs1, i32 %rs2) {
+; RV32-LABEL: test_mulhu_u32:
+; RV32: # %bb.0:
+; RV32-NEXT: mulhu a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_mulhu_u32:
+; RV64: # %bb.0:
+; RV64-NEXT: pmulhu.w a0, a0, a1
+; RV64-NEXT: ret
+ %res = call i32 @llvm.riscv.mulhu.u32(i32 %rs1, i32 %rs2)
+ ret i32 %res
+}
+
+define i32 @test_mulhru_u32(i32 %rs1, i32 %rs2) {
+; RV32-LABEL: test_mulhru_u32:
+; RV32: # %bb.0:
+; RV32-NEXT: mulhru a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_mulhru_u32:
+; RV64: # %bb.0:
+; RV64-NEXT: pmulhru.w a0, a0, a1
+; RV64-NEXT: ret
+ %res = call i32 @llvm.riscv.mulhru.u32(i32 %rs1, i32 %rs2)
+ ret i32 %res
+}
+
+define i32 @test_mulhsu_i32(i32 %rs1, i32 %rs2) {
+; RV32-LABEL: test_mulhsu_i32:
+; RV32: # %bb.0:
+; RV32-NEXT: mulhsu a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_mulhsu_i32:
+; RV64: # %bb.0:
+; RV64-NEXT: pmulhsu.w a0, a0, a1
+; RV64-NEXT: ret
+ %res = call i32 @llvm.riscv.mulhsu.i32(i32 %rs1, i32 %rs2)
+ ret i32 %res
+}
+
+define i32 @test_mulhrsu_i32(i32 %rs1, i32 %rs2) {
+; RV32-LABEL: test_mulhrsu_i32:
+; RV32: # %bb.0:
+; RV32-NEXT: mulhrsu a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_mulhrsu_i32:
+; RV64: # %bb.0:
+; RV64-NEXT: pmulhrsu.w a0, a0, a1
+; RV64-NEXT: ret
+ %res = call i32 @llvm.riscv.mulhrsu.i32(i32 %rs1, i32 %rs2)
+ ret i32 %res
+}
>From 685152eeec0c8de57334507daf139d1bec2d1dbd Mon Sep 17 00:00:00 2001
From: "ZhiQiang.Fan" <571253675 at QQ.com>
Date: Wed, 23 Sep 2026 10:40:42 +0800
Subject: [PATCH 2/2] modify helper func
---
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 67 +++++++--------------
1 file changed, 21 insertions(+), 46 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index d2835fd042d05..12f5f42900e25 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -12352,6 +12352,25 @@ static unsigned getRVPMulHighOpcode(unsigned IntNo) {
}
}
+static unsigned getRVScalarMulHighOpcode(unsigned IntNo) {
+ switch (IntNo) {
+ default:
+ llvm_unreachable("Unexpected RISC-V scalar multiply high intrinsic");
+ case Intrinsic::riscv_mulh_i32:
+ return ISD::MULHS;
+ case Intrinsic::riscv_mulhr_i32:
+ return RISCVISD::MULHR;
+ case Intrinsic::riscv_mulhu_u32:
+ return ISD::MULHU;
+ case Intrinsic::riscv_mulhru_u32:
+ return RISCVISD::MULHRU;
+ case Intrinsic::riscv_mulhsu_i32:
+ return RISCVISD::MULHSU;
+ case Intrinsic::riscv_mulhrsu_i32:
+ return RISCVISD::MULHRSU;
+ }
+}
+
static unsigned getRVPMulHighAccumulateOpcode(unsigned IntNo) {
switch (IntNo) {
default:
@@ -13185,29 +13204,7 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
case Intrinsic::riscv_mulhrsu_i32: {
// RV32 maps the non-rounding forms onto the M extension and the rounding
// forms onto the scalar P instructions. RV64 goes via ReplaceNodeResults.
- unsigned Opc;
- switch (IntNo) {
- default:
- llvm_unreachable("unexpected multiply high intrinsic");
- case Intrinsic::riscv_mulh_i32:
- Opc = ISD::MULHS;
- break;
- case Intrinsic::riscv_mulhu_u32:
- Opc = ISD::MULHU;
- break;
- case Intrinsic::riscv_mulhsu_i32:
- Opc = RISCVISD::MULHSU;
- break;
- case Intrinsic::riscv_mulhr_i32:
- Opc = RISCVISD::MULHR;
- break;
- case Intrinsic::riscv_mulhru_u32:
- Opc = RISCVISD::MULHRU;
- break;
- case Intrinsic::riscv_mulhrsu_i32:
- Opc = RISCVISD::MULHRSU;
- break;
- }
+ unsigned Opc = getRVScalarMulHighOpcode(IntNo);
return DAG.getNode(Opc, DL, MVT::i32, Op.getOperand(1), Op.getOperand(2));
}
case Intrinsic::riscv_pmhacc:
@@ -17634,29 +17631,7 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
MVT VT = N->getSimpleValueType(0);
if (!Subtarget.is64Bit() || VT != MVT::i32)
return;
- unsigned Opc;
- switch (IntNo) {
- default:
- llvm_unreachable("unexpected multiply high intrinsic");
- case Intrinsic::riscv_mulh_i32:
- Opc = ISD::MULHS;
- break;
- case Intrinsic::riscv_mulhu_u32:
- Opc = ISD::MULHU;
- break;
- case Intrinsic::riscv_mulhsu_i32:
- Opc = RISCVISD::MULHSU;
- break;
- case Intrinsic::riscv_mulhr_i32:
- Opc = RISCVISD::MULHR;
- break;
- case Intrinsic::riscv_mulhru_u32:
- Opc = RISCVISD::MULHRU;
- break;
- case Intrinsic::riscv_mulhrsu_i32:
- Opc = RISCVISD::MULHRSU;
- break;
- }
+ unsigned Opc = getRVScalarMulHighOpcode(IntNo);
SDValue Rd =
DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2i32, N->getOperand(1));
SDValue Rs =
More information about the llvm-commits
mailing list