https://github.com/TelGome created https://github.com/llvm/llvm-project/pull/223313
This pr support [Packed Load](https://github.com/riscv/riscv-p-spec/blob/master/P-ext-intrinsics.adoc#packed-load) >From 78c87c6aae75725265d587704c5e095182950aca Mon Sep 17 00:00:00 2001 From: Dongyan Chen <[email protected]> Date: Sun, 13 Sep 2026 17:26:28 +0800 Subject: [PATCH] [RISCV][P-ext] Support Packed Load --- clang/lib/Headers/riscv_packed_simd.h | 43 ++++ clang/test/CodeGen/RISCV/rvp-intrinsics.c | 200 ++++++++++++++++++ clang/test/Sema/riscv-pld-pointer-types.c | 42 ++++ .../riscv_packed_simd.c | 80 +++++++ 4 files changed, 365 insertions(+) create mode 100644 clang/test/Sema/riscv-pld-pointer-types.c diff --git a/clang/lib/Headers/riscv_packed_simd.h b/clang/lib/Headers/riscv_packed_simd.h index 5c359337d8b7f..af1b45813ff1b 100644 --- a/clang/lib/Headers/riscv_packed_simd.h +++ b/clang/lib/Headers/riscv_packed_simd.h @@ -30,6 +30,29 @@ typedef uint16_t uint16x4_t __attribute__((__vector_size__(8))); typedef int32_t int32x2_t __attribute__((__vector_size__(8))); typedef uint32_t uint32x2_t __attribute__((__vector_size__(8))); +/* Unaligned views of the packed types, used by the load/store intrinsics to + * express that the pointer may have arbitrary alignment. */ +typedef int8_t __packed_i8x4_unaligned + __attribute__((__vector_size__(4), __aligned__(1))); +typedef uint8_t __packed_u8x4_unaligned + __attribute__((__vector_size__(4), __aligned__(1))); +typedef int16_t __packed_i16x2_unaligned + __attribute__((__vector_size__(4), __aligned__(1))); +typedef uint16_t __packed_u16x2_unaligned + __attribute__((__vector_size__(4), __aligned__(1))); +typedef int8_t __packed_i8x8_unaligned + __attribute__((__vector_size__(8), __aligned__(1))); +typedef uint8_t __packed_u8x8_unaligned + __attribute__((__vector_size__(8), __aligned__(1))); +typedef int16_t __packed_i16x4_unaligned + __attribute__((__vector_size__(8), __aligned__(1))); +typedef uint16_t __packed_u16x4_unaligned + __attribute__((__vector_size__(8), __aligned__(1))); +typedef int32_t __packed_i32x2_unaligned + __attribute__((__vector_size__(8), __aligned__(1))); +typedef uint32_t __packed_u32x2_unaligned + __attribute__((__vector_size__(8), __aligned__(1))); + #define __DEFAULT_FN_ATTRS __attribute__((__always_inline__, __nodebug__)) #define __packed_splat2(ty, x) ((ty){(x), (x)}) @@ -312,6 +335,11 @@ typedef uint32_t uint32x2_t __attribute__((__vector_size__(8))); return __builtin_bit_cast(rty, __x); \ } +#define __packed_load(name, ty, elt_ty, ua_ty) \ + static __inline__ ty __DEFAULT_FN_ATTRS __riscv_##name(elt_ty *__p) { \ + return *(ua_ty *)__p; \ + } + // clang-format off: macro call sites have no trailing semicolons, which // confuses clang-format into a deeply nested expression. @@ -1040,6 +1068,20 @@ __packed_extract(pget_u16x4_u16, uint16_t, uint16x4_t, 3) __packed_extract(pget_i32x2_i32, int32_t, int32x2_t, 1) __packed_extract(pget_u32x2_u32, uint32_t, uint32x2_t, 1) +/* Packed Load (32-bit) */ +__packed_load(pld_i8x4, int8x4_t, int8_t, __packed_i8x4_unaligned) +__packed_load(pld_u8x4, uint8x4_t, uint8_t, __packed_u8x4_unaligned) +__packed_load(pld_i16x2, int16x2_t, int16_t, __packed_i16x2_unaligned) +__packed_load(pld_u16x2, uint16x2_t, uint16_t, __packed_u16x2_unaligned) + +/* Packed Load (64-bit) */ +__packed_load(pld_i8x8, int8x8_t, int8_t, __packed_i8x8_unaligned) +__packed_load(pld_u8x8, uint8x8_t, uint8_t, __packed_u8x8_unaligned) +__packed_load(pld_i16x4, int16x4_t, int16_t, __packed_i16x4_unaligned) +__packed_load(pld_u16x4, uint16x4_t, uint16_t, __packed_u16x4_unaligned) +__packed_load(pld_i32x2, int32x2_t, int32_t, __packed_i32x2_unaligned) +__packed_load(pld_u32x2, uint32x2_t, uint32_t, __packed_u32x2_unaligned) + /* Reinterpret Casts, Packed <-> Scalar (32-bit) */ __packed_reinterpret(u8x4_u32, uint32_t, uint8x4_t) __packed_reinterpret(u16x2_u32, uint32_t, uint16x2_t) @@ -1189,6 +1231,7 @@ __packed_reinterpret(u32x2_i32x2, int32x2_t, uint32x2_t) #undef __packed_abdsum #undef __packed_ternary_builtin_cast #undef __packed_extract +#undef __packed_load #undef __packed_reinterpret #undef __DEFAULT_FN_ATTRS diff --git a/clang/test/CodeGen/RISCV/rvp-intrinsics.c b/clang/test/CodeGen/RISCV/rvp-intrinsics.c index 7589157c38754..3ffda0ccc4571 100644 --- a/clang/test/CodeGen/RISCV/rvp-intrinsics.c +++ b/clang/test/CodeGen/RISCV/rvp-intrinsics.c @@ -10883,3 +10883,203 @@ int32_t test_pget_i32x2_i32(int32x2_t v) { uint32_t test_pget_u32x2_u32(uint32x2_t v) { return __riscv_pget_u32x2_u32(v, 1); } + +/* Packed Load (32-bit) */ +// RV32-LABEL: define dso_local i32 @test_pld_i8x4( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local i32 @test_pld_i8x4( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV64-NEXT: ret i32 [[TMP0]] +// +int8x4_t test_pld_i8x4(int8_t *p) { + return __riscv_pld_i8x4(p); +} + +// RV32-LABEL: define dso_local i32 @test_pld_u8x4( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local i32 @test_pld_u8x4( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV64-NEXT: ret i32 [[TMP0]] +// +uint8x4_t test_pld_u8x4(uint8_t *p) { + return __riscv_pld_u8x4(p); +} + +// RV32-LABEL: define dso_local i32 @test_pld_i16x2( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local i32 @test_pld_i16x2( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV64-NEXT: ret i32 [[TMP0]] +// +int16x2_t test_pld_i16x2(int16_t *p) { + return __riscv_pld_i16x2(p); +} + +// RV32-LABEL: define dso_local i32 @test_pld_u16x2( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local i32 @test_pld_u16x2( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV64-NEXT: ret i32 [[TMP0]] +// +uint16x2_t test_pld_u16x2(uint16_t *p) { + return __riscv_pld_u16x2(p); +} + +/* Packed Load (64-bit) */ +// RV32-LABEL: define dso_local i64 @test_pld_i8x8( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_i8x8( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +int8x8_t test_pld_i8x8(int8_t *p) { + return __riscv_pld_i8x8(p); +} + +// RV32-LABEL: define dso_local i64 @test_pld_u8x8( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_u8x8( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +uint8x8_t test_pld_u8x8(uint8_t *p) { + return __riscv_pld_u8x8(p); +} + +// RV32-LABEL: define dso_local i64 @test_pld_i16x4( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_i16x4( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +int16x4_t test_pld_i16x4(int16_t *p) { + return __riscv_pld_i16x4(p); +} + +// RV32-LABEL: define dso_local i64 @test_pld_u16x4( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_u16x4( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +uint16x4_t test_pld_u16x4(uint16_t *p) { + return __riscv_pld_u16x4(p); +} + +// RV32-LABEL: define dso_local i64 @test_pld_i32x2( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_i32x2( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +int32x2_t test_pld_i32x2(int32_t *p) { + return __riscv_pld_i32x2(p); +} + +// RV32-LABEL: define dso_local i64 @test_pld_u32x2( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_u32x2( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +uint32x2_t test_pld_u32x2(uint32_t *p) { + return __riscv_pld_u32x2(p); +} + +/* Packed Load with provable alignment (cf. the P-ext spec's note on + * __builtin_assume_aligned) */ +// RV32-LABEL: define dso_local i32 @test_pld_i8x4_aligned( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i32 4) ] +// RV32-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV32-NEXT: ret i32 [[TMP0]] +// +// RV64-LABEL: define dso_local i32 @test_pld_i8x4_aligned( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i64 4) ] +// RV64-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 1 +// RV64-NEXT: ret i32 [[TMP0]] +// +int8x4_t test_pld_i8x4_aligned(int8_t *p) { + return __riscv_pld_i8x4(__builtin_assume_aligned(p, 4)); +} + +// RV32-LABEL: define dso_local i64 @test_pld_i32x2_aligned( +// RV32-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV32-NEXT: [[ENTRY:.*:]] +// RV32-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i32 8) ] +// RV32-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV32-NEXT: ret i64 [[TMP0]] +// +// RV64-LABEL: define dso_local i64 @test_pld_i32x2_aligned( +// RV64-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// RV64-NEXT: [[ENTRY:.*:]] +// RV64-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P]], i64 8) ] +// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[P]], align 1 +// RV64-NEXT: ret i64 [[TMP0]] +// +int32x2_t test_pld_i32x2_aligned(int32_t *p) { + return __riscv_pld_i32x2(__builtin_assume_aligned(p, 8)); +} diff --git a/clang/test/Sema/riscv-pld-pointer-types.c b/clang/test/Sema/riscv-pld-pointer-types.c new file mode 100644 index 0000000000000..d4e739694ca94 --- /dev/null +++ b/clang/test/Sema/riscv-pld-pointer-types.c @@ -0,0 +1,42 @@ +// 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> + +// The __riscv_pld_* intrinsics take a pointer to the element type; passing a +// pointer to an unrelated type is ill-formed. + +int8x4_t test_pld_i8x4_ok(int8_t *p) { + return __riscv_pld_i8x4(p); +} + +int8x4_t test_pld_i8x4_void_ptr(void *p) { + return __riscv_pld_i8x4(p); +} + +int8x4_t test_pld_i8x4_array(void) { + int8_t a[4] = {1, 2, 3, 4}; + return __riscv_pld_i8x4(a); +} + +int8x4_t test_pld_i8x4_wrong_pointer_type(float *p) { + // expected-error@+1 {{incompatible pointer types passing 'float *' to parameter of type 'int8_t *' (aka 'signed char *')}} + return __riscv_pld_i8x4(p); +} + +uint16x2_t test_pld_u16x2_wrong_pointer_type(uint32_t *p) { + // expected-error@+1 {{incompatible pointer types passing 'uint32_t *' (aka 'unsigned int *') to parameter of type 'uint16_t *' (aka 'unsigned short *')}} + return __riscv_pld_u16x2(p); +} + +int32x2_t test_pld_i32x2_wrong_pointer_type(int8x4_t *p) { + // expected-error@+1 {{incompatible pointer types passing 'int8x4_t *' to parameter of type 'int32_t *' (aka 'int *')}} + return __riscv_pld_i32x2(p); +} + +int16x4_t test_pld_i16x4_const_discards_qualifiers(const int16_t *p) { + // expected-warning@+1 {{passing 'const int16_t *' (aka 'const short *') to parameter of type 'int16_t *' (aka 'short *') discards qualifiers}} + return __riscv_pld_i16x4(p); +} 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 df9623edfa71a..6fdaccb11cdd3 100644 --- a/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c +++ b/cross-project-tests/intrinsic-header-tests/riscv_packed_simd.c @@ -4420,3 +4420,83 @@ int64_t test_maccsu_w00_i64(int64_t rd, int32x2_t a, uint32x2_t b) { int64_t test_maccsu_w11_i64(int64_t rd, int32x2_t a, uint32x2_t b) { return __riscv_maccsu_w11_i64(rd, a, b); } + +/* Packed Load: the load is emitted with align 1, so the backend either emits + * a single lw/ld (targets with fast misaligned GPR loads) or splits it into + * per-byte loads. The generic target used here takes the split path. */ + +// CHECK-LABEL: test_pld_i8x4: +// CHECK-COUNT-4: lbu{{[[:space:]]}} +int8x4_t test_pld_i8x4(int8_t *p) { return __riscv_pld_i8x4(p); } + +// CHECK-LABEL: test_pld_u8x4: +// CHECK-COUNT-4: lbu{{[[:space:]]}} +uint8x4_t test_pld_u8x4(uint8_t *p) { return __riscv_pld_u8x4(p); } + +// CHECK-LABEL: test_pld_i16x2: +// CHECK-COUNT-4: lbu{{[[:space:]]}} +int16x2_t test_pld_i16x2(int16_t *p) { return __riscv_pld_i16x2(p); } + +// CHECK-LABEL: test_pld_u16x2: +// CHECK-COUNT-4: lbu{{[[:space:]]}} +uint16x2_t test_pld_u16x2(uint16_t *p) { return __riscv_pld_u16x2(p); } + +// CHECK-LABEL: test_pld_i8x8: +// RV32-COUNT-8: lbu{{[[:space:]]}} +// RV64-COUNT-8: lbu{{[[:space:]]}} +int8x8_t test_pld_i8x8(int8_t *p) { return __riscv_pld_i8x8(p); } + +// CHECK-LABEL: test_pld_u8x8: +// CHECK-COUNT-8: lbu{{[[:space:]]}} +uint8x8_t test_pld_u8x8(uint8_t *p) { return __riscv_pld_u8x8(p); } + +// CHECK-LABEL: test_pld_i16x4: +// CHECK-COUNT-8: lbu{{[[:space:]]}} +int16x4_t test_pld_i16x4(int16_t *p) { return __riscv_pld_i16x4(p); } + +// CHECK-LABEL: test_pld_u16x4: +// CHECK-COUNT-8: lbu{{[[:space:]]}} +uint16x4_t test_pld_u16x4(uint16_t *p) { return __riscv_pld_u16x4(p); } + +// CHECK-LABEL: test_pld_i32x2: +// CHECK-COUNT-8: lbu{{[[:space:]]}} +int32x2_t test_pld_i32x2(int32_t *p) { return __riscv_pld_i32x2(p); } + +// CHECK-LABEL: test_pld_u32x2: +// CHECK-COUNT-8: lbu{{[[:space:]]}} +uint32x2_t test_pld_u32x2(uint32_t *p) { return __riscv_pld_u32x2(p); } + +/* Packed Load with provable alignment: __builtin_assume_aligned restores a + * known alignment, so the load is not split. */ + +// CHECK-LABEL: test_pld_i8x4_aligned: +// CHECK: lw +// CHECK-NOT: lbu +int8x4_t test_pld_i8x4_aligned(int8_t *p) { + return __riscv_pld_i8x4(__builtin_assume_aligned(p, 4)); +} + +// CHECK-LABEL: test_pld_u16x2_aligned: +// CHECK: lw +// CHECK-NOT: lbu +uint16x2_t test_pld_u16x2_aligned(uint16_t *p) { + return __riscv_pld_u16x2(__builtin_assume_aligned(p, 4)); +} + +// CHECK-LABEL: test_pld_i8x8_aligned: +// RV32: lw +// RV32-NOT: lbu +// RV64: ld +// RV64-NOT: lbu +int8x8_t test_pld_i8x8_aligned(int8_t *p) { + return __riscv_pld_i8x8(__builtin_assume_aligned(p, 8)); +} + +// CHECK-LABEL: test_pld_u32x2_aligned: +// RV32: lw +// RV32-NOT: lbu +// RV64: ld +// RV64-NOT: lbu +uint32x2_t test_pld_u32x2_aligned(uint32_t *p) { + return __riscv_pld_u32x2(__builtin_assume_aligned(p, 8)); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
