[clang] cb5e1ce - [Clang][RISCV] Add packed widening multiply intrinsics (#217534)

via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 21 10:49:07 PDT 2026


Author: 陈子昂
Date: 2026-08-22T01:49:02+08:00
New Revision: cb5e1ce91a8aca87680d22a92430c6e896c93cb1

URL: https://github.com/llvm/llvm-project/commit/cb5e1ce91a8aca87680d22a92430c6e896c93cb1
DIFF: https://github.com/llvm/llvm-project/commit/cb5e1ce91a8aca87680d22a92430c6e896c93cb1.diff

LOG: [Clang][RISCV] Add packed widening multiply intrinsics (#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.

Added: 
    

Modified: 
    clang/lib/Headers/riscv_packed_simd.h
    clang/test/CodeGen/RISCV/rvp-intrinsics.c
    cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
    llvm/lib/Target/RISCV/RISCVISelLowering.cpp
    llvm/lib/Target/RISCV/RISCVInstrInfoP.td
    llvm/test/CodeGen/RISCV/rvp-simd-32.ll
    llvm/test/CodeGen/RISCV/rvp-simd-64.ll

Removed: 
    


################################################################################
diff  --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h
index 47d82dbcfb60a..318900805043c 100644
--- a/clang/lib/Headers/riscv_packed_simd.h
+++ b/clang/lib/Headers/riscv_packed_simd.h
@@ -142,6 +142,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);           \
@@ -592,6 +604,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)
@@ -961,6 +982,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 3a558d9750db2..87bc81ed7a645 100644
--- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c
+++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c
@@ -8032,6 +8032,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 9462d41bde855..75dcb796b66cd 100644
--- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
+++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c
@@ -2293,6 +2293,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 5bd12f635ac76..9406b697882d7 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -18959,6 +18959,68 @@ 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 RV32Opc, RV64Opc;
+  bool IsSignedUnsigned = false;
+  if (N0IsSExt && N1IsSExt) {
+    RV32Opc = RISCVISD::PWMUL;
+    RV64Opc = VT == MVT::v4i16 ? RISCVISD::PMUL_H_B01 : RISCVISD::PMUL_W_H01;
+  } else if (N0IsZExt && N1IsZExt) {
+    RV32Opc = RISCVISD::PWMULU;
+    RV64Opc = VT == MVT::v4i16 ? RISCVISD::PMULU_H_B01 : RISCVISD::PMULU_W_H01;
+  } else {
+    IsSignedUnsigned = true;
+    RV32Opc = RISCVISD::PWMULSU;
+    RV64Opc =
+        VT == MVT::v4i16 ? RISCVISD::PMULSU_H_B00 : RISCVISD::PMULSU_W_H00;
+    if (N0IsZExt && N1IsSExt)
+      std::swap(A, B);
+  }
+
+  SDLoc DL(N);
+  if (!Subtarget.is64Bit())
+    return DAG.getNode(RV32Opc, DL, VT, A, B);
+
+  MVT LegalSrcVT = VT == MVT::v4i16 ? MVT::v8i8 : MVT::v4i16;
+  A = DAG.getNode(ISD::CONCAT_VECTORS, DL, LegalSrcVT, A, DAG.getUNDEF(SrcVT));
+  B = DAG.getNode(ISD::CONCAT_VECTORS, DL, LegalSrcVT, B, DAG.getUNDEF(SrcVT));
+
+  if (IsSignedUnsigned) {
+    SDValue Zero = DAG.getConstant(0, DL, LegalSrcVT);
+    A = DAG.getNode(RISCVISD::PZIP, DL, LegalSrcVT, A, Zero);
+    B = DAG.getNode(RISCVISD::PZIP, DL, LegalSrcVT, B, Zero);
+    return DAG.getNode(RV64Opc, DL, VT, A, B);
+  }
+
+  SDValue Zip = DAG.getNode(RISCVISD::PZIP, DL, LegalSrcVT, A, B);
+  return DAG.getNode(RV64Opc, DL, VT, Zip, Zip);
+}
+
 static SDValue performMULCombine(SDNode *N, SelectionDAG &DAG,
                                  TargetLowering::DAGCombinerInfo &DCI,
                                  const RISCVSubtarget &Subtarget) {
@@ -19002,6 +19064,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 eb11b0dda06b6..ac893d83cf3ac 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td
@@ -1855,6 +1855,23 @@ def riscv_pwmul   : RVSDNode<"PWMUL",   SDT_RISCVPackedWideningMul, [SDNPCommuta
 def riscv_pwmulu  : RVSDNode<"PWMULU",  SDT_RISCVPackedWideningMul, [SDNPCommutative]>;
 def riscv_pwmulsu : RVSDNode<"PWMULSU", SDT_RISCVPackedWideningMul>;
 
+def SDT_RISCVPackedWideningMulByHalves
+    : SDTypeProfile<1, 2, [SDTCisVec<0>,
+                           SDTCisSameAs<1, 2>,
+                           SDTCisOpSmallerThanOp<1, 0>]>;
+def riscv_pmul_h_b01
+    : RVSDNode<"PMUL_H_B01", SDT_RISCVPackedWideningMulByHalves>;
+def riscv_pmul_w_h01
+    : RVSDNode<"PMUL_W_H01", SDT_RISCVPackedWideningMulByHalves>;
+def riscv_pmulu_h_b01
+    : RVSDNode<"PMULU_H_B01", SDT_RISCVPackedWideningMulByHalves>;
+def riscv_pmulu_w_h01
+    : RVSDNode<"PMULU_W_H01", SDT_RISCVPackedWideningMulByHalves>;
+def riscv_pmulsu_h_b00
+    : RVSDNode<"PMULSU_H_B00", SDT_RISCVPackedWideningMulByHalves>;
+def riscv_pmulsu_w_h00
+    : RVSDNode<"PMULSU_W_H00", SDT_RISCVPackedWideningMulByHalves>;
+
 def SDT_RISCVWideningShiftLeft : SDTypeProfile<2, 2, [SDTCisVT<0, i32>,
                                                       SDTCisSameAs<0, 1>,
                                                       SDTCisSameAs<0, 2>,
@@ -2339,19 +2356,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
@@ -2848,6 +2864,20 @@ 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_pmul_h_b01 (v8i8 GPR:$rs1), (v8i8 GPR:$rs2))),
+            (PMUL_H_B01 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i32 (riscv_pmul_w_h01 (v4i16 GPR:$rs1), (v4i16 GPR:$rs2))),
+            (PMUL_W_H01 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i16 (riscv_pmulu_h_b01 (v8i8 GPR:$rs1), (v8i8 GPR:$rs2))),
+            (PMULU_H_B01 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i32 (riscv_pmulu_w_h01 (v4i16 GPR:$rs1), (v4i16 GPR:$rs2))),
+            (PMULU_W_H01 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v4i16 (riscv_pmulsu_h_b00 (v8i8 GPR:$rs1), (v8i8 GPR:$rs2))),
+            (PMULSU_H_B00 GPR:$rs1, GPR:$rs2)>;
+  def : Pat<(v2i32 (riscv_pmulsu_w_h00 (v4i16 GPR:$rs1), (v4i16 GPR:$rs2))),
+            (PMULSU_W_H00 GPR:$rs1, GPR:$rs2)>;
+
   // 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 a40267addf9c0..d38012912872d 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll
@@ -1356,6 +1356,116 @@ define <2 x i16> @test_pssha_s_i16x2_neg_imm_too_large(<2 x i16> %a) {
   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:
@@ -1404,23 +1514,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
@@ -1435,23 +1537,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
@@ -2314,10 +2408,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, .LBB164_2
+; CHECK-NEXT:    bnez a3, [[SELECT_BB:.LBB[0-9]+_2]]
 ; CHECK-NEXT:  # %bb.1:
 ; CHECK-NEXT:    mv a0, a2
-; CHECK-NEXT:  .LBB164_2:
+; CHECK-NEXT:  [[SELECT_BB]]:
 ; CHECK-NEXT:    ret
   %res = select i1 %cond, <2 x i16> %a, <2 x i16> %b
   ret <2 x i16> %res
@@ -2328,10 +2422,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, .LBB165_2
+; CHECK-NEXT:    bnez a3, [[SELECT_BB:.LBB[0-9]+_2]]
 ; CHECK-NEXT:  # %bb.1:
 ; CHECK-NEXT:    mv a0, a2
-; CHECK-NEXT:  .LBB165_2:
+; CHECK-NEXT:  [[SELECT_BB]]:
 ; 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 f862ea5e98d0e..96f16c3d52103 100644
--- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
+++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll
@@ -2697,18 +2697,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
@@ -2756,18 +2746,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