#![allow(clippy::missing_safety_doc)]
pub(crate) trait F32x: Copy {
const LANES: usize;
unsafe fn splat(value: f32) -> Self;
unsafe fn load(src: &[f32]) -> Self;
unsafe fn store(self, dst: &mut [f32]);
unsafe fn add(self, other: Self) -> Self;
unsafe fn sub(self, other: Self) -> Self;
unsafe fn mul(self, other: Self) -> Self;
unsafe fn neg(self) -> Self;
unsafe fn gt(self, other: Self) -> Self;
unsafe fn lt(self, other: Self) -> Self;
unsafe fn select(mask: Self, a: Self, b: Self) -> Self;
#[inline(always)]
unsafe fn raise_to(self, low: Self) -> Self {
unsafe { Self::select(self.lt(low), low, self) }
}
#[inline(always)]
unsafe fn lower_to(self, high: Self) -> Self {
unsafe { Self::select(self.gt(high), high, self) }
}
unsafe fn reverse(self) -> Self;
unsafe fn deinterleave(self, next: Self) -> (Self, Self);
unsafe fn interleave(self, odd: Self) -> (Self, Self);
}
#[cfg(target_arch = "x86_64")]
mod x86 {
use super::F32x;
use core::arch::x86_64::*;
#[derive(Clone, Copy)]
pub(crate) struct Sse4(__m128);
impl F32x for Sse4 {
const LANES: usize = 4;
#[inline(always)]
unsafe fn splat(value: f32) -> Self {
unsafe { Self(_mm_set1_ps(value)) }
}
#[inline(always)]
unsafe fn load(src: &[f32]) -> Self {
debug_assert!(src.len() >= Self::LANES);
unsafe { Self(_mm_loadu_ps(src.as_ptr())) }
}
#[inline(always)]
unsafe fn store(self, dst: &mut [f32]) {
debug_assert!(dst.len() >= Self::LANES);
unsafe { _mm_storeu_ps(dst.as_mut_ptr(), self.0) }
}
#[inline(always)]
unsafe fn add(self, other: Self) -> Self {
unsafe { Self(_mm_add_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn sub(self, other: Self) -> Self {
unsafe { Self(_mm_sub_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn mul(self, other: Self) -> Self {
unsafe { Self(_mm_mul_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn neg(self) -> Self {
unsafe { Self(_mm_xor_ps(self.0, _mm_set1_ps(-0.0))) }
}
#[inline(always)]
unsafe fn gt(self, other: Self) -> Self {
unsafe { Self(_mm_cmpgt_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn lt(self, other: Self) -> Self {
unsafe { Self(_mm_cmplt_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn select(mask: Self, a: Self, b: Self) -> Self {
unsafe { Self(_mm_blendv_ps(b.0, a.0, mask.0)) }
}
#[inline(always)]
unsafe fn raise_to(self, low: Self) -> Self {
unsafe { Self(_mm_max_ps(low.0, self.0)) }
}
#[inline(always)]
unsafe fn lower_to(self, high: Self) -> Self {
unsafe { Self(_mm_min_ps(high.0, self.0)) }
}
#[inline(always)]
unsafe fn reverse(self) -> Self {
unsafe { Self(_mm_shuffle_ps::<0b00_01_10_11>(self.0, self.0)) }
}
#[inline(always)]
unsafe fn deinterleave(self, next: Self) -> (Self, Self) {
unsafe {
(
Self(_mm_shuffle_ps::<0b10_00_10_00>(self.0, next.0)),
Self(_mm_shuffle_ps::<0b11_01_11_01>(self.0, next.0)),
)
}
}
#[inline(always)]
unsafe fn interleave(self, odd: Self) -> (Self, Self) {
unsafe {
(
Self(_mm_unpacklo_ps(self.0, odd.0)),
Self(_mm_unpackhi_ps(self.0, odd.0)),
)
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct Avx2(__m256);
impl F32x for Avx2 {
const LANES: usize = 8;
#[inline(always)]
unsafe fn splat(value: f32) -> Self {
unsafe { Self(_mm256_set1_ps(value)) }
}
#[inline(always)]
unsafe fn load(src: &[f32]) -> Self {
debug_assert!(src.len() >= Self::LANES);
unsafe { Self(_mm256_loadu_ps(src.as_ptr())) }
}
#[inline(always)]
unsafe fn store(self, dst: &mut [f32]) {
debug_assert!(dst.len() >= Self::LANES);
unsafe { _mm256_storeu_ps(dst.as_mut_ptr(), self.0) }
}
#[inline(always)]
unsafe fn add(self, other: Self) -> Self {
unsafe { Self(_mm256_add_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn sub(self, other: Self) -> Self {
unsafe { Self(_mm256_sub_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn mul(self, other: Self) -> Self {
unsafe { Self(_mm256_mul_ps(self.0, other.0)) }
}
#[inline(always)]
unsafe fn neg(self) -> Self {
unsafe { Self(_mm256_xor_ps(self.0, _mm256_set1_ps(-0.0))) }
}
#[inline(always)]
unsafe fn gt(self, other: Self) -> Self {
unsafe { Self(_mm256_cmp_ps::<_CMP_GT_OQ>(self.0, other.0)) }
}
#[inline(always)]
unsafe fn lt(self, other: Self) -> Self {
unsafe { Self(_mm256_cmp_ps::<_CMP_LT_OQ>(self.0, other.0)) }
}
#[inline(always)]
unsafe fn select(mask: Self, a: Self, b: Self) -> Self {
unsafe { Self(_mm256_blendv_ps(b.0, a.0, mask.0)) }
}
#[inline(always)]
unsafe fn raise_to(self, low: Self) -> Self {
unsafe { Self(_mm256_max_ps(low.0, self.0)) }
}
#[inline(always)]
unsafe fn lower_to(self, high: Self) -> Self {
unsafe { Self(_mm256_min_ps(high.0, self.0)) }
}
#[inline(always)]
unsafe fn reverse(self) -> Self {
unsafe {
Self(_mm256_permutevar8x32_ps(
self.0,
_mm256_setr_epi32(7, 6, 5, 4, 3, 2, 1, 0),
))
}
}
#[inline(always)]
unsafe fn deinterleave(self, next: Self) -> (Self, Self) {
unsafe {
let even = _mm256_shuffle_ps::<0b10_00_10_00>(self.0, next.0);
let odd = _mm256_shuffle_ps::<0b11_01_11_01>(self.0, next.0);
(
Self(_mm256_castpd_ps(_mm256_permute4x64_pd::<0b11_01_10_00>(
_mm256_castps_pd(even),
))),
Self(_mm256_castpd_ps(_mm256_permute4x64_pd::<0b11_01_10_00>(
_mm256_castps_pd(odd),
))),
)
}
}
#[inline(always)]
unsafe fn interleave(self, odd: Self) -> (Self, Self) {
unsafe {
let low = _mm256_unpacklo_ps(self.0, odd.0);
let high = _mm256_unpackhi_ps(self.0, odd.0);
(
Self(_mm256_permute2f128_ps::<0x20>(low, high)),
Self(_mm256_permute2f128_ps::<0x31>(low, high)),
)
}
}
}
}
#[cfg(target_arch = "x86_64")]
pub(crate) use x86::{Avx2, Sse4};
#[cfg(target_arch = "aarch64")]
mod arm {
use super::F32x;
use core::arch::aarch64::*;
#[derive(Clone, Copy)]
pub(crate) struct Neon(float32x4_t);
impl F32x for Neon {
const LANES: usize = 4;
#[inline(always)]
unsafe fn splat(value: f32) -> Self {
unsafe { Self(vdupq_n_f32(value)) }
}
#[inline(always)]
unsafe fn load(src: &[f32]) -> Self {
debug_assert!(src.len() >= Self::LANES);
unsafe { Self(vld1q_f32(src.as_ptr())) }
}
#[inline(always)]
unsafe fn store(self, dst: &mut [f32]) {
debug_assert!(dst.len() >= Self::LANES);
unsafe { vst1q_f32(dst.as_mut_ptr(), self.0) }
}
#[inline(always)]
unsafe fn add(self, other: Self) -> Self {
unsafe { Self(vaddq_f32(self.0, other.0)) }
}
#[inline(always)]
unsafe fn sub(self, other: Self) -> Self {
unsafe { Self(vsubq_f32(self.0, other.0)) }
}
#[inline(always)]
unsafe fn mul(self, other: Self) -> Self {
unsafe { Self(vmulq_f32(self.0, other.0)) }
}
#[inline(always)]
unsafe fn neg(self) -> Self {
unsafe { Self(vnegq_f32(self.0)) }
}
#[inline(always)]
unsafe fn gt(self, other: Self) -> Self {
unsafe { Self(vreinterpretq_f32_u32(vcgtq_f32(self.0, other.0))) }
}
#[inline(always)]
unsafe fn lt(self, other: Self) -> Self {
unsafe { Self(vreinterpretq_f32_u32(vcltq_f32(self.0, other.0))) }
}
#[inline(always)]
unsafe fn select(mask: Self, a: Self, b: Self) -> Self {
unsafe { Self(vbslq_f32(vreinterpretq_u32_f32(mask.0), a.0, b.0)) }
}
#[inline(always)]
unsafe fn reverse(self) -> Self {
unsafe {
let pairs = vrev64q_f32(self.0);
Self(vextq_f32::<2>(pairs, pairs))
}
}
#[inline(always)]
unsafe fn deinterleave(self, next: Self) -> (Self, Self) {
unsafe {
(
Self(vuzp1q_f32(self.0, next.0)),
Self(vuzp2q_f32(self.0, next.0)),
)
}
}
#[inline(always)]
unsafe fn interleave(self, odd: Self) -> (Self, Self) {
unsafe {
(
Self(vzip1q_f32(self.0, odd.0)),
Self(vzip2q_f32(self.0, odd.0)),
)
}
}
}
}
#[cfg(target_arch = "aarch64")]
pub(crate) use arm::Neon;