use core::arch::asm;
#[inline]
pub(crate) fn ifma() -> bool {
use core::sync::atomic::{AtomicU8, Ordering};
static CACHED: AtomicU8 = AtomicU8::new(0);
match CACHED.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let have = !cfg!(ic_no_avx512) && detect();
CACHED.store(if have { 1 } else { 2 }, Ordering::Relaxed);
have
}
}
}
fn cpuid(leaf: u32, sub: u32) -> (u32, u32, u32, u32) {
let (a, b, c, d): (u32, u64, u32, u32);
unsafe {
asm!(
".p2align 6",
"mov {b}, rbx",
"cpuid",
"xchg {b}, rbx",
b = out(reg) b,
inout("eax") leaf => a,
inout("ecx") sub => c,
out("edx") d,
options(nostack, preserves_flags, nomem),
);
}
(a, b as u32, c, d)
}
#[inline]
#[cfg_attr(
any(not(feature = "std"), ic_limb32, ic_ec_portable, ic_approved_only),
allow(dead_code)
)]
pub(crate) fn avx2() -> bool {
use core::sync::atomic::{AtomicU8, Ordering};
static CACHED: AtomicU8 = AtomicU8::new(0);
match CACHED.load(Ordering::Relaxed) {
1 => true,
2 => false,
_ => {
let have = detect_avx2();
CACHED.store(if have { 1 } else { 2 }, Ordering::Relaxed);
have
}
}
}
#[cfg_attr(
any(not(feature = "std"), ic_limb32, ic_ec_portable, ic_approved_only),
allow(dead_code)
)]
fn detect_avx2() -> bool {
let Some(xcr0) = xcr0() else {
return false;
};
if xcr0 & 0b110 != 0b110 {
return false;
}
let (_, ebx7, _, _) = cpuid(7, 0);
ebx7 & (1 << 5) != 0
}
fn detect() -> bool {
let Some(xcr0) = xcr0() else {
return false;
};
detect_with(xcr0)
}
fn xcr0() -> Option<u64> {
let (max, _, _, _) = cpuid(0, 0);
if max < 7 {
return None;
}
let (_, _, ecx1, _) = cpuid(1, 0);
if ecx1 & (1 << 27) == 0 || ecx1 & (1 << 28) == 0 {
return None;
}
Some({
let (lo, hi): (u32, u32);
unsafe {
asm!(
"xgetbv",
in("ecx") 0u32,
out("eax") lo,
out("edx") hi,
options(nostack, preserves_flags, nomem),
);
}
(u64::from(hi) << 32) | u64::from(lo)
})
}
fn detect_with(xcr0: u64) -> bool {
let needed = 0b1110_0110;
if xcr0 & needed != needed {
return false;
}
let (_, ebx7, _, _) = cpuid(7, 0);
let want = (1 << 5) | (1 << 16) | (1 << 17) | (1 << 21) | (1 << 31);
ebx7 & want == want
}