[clang] [llvm] [RISCV][P-ext] Support Packed Multiplication with Widening Horizontal Addition intrinsics (PR #232612)
Craig Topper via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 18 18:35:09 PDT 2026
https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/232612
>From 5bec3058d44c213f8818ddf169785e953eafbdab Mon Sep 17 00:00:00 2001
From: Dongyan Chen <chendongyan at isrc.iscas.ac.cn>
Date: Fri, 16 Oct 2026 06:19:28 +0000
Subject: [PATCH 1/2] [RISCV][P-ext] Add Packed Multiplication with Widening
Horizontal Addition intrinsics
---
clang/include/clang/Basic/BuiltinsRISCV.td | 8 ++
clang/lib/CodeGen/TargetBuiltins/RISCV.cpp | 27 ++++-
clang/lib/Headers/riscv_packed_simd.h | 8 ++
clang/test/CodeGen/RISCV/rvp-intrinsics.c | 72 +++++++++++++
.../riscv_packed_simd.c | 50 +++++++++
llvm/include/llvm/IR/IntrinsicsRISCV.td | 8 ++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 102 ++++++++++++++++++
llvm/lib/Target/RISCV/RISCVInstrInfoP.td | 12 +++
llvm/test/CodeGen/RISCV/rvp-simd-64.ll | 92 ++++++++++++++++
9 files changed, 378 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td
index d496333952052f..8382d025775ba2 100644
--- a/clang/include/clang/Basic/BuiltinsRISCV.td
+++ b/clang/include/clang/Basic/BuiltinsRISCV.td
@@ -384,6 +384,14 @@ def pm4add_i16x4 : RISCVBuiltin<"int64_t(_Vector<4, short>, _Vector<4, short>)">
def pm4addu_u16x4 : RISCVBuiltin<"uint64_t(_Vector<4, unsigned short>, _Vector<4, unsigned short>)">;
def pm4addsu_i16x4 : RISCVBuiltin<"int64_t(_Vector<4, short>, _Vector<4, unsigned short>)">;
+// Packed Multiplication with Widening Horizontal Addition (32-bit)
+def pm2wadd_i64 : RISCVBuiltin<"int64_t(_Vector<2, short>, _Vector<2, short>)">;
+def pm2wadd_x_i64 : RISCVBuiltin<"int64_t(_Vector<2, short>, _Vector<2, short>)">;
+def pm2waddu_u64 : RISCVBuiltin<"uint64_t(_Vector<2, unsigned short>, _Vector<2, unsigned short>)">;
+def pm2wsub_i64 : RISCVBuiltin<"int64_t(_Vector<2, short>, _Vector<2, short>)">;
+def pm2wsub_x_i64 : RISCVBuiltin<"int64_t(_Vector<2, short>, _Vector<2, short>)">;
+def pm2waddsu_i64 : RISCVBuiltin<"int64_t(_Vector<2, short>, _Vector<2, unsigned short>)">;
+
// Packed Absolute Difference Sum (32-bit)
def pabdsumu_u8x4_u32 : RISCVBuiltin<"unsigned int(_Vector<4, unsigned char>, _Vector<4, unsigned char>)">;
def pabdsumau_u8x4_u32 : RISCVBuiltin<"unsigned int(unsigned int, _Vector<4, unsigned char>, _Vector<4, unsigned char>)">;
diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
index e6f1d3c59b9460..dd93a7da75f83f 100644
--- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
@@ -1765,7 +1765,14 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
case RISCV::BI__builtin_riscv_pm4addsu_i16x4:
case RISCV::BI__builtin_riscv_pm2addsu_i16x2:
case RISCV::BI__builtin_riscv_pm2addsu_i16x4:
- case RISCV::BI__builtin_riscv_pm2addsu_i32x2: {
+ case RISCV::BI__builtin_riscv_pm2addsu_i32x2:
+ // Packed Multiplication with Widening Horizontal Addition
+ case RISCV::BI__builtin_riscv_pm2wadd_i64:
+ case RISCV::BI__builtin_riscv_pm2wadd_x_i64:
+ case RISCV::BI__builtin_riscv_pm2waddu_u64:
+ case RISCV::BI__builtin_riscv_pm2wsub_i64:
+ case RISCV::BI__builtin_riscv_pm2wsub_x_i64:
+ case RISCV::BI__builtin_riscv_pm2waddsu_i64: {
switch (BuiltinID) {
default:
llvm_unreachable("unexpected builtin ID");
@@ -1832,6 +1839,24 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
case RISCV::BI__builtin_riscv_pm2addsu_i32x2:
ID = Intrinsic::riscv_pm2addsu;
break;
+ case RISCV::BI__builtin_riscv_pm2wadd_i64:
+ ID = Intrinsic::riscv_pm2wadd;
+ break;
+ case RISCV::BI__builtin_riscv_pm2wadd_x_i64:
+ ID = Intrinsic::riscv_pm2wadd_x;
+ break;
+ case RISCV::BI__builtin_riscv_pm2waddu_u64:
+ ID = Intrinsic::riscv_pm2waddu;
+ break;
+ case RISCV::BI__builtin_riscv_pm2wsub_i64:
+ ID = Intrinsic::riscv_pm2wsub;
+ break;
+ case RISCV::BI__builtin_riscv_pm2wsub_x_i64:
+ ID = Intrinsic::riscv_pm2wsub_x;
+ break;
+ case RISCV::BI__builtin_riscv_pm2waddsu_i64:
+ ID = Intrinsic::riscv_pm2waddsu;
+ break;
}
IntrinsicTypes = {ResultType, Ops[0]->getType()};
diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h
index 4c3599e8f192bc..eb16fcfde06eef 100644
--- a/clang/lib/Headers/riscv_packed_simd.h
+++ b/clang/lib/Headers/riscv_packed_simd.h
@@ -1173,6 +1173,14 @@ __packed_binary_builtin_mixed(pm4add_i16x4, int64_t, int16x4_t, int16x4_t, __bui
__packed_binary_builtin_mixed(pm4addu_u16x4, uint64_t, uint16x4_t, uint16x4_t, __builtin_riscv_pm4addu_u16x4)
__packed_binary_builtin_mixed(pm4addsu_i16x4, int64_t, int16x4_t, uint16x4_t, __builtin_riscv_pm4addsu_i16x4)
+/* Packed Multiplication with Widening Horizontal Addition */
+__packed_binary_builtin_mixed(pm2wadd_i64, int64_t, int16x2_t, int16x2_t, __builtin_riscv_pm2wadd_i64)
+__packed_binary_builtin_mixed(pm2wadd_x_i64, int64_t, int16x2_t, int16x2_t, __builtin_riscv_pm2wadd_x_i64)
+__packed_binary_builtin_mixed(pm2waddu_u64, uint64_t, uint16x2_t, uint16x2_t, __builtin_riscv_pm2waddu_u64)
+__packed_binary_builtin_mixed(pm2wsub_i64, int64_t, int16x2_t, int16x2_t, __builtin_riscv_pm2wsub_i64)
+__packed_binary_builtin_mixed(pm2wsub_x_i64, int64_t, int16x2_t, int16x2_t, __builtin_riscv_pm2wsub_x_i64)
+__packed_binary_builtin_mixed(pm2waddsu_i64, int64_t, int16x2_t, uint16x2_t, __builtin_riscv_pm2waddsu_i64)
+
/* Packed Absolute Difference Sum (32-bit) */
__packed_abdsum(pabdsumu_u8x4_u32, uint32_t, uint8x4_t, __builtin_riscv_pabdsumu_u8x4_u32)
__packed_ternary_builtin_cast(pabdsumau_u8x4_u32, uint32_t, uint8x4_t, __builtin_riscv_pabdsumau_u8x4_u32)
diff --git a/clang/test/CodeGen/RISCV/rvp-intrinsics.c b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
index 9866b4915d2674..6aab6cebde991c 100644
--- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c
+++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
@@ -9892,3 +9892,75 @@ int32x2_t test_pslide1down_i32x2(int32x2_t rd, int32_t rs1) {
uint32x2_t test_pslide1down_u32x2(uint32x2_t rd, uint32_t rs1) {
return __riscv_pslide1down_u32x2(rd, rs1);
}
+
+// CHECK-LABEL: define dso_local i64 @test_pm2wadd_i64(
+// CHECK-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.pm2wadd.i64.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
+// CHECK-NEXT: ret i64 [[TMP2]]
+//
+int64_t test_pm2wadd_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wadd_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: define dso_local i64 @test_pm2wadd_x_i64(
+// CHECK-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.pm2wadd.x.i64.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
+// CHECK-NEXT: ret i64 [[TMP2]]
+//
+int64_t test_pm2wadd_x_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wadd_x_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: define dso_local i64 @test_pm2waddu_u64(
+// CHECK-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.pm2waddu.i64.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
+// CHECK-NEXT: ret i64 [[TMP2]]
+//
+uint64_t test_pm2waddu_u64(uint16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pm2waddu_u64(rs1, rs2);
+}
+
+// CHECK-LABEL: define dso_local i64 @test_pm2wsub_i64(
+// CHECK-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.pm2wsub.i64.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
+// CHECK-NEXT: ret i64 [[TMP2]]
+//
+int64_t test_pm2wsub_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wsub_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: define dso_local i64 @test_pm2wsub_x_i64(
+// CHECK-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.pm2wsub.x.i64.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
+// CHECK-NEXT: ret i64 [[TMP2]]
+//
+int64_t test_pm2wsub_x_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wsub_x_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: define dso_local i64 @test_pm2waddsu_i64(
+// CHECK-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.pm2waddsu.i64.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
+// CHECK-NEXT: ret i64 [[TMP2]]
+//
+int64_t test_pm2waddsu_i64(int16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pm2waddsu_i64(rs1, rs2);
+}
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 507ad0c83f776a..1a17d4a26b3226 100644
--- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
+++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
@@ -4452,6 +4452,56 @@ int64_t test_pm4addsu_i16x4(int16x4_t rs1, uint16x4_t rs2) {
return __riscv_pm4addsu_i16x4(rs1, rs2);
}
+// Packed Multiplication with Widening Horizontal Addition.
+// CHECK-LABEL: test_pm2wadd_i64:
+// RV32: pm2wadd.h
+// RV64: zext.w
+// RV64: pm4add.h
+int64_t test_pm2wadd_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wadd_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pm2wadd_x_i64:
+// RV32: pm2wadd.hx
+// RV64: ppairoe.h
+// RV64: zext.w
+// RV64: pm4add.h
+int64_t test_pm2wadd_x_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wadd_x_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pm2waddu_u64:
+// RV32: pm2waddu.h
+// RV64: zext.w
+// RV64: pm4addu.h
+uint64_t test_pm2waddu_u64(uint16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pm2waddu_u64(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pm2wsub_i64:
+// RV32: pm2wsub.h
+// RV64: pm2sub.h
+// RV64: sext.w
+int64_t test_pm2wsub_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wsub_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pm2wsub_x_i64:
+// RV32: pm2wsub.hx
+// RV64: pm2sub.hx
+// RV64: sext.w
+int64_t test_pm2wsub_x_i64(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pm2wsub_x_i64(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pm2waddsu_i64:
+// RV32: pm2waddsu.h
+// RV64: zext.w
+// RV64: pm4addsu.h
+int64_t test_pm2waddsu_i64(int16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pm2waddsu_i64(rs1, rs2);
+}
+
// Packed Multiply Parts.
// CHECK-LABEL: test_pmul_b00_i16x2:
// RV32: pmul.h.b00
diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td
index c33f0fa792f0f2..523f3b58145767 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -2294,6 +2294,14 @@ class RVPBinaryIntrinsic
def int_riscv_pm4addsu : RVPHorizontalMulIntrinsic;
def int_riscv_pm2addsu : RVPHorizontalMulIntrinsic;
+ // Packed Multiplication with Widening Horizontal Addition.
+ def int_riscv_pm2wadd : RVPHorizontalMulIntrinsic;
+ def int_riscv_pm2wadd_x : RVPHorizontalMulIntrinsic;
+ def int_riscv_pm2waddu : RVPHorizontalMulIntrinsic;
+ def int_riscv_pm2wsub : RVPHorizontalMulIntrinsic;
+ def int_riscv_pm2wsub_x : RVPHorizontalMulIntrinsic;
+ def int_riscv_pm2waddsu : RVPHorizontalMulIntrinsic;
+
// Packed Multiply Parts. The packed forms multiply the selected element of
// every pair, so the operands are the result with each element split in two.
class RVPPackedMulPartsIntrinsic
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 668e037415a9d7..1735dc92531e3f 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -12597,6 +12597,18 @@ static unsigned getRVPHorizontalMulOpcode(unsigned IntNo) {
return RISCVISD::PM4ADDSU;
case Intrinsic::riscv_pm2addsu:
return RISCVISD::PM2ADDSU;
+ case Intrinsic::riscv_pm2wadd:
+ return RISCVISD::PM2WADD;
+ case Intrinsic::riscv_pm2wadd_x:
+ return RISCVISD::PM2WADD_X;
+ case Intrinsic::riscv_pm2waddu:
+ return RISCVISD::PM2WADDU;
+ case Intrinsic::riscv_pm2wsub:
+ return RISCVISD::PM2WSUB;
+ case Intrinsic::riscv_pm2wsub_x:
+ return RISCVISD::PM2WSUB_X;
+ case Intrinsic::riscv_pm2waddsu:
+ return RISCVISD::PM2WADDSU;
}
}
@@ -12607,6 +12619,12 @@ static SDValue lowerRV32HorizontalMul64(unsigned IntNo, SDValue Rs1,
return DAG.getExtractVectorElt(DL, MVT::i32, V, Idx);
};
+ if (Rs1.getSimpleValueType() == MVT::v2i16) {
+ SDValue Res =
+ DAG.getNode(getRVPHorizontalMulOpcode(IntNo), DL, MVT::v2i32, Rs1, Rs2);
+ return DAG.getBitcast(MVT::i64, Res);
+ }
+
if (Rs1.getSimpleValueType() == MVT::v4i16) {
auto [Rs1Lo, Rs1Hi] = DAG.SplitVector(Rs1, DL);
auto [Rs2Lo, Rs2Hi] = DAG.SplitVector(Rs2, DL);
@@ -13573,6 +13591,74 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
return DAG.getNode(Opc, DL, VT, Rs1, Rs2);
}
+ case Intrinsic::riscv_pm2wadd:
+ case Intrinsic::riscv_pm2wadd_x:
+ case Intrinsic::riscv_pm2waddu:
+ case Intrinsic::riscv_pm2wsub:
+ case Intrinsic::riscv_pm2wsub_x:
+ case Intrinsic::riscv_pm2waddsu: {
+ // These intrinsics only have a dedicated widening instruction on RV32
+ // (handled in ReplaceNodeResults). On RV64 the i64 result type is legal,
+ // so synthesize the equivalent value directly at i64 width: the add forms
+ // zero-extend their operands to v4i16 and reuse pm4add(u/su).h, relying on
+ // zero lanes contributing zero products (the "_x" exchanged form
+ // additionally swaps rs2's halfwords via ppairoe.h first); the sub forms
+ // extract and sign-extend each halfword lane and compute the product
+ // difference with plain i64 arithmetic. A custom RISCVISD opcode must
+ // never be the direct result of an i32-producing DAG.getNode here: i32 is
+ // not a legal type on RV64 (only i64 has a register class), and the
+ // generic legalizer's post-hoc promotion of such a "naked" custom-opcode
+ // node mis-widens its vector operands instead of leaving the node alone.
+ assert(Subtarget.is64Bit() && Op.getValueType() == MVT::i64 &&
+ "Unexpected widening horizontal multiply-add legalization");
+ SDValue Rs1 = Op.getOperand(1);
+ SDValue Rs2 = Op.getOperand(2);
+
+ if (IntNo == Intrinsic::riscv_pm2wsub ||
+ IntNo == Intrinsic::riscv_pm2wsub_x) {
+ // Route through the existing riscv_pm2sub(_x) intrinsic (a plain
+ // ISD::INTRINSIC_WO_CHAIN, which is registered Custom for i32 on RV64)
+ // rather than calling RISCVISD::PM2SUB(_X) directly: a "naked" custom
+ // opcode freshly materialized here with an i32 result hits the same
+ // legalizer trap described above, but INTRINSIC_WO_CHAIN's i32 action
+ // is a recognized Custom target that correctly re-enters LowerOperation.
+ unsigned SubIntNo = IntNo == Intrinsic::riscv_pm2wsub
+ ? Intrinsic::riscv_pm2sub
+ : Intrinsic::riscv_pm2sub_x;
+ SDValue IntrId = DAG.getTargetConstant(SubIntNo, DL, MVT::i64);
+ SDValue Res =
+ DAG.getNode(ISD::INTRINSIC_WO_CHAIN, DL, MVT::i32, IntrId, Rs1, Rs2);
+ return DAG.getNode(ISD::SIGN_EXTEND, DL, MVT::i64, Res);
+ }
+
+ auto WidenAny = [&](SDValue V) {
+ SDValue Ext = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64,
+ DAG.getBitcast(MVT::i32, V));
+ return DAG.getBitcast(MVT::v4i16, Ext);
+ };
+ auto WidenZero = [&](SDValue V32) {
+ SDValue Ext = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, V32);
+ return DAG.getBitcast(MVT::v4i16, Ext);
+ };
+
+ SDValue A = WidenAny(Rs1);
+ SDValue B;
+ if (IntNo == Intrinsic::riscv_pm2wadd_x) {
+ SDValue Rs2Dup = WidenAny(Rs2);
+ SDValue Swapped =
+ DAG.getNode(RISCVISD::PPAIROE, DL, MVT::v4i16, Rs2Dup, Rs2Dup);
+ SDValue SwappedLo32 = DAG.getNode(ISD::TRUNCATE, DL, MVT::i32,
+ DAG.getBitcast(MVT::i64, Swapped));
+ B = WidenZero(SwappedLo32);
+ } else {
+ B = WidenZero(DAG.getBitcast(MVT::i32, Rs2));
+ }
+
+ unsigned MulOpc = IntNo == Intrinsic::riscv_pm2waddu ? RISCVISD::PM4ADDU
+ : IntNo == Intrinsic::riscv_pm2waddsu ? RISCVISD::PM4ADDSU
+ : RISCVISD::PM4ADD;
+ return DAG.getNode(MulOpc, DL, MVT::i64, A, B);
+ }
case Intrinsic::riscv_pmerge: {
EVT VT = Op.getValueType();
auto buildMerge = [&](SDValue Rs1, SDValue Rs2, SDValue Mask,
@@ -17786,6 +17872,22 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
return;
}
+ case Intrinsic::riscv_pm2wadd:
+ case Intrinsic::riscv_pm2wadd_x:
+ case Intrinsic::riscv_pm2waddu:
+ case Intrinsic::riscv_pm2wsub:
+ case Intrinsic::riscv_pm2wsub_x:
+ case Intrinsic::riscv_pm2waddsu: {
+ // i64 is only illegal here on RV32; the RV64 (legal i64) case is
+ // handled directly in LowerOperation.
+ assert(!Subtarget.is64Bit() && N->getSimpleValueType(0) == MVT::i64 &&
+ "Unexpected widening horizontal multiply-add legalization");
+ SDValue Rs1 = N->getOperand(1);
+ SDValue Rs2 = N->getOperand(2);
+ Results.push_back(lowerRV32HorizontalMul64(IntNo, Rs1, Rs2, DL, DAG));
+ return;
+ }
+
case Intrinsic::riscv_pmul_00:
case Intrinsic::riscv_pmul_01:
case Intrinsic::riscv_pmul_11:
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
index 44050f1b29bb3d..b344638a754c88 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
@@ -1925,6 +1925,12 @@ def riscv_pm2waddu
: RVSDNode<"PM2WADDU", SDT_RISCVPackedWideningHorizontalAdd>;
def riscv_pm2waddsu
: RVSDNode<"PM2WADDSU", SDT_RISCVPackedWideningHorizontalAdd>;
+def riscv_pm2wadd_x
+ : RVSDNode<"PM2WADD_X", SDT_RISCVPackedWideningHorizontalAdd>;
+def riscv_pm2wsub
+ : RVSDNode<"PM2WSUB", SDT_RISCVPackedWideningHorizontalAdd>;
+def riscv_pm2wsub_x
+ : RVSDNode<"PM2WSUB_X", SDT_RISCVPackedWideningHorizontalAdd>;
// Packed widening multiply-accumulate operations.
def SDT_RISCVPackedWideningAcc
@@ -2736,6 +2742,12 @@ let append Predicates = [IsRV32] in {
(PM2WADDU_H GPR:$rs1, GPR:$rs2)>;
def : Pat<(v2i32 (riscv_pm2waddsu (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
(PM2WADDSU_H GPR:$rs1, GPR:$rs2)>;
+ def : Pat<(v2i32 (riscv_pm2wadd_x (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
+ (PM2WADD_HX GPR:$rs1, GPR:$rs2)>;
+ def : Pat<(v2i32 (riscv_pm2wsub (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
+ (PM2WSUB_H GPR:$rs1, GPR:$rs2)>;
+ def : Pat<(v2i32 (riscv_pm2wsub_x (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
+ (PM2WSUB_HX GPR:$rs1, GPR:$rs2)>;
// Packed multiplication with widening horizontal addition and accumulate
// patterns.
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
index ee26f6ab53dd02..aae7002ea6aa2e 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
@@ -8049,6 +8049,98 @@ define i64 @test_pm4addsu_v4i16_i64(<4 x i16> %a, <4 x i16> %b) {
ret i64 %r
}
+; Packed Multiplication with Widening Horizontal Addition.
+define i64 @test_pm2wadd_v2i16_i64(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pm2wadd_v2i16_i64:
+; RV32: # %bb.0:
+; RV32-NEXT: pm2wadd.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pm2wadd_v2i16_i64:
+; RV64: # %bb.0:
+; RV64-NEXT: zext.w a1, a1
+; RV64-NEXT: pm4add.h a0, a0, a1
+; RV64-NEXT: ret
+ %r = call i64 @llvm.riscv.pm2wadd.i64.v2i16(<2 x i16> %a, <2 x i16> %b)
+ ret i64 %r
+}
+
+define i64 @test_pm2wadd_x_v2i16_i64(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pm2wadd_x_v2i16_i64:
+; RV32: # %bb.0:
+; RV32-NEXT: pm2wadd.hx a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pm2wadd_x_v2i16_i64:
+; RV64: # %bb.0:
+; RV64-NEXT: ppairoe.h a1, a1, a1
+; RV64-NEXT: zext.w a1, a1
+; RV64-NEXT: pm4add.h a0, a0, a1
+; RV64-NEXT: ret
+ %r = call i64 @llvm.riscv.pm2wadd.x.i64.v2i16(<2 x i16> %a, <2 x i16> %b)
+ ret i64 %r
+}
+
+define i64 @test_pm2waddu_v2i16_i64(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pm2waddu_v2i16_i64:
+; RV32: # %bb.0:
+; RV32-NEXT: pm2waddu.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pm2waddu_v2i16_i64:
+; RV64: # %bb.0:
+; RV64-NEXT: zext.w a1, a1
+; RV64-NEXT: pm4addu.h a0, a0, a1
+; RV64-NEXT: ret
+ %r = call i64 @llvm.riscv.pm2waddu.i64.v2i16(<2 x i16> %a, <2 x i16> %b)
+ ret i64 %r
+}
+
+define i64 @test_pm2wsub_v2i16_i64(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pm2wsub_v2i16_i64:
+; RV32: # %bb.0:
+; RV32-NEXT: pm2wsub.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pm2wsub_v2i16_i64:
+; RV64: # %bb.0:
+; RV64-NEXT: pm2sub.h a0, a0, a1
+; RV64-NEXT: sext.w a0, a0
+; RV64-NEXT: ret
+ %r = call i64 @llvm.riscv.pm2wsub.i64.v2i16(<2 x i16> %a, <2 x i16> %b)
+ ret i64 %r
+}
+
+define i64 @test_pm2wsub_x_v2i16_i64(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pm2wsub_x_v2i16_i64:
+; RV32: # %bb.0:
+; RV32-NEXT: pm2wsub.hx a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pm2wsub_x_v2i16_i64:
+; RV64: # %bb.0:
+; RV64-NEXT: pm2sub.hx a0, a0, a1
+; RV64-NEXT: sext.w a0, a0
+; RV64-NEXT: ret
+ %r = call i64 @llvm.riscv.pm2wsub.x.i64.v2i16(<2 x i16> %a, <2 x i16> %b)
+ ret i64 %r
+}
+
+define i64 @test_pm2waddsu_v2i16_i64(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pm2waddsu_v2i16_i64:
+; RV32: # %bb.0:
+; RV32-NEXT: pm2waddsu.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pm2waddsu_v2i16_i64:
+; RV64: # %bb.0:
+; RV64-NEXT: zext.w a1, a1
+; RV64-NEXT: pm4addsu.h a0, a0, a1
+; RV64-NEXT: ret
+ %r = call i64 @llvm.riscv.pm2waddsu.i64.v2i16(<2 x i16> %a, <2 x i16> %b)
+ ret i64 %r
+}
+
; Packed Multiply Parts Accumulate.
declare <2 x i32> @llvm.riscv.pmacc.00.v2i32(<2 x i32>, <4 x i16>, <4 x i16>)
declare <2 x i32> @llvm.riscv.pmacc.01.v2i32(<2 x i32>, <4 x i16>, <4 x i16>)
>From 7584f9ab06d9a94eef2e59826e390e57cb0e1b77 Mon Sep 17 00:00:00 2001
From: Dongyan Chen <chendongyan at isrc.iscas.ac.cn>
Date: Sun, 18 Oct 2026 05:29:22 +0000
Subject: [PATCH 2/2] [RISCV][P-ext] Address review comments for
pm2wadd/pm2wsub RV64 lowering
---
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 22 ++++++++-------------
1 file changed, 8 insertions(+), 14 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 1735dc92531e3f..4b09d678b7bfb1 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -12868,6 +12868,9 @@ getRVPWordMulPartsAccOpcodeAndLanes(unsigned IntNo) {
}
}
+static SDValue widenPackedVectorWithZeros(SelectionDAG &DAG, const SDLoc &DL,
+ SDValue V, MVT WideVT);
+
SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
SelectionDAG &DAG) const {
unsigned IntNo = Op.getConstantOperandVal(0);
@@ -13632,13 +13635,8 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
}
auto WidenAny = [&](SDValue V) {
- SDValue Ext = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64,
- DAG.getBitcast(MVT::i32, V));
- return DAG.getBitcast(MVT::v4i16, Ext);
- };
- auto WidenZero = [&](SDValue V32) {
- SDValue Ext = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, V32);
- return DAG.getBitcast(MVT::v4i16, Ext);
+ return DAG.getNode(ISD::CONCAT_VECTORS, DL, MVT::v4i16, V,
+ DAG.getUNDEF(MVT::v2i16));
};
SDValue A = WidenAny(Rs1);
@@ -13647,11 +13645,10 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
SDValue Rs2Dup = WidenAny(Rs2);
SDValue Swapped =
DAG.getNode(RISCVISD::PPAIROE, DL, MVT::v4i16, Rs2Dup, Rs2Dup);
- SDValue SwappedLo32 = DAG.getNode(ISD::TRUNCATE, DL, MVT::i32,
- DAG.getBitcast(MVT::i64, Swapped));
- B = WidenZero(SwappedLo32);
+ SDValue SwappedLo = DAG.getExtractSubvector(DL, MVT::v2i16, Swapped, 0);
+ B = widenPackedVectorWithZeros(DAG, DL, SwappedLo, MVT::v4i16);
} else {
- B = WidenZero(DAG.getBitcast(MVT::i32, Rs2));
+ B = widenPackedVectorWithZeros(DAG, DL, Rs2, MVT::v4i16);
}
unsigned MulOpc = IntNo == Intrinsic::riscv_pm2waddu ? RISCVISD::PM4ADDU
@@ -14512,9 +14509,6 @@ SDValue RISCVTargetLowering::lowerVPREDUCE(SDValue Op,
DAG.getConstantFP(APFloat::getNaN(ResVT.getFltSemantics()), DL, ResVT));
}
-static SDValue widenPackedVectorWithZeros(SelectionDAG &DAG, const SDLoc &DL,
- SDValue V, MVT WideVT);
-
SDValue RISCVTargetLowering::lowerINSERT_SUBVECTOR(SDValue Op,
SelectionDAG &DAG) const {
SDValue Vec = Op.getOperand(0);
More information about the cfe-commits
mailing list