Skip to content

Commit d1471fe

Browse files
authored
Merge pull request #42 from okaneco/prep_new_release
Bump version to `0.2.4`, update docs
2 parents 058a986 + 4447723 commit d1471fe

5 files changed

Lines changed: 120 additions & 106 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# `safe_unaligned_simd` changelog
22

3+
## Version 0.2.4 - 2026-02
4+
5+
Added support for *n*-element structure loads and stores on `aarch64` NEON (i.e., into `int16x8x3_t`).
6+
This provides wrappers for `vldNq` and `vstNq` intrinsics which deinterleave into N registers on load and interleave into memory on store.
7+
8+
### Notable PRs
9+
10+
[`#41`][41] - Add wrappers for NEON interleaved load/store
11+
312
## Version 0.2.3 - 2025-09
413

514
Added support for AVX-512 intrinsics on `x86`/`x86_64`.
@@ -67,6 +76,7 @@ The functions are located within an architecture's `cell` module.
6776

6877
Initial release
6978

79+
[41]: https://github.com/okaneco/safe_unaligned_simd/pull/41
7080
[30]: https://github.com/okaneco/safe_unaligned_simd/pull/30
7181
[29]: https://github.com/okaneco/safe_unaligned_simd/pull/29
7282
[28]: https://github.com/okaneco/safe_unaligned_simd/pull/28

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "safe_unaligned_simd"
3-
version = "0.2.3"
3+
version = "0.2.4"
44
edition = "2024"
55
rust-version = "1.88"
66
license = "MIT OR Apache-2.0"

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,15 @@ pub fn v128_store<T: Is16BytesUnaligned>(t: &mut T, v: v128);
7777

7878
## A note on creating mutable array references from slices
7979

80+
**_tl;dr:_ Use [`as_mut_array`][as_mut_array] to avoid this bug, stable since `1.93`.**
81+
8082
Beware of accidentally creating mutable references to temporary arrays.
8183

8284
Rust will implicitly clone an array from a slice and return a mutable reference to that clone if not wrapped properly in parentheses.
8385

8486
```rust
8587
// Valid mutable array reference creation
88+
let out_data: &mut [u8; 4] = chunk[..4].as_mut_array().unwrap(); // since 1.93
8689
let out_data: &mut [u8; 4] = (&mut chunk[..4]).try_into().unwrap();
8790
let out_data = TryInto::<&mut [u8; 4]>::try_into(&mut chunk[..4]).unwrap();
8891

@@ -93,10 +96,9 @@ let out_data = TryInto::<&mut [u8; 4]>::try_into(&mut chunk[..4]).unwrap();
9396
let out_data = &mut chunk[..4].try_into().unwrap();
9497
```
9598

96-
A better solution is to use [`as_mut_array`][as_mut_array] to sidestep this entirely.<br>
97-
As of the time of this writing (`rustc 1.91`), `as_mut_array` is unstable but in the process of being stabilized.
99+
The now-stable [`as_mut_array`][as_mut_array] sidesteps this issue entirely.<br>
98100

99-
[as_mut_array]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array
101+
[as_mut_array]: https://doc.rust-lang.org/1.93.0/std/primitive.slice.html#method.as_mut_array
100102

101103
## License
102104
This crate is licensed under either

src/aarch64.rs

