[RISCV][P-ext] Support Packed "Q-format" Multiplication (#211236)

This commit is contained in:
TelGome 2026-07-23 17:15:50 +08:00 committed by GitHub
parent 09ab7a31cf
commit 58cebee609
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 367 additions and 2 deletions

View file

@ -265,6 +265,16 @@ def psabs_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>)">;
def psabs_i8x8 : RISCVBuiltin<"_Vector<8, signed char>(_Vector<8, signed char>)">;
def psabs_i16x4 : RISCVBuiltin<"_Vector<4, short>(_Vector<4, short>)">;
// Packed "Q-format" Multiplication (32-bit)
def pmulq_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Vector<2, short>)">;
def pmulqr_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Vector<2, short>)">;
// Packed "Q-format" Multiplication (64-bit)
def pmulq_i16x4 : RISCVBuiltin<"_Vector<4, short>(_Vector<4, short>, _Vector<4, short>)">;
def pmulqr_i16x4 : RISCVBuiltin<"_Vector<4, short>(_Vector<4, short>, _Vector<4, short>)">;
def pmulq_i32x2 : RISCVBuiltin<"_Vector<2, int>(_Vector<2, int>, _Vector<2, int>)">;
def pmulqr_i32x2 : RISCVBuiltin<"_Vector<2, int>(_Vector<2, int>, _Vector<2, int>)">;
} // Features = "experimental-p"
//===----------------------------------------------------------------------===//

View file

