#![allow(unsafe_code)]
#![allow(clippy::redundant_pub_crate)]
use core::sync::atomic::{AtomicU32, Ordering};
pub(super) const X86V3: u32 = 1 << 0;
pub(super) const X86V4: u32 = 1 << 1;
pub(super) const X86V2: u32 = 1 << 2;
const KNOWN: u32 = 1 << 31;
static CACHE: AtomicU32 = AtomicU32::new(0);
#[inline]
pub(super) fn x86v2() -> bool {
x86v2_in_build() || detected(X86V2)
}
#[inline]
pub(super) fn x86v3() -> bool {
x86v3_in_build() || detected(X86V3)
}
#[inline]
pub(super) fn x86v4() -> bool {
x86v4_in_build() || detected(X86V4)
}
pub(super) const fn x86v2_in_build() -> bool {
cfg!(all(
target_feature = "sse3",
target_feature = "ssse3",
target_feature = "sse4.1",
target_feature = "sse4.2",
target_feature = "popcnt",
target_feature = "cmpxchg16b"
))
}
pub(super) const fn x86v3_in_build() -> bool {
cfg!(all(
target_feature = "sse3",
target_feature = "ssse3",
target_feature = "sse4.1",
target_feature = "sse4.2",
target_feature = "popcnt",
target_feature = "cmpxchg16b",
target_feature = "avx",
target_feature = "avx2",
target_feature = "bmi1",
target_feature = "bmi2",
target_feature = "fma",
target_feature = "lzcnt",
target_feature = "movbe",
target_feature = "f16c",
target_feature = "xsave",
target_feature = "pclmulqdq"
))
}
pub(super) const fn x86v4_in_build() -> bool {
x86v3_in_build()
&& cfg!(all(
target_feature = "avx512f",
target_feature = "avx512dq",
target_feature = "avx512cd",
target_feature = "avx512bw",
target_feature = "avx512vl",
target_feature = "avx512vbmi",
target_feature = "gfni"
))
}
#[inline]
pub(super) fn levels() -> u32 {
let mut bits = CACHE.load(Ordering::Relaxed);
if bits & KNOWN == 0 {
bits = detect() | KNOWN;
CACHE.store(bits, Ordering::Relaxed);
}
bits
}
#[inline]
fn detected(feature: u32) -> bool {
levels() & feature != 0
}
#[cfg(miri)]
const fn detect() -> u32 {
0
}
#[cfg(not(miri))]
#[cold]
#[allow(unused_unsafe)]
fn detect() -> u32 {
use core::arch::x86_64::{__cpuid, __cpuid_count, _xgetbv};
let max_leaf = unsafe { __cpuid(0) }.eax;
if max_leaf < 1 {
return 0;
}
let leaf1 = unsafe { __cpuid(1) };
let bit = |word: u32, n: u32| word & (1 << n) != 0;
let mut out = 0;
if [0, 9, 13, 19, 20, 23].iter().all(|&n| bit(leaf1.ecx, n)) {
out |= X86V2;
}
if max_leaf < 7 || !bit(leaf1.ecx, 27) {
return out;
}
let leaf7 = unsafe { __cpuid_count(7, 0) };
let xcr0 = unsafe { _xgetbv(0) };
let ymm = xcr0 & 0b110 == 0b110;
let zmm = ymm && xcr0 & 0b1110_0000 == 0b1110_0000;
let v3_leaf1 = [0, 1, 9, 12, 13, 19, 20, 22, 23, 26, 28, 29];
let lzcnt = unsafe { __cpuid(0x8000_0000) }.eax >= 0x8000_0001
&& bit(unsafe { __cpuid(0x8000_0001) }.ecx, 5);
if ymm
&& v3_leaf1.iter().all(|&n| bit(leaf1.ecx, n))
&& [3, 5, 8].iter().all(|&n| bit(leaf7.ebx, n))
&& lzcnt
{
out |= X86V3;
}
if out & X86V3 != 0
&& zmm
&& [16, 17, 28, 30, 31].iter().all(|&n| bit(leaf7.ebx, n))
&& [1, 8].iter().all(|&n| bit(leaf7.ecx, n))
{
out |= X86V4;
}
out
}