Lines changed: 98 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -334,137 +334,68 @@ vld_n_replicate_k! {
334334

335335
/// Load `u8` data to two registers, with de-interleaving.
336336
fn vld2q_u8(_: &[u8; 16][..2] as [u8; 32]) -> uint8x16x2_t;
337-
/// Load `i8` data to two registers, with de-interleaving
337+
/// Load `i8` data to two registers, with de-interleaving.
338338
fn vld2q_s8(_: &[i8; 16][..2] as [i8; 32]) -> int8x16x2_t;
339-
/// Load `u16` data to two registers, with de-interleaving
339+
/// Load `u16` data to two registers, with de-interleaving.
340340
fn vld2q_u16(_: &[u16; 8][..2] as [u16; 16]) -> uint16x8x2_t;
341-
/// Load `i16` data to two registers, with de-interleaving
341+
/// Load `i16` data to two registers, with de-interleaving.
342342
fn vld2q_s16(_: &[i16; 8][..2] as [i16; 16]) -> int16x8x2_t;
343-
/// Load `u32` data to two registers, with de-interleaving
343+
/// Load `u32` data to two registers, with de-interleaving.
344344
fn vld2q_u32(_: &[u32; 4][..2] as [u32; 8]) -> uint32x4x2_t;
345-
/// Load `i32` data to two registers, with de-interleaving
345+
/// Load `i32` data to two registers, with de-interleaving.
346346
fn vld2q_s32(_: &[i32; 4][..2] as [i32; 8]) -> int32x4x2_t;
347-
/// Load `f32` data to two registers, with de-interleaving
347+
/// Load `f32` data to two registers, with de-interleaving.
348348
fn vld2q_f32(_: &[f32; 4][..2] as [f32; 8]) -> float32x4x2_t;
349-
/// Load `u64` data to two registers, with de-interleaving
349+
/// Load `u64` data to two registers, with de-interleaving.
350350
fn vld2q_u64(_: &[u64; 2][..2] as [u64; 4]) -> uint64x2x2_t;
351-
/// Load `i64` data to two registers, with de-interleaving
351+
/// Load `i64` data to two registers, with de-interleaving.
352352
fn vld2q_s64(_: &[i64; 2][..2] as [i64; 4]) -> int64x2x2_t;
353-
/// Load `f64` data to two registers, with de-interleaving
353+
/// Load `f64` data to two registers, with de-interleaving.
354354
fn vld2q_f64(_: &[f64; 2][..2] as [f64; 4]) -> float64x2x2_t;
355355

356-
/// Load `u8` data to three registers, with de-interleaving
356+
/// Load `u8` data to three registers, with de-interleaving.
357357
fn vld3q_u8(_: &[u8; 16][..3] as [u8; 48]) -> uint8x16x3_t;
358-
/// Load `i8` data to three registers, with de-interleaving
358+
/// Load `i8` data to three registers, with de-interleaving.
359359
fn vld3q_s8(_: &[i8; 16][..3] as [i8; 48]) -> int8x16x3_t;
360-
/// Load `u16` data to three registers, with de-interleaving
360+
/// Load `u16` data to three registers, with de-interleaving.
361361
fn vld3q_u16(_: &[u16; 8][..3] as [u16; 24]) -> uint16x8x3_t;
362-
/// Load `i16` data to three registers, with de-interleaving
362+
/// Load `i16` data to three registers, with de-interleaving.
363363
fn vld3q_s16(_: &[i16; 8][..3] as [i16; 24]) -> int16x8x3_t;
364-
/// Load `u32` data to three registers, with de-interleaving
364+
/// Load `u32` data to three registers, with de-interleaving.
365365
fn vld3q_u32(_: &[u32; 4][..3] as [u32; 12]) -> uint32x4x3_t;
366-
/// Load `i32` data to three registers, with de-interleaving
366+
/// Load `i32` data to three registers, with de-interleaving.
367367
fn vld3q_s32(_: &[i32; 4][..3] as [i32; 12]) -> int32x4x3_t;
368-
/// Load `f32` data to three registers, with de-interleaving
368+
/// Load `f32` data to three registers, with de-interleaving.
369369
fn vld3q_f32(_: &[f32; 4][..3] as [f32; 12]) -> float32x4x3_t;
370-
/// Load `u64` data to three registers, with de-interleaving
370+
/// Load `u64` data to three registers, with de-interleaving.
371371
fn vld3q_u64(_: &[u64; 2][..3] as [u64; 6]) -> uint64x2x3_t;
372-
/// Load `i64` data to three registers, with de-interleaving
372+
/// Load `i64` data to three registers, with de-interleaving.
373373
fn vld3q_s64(_: &[i64; 2][..3] as [i64; 6]) -> int64x2x3_t;
374-
/// Load `f64` data to three registers, with de-interleaving
374+
/// Load `f64` data to three registers, with de-interleaving.
375375
fn vld3q_f64(_: &[f64; 2][..3] as [f64; 6]) -> float64x2x3_t;
376376

377-
/// Load `u8` data to four registers, with de-interleaving
377+
/// Load `u8` data to four registers, with de-interleaving.
378378
fn vld4q_u8(_: &[u8; 16][..4] as [u8; 64]) -> uint8x16x4_t;
379-
/// Load `i8` data to four registers, with de-interleaving
379+
/// Load `i8` data to four registers, with de-interleaving.
380380
fn vld4q_s8(_: &[i8; 16][..4] as [i8; 64]) -> int8x16x4_t;
381-
/// Load `u16` data to four registers, with de-interleaving
381+
/// Load `u16` data to four registers, with de-interleaving.
382382
fn vld4q_u16(_: &[u16; 8][..4] as [u16; 32]) -> uint16x8x4_t;
383-
/// Load `i16` data to four registers, with de-interleaving
383+
/// Load `i16` data to four registers, with de-interleaving.
384384
fn vld4q_s16(_: &[i16; 8][..4] as [i16; 32]) -> int16x8x4_t;
385-
/// Load `u32` data to four registers, with de-interleaving
385+
/// Load `u32` data to four registers, with de-interleaving.
386386
fn vld4q_u32(_: &[u32; 4][..4] as [u32; 16]) -> uint32x4x4_t;
387-
/// Load `i32` data to four registers, with de-interleaving
387+
/// Load `i32` data to four registers, with de-interleaving.
388388
fn vld4q_s32(_: &[i32; 4][..4] as [i32; 16]) -> int32x4x4_t;
389-
/// Load `f32` data to four registers, with de-interleaving
389+
/// Load `f32` data to four registers, with de-interleaving.
390390
fn vld4q_f32(_: &[f32; 4][..4] as [f32; 16]) -> float32x4x4_t;
391-
/// Load `u64` data to four registers, with de-interleaving
391+
/// Load `u64` data to four registers, with de-interleaving.
392392
fn vld4q_u64(_: &[u64; 2][..4] as [u64; 8]) -> uint64x2x4_t;
393-
/// Load `i64` data to four registers, with de-interleaving
393+
/// Load `i64` data to four registers, with de-interleaving.
394394
fn vld4q_s64(_: &[i64; 2][..4] as [i64; 8]) -> int64x2x4_t;
395-
/// Load `f64` data to four registers, with de-interleaving
395+
/// Load `f64` data to four registers, with de-interleaving.
396396
fn vld4q_f64(_: &[f64; 2][..4] as [f64; 8]) -> float64x2x4_t;
397397
}
398398