@ -1263,7 +1263,14 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
case RISCV::BI__builtin_riscv_psabs_i8x4:
case RISCV::BI__builtin_riscv_psabs_i16x2:
case RISCV::BI__builtin_riscv_psabs_i8x8:
case RISCV::BI__builtin_riscv_psabs_i16x4: {
case RISCV::BI__builtin_riscv_psabs_i16x4:
// Packed "Q-format" Multiplication
case RISCV::BI__builtin_riscv_pmulq_i16x2:
case RISCV::BI__builtin_riscv_pmulqr_i16x2:
case RISCV::BI__builtin_riscv_pmulq_i16x4:
case RISCV::BI__builtin_riscv_pmulqr_i16x4:
case RISCV::BI__builtin_riscv_pmulq_i32x2:
case RISCV::BI__builtin_riscv_pmulqr_i32x2: {
switch (BuiltinID) {
default:
llvm_unreachable("unexpected builtin ID");
@ -1355,6 +1362,16 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
case RISCV::BI__builtin_riscv_psabs_i16x4:
ID = Intrinsic::riscv_psabs;
break;
case RISCV::BI__builtin_riscv_pmulq_i16x2:
case RISCV::BI__builtin_riscv_pmulq_i16x4:
case RISCV::BI__builtin_riscv_pmulq_i32x2:
ID = Intrinsic::riscv_pmulq;
break;
case RISCV::BI__builtin_riscv_pmulqr_i16x2:
case RISCV::BI__builtin_riscv_pmulqr_i16x4:
case RISCV::BI__builtin_riscv_pmulqr_i32x2:
ID = Intrinsic::riscv_pmulqr;
break;
}
IntrinsicTypes = {ResultType};

View file

@ -690,6 +690,16 @@ __packed_psabs(psabs_i16x2, int16x2_t, __builtin_riscv_psabs_i16x2)
__packed_psabs(psabs_i8x8, int8x8_t, __builtin_riscv_psabs_i8x8)
__packed_psabs(psabs_i16x4, int16x4_t, __builtin_riscv_psabs_i16x4)
/* Packed "Q-format" Multiplication (32-bit) */
__packed_binary_builtin(pmulq_i16x2, int16x2_t, __builtin_riscv_pmulq_i16x2)
__packed_binary_builtin(pmulqr_i16x2, int16x2_t, __builtin_riscv_pmulqr_i16x2)
/* Packed "Q-format" Multiplication (64-bit) */
__packed_binary_builtin(pmulq_i16x4, int16x4_t, __builtin_riscv_pmulq_i16x4)
__packed_binary_builtin(pmulqr_i16x4, int16x4_t, __builtin_riscv_pmulqr_i16x4)
__packed_binary_builtin(pmulq_i32x2, int32x2_t, __builtin_riscv_pmulq_i32x2)
__packed_binary_builtin(pmulqr_i32x2, int32x2_t, __builtin_riscv_pmulqr_i32x2)
/* Reinterpret Casts, Packed <-> Scalar (32-bit) */
__packed_reinterpret(u8x4_u32, uint32_t, uint8x4_t)
__packed_reinterpret(u16x2_u32, uint32_t, uint16x2_t)

View file

@ -7352,6 +7352,142 @@ uint16x2_t test_pncvth_u16x2(uint32x2_t rs1) {
return __riscv_pncvth_u16x2(rs1);
}
/* Packed "Q-format" Multiplication (32-bit) */
// RV32-LABEL: define dso_local i32 @test_pmulq_i16x2(
// 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: [[TMP2:%.*]] = call <2 x i16> @llvm.riscv.pmulq.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
// RV32-NEXT: [[TMP3:%.*]] = bitcast <2 x i16> [[TMP2]] to i32
// RV32-NEXT: ret i32 [[TMP3]]
//
// RV64-LABEL: define dso_local i32 @test_pmulq_i16x2(
// 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: [[TMP2:%.*]] = call <2 x i16> @llvm.riscv.pmulq.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
// RV64-NEXT: [[TMP3:%.*]] = bitcast <2 x i16> [[TMP2]] to i32
// RV64-NEXT: ret i32 [[TMP3]]
//
int16x2_t test_pmulq_i16x2(int16x2_t rs1, int16x2_t rs2) {
return __riscv_pmulq_i16x2(rs1, rs2);
}
// RV32-LABEL: define dso_local i32 @test_pmulqr_i16x2(
// 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: [[TMP2:%.*]] = call <2 x i16> @llvm.riscv.pmulqr.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
// RV32-NEXT: [[TMP3:%.*]] = bitcast <2 x i16> [[TMP2]] to i32
// RV32-NEXT: ret i32 [[TMP3]]
//
// RV64-LABEL: define dso_local i32 @test_pmulqr_i16x2(
// 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: [[TMP2:%.*]] = call <2 x i16> @llvm.riscv.pmulqr.v2i16(<2 x i16> [[TMP0]], <2 x i16> [[TMP1]])
// RV64-NEXT: [[TMP3:%.*]] = bitcast <2 x i16> [[TMP2]] to i32
// RV64-NEXT: ret i32 [[TMP3]]
//
int16x2_t test_pmulqr_i16x2(int16x2_t rs1, int16x2_t rs2) {
return __riscv_pmulqr_i16x2(rs1, rs2);
}
/* Packed "Q-format" Multiplication (64-bit) */
// RV32-LABEL: define dso_local i64 @test_pmulq_i16x4(
// RV32-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV32-NEXT: [[ENTRY:.*:]]
// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <4 x i16>
// RV32-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <4 x i16>
// RV32-NEXT: [[TMP2:%.*]] = call <4 x i16> @llvm.riscv.pmulq.v4i16(<4 x i16> [[TMP0]], <4 x i16> [[TMP1]])
// RV32-NEXT: [[TMP3:%.*]] = bitcast <4 x i16> [[TMP2]] to i64
// RV32-NEXT: ret i64 [[TMP3]]
//
// RV64-LABEL: define dso_local i64 @test_pmulq_i16x4(
// RV64-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV64-NEXT: [[ENTRY:.*:]]
// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <4 x i16>
// RV64-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <4 x i16>
// RV64-NEXT: [[TMP2:%.*]] = call <4 x i16> @llvm.riscv.pmulq.v4i16(<4 x i16> [[TMP0]], <4 x i16> [[TMP1]])
// RV64-NEXT: [[TMP3:%.*]] = bitcast <4 x i16> [[TMP2]] to i64
// RV64-NEXT: ret i64 [[TMP3]]
//
int16x4_t test_pmulq_i16x4(int16x4_t rs1, int16x4_t rs2) {
return __riscv_pmulq_i16x4(rs1, rs2);
}
// RV32-LABEL: define dso_local i64 @test_pmulqr_i16x4(
// RV32-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV32-NEXT: [[ENTRY:.*:]]
// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <4 x i16>
// RV32-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <4 x i16>
// RV32-NEXT: [[TMP2:%.*]] = call <4 x i16> @llvm.riscv.pmulqr.v4i16(<4 x i16> [[TMP0]], <4 x i16> [[TMP1]])
// RV32-NEXT: [[TMP3:%.*]] = bitcast <4 x i16> [[TMP2]] to i64
// RV32-NEXT: ret i64 [[TMP3]]
//
// RV64-LABEL: define dso_local i64 @test_pmulqr_i16x4(
// RV64-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV64-NEXT: [[ENTRY:.*:]]
// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <4 x i16>
// RV64-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <4 x i16>
// RV64-NEXT: [[TMP2:%.*]] = call <4 x i16> @llvm.riscv.pmulqr.v4i16(<4 x i16> [[TMP0]], <4 x i16> [[TMP1]])
// RV64-NEXT: [[TMP3:%.*]] = bitcast <4 x i16> [[TMP2]] to i64
// RV64-NEXT: ret i64 [[TMP3]]
//
int16x4_t test_pmulqr_i16x4(int16x4_t rs1, int16x4_t rs2) {
return __riscv_pmulqr_i16x4(rs1, rs2);
}
// RV32-LABEL: define dso_local i64 @test_pmulq_i32x2(
// RV32-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV32-NEXT: [[ENTRY:.*:]]
// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <2 x i32>
// RV32-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <2 x i32>
// RV32-NEXT: [[TMP2:%.*]] = call <2 x i32> @llvm.riscv.pmulq.v2i32(<2 x i32> [[TMP0]], <2 x i32> [[TMP1]])
// RV32-NEXT: [[TMP3:%.*]] = bitcast <2 x i32> [[TMP2]] to i64
// RV32-NEXT: ret i64 [[TMP3]]
//
// RV64-LABEL: define dso_local i64 @test_pmulq_i32x2(
// RV64-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV64-NEXT: [[ENTRY:.*:]]
// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <2 x i32>
// RV64-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <2 x i32>
// RV64-NEXT: [[TMP2:%.*]] = call <2 x i32> @llvm.riscv.pmulq.v2i32(<2 x i32> [[TMP0]], <2 x i32> [[TMP1]])
// RV64-NEXT: [[TMP3:%.*]] = bitcast <2 x i32> [[TMP2]] to i64
// RV64-NEXT: ret i64 [[TMP3]]
//
int32x2_t test_pmulq_i32x2(int32x2_t rs1, int32x2_t rs2) {
return __riscv_pmulq_i32x2(rs1, rs2);
}
// RV32-LABEL: define dso_local i64 @test_pmulqr_i32x2(
// RV32-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV32-NEXT: [[ENTRY:.*:]]
// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <2 x i32>
// RV32-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <2 x i32>
// RV32-NEXT: [[TMP2:%.*]] = call <2 x i32> @llvm.riscv.pmulqr.v2i32(<2 x i32> [[TMP0]], <2 x i32> [[TMP1]])
// RV32-NEXT: [[TMP3:%.*]] = bitcast <2 x i32> [[TMP2]] to i64
// RV32-NEXT: ret i64 [[TMP3]]
//
// RV64-LABEL: define dso_local i64 @test_pmulqr_i32x2(
// RV64-SAME: i64 noundef [[RS1_COERCE:%.*]], i64 noundef [[RS2_COERCE:%.*]]) #[[ATTR0]] {
// RV64-NEXT: [[ENTRY:.*:]]
// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[RS1_COERCE]] to <2 x i32>
// RV64-NEXT: [[TMP1:%.*]] = bitcast i64 [[RS2_COERCE]] to <2 x i32>
// RV64-NEXT: [[TMP2:%.*]] = call <2 x i32> @llvm.riscv.pmulqr.v2i32(<2 x i32> [[TMP0]], <2 x i32> [[TMP1]])
// RV64-NEXT: [[TMP3:%.*]] = bitcast <2 x i32> [[TMP2]] to i64
// RV64-NEXT: ret i64 [[TMP3]]
//
int32x2_t test_pmulqr_i32x2(int32x2_t rs1, int32x2_t rs2) {
return __riscv_pmulqr_i32x2(rs1, rs2);
}
/* Reinterpret Casts, Packed <-> Scalar (32-bit) */
// RV32-LABEL: define dso_local i32 @test_preinterpret_u8x4_u32(
// RV32-SAME: i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] {

View file

@ -2495,6 +2495,50 @@ int8x8_t test_psabs_i8x8(int8x8_t a) { return __riscv_psabs_i8x8(a); }
// RV64: psabs.h
int16x4_t test_psabs_i16x4(int16x4_t a) { return __riscv_psabs_i16x4(a); }
// Packed "Q-format" Multiplication
// CHECK-LABEL: test_pmulq_i16x2:
// CHECK: pmulq.h
int16x2_t test_pmulq_i16x2(int16x2_t a, int16x2_t b) {
return __riscv_pmulq_i16x2(a, b);
}
// CHECK-LABEL: test_pmulqr_i16x2:
// CHECK: pmulqr.h
int16x2_t test_pmulqr_i16x2(int16x2_t a, int16x2_t b) {
return __riscv_pmulqr_i16x2(a, b);
}
// CHECK-LABEL: test_pmulq_i16x4:
// RV32: pmulq.h
// RV32: pmulq.h
// RV64: pmulq.h
int16x4_t test_pmulq_i16x4(int16x4_t a, int16x4_t b) {
return __riscv_pmulq_i16x4(a, b);
}
// CHECK-LABEL: test_pmulqr_i16x4:
// RV32: pmulqr.h
// RV32: pmulqr.h
// RV64: pmulqr.h
int16x4_t test_pmulqr_i16x4(int16x4_t a, int16x4_t b) {
return __riscv_pmulqr_i16x4(a, b);
}
// CHECK-LABEL: test_pmulq_i32x2:
// RV32: mulq
// RV32: mulq
// RV64: pmulq.w
int32x2_t test_pmulq_i32x2(int32x2_t a, int32x2_t b) {
return __riscv_pmulq_i32x2(a, b);
}
// CHECK-LABEL: test_pmulqr_i32x2:
// RV32: mulqr
// RV32: mulqr
// RV64: pmulqr.w
int32x2_t test_pmulqr_i32x2(int32x2_t a, int32x2_t b) {
return __riscv_pmulqr_i32x2(a, b);
}
// CHECK-LABEL: test_pnzip_i8x4:
// CHECK: ppaire.b
int8x4_t test_pnzip_i8x4(int16x2_t rs1, int16x2_t rs2) {

View file

@ -2061,6 +2061,10 @@ class RVPBinaryIntrinsic
def int_riscv_pabd : RVPBinaryIntrinsic;
def int_riscv_pabdu : RVPBinaryIntrinsic;
// Packed "Q-format" Multiplication.
def int_riscv_pmulq : RVPBinaryIntrinsic;
def int_riscv_pmulqr : RVPBinaryIntrinsic;
// Packed Saturating and Rounding Shifts.
class RVPShiftIntrinsic
: DefaultAttrsIntrinsic<[llvm_anyvector_ty],

View file

@ -12198,6 +12198,48 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
return DAG.getNode(Opc, DL, Op.getValueType(), Op.getOperand(1),
Op.getOperand(2));
}
case Intrinsic::riscv_pmulq:
case Intrinsic::riscv_pmulqr: {
unsigned Opc;
switch (IntNo) {
case Intrinsic::riscv_pmulq:
Opc = RISCVISD::MULQ;
break;
case Intrinsic::riscv_pmulqr:
Opc = RISCVISD::MULQR;
break;
}
EVT VT = Op.getValueType();
SDValue Rs1 = Op.getOperand(1);
SDValue Rs2 = Op.getOperand(2);
// On RV32 the 64-bit packed vectors (v2i32, v4i16) have no single Q-format
// multiply instruction. Split v4i16 into two v2i16 halves (each lowered to
// a single pmulq.h/pmulqr.h via the existing patterns), and v2i32 into two
// scalar mulq/mulqr (matched by the RV32 PatGprGpr patterns).
if (!Subtarget.is64Bit()) {
if (VT == MVT::v2i32) {
MVT XLenVT = Subtarget.getXLenVT();
SDValue Lo1 = DAG.getExtractVectorElt(DL, XLenVT, Rs1, 0);
SDValue Lo2 = DAG.getExtractVectorElt(DL, XLenVT, Rs2, 0);
SDValue Hi1 = DAG.getExtractVectorElt(DL, XLenVT, Rs1, 1);
SDValue Hi2 = DAG.getExtractVectorElt(DL, XLenVT, Rs2, 1);
SDValue LoRes = DAG.getNode(Opc, DL, XLenVT, Lo1, Lo2);
SDValue HiRes = DAG.getNode(Opc, DL, XLenVT, Hi1, Hi2);
return DAG.getNode(ISD::BUILD_VECTOR, DL, VT, LoRes, HiRes);
}
if (VT == MVT::v4i16) {
auto [Rs1Lo, Rs1Hi] = DAG.SplitVector(Rs1, DL);
auto [Rs2Lo, Rs2Hi] = DAG.SplitVector(Rs2, DL);
SDValue LoRes = DAG.getNode(Opc, DL, MVT::v2i16, Rs1Lo, Rs2Lo);
SDValue HiRes = DAG.getNode(Opc, DL, MVT::v2i16, Rs1Hi, Rs2Hi);
return DAG.getNode(ISD::CONCAT_VECTORS, DL, VT, LoRes, HiRes);
}
}
return DAG.getNode(Opc, DL, VT, Rs1, Rs2);
}
case Intrinsic::riscv_pssha:
case Intrinsic::riscv_psshar:
case Intrinsic::riscv_psshl:
@ -16344,7 +16386,9 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
case Intrinsic::riscv_paas:
case Intrinsic::riscv_pasa:
case Intrinsic::riscv_pmerge:
case Intrinsic::riscv_psabs: {
case Intrinsic::riscv_psabs:
case Intrinsic::riscv_pmulq:
case Intrinsic::riscv_pmulqr: {
EVT VT = N->getValueType(0);
if (!Subtarget.is64Bit() || (VT != MVT::v4i8 && VT != MVT::v2i16))
return;
@ -16372,6 +16416,12 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N,
case Intrinsic::riscv_psabs:
Opc = RISCVISD::PSABS;
break;
case Intrinsic::riscv_pmulq:
Opc = RISCVISD::MULQ;
break;
case Intrinsic::riscv_pmulqr:
Opc = RISCVISD::MULQR;
break;
default:
// pas/psa/psas/pssa/paas/pasa and pmerge: re-emit at the widened type
// rather than lowering to a generic node.

View file

@ -1885,6 +1885,10 @@ def riscv_mulhr : RVSDNode<"MULHR", SDTIntBinOp>;
def riscv_mulhru : RVSDNode<"MULHRU", SDTIntBinOp>;
def riscv_mulhrsu : RVSDNode<"MULHRSU", SDTIntBinOp>;
// "Q-format" multiplication
def riscv_mulq : RVSDNode<"MULQ", SDTIntBinOp>;
def riscv_mulqr : RVSDNode<"MULQR", SDTIntBinOp>;
def SDT_RISCVPackedShift : SDTypeProfile<1, 2, [SDTCisVec<0>,
SDTCisSameAs<0, 1>,
SDTCisVT<2, XLenVT>]>;
@ -2098,6 +2102,10 @@ let Predicates = [HasStdExtP] in {
def : PatGprGpr<riscv_mulhru, PMULHRU_H, XLenVecI16VT>;
def : PatGprGpr<riscv_mulhrsu, PMULHRSU_H, XLenVecI16VT>;
// 16-bit "Q-format" multiplication patterns
def : PatGprGpr<riscv_mulq, PMULQ_H, XLenVecI16VT>;
def : PatGprGpr<riscv_mulqr, PMULQR_H, XLenVecI16VT>;
// 8-bit logical shift left/right patterns
def : PatGprImm<riscv_pshl, PSLLI_B, uimm3, XLenVecI8VT>;
def : PatGprImm<riscv_psrl, PSRLI_B, uimm3, XLenVecI8VT>;
@ -2208,6 +2216,10 @@ let append Predicates = [IsRV32] in {
def : PatGprGpr<riscv_mulhru, MULHRU, i32>;
def : PatGprGpr<riscv_mulhrsu, MULHRSU, i32>;
// 32-bit "Q-format" multiplication patterns
def : PatGprGpr<riscv_mulq, MULQ, i32>;
def : PatGprGpr<riscv_mulqr, MULQR, i32>;
// Halfword multiply patterns where one operand is a sext.h or zext.h and
// the other is a sext.h or zext.h or is known to be sign/zero-extended. We
// prefer plain mul when both operands are known to be sign/zero-extended.
@ -2699,6 +2711,10 @@ let append Predicates = [IsRV64] in {
def : PatGprGpr<riscv_mulhru, PMULHRU_W, v2i32>;
def : PatGprGpr<riscv_mulhrsu, PMULHRSU_W, v2i32>;
// 32-bit "Q-format" multiplication patterns
def : PatGprGpr<riscv_mulq, PMULQ_W, v2i32>;
def : PatGprGpr<riscv_mulqr, PMULQR_W, v2i32>;
// 8/16/32-bit multiply low patterns
// FIXME custom lower
def : Pat<(v8i8 (mul GPR:$rs1, GPR:$rs2)),

View file

@ -2774,3 +2774,21 @@ define <2 x i16> @test_undef_v2i16() {
; CHECK-NEXT: ret
ret <2 x i16> undef
}
define <2 x i16> @test_pmulq_v2i16(<2 x i16> %a, <2 x i16> %b) {
; CHECK-LABEL: test_pmulq_v2i16:
; CHECK: # %bb.0:
; CHECK-NEXT: pmulq.h a0, a0, a1
; CHECK-NEXT: ret
%res = call <2 x i16> @llvm.riscv.pmulq.v2i16(<2 x i16> %a, <2 x i16> %b)
ret <2 x i16> %res
}
define <2 x i16> @test_pmulqr_v2i16(<2 x i16> %a, <2 x i16> %b) {
; CHECK-LABEL: test_pmulqr_v2i16:
; CHECK: # %bb.0:
; CHECK-NEXT: pmulqr.h a0, a0, a1
; CHECK-NEXT: ret
%res = call <2 x i16> @llvm.riscv.pmulqr.v2i16(<2 x i16> %a, <2 x i16> %b)
ret <2 x i16> %res
}

View file

@ -6074,6 +6074,66 @@ define <4 x i16> @test_psabs_v4i16(<4 x i16> %a) {
ret <4 x i16> %res
}
define <4 x i16> @test_pmulq_v4i16(<4 x i16> %a, <4 x i16> %b) {
; RV32-LABEL: test_pmulq_v4i16:
; RV32: # %bb.0:
; RV32-NEXT: pmulq.h a1, a1, a3
; RV32-NEXT: pmulq.h a0, a0, a2
; RV32-NEXT: ret
;
; RV64-LABEL: test_pmulq_v4i16:
; RV64: # %bb.0:
; RV64-NEXT: pmulq.h a0, a0, a1
; RV64-NEXT: ret
%res = call <4 x i16> @llvm.riscv.pmulq.v4i16(<4 x i16> %a, <4 x i16> %b)
ret <4 x i16> %res
}
define <4 x i16> @test_pmulqr_v4i16(<4 x i16> %a, <4 x i16> %b) {
; RV32-LABEL: test_pmulqr_v4i16:
; RV32: # %bb.0:
; RV32-NEXT: pmulqr.h a1, a1, a3
; RV32-NEXT: pmulqr.h a0, a0, a2
; RV32-NEXT: ret
;
; RV64-LABEL: test_pmulqr_v4i16:
; RV64: # %bb.0:
; RV64-NEXT: pmulqr.h a0, a0, a1
; RV64-NEXT: ret
%res = call <4 x i16> @llvm.riscv.pmulqr.v4i16(<4 x i16> %a, <4 x i16> %b)
ret <4 x i16> %res
}
define <2 x i32> @test_pmulq_v2i32(<2 x i32> %a, <2 x i32> %b) {
; RV32-LABEL: test_pmulq_v2i32:
; RV32: # %bb.0:
; RV32-NEXT: mulq a1, a1, a3
; RV32-NEXT: mulq a0, a0, a2
; RV32-NEXT: ret
;
; RV64-LABEL: test_pmulq_v2i32:
; RV64: # %bb.0:
; RV64-NEXT: pmulq.w a0, a0, a1
; RV64-NEXT: ret
%res = call <2 x i32> @llvm.riscv.pmulq.v2i32(<2 x i32> %a, <2 x i32> %b)
ret <2 x i32> %res
}
define <2 x i32> @test_pmulqr_v2i32(<2 x i32> %a, <2 x i32> %b) {
; RV32-LABEL: test_pmulqr_v2i32:
; RV32: # %bb.0:
; RV32-NEXT: mulqr a1, a1, a3
; RV32-NEXT: mulqr a0, a0, a2
; RV32-NEXT: ret
;
; RV64-LABEL: test_pmulqr_v2i32:
; RV64: # %bb.0:
; RV64-NEXT: pmulqr.w a0, a0, a1
; RV64-NEXT: ret
%res = call <2 x i32> @llvm.riscv.pmulqr.v2i32(<2 x i32> %a, <2 x i32> %b)
ret <2 x i32> %res
}
define <2 x i32> @test_return_zero() {
; RV32-LABEL: test_return_zero:
; RV32: # %bb.0: