use hermes_simd::{Scalar, SimdError, Vector};
#[test]
fn safe_unaligned_load_and_store_preserve_one_vector() {
let input = [1.0f32, -2.0, 3.5, 4.25];
let vector = Vector::<f32, Scalar>::load_unaligned_from_slice(&input).unwrap();
let mut out = [0.0f32; 4];
vector.store_unaligned_to_slice(&mut out).unwrap();
assert_eq!(out, input);
}
#[test]
fn safe_slice_load_rejects_short_input() {
let input = [1.0f32, 2.0, 3.0];
assert_eq!(
Vector::<f32, Scalar>::load_unaligned_from_slice(&input),
Err(SimdError::InsufficientInputLength)
);
assert_eq!(
Vector::<f32, Scalar>::load_aligned_from_slice(&input),
Err(SimdError::InsufficientInputLength)
);
}
#[test]
fn safe_slice_store_rejects_short_output() {
let vector = Vector::<f32, Scalar>::splat(1.0);
let mut out = [0.0f32; 3];
assert_eq!(
vector.store_unaligned_to_slice(&mut out),
Err(SimdError::InsufficientOutputLength)
);
assert_eq!(
vector.store_aligned_to_slice(&mut out),
Err(SimdError::InsufficientOutputLength)
);
}
#[test]
fn safe_aligned_load_and_store_preserve_one_vector() {
#[repr(align(64))]
struct Aligned([f32; 4]);
let input = Aligned([4.0f32, 3.0, 2.0, 1.0]);
let vector = Vector::<f32, Scalar>::load_aligned_from_slice(&input.0).unwrap();
let mut out = Aligned([0.0f32; 4]);
vector.store_aligned_to_slice(&mut out.0).unwrap();
assert_eq!(out.0, input.0);
}
#[test]
fn safe_aligned_load_and_store_reject_unaligned_slices() {
let input = [0.0f32; 8];
let unaligned_input = (0..=4)
.map(|offset| &input[offset..offset + 4])
.find(|slice| {
!(slice.as_ptr() as usize)
.is_multiple_of(<Scalar as hermes_simd::SimdKernel<f32>>::LANE_COUNT * 4)
})
.expect("at least one f32 subslice offset is unaligned to the scalar vector width");
assert_eq!(
Vector::<f32, Scalar>::load_aligned_from_slice(unaligned_input),
Err(SimdError::UnalignedAddress)
);
let vector = Vector::<f32, Scalar>::splat(2.0);
let mut output = [0.0f32; 8];
let unaligned_offset = (0..=4)
.find(|&offset| {
!(output[offset..].as_ptr() as usize)
.is_multiple_of(<Scalar as hermes_simd::SimdKernel<f32>>::LANE_COUNT * 4)
})
.expect("at least one f32 mutable subslice offset is unaligned to the scalar vector width");
assert_eq!(
vector.store_aligned_to_slice(&mut output[unaligned_offset..unaligned_offset + 4]),
Err(SimdError::UnalignedAddress)
);
assert_eq!(output, [0.0; 8]);
}
#[test]
fn test_masked_load_store_slice_scalar() {
use hermes_simd::{BitMask, Mask, Scalar, Vector};
let data = [42.0f32];
let mask_arr = [true];
let src = Vector::<f32, Scalar>::splat(0.0);
unsafe {
let bm = BitMask::<64>(BitMask::<1>::from_bools(&mask_arr).0);
let mask = Mask::<f32, Scalar>::from_bitmask(bm);
let vec = Vector::<f32, Scalar>::masked_load_from_slice(&data, mask, src).unwrap();
let mut out = [9.0f32];
vec.masked_store_to_slice(&mut out, mask).unwrap();
assert_eq!(out[0], 42.0);
}
let mask_arr_inactive = [false];
unsafe {
let bm = BitMask::<64>(BitMask::<1>::from_bools(&mask_arr_inactive).0);
let mask = Mask::<f32, Scalar>::from_bitmask(bm);
let vec = Vector::<f32, Scalar>::masked_load_from_slice(&data, mask, src).unwrap();
let mut out = [9.0f32];
vec.masked_store_to_slice(&mut out, mask).unwrap();
assert_eq!(out[0], 9.0);
let mut out2 = [9.0f32];
let active_bm = BitMask::<64>(BitMask::<1>::from_bools(&[true]).0);
let active_mask = Mask::<f32, Scalar>::from_bitmask(active_bm);
vec.masked_store_to_slice(&mut out2, active_mask).unwrap();
assert_eq!(out2[0], 0.0);
}
unsafe {
let bm = BitMask::<64>(BitMask::<1>::from_bools(&[true]).0);
let mask = Mask::<f32, Scalar>::from_bitmask(bm);
let res = Vector::<f32, Scalar>::masked_load_from_slice(&[], mask, src);
assert_eq!(res, Err(SimdError::IndexOutOfBounds));
let mut out: [f32; 0] = [];
let res_store = src.masked_store_to_slice(&mut out, mask);
assert_eq!(res_store, Err(SimdError::IndexOutOfBounds));
}
unsafe {
let bm = BitMask::<64>(BitMask::<1>::from_bools(&[false]).0);
let mask = Mask::<f32, Scalar>::from_bitmask(bm);
let _res = Vector::<f32, Scalar>::masked_load_from_slice(&[], mask, src).unwrap();
let mut out: [f32; 0] = [];
src.masked_store_to_slice(&mut out, mask).unwrap();
}
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "std"))]
#[test]
fn test_masked_load_store_slice_avx2() {
use hermes_simd::{target::TargetId, Avx2, BitMask, Mask, Vector};
if !TargetId::Avx2.is_supported() {
return;
}
let data = [1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let mask_arr = [true, false, true, false, true, false, true, false];
let src = Vector::<f32, Avx2>::splat(0.0);
unsafe {
let bm = BitMask::<64>(BitMask::<8>::from_bools(&mask_arr).0);
let mask = Mask::<f32, Avx2>::from_bitmask(bm);
let vec = Vector::<f32, Avx2>::masked_load_from_slice(&data, mask, src).unwrap();
let mut out = [9.0f32; 8];
vec.masked_store_to_slice(&mut out, mask).unwrap();
assert_eq!(out, [1.0, 9.0, 3.0, 9.0, 5.0, 9.0, 7.0, 9.0]);
}
let short_data = [10.0f32, 20.0, 30.0, 40.0, 50.0];
let mask_arr_short = [true, false, true, false, true, false, false, false];
unsafe {
let bm = BitMask::<64>(BitMask::<8>::from_bools(&mask_arr_short).0);
let mask = Mask::<f32, Avx2>::from_bitmask(bm);
let vec = Vector::<f32, Avx2>::masked_load_from_slice(&short_data, mask, src).unwrap();
let mut out = [99.0f32; 5];
vec.masked_store_to_slice(&mut out, mask).unwrap();
assert_eq!(out, [10.0, 99.0, 30.0, 99.0, 50.0]);
}
let mask_arr_oob = [true, false, true, false, true, false, true, false];
unsafe {
let bm = BitMask::<64>(BitMask::<8>::from_bools(&mask_arr_oob).0);
let mask = Mask::<f32, Avx2>::from_bitmask(bm);
let res = Vector::<f32, Avx2>::masked_load_from_slice(&short_data, mask, src);
assert_eq!(res, Err(SimdError::IndexOutOfBounds));
let mut out = [99.0f32; 5];
let res_store = src.masked_store_to_slice(&mut out, mask);
assert_eq!(res_store, Err(SimdError::IndexOutOfBounds));
}
}
#[cfg(all(target_arch = "aarch64", feature = "std"))]
#[test]
fn test_masked_load_store_slice_neon() {
use hermes_simd::{target::TargetId, BitMask, Mask, Neon, Vector};
if !TargetId::Neon.is_supported() {
return;
}
let data = [1.0f32, 2.0, 3.0, 4.0];
let mask_arr = [true, false, true, false];
let src = Vector::<f32, Neon>::splat(0.0);
unsafe {
let bm = BitMask::<64>(BitMask::<4>::from_bools(&mask_arr).0);
let mask = Mask::<f32, Neon>::from_bitmask(bm);
let vec = Vector::<f32, Neon>::masked_load_from_slice(&data, mask, src).unwrap();
let mut out = [9.0f32; 4];
vec.masked_store_to_slice(&mut out, mask).unwrap();
assert_eq!(out, [1.0, 9.0, 3.0, 9.0]);
}
let short_data = [10.0f32, 20.0, 30.0];
let mask_arr_short = [true, false, true, false];
unsafe {
let bm = BitMask::<64>(BitMask::<4>::from_bools(&mask_arr_short).0);
let mask = Mask::<f32, Neon>::from_bitmask(bm);
let vec = Vector::<f32, Neon>::masked_load_from_slice(&short_data, mask, src).unwrap();
let mut out = [99.0f32; 3];
vec.masked_store_to_slice(&mut out, mask).unwrap();
assert_eq!(out, [10.0, 99.0, 30.0]);
}
let mask_arr_oob = [true, false, true, true];
unsafe {
let bm = BitMask::<64>(BitMask::<4>::from_bools(&mask_arr_oob).0);
let mask = Mask::<f32, Neon>::from_bitmask(bm);
let res = Vector::<f32, Neon>::masked_load_from_slice(&short_data, mask, src);
assert_eq!(res, Err(SimdError::IndexOutOfBounds));
let mut out = [99.0f32; 3];
let res_store = src.masked_store_to_slice(&mut out, mask);
assert_eq!(res_store, Err(SimdError::IndexOutOfBounds));
}
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "std"))]
#[test]
fn safe_avx512_vector_constructors_reject_unsupported_target() {
use hermes_simd::{target::TargetId, Avx512, Vector};
if TargetId::Avx512.is_supported() {
return;
}
let data = [1.0f32; 16];
assert_eq!(
Vector::<f32, Avx512>::try_zero(),
Err(SimdError::UnsupportedTarget)
);
assert_eq!(
Vector::<f32, Avx512>::try_splat(1.0),
Err(SimdError::UnsupportedTarget)
);
assert_eq!(
Vector::<f32, Avx512>::try_from_array(data),
Err(SimdError::UnsupportedTarget)
);
assert_eq!(
Vector::<f32, Avx512>::load_unaligned_from_slice(&data),
Err(SimdError::UnsupportedTarget)
);
let panic = std::panic::catch_unwind(|| Vector::<f32, Avx512>::splat(1.0));
assert!(panic.is_err());
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "std"))]
#[test]
fn test_masked_load_store_slice_avx512() {
use hermes_simd::{target::TargetId, Avx512, BitMask, Mask, Vector};
if !TargetId::Avx512.is_supported() {
return;
}
let mut data = [0.0f32; 16];
for (i, d) in data.iter_mut().enumerate() {
*d = (i + 1) as f32;
}
let mut mask_arr = [false; 16];
for (i, m) in mask_arr.iter_mut().enumerate() {
if i % 2 == 0 {
*m = true;
}
}
let src = Vector::<f32, Avx512>::splat(0.0);
unsafe {
let bm = BitMask::<64>(BitMask::<16>::from_bools(&mask_arr).0);
let mask = Mask::<f32, Avx512>::from_bitmask(bm);
let vec = Vector::<f32, Avx512>::masked_load_from_slice(&data, mask, src).unwrap();
let mut out = [99.0f32; 16];
vec.masked_store_to_slice(&mut out, mask).unwrap();
for (i, &o) in out.iter().enumerate() {
if i % 2 == 0 {
assert_eq!(o, (i + 1) as f32);
} else {
assert_eq!(o, 99.0);
}
}
}
}
#[test]
fn test_widen_i8_simd_and_tails() {
use hermes_simd::{
widen_I8_to_I16, widen_I8_to_I32, widen_i8_to_i16, widen_i8_to_i32, I16, I32, I8,
};
for len in 0..100 {
let mut src = vec![0i8; len];
for (j, s) in src.iter_mut().enumerate() {
*s = (j as i8).wrapping_mul(31).wrapping_add(7);
}
let mut dest_i16 = vec![0i16; len];
let mut dest_i32 = vec![0i32; len];
widen_i8_to_i16(&src, &mut dest_i16);
widen_i8_to_i32(&src, &mut dest_i32);
let expected_i16: Vec<i16> = src.iter().map(|&x| x as i16).collect();
let expected_i32: Vec<i32> = src.iter().map(|&x| x as i32).collect();
assert_eq!(dest_i16, expected_i16, "Failed for length i16: {}", len);
assert_eq!(dest_i32, expected_i32, "Failed for length i32: {}", len);
let src_wrapped: Vec<I8> = src.iter().map(|&x| I8(x)).collect();
let mut dest_i16_wrapped = vec![I16(0); len];
let mut dest_i32_wrapped = vec![I32(0); len];
widen_I8_to_I16(&src_wrapped, &mut dest_i16_wrapped);
widen_I8_to_I32(&src_wrapped, &mut dest_i32_wrapped);
let expected_i16_wrapped: Vec<I16> = src.iter().map(|&x| I16(x as i16)).collect();
let expected_i32_wrapped: Vec<I32> = src.iter().map(|&x| I32(x as i32)).collect();
assert_eq!(
dest_i16_wrapped, expected_i16_wrapped,
"Failed for wrapped i16: {}",
len
);
assert_eq!(
dest_i32_wrapped, expected_i32_wrapped,
"Failed for wrapped i32: {}",
len
);
}
}