399-
vld_n_replicate_k! {
400-
unsafe: store;
401-
// Stores full registers, so 16 bytes per register
402-
size: assert_size_16bytes;
403-
404-
/// Store `u8` data from two registers, with interleaving
405-
fn vst2q_u8(_: &[u8; 16][..2] as [u8; 32]) -> uint8x16x2_t;
406-
/// Store `i8` data from two registers, with interleaving
407-
fn vst2q_s8(_: &[i8; 16][..2] as [i8; 32]) -> int8x16x2_t;
408-
/// Store `u16` data from two registers, with interleaving
409-
fn vst2q_u16(_: &[u16; 8][..2] as [u16; 16]) -> uint16x8x2_t;
410-
/// Store `i16` data from two registers, with interleaving
411-
fn vst2q_s16(_: &[i16; 8][..2] as [i16; 16]) -> int16x8x2_t;
412-
/// Store `u32` data from two registers, with interleaving
413-
fn vst2q_u32(_: &[u32; 4][..2] as [u32; 8]) -> uint32x4x2_t;
414-
/// Store `i32` data from two registers, with interleaving
415-
fn vst2q_s32(_: &[i32; 4][..2] as [i32; 8]) -> int32x4x2_t;
416-
/// Store `f32` data from two registers, with interleaving
417-
fn vst2q_f32(_: &[f32; 4][..2] as [f32; 8]) -> float32x4x2_t;
418-
/// Store `u64` data from two registers, with interleaving
419-
fn vst2q_u64(_: &[u64; 2][..2] as [u64; 4]) -> uint64x2x2_t;
420-
/// Store `i64` data from two registers, with interleaving
421-
fn vst2q_s64(_: &[i64; 2][..2] as [i64; 4]) -> int64x2x2_t;
422-
/// Store `f64` data from two registers, with interleaving
423-
fn vst2q_f64(_: &[f64; 2][..2] as [f64; 4]) -> float64x2x2_t;
424-
425-
/// Store `u8` data from three registers, with interleaving
426-
fn vst3q_u8(_: &[u8; 16][..3] as [u8; 48]) -> uint8x16x3_t;
427-
/// Store `i8` data from three registers, with interleaving
428-
fn vst3q_s8(_: &[i8; 16][..3] as [i8; 48]) -> int8x16x3_t;
429-
/// Store `u16` data from three registers, with interleaving
430-
fn vst3q_u16(_: &[u16; 8][..3] as [u16; 24]) -> uint16x8x3_t;
431-
/// Store `i16` data from three registers, with interleaving
432-
fn vst3q_s16(_: &[i16; 8][..3] as [i16; 24]) -> int16x8x3_t;
433-
/// Store `u32` data from three registers, with interleaving
434-
fn vst3q_u32(_: &[u32; 4][..3] as [u32; 12]) -> uint32x4x3_t;
435-
/// Store `i32` data from three registers, with interleaving
436-
fn vst3q_s32(_: &[i32; 4][..3] as [i32; 12]) -> int32x4x3_t;
437-
/// Store `f32` data from three registers, with interleaving
438-
fn vst3q_f32(_: &[f32; 4][..3] as [f32; 12]) -> float32x4x3_t;
439-
/// Store `u64` data from three registers, with interleaving
440-
fn vst3q_u64(_: &[u64; 2][..3] as [u64; 6]) -> uint64x2x3_t;
441-
/// Store `i64` data from three registers, with interleaving
442-
fn vst3q_s64(_: &[i64; 2][..3] as [i64; 6]) -> int64x2x3_t;
443-
/// Store `f64` data from three registers, with interleaving
444-
fn vst3q_f64(_: &[f64; 2][..3] as [f64; 6]) -> float64x2x3_t;
445-
446-
/// Store `u8` data from four registers, with interleaving
447-
fn vst4q_u8(_: &[u8; 16][..4] as [u8; 64]) -> uint8x16x4_t;
448-
/// Store `i8` data from four registers, with interleaving
449-
fn vst4q_s8(_: &[i8; 16][..4] as [i8; 64]) -> int8x16x4_t;
450-
/// Store `u16` data from four registers, with interleaving
451-
fn vst4q_u16(_: &[u16; 8][..4] as [u16; 32]) -> uint16x8x4_t;
452-
/// Store `i16` data from four registers, with interleaving
453-
fn vst4q_s16(_: &[i16; 8][..4] as [i16; 32]) -> int16x8x4_t;
454-
/// Store `u32` data from four registers, with interleaving
455-
fn vst4q_u32(_: &[u32; 4][..4] as [u32; 16]) -> uint32x4x4_t;
456-
/// Store `i32` data from four registers, with interleaving
457-
fn vst4q_s32(_: &[i32; 4][..4] as [i32; 16]) -> int32x4x4_t;
458-
/// Store `f32` data from four registers, with interleaving
459-
fn vst4q_f32(_: &[f32; 4][..4] as [f32; 16]) -> float32x4x4_t;
460-
/// Store `u64` data from four registers, with interleaving
461-
fn vst4q_u64(_: &[u64; 2][..4] as [u64; 8]) -> uint64x2x4_t;
462-
/// Store `i64` data from four registers, with interleaving
463-
fn vst4q_s64(_: &[i64; 2][..4] as [i64; 8]) -> int64x2x4_t;
464-
/// Store `f64` data from four registers, with interleaving
465-
fn vst4q_f64(_: &[f64; 2][..4] as [f64; 8]) -> float64x2x4_t;
466-
}
467-
468399
vld_n_replicate_k! {
469400
unsafe: store;
470401
// Stores full registers, so 8 bytes per register
@@ -645,6 +576,75 @@ vld_n_replicate_k! {
645576
fn vst1q_f64_x4(_: &[f64; 2][..4] as [[f64; 2]; 4]) -> float64x2x4_t;
646577
}
647578

579+
vld_n_replicate_k! {
580+
unsafe: store;
581+
// Stores full registers, so 16 bytes per register
582+
size: assert_size_16bytes;
583+
584+
/// Store `u8` data from two registers, with interleaving.
585+
fn vst2q_u8(_: &[u8; 16][..2] as [u8; 32]) -> uint8x16x2_t;
586+
/// Store `i8` data from two registers, with interleaving.
587+
fn vst2q_s8(_: &[i8; 16][..2] as [i8; 32]) -> int8x16x2_t;
588+
/// Store `u16` data from two registers, with interleaving.
589+
fn vst2q_u16(_: &[u16; 8][..2] as [u16; 16]) -> uint16x8x2_t;
590+
/// Store `i16` data from two registers, with interleaving.
591+
fn vst2q_s16(_: &[i16; 8][..2] as [i16; 16]) -> int16x8x2_t;
592+
/// Store `u32` data from two registers, with interleaving.
593+
fn vst2q_u32(_: &[u32; 4][..2] as [u32; 8]) -> uint32x4x2_t;
594+
/// Store `i32` data from two registers, with interleaving.
595+
fn vst2q_s32(_: &[i32; 4][..2] as [i32; 8]) -> int32x4x2_t;
596+
/// Store `f32` data from two registers, with interleaving.
597+
fn vst2q_f32(_: &[f32; 4][..2] as [f32; 8]) -> float32x4x2_t;
598+
/// Store `u64` data from two registers, with interleaving.
599+
fn vst2q_u64(_: &[u64; 2][..2] as [u64; 4]) -> uint64x2x2_t;
600+
/// Store `i64` data from two registers, with interleaving.
601+
fn vst2q_s64(_: &[i64; 2][..2] as [i64; 4]) -> int64x2x2_t;
602+
/// Store `f64` data from two registers, with interleaving.
603+
fn vst2q_f64(_: &[f64; 2][..2] as [f64; 4]) -> float64x2x2_t;
604+
605+
/// Store `u8` data from three registers, with interleaving.
606+
fn vst3q_u8(_: &[u8; 16][..3] as [u8; 48]) -> uint8x16x3_t;
607+
/// Store `i8` data from three registers, with interleaving.
608+
fn vst3q_s8(_: &[i8; 16][..3] as [i8; 48]) -> int8x16x3_t;
609+
/// Store `u16` data from three registers, with interleaving.
610+
fn vst3q_u16(_: &[u16; 8][..3] as [u16; 24]) -> uint16x8x3_t;
611+
/// Store `i16` data from three registers, with interleaving.
612+
fn vst3q_s16(_: &[i16; 8][..3] as [i16; 24]) -> int16x8x3_t;
613+
/// Store `u32` data from three registers, with interleaving.
614+
fn vst3q_u32(_: &[u32; 4][..3] as [u32; 12]) -> uint32x4x3_t;
615+
/// Store `i32` data from three registers, with interleaving.
616+
fn vst3q_s32(_: &[i32; 4][..3] as [i32; 12]) -> int32x4x3_t;
617+
/// Store `f32` data from three registers, with interleaving.
618+
fn vst3q_f32(_: &[f32; 4][..3] as [f32; 12]) -> float32x4x3_t;
619+
/// Store `u64` data from three registers, with interleaving.
620+
fn vst3q_u64(_: &[u64; 2][..3] as [u64; 6]) -> uint64x2x3_t;
621+
/// Store `i64` data from three registers, with interleaving.
622+
fn vst3q_s64(_: &[i64; 2][..3] as [i64; 6]) -> int64x2x3_t;
623+
/// Store `f64` data from three registers, with interleaving.
624+
fn vst3q_f64(_: &[f64; 2][..3] as [f64; 6]) -> float64x2x3_t;
625+
626+
/// Store `u8` data from four registers, with interleaving.
627+
fn vst4q_u8(_: &[u8; 16][..4] as [u8; 64]) -> uint8x16x4_t;
628+
/// Store `i8` data from four registers, with interleaving.
629+
fn vst4q_s8(_: &[i8; 16][..4] as [i8; 64]) -> int8x16x4_t;
630+
/// Store `u16` data from four registers, with interleaving.
631+
fn vst4q_u16(_: &[u16; 8][..4] as [u16; 32]) -> uint16x8x4_t;
632+
/// Store `i16` data from four registers, with interleaving.
633+
fn vst4q_s16(_: &[i16; 8][..4] as [i16; 32]) -> int16x8x4_t;
634+
/// Store `u32` data from four registers, with interleaving.
635+
fn vst4q_u32(_: &[u32; 4][..4] as [u32; 16]) -> uint32x4x4_t;
636+
/// Store `i32` data from four registers, with interleaving.
637+
fn vst4q_s32(_: &[i32; 4][..4] as [i32; 16]) -> int32x4x4_t;
638+
/// Store `f32` data from four registers, with interleaving.
639+
fn vst4q_f32(_: &[f32; 4][..4] as [f32; 16]) -> float32x4x4_t;
640+
/// Store `u64` data from four registers, with interleaving.
641+
fn vst4q_u64(_: &[u64; 2][..4] as [u64; 8]) -> uint64x2x4_t;
642+
/// Store `i64` data from four registers, with interleaving.
643+
fn vst4q_s64(_: &[i64; 2][..4] as [i64; 8]) -> int64x2x4_t;
644+
/// Store `f64` data from four registers, with interleaving.
645+
fn vst4q_f64(_: &[f64; 2][..4] as [f64; 8]) -> float64x2x4_t;
646+
}
647+
648648
vld_n_replicate_k! {
649649
unsafe: load;
650650
size: various_sizes;

src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@
4444
//!
4545
//! ## A note on creating mutable array references from slices
4646
//!
47+
//! **_tl;dr:_ Use [`as_mut_array`][as_mut_array] to avoid this bug, stable since `1.93`.**
48+
//!
4749
//! Beware of accidentally creating mutable references to temporary arrays.
4850
//!
4951
//! Rust will implicitly clone an array from a slice and return a mutable reference to that clone if not wrapped properly in parentheses.
5052
//!
51-
//! ```rust,no_run
53+
//! ```rust,ignore
5254
//! # let mut chunk = [0u8; 8];
5355
//! // Valid mutable array reference creation
56+
//! let out_data: &mut [u8; 4] = chunk[..4].as_mut_array().unwrap(); // since 1.93
5457
//! let out_data: &mut [u8; 4] = (&mut chunk[..4]).try_into().unwrap();
5558
//! let out_data = TryInto::<&mut [u8; 4]>::try_into(&mut chunk[..4]).unwrap();
5659
//!
@@ -62,10 +65,9 @@
6265
//! # *out_data = [1u8; 4];
6366
//! ```
6467
//!
65-
//! A better solution is to use [`as_mut_array`][as_mut_array] to sidestep this entirely.<br>
66-
//! As of the time of this writing (`rustc 1.91`), `as_mut_array` is unstable but in the process of being stabilized.
68+
//! The now-stable [`as_mut_array`][as_mut_array] sidesteps this issue entirely.<br>
6769
//!
68-
//! [as_mut_array]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array
70+
//! [as_mut_array]: https://doc.rust-lang.org/1.93.0/std/primitive.slice.html#method.as_mut_array
6971
#![forbid(missing_docs, non_ascii_idents)]
7072
#![cfg_attr(not(test), no_std)]
7173

0 commit comments

Comments
 (0)