From 91b1741ddef21aeeae837cf0bc6b60f6daa99e57 Mon Sep 17 00:00:00 2001 From: CarolineConcatto Date: Tue, 20 Oct 2026 13:57:09 +0000 Subject: [PATCH] Add ACLE intrinsics for unprivileged pair loads/stores Add ACLE intrinsics for: LDTP[1]/STTP[2] unprivileged pairs. These instructions require FEAT_LSUI. The LDTP[1]/STTP[2] unprivileged pairs will only be offer one prototype format, despite the fact it has encoding for pre/post-index and offset. The idead is that the compiler will select the best instruction to lower according to the code, but I suggest by default it would lower to the offset instruction encoding. The 128-bit instruction forms are proposed using Neon vector types because there are no scalar arithmetic instructions with a 128-bit scalar data type. A scalar 128-bit floating-point type is not consistently available across AArch64 targets. For example, long double is 64-bit on Darwin and Windows AArch64 targets. References: [1]https://support.arm.com/documentation/ddi0602/2026-09/SIMD-FP-Instructions/LDTP--SIMD-FP---Load-unprivileged-pair-of-SIMD-FP-registers-?lang=en [2]https://support.arm.com/documentation/ddi0602/2026-09/SIMD-FP-Instructions/STTP--SIMD-FP---Store-unprivileged-pair-of-SIMD-FP-registers-?lang=en --- neon_intrinsics/advsimd.md | 43 +++++++++++++++++++ neon_intrinsics/advsimd.template.md | 1 + tools/intrinsic_db/advsimd.csv | 27 ++++++++++++ tools/intrinsic_db/advsimd_classification.csv | 26 +++++++++++ 4 files changed, 97 insertions(+) diff --git a/neon_intrinsics/advsimd.md b/neon_intrinsics/advsimd.md index b44f4769..b5b341a8 100644 --- a/neon_intrinsics/advsimd.md +++ b/neon_intrinsics/advsimd.md @@ -118,6 +118,7 @@ See [Changelog for year 2026](#changelog-for-year-2026). ### Changelog for year 2026 +* Added proposed support for FEAT_LSUI LDTP/STTP intrinsics. * Added support for FEAT_F16F32DOT * Added support for FEAT_F16F32MM and FEAT_F16MM * Renamed vmmlaq_f16_f16 to vmmlaq_f16 @@ -3829,6 +3830,30 @@ The intrinsics in this section are guarded by the macro ``__ARM_NEON``. ### Load + + +#### Unprivileged pair + +The proposed FEAT_LSUI AArch64 intrinsics expose LDTP and STTP for two 128-bit SIMD&FP registers. + +| Intrinsic | Argument preparation | AArch64 Instruction | Result | Supported architectures | +|---|---|---|---|---| +| `int8x16x2_t vldtq_s8_x2(int8_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `int16x8x2_t vldtq_s16_x2(int16_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `int32x4x2_t vldtq_s32_x2(int32_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `int64x2x2_t vldtq_s64_x2(int64_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `uint8x16x2_t vldtq_u8_x2(uint8_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `uint16x8x2_t vldtq_u16_x2(uint16_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `uint32x4x2_t vldtq_u32_x2(uint32_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `uint64x2x2_t vldtq_u64_x2(uint64_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `float16x8x2_t vldtq_f16_x2(float16_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `bfloat16x8x2_t vldtq_bf16_x2(bfloat16_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `float32x4x2_t vldtq_f32_x2(float32_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `float64x2x2_t vldtq_f64_x2(float64_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | +| `mfloat8x16x2_t vldtq_mf8_x2(mfloat8_t const *ptr)` | `ptr -> Xn` | `LDTP Qt1,Qt2,[Xn]` | `Qt1/Qt2 -> result.val[0]/result.val[1]` | `A64` | + +The architectural signed immediate is not exposed by this pointer-only API; normal compiler address formation may be used when an offset is needed. + #### Stride | Intrinsic | Argument preparation | AArch64 Instruction | Result | Supported architectures | @@ -4308,6 +4333,24 @@ The intrinsics in this section are guarded by the macro ``__ARM_NEON``. ### Store +#### Unprivileged pair + +| Intrinsic | Argument preparation | AArch64 Instruction | Result | Supported architectures | +|---|---|---|---|---| +| `void vsttq_s8_x2(int8_t *ptr, int8x16x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_s16_x2(int16_t *ptr, int16x8x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_s32_x2(int32_t *ptr, int32x4x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_s64_x2(int64_t *ptr, int64x2x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_u8_x2(uint8_t *ptr, uint8x16x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_u16_x2(uint16_t *ptr, uint16x8x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_u32_x2(uint32_t *ptr, uint32x4x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_u64_x2(uint64_t *ptr, uint64x2x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_f16_x2(float16_t *ptr, float16x8x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_bf16_x2(bfloat16_t *ptr, bfloat16x8x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_f32_x2(float32_t *ptr, float32x4x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_f64_x2(float64_t *ptr, float64x2x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | +| `void vsttq_mf8_x2(mfloat8_t *ptr, mfloat8x16x2_t val)` | `val.val[0]/val.val[1] -> Qt1/Qt2`
`ptr -> Xn` | `STTP Qt1,Qt2,[Xn]` | | `A64` | + #### Stride | Intrinsic | Argument preparation | AArch64 Instruction | Result | Supported architectures | diff --git a/neon_intrinsics/advsimd.template.md b/neon_intrinsics/advsimd.template.md index 45fee375..829c98a1 100644 --- a/neon_intrinsics/advsimd.template.md +++ b/neon_intrinsics/advsimd.template.md @@ -118,6 +118,7 @@ See [Changelog for year 2026](#changelog-for-year-2026). ### Changelog for year 2026 +* Added proposed support for FEAT_LSUI LDTP/STTP intrinsics. * Added support for FEAT_F16F32DOT * Added support for FEAT_F16F32MM and FEAT_F16MM * Renamed vmmlaq_f16_f16 to vmmlaq_f16 diff --git a/tools/intrinsic_db/advsimd.csv b/tools/intrinsic_db/advsimd.csv index d5bac768..16bf8167 100644 --- a/tools/intrinsic_db/advsimd.csv +++ b/tools/intrinsic_db/advsimd.csv @@ -4863,3 +4863,30 @@ float32x4_t vmmlaq_f32_f16(float32x4_t r, float16x8_t a, float16x8_t b) r -> Vd.
Non-widening half-precision matrix multiply instruction. Requires the +f16mm architecture extension. float16x8_t vmmlaq_f16(float16x8_t r, float16x8_t a, float16x8_t b) r -> Vd.8H;a -> Vn.8H;b -> Vm.8H FMMLA Vd.8H, Vn.8H, Vm.8H Vd.8H -> result A64 +
Unprivileged pair with non-temporal hint +int8x16x2_t vldtq_s8_x2(int8_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +int16x8x2_t vldtq_s16_x2(int16_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +int32x4x2_t vldtq_s32_x2(int32_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +int64x2x2_t vldtq_s64_x2(int64_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +uint8x16x2_t vldtq_u8_x2(uint8_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +uint16x8x2_t vldtq_u16_x2(uint16_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +uint32x4x2_t vldtq_u32_x2(uint32_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +uint64x2x2_t vldtq_u64_x2(uint64_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +float16x8x2_t vldtq_f16_x2(float16_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +bfloat16x8x2_t vldtq_bf16_x2(bfloat16_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +float32x4x2_t vldtq_f32_x2(float32_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +float64x2x2_t vldtq_f64_x2(float64_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +mfloat8x16x2_t vldtq_mf8_x2(mfloat8_t const *ptr) ptr -> Xn LDTP Qt1,Qt2,[Xn] Qt2 -> result.val[1];Qt1 -> result.val[0] A64 +void vsttq_s8_x2(int8_t *ptr, int8x16x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_s16_x2(int16_t *ptr, int16x8x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_s32_x2(int32_t *ptr, int32x4x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_s64_x2(int64_t *ptr, int64x2x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_u8_x2(uint8_t *ptr, uint8x16x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_u16_x2(uint16_t *ptr, uint16x8x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_u32_x2(uint32_t *ptr, uint32x4x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_u64_x2(uint64_t *ptr, uint64x2x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_f16_x2(float16_t *ptr, float16x8x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_bf16_x2(bfloat16_t *ptr, bfloat16x8x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_f32_x2(float32_t *ptr, float32x4x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_f64_x2(float64_t *ptr, float64x2x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 +void vsttq_mf8_x2(mfloat8_t *ptr, mfloat8x16x2_t val) val.val[1] -> Qt2;val.val[0] -> Qt1;ptr -> Xn STTP Qt1,Qt2,[Xn] A64 diff --git a/tools/intrinsic_db/advsimd_classification.csv b/tools/intrinsic_db/advsimd_classification.csv index 312f9b05..e939d123 100644 --- a/tools/intrinsic_db/advsimd_classification.csv +++ b/tools/intrinsic_db/advsimd_classification.csv @@ -4745,3 +4745,29 @@ vdot_laneq_f32_f16 Vector arithmetic|Dot product vdotq_lane_f32_f16 Vector arithmetic|Dot product vmmlaq_f32_f16 Vector arithmetic|Matrix multiply vmmlaq_f16 Vector arithmetic|Matrix multiply +vldtq_s8_x2 Load|Stride +vldtq_s16_x2 Load|Stride +vldtq_s32_x2 Load|Stride +vldtq_s64_x2 Load|Stride +vldtq_u8_x2 Load|Stride +vldtq_u16_x2 Load|Stride +vldtq_u32_x2 Load|Stride +vldtq_u64_x2 Load|Stride +vldtq_f16_x2 Load|Stride +vldtq_bf16_x2 Load|Stride +vldtq_f32_x2 Load|Stride +vldtq_f64_x2 Load|Stride +vldtq_mf8_x2 Load|Stride +vsttq_s8_x2 Store|Stride +vsttq_s16_x2 Store|Stride +vsttq_s32_x2 Store|Stride +vsttq_s64_x2 Store|Stride +vsttq_u8_x2 Store|Stride +vsttq_u16_x2 Store|Stride +vsttq_u32_x2 Store|Stride +vsttq_u64_x2 Store|Stride +vsttq_f16_x2 Store|Stride +vsttq_bf16_x2 Store|Stride +vsttq_f32_x2 Store|Stride +vsttq_f64_x2 Store|Stride +vsttq_mf8_x2 Store|Stride