Layout of packed SIMD vectors

Disclaimer: This chapter represents the consensus from issue #38. The statements in here are not (yet) "guaranteed" not to change until an RFC ratifies them.

Rust currently exposes packed1 SIMD vector types like __m128 to users, but it does not expose a way for users to construct their own vector types.

The set of currently-exposed packed SIMD vector types is implementation-defined and it is currently different for each architecture.

1

packed denotes that these SIMD vectors have a compile-time fixed size, distinguishing these from SIMD vector types whose size is only known at run-time. Rust currently only supports packed SIMD vector types. This is elaborated further in RFC2366.

Packed SIMD vector types

Packed SIMD vector types are repr(simd) homogeneous tuple-structs containing N elements of type T where N is a power-of-two and the size and alignment requirements of T are equal:

#[repr(simd)]
struct Vector<T, N>(T_0, ..., T_(N - 1));

The set of supported values of T and N is implementation-defined.

The size of Vector is N * size_of::<T>() and its alignment is an implementation-defined function of T and N greater than or equal to align_of::<T>(). That is:

assert_eq!(size_of::<Vector<T, N>>(), size_of::<T>() * N);
assert!(align_of::<Vector<T, N>>() >= align_of::<T>());

That is, two distinct repr(simd) vector types that have the same T and the same N have the same size and alignment.

Vector elements are laid out in source field order, enabling random access to vector elements by reinterpreting the vector as an array:

union U {
   vec: Vector<T, N>,
   arr: [T; N]
}

assert_eq!(size_of::<Vector<T, N>>(), size_of::<[T; N]>());
assert!(align_of::<Vector<T, N>>() >= align_of::<[T; N]>());

unsafe {
  let u = U { vec: Vector<T, N>(t_0, ..., t_(N - 1)) };

  assert_eq!(u.vec.0, u.arr[0]);
  // ... 
  assert_eq!(u.vec.(N - 1), u.arr[N - 1]);
}

Unresolved questions

  • Blocked: Should the layout of packed SIMD vectors be the same as that of homogeneous tuples ? Such that:

    union U {
      vec: Vector<T, N>,
      tup: (T_0, ..., T_(N-1)),
    }
    
    assert_eq!(size_of::<Vector<T, N>>(), size_of::<(T_0, ..., T_(N-1))>());
    assert!(align_of::<Vector<T, N>>() >= align_of::<(T_0, ..., T_(N-1))>());
    
    unsafe {
      let u = U { vec: Vector(t_0, ..., t_(N - 1)) };
    
      assert_eq!(u.vec.0, u.tup.0);
      // ... 
      assert_eq!(u.vec.(N - 1), u.tup.(N - 1));
    }

    This is blocked on the resolution of issue #36 about the layout of homogeneous structs and tuples.

  • MaybeUninit<T> does not have the same repr as T, so MaybeUninit<Vector<T, N>> are not repr(simd), which has performance consequences and means that MaybeUninit<Vector<T, N>> is not C-FFI safe.