https://github.com/sihuan updated https://github.com/llvm/llvm-project/pull/224432
>From 3ba62d4a6ff409b9a7faf6b0382b2197678570da Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Thu, 17 Sep 2026 20:40:14 +0000 Subject: [PATCH 1/6] [RISCV] Add OPERAND_UIMM4_PLUS1 to RISCVInstrInfo::verifyInstruction It is used by psati.h and psati.dh. --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 6d83480fa6c69..b057c6066516f 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -3046,6 +3046,9 @@ bool RISCVInstrInfo::verifyInstruction(const MachineInstr &MI, CASE_OPERAND_UIMM_LSB_ZEROS(8, 000) CASE_OPERAND_UIMM_LSB_ZEROS(9, 000) // clang-format on + case RISCVOp::OPERAND_UIMM4_PLUS1: + Ok = Imm >= 1 && Imm <= 16; + break; case RISCVOp::OPERAND_UIMM5_NONZERO: Ok = isUInt<5>(Imm) && (Imm != 0); break; >From 50f06a52304b9a6bbf11f7021f818a9a085abf3e Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Thu, 17 Sep 2026 20:21:16 +0000 Subject: [PATCH 2/6] [RISCV][P-ext] Add packed saturation intrinsics Lower llvm.riscv.psati/pusati to psati.h/psati.w and the RV32 paired psati.dh/psati.dw forms. On RV64 the 32-bit v2i16 type is widened with zeroed upper lanes. --- llvm/include/llvm/IR/IntrinsicsRISCV.td | 8 ++ llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 24 ++++ llvm/lib/Target/RISCV/RISCVInstrInfoP.td | 23 ++++ llvm/test/CodeGen/RISCV/rvp-simd-32.ll | 75 ++++++++++++ llvm/test/CodeGen/RISCV/rvp-simd-64.ll | 126 ++++++++++++++++++++ 5 files changed, 256 insertions(+) diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td index 09399b0ea3f36..cf8339c9a2a75 100644 --- a/llvm/include/llvm/IR/IntrinsicsRISCV.td +++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td @@ -2076,6 +2076,14 @@ class RVPBinaryIntrinsic def int_riscv_psshl : RVPShiftIntrinsic; def int_riscv_psshlr : RVPShiftIntrinsic; + // Packed Saturation. + class RVPSaturationIntrinsic + : DefaultAttrsIntrinsic<[llvm_anyvector_ty], + [LLVMMatchType<0>, llvm_i32_ty], + [IntrNoMem, IntrSpeculatable, ImmArg<ArgIndex<1>>]>; + def int_riscv_psati : RVPSaturationIntrinsic; + def int_riscv_pusati : RVPSaturationIntrinsic; + // Packed Exchanged Addition and Subtraction. def int_riscv_pas : RVPBinaryIntrinsic; def int_riscv_psa : RVPBinaryIntrinsic; diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 69d813fbce12d..539fb6d31f37d 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -13086,6 +13086,13 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, return DAG.getNode(getRVPShiftOpcode(IntNo), DL, Op.getValueType(), Op.getOperand(1), ShAmt); } + case Intrinsic::riscv_psati: + case Intrinsic::riscv_pusati: { + unsigned Opc = + IntNo == Intrinsic::riscv_psati ? RISCVISD::PSATI : RISCVISD::PUSATI; + SDValue Width = DAG.getAnyExtOrTrunc(Op.getOperand(2), DL, XLenVT); + return DAG.getNode(Opc, DL, Op.getValueType(), Op.getOperand(1), Width); + } case Intrinsic::riscv_psext_b: case Intrinsic::riscv_psext_h: { EVT VT = Op.getValueType(); @@ -17628,6 +17635,23 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, Results.push_back(DAG.getExtractSubvector(DL, VT, Res, 0)); return; } + case Intrinsic::riscv_psati: + case Intrinsic::riscv_pusati: { + MVT VT = N->getSimpleValueType(0); + if (!Subtarget.is64Bit() || VT != MVT::v2i16) + return; + + MVT WideVT = MVT::v4i16; + SDValue Rs1 = + widenPackedVectorWithZeros(DAG, DL, N->getOperand(1), WideVT); + SDValue Width = + DAG.getAnyExtOrTrunc(N->getOperand(2), DL, Subtarget.getXLenVT()); + unsigned Opc = + IntNo == Intrinsic::riscv_psati ? RISCVISD::PSATI : RISCVISD::PUSATI; + SDValue Res = DAG.getNode(Opc, DL, WideVT, Rs1, Width); + Results.push_back(DAG.getExtractSubvector(DL, VT, Res, 0)); + return; + } case Intrinsic::riscv_predsum: case Intrinsic::riscv_predsumu: { bool IsSigned = IntNo == Intrinsic::riscv_predsum; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td index 831a591cf6283..8adce67d0c221 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td @@ -2044,6 +2044,13 @@ def riscv_psshar : RVSDNode<"PSSHAR", SDT_RISCVPackedShift>; def riscv_psshl : RVSDNode<"PSSHL", SDT_RISCVPackedShift>; def riscv_psshlr : RVSDNode<"PSSHLR", SDT_RISCVPackedShift>; +// Packed saturation to an immediate width. +def SDT_RISCVPackedSaturation + : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, XLenVT>]>; +def riscv_psati : RVSDNode<"PSATI", SDT_RISCVPackedSaturation>; +def riscv_pusati : RVSDNode<"PUSATI", SDT_RISCVPackedSaturation>; + // RV32 packed narrowing shift. def SDT_RISCVPackedNarrowingShift : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, @@ -2338,6 +2345,10 @@ let Predicates = [HasStdExtP] in { def : PatGprShift<riscv_psshl, PSSHL_HS, XLenVecI16VT>; def : PatGprShift<riscv_psshlr, PSSHLR_HS, XLenVecI16VT>; + // 16-bit packed saturation patterns + def : PatGprImm<riscv_psati, PSATI_H, uimm4_plus1, XLenVecI16VT>; + def : PatGprImm<riscv_pusati, PUSATI_H, uimm4, XLenVecI16VT>; + // 8-bit logical shift left/right def : PatGprShiftMask<riscv_pshl, PSLL_BS, shiftMask32, XLenVecI8VT>; def : PatGprShiftMask<riscv_psrl, PSRL_BS, shiftMask32, XLenVecI8VT>; @@ -2815,6 +2826,10 @@ let append Predicates = [IsRV32] in { def : PatGprPairShift<riscv_psshl, PSSHL_DHS, v4i16>; def : PatGprPairShift<riscv_psshlr, PSSHLR_DHS, v4i16>; + // 16-bit packed saturation patterns + def : PatGprPairImm<riscv_psati, PSATI_DH, uimm4_plus1, v4i16>; + def : PatGprPairImm<riscv_pusati, PUSATI_DH, uimm4, v4i16>; + // 32-bit saturating shift patterns def : PatGprPairImm<riscv_pssha, PSSLAI_DW, uimm5, v2i32>; def : PatGprPairImm<riscv_psshar, PSSLAI_DW, uimm5, v2i32>; @@ -2825,6 +2840,10 @@ let append Predicates = [IsRV32] in { def : PatGprPairShift<riscv_psshl, PSSHL_DWS, v2i32>; def : PatGprPairShift<riscv_psshlr, PSSHLR_DWS, v2i32>; + // 32-bit packed saturation patterns + def : PatGprPairImm<riscv_psati, PSATI_DW, uimm5_plus1, v2i32>; + def : PatGprPairImm<riscv_pusati, PUSATI_DW, uimm5, v2i32>; + // 8-bit logical shift left/right def : PatGprPairShiftMask<riscv_pshl, PSLL_DBS, shiftMask32, v8i8>; def : PatGprPairShiftMask<riscv_psrl, PSRL_DBS, shiftMask32, v8i8>; @@ -3274,6 +3293,10 @@ let append Predicates = [IsRV64] in { def : PatGprShift<riscv_psshl, PSSHL_WS, v2i32>; def : PatGprShift<riscv_psshlr, PSSHLR_WS, v2i32>; + // 32-bit packed saturation patterns + def : PatGprImm<riscv_psati, PSATI_W, uimm5_plus1, v2i32>; + def : PatGprImm<riscv_pusati, PUSATI_W, uimm5, v2i32>; + // 32-bit logical shift left/right def : PatGprShiftMask<riscv_pshl, PSLL_WS, shiftMask32, v2i32>; def : PatGprShiftMask<riscv_psrl, PSRL_WS, shiftMask32, v2i32>; diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll index e9e04eb6b5ebe..5adc48d167f6e 100644 --- a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll +++ b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll @@ -3827,3 +3827,78 @@ define i32 @test_maccsu_h11_i32(i32 %rd, <2 x i16> %a, <2 x i16> %b) { %r = call i32 @llvm.riscv.maccsu.11.i32.v2i16(i32 %rd, <2 x i16> %a, <2 x i16> %b) ret i32 %r } + +define <2 x i16> @test_psati_i16x2(<2 x i16> %a) { +; RV32-LABEL: test_psati_i16x2: +; RV32: # %bb.0: +; RV32-NEXT: psati.h a0, a0, 8 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i16x2: +; RV64: # %bb.0: +; RV64-NEXT: zext.w a0, a0 +; RV64-NEXT: psati.h a0, a0, 8 +; RV64-NEXT: ret + %res = call <2 x i16> @llvm.riscv.psati.v2i16.i32(<2 x i16> %a, i32 8) + ret <2 x i16> %res +} + +define <2 x i16> @test_psati_i16x2_min_width(<2 x i16> %a) { +; RV32-LABEL: test_psati_i16x2_min_width: +; RV32: # %bb.0: +; RV32-NEXT: psati.h a0, a0, 1 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i16x2_min_width: +; RV64: # %bb.0: +; RV64-NEXT: zext.w a0, a0 +; RV64-NEXT: psati.h a0, a0, 1 +; RV64-NEXT: ret + %res = call <2 x i16> @llvm.riscv.psati.v2i16.i32(<2 x i16> %a, i32 1) + ret <2 x i16> %res +} + +define <2 x i16> @test_psati_i16x2_max_width(<2 x i16> %a) { +; RV32-LABEL: test_psati_i16x2_max_width: +; RV32: # %bb.0: +; RV32-NEXT: psati.h a0, a0, 16 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i16x2_max_width: +; RV64: # %bb.0: +; RV64-NEXT: zext.w a0, a0 +; RV64-NEXT: psati.h a0, a0, 16 +; RV64-NEXT: ret + %res = call <2 x i16> @llvm.riscv.psati.v2i16.i32(<2 x i16> %a, i32 16) + ret <2 x i16> %res +} + +define <2 x i16> @test_pusati_u16x2(<2 x i16> %a) { +; RV32-LABEL: test_pusati_u16x2: +; RV32: # %bb.0: +; RV32-NEXT: pusati.h a0, a0, 0 +; RV32-NEXT: ret +; +; RV64-LABEL: test_pusati_u16x2: +; RV64: # %bb.0: +; RV64-NEXT: zext.w a0, a0 +; RV64-NEXT: pusati.h a0, a0, 0 +; RV64-NEXT: ret + %res = call <2 x i16> @llvm.riscv.pusati.v2i16.i32(<2 x i16> %a, i32 0) + ret <2 x i16> %res +} + +define <2 x i16> @test_pusati_u16x2_max_width(<2 x i16> %a) { +; RV32-LABEL: test_pusati_u16x2_max_width: +; RV32: # %bb.0: +; RV32-NEXT: pusati.h a0, a0, 15 +; RV32-NEXT: ret +; +; RV64-LABEL: test_pusati_u16x2_max_width: +; RV64: # %bb.0: +; RV64-NEXT: zext.w a0, a0 +; RV64-NEXT: pusati.h a0, a0, 15 +; RV64-NEXT: ret + %res = call <2 x i16> @llvm.riscv.pusati.v2i16.i32(<2 x i16> %a, i32 15) + ret <2 x i16> %res +} diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll index 43107137fe1fa..0330870cb14bb 100644 --- a/llvm/test/CodeGen/RISCV/rvp-simd-64.ll +++ b/llvm/test/CodeGen/RISCV/rvp-simd-64.ll @@ -8116,3 +8116,129 @@ define i64 @test_maccsu_w11_i64(i64 %rd, <2 x i32> %a, <2 x i32> %b) { %r = call i64 @llvm.riscv.maccsu.11.i64.v2i32(i64 %rd, <2 x i32> %a, <2 x i32> %b) ret i64 %r } + +define <4 x i16> @test_psati_i16x4(<4 x i16> %a) { +; RV32-LABEL: test_psati_i16x4: +; RV32: # %bb.0: +; RV32-NEXT: psati.dh a0, a0, 8 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i16x4: +; RV64: # %bb.0: +; RV64-NEXT: psati.h a0, a0, 8 +; RV64-NEXT: ret + %res = call <4 x i16> @llvm.riscv.psati.v4i16.i32(<4 x i16> %a, i32 8) + ret <4 x i16> %res +} + +define <4 x i16> @test_psati_i16x4_min_width(<4 x i16> %a) { +; RV32-LABEL: test_psati_i16x4_min_width: +; RV32: # %bb.0: +; RV32-NEXT: psati.dh a0, a0, 1 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i16x4_min_width: +; RV64: # %bb.0: +; RV64-NEXT: psati.h a0, a0, 1 +; RV64-NEXT: ret + %res = call <4 x i16> @llvm.riscv.psati.v4i16.i32(<4 x i16> %a, i32 1) + ret <4 x i16> %res +} + +define <4 x i16> @test_psati_i16x4_max_width(<4 x i16> %a) { +; RV32-LABEL: test_psati_i16x4_max_width: +; RV32: # %bb.0: +; RV32-NEXT: psati.dh a0, a0, 16 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i16x4_max_width: +; RV64: # %bb.0: +; RV64-NEXT: psati.h a0, a0, 16 +; RV64-NEXT: ret + %res = call <4 x i16> @llvm.riscv.psati.v4i16.i32(<4 x i16> %a, i32 16) + ret <4 x i16> %res +} + +define <2 x i32> @test_psati_i32x2(<2 x i32> %a) { +; RV32-LABEL: test_psati_i32x2: +; RV32: # %bb.0: +; RV32-NEXT: psati.dw a0, a0, 16 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i32x2: +; RV64: # %bb.0: +; RV64-NEXT: psati.w a0, a0, 16 +; RV64-NEXT: ret + %res = call <2 x i32> @llvm.riscv.psati.v2i32.i32(<2 x i32> %a, i32 16) + ret <2 x i32> %res +} + +define <2 x i32> @test_psati_i32x2_max_width(<2 x i32> %a) { +; RV32-LABEL: test_psati_i32x2_max_width: +; RV32: # %bb.0: +; RV32-NEXT: psati.dw a0, a0, 32 +; RV32-NEXT: ret +; +; RV64-LABEL: test_psati_i32x2_max_width: +; RV64: # %bb.0: +; RV64-NEXT: psati.w a0, a0, 32 +; RV64-NEXT: ret + %res = call <2 x i32> @llvm.riscv.psati.v2i32.i32(<2 x i32> %a, i32 32) + ret <2 x i32> %res +} + +define <4 x i16> @test_pusati_u16x4(<4 x i16> %a) { +; RV32-LABEL: test_pusati_u16x4: +; RV32: # %bb.0: +; RV32-NEXT: pusati.dh a0, a0, 8 +; RV32-NEXT: ret +; +; RV64-LABEL: test_pusati_u16x4: +; RV64: # %bb.0: +; RV64-NEXT: pusati.h a0, a0, 8 +; RV64-NEXT: ret + %res = call <4 x i16> @llvm.riscv.pusati.v4i16.i32(<4 x i16> %a, i32 8) + ret <4 x i16> %res +} + +define <4 x i16> @test_pusati_u16x4_max_width(<4 x i16> %a) { +; RV32-LABEL: test_pusati_u16x4_max_width: +; RV32: # %bb.0: +; RV32-NEXT: pusati.dh a0, a0, 15 +; RV32-NEXT: ret +; +; RV64-LABEL: test_pusati_u16x4_max_width: +; RV64: # %bb.0: +; RV64-NEXT: pusati.h a0, a0, 15 +; RV64-NEXT: ret + %res = call <4 x i16> @llvm.riscv.pusati.v4i16.i32(<4 x i16> %a, i32 15) + ret <4 x i16> %res +} + +define <2 x i32> @test_pusati_u32x2(<2 x i32> %a) { +; RV32-LABEL: test_pusati_u32x2: +; RV32: # %bb.0: +; RV32-NEXT: pusati.dw a0, a0, 16 +; RV32-NEXT: ret +; +; RV64-LABEL: test_pusati_u32x2: +; RV64: # %bb.0: +; RV64-NEXT: pusati.w a0, a0, 16 +; RV64-NEXT: ret + %res = call <2 x i32> @llvm.riscv.pusati.v2i32.i32(<2 x i32> %a, i32 16) + ret <2 x i32> %res +} + +define <2 x i32> @test_pusati_u32x2_max_width(<2 x i32> %a) { +; RV32-LABEL: test_pusati_u32x2_max_width: +; RV32: # %bb.0: +; RV32-NEXT: pusati.dw a0, a0, 31 +; RV32-NEXT: ret +; +; RV64-LABEL: test_pusati_u32x2_max_width: +; RV64: # %bb.0: +; RV64-NEXT: pusati.w a0, a0, 31 +; RV64-NEXT: ret + %res = call <2 x i32> @llvm.riscv.pusati.v2i32.i32(<2 x i32> %a, i32 31) + ret <2 x i32> %res +} >From 3e72ecd76c9388696d08767679e91586bd980439 Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Thu, 17 Sep 2026 20:21:23 +0000 Subject: [PATCH 3/6] [Clang][RISCV] Add packed saturation intrinsics Expose __riscv_psati_* and __riscv_pusati_* from riscv_packed_simd.h. The saturation width must be a compile-time constant, so the wrappers are macros around builtins that take a _Constant argument; Sema checks the width range. --- clang/include/clang/Basic/BuiltinsRISCV.td | 10 ++ clang/lib/CodeGen/TargetBuiltins/RISCV.cpp | 26 +++++ clang/lib/Headers/riscv_packed_simd.h | 13 +++ clang/lib/Sema/SemaRISCV.cpp | 11 ++ clang/test/CodeGen/RISCV/rvp-intrinsics.c | 110 ++++++++++++++++++ .../Sema/riscv-psati-width-out-of-range.c | 41 +++++++ .../riscv_packed_simd.c | 42 +++++++ 7 files changed, 253 insertions(+) create mode 100644 clang/test/Sema/riscv-psati-width-out-of-range.c diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td index ee840e45a65ba..e97bb0e66da53 100644 --- a/clang/include/clang/Basic/BuiltinsRISCV.td +++ b/clang/include/clang/Basic/BuiltinsRISCV.td @@ -496,6 +496,16 @@ def psshl_s_u32x2 : RISCVBuiltin<"_Vector<2, unsigned int>(_Vector<2, unsigned i def psshlr_s_u16x4 : RISCVBuiltin<"_Vector<4, unsigned short>(_Vector<4, unsigned short>, int)">; def psshlr_s_u32x2 : RISCVBuiltin<"_Vector<2, unsigned int>(_Vector<2, unsigned int>, int)">; +// Packed Saturation (32-bit) +def pusati_u16x2 : RISCVBuiltin<"_Vector<2, unsigned short>(_Vector<2, short>, _Constant unsigned int)">; +def psati_i16x2 : RISCVBuiltin<"_Vector<2, short>(_Vector<2, short>, _Constant unsigned int)">; + +// Packed Saturation (64-bit) +def pusati_u16x4 : RISCVBuiltin<"_Vector<4, unsigned short>(_Vector<4, short>, _Constant unsigned int)">; +def pusati_u32x2 : RISCVBuiltin<"_Vector<2, unsigned int>(_Vector<2, int>, _Constant unsigned int)">; +def psati_i16x4 : RISCVBuiltin<"_Vector<4, short>(_Vector<4, short>, _Constant unsigned int)">; +def psati_i32x2 : RISCVBuiltin<"_Vector<2, int>(_Vector<2, int>, _Constant unsigned int)">; + } // Features = "experimental-p" //===----------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp index f99a05ce673aa..4f7b9ceae6eaa 100644 --- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp @@ -1531,6 +1531,32 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, break; } + // Packed Saturation + case RISCV::BI__builtin_riscv_psati_i16x2: + case RISCV::BI__builtin_riscv_psati_i16x4: + case RISCV::BI__builtin_riscv_psati_i32x2: + case RISCV::BI__builtin_riscv_pusati_u16x2: + case RISCV::BI__builtin_riscv_pusati_u16x4: + case RISCV::BI__builtin_riscv_pusati_u32x2: { + switch (BuiltinID) { + default: + llvm_unreachable("unexpected builtin ID"); + case RISCV::BI__builtin_riscv_psati_i16x2: + case RISCV::BI__builtin_riscv_psati_i16x4: + case RISCV::BI__builtin_riscv_psati_i32x2: + ID = Intrinsic::riscv_psati; + break; + case RISCV::BI__builtin_riscv_pusati_u16x2: + case RISCV::BI__builtin_riscv_pusati_u16x4: + case RISCV::BI__builtin_riscv_pusati_u32x2: + ID = Intrinsic::riscv_pusati; + break; + } + + IntrinsicTypes = {ResultType}; + break; + } + // Packed Multiplication with Horizontal Addition case RISCV::BI__builtin_riscv_pm4add_i8x4: case RISCV::BI__builtin_riscv_pm4add_i8x8: diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h index 8c20d00e68652..0b8c516d5fe3e 100644 --- a/clang/lib/Headers/riscv_packed_simd.h +++ b/clang/lib/Headers/riscv_packed_simd.h @@ -615,6 +615,19 @@ __packed_binary_builtin_mixed(psshl_s_u32x2, uint32x2_t, uint32x2_t, int, __buil __packed_binary_builtin_mixed(psshlr_s_u16x4, uint16x4_t, uint16x4_t, int, __builtin_riscv_psshlr_s_u16x4) __packed_binary_builtin_mixed(psshlr_s_u32x2, uint32x2_t, uint32x2_t, int, __builtin_riscv_psshlr_s_u32x2) +/* Packed Saturation (32-bit) */ +#define __riscv_pusati_u16x2(rs1, width) \ + __builtin_riscv_pusati_u16x2(rs1, width) +#define __riscv_psati_i16x2(rs1, width) __builtin_riscv_psati_i16x2(rs1, width) + +/* Packed Saturation (64-bit) */ +#define __riscv_pusati_u16x4(rs1, width) \ + __builtin_riscv_pusati_u16x4(rs1, width) +#define __riscv_pusati_u32x2(rs1, width) \ + __builtin_riscv_pusati_u32x2(rs1, width) +#define __riscv_psati_i16x4(rs1, width) __builtin_riscv_psati_i16x4(rs1, width) +#define __riscv_psati_i32x2(rs1, width) __builtin_riscv_psati_i32x2(rs1, width) + /* Packed Element Insert (32-bit) */ __packed_insert(pset_i8_i8x4, int8x4_t, int8_t, 3) __packed_insert(pset_u8_u8x4, uint8x4_t, uint8_t, 3) diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp index be6bbe8e5f9eb..58a52091ee8b2 100644 --- a/clang/lib/Sema/SemaRISCV.cpp +++ b/clang/lib/Sema/SemaRISCV.cpp @@ -968,6 +968,17 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI, case RISCV::BI__builtin_riscv_sm4ks: case RISCV::BI__builtin_riscv_sm4ed: return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 3); + // Check the saturation width for the packed saturating instructions. + case RISCV::BI__builtin_riscv_psati_i16x2: + case RISCV::BI__builtin_riscv_psati_i16x4: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 1, 16); + case RISCV::BI__builtin_riscv_psati_i32x2: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 1, 32); + case RISCV::BI__builtin_riscv_pusati_u16x2: + case RISCV::BI__builtin_riscv_pusati_u16x4: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 15); + case RISCV::BI__builtin_riscv_pusati_u32x2: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 31); // Check if rnum is in [0, 10] case RISCV::BI__builtin_riscv_aes64ks1i: return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 10); diff --git a/clang/test/CodeGen/RISCV/rvp-intrinsics.c b/clang/test/CodeGen/RISCV/rvp-intrinsics.c index b2b2b1b9a777b..815609577420d 100644 --- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c +++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c @@ -11864,3 +11864,113 @@ int16x4_t test_pjoin2_i16x4(int16x2_t lo, int16x2_t hi) { uint16x4_t test_pjoin2_u16x4(uint16x2_t lo, uint16x2_t hi) { return __riscv_pjoin2_u16x4(lo, hi); } + +/* Packed Saturation (32-bit) */ +// RV32-LABEL: define dso_local i32 @test_pusati_u16x2( +// RV32-SAME: i32 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[A_COERCE]] to <2 x i16> +// RV32-NEXT: [[TMP1:%.*]] = call <2 x i16> @llvm.riscv.pusati.v2i16(<2 x i16> [[TMP0]], i32 8) +// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i16> [[TMP1]] to i32 +// RV32-NEXT: ret i32 [[TMP2]] +// +// RV64-LABEL: define dso_local i32 @test_pusati_u16x2( +// RV64-SAME: i32 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[A_COERCE]] to <2 x i16> +// RV64-NEXT: [[TMP1:%.*]] = call <2 x i16> @llvm.riscv.pusati.v2i16(<2 x i16> [[TMP0]], i32 8) +// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i16> [[TMP1]] to i32 +// RV64-NEXT: ret i32 [[TMP2]] +// +uint16x2_t test_pusati_u16x2(int16x2_t a) { return __riscv_pusati_u16x2(a, 8); } + +// RV32-LABEL: define dso_local i32 @test_psati_i16x2( +// RV32-SAME: i32 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = bitcast i32 [[A_COERCE]] to <2 x i16> +// RV32-NEXT: [[TMP1:%.*]] = call <2 x i16> @llvm.riscv.psati.v2i16(<2 x i16> [[TMP0]], i32 8) +// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i16> [[TMP1]] to i32 +// RV32-NEXT: ret i32 [[TMP2]] +// +// RV64-LABEL: define dso_local i32 @test_psati_i16x2( +// RV64-SAME: i32 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = bitcast i32 [[A_COERCE]] to <2 x i16> +// RV64-NEXT: [[TMP1:%.*]] = call <2 x i16> @llvm.riscv.psati.v2i16(<2 x i16> [[TMP0]], i32 8) +// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i16> [[TMP1]] to i32 +// RV64-NEXT: ret i32 [[TMP2]] +// +int16x2_t test_psati_i16x2(int16x2_t a) { return __riscv_psati_i16x2(a, 8); } + +/* Packed Saturation (64-bit) */ +// RV32-LABEL: define dso_local i64 @test_pusati_u16x4( +// RV32-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <4 x i16> +// RV32-NEXT: [[TMP1:%.*]] = call <4 x i16> @llvm.riscv.pusati.v4i16(<4 x i16> [[TMP0]], i32 8) +// RV32-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[TMP1]] to i64 +// RV32-NEXT: ret i64 [[TMP2]] +// +// RV64-LABEL: define dso_local i64 @test_pusati_u16x4( +// RV64-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <4 x i16> +// RV64-NEXT: [[TMP1:%.*]] = call <4 x i16> @llvm.riscv.pusati.v4i16(<4 x i16> [[TMP0]], i32 8) +// RV64-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[TMP1]] to i64 +// RV64-NEXT: ret i64 [[TMP2]] +// +uint16x4_t test_pusati_u16x4(int16x4_t a) { return __riscv_pusati_u16x4(a, 8); } + +// RV32-LABEL: define dso_local i64 @test_pusati_u32x2( +// RV32-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <2 x i32> +// RV32-NEXT: [[TMP1:%.*]] = call <2 x i32> @llvm.riscv.pusati.v2i32(<2 x i32> [[TMP0]], i32 16) +// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[TMP1]] to i64 +// RV32-NEXT: ret i64 [[TMP2]] +// +// RV64-LABEL: define dso_local i64 @test_pusati_u32x2( +// RV64-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <2 x i32> +// RV64-NEXT: [[TMP1:%.*]] = call <2 x i32> @llvm.riscv.pusati.v2i32(<2 x i32> [[TMP0]], i32 16) +// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[TMP1]] to i64 +// RV64-NEXT: ret i64 [[TMP2]] +// +uint32x2_t test_pusati_u32x2(int32x2_t a) { return __riscv_pusati_u32x2(a, 16); } + +// RV32-LABEL: define dso_local i64 @test_psati_i16x4( +// RV32-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <4 x i16> +// RV32-NEXT: [[TMP1:%.*]] = call <4 x i16> @llvm.riscv.psati.v4i16(<4 x i16> [[TMP0]], i32 8) +// RV32-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[TMP1]] to i64 +// RV32-NEXT: ret i64 [[TMP2]] +// +// RV64-LABEL: define dso_local i64 @test_psati_i16x4( +// RV64-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <4 x i16> +// RV64-NEXT: [[TMP1:%.*]] = call <4 x i16> @llvm.riscv.psati.v4i16(<4 x i16> [[TMP0]], i32 8) +// RV64-NEXT: [[TMP2:%.*]] = bitcast <4 x i16> [[TMP1]] to i64 +// RV64-NEXT: ret i64 [[TMP2]] +// +int16x4_t test_psati_i16x4(int16x4_t a) { return __riscv_psati_i16x4(a, 8); } + +// RV32-LABEL: define dso_local i64 @test_psati_i32x2( +// RV32-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <2 x i32> +// RV32-NEXT: [[TMP1:%.*]] = call <2 x i32> @llvm.riscv.psati.v2i32(<2 x i32> [[TMP0]], i32 16) +// RV32-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[TMP1]] to i64 +// RV32-NEXT: ret i64 [[TMP2]] +// +// RV64-LABEL: define dso_local i64 @test_psati_i32x2( +// RV64-SAME: i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = bitcast i64 [[A_COERCE]] to <2 x i32> +// RV64-NEXT: [[TMP1:%.*]] = call <2 x i32> @llvm.riscv.psati.v2i32(<2 x i32> [[TMP0]], i32 16) +// RV64-NEXT: [[TMP2:%.*]] = bitcast <2 x i32> [[TMP1]] to i64 +// RV64-NEXT: ret i64 [[TMP2]] +// +int32x2_t test_psati_i32x2(int32x2_t a) { return __riscv_psati_i32x2(a, 16); } diff --git a/clang/test/Sema/riscv-psati-width-out-of-range.c b/clang/test/Sema/riscv-psati-width-out-of-range.c new file mode 100644 index 0000000000000..694a294e77fa1 --- /dev/null +++ b/clang/test/Sema/riscv-psati-width-out-of-range.c @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple riscv32 -target-feature +experimental-p \ +// RUN: -fsyntax-only -verify -verify-ignore-unexpected=note %s +// RUN: %clang_cc1 -triple riscv64 -target-feature +experimental-p \ +// RUN: -fsyntax-only -verify -verify-ignore-unexpected=note %s + +#include <riscv_packed_simd.h> + +int16x2_t test_psati_i16x2_nonconstant(int16x2_t v, unsigned width) { + // expected-error@+1 {{argument to '__builtin_riscv_psati_i16x2' must be a constant integer}} + return __riscv_psati_i16x2(v, width); +} + +int16x2_t test_psati_i16x2_out_of_range(int16x2_t v) { + // expected-error@+1 {{argument value 17 is outside the valid range [1, 16]}} + return __riscv_psati_i16x2(v, 17); +} + +int16x4_t test_psati_i16x4_out_of_range(int16x4_t v) { + // expected-error@+1 {{argument value 0 is outside the valid range [1, 16]}} + return __riscv_psati_i16x4(v, 0); +} + +int32x2_t test_psati_i32x2_out_of_range(int32x2_t v) { + // expected-error@+1 {{argument value 33 is outside the valid range [1, 32]}} + return __riscv_psati_i32x2(v, 33); +} + +uint16x2_t test_pusati_u16x2_out_of_range(int16x2_t v) { + // expected-error@+1 {{argument value 16 is outside the valid range [0, 15]}} + return __riscv_pusati_u16x2(v, 16); +} + +uint16x4_t test_pusati_u16x4_out_of_range(int16x4_t v) { + // expected-error@+1 {{argument value 16 is outside the valid range [0, 15]}} + return __riscv_pusati_u16x4(v, 16); +} + +uint32x2_t test_pusati_u32x2_out_of_range(int32x2_t v) { + // expected-error@+1 {{argument value 32 is outside the valid range [0, 31]}} + return __riscv_pusati_u32x2(v, 32); +} 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 f3857cedc3b5a..4c5d01c1cda59 100644 --- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c +++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c @@ -1239,6 +1239,48 @@ int32x2_t test_psshar_s_i32x2_neg_imm(int32x2_t a) { return __riscv_psshar_s_i32x2(a, -5); } +/* Packed Saturation (32-bit) */ +// CHECK-LABEL: test_pusati_u16x2: +// CHECK: pusati.h{{[[:space:]]}} +uint16x2_t test_pusati_u16x2(int16x2_t a) { + return __riscv_pusati_u16x2(a, 8); +} + +// CHECK-LABEL: test_psati_i16x2: +// CHECK: psati.h{{[[:space:]]}} +int16x2_t test_psati_i16x2(int16x2_t a) { + return __riscv_psati_i16x2(a, 8); +} + +/* Packed Saturation (64-bit) */ +// CHECK-LABEL: test_pusati_u16x4: +// RV32: pusati.dh{{[[:space:]]}} +// RV64: pusati.h{{[[:space:]]}} +uint16x4_t test_pusati_u16x4(int16x4_t a) { + return __riscv_pusati_u16x4(a, 8); +} + +// CHECK-LABEL: test_pusati_u32x2: +// RV32: pusati.dw{{[[:space:]]}} +// RV64: pusati.w{{[[:space:]]}} +uint32x2_t test_pusati_u32x2(int32x2_t a) { + return __riscv_pusati_u32x2(a, 16); +} + +// CHECK-LABEL: test_psati_i16x4: +// RV32: psati.dh{{[[:space:]]}} +// RV64: psati.h{{[[:space:]]}} +int16x4_t test_psati_i16x4(int16x4_t a) { + return __riscv_psati_i16x4(a, 8); +} + +// CHECK-LABEL: test_psati_i32x2: +// RV32: psati.dw{{[[:space:]]}} +// RV64: psati.w{{[[:space:]]}} +int32x2_t test_psati_i32x2(int32x2_t a) { + return __riscv_psati_i32x2(a, 16); +} + // CHECK-LABEL: test_pand_i8x4: // CHECK: and{{[[:space:]]}} int8x4_t test_pand_i8x4(int8x4_t a, int8x4_t b) { >From 9faa749a148a8fbb6d916458ed7585db0d222d40 Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Thu, 17 Sep 2026 21:17:18 +0000 Subject: [PATCH 4/6] code format --- .../riscv_packed_simd.c | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) 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 4c5d01c1cda59..3d2350bedb720 100644 --- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c +++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c @@ -1242,23 +1242,17 @@ int32x2_t test_psshar_s_i32x2_neg_imm(int32x2_t a) { /* Packed Saturation (32-bit) */ // CHECK-LABEL: test_pusati_u16x2: // CHECK: pusati.h{{[[:space:]]}} -uint16x2_t test_pusati_u16x2(int16x2_t a) { - return __riscv_pusati_u16x2(a, 8); -} +uint16x2_t test_pusati_u16x2(int16x2_t a) { return __riscv_pusati_u16x2(a, 8); } // CHECK-LABEL: test_psati_i16x2: // CHECK: psati.h{{[[:space:]]}} -int16x2_t test_psati_i16x2(int16x2_t a) { - return __riscv_psati_i16x2(a, 8); -} +int16x2_t test_psati_i16x2(int16x2_t a) { return __riscv_psati_i16x2(a, 8); } /* Packed Saturation (64-bit) */ // CHECK-LABEL: test_pusati_u16x4: // RV32: pusati.dh{{[[:space:]]}} // RV64: pusati.h{{[[:space:]]}} -uint16x4_t test_pusati_u16x4(int16x4_t a) { - return __riscv_pusati_u16x4(a, 8); -} +uint16x4_t test_pusati_u16x4(int16x4_t a) { return __riscv_pusati_u16x4(a, 8); } // CHECK-LABEL: test_pusati_u32x2: // RV32: pusati.dw{{[[:space:]]}} @@ -1270,16 +1264,12 @@ uint32x2_t test_pusati_u32x2(int32x2_t a) { // CHECK-LABEL: test_psati_i16x4: // RV32: psati.dh{{[[:space:]]}} // RV64: psati.h{{[[:space:]]}} -int16x4_t test_psati_i16x4(int16x4_t a) { - return __riscv_psati_i16x4(a, 8); -} +int16x4_t test_psati_i16x4(int16x4_t a) { return __riscv_psati_i16x4(a, 8); } // CHECK-LABEL: test_psati_i32x2: // RV32: psati.dw{{[[:space:]]}} // RV64: psati.w{{[[:space:]]}} -int32x2_t test_psati_i32x2(int32x2_t a) { - return __riscv_psati_i32x2(a, 16); -} +int32x2_t test_psati_i32x2(int32x2_t a) { return __riscv_psati_i32x2(a, 16); } // CHECK-LABEL: test_pand_i8x4: // CHECK: and{{[[:space:]]}} >From 6bea92e319b6a88848208939d93e62bf95596aab Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Wed, 23 Sep 2026 13:31:58 +0000 Subject: [PATCH 5/6] [RISCV][P-ext] Reuse RISCVISD::SATI/USATI for packed saturation Generalize the sati/usati type profile to also accept a packed vector instead of adding separate PSATI/PUSATI nodes, so the packed forms reuse the existing computeKnownBits/ComputeNumSignBits handling. Build the width TargetConstant directly instead of retyping the intrinsic operand with getAnyExtOrTrunc, which folds it to a plain Constant, and widen a v2i16 result on RV64 with undef through the shared widening path rather than zeroing the high lanes. --- llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 35 ++++------- llvm/lib/Target/RISCV/RISCVInstrInfoP.td | 42 +++++++------ llvm/test/CodeGen/RISCV/rvp-simd-32.ll | 70 ++++++--------------- 3 files changed, 55 insertions(+), 92 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index 539fb6d31f37d..863d1282e65f1 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -13088,10 +13088,12 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, } case Intrinsic::riscv_psati: case Intrinsic::riscv_pusati: { - unsigned Opc = - IntNo == Intrinsic::riscv_psati ? RISCVISD::PSATI : RISCVISD::PUSATI; - SDValue Width = DAG.getAnyExtOrTrunc(Op.getOperand(2), DL, XLenVT); - return DAG.getNode(Opc, DL, Op.getValueType(), Op.getOperand(1), Width); + bool IsSigned = IntNo == Intrinsic::riscv_psati; + unsigned Opc = IsSigned ? RISCVISD::SATI : RISCVISD::USATI; + // psati's width counts the sign bit, RISCVISD::SATI's immediate does not. + unsigned Width = Op.getConstantOperandVal(2) - (IsSigned ? 1 : 0); + return DAG.getNode(Opc, DL, Op.getValueType(), Op.getOperand(1), + DAG.getTargetConstant(Width, DL, XLenVT)); } case Intrinsic::riscv_psext_b: case Intrinsic::riscv_psext_h: { @@ -17529,7 +17531,9 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, case Intrinsic::riscv_pmulhru: case Intrinsic::riscv_pmulhsu: case Intrinsic::riscv_pmulhrsu: - case Intrinsic::riscv_psabs: { + case Intrinsic::riscv_psabs: + case Intrinsic::riscv_psati: + case Intrinsic::riscv_pusati: { EVT VT = N->getValueType(0); if (!Subtarget.is64Bit() || (VT != MVT::v4i8 && VT != MVT::v2i16)) return; @@ -17572,8 +17576,8 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, Opc = getRVPMulHighOpcode(IntNo); break; default: - // pas/psa/psas/pssa/paas/pasa and pmerge: re-emit at the widened type - // rather than lowering to a generic node. + // pas/psa/psas/pssa/paas/pasa, pmerge and psati/pusati: re-emit at the + // widened type rather than lowering to a generic node. Opc = ISD::INTRINSIC_WO_CHAIN; break; } @@ -17635,23 +17639,6 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, Results.push_back(DAG.getExtractSubvector(DL, VT, Res, 0)); return; } - case Intrinsic::riscv_psati: - case Intrinsic::riscv_pusati: { - MVT VT = N->getSimpleValueType(0); - if (!Subtarget.is64Bit() || VT != MVT::v2i16) - return; - - MVT WideVT = MVT::v4i16; - SDValue Rs1 = - widenPackedVectorWithZeros(DAG, DL, N->getOperand(1), WideVT); - SDValue Width = - DAG.getAnyExtOrTrunc(N->getOperand(2), DL, Subtarget.getXLenVT()); - unsigned Opc = - IntNo == Intrinsic::riscv_psati ? RISCVISD::PSATI : RISCVISD::PUSATI; - SDValue Res = DAG.getNode(Opc, DL, WideVT, Rs1, Width); - Results.push_back(DAG.getExtractSubvector(DL, VT, Res, 0)); - return; - } case Intrinsic::riscv_predsum: case Intrinsic::riscv_predsumu: { bool IsSigned = IntNo == Intrinsic::riscv_predsum; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td index 8adce67d0c221..cb402a06452c9 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoP.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoP.td @@ -2044,13 +2044,6 @@ def riscv_psshar : RVSDNode<"PSSHAR", SDT_RISCVPackedShift>; def riscv_psshl : RVSDNode<"PSSHL", SDT_RISCVPackedShift>; def riscv_psshlr : RVSDNode<"PSSHLR", SDT_RISCVPackedShift>; -// Packed saturation to an immediate width. -def SDT_RISCVPackedSaturation - : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisSameAs<0, 1>, - SDTCisVT<2, XLenVT>]>; -def riscv_psati : RVSDNode<"PSATI", SDT_RISCVPackedSaturation>; -def riscv_pusati : RVSDNode<"PUSATI", SDT_RISCVPackedSaturation>; - // RV32 packed narrowing shift. def SDT_RISCVPackedNarrowingShift : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, @@ -2107,9 +2100,12 @@ def riscv_pmqracc_h11 : RVSDNode<"PMQRACC_W_H11", SDT_RISCVQFormatAccP>; def riscv_mqwacc : RVSDNode<"MQWACC", SDT_RISCVWideningAddSubAccumulate>; def riscv_mqrwacc : RVSDNode<"MQRWACC", SDT_RISCVWideningAddSubAccumulate>; -// The immediate for these is the number of trailing ones in the max value. -def riscv_sati : RVSDNode<"SATI", SDTIntBinOp>; -def riscv_usati : RVSDNode<"USATI", SDTIntBinOp>; +// The immediate for these is the number of trailing ones in the max value. The +// saturated operand is a scalar (sati/usati) or a packed vector (psati/pusati). +def SDT_RISCVSaturate : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, XLenVT>]>; +def riscv_sati : RVSDNode<"SATI", SDT_RISCVSaturate>; +def riscv_usati : RVSDNode<"USATI", SDT_RISCVSaturate>; // Bitwise merge: res = (~op0 & op1) | (op0 & op2) def SDT_RISCVMERGE : SDTypeProfile<1, 3, [SDTCisInt<0>, @@ -2147,6 +2143,16 @@ def IncImm : SDNodeXForm<imm, [{ N->getValueType(0)); }]>; +// Matches the immediate of RISCVISD::SATI/USATI. The signed instructions count +// the sign bit in their width field, which IncImm adds back. +class RISCVSatWidthLeaf<int bitsNum, SDNodeXForm xform = NOOP_SDNodeXForm> + : TImmLeaf<XLenVT, "return isUInt<" # bitsNum # ">(Imm);", xform>; + +def sati_width4 : RISCVSatWidthLeaf<4, IncImm>; +def sati_width5 : RISCVSatWidthLeaf<5, IncImm>; +def usati_width4 : RISCVSatWidthLeaf<4>; +def usati_width5 : RISCVSatWidthLeaf<5>; + def SDT_RISCVBuildPairGPRVec : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVT<1, i32>, SDTCisSameAs<1, 2>]>; @@ -2346,8 +2352,8 @@ let Predicates = [HasStdExtP] in { def : PatGprShift<riscv_psshlr, PSSHLR_HS, XLenVecI16VT>; // 16-bit packed saturation patterns - def : PatGprImm<riscv_psati, PSATI_H, uimm4_plus1, XLenVecI16VT>; - def : PatGprImm<riscv_pusati, PUSATI_H, uimm4, XLenVecI16VT>; + def : PatGprImm<riscv_sati, PSATI_H, sati_width4, XLenVecI16VT>; + def : PatGprImm<riscv_usati, PUSATI_H, usati_width4, XLenVecI16VT>; // 8-bit logical shift left/right def : PatGprShiftMask<riscv_pshl, PSLL_BS, shiftMask32, XLenVecI8VT>; @@ -2827,8 +2833,8 @@ let append Predicates = [IsRV32] in { def : PatGprPairShift<riscv_psshlr, PSSHLR_DHS, v4i16>; // 16-bit packed saturation patterns - def : PatGprPairImm<riscv_psati, PSATI_DH, uimm4_plus1, v4i16>; - def : PatGprPairImm<riscv_pusati, PUSATI_DH, uimm4, v4i16>; + def : PatGprPairImm<riscv_sati, PSATI_DH, sati_width4, v4i16>; + def : PatGprPairImm<riscv_usati, PUSATI_DH, usati_width4, v4i16>; // 32-bit saturating shift patterns def : PatGprPairImm<riscv_pssha, PSSLAI_DW, uimm5, v2i32>; @@ -2841,8 +2847,8 @@ let append Predicates = [IsRV32] in { def : PatGprPairShift<riscv_psshlr, PSSHLR_DWS, v2i32>; // 32-bit packed saturation patterns - def : PatGprPairImm<riscv_psati, PSATI_DW, uimm5_plus1, v2i32>; - def : PatGprPairImm<riscv_pusati, PUSATI_DW, uimm5, v2i32>; + def : PatGprPairImm<riscv_sati, PSATI_DW, sati_width5, v2i32>; + def : PatGprPairImm<riscv_usati, PUSATI_DW, usati_width5, v2i32>; // 8-bit logical shift left/right def : PatGprPairShiftMask<riscv_pshl, PSLL_DBS, shiftMask32, v8i8>; @@ -3294,8 +3300,8 @@ let append Predicates = [IsRV64] in { def : PatGprShift<riscv_psshlr, PSSHLR_WS, v2i32>; // 32-bit packed saturation patterns - def : PatGprImm<riscv_psati, PSATI_W, uimm5_plus1, v2i32>; - def : PatGprImm<riscv_pusati, PUSATI_W, uimm5, v2i32>; + def : PatGprImm<riscv_sati, PSATI_W, sati_width5, v2i32>; + def : PatGprImm<riscv_usati, PUSATI_W, usati_width5, v2i32>; // 32-bit logical shift left/right def : PatGprShiftMask<riscv_pshl, PSLL_WS, shiftMask32, v2i32>; diff --git a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll index 5adc48d167f6e..704726080593f 100644 --- a/llvm/test/CodeGen/RISCV/rvp-simd-32.ll +++ b/llvm/test/CodeGen/RISCV/rvp-simd-32.ll @@ -3829,76 +3829,46 @@ define i32 @test_maccsu_h11_i32(i32 %rd, <2 x i16> %a, <2 x i16> %b) { } define <2 x i16> @test_psati_i16x2(<2 x i16> %a) { -; RV32-LABEL: test_psati_i16x2: -; RV32: # %bb.0: -; RV32-NEXT: psati.h a0, a0, 8 -; RV32-NEXT: ret -; -; RV64-LABEL: test_psati_i16x2: -; RV64: # %bb.0: -; RV64-NEXT: zext.w a0, a0 -; RV64-NEXT: psati.h a0, a0, 8 -; RV64-NEXT: ret +; CHECK-LABEL: test_psati_i16x2: +; CHECK: # %bb.0: +; CHECK-NEXT: psati.h a0, a0, 8 +; CHECK-NEXT: ret %res = call <2 x i16> @llvm.riscv.psati.v2i16.i32(<2 x i16> %a, i32 8) ret <2 x i16> %res } define <2 x i16> @test_psati_i16x2_min_width(<2 x i16> %a) { -; RV32-LABEL: test_psati_i16x2_min_width: -; RV32: # %bb.0: -; RV32-NEXT: psati.h a0, a0, 1 -; RV32-NEXT: ret -; -; RV64-LABEL: test_psati_i16x2_min_width: -; RV64: # %bb.0: -; RV64-NEXT: zext.w a0, a0 -; RV64-NEXT: psati.h a0, a0, 1 -; RV64-NEXT: ret +; CHECK-LABEL: test_psati_i16x2_min_width: +; CHECK: # %bb.0: +; CHECK-NEXT: psati.h a0, a0, 1 +; CHECK-NEXT: ret %res = call <2 x i16> @llvm.riscv.psati.v2i16.i32(<2 x i16> %a, i32 1) ret <2 x i16> %res } define <2 x i16> @test_psati_i16x2_max_width(<2 x i16> %a) { -; RV32-LABEL: test_psati_i16x2_max_width: -; RV32: # %bb.0: -; RV32-NEXT: psati.h a0, a0, 16 -; RV32-NEXT: ret -; -; RV64-LABEL: test_psati_i16x2_max_width: -; RV64: # %bb.0: -; RV64-NEXT: zext.w a0, a0 -; RV64-NEXT: psati.h a0, a0, 16 -; RV64-NEXT: ret +; CHECK-LABEL: test_psati_i16x2_max_width: +; CHECK: # %bb.0: +; CHECK-NEXT: psati.h a0, a0, 16 +; CHECK-NEXT: ret %res = call <2 x i16> @llvm.riscv.psati.v2i16.i32(<2 x i16> %a, i32 16) ret <2 x i16> %res } define <2 x i16> @test_pusati_u16x2(<2 x i16> %a) { -; RV32-LABEL: test_pusati_u16x2: -; RV32: # %bb.0: -; RV32-NEXT: pusati.h a0, a0, 0 -; RV32-NEXT: ret -; -; RV64-LABEL: test_pusati_u16x2: -; RV64: # %bb.0: -; RV64-NEXT: zext.w a0, a0 -; RV64-NEXT: pusati.h a0, a0, 0 -; RV64-NEXT: ret +; CHECK-LABEL: test_pusati_u16x2: +; CHECK: # %bb.0: +; CHECK-NEXT: pusati.h a0, a0, 0 +; CHECK-NEXT: ret %res = call <2 x i16> @llvm.riscv.pusati.v2i16.i32(<2 x i16> %a, i32 0) ret <2 x i16> %res } define <2 x i16> @test_pusati_u16x2_max_width(<2 x i16> %a) { -; RV32-LABEL: test_pusati_u16x2_max_width: -; RV32: # %bb.0: -; RV32-NEXT: pusati.h a0, a0, 15 -; RV32-NEXT: ret -; -; RV64-LABEL: test_pusati_u16x2_max_width: -; RV64: # %bb.0: -; RV64-NEXT: zext.w a0, a0 -; RV64-NEXT: pusati.h a0, a0, 15 -; RV64-NEXT: ret +; CHECK-LABEL: test_pusati_u16x2_max_width: +; CHECK: # %bb.0: +; CHECK-NEXT: pusati.h a0, a0, 15 +; CHECK-NEXT: ret %res = call <2 x i16> @llvm.riscv.pusati.v2i16.i32(<2 x i16> %a, i32 15) ret <2 x i16> %res } >From 3b408fbf5468ce8aa11cf03e9b38214edcdd934c Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Wed, 23 Sep 2026 13:31:58 +0000 Subject: [PATCH 6/6] [Clang][RISCV] Fold psati/pusati into the shared packed builtin lowering They only need the intrinsic ID and IntrinsicTypes = {ResultType}, so handle them in the case group that already does that instead of a switch of their own. Also cover the accepted widths in the Sema test and drop its -verify-ignore-unexpected=note: these diagnostics come without notes, unlike the enable_if based pget/pset checks the RUN lines were copied from. --- clang/lib/CodeGen/TargetBuiltins/RISCV.cpp | 25 ++++++------------- clang/lib/Sema/SemaRISCV.cpp | 2 +- .../Sema/riscv-psati-width-out-of-range.c | 16 ++++++++++-- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp index 4f7b9ceae6eaa..c798f5fb68bd5 100644 --- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp @@ -1328,7 +1328,14 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, case RISCV::BI__builtin_riscv_psshl_s_u16x4: case RISCV::BI__builtin_riscv_psshl_s_u32x2: case RISCV::BI__builtin_riscv_psshlr_s_u16x4: - case RISCV::BI__builtin_riscv_psshlr_s_u32x2: { + case RISCV::BI__builtin_riscv_psshlr_s_u32x2: + // Packed Saturation + case RISCV::BI__builtin_riscv_pusati_u16x2: + case RISCV::BI__builtin_riscv_psati_i16x2: + case RISCV::BI__builtin_riscv_pusati_u16x4: + case RISCV::BI__builtin_riscv_pusati_u32x2: + case RISCV::BI__builtin_riscv_psati_i16x4: + case RISCV::BI__builtin_riscv_psati_i32x2: { switch (BuiltinID) { default: llvm_unreachable("unexpected builtin ID"); @@ -1525,22 +1532,6 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, case RISCV::BI__builtin_riscv_psshlr_s_u32x2: ID = Intrinsic::riscv_psshlr; break; - } - - IntrinsicTypes = {ResultType}; - break; - } - - // Packed Saturation - case RISCV::BI__builtin_riscv_psati_i16x2: - case RISCV::BI__builtin_riscv_psati_i16x4: - case RISCV::BI__builtin_riscv_psati_i32x2: - case RISCV::BI__builtin_riscv_pusati_u16x2: - case RISCV::BI__builtin_riscv_pusati_u16x4: - case RISCV::BI__builtin_riscv_pusati_u32x2: { - switch (BuiltinID) { - default: - llvm_unreachable("unexpected builtin ID"); case RISCV::BI__builtin_riscv_psati_i16x2: case RISCV::BI__builtin_riscv_psati_i16x4: case RISCV::BI__builtin_riscv_psati_i32x2: diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp index 58a52091ee8b2..ee3858a6448d2 100644 --- a/clang/lib/Sema/SemaRISCV.cpp +++ b/clang/lib/Sema/SemaRISCV.cpp @@ -968,7 +968,7 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI, case RISCV::BI__builtin_riscv_sm4ks: case RISCV::BI__builtin_riscv_sm4ed: return SemaRef.BuiltinConstantArgRange(TheCall, 2, 0, 3); - // Check the saturation width for the packed saturating instructions. + // Check the psati/pusati saturation width. case RISCV::BI__builtin_riscv_psati_i16x2: case RISCV::BI__builtin_riscv_psati_i16x4: return SemaRef.BuiltinConstantArgRange(TheCall, 1, 1, 16); diff --git a/clang/test/Sema/riscv-psati-width-out-of-range.c b/clang/test/Sema/riscv-psati-width-out-of-range.c index 694a294e77fa1..93e72dcf7e9e7 100644 --- a/clang/test/Sema/riscv-psati-width-out-of-range.c +++ b/clang/test/Sema/riscv-psati-width-out-of-range.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple riscv32 -target-feature +experimental-p \ -// RUN: -fsyntax-only -verify -verify-ignore-unexpected=note %s +// RUN: -fsyntax-only -verify %s // RUN: %clang_cc1 -triple riscv64 -target-feature +experimental-p \ -// RUN: -fsyntax-only -verify -verify-ignore-unexpected=note %s +// RUN: -fsyntax-only -verify %s #include <riscv_packed_simd.h> @@ -10,6 +10,18 @@ int16x2_t test_psati_i16x2_nonconstant(int16x2_t v, unsigned width) { return __riscv_psati_i16x2(v, width); } +int16x2_t test_psati_i16x2_min_width(int16x2_t v) { + return __riscv_psati_i16x2(v, 1); +} + +int16x2_t test_psati_i16x2_max_width(int16x2_t v) { + return __riscv_psati_i16x2(v, 16); +} + +uint16x2_t test_pusati_u16x2_max_width(int16x2_t v) { + return __riscv_pusati_u16x2(v, 15); +} + int16x2_t test_psati_i16x2_out_of_range(int16x2_t v) { // expected-error@+1 {{argument value 17 is outside the valid range [1, 16]}} return __riscv_psati_i16x2(v, 17); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
