Safe_unaligned_simd - Safe wrappers for SIMD load/store intrinsics

⚓ Rust    📅 2025-10-02    👤 surdeus    👁️ 7      

surdeus

safe_unaligned_simd is a library crate that provides safe wrappers over std::arch SIMD intrinsics for loading and storing data in memory.

With this library, users can write entirely safe functions that access memory with SIMD platform intrinsics, where unsafe is only needed to call into the target-feature annotated function. This crate does not provide wrappers over gather/scatter intrinsics or masked load/store past the end of an array.

I wanted to make a post after finally adding support for the recently stabilized AVX-512 intrinsics and releasing the latest version, 0.2.3. Supporting the new intrinsics was a large task which added 192 functions and was the last major milestone I had planned.

Implemented target features (and platform docs):

Implementation

std::arch intrinsics take raw pointer arguments, but many of them can be wrapped in functions that uphold the safety requirements by taking array references as the input arguments. These are the unaligned intrinsics that this library is named after.

aarch64 has more strongly-typed SIMD types, so aarch64 wrappers only take concrete types.

On wasm and x86, marker traits are used to designate the size of an argument type which ensure that the safety invariants are satisfied. The following code shows an intrinsic that stores 32 bits into memory:

// std::arch function signature takes a raw byte pointer
pub unsafe fn _mm_storeu_si32(mem_addr: *mut u8, a: __m128i);
// Safe wrapper requires `mem_addr` to be 32 bits
pub fn _mm_storeu_si32<T: Is32BitsUnaligned>(mem_addr: &mut T, a: __m128i) {
    unsafe { arch::_mm_storeu_si32(ptr::from_mut(mem_addr).cast(), a) }
}

The types implementing Is32BitsUnaligned are primitives and primitive arrays matching that size in memory. Traits are implemented for power-of-two sizes from 8 bits through 512 bits.

Motivation

After target_feature_11 stabilization, SIMD intrinsics were marked safe unless they took pointer arguments. Unfortunately, there's no safe wrapper provided by the standard library to get data in and out of SIMD registers until portable_simd is stabilized. Even after stabilization, portable_simd won't cover all the available intrinsic loads/stores, so I think this crate can remain useful into the future.

I started work on this after an unaccepted libs ACP to add safe conversions between arrays and platform SIMD types. I would like to see this functionality upstreamed to the standard library eventually, but it's in an awkward place of being platform-specific yet too "high-level" for std::arch according to the feedback received in the ACP. That was before this library existed, so now, at least a proof-of-concept exists for what an interface could look like.

1 post - 1 participant

Read full topic

🏷️ Rust_feed