use std::arch::x86_64::*;
pub fn has_avx512() -> bool {
is_x86_feature_detected!("avx512f")
&& is_x86_feature_detected!("avx512vl")
&& is_x86_feature_detected!("avx512bw")
}
pub fn has_avx2_fma() -> bool {
is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma")
}
pub(super) const ALPHA_LANES_512: __mmask16 = 0b1000_1000_1000_1000;
#[target_feature(enable = "sse")]
#[inline]
pub(super) unsafe fn transpose4_ps(
mut a: __m128,
mut b: __m128,
mut c: __m128,
mut d: __m128,
) -> (__m128, __m128, __m128, __m128) {
_MM_TRANSPOSE4_PS(&mut a, &mut b, &mut c, &mut d);
(a, b, c, d)
}
#[target_feature(enable = "sse")]
#[inline]
pub(super) unsafe fn load_planar4_ps(src: *const f32) -> (__m128, __m128, __m128, __m128) {
unsafe {
transpose4_ps(
_mm_loadu_ps(src),
_mm_loadu_ps(src.add(4)),
_mm_loadu_ps(src.add(8)),
_mm_loadu_ps(src.add(12)),
)
}
}
#[target_feature(enable = "sse2")]
#[inline]
pub(super) unsafe fn load_planar4_epi32(src: *const u32) -> (__m128i, __m128i, __m128i, __m128i) {
let (a, b, c, d) = unsafe {
transpose4_ps(
_mm_castsi128_ps(_mm_loadu_si128(src.cast())),
_mm_castsi128_ps(_mm_loadu_si128(src.add(4).cast())),
_mm_castsi128_ps(_mm_loadu_si128(src.add(8).cast())),
_mm_castsi128_ps(_mm_loadu_si128(src.add(12).cast())),
)
};
(
_mm_castps_si128(a),
_mm_castps_si128(b),
_mm_castps_si128(c),
_mm_castps_si128(d),
)
}
#[target_feature(enable = "sse")]
#[inline]
pub(super) unsafe fn store_interleaved_ps(
r: __m128,
g: __m128,
b: __m128,
a: __m128,
dst: *mut f32,
count: usize,
) {
debug_assert!(count <= 4);
let (p0, p1, p2, p3) = unsafe { transpose4_ps(r, g, b, a) };
if count > 0 {
unsafe { _mm_storeu_ps(dst, p0) };
}
if count > 1 {
unsafe { _mm_storeu_ps(dst.add(4), p1) };
}
if count > 2 {
unsafe { _mm_storeu_ps(dst.add(8), p2) };
}
if count > 3 {
unsafe { _mm_storeu_ps(dst.add(12), p3) };
}
}
#[target_feature(enable = "sse")]
#[inline]
pub(super) unsafe fn store_interleaved4_ps(
r: __m128,
g: __m128,
b: __m128,
a: __m128,
dst: *mut f32,
) {
unsafe { store_interleaved_ps(r, g, b, a, dst, 4) }
}
#[target_feature(enable = "sse2")]
#[inline]
pub(super) unsafe fn store_interleaved4_epi32(
r: __m128i,
g: __m128i,
b: __m128i,
a: __m128i,
dst: *mut u32,
) {
let (p0, p1, p2, p3) = unsafe {
transpose4_ps(
_mm_castsi128_ps(r),
_mm_castsi128_ps(g),
_mm_castsi128_ps(b),
_mm_castsi128_ps(a),
)
};
unsafe {
_mm_storeu_si128(dst.cast(), _mm_castps_si128(p0));
_mm_storeu_si128(dst.add(4).cast(), _mm_castps_si128(p1));
_mm_storeu_si128(dst.add(8).cast(), _mm_castps_si128(p2));
_mm_storeu_si128(dst.add(12).cast(), _mm_castps_si128(p3));
}
}
#[target_feature(enable = "avx")]
#[inline]
pub(super) fn combine2_ps(low: __m128, high: __m128) -> __m256 {
_mm256_set_m128(high, low)
}
#[target_feature(enable = "avx2")]
#[inline]
pub(super) fn combine2_epi32(low: __m128i, high: __m128i) -> __m256i {
_mm256_set_m128i(high, low)
}
#[target_feature(enable = "avx")]
#[inline]
pub(super) unsafe fn load_planar8_ps(src: *const f32) -> (__m256, __m256, __m256, __m256) {
let (r0, g0, b0, a0) = unsafe { load_planar4_ps(src) };
let (r1, g1, b1, a1) = unsafe { load_planar4_ps(src.add(16)) };
(
combine2_ps(r0, r1),
combine2_ps(g0, g1),
combine2_ps(b0, b1),
combine2_ps(a0, a1),
)
}
#[target_feature(enable = "avx2")]
#[inline]
pub(super) unsafe fn load_planar8_epi32(src: *const u32) -> (__m256i, __m256i, __m256i, __m256i) {
let (r0, g0, b0, a0) = unsafe { load_planar4_epi32(src) };
let (r1, g1, b1, a1) = unsafe { load_planar4_epi32(src.add(16)) };
(
combine2_epi32(r0, r1),
combine2_epi32(g0, g1),
combine2_epi32(b0, b1),
combine2_epi32(a0, a1),
)
}
#[inline]
fn interleaved16_masks(pixels: usize) -> [__mmask16; 4] {
debug_assert!(pixels <= 16);
let lanes = pixels as u32 * 4;
let live = 1u64.checked_shl(lanes).map_or(u64::MAX, |bit| bit - 1);
std::array::from_fn(|k| (live >> (16 * k)) as __mmask16)
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) fn interleave16_epi32(
r: __m512i,
g: __m512i,
b: __m512i,
a: __m512i,
) -> (__m512i, __m512i, __m512i, __m512i) {
let lo = _mm512_setr_epi32(0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23);
let hi = _mm512_setr_epi32(8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31);
let rg_lo = _mm512_permutex2var_epi32(r, lo, g);
let rg_hi = _mm512_permutex2var_epi32(r, hi, g);
let ba_lo = _mm512_permutex2var_epi32(b, lo, a);
let ba_hi = _mm512_permutex2var_epi32(b, hi, a);
let q_lo = _mm512_setr_epi64(0, 8, 1, 9, 2, 10, 3, 11);
let q_hi = _mm512_setr_epi64(4, 12, 5, 13, 6, 14, 7, 15);
(
_mm512_permutex2var_epi64(rg_lo, q_lo, ba_lo),
_mm512_permutex2var_epi64(rg_lo, q_hi, ba_lo),
_mm512_permutex2var_epi64(rg_hi, q_lo, ba_hi),
_mm512_permutex2var_epi64(rg_hi, q_hi, ba_hi),
)
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) fn deinterleave16_epi32(
p0: __m512i,
p1: __m512i,
p2: __m512i,
p3: __m512i,
) -> (__m512i, __m512i, __m512i, __m512i) {
let q_even = _mm512_setr_epi64(0, 2, 4, 6, 8, 10, 12, 14);
let q_odd = _mm512_setr_epi64(1, 3, 5, 7, 9, 11, 13, 15);
let rg_lo = _mm512_permutex2var_epi64(p0, q_even, p1);
let ba_lo = _mm512_permutex2var_epi64(p0, q_odd, p1);
let rg_hi = _mm512_permutex2var_epi64(p2, q_even, p3);
let ba_hi = _mm512_permutex2var_epi64(p2, q_odd, p3);
let even = _mm512_setr_epi32(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30);
let odd = _mm512_setr_epi32(1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31);
(
_mm512_permutex2var_epi32(rg_lo, even, rg_hi),
_mm512_permutex2var_epi32(rg_lo, odd, rg_hi),
_mm512_permutex2var_epi32(ba_lo, even, ba_hi),
_mm512_permutex2var_epi32(ba_lo, odd, ba_hi),
)
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn store_interleaved16_ps(
r: __m512,
g: __m512,
b: __m512,
a: __m512,
dst: *mut f32,
) {
let (p0, p1, p2, p3) = interleave16_epi32(
_mm512_castps_si512(r),
_mm512_castps_si512(g),
_mm512_castps_si512(b),
_mm512_castps_si512(a),
);
unsafe {
_mm512_storeu_si512(dst.cast(), p0);
_mm512_storeu_si512(dst.add(16).cast(), p1);
_mm512_storeu_si512(dst.add(32).cast(), p2);
_mm512_storeu_si512(dst.add(48).cast(), p3);
}
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn store_interleaved16_mask_ps(
r: __m512,
g: __m512,
b: __m512,
a: __m512,
dst: *mut f32,
pixels: usize,
) {
let (p0, p1, p2, p3) = interleave16_epi32(
_mm512_castps_si512(r),
_mm512_castps_si512(g),
_mm512_castps_si512(b),
_mm512_castps_si512(a),
);
let [m0, m1, m2, m3] = interleaved16_masks(pixels);
unsafe {
_mm512_mask_storeu_epi32(dst.cast(), m0, p0);
_mm512_mask_storeu_epi32(dst.add(16).cast(), m1, p1);
_mm512_mask_storeu_epi32(dst.add(32).cast(), m2, p2);
_mm512_mask_storeu_epi32(dst.add(48).cast(), m3, p3);
}
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn store_interleaved16_epi32(
r: __m512i,
g: __m512i,
b: __m512i,
a: __m512i,
dst: *mut u32,
) {
let (p0, p1, p2, p3) = interleave16_epi32(r, g, b, a);
unsafe {
_mm512_storeu_si512(dst.cast(), p0);
_mm512_storeu_si512(dst.add(16).cast(), p1);
_mm512_storeu_si512(dst.add(32).cast(), p2);
_mm512_storeu_si512(dst.add(48).cast(), p3);
}
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn store_interleaved16_mask_epi32(
r: __m512i,
g: __m512i,
b: __m512i,
a: __m512i,
dst: *mut u32,
pixels: usize,
) {
let (p0, p1, p2, p3) = interleave16_epi32(r, g, b, a);
let [m0, m1, m2, m3] = interleaved16_masks(pixels);
unsafe {
_mm512_mask_storeu_epi32(dst.cast(), m0, p0);
_mm512_mask_storeu_epi32(dst.add(16).cast(), m1, p1);
_mm512_mask_storeu_epi32(dst.add(32).cast(), m2, p2);
_mm512_mask_storeu_epi32(dst.add(48).cast(), m3, p3);
}
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn load_planar16_ps(src: *const f32) -> (__m512, __m512, __m512, __m512) {
let (r, g, b, a) = unsafe {
deinterleave16_epi32(
_mm512_loadu_si512(src.cast()),
_mm512_loadu_si512(src.add(16).cast()),
_mm512_loadu_si512(src.add(32).cast()),
_mm512_loadu_si512(src.add(48).cast()),
)
};
(
_mm512_castsi512_ps(r),
_mm512_castsi512_ps(g),
_mm512_castsi512_ps(b),
_mm512_castsi512_ps(a),
)
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn load_planar16_mask_ps(
src: *const f32,
pixels: usize,
) -> (__m512, __m512, __m512, __m512) {
let [m0, m1, m2, m3] = interleaved16_masks(pixels);
let (r, g, b, a) = unsafe {
deinterleave16_epi32(
_mm512_maskz_loadu_epi32(m0, src.cast()),
_mm512_maskz_loadu_epi32(m1, src.add(16).cast()),
_mm512_maskz_loadu_epi32(m2, src.add(32).cast()),
_mm512_maskz_loadu_epi32(m3, src.add(48).cast()),
)
};
(
_mm512_castsi512_ps(r),
_mm512_castsi512_ps(g),
_mm512_castsi512_ps(b),
_mm512_castsi512_ps(a),
)
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn load_planar16_epi32(src: *const u32) -> (__m512i, __m512i, __m512i, __m512i) {
unsafe {
deinterleave16_epi32(
_mm512_loadu_si512(src.cast()),
_mm512_loadu_si512(src.add(16).cast()),
_mm512_loadu_si512(src.add(32).cast()),
_mm512_loadu_si512(src.add(48).cast()),
)
}
}
#[target_feature(enable = "avx512f")]
#[inline]
pub(super) unsafe fn load_planar16_mask_epi32(
src: *const u32,
pixels: usize,
) -> (__m512i, __m512i, __m512i, __m512i) {
let [m0, m1, m2, m3] = interleaved16_masks(pixels);
unsafe {
deinterleave16_epi32(
_mm512_maskz_loadu_epi32(m0, src.cast()),
_mm512_maskz_loadu_epi32(m1, src.add(16).cast()),
_mm512_maskz_loadu_epi32(m2, src.add(32).cast()),
_mm512_maskz_loadu_epi32(m3, src.add(48).cast()),
)
}
}