libm/math/arch/x86/
detect.rs1#[cfg(target_arch = "x86")]
5use core::arch::x86::{__cpuid, __cpuid_count, _xgetbv, CpuidResult};
6#[cfg(target_arch = "x86_64")]
7use core::arch::x86_64::{__cpuid, __cpuid_count, _xgetbv, CpuidResult};
8
9use crate::support::feature_detect::{Flags, get_or_init_flags_cache, unique_masks};
10
11pub mod cpu_flags {
13 use super::unique_masks;
14
15 pub const SSE3: u32 = 1 << 0;
const _: () =
if !(SSE3 != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: SSE3 != (1 << (<u32>::BITS - 1))")
};
pub const F16C: u32 = 1 << 0 + 1;
const _: () =
if !(F16C != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: F16C != (1 << (<u32>::BITS - 1))")
};
pub const SSE: u32 = 1 << 0 + 1 + 1;
const _: () =
if !(SSE != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: SSE != (1 << (<u32>::BITS - 1))")
};
pub const SSE2: u32 = 1 << 0 + 1 + 1 + 1;
const _: () =
if !(SSE2 != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: SSE2 != (1 << (<u32>::BITS - 1))")
};
pub const ERMSB: u32 = 1 << 0 + 1 + 1 + 1 + 1;
const _: () =
if !(ERMSB != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: ERMSB != (1 << (<u32>::BITS - 1))")
};
pub const MOVRS: u32 = 1 << 0 + 1 + 1 + 1 + 1 + 1;
const _: () =
if !(MOVRS != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: MOVRS != (1 << (<u32>::BITS - 1))")
};
pub const FMA: u32 = 1 << 0 + 1 + 1 + 1 + 1 + 1 + 1;
const _: () =
if !(FMA != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: FMA != (1 << (<u32>::BITS - 1))")
};
pub const FMA4: u32 = 1 << 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
const _: () =
if !(FMA4 != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: FMA4 != (1 << (<u32>::BITS - 1))")
};
pub const AVX512FP16: u32 = 1 << 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
const _: () =
if !(AVX512FP16 != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: AVX512FP16 != (1 << (<u32>::BITS - 1))")
};
pub const AVX512BF16: u32 = 1 << 0 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1;
const _: () =
if !(AVX512BF16 != (1 << (<u32>::BITS - 1))) {
::core::panicking::panic("assertion failed: AVX512BF16 != (1 << (<u32>::BITS - 1))")
};unique_masks! {
16 u32,
17 SSE3,
18 F16C,
19 SSE,
20 SSE2,
21 ERMSB,
22 MOVRS,
23 FMA,
24 FMA4,
25 AVX512FP16,
26 AVX512BF16,
27 }
28}
29
30pub fn get_cpu_features() -> Flags {
32 use core::sync::atomic::AtomicU32;
33 static CACHE: AtomicU32 = AtomicU32::new(0);
34 get_or_init_flags_cache(&CACHE, load_x86_features)
35}
36
37#[allow(unused_unsafe)]
44fn load_x86_features() -> Flags {
45 let mut value = Flags::empty();
46
47 if falsecfg!(target_env = "sgx") {
48 return Flags::empty();
50 }
51
52 let mut vendor_id = [0u8; 12];
61 let max_basic_leaf;
62 unsafe {
63 let CpuidResult { eax, ebx, ecx, edx } = __cpuid(0);
64 max_basic_leaf = eax;
65 vendor_id[0..4].copy_from_slice(&ebx.to_ne_bytes());
66 vendor_id[4..8].copy_from_slice(&edx.to_ne_bytes());
67 vendor_id[8..12].copy_from_slice(&ecx.to_ne_bytes());
68 }
69
70 if max_basic_leaf < 1 {
71 return value;
73 }
74
75 let CpuidResult { ecx, edx, .. } = unsafe { __cpuid(0x0000_0001_u32) };
78 let proc_info_ecx = Flags::from_bits(ecx);
79 let proc_info_edx = Flags::from_bits(edx);
80
81 let mut extended_features_ebx = Flags::empty();
84 let mut extended_features_edx = Flags::empty();
85 let mut extended_features_eax_leaf_1 = Flags::empty();
86 if max_basic_leaf >= 7 {
87 let CpuidResult { ebx, edx, .. } = unsafe { __cpuid(0x0000_0007_u32) };
88 extended_features_ebx = Flags::from_bits(ebx);
89 extended_features_edx = Flags::from_bits(edx);
90
91 let CpuidResult { eax, .. } = unsafe { __cpuid_count(0x0000_0007_u32, 0x0000_0001_u32) };
92 extended_features_eax_leaf_1 = Flags::from_bits(eax)
93 }
94
95 let extended_max_basic_leaf = unsafe { __cpuid(0x8000_0000_u32) }.eax;
99
100 let mut extended_proc_info_ecx = Flags::empty();
102 if extended_max_basic_leaf >= 1 {
103 let CpuidResult { ecx, .. } = unsafe { __cpuid(0x8000_0001_u32) };
104 extended_proc_info_ecx = Flags::from_bits(ecx);
105 }
106
107 let mut enable = |regflags: Flags, regbit, flag| {
108 if regflags.test_nth(regbit) {
109 value.insert(flag);
110 }
111 };
112
113 enable(proc_info_ecx, 0, cpu_flags::SSE3);
114 enable(proc_info_ecx, 29, cpu_flags::F16C);
115 enable(proc_info_edx, 25, cpu_flags::SSE);
116 enable(proc_info_edx, 26, cpu_flags::SSE2);
117 enable(extended_features_ebx, 9, cpu_flags::ERMSB);
118 enable(extended_features_eax_leaf_1, 31, cpu_flags::MOVRS);
119
120 let cpu_xsave = proc_info_ecx.test_nth(26);
122 if cpu_xsave {
123 let cpu_osxsave = proc_info_ecx.test_nth(27);
135
136 if cpu_osxsave {
137 let xcr0 = unsafe { _xgetbv(0) };
149 let os_avx_support = xcr0 & 6 == 6;
151 let os_avx512_support = xcr0 & 0xe0 == 0xe0;
153
154 if os_avx_support {
157 enable(proc_info_ecx, 12, cpu_flags::FMA);
169
170 if os_avx512_support {
173 enable(extended_features_edx, 23, cpu_flags::AVX512FP16);
174 enable(extended_features_eax_leaf_1, 5, cpu_flags::AVX512BF16);
175 }
176 }
177 }
178 }
179
180 if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
192 enable(extended_proc_info_ecx, 16, cpu_flags::FMA4);
194 }
195
196 value
197}
198
199#[cfg(test)]
200mod tests {
201 extern crate std;
202 use std::is_x86_feature_detected;
203
204 use super::*;
205
206 #[test]
207 fn check_matches_std() {
208 let features = get_cpu_features();
209 for i in 0..cpu_flags::ALL.len() {
210 let flag = cpu_flags::ALL[i];
211 let name = cpu_flags::NAMES[i];
212
213 let std_detected = match flag {
214 cpu_flags::SSE3 => is_x86_feature_detected!("sse3"),
215 cpu_flags::F16C => is_x86_feature_detected!("f16c"),
216 cpu_flags::SSE => is_x86_feature_detected!("sse"),
217 cpu_flags::SSE2 => is_x86_feature_detected!("sse2"),
218 cpu_flags::ERMSB => is_x86_feature_detected!("ermsb"),
219 cpu_flags::MOVRS => continue, cpu_flags::FMA => is_x86_feature_detected!("fma"),
221 cpu_flags::FMA4 => continue, cpu_flags::AVX512FP16 => is_x86_feature_detected!("avx512fp16"),
223 cpu_flags::AVX512BF16 => is_x86_feature_detected!("avx512bf16"),
224 _ => panic!("untested CPU flag {name}"),
225 };
226
227 assert_eq!(
228 std_detected,
229 features.contains(flag),
230 "different flag {name}. flags: {features:?}"
231 );
232 }
233 }
234}