llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-backend-x86 @llvm/pr-subscribers-backend-risc-v Author: SiHuaN (sihuan) <details> <summary>Changes</summary> Add the scalar Saturation intrinsics from the P-extension intrinsic spec: https://github.com/riscv/riscv-p-spec/blob/master/P-ext-intrinsics.adoc#saturation - `__riscv_sati_i32`, `__riscv_usati_u32` - `__riscv_sati_i64`, `__riscv_usati_u64` (RV64 only) `llvm.riscv.sati`/`llvm.riscv.usati` are overloaded on the integer type. RV32 and the i64 forms select `sati`/`usati` directly. RV64 has no 32-bit scalar forms, so the i32 intrinsics use `psati.w`/`pusati.w` on the low word. --- Full diff: https://github.com/llvm/llvm-project/pull/233769.diff 11 Files Affected: - (modified) clang/include/clang/Basic/BuiltinsRISCV.td (+8) - (modified) clang/lib/CodeGen/TargetBuiltins/RISCV.cpp (+13) - (modified) clang/lib/Headers/riscv_packed_simd.h (+8) - (modified) clang/lib/Sema/SemaRISCV.cpp (+9) - (modified) clang/test/CodeGen/RISCV/rvp-intrinsics.c (+56) - (modified) clang/test/Sema/riscv-psati-width-out-of-range.c (+41) - (modified) cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c (+20) - (modified) llvm/include/llvm/IR/IntrinsicsRISCV.td (+8) - (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+20-2) - (modified) llvm/test/CodeGen/RISCV/rvp-scalar-rv64.ll (+36) - (modified) llvm/test/CodeGen/RISCV/rvp-scalar.ll (+56) ``````````diff diff --git a/clang/include/clang/Basic/BuiltinsRISCV.td b/clang/include/clang/Basic/BuiltinsRISCV.td index b7305e46cad6dc..4454b14bc75196 100644 --- a/clang/include/clang/Basic/BuiltinsRISCV.td +++ b/clang/include/clang/Basic/BuiltinsRISCV.td @@ -271,6 +271,14 @@ def aaddu_u32 : RISCVBuiltin<"uint32_t(uint32_t, uint32_t)">; def asub_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">; def asubu_u32 : RISCVBuiltin<"uint32_t(uint32_t, uint32_t)">; +// Scalar Saturation +def sati_i32 : RISCVBuiltin<"int32_t(int32_t, _Constant unsigned int)">; +def usati_u32 : RISCVBuiltin<"uint32_t(int32_t, _Constant unsigned int)">; +let Features = "experimental-p,64bit" in { +def sati_i64 : RISCVBuiltin<"int64_t(int64_t, _Constant unsigned int)">; +def usati_u64 : RISCVBuiltin<"uint64_t(int64_t, _Constant unsigned int)">; +} // Features = "experimental-p,64bit" + // Scalar Multiply High def mulh_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">; def mulhr_i32 : RISCVBuiltin<"int32_t(int32_t, int32_t)">; diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp index 879693b9811c39..5a9b0e0b4a1919 100644 --- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp @@ -1416,6 +1416,11 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, case RISCV::BI__builtin_riscv_pusati_u32x2: case RISCV::BI__builtin_riscv_psati_i16x4: case RISCV::BI__builtin_riscv_psati_i32x2: + // Scalar Saturation + case RISCV::BI__builtin_riscv_sati_i32: + case RISCV::BI__builtin_riscv_usati_u32: + case RISCV::BI__builtin_riscv_sati_i64: + case RISCV::BI__builtin_riscv_usati_u64: // Scalar Multiply High case RISCV::BI__builtin_riscv_mulh_i32: case RISCV::BI__builtin_riscv_mulhu_u32: { @@ -1669,6 +1674,14 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID, case RISCV::BI__builtin_riscv_pusati_u32x2: ID = Intrinsic::riscv_pusati; break; + case RISCV::BI__builtin_riscv_sati_i32: + case RISCV::BI__builtin_riscv_sati_i64: + ID = Intrinsic::riscv_sati; + break; + case RISCV::BI__builtin_riscv_usati_u32: + case RISCV::BI__builtin_riscv_usati_u64: + ID = Intrinsic::riscv_usati; + break; case RISCV::BI__builtin_riscv_mulh_i32: ID = Intrinsic::smulh; break; diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h index a4b27eb2e62190..ef5708689331c5 100644 --- a/clang/lib/Headers/riscv_packed_simd.h +++ b/clang/lib/Headers/riscv_packed_simd.h @@ -464,6 +464,14 @@ __packed_pabs(abs_u32, int32_t, uint32_t) __packed_pabs(abs_u64, int64_t, uint64_t) #endif +/* Scalar Saturation */ +#define __riscv_sati_i32(rs1, width) __builtin_riscv_sati_i32(rs1, width) +#define __riscv_usati_u32(rs1, width) __builtin_riscv_usati_u32(rs1, width) +#if __riscv_xlen == 64 +#define __riscv_sati_i64(rs1, width) __builtin_riscv_sati_i64(rs1, width) +#define __riscv_usati_u64(rs1, width) __builtin_riscv_usati_u64(rs1, width) +#endif + /* Scalar Multiply High */ #define __packed_mulh_builtin(name, res_ty, ty1, ty2) \ static __inline__ res_ty __DEFAULT_FN_ATTRS __riscv_##name(ty1 __rs1, \ diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp index ee3858a6448d28..a1ba3b3934daa6 100644 --- a/clang/lib/Sema/SemaRISCV.cpp +++ b/clang/lib/Sema/SemaRISCV.cpp @@ -979,6 +979,15 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI, return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 15); case RISCV::BI__builtin_riscv_pusati_u32x2: return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 31); + // Check the sati/usati saturation width. + case RISCV::BI__builtin_riscv_sati_i32: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 1, 32); + case RISCV::BI__builtin_riscv_sati_i64: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 1, 64); + case RISCV::BI__builtin_riscv_usati_u32: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 31); + case RISCV::BI__builtin_riscv_usati_u64: + return SemaRef.BuiltinConstantArgRange(TheCall, 1, 0, 63); // 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 bc9d12a8f59f80..74500ed9d79be5 100644 --- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c +++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c @@ -300,6 +300,62 @@ uint64_t test_abs_u64(int64_t a) { } #endif +/* Scalar Saturation */ + +// RV32-LABEL: define dso_local i32 @test_sati_i32( +// RV32-SAME: i32 noundef [[A:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.sati.i32(i32 [[A]], i32 16) +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local signext i32 @test_sati_i32( +// RV64-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.sati.i32(i32 [[A]], i32 16) +// RV64-NEXT: ret i32 [[TMP0]] +// +int32_t test_sati_i32(int32_t a) { + return __riscv_sati_i32(a, 16); +} + +// RV32-LABEL: define dso_local i32 @test_usati_u32( +// RV32-SAME: i32 noundef [[A:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.usati.i32(i32 [[A]], i32 16) +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local signext i32 @test_usati_u32( +// RV64-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.usati.i32(i32 [[A]], i32 16) +// RV64-NEXT: ret i32 [[TMP0]] +// +uint32_t test_usati_u32(int32_t a) { + return __riscv_usati_u32(a, 16); +} + +#if __riscv_xlen == 64 +// RV64-LABEL: define dso_local i64 @test_sati_i64( +// RV64-SAME: i64 noundef [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.sati.i64(i64 [[A]], i32 16) +// RV64-NEXT: ret i64 [[TMP0]] +// +int64_t test_sati_i64(int64_t a) { + return __riscv_sati_i64(a, 16); +} + +// RV64-LABEL: define dso_local i64 @test_usati_u64( +// RV64-SAME: i64 noundef [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.usati.i64(i64 [[A]], i32 16) +// RV64-NEXT: ret i64 [[TMP0]] +// +uint64_t test_usati_u64(int64_t a) { + return __riscv_usati_u64(a, 16); +} +#endif + // RV32-LABEL: define dso_local i32 @test_mulh_i32( // RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { // RV32-NEXT: [[ENTRY:.*:]] 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 93e72dcf7e9e7e..15ab9ac0f6eedb 100644 --- a/clang/test/Sema/riscv-psati-width-out-of-range.c +++ b/clang/test/Sema/riscv-psati-width-out-of-range.c @@ -51,3 +51,44 @@ 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); } + +int32_t test_sati_i32_min_width(int32_t v) { return __riscv_sati_i32(v, 1); } + +int32_t test_sati_i32_max_width(int32_t v) { return __riscv_sati_i32(v, 32); } + +uint32_t test_usati_u32_max_width(int32_t v) { + return __riscv_usati_u32(v, 31); +} + +int32_t test_sati_i32_out_of_range(int32_t v) { + // expected-error@+1 {{argument value 33 is outside the valid range [1, 32]}} + return __riscv_sati_i32(v, 33); +} + +int32_t test_sati_i32_zero_width(int32_t v) { + // expected-error@+1 {{argument value 0 is outside the valid range [1, 32]}} + return __riscv_sati_i32(v, 0); +} + +uint32_t test_usati_u32_out_of_range(int32_t v) { + // expected-error@+1 {{argument value 32 is outside the valid range [0, 31]}} + return __riscv_usati_u32(v, 32); +} + +#if __riscv_xlen == 64 +int64_t test_sati_i64_max_width(int64_t v) { return __riscv_sati_i64(v, 64); } + +uint64_t test_usati_u64_max_width(int64_t v) { + return __riscv_usati_u64(v, 63); +} + +int64_t test_sati_i64_out_of_range(int64_t v) { + // expected-error@+1 {{argument value 65 is outside the valid range [1, 64]}} + return __riscv_sati_i64(v, 65); +} + +uint64_t test_usati_u64_out_of_range(int64_t v) { + // expected-error@+1 {{argument value 64 is outside the valid range [0, 63]}} + return __riscv_usati_u64(v, 64); +} +#endif 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 a54807804b9836..875d2de46a0b3f 100644 --- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c +++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c @@ -132,6 +132,26 @@ uint32_t test_abs_u32(int32_t a) { return __riscv_abs_u32(a); } uint64_t test_abs_u64(int64_t a) { return __riscv_abs_u64(a); } #endif +// CHECK-LABEL: test_sati_i32: +// RV32: sati{{[[:space:]]}} +// RV64: psati.w +int32_t test_sati_i32(int32_t a) { return __riscv_sati_i32(a, 16); } + +// CHECK-LABEL: test_usati_u32: +// RV32: usati{{[[:space:]]}} +// RV64: pusati.w +uint32_t test_usati_u32(int32_t a) { return __riscv_usati_u32(a, 16); } + +#if __riscv_xlen == 64 +// RV64-LABEL: test_sati_i64: +// RV64: sati{{[[:space:]]}} +int64_t test_sati_i64(int64_t a) { return __riscv_sati_i64(a, 16); } + +// RV64-LABEL: test_usati_u64: +// RV64: usati{{[[:space:]]}} +uint64_t test_usati_u64(int64_t a) { return __riscv_usati_u64(a, 16); } +#endif + // CHECK-LABEL: test_pmv_s_u8x4: // CHECK: pmv.bs uint8x4_t test_pmv_s_u8x4(uint8_t x) { return __riscv_pmv_s_u8x4(x); } diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td index 321d6ac5e93af0..171c2afe6d8aaa 100644 --- a/llvm/include/llvm/IR/IntrinsicsRISCV.td +++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td @@ -2122,6 +2122,14 @@ class RVPBinaryIntrinsic def int_riscv_psati : RVPSaturationIntrinsic; def int_riscv_pusati : RVPSaturationIntrinsic; + // Scalar Saturation. + class RVPScalarSaturationIntrinsic + : DefaultAttrsIntrinsic<[llvm_anyint_ty], + [LLVMMatchType<0>, llvm_i32_ty], + [IntrNoMem, IntrSpeculatable, ImmArg<ArgIndex<1>>]>; + def int_riscv_sati : RVPScalarSaturationIntrinsic; + def int_riscv_usati : RVPScalarSaturationIntrinsic; + // 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 0cc05e5f2c374d..4f72251e9fabaa 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -13315,8 +13315,11 @@ SDValue RISCVTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op, ShAmt); } case Intrinsic::riscv_psati: - case Intrinsic::riscv_pusati: { - bool IsSigned = IntNo == Intrinsic::riscv_psati; + case Intrinsic::riscv_pusati: + case Intrinsic::riscv_sati: + case Intrinsic::riscv_usati: { + bool IsSigned = + IntNo == Intrinsic::riscv_psati || IntNo == Intrinsic::riscv_sati; unsigned Opc = IsSigned ? RISCVISD::SATI : RISCVISD::USATI; return DAG.getNode( Opc, DL, Op.getValueType(), Op.getOperand(1), @@ -18179,6 +18182,21 @@ void RISCVTargetLowering::ReplaceNodeResults(SDNode *N, Results.push_back(DAG.getExtractVectorElt(DL, MVT::i32, Res, 0)); return; } + case Intrinsic::riscv_sati: + case Intrinsic::riscv_usati: { + // RV64 has no 32-bit sati; use psati.w on the low word. + if (!Subtarget.is64Bit() || N->getValueType(0) != MVT::i32) + return; + unsigned Opc = + IntNo == Intrinsic::riscv_sati ? RISCVISD::SATI : RISCVISD::USATI; + SDValue Rs1 = + DAG.getNode(ISD::SCALAR_TO_VECTOR, DL, MVT::v2i32, N->getOperand(1)); + SDValue Res = DAG.getNode( + Opc, DL, MVT::v2i32, Rs1, + DAG.getTargetConstant(N->getConstantOperandVal(2), DL, MVT::i64)); + Results.push_back(DAG.getExtractVectorElt(DL, MVT::i32, Res, 0)); + return; + } case Intrinsic::riscv_mulhr_i32: case Intrinsic::riscv_mulhru_u32: case Intrinsic::riscv_mulhsu_i32: diff --git a/llvm/test/CodeGen/RISCV/rvp-scalar-rv64.ll b/llvm/test/CodeGen/RISCV/rvp-scalar-rv64.ll index 8014b37af4e752..34c24d6cfd1f90 100644 --- a/llvm/test/CodeGen/RISCV/rvp-scalar-rv64.ll +++ b/llvm/test/CodeGen/RISCV/rvp-scalar-rv64.ll @@ -1366,3 +1366,39 @@ define i64 @rev16_intrinsic(i64 %x) { %r = call i64 @llvm.riscv.rev16.64(i64 %x) ret i64 %r } + +define i64 @test_sati_i64_w1(i64 %a) { +; CHECK-LABEL: test_sati_i64_w1: +; CHECK: # %bb.0: +; CHECK-NEXT: sati a0, a0, 1 +; CHECK-NEXT: ret + %res = call i64 @llvm.riscv.sati.i64(i64 %a, i32 1) + ret i64 %res +} + +define i64 @test_sati_i64_w64(i64 %a) { +; CHECK-LABEL: test_sati_i64_w64: +; CHECK: # %bb.0: +; CHECK-NEXT: sati a0, a0, 64 +; CHECK-NEXT: ret + %res = call i64 @llvm.riscv.sati.i64(i64 %a, i32 64) + ret i64 %res +} + +define i64 @test_usati_u64_w0(i64 %a) { +; CHECK-LABEL: test_usati_u64_w0: +; CHECK: # %bb.0: +; CHECK-NEXT: usati a0, a0, 0 +; CHECK-NEXT: ret + %res = call i64 @llvm.riscv.usati.i64(i64 %a, i32 0) + ret i64 %res +} + +define i64 @test_usati_u64_w63(i64 %a) { +; CHECK-LABEL: test_usati_u64_w63: +; CHECK: # %bb.0: +; CHECK-NEXT: usati a0, a0, 63 +; CHECK-NEXT: ret + %res = call i64 @llvm.riscv.usati.i64(i64 %a, i32 63) + ret i64 %res +} diff --git a/llvm/test/CodeGen/RISCV/rvp-scalar.ll b/llvm/test/CodeGen/RISCV/rvp-scalar.ll index 9170f578275bd6..6fab93896d7dc7 100644 --- a/llvm/test/CodeGen/RISCV/rvp-scalar.ll +++ b/llvm/test/CodeGen/RISCV/rvp-scalar.ll @@ -3321,6 +3321,62 @@ define i8 @usati_i8_from_i16(i16 %x) { ret i8 %sel } +define i32 @test_sati_i32_w1(i32 %a) { +; RV32-LABEL: test_sati_i32_w1: +; RV32: # %bb.0: +; RV32-NEXT: sati a0, a0, 1 +; RV32-NEXT: ret +; +; RV64-LABEL: test_sati_i32_w1: +; RV64: # %bb.0: +; RV64-NEXT: psati.w a0, a0, 1 +; RV64-NEXT: ret + %res = call i32 @llvm.riscv.sati.i32(i32 %a, i32 1) + ret i32 %res +} + +define i32 @test_sati_i32_w32(i32 %a) { +; RV32-LABEL: test_sati_i32_w32: +; RV32: # %bb.0: +; RV32-NEXT: sati a0, a0, 32 +; RV32-NEXT: ret +; +; RV64-LABEL: test_sati_i32_w32: +; RV64: # %bb.0: +; RV64-NEXT: psati.w a0, a0, 32 +; RV64-NEXT: ret + %res = call i32 @llvm.riscv.sati.i32(i32 %a, i32 32) + ret i32 %res +} + +define i32 @test_usati_u32_w0(i32 %a) { +; RV32-LABEL: test_usati_u32_w0: +; RV32: # %bb.0: +; RV32-NEXT: usati a0, a0, 0 +; RV32-NEXT: ret +; +; RV64-LABEL: test_usati_u32_w0: +; RV64: # %bb.0: +; RV64-NEXT: li a0, 0 +; RV64-NEXT: ret + %res = call i32 @llvm.riscv.usati.i32(i32 %a, i32 0) + ret i32 %res +} + +define i32 @test_usati_u32_w31(i32 %a) { +; RV32-LABEL: test_usati_u32_w31: +; RV32: # %bb.0: +; RV32-NEXT: usati a0, a0, 31 +; RV32-NEXT: ret +; +; RV64-LABEL: test_usati_u32_w31: +; RV64: # %bb.0: +; RV64-NEXT: pusati.w a0, a0, 31 +; RV64-NEXT: ret + %res = call i32 @llvm.riscv.usati.i32(i32 %a, i32 31) + ret i32 %res +} + ; Test that pli.b is rematerialized rather than spilled define i32 @test_pli_b_remat(ptr %p) nounwind { ; RV32-LABEL: test_pli_b_remat: `````````` </details> https://github.com/llvm/llvm-project/pull/233769 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
