1use core::arch::x86_64::{CpuidResult, __cpuid_count};
13use core::sync::atomic::{AtomicU32, Ordering};
14
15pub const CPU_SSE41: u32 = 1;
16pub const CPU_AVX2: u32 = 2; pub const CPU_FMA: u32 = 4; pub const CPU_POPCNT: u32 = 8;
19pub const CPU_BMI2: u32 = 16;
20
21const VALID: u32 = 0x8000_0000; static FEATURES: AtomicU32 = AtomicU32::new(0);
23
24#[allow(unused_unsafe)]
25pub fn cpuid(leaf: u32) -> CpuidResult {
26 unsafe { __cpuid_count(leaf, 0) }
28}
29
30fn xgetbv0() -> u32 {
31 let lo: u32;
32 unsafe { core::arch::asm!("xgetbv", in("ecx") 0, out("eax") lo, out("edx") _, options(nomem, nostack)) };
34 lo
35}
36
37fn detect() -> u32 {
38 let mut f = 0;
39 let max = cpuid(0).eax;
40 if max != 0 {
41 let c1 = cpuid(1).ecx;
42 if c1 & 1 << 19 != 0 {
43 f |= CPU_SSE41;
44 }
45 if c1 & 1 << 23 != 0 {
46 f |= CPU_POPCNT;
47 }
48 let mut ymm = false; if c1 & 1 << 27 != 0 && c1 & 1 << 28 != 0 && xgetbv0() & 6 == 6 {
50 ymm = true;
51 if c1 & 1 << 12 != 0 {
52 f |= CPU_FMA;
53 }
54 }
55 if max >= 7 {
56 let b7 = cpuid(7).ebx;
57 if b7 & 1 << 8 != 0 {
58 f |= CPU_BMI2;
59 }
60 if ymm && b7 & 1 << 5 != 0 {
61 f |= CPU_AVX2;
62 }
63 }
64 }
65 f
66}
67
68#[inline]
70pub fn features() -> u32 {
71 let v = FEATURES.load(Ordering::Relaxed);
72 if v != 0 {
73 return v & !VALID;
74 }
75 let f = detect();
76 FEATURES.store(f | VALID, Ordering::Relaxed);
77 f
78}
79
80pub fn force_baseline() {
82 FEATURES.store(VALID, Ordering::Relaxed);
83}