https://github.com/sihuan updated https://github.com/llvm/llvm-project/pull/224377
>From 6e9c2454d05c770cf664168d8154d1fe0aa4739c Mon Sep 17 00:00:00 2001 From: SiHuaN <[email protected]> Date: Mon, 14 Sep 2026 07:47:13 +0000 Subject: [PATCH] [Clang][RISCV] Add scalar saturating add/sub, absolute value and rev intrinsics First batch of the P extension `Scalar Intrinsics` group, covering the operations that map onto a single existing LLVM IR intrinsic (llvm.sadd.sat, llvm.uadd.sat, llvm.ssub.sat, llvm.usub.sat, llvm.abs, llvm.bitreverse) that the backend already selects, so no codegen changes are needed. The 64-bit forms are RV64 only, guarded with __riscv_xlen as in riscv_bitmanip.h. On RV64 the 32-bit saturating operations select the packed psadd.w/psaddu.w/pssub.w/pssubu.w forms and rev_32 expands to rev+srai, both as required by the intrinsic spec. --- clang/lib/Headers/riscv_packed_simd.h | 18 +++ clang/test/CodeGen/RISCV/rvp-intrinsics.c | 126 ++++++++++++++++++ .../riscv_packed_simd.c | 48 +++++++ 3 files changed, 192 insertions(+) diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h index 8c20d00e68652..cfa673e365d9f 100644 --- a/clang/lib/Headers/riscv_packed_simd.h +++ b/clang/lib/Headers/riscv_packed_simd.h @@ -365,6 +365,24 @@ typedef uint32_t uint32x2_t __attribute__((__vector_size__(8))); // clang-format off: macro call sites have no trailing semicolons, which // confuses clang-format into a deeply nested expression. +/* Scalar Bitmanip */ +__packed_unary_builtin(rev_32, uint32_t, __builtin_bitreverse32) +#if __riscv_xlen == 64 +__packed_unary_builtin(rev_64, uint64_t, __builtin_bitreverse64) +#endif + +/* Scalar Saturating Addition and Subtraction */ +__packed_binary_builtin(sadd_i32, int32_t, __builtin_elementwise_add_sat) +__packed_binary_builtin(saddu_u32, uint32_t, __builtin_elementwise_add_sat) +__packed_binary_builtin(ssub_i32, int32_t, __builtin_elementwise_sub_sat) +__packed_binary_builtin(ssubu_u32, uint32_t, __builtin_elementwise_sub_sat) + +/* Scalar Absolute Value */ +__packed_pabs(abs_u32, int32_t, uint32_t) +#if __riscv_xlen == 64 +__packed_pabs(abs_u64, int64_t, uint64_t) +#endif + /* 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 b2b2b1b9a777b..412c63a182b11 100644 --- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c +++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c @@ -8,6 +8,132 @@ #include <riscv_packed_simd.h> +/* Scalar Bitmanip */ + +// RV32-LABEL: define dso_local i32 @test_rev_32( +// RV32-SAME: i32 noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.bitreverse.i32(i32 [[A]]) +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local signext i32 @test_rev_32( +// RV64-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0:[0-9]+]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i32 @llvm.bitreverse.i32(i32 [[A]]) +// RV64-NEXT: ret i32 [[TMP0]] +// +uint32_t test_rev_32(uint32_t a) { + return __riscv_rev_32(a); +} + +#if __riscv_xlen == 64 +// RV64-LABEL: define dso_local i64 @test_rev_64( +// RV64-SAME: i64 noundef [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.bitreverse.i64(i64 [[A]]) +// RV64-NEXT: ret i64 [[TMP0]] +// +uint64_t test_rev_64(uint64_t a) { + return __riscv_rev_64(a); +} +#endif + +/* Scalar Saturating Addition and Subtraction */ + +// RV32-LABEL: define dso_local i32 @test_sadd_i32( +// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[A]], i32 [[B]]) +// RV32-NEXT: ret i32 [[ELT_SAT_I]] +// +// RV64-LABEL: define dso_local signext i32 @test_sadd_i32( +// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[A]], i32 [[B]]) +// RV64-NEXT: ret i32 [[ELT_SAT_I]] +// +int32_t test_sadd_i32(int32_t a, int32_t b) { + return __riscv_sadd_i32(a, b); +} + +// RV32-LABEL: define dso_local i32 @test_saddu_u32( +// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[A]], i32 [[B]]) +// RV32-NEXT: ret i32 [[ELT_SAT_I]] +// +// RV64-LABEL: define dso_local signext i32 @test_saddu_u32( +// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[A]], i32 [[B]]) +// RV64-NEXT: ret i32 [[ELT_SAT_I]] +// +uint32_t test_saddu_u32(uint32_t a, uint32_t b) { + return __riscv_saddu_u32(a, b); +} + +// RV32-LABEL: define dso_local i32 @test_ssub_i32( +// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[A]], i32 [[B]]) +// RV32-NEXT: ret i32 [[ELT_SAT_I]] +// +// RV64-LABEL: define dso_local signext i32 @test_ssub_i32( +// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[A]], i32 [[B]]) +// RV64-NEXT: ret i32 [[ELT_SAT_I]] +// +int32_t test_ssub_i32(int32_t a, int32_t b) { + return __riscv_ssub_i32(a, b); +} + +// RV32-LABEL: define dso_local i32 @test_ssubu_u32( +// RV32-SAME: i32 noundef [[A:%.*]], i32 noundef [[B:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[A]], i32 [[B]]) +// RV32-NEXT: ret i32 [[ELT_SAT_I]] +// +// RV64-LABEL: define dso_local signext i32 @test_ssubu_u32( +// RV64-SAME: i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[ELT_SAT_I:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[A]], i32 [[B]]) +// RV64-NEXT: ret i32 [[ELT_SAT_I]] +// +uint32_t test_ssubu_u32(uint32_t a, uint32_t b) { + return __riscv_ssubu_u32(a, b); +} + +/* Scalar Absolute Value */ + +// RV32-LABEL: define dso_local i32 @test_abs_u32( +// RV32-SAME: i32 noundef [[A:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[ELT_ABS_I:%.*]] = call i32 @llvm.abs.i32(i32 [[A]], i1 false) +// RV32-NEXT: ret i32 [[ELT_ABS_I]] +// +// RV64-LABEL: define dso_local signext i32 @test_abs_u32( +// RV64-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[ELT_ABS_I:%.*]] = call i32 @llvm.abs.i32(i32 [[A]], i1 false) +// RV64-NEXT: ret i32 [[ELT_ABS_I]] +// +uint32_t test_abs_u32(int32_t a) { + return __riscv_abs_u32(a); +} + +#if __riscv_xlen == 64 +// RV64-LABEL: define dso_local i64 @test_abs_u64( +// RV64-SAME: i64 noundef [[A:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[ELT_ABS_I:%.*]] = call i64 @llvm.abs.i64(i64 [[A]], i1 false) +// RV64-NEXT: ret i64 [[ELT_ABS_I]] +// +uint64_t test_abs_u64(int64_t a) { + return __riscv_abs_u64(a); +} +#endif + /* Packed Splat (32-bit) */ // RV32-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 f3857cedc3b5a..e68c9156311ba 100644 --- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c +++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c @@ -12,6 +12,54 @@ #include <riscv_packed_simd.h> +// Note: RV64 has no 32-bit `rev`; the spec expands it to `rev`+`srai`. +// CHECK-LABEL: test_rev_32: +// RV32: rev{{[[:space:]]}} +// RV64: rev{{[[:space:]]}} +// RV64-NEXT: srai +uint32_t test_rev_32(uint32_t a) { return __riscv_rev_32(a); } + +#if __riscv_xlen == 64 +// RV64-LABEL: test_rev_64: +// RV64: rev{{[[:space:]]}} +uint64_t test_rev_64(uint64_t a) { return __riscv_rev_64(a); } +#endif + +// CHECK-LABEL: test_sadd_i32: +// RV32: sadd{{[[:space:]]}} +// RV64: psadd.w +int32_t test_sadd_i32(int32_t a, int32_t b) { return __riscv_sadd_i32(a, b); } + +// CHECK-LABEL: test_saddu_u32: +// RV32: saddu{{[[:space:]]}} +// RV64: psaddu.w +uint32_t test_saddu_u32(uint32_t a, uint32_t b) { + return __riscv_saddu_u32(a, b); +} + +// CHECK-LABEL: test_ssub_i32: +// RV32: ssub{{[[:space:]]}} +// RV64: pssub.w +int32_t test_ssub_i32(int32_t a, int32_t b) { return __riscv_ssub_i32(a, b); } + +// CHECK-LABEL: test_ssubu_u32: +// RV32: ssubu{{[[:space:]]}} +// RV64: pssubu.w +uint32_t test_ssubu_u32(uint32_t a, uint32_t b) { + return __riscv_ssubu_u32(a, b); +} + +// CHECK-LABEL: test_abs_u32: +// RV32: abs{{[[:space:]]}} +// RV64: absw +uint32_t test_abs_u32(int32_t a) { return __riscv_abs_u32(a); } + +#if __riscv_xlen == 64 +// RV64-LABEL: test_abs_u64: +// RV64: abs{{[[:space:]]}} +uint64_t test_abs_u64(int64_t a) { return __riscv_abs_u64(a); } +#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); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
