llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-backend-risc-v Author: SiHuaN (sihuan) <details> <summary>Changes</summary> Add the scalar "Q-format" Multiplication intrinsics from the P-extension intrinsic spec: https://github.com/riscv/riscv-p-spec/blob/master/P-ext-intrinsics.adoc#q-format-multiplication - `__riscv_mulq_i32` - `__riscv_mulqr_i32` RV32 selects `mulq`/`mulqr` directly. RV64 has no scalar forms, so the intrinsics use `pmulq.w`/`pmulqr.w` on the low words, the same way as the scalar `aadd`/`asub` intrinsics (#<!-- -->230710). --- Full diff: https://github.com/llvm/llvm-project/pull/232316.diff 9 Files Affected: - (modified) clang/include/clang/Basic/BuiltinsRISCV.td (+4) - (modified) clang/lib/CodeGen/TargetBuiltins/RISCV.cpp (+8) - (modified) clang/lib/Headers/riscv_packed_simd.h (+4) - (modified) clang/test/CodeGen/RISCV/rvp-intrinsics.c (+34) - (modified) cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c (+15) - (modified) llvm/include/llvm/IR/IntrinsicsRISCV.td (+5) - (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+34) - (modified) llvm/test/CodeGen/RISCV/rv32p.ll (+18) - (modified) llvm/test/CodeGen/RISCV/rv64p.ll (+18) ``````````diff diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td index 9004c815c8a1e..44ab52124d117 100644 --- a/clang/include/clang/Basic/BuiltinsRISCV.td +++ b/clang/include/clang/Basic/BuiltinsRISCV.td @@ -278,6 +278,10 @@ def mulhru_u32 : RISCVBuiltin<"uint32_t(uint32_t, uint32_t)">; def mulhsu_i32 : RISCVBuiltin<"int32_t(int32_t, uint32_t)">; def mulhrsu_i32 : RISCVBuiltin<"int32_t(int32_t, uint32_t)">; +// Scalar "Q-format" Multiplication +def mulq_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">; +def mulqr_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">; + // Packed Multiply High (32-bit) def pmulh_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Vector<2, short>)">; def pmulhr_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Vector<2, short>)">; diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp index e6f1d3c59b946..08c237196d5a0 100644 --- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp @@ -1719,6 +1719,14 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, break; } + // Scalar "Q-format" Multiplication + case RISCV::BI__builtin_riscv_mulq_i32: + ID = Intrinsic::riscv_mulq_i32; + break; + case RISCV::BI__builtin_riscv_mulqr_i32: + ID = Intrinsic::riscv_mulqr_i32; + break; + // Packed "Q-format" Multiply with Widening Accumulate case RISCV::BI__builtin_riscv_pmqwacc_i32x2: case RISCV::BI__builtin_riscv_pmqrwacc_i32x2: { diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h index 4bf017f81db9f..3627776c290f6 100644 --- a/clang/lib/Headers/riscv_packed_simd.h +++ b/clang/lib/Headers/riscv_packed_simd.h @@ -478,6 +478,10 @@ __packed_mulh_builtin(mulhsu_i32, int32_t, int32_t, uint32_t) __packed_mulh_builtin(mulhrsu_i32, int32_t, int32_t, uint32_t) #undef __packed_mulh_builtin +/* Scalar "Q-format" Multiplication */ +__packed_binary_builtin(mulq_i32, int32_t, __builtin_riscv_mulq_i32) +__packed_binary_builtin(mulqr_i32, int32_t, __builtin_riscv_mulqr_i32) + /* Packed Splat (32-bit) */ __packed_splat(pmv_s_u8x4, uint8x4_t, uint8_t, __packed_splat4) __packed_splat(pmv_s_i8x4, int8x4_t, int8_t, __packed_splat4) diff --git a/clang/test/CodeGen/RISCV/rvp-intrinsics.c b/clang/test/CodeGen/RISCV/rvp-intrinsics.c index b672c04bc35d5..cdc5c7b980f4b 100644 --- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c +++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c @@ -396,6 +396,40 @@ int32_t test_mulhrsu_i32(int32_t a, uint32_t b) { return __riscv_mulhrsu_i32(a, b); } +/* Scalar "Q-format" Multiplication */ + +// RV32-LABEL: define dso_local i32 @test_mulq_i32( +// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulq.i32(i32 [[A]], i32 [[B]]) +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local signext i32 @test_mulq_i32( +// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulq.i32(i32 [[A]], i32 [[B]]) +// RV64-NEXT: ret i32 [[TMP0]] +// +int32_t test_mulq_i32(int32_t a, int32_t b) { + return __riscv_mulq_i32(a, b); +} + +// RV32-LABEL: define dso_local i32 @test_mulqr_i32( +// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulqr.i32(i32 [[A]], i32 [[B]]) +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local signext i32 @test_mulqr_i32( +// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.mulqr.i32(i32 [[A]], i32 [[B]]) +// RV64-NEXT: ret i32 [[TMP0]] +// +int32_t test_mulqr_i32(int32_t a, int32_t b) { + return __riscv_mulqr_i32(a, b); +} + /* Packed Splat (32-bit) */ // CHECK-LABEL: define dso_local i32 @test_pmv_s_u8x4( diff --git a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c index 75c5a1ff8b75d..5b580bf13cc93 100644 --- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c +++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c @@ -3122,6 +3122,21 @@ int32_t test_mulhrsu_i32(int32_t rs1, uint32_t rs2) { return __riscv_mulhrsu_i32(rs1, rs2); } +// Scalar "Q-format" multiplication +// CHECK-LABEL: test_mulq_i32: +// RV32: mulq{{[[:space:]]}} +// RV64: pmulq.w +int32_t test_mulq_i32(int32_t rs1, int32_t rs2) { + return __riscv_mulq_i32(rs1, rs2); +} + +// CHECK-LABEL: test_mulqr_i32: +// RV32: mulqr{{[[:space:]]}} +// RV64: pmulqr.w +int32_t test_mulqr_i32(int32_t rs1, int32_t rs2) { + return __riscv_mulqr_i32(rs1, rs2); +} + // CHECK-LABEL: test_pmulh_i16x2: // CHECK: pmulh.h int16x2_t test_pmulh_i16x2(int16x2_t rs1, int16x2_t rs2) { diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td index c33f0fa792f0f..926641511ae38 100644 --- a/llvm/include/llvm/IR/IntrinsicsRISCV.td +++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td @@ -2186,6 +2186,11 @@ class RVPBinaryIntrinsic def int_riscv_mulhsu_i32 : RVPScalarMulHighIntrinsic; def int_riscv_mulhrsu_i32 : RVPScalarMulHighIntrinsic; + // Scalar "Q-format" Multiplication. On RV64 these map to the packed + // pmulq.w family on the low words. + def int_riscv_mulq_i32 : RVPScalarBinaryIntrinsic; + def int_riscv_mulqr_i32 : RVPScalarBinaryIntrinsic; + // Packed Multiply High Accumulate. def int_riscv_pmhacc : RVPTernaryIntrinsic; def int_riscv_pmhracc : RVPTernaryIntrinsic; diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 00dbaeab507cb..f6afbc158baad 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -12433,6 +12433,17 @@ static unsigned getRVScalarMulHighOpcode(unsigned IntNo) { } } +static unsigned getRVScalarQFormatMulOpcode(unsigned IntNo) { + switch (IntNo) { + default: + llvm_unreachable("Unexpected RISC-V scalar Q-format multiply intrinsic"); + case Intrinsic::riscv_mulq_i32: + return RISCVISD::MULQ; + case Intrinsic::riscv_mulqr_i32: + return RISCVISD::MULQR; + } +} + static unsigned getRVPMulHighAccumulateOpcode(unsigned IntNo) { switch (IntNo) { default: @@ -13364,6 +13375,13 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, unsigned Opc = getRVScalarMulHighOpcode(IntNo); return DAG.getNode(Opc, DL, MVT::i32, Op.getOperand(1), Op.getOperand(2)); } + case Intrinsic::riscv_mulq_i32: + case Intrinsic::riscv_mulqr_i32: { + // RV32 selects the scalar P instructions directly. RV64 goes via + // ReplaceNodeResults. + unsigned Opc = getRVScalarQFormatMulOpcode(IntNo); + return DAG.getNode(Opc, DL, MVT::i32, Op.getOperand(1), Op.getOperand(2)); + } case Intrinsic::riscv_pmhacc: case Intrinsic::riscv_pmhracc: case Intrinsic::riscv_pmhaccu: @@ -18059,6 +18077,22 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, Results.push_back(DAG.getExtractVectorElt(DL, MVT::i32, Res, 0)); return; } + case Intrinsic::riscv_mulq_i32: + case Intrinsic::riscv_mulqr_i32: { + // RV64 has no scalar Q-format multiply instructions; reuse the packed + // pmulq.w family on the low words, whose element 0 is the scalar result. + MVT VT = N->getSimpleValueType(0); + if (!Subtarget.is64Bit() || VT != MVT::i32) + return; + unsigned Opc = getRVScalarQFormatMulOpcode(IntNo); + SDValue Rs1 = + DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2i32, N->getOperand(1)); + SDValue Rs2 = + DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2i32, N->getOperand(2)); + SDValue Res = DAG.getNode(Opc, DL, MVT::v2i32, Rs1, Rs2); + Results.push_back(DAG.getExtractVectorElt(DL, MVT::i32, Res, 0)); + return; + } case Intrinsic::riscv_pnclipp: case Intrinsic::riscv_pnclipup: { bool IsSigned = IntNo == Intrinsic::riscv_pnclipp; diff --git a/llvm/test/CodeGen/RISCV/rv32p.ll b/llvm/test/CodeGen/RISCV/rv32p.ll index 1a4d623427a0a..9bd8e87a1e253 100644 --- a/llvm/test/CodeGen/RISCV/rv32p.ll +++ b/llvm/test/CodeGen/RISCV/rv32p.ll @@ -2421,3 +2421,21 @@ define i32 @test_mulhrsu_i32(i32 %rs1, i32 %rs2) { %res = call i32 @llvm.riscv.mulhrsu.i32(i32 %rs1, i32 %rs2) ret i32 %res } + +define i32 @test_mulq_i32(i32 %a, i32 %b) { +; CHECK-LABEL: test_mulq_i32: +; CHECK: # %bb.0: +; CHECK-NEXT: mulq a0, a0, a1 +; CHECK-NEXT: ret + %res = call i32 @llvm.riscv.mulq.i32(i32 %a, i32 %b) + ret i32 %res +} + +define i32 @test_mulqr_i32(i32 %a, i32 %b) { +; CHECK-LABEL: test_mulqr_i32: +; CHECK: # %bb.0: +; CHECK-NEXT: mulqr a0, a0, a1 +; CHECK-NEXT: ret + %res = call i32 @llvm.riscv.mulqr.i32(i32 %a, i32 %b) + ret i32 %res +} diff --git a/llvm/test/CodeGen/RISCV/rv64p.ll b/llvm/test/CodeGen/RISCV/rv64p.ll index 5ceae2996fe3d..d94ff361cf4f1 100644 --- a/llvm/test/CodeGen/RISCV/rv64p.ll +++ b/llvm/test/CodeGen/RISCV/rv64p.ll @@ -1897,3 +1897,21 @@ define i32 @test_asubu_u32(i32 %a, i32 %b) { %res = call i32 @llvm.riscv.asubu.u32(i32 %a, i32 %b) ret i32 %res } + +define i32 @test_mulq_i32(i32 %a, i32 %b) { +; CHECK-LABEL: test_mulq_i32: +; CHECK: # %bb.0: +; CHECK-NEXT: pmulq.w a0, a0, a1 +; CHECK-NEXT: ret + %res = call i32 @llvm.riscv.mulq.i32(i32 %a, i32 %b) + ret i32 %res +} + +define i32 @test_mulqr_i32(i32 %a, i32 %b) { +; CHECK-LABEL: test_mulqr_i32: +; CHECK: # %bb.0: +; CHECK-NEXT: pmulqr.w a0, a0, a1 +; CHECK-NEXT: ret + %res = call i32 @llvm.riscv.mulqr.i32(i32 %a, i32 %b) + ret i32 %res +} `````````` </details> https://github.com/llvm/llvm-project/pull/232316 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
