cpufeatures/
x86.rs

1//! x86/x86-64 CPU feature detection support.
2//!
3//! Portable, `no_std`-friendly implementation that relies on the x86 `CPUID`
4//! instruction for feature detection.
5
6// Evaluate the given `$body` expression any of the supplied target features
7// are not enabled. Otherwise returns true.
8//
9// The `$body` expression is not evaluated on SGX targets, and returns false
10// on these targets unless *all* supplied target features are enabled.
11#[macro_export]
12#[doc(hidden)]
13macro_rules! __unless_target_features {
14    ($($tf:tt),+ => $body:expr ) => {{
15        #[cfg(not(all($(target_feature=$tf,)*)))]
16        {
17            #[cfg(not(target_env = "sgx"))]
18            $body
19
20            // CPUID is not available on SGX targets
21            #[cfg(target_env = "sgx")]
22            false
23        }
24
25        #[cfg(all($(target_feature=$tf,)*))]
26        true
27    }};
28}
29
30// Use CPUID to detect the presence of all supplied target features.
31#[macro_export]
32#[doc(hidden)]
33macro_rules! __detect_target_features {
34    ($($tf:tt),+) => {{
35        #[cfg(target_arch = "x86")]
36        use core::arch::x86::{__cpuid, __cpuid_count, CpuidResult};
37        #[cfg(target_arch = "x86_64")]
38        use core::arch::x86_64::{__cpuid, __cpuid_count, CpuidResult};
39
40        // These wrappers are workarounds around
41        // https://github.com/rust-lang/rust/issues/101346
42        //
43        // DO NOT remove it until MSRV is bumped to a version
44        // with the issue fix (at least 1.64).
45        #[inline(never)]
46        unsafe fn cpuid(leaf: u32) -> CpuidResult {
47            __cpuid(leaf)
48        }
49
50        #[inline(never)]
51        unsafe fn cpuid_count(leaf: u32, sub_leaf: u32) -> CpuidResult {
52            __cpuid_count(leaf, sub_leaf)
53        }
54
55        let cr = unsafe {
56            [cpuid(1), cpuid_count(7, 0)]
57        };
58
59        $($crate::check!(cr, $tf) & )+ true
60    }};
61}
62
63macro_rules! __expand_check_macro {
64    ($(($name:tt $(, $i:expr, $reg:ident, $offset:expr)*)),* $(,)?) => {
65        #[macro_export]
66        #[doc(hidden)]
67        macro_rules! check {
68            $(
69                ($cr:expr, $name) => {
70                    true
71                    $(
72                        & ($cr[$i].$reg & (1 << $offset) != 0)
73                    )*
74                };
75            )*
76        }
77    };
78}
79
80// Note that according to the [Intel manual][0] AVX2 and FMA require
81// that we check availability of AVX before using them.
82//
83// [0]: https://www.intel.com/content/dam/develop/external/us/en/documents/36945
84__expand_check_macro! {
85    ("mmx", 0, edx, 23),
86    ("sse", 0, edx, 25),
87    ("sse2", 0, edx, 26),
88    ("sse3", 0, ecx, 0),
89    ("pclmulqdq", 0, ecx, 1),
90    ("ssse3", 0, ecx, 9),
91    ("fma", 0, ecx, 28, 0, ecx, 12),
92    ("sse4.1", 0, ecx, 19),
93    ("sse4.2", 0, ecx, 20),
94    ("popcnt", 0, ecx, 23),
95    ("aes", 0, ecx, 25),
96    ("avx", 0, ecx, 28),
97    ("rdrand", 0, ecx, 30),
98    ("sgx", 1, ebx, 2),
99    ("bmi1", 1, ebx, 3),
100    ("avx2", 0, ecx, 28, 1, ebx, 5),
101    ("bmi2", 1, ebx, 8),
102    ("rdseed", 1, ebx, 18),
103    ("adx", 1, ebx, 19),
104    ("sha", 1, ebx, 29),
105}