use crate::f8;
use core::arch::{asm, x86_64::_mm256_set1_epi32};
#[cfg(target_feature = "avx2")]
#[inline]
pub(crate) fn available() -> bool {
true
}
#[cfg(not(target_feature = "avx2"))]
pub(crate) fn available() -> bool {
use core::{
arch::x86_64::{__cpuid, __cpuid_count, _xgetbv},
sync::atomic::{AtomicU8, Ordering},
};
static SUPPORT: AtomicU8 = AtomicU8::new(0);
let cached = SUPPORT.load(Ordering::Relaxed);
if cached != 0 {
return cached == 2;
}
let supported = unsafe {
__cpuid(0).eax >= 7
&& __cpuid(1).ecx & 0x1c00_0000 == 0x1c00_0000
&& _xgetbv(0) & 6 == 6
&& __cpuid_count(7, 0).ebx & (1 << 5) != 0
};
SUPPORT.store(if supported { 2 } else { 1 }, Ordering::Relaxed);
supported
}
#[target_feature(enable = "avx2")]
pub(crate) unsafe fn encode(src: &[f32], dst: &mut [f8]) {
unsafe {
let one = _mm256_set1_epi32(1);
let min = _mm256_set1_epi32(0x3b00_0000);
let max = _mm256_set1_epi32(0x3f80_0000);
let infinity = _mm256_set1_epi32(0x7f80_0000);
let mantissa = _mm256_set1_epi32(0x007f_ffff);
let hidden = _mm256_set1_epi32(0x0080_0000);
let bias = _mm256_set1_epi32(149);
let end = src.len() / 8 * 8;
let mut offset = 0;
while offset < end {
asm!(
"vmovdqu {value}, [{src}]",
"vpsrad {invalid}, {value}, 31",
"vpcmpgtd {temp}, {value}, {infinity}",
"vpor {invalid}, {invalid}, {temp}",
"vpandn {value}, {invalid}, {value}",
"vpminud {value}, {value}, {max}",
"vpmaxud {value}, {value}, {min}",
"vpsrld {shift}, {value}, 23",
"vpsubd {shift}, {bias}, {shift}",
"vpand {value}, {value}, {mantissa}",
"vpor {value}, {value}, {hidden}",
"vpslld {temp}, {value}, 8",
"vpsubd {value}, {temp}, {value}",
"vpsrlvd {guard}, {value}, {shift}",
"vpsllvd {temp}, {one}, {shift}",
"vpsubd {temp}, {temp}, {one}",
"vpand {value}, {value}, {temp}",
"vpxor {temp}, {temp}, {temp}",
"vpcmpeqd {value}, {value}, {temp}",
"vpandn {value}, {value}, {one}",
"vpsrld {invalid}, {guard}, 1",
"vpor {value}, {value}, {invalid}",
"vpand {value}, {value}, {guard}",
"vpand {value}, {value}, {one}",
"vpaddd {value}, {invalid}, {value}",
"vextracti128 {temp:x}, {value}, 1",
"vpackusdw {value:x}, {value:x}, {temp:x}",
"vpackuswb {value:x}, {value:x}, {value:x}",
"vmovq [{dst}], {value:x}",
src = in(reg) src.as_ptr().add(offset),
dst = in(reg) dst.as_mut_ptr().add(offset),
one = in(ymm_reg) one,
min = in(ymm_reg) min,
max = in(ymm_reg) max,
infinity = in(ymm_reg) infinity,
mantissa = in(ymm_reg) mantissa,
hidden = in(ymm_reg) hidden,
bias = in(ymm_reg) bias,
value = out(ymm_reg) _,
invalid = out(ymm_reg) _,
temp = out(ymm_reg) _,
shift = out(ymm_reg) _,
guard = out(ymm_reg) _,
options(nostack, preserves_flags),
);
offset += 8;
}
for (&value, out) in src[end..].iter().zip(&mut dst[end..]) {
*out = f8::from_f32(value);
}
}
}
#[cfg(test)]
mod tests {
extern crate std;
#[test]
fn dispatch_agrees_with_standard_library_detection() {
assert_eq!(super::available(), std::is_x86_feature_detected!("avx2"));
assert_eq!(super::available(), std::is_x86_feature_detected!("avx2"));
}
}