[clang] [llvm] [Clang][RISCV] Add packed widening multiply intrinsics (PR #217534)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 19 23:17:41 PDT 2026
https://github.com/Michael-Chen-NJU created https://github.com/llvm/llvm-project/pull/217534
Add the 32-bit forms of the RISC-V P-extension packed widening multiply intrinsics to riscv_packed_simd.h using generic extend-and-multiply IR.
Recognize the generic widening multiply pattern in the RISC-V backend and select the spec-listed RV32 instructions and RV64 composed sequences.
Add Clang CodeGen, LLVM CodeGen, and intrinsic header tests for the new forms.
>From 88bbbb1e3f57ff18c1c2e7b332499e508c728567 Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Thu, 20 Aug 2026 14:14:12 +0800
Subject: [PATCH] [Clang][RISCV] Add packed widening multiply intrinsics
---
clang/lib/Headers/riscv_packed_simd.h | 23 +++
clang/test/CodeGen/RISCV/rvp-intrinsics.c | 156 ++++++++++++++++++
.../riscv_packed_simd.c | 50 ++++++
llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 52 ++++++
llvm/lib/Target/RISCV/RISCVInstrInfoP.td | 38 +++--
llvm/test/CodeGen/RISCV/rvp-simd-32.ll | 146 +++++++++++++---
llvm/test/CodeGen/RISCV/rvp-simd-64.ll | 28 +---
7 files changed, 433 insertions(+), 60 deletions(-)
diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h
index 491e219bec297..bd146eb40eb6a 100644
--- a/clang/lib/Headers/riscv_packed_simd.h
+++ b/clang/lib/Headers/riscv_packed_simd.h
@@ -130,6 +130,18 @@ typedef uint32_t uint32x2_t __attribute__((__vector_size__(8)));
static __inline__ rty __DEFAULT_FN_ATTRS __riscv_##name(ty __rs1) { \
return __builtin_convertvector(__rs1, rty); \
}
+#define __packed_widen_mul(name, rty, ty) \
+ static __inline__ rty __DEFAULT_FN_ATTRS __riscv_##name(ty __rs1, \
+ ty __rs2) { \
+ return __builtin_convertvector(__rs1, rty) * \
+ __builtin_convertvector(__rs2, rty); \
+ }
+#define __packed_widen_mulsu(name, rty, ty1, ty2, uty) \
+ static __inline__ rty __DEFAULT_FN_ATTRS __riscv_##name(ty1 __rs1, \
+ ty2 __rs2) { \
+ return __builtin_convertvector(__rs1, rty) * \
+ (rty) __builtin_convertvector(__rs2, uty); \
+ }
#define __packed_widen_high2(name, rty, ty) \
static __inline__ rty __DEFAULT_FN_ATTRS __riscv_##name(ty __rs1) { \
return (rty)__builtin_shufflevector((ty){0}, __rs1, 0, 2, 1, 3); \
@@ -564,6 +576,15 @@ __packed_widen_high4(pwcvth_u16x4, uint16x4_t, uint8x4_t)
__packed_widen_high2(pwcvth_i32x2, int32x2_t, int16x2_t)
__packed_widen_high2(pwcvth_u32x2, uint32x2_t, uint16x2_t)
+/* Packed Widening Multiply (32-bit) */
+__packed_widen_mul(pwmul_i16x4, int16x4_t, int8x4_t)
+__packed_widen_mul(pwmul_i32x2, int32x2_t, int16x2_t)
+__packed_widen_mul(pwmulu_u16x4, uint16x4_t, uint8x4_t)
+__packed_widen_mul(pwmulu_u32x2, uint32x2_t, uint16x2_t)
+__packed_widen_mulsu(pwmulsu_i16x4, int16x4_t, int8x4_t, uint8x4_t, uint16x4_t)
+__packed_widen_mulsu(pwmulsu_i32x2, int32x2_t, int16x2_t, uint16x2_t,
+ uint32x2_t)
+
/* Packed Narrowing Convert */
__packed_narrow_even4(pncvt_i8x4, int8x4_t, int16x4_t, int8x8_t)
__packed_narrow_even4(pncvt_u8x4, uint8x4_t, uint16x4_t, uint8x8_t)
@@ -909,6 +930,8 @@ __packed_reinterpret(u32x2_i32x2, int32x2_t, uint32x2_t)
#undef __packed_merge_builtin
#undef __packed_unary_builtin
#undef __packed_widen_convert
+#undef __packed_widen_mul
+#undef __packed_widen_mulsu
#undef __packed_widen_high2
#undef __packed_widen_high4
#undef __packed_narrow_even2
diff --git a/clang/test/CodeGen/RISCV/rvp-intrinsics.c b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
index d6afc4d18cc6a..af80116d44f65 100644
--- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c
+++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
@@ -7788,6 +7788,162 @@ uint32x2_t test_pwcvtu_u32x2(uint16x2_t rs1) {
return __riscv_pwcvtu_u32x2(rs1);
}
+// RV32-LABEL: define dso_local i64 @test_pwmul_i16x4(
+// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <4 x i8>
+// RV32-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <4 x i8>
+// RV32-NEXT: [[CONV_I:%.*]] = sext <4 x i8> [[TMP0]] to <4 x i16>
+// RV32-NEXT: [[CONV3_I:%.*]] = sext <4 x i8> [[TMP1]] to <4 x i16>
+// RV32-NEXT: [[MUL_I:%.*]] = mul nsw <4 x i16> [[CONV_I]], [[CONV3_I]]
+// RV32-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[MUL_I]] to i64
+// RV32-NEXT: ret i64 [[TMP2]]
+//
+// RV64-LABEL: define dso_local i64 @test_pwmul_i16x4(
+// RV64-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <4 x i8>
+// RV64-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <4 x i8>
+// RV64-NEXT: [[CONV_I:%.*]] = sext <4 x i8> [[TMP0]] to <4 x i16>
+// RV64-NEXT: [[CONV3_I:%.*]] = sext <4 x i8> [[TMP1]] to <4 x i16>
+// RV64-NEXT: [[MUL_I:%.*]] = mul nsw <4 x i16> [[CONV_I]], [[CONV3_I]]
+// RV64-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[MUL_I]] to i64
+// RV64-NEXT: ret i64 [[TMP2]]
+//
+int16x4_t test_pwmul_i16x4(int8x4_t rs1, int8x4_t rs2) {
+ return __riscv_pwmul_i16x4(rs1, rs2);
+}
+
+// RV32-LABEL: define dso_local i64 @test_pwmul_i32x2(
+// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// RV32-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// RV32-NEXT: [[CONV_I:%.*]] = sext <2 x i16> [[TMP0]] to <2 x i32>
+// RV32-NEXT: [[CONV3_I:%.*]] = sext <2 x i16> [[TMP1]] to <2 x i32>
+// RV32-NEXT: [[MUL_I:%.*]] = mul nsw <2 x i32> [[CONV_I]], [[CONV3_I]]
+// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[MUL_I]] to i64
+// RV32-NEXT: ret i64 [[TMP2]]
+//
+// RV64-LABEL: define dso_local i64 @test_pwmul_i32x2(
+// RV64-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// RV64-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// RV64-NEXT: [[CONV_I:%.*]] = sext <2 x i16> [[TMP0]] to <2 x i32>
+// RV64-NEXT: [[CONV3_I:%.*]] = sext <2 x i16> [[TMP1]] to <2 x i32>
+// RV64-NEXT: [[MUL_I:%.*]] = mul nsw <2 x i32> [[CONV_I]], [[CONV3_I]]
+// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[MUL_I]] to i64
+// RV64-NEXT: ret i64 [[TMP2]]
+//
+int32x2_t test_pwmul_i32x2(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pwmul_i32x2(rs1, rs2);
+}
+
+// RV32-LABEL: define dso_local i64 @test_pwmulu_u16x4(
+// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <4 x i8>
+// RV32-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <4 x i8>
+// RV32-NEXT: [[CONV_I:%.*]] = zext <4 x i8> [[TMP0]] to <4 x i16>
+// RV32-NEXT: [[CONV3_I:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16>
+// RV32-NEXT: [[MUL_I:%.*]] = mul nuw <4 x i16> [[CONV_I]], [[CONV3_I]]
+// RV32-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[MUL_I]] to i64
+// RV32-NEXT: ret i64 [[TMP2]]
+//
+// RV64-LABEL: define dso_local i64 @test_pwmulu_u16x4(
+// RV64-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <4 x i8>
+// RV64-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <4 x i8>
+// RV64-NEXT: [[CONV_I:%.*]] = zext <4 x i8> [[TMP0]] to <4 x i16>
+// RV64-NEXT: [[CONV3_I:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16>
+// RV64-NEXT: [[MUL_I:%.*]] = mul nuw <4 x i16> [[CONV_I]], [[CONV3_I]]
+// RV64-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[MUL_I]] to i64
+// RV64-NEXT: ret i64 [[TMP2]]
+//
+uint16x4_t test_pwmulu_u16x4(uint8x4_t rs1, uint8x4_t rs2) {
+ return __riscv_pwmulu_u16x4(rs1, rs2);
+}
+
+// RV32-LABEL: define dso_local i64 @test_pwmulu_u32x2(
+// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// RV32-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// RV32-NEXT: [[CONV_I:%.*]] = zext <2 x i16> [[TMP0]] to <2 x i32>
+// RV32-NEXT: [[CONV3_I:%.*]] = zext <2 x i16> [[TMP1]] to <2 x i32>
+// RV32-NEXT: [[MUL_I:%.*]] = mul nuw <2 x i32> [[CONV_I]], [[CONV3_I]]
+// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[MUL_I]] to i64
+// RV32-NEXT: ret i64 [[TMP2]]
+//
+// RV64-LABEL: define dso_local i64 @test_pwmulu_u32x2(
+// RV64-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// RV64-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// RV64-NEXT: [[CONV_I:%.*]] = zext <2 x i16> [[TMP0]] to <2 x i32>
+// RV64-NEXT: [[CONV3_I:%.*]] = zext <2 x i16> [[TMP1]] to <2 x i32>
+// RV64-NEXT: [[MUL_I:%.*]] = mul nuw <2 x i32> [[CONV_I]], [[CONV3_I]]
+// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[MUL_I]] to i64
+// RV64-NEXT: ret i64 [[TMP2]]
+//
+uint32x2_t test_pwmulu_u32x2(uint16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pwmulu_u32x2(rs1, rs2);
+}
+
+// RV32-LABEL: define dso_local i64 @test_pwmulsu_i16x4(
+// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <4 x i8>
+// RV32-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <4 x i8>
+// RV32-NEXT: [[CONV_I:%.*]] = sext <4 x i8> [[TMP0]] to <4 x i16>
+// RV32-NEXT: [[CONV3_I:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16>
+// RV32-NEXT: [[MUL_I:%.*]] = mul nsw <4 x i16> [[CONV_I]], [[CONV3_I]]
+// RV32-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[MUL_I]] to i64
+// RV32-NEXT: ret i64 [[TMP2]]
+//
+// RV64-LABEL: define dso_local i64 @test_pwmulsu_i16x4(
+// RV64-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <4 x i8>
+// RV64-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <4 x i8>
+// RV64-NEXT: [[CONV_I:%.*]] = sext <4 x i8> [[TMP0]] to <4 x i16>
+// RV64-NEXT: [[CONV3_I:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16>
+// RV64-NEXT: [[MUL_I:%.*]] = mul nsw <4 x i16> [[CONV_I]], [[CONV3_I]]
+// RV64-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[MUL_I]] to i64
+// RV64-NEXT: ret i64 [[TMP2]]
+//
+int16x4_t test_pwmulsu_i16x4(int8x4_t rs1, uint8x4_t rs2) {
+ return __riscv_pwmulsu_i16x4(rs1, rs2);
+}
+
+// RV32-LABEL: define dso_local i64 @test_pwmulsu_i32x2(
+// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// RV32-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// RV32-NEXT: [[CONV_I:%.*]] = sext <2 x i16> [[TMP0]] to <2 x i32>
+// RV32-NEXT: [[CONV3_I:%.*]] = zext <2 x i16> [[TMP1]] to <2 x i32>
+// RV32-NEXT: [[MUL_I:%.*]] = mul nsw <2 x i32> [[CONV_I]], [[CONV3_I]]
+// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[MUL_I]] to i64
+// RV32-NEXT: ret i64 [[TMP2]]
+//
+// RV64-LABEL: define dso_local i64 @test_pwmulsu_i32x2(
+// RV64-SAME: i32 noundef [[RS1_COERCE:%.*]], i32 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[RS1_COERCE]] to <2 x i16>
+// RV64-NEXT: [[TMP1:%.*]] = bitcast i32 [[RS2_COERCE]] to <2 x i16>
+// RV64-NEXT: [[CONV_I:%.*]] = sext <2 x i16> [[TMP0]] to <2 x i32>
+// RV64-NEXT: [[CONV3_I:%.*]] = zext <2 x i16> [[TMP1]] to <2 x i32>
+// RV64-NEXT: [[MUL_I:%.*]] = mul nsw <2 x i32> [[CONV_I]], [[CONV3_I]]
+// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[MUL_I]] to i64
+// RV64-NEXT: ret i64 [[TMP2]]
+//
+int32x2_t test_pwmulsu_i32x2(int16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pwmulsu_i32x2(rs1, rs2);
+}
+
// RV32-LABEL: define dso_local i64 @test_pwcvth_i16x4(
// RV32-SAME: i32 noundef [[RS1_COERCE:%.*]]) #[[ATTR0]] {
// RV32-NEXT: [[ENTRY:.*:]]
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 eac091c66bba0..74635cda5acd2 100644
--- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
+++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
@@ -2133,6 +2133,56 @@ uint32x2_t test_pwcvth_u32x2(uint16x2_t rs1) {
return __riscv_pwcvth_u32x2(rs1);
}
+// CHECK-LABEL: test_pwmul_i16x4:
+// RV32: pwmul.b
+// RV64: zip8p
+// RV64: pmul.h.b01
+int16x4_t test_pwmul_i16x4(int8x4_t rs1, int8x4_t rs2) {
+ return __riscv_pwmul_i16x4(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pwmul_i32x2:
+// RV32: pwmul.h
+// RV64: zip16p
+// RV64: pmul.w.h01
+int32x2_t test_pwmul_i32x2(int16x2_t rs1, int16x2_t rs2) {
+ return __riscv_pwmul_i32x2(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pwmulu_u16x4:
+// RV32: pwmulu.b
+// RV64: zip8p
+// RV64: pmulu.h.b01
+uint16x4_t test_pwmulu_u16x4(uint8x4_t rs1, uint8x4_t rs2) {
+ return __riscv_pwmulu_u16x4(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pwmulu_u32x2:
+// RV32: pwmulu.h
+// RV64: zip16p
+// RV64: pmulu.w.h01
+uint32x2_t test_pwmulu_u32x2(uint16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pwmulu_u32x2(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pwmulsu_i16x4:
+// RV32: pwmulsu.b
+// RV64: pwcvtu.wb
+// RV64: pwcvtu.wb
+// RV64: pmulsu.h.b00
+int16x4_t test_pwmulsu_i16x4(int8x4_t rs1, uint8x4_t rs2) {
+ return __riscv_pwmulsu_i16x4(rs1, rs2);
+}
+
+// CHECK-LABEL: test_pwmulsu_i32x2:
+// RV32: pwmulsu.h
+// RV64: pwcvtu.wh
+// RV64: pwcvtu.wh
+// RV64: pmulsu.w.h00
+int32x2_t test_pwmulsu_i32x2(int16x2_t rs1, uint16x2_t rs2) {
+ return __riscv_pwmulsu_i32x2(rs1, rs2);
+}
+
// CHECK-LABEL: test_pncvt_i8x4:
// RV32: pncvt.b
// RV64: pncvt.wb
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 44359a9e6155f..c177a126b54e2 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -18795,6 +18795,55 @@ static SDValue combineVectorMulToSraBitcast(SDNode *N, SelectionDAG &DAG) {
return DAG.getNode(ISD::BITCAST, DL, VT, Sra);
}
+static SDValue combinePExtWideningMul(SDNode *N, SelectionDAG &DAG,
+ const RISCVSubtarget &Subtarget) {
+ if (!Subtarget.hasStdExtP())
+ return SDValue();
+
+ EVT VT = N->getValueType(0);
+ if (VT != MVT::v4i16 && VT != MVT::v2i32)
+ return SDValue();
+
+ SDValue N0 = N->getOperand(0);
+ SDValue N1 = N->getOperand(1);
+ bool N0IsSExt = N0.getOpcode() == ISD::SIGN_EXTEND;
+ bool N0IsZExt = N0.getOpcode() == ISD::ZERO_EXTEND;
+ bool N1IsSExt = N1.getOpcode() == ISD::SIGN_EXTEND;
+ bool N1IsZExt = N1.getOpcode() == ISD::ZERO_EXTEND;
+
+ if (!(N0IsSExt || N0IsZExt) || !(N1IsSExt || N1IsZExt) || !N0.hasOneUse() ||
+ !N1.hasOneUse())
+ return SDValue();
+
+ SDValue A = N0.getOperand(0);
+ SDValue B = N1.getOperand(0);
+ EVT SrcVT = VT == MVT::v4i16 ? MVT::v4i8 : MVT::v2i16;
+ if (A.getValueType() != SrcVT || B.getValueType() != SrcVT)
+ return SDValue();
+
+ unsigned Opc;
+ if (N0IsSExt && N1IsSExt) {
+ Opc = RISCVISD::PWMUL;
+ } else if (N0IsZExt && N1IsZExt) {
+ Opc = RISCVISD::PWMULU;
+ } else {
+ Opc = RISCVISD::PWMULSU;
+ if (N0IsZExt && N1IsSExt)
+ std::swap(A, B);
+ }
+
+ if (Subtarget.is64Bit()) {
+ MVT LegalSrcVT = VT == MVT::v4i16 ? MVT::v8i8 : MVT::v4i16;
+ SDLoc DL(N);
+ A = DAG.getNode(ISD::CONCAT_VECTORS, DL, LegalSrcVT, A,
+ DAG.getUNDEF(SrcVT));
+ B = DAG.getNode(ISD::CONCAT_VECTORS, DL, LegalSrcVT, B,
+ DAG.getUNDEF(SrcVT));
+ }
+
+ return DAG.getNode(Opc, SDLoc(N), VT, A, B);
+}
+
static SDValue performMULCombine(SDNode *N, SelectionDAG &DAG,
TargetLowering::DAGCombinerInfo &DCI,
const RISCVSubtarget &Subtarget) {
@@ -18838,6 +18887,9 @@ static SDValue performMULCombine(SDNode *N, SelectionDAG &DAG,
if (SDValue V = combineBinOpOfZExt(N, DAG))
return V;
+ if (SDValue V = combinePExtWideningMul(N, DAG, Subtarget))
+ return V;
+
if (SDValue V = combineVectorMulToSraBitcast(N, DAG))
return V;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
index f09df843e0c40..79589ffbc8ca5 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
@@ -1837,8 +1837,7 @@ def riscv_wmulsu : RVSDNode<"WMULSU", SDTIntBinHiLoOp>;
def SDT_RISCVPackedWideningMul : SDTypeProfile<1, 2, [SDTCisVec<0>,
SDTCisSameAs<1, 2>,
- SDTCisOpSmallerThanOp<1, 0>,
- SDTCisSameNumEltsAs<0, 1>]>;
+ SDTCisOpSmallerThanOp<1, 0>]>;
def riscv_pwmul : RVSDNode<"PWMUL", SDT_RISCVPackedWideningMul, [SDNPCommutative]>;
def riscv_pwmulu : RVSDNode<"PWMULU", SDT_RISCVPackedWideningMul, [SDNPCommutative]>;
def riscv_pwmulsu : RVSDNode<"PWMULSU", SDT_RISCVPackedWideningMul>;
@@ -2296,19 +2295,18 @@ let append Predicates = [IsRV32] in {
(XLenVT (PPAIRE_B zexti8:$op1rs1, GPR:$op1rs2)))>;
// Packed widening multiply patterns.
- // The v2i32 patterns are not needed yet.
def : Pat<(v4i16 (riscv_pwmul (v4i8 GPR:$rs1), (v4i8 GPR:$rs2))),
- (PWMUL_B GPR:$rs1, GPR:$rs2)>;
+ (PWMUL_B GPR:$rs1, GPR:$rs2)>;
def : Pat<(v2i32 (riscv_pwmul (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
- (PWMUL_H GPR:$rs1, GPR:$rs2)>;
+ (PWMUL_H GPR:$rs1, GPR:$rs2)>;
def : Pat<(v4i16 (riscv_pwmulu (v4i8 GPR:$rs1), (v4i8 GPR:$rs2))),
- (PWMULU_B GPR:$rs1, GPR:$rs2)>;
- //def : Pat<(v2i32 (riscv_pwmulu (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
- // (PWMULU_H GPR:$rs1, GPR:$rs2)>;
+ (PWMULU_B GPR:$rs1, GPR:$rs2)>;
+ def : Pat<(v2i32 (riscv_pwmulu (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
+ (PWMULU_H GPR:$rs1, GPR:$rs2)>;
def : Pat<(v4i16 (riscv_pwmulsu (v4i8 GPR:$rs1), (v4i8 GPR:$rs2))),
(PWMULSU_B GPR:$rs1, GPR:$rs2)>;
- //def : Pat<(v2i32 (riscv_pwmulsu (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
- // (PWMULSU_H GPR:$rs1, GPR:$rs2)>;
+ def : Pat<(v2i32 (riscv_pwmulsu (v2i16 GPR:$rs1), (v2i16 GPR:$rs2))),
+ (PWMULSU_H GPR:$rs1, GPR:$rs2)>;
// 8/16-bit bitreverse patterns
// With Zbkb, brev8 reverses the bits within each byte directly; otherwise
@@ -2791,6 +2789,26 @@ let append Predicates = [IsRV64] in {
def : Pat<(v2i32 (mul GPR:$rs1, GPR:$rs2)),
(PACK (MUL_W00 GPR:$rs1, GPR:$rs2), (MUL_W11 GPR:$rs1, GPR:$rs2))>;
+ // Packed widening multiply patterns.
+ def : Pat<(v4i16 (riscv_pwmul (v8i8 GPR:$rs1), (v8i8 GPR:$rs2))),
+ (PMUL_H_B01 (ZIP8P GPR:$rs1, GPR:$rs2),
+ (ZIP8P GPR:$rs1, GPR:$rs2))>;
+ def : Pat<(v2i32 (riscv_pwmul (v4i16 GPR:$rs1), (v4i16 GPR:$rs2))),
+ (PMUL_W_H01 (ZIP16P GPR:$rs1, GPR:$rs2),
+ (ZIP16P GPR:$rs1, GPR:$rs2))>;
+ def : Pat<(v4i16 (riscv_pwmulu (v8i8 GPR:$rs1), (v8i8 GPR:$rs2))),
+ (PMULU_H_B01 (ZIP8P GPR:$rs1, GPR:$rs2),
+ (ZIP8P GPR:$rs1, GPR:$rs2))>;
+ def : Pat<(v2i32 (riscv_pwmulu (v4i16 GPR:$rs1), (v4i16 GPR:$rs2))),
+ (PMULU_W_H01 (ZIP16P GPR:$rs1, GPR:$rs2),
+ (ZIP16P GPR:$rs1, GPR:$rs2))>;
+ def : Pat<(v4i16 (riscv_pwmulsu (v8i8 GPR:$rs1), (v8i8 GPR:$rs2))),
+ (PMULSU_H_B00 (ZIP8P GPR:$rs1, (v8i8 X0)),
+ (ZIP8P GPR:$rs2, (v8i8 X0)))>;
+ def : Pat<(v2i32 (riscv_pwmulsu (v4i16 GPR:$rs1), (v4i16 GPR:$rs2))),
+ (PMULSU_W_H00 (ZIP16P GPR:$rs1, (v4i16 X0)),
+ (ZIP16P GPR:$rs2, (v4i16 X0)))>;
+
// 8-bit multiply high patterns
// FIXME custom lower
def : Pat<(v8i8 (mulhs GPR:$rs1, GPR:$rs2)),
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
index 8f96ec6e8a811..1c0bd28e27600 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
@@ -1300,6 +1300,116 @@ define <2 x i16> @test_psshlr_s_u16x2(<2 x i16> %a, i32 %shamt) {
ret <2 x i16> %res
}
+; Test packed widening multiply signed for v4i8
+define <4 x i16> @test_pwmul_b(<4 x i8> %a, <4 x i8> %b) {
+; RV32-LABEL: test_pwmul_b:
+; RV32: # %bb.0:
+; RV32-NEXT: pwmul.b a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pwmul_b:
+; RV64: # %bb.0:
+; RV64-NEXT: zip8p a0, a0, a1
+; RV64-NEXT: pmul.h.b01 a0, a0, a0
+; RV64-NEXT: ret
+ %a_ext = sext <4 x i8> %a to <4 x i16>
+ %b_ext = sext <4 x i8> %b to <4 x i16>
+ %res = mul <4 x i16> %a_ext, %b_ext
+ ret <4 x i16> %res
+}
+
+; Test packed widening multiply signed for v2i16
+define <2 x i32> @test_pwmul_h(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pwmul_h:
+; RV32: # %bb.0:
+; RV32-NEXT: pwmul.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pwmul_h:
+; RV64: # %bb.0:
+; RV64-NEXT: zip16p a0, a0, a1
+; RV64-NEXT: pmul.w.h01 a0, a0, a0
+; RV64-NEXT: ret
+ %a_ext = sext <2 x i16> %a to <2 x i32>
+ %b_ext = sext <2 x i16> %b to <2 x i32>
+ %res = mul <2 x i32> %a_ext, %b_ext
+ ret <2 x i32> %res
+}
+
+; Test packed widening multiply unsigned for v4i8
+define <4 x i16> @test_pwmulu_b(<4 x i8> %a, <4 x i8> %b) {
+; RV32-LABEL: test_pwmulu_b:
+; RV32: # %bb.0:
+; RV32-NEXT: pwmulu.b a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pwmulu_b:
+; RV64: # %bb.0:
+; RV64-NEXT: zip8p a0, a0, a1
+; RV64-NEXT: pmulu.h.b01 a0, a0, a0
+; RV64-NEXT: ret
+ %a_ext = zext <4 x i8> %a to <4 x i16>
+ %b_ext = zext <4 x i8> %b to <4 x i16>
+ %res = mul <4 x i16> %a_ext, %b_ext
+ ret <4 x i16> %res
+}
+
+; Test packed widening multiply unsigned for v2i16
+define <2 x i32> @test_pwmulu_h(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pwmulu_h:
+; RV32: # %bb.0:
+; RV32-NEXT: pwmulu.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pwmulu_h:
+; RV64: # %bb.0:
+; RV64-NEXT: zip16p a0, a0, a1
+; RV64-NEXT: pmulu.w.h01 a0, a0, a0
+; RV64-NEXT: ret
+ %a_ext = zext <2 x i16> %a to <2 x i32>
+ %b_ext = zext <2 x i16> %b to <2 x i32>
+ %res = mul <2 x i32> %a_ext, %b_ext
+ ret <2 x i32> %res
+}
+
+; Test packed widening multiply signed-unsigned for v4i8
+define <4 x i16> @test_pwmulsu_b(<4 x i8> %a, <4 x i8> %b) {
+; RV32-LABEL: test_pwmulsu_b:
+; RV32: # %bb.0:
+; RV32-NEXT: pwmulsu.b a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pwmulsu_b:
+; RV64: # %bb.0:
+; RV64-NEXT: pwcvtu.wb a1, a1
+; RV64-NEXT: pwcvtu.wb a0, a0
+; RV64-NEXT: pmulsu.h.b00 a0, a0, a1
+; RV64-NEXT: ret
+ %a_ext = sext <4 x i8> %a to <4 x i16>
+ %b_ext = zext <4 x i8> %b to <4 x i16>
+ %res = mul <4 x i16> %a_ext, %b_ext
+ ret <4 x i16> %res
+}
+
+; Test packed widening multiply signed-unsigned for v2i16
+define <2 x i32> @test_pwmulsu_h(<2 x i16> %a, <2 x i16> %b) {
+; RV32-LABEL: test_pwmulsu_h:
+; RV32: # %bb.0:
+; RV32-NEXT: pwmulsu.h a0, a0, a1
+; RV32-NEXT: ret
+;
+; RV64-LABEL: test_pwmulsu_h:
+; RV64: # %bb.0:
+; RV64-NEXT: pwcvtu.wh a1, a1
+; RV64-NEXT: pwcvtu.wh a0, a0
+; RV64-NEXT: pmulsu.w.h00 a0, a0, a1
+; RV64-NEXT: ret
+ %a_ext = sext <2 x i16> %a to <2 x i32>
+ %b_ext = zext <2 x i16> %b to <2 x i32>
+ %res = mul <2 x i32> %a_ext, %b_ext
+ ret <2 x i32> %res
+}
+
; Test packed multiply high signed for v4i8
define <4 x i8> @test_pmulh_b(<4 x i8> %a, <4 x i8> %b) {
; RV32-LABEL: test_pmulh_b:
@@ -1348,23 +1458,15 @@ define <4 x i8> @test_pmulhu_b(<4 x i8> %a, <4 x i8> %b) {
define <4 x i8> @test_pmulhsu_b(<4 x i8> %a, <4 x i8> %b) {
; RV32-LABEL: test_pmulhsu_b:
; RV32: # %bb.0:
-; RV32-NEXT: pwcvt.b a2, a0
-; RV32-NEXT: pwcvtu.b a0, a1
-; RV32-NEXT: pwmul.h a4, a3, a1
-; RV32-NEXT: pncvt.h a1, a4
-; RV32-NEXT: pwmul.h a2, a2, a0
-; RV32-NEXT: pncvt.h a0, a2
+; RV32-NEXT: pwmulsu.b a0, a0, a1
; RV32-NEXT: pncvth.b a0, a0
; RV32-NEXT: ret
;
; RV64-LABEL: test_pmulhsu_b:
; RV64: # %bb.0:
-; RV64-NEXT: pwcvtu.wb a0, a0
-; RV64-NEXT: psext.h.b a0, a0
; RV64-NEXT: pwcvtu.wb a1, a1
-; RV64-NEXT: pmul.w.h11 a2, a0, a1
-; RV64-NEXT: pmul.w.h00 a0, a0, a1
-; RV64-NEXT: ppaire.h a0, a0, a2
+; RV64-NEXT: pwcvtu.wb a0, a0
+; RV64-NEXT: pmulsu.h.b00 a0, a0, a1
; RV64-NEXT: psrli.h a0, a0, 8
; RV64-NEXT: pncvt.wb a0, a0
; RV64-NEXT: ret
@@ -1379,23 +1481,15 @@ define <4 x i8> @test_pmulhsu_b(<4 x i8> %a, <4 x i8> %b) {
define <4 x i8> @test_pmulhsu_b_commuted(<4 x i8> %a, <4 x i8> %b) {
; RV32-LABEL: test_pmulhsu_b_commuted:
; RV32: # %bb.0:
-; RV32-NEXT: pwcvtu.b a2, a0
-; RV32-NEXT: pwcvt.b a0, a1
-; RV32-NEXT: pwmul.h a4, a3, a1
-; RV32-NEXT: pncvt.h a1, a4
-; RV32-NEXT: pwmul.h a2, a2, a0
-; RV32-NEXT: pncvt.h a0, a2
+; RV32-NEXT: pwmulsu.b a0, a1, a0
; RV32-NEXT: pncvth.b a0, a0
; RV32-NEXT: ret
;
; RV64-LABEL: test_pmulhsu_b_commuted:
; RV64: # %bb.0:
-; RV64-NEXT: pwcvtu.wb a1, a1
; RV64-NEXT: pwcvtu.wb a0, a0
-; RV64-NEXT: psext.h.b a1, a1
-; RV64-NEXT: pmul.w.h11 a2, a0, a1
-; RV64-NEXT: pmul.w.h00 a0, a0, a1
-; RV64-NEXT: ppaire.h a0, a0, a2
+; RV64-NEXT: pwcvtu.wb a1, a1
+; RV64-NEXT: pmulsu.h.b00 a0, a1, a0
; RV64-NEXT: psrli.h a0, a0, 8
; RV64-NEXT: pncvt.wb a0, a0
; RV64-NEXT: ret
@@ -2258,10 +2352,10 @@ define <2 x i16> @test_select_v2i16(i1 %cond, <2 x i16> %a, <2 x i16> %b) {
; CHECK: # %bb.0:
; CHECK-NEXT: andi a3, a0, 1
; CHECK-NEXT: mv a0, a1
-; CHECK-NEXT: bnez a3, .LBB158_2
+; CHECK-NEXT: bnez a3, .LBB164_2
; CHECK-NEXT: # %bb.1:
; CHECK-NEXT: mv a0, a2
-; CHECK-NEXT: .LBB158_2:
+; CHECK-NEXT: .LBB164_2:
; CHECK-NEXT: ret
%res = select i1 %cond, <2 x i16> %a, <2 x i16> %b
ret <2 x i16> %res
@@ -2272,10 +2366,10 @@ define <4 x i8> @test_select_v4i8(i1 %cond, <4 x i8> %a, <4 x i8> %b) {
; CHECK: # %bb.0:
; CHECK-NEXT: andi a3, a0, 1
; CHECK-NEXT: mv a0, a1
-; CHECK-NEXT: bnez a3, .LBB159_2
+; CHECK-NEXT: bnez a3, .LBB165_2
; CHECK-NEXT: # %bb.1:
; CHECK-NEXT: mv a0, a2
-; CHECK-NEXT: .LBB159_2:
+; CHECK-NEXT: .LBB165_2:
; CHECK-NEXT: ret
%res = select i1 %cond, <4 x i8> %a, <4 x i8> %b
ret <4 x i8> %res
diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
index fe067d8225fc3..d73f00e49d225 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
@@ -2554,18 +2554,8 @@ define <2 x i32> @test_pmulhu_w(<2 x i32> %a, <2 x i32> %b) {
define <8 x i8> @test_pmulhsu_b(<8 x i8> %a, <8 x i8> %b) {
; RV32-LABEL: test_pmulhsu_b:
; RV32: # %bb.0:
-; RV32-NEXT: pwcvt.b a4, a0
-; RV32-NEXT: pwcvtu.b a6, a2
-; RV32-NEXT: pwcvt.b a0, a1
-; RV32-NEXT: pwmul.h t1, a5, a7
-; RV32-NEXT: pwcvtu.b a2, a3
-; RV32-NEXT: pncvt.h a5, t1
-; RV32-NEXT: pwmul.h a6, a4, a6
-; RV32-NEXT: pwmul.h t1, a1, a3
-; RV32-NEXT: pncvt.h a1, t1
-; RV32-NEXT: pwmul.h a2, a0, a2
-; RV32-NEXT: pncvt.h a4, a6
-; RV32-NEXT: pncvt.h a0, a2
+; RV32-NEXT: pwmulsu.b a4, a0, a2
+; RV32-NEXT: pwmulsu.b a0, a1, a3
; RV32-NEXT: pncvth.b a1, a0
; RV32-NEXT: pncvth.b a0, a4
; RV32-NEXT: ret
@@ -2613,18 +2603,8 @@ define <8 x i8> @test_pmulhsu_b(<8 x i8> %a, <8 x i8> %b) {
define <8 x i8> @test_pmulhsu_b_commuted(<8 x i8> %a, <8 x i8> %b) {
; RV32-LABEL: test_pmulhsu_b_commuted:
; RV32: # %bb.0:
-; RV32-NEXT: pwcvtu.b a4, a0
-; RV32-NEXT: pwcvt.b a6, a2
-; RV32-NEXT: pwcvtu.b a0, a1
-; RV32-NEXT: pwmul.h t1, a5, a7
-; RV32-NEXT: pwcvt.b a2, a3
-; RV32-NEXT: pncvt.h a5, t1
-; RV32-NEXT: pwmul.h a6, a4, a6
-; RV32-NEXT: pwmul.h t1, a1, a3
-; RV32-NEXT: pncvt.h a1, t1
-; RV32-NEXT: pwmul.h a2, a0, a2
-; RV32-NEXT: pncvt.h a4, a6
-; RV32-NEXT: pncvt.h a0, a2
+; RV32-NEXT: pwmulsu.b a4, a2, a0
+; RV32-NEXT: pwmulsu.b a0, a3, a1
; RV32-NEXT: pncvth.b a1, a0
; RV32-NEXT: pncvth.b a0, a4
; RV32-NEXT: ret
More information about the cfe-commits
mailing list