]> git.proxmox.com Git - rustc.git/blob - vendor/cpuid-bool/src/lib.rs
New upstream version 1.48.0~beta.8+dfsg1
[rustc.git] / vendor / cpuid-bool / src / lib.rs
1 //! Macro for checking CPU capabilities at runtime.
2 //!
3 //! # Usage example
4 //! ```
5 //! if cpuid_bool::cpuid_bool!("sha", "aes") {
6 //! println!("CPU supports both SHA and AES extensions");
7 //! } else {
8 //! println!("SHA and AES extensions are not supported");
9 //! }
10 //! ```
11 //! Note that if all tested target features are enabled via compiler options
12 //! (e.g. by using `RUSTFLAGS`), `cpuid_bool!` macro immideatly will expand
13 //! to `true` and will not use CPUID instruction. Such behavior allows
14 //! compiler to eliminate fallback code.
15 //!
16 //! After first call macro caches result and returns it in subsequent
17 //! calls, thus runtime overhead for them is minimal.
18 #![no_std]
19 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
20 compile_error!("This crate works only on x86 and x86-64 targets.");
21
22 use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
23
24 /// This structure represents a lazily initialized static boolean value.
25 ///
26 /// Useful when it is preferable to just rerun initialization instead of
27 /// locking. Used internally by the `cpuid_bool` macro.
28 pub struct LazyBool(AtomicU8);
29
30 impl LazyBool {
31 const UNINIT: u8 = u8::max_value();
32
33 pub const fn new() -> Self {
34 Self(AtomicU8::new(Self::UNINIT))
35 }
36
37 // Runs the init() function at least once, returning the value of some run
38 // of init(). Multiple callers can run their init() functions in parallel.
39 // init() should always return the same value, if it succeeds.
40 pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
41 // Relaxed ordering is fine, as we only have a single atomic variable.
42 let mut val = self.0.load(Relaxed);
43 if val == Self::UNINIT {
44 val = init() as u8;
45 self.0.store(val as u8, Relaxed);
46 }
47 val != 0
48 }
49 }
50
51 // TODO: find how to define private macro usable inside a public one
52 macro_rules! expand_check_macro {
53 ($(($name:tt, $i:expr, $reg:ident, $offset:expr)),* $(,)?) => {
54 #[macro_export]
55 #[doc(hidden)]
56 macro_rules! check {
57 $(
58 ($cr:expr, $name) => { ($cr[$i].$reg & (1 << $offset) != 0) };
59 )*
60 }
61 };
62 }
63
64 expand_check_macro! {
65 ("mmx", 0, edx, 23),
66 ("sse", 0, edx, 25),
67 ("sse2", 0, edx, 26),
68 ("sse3", 0, ecx, 0),
69 ("pclmulqdq", 0, ecx, 1),
70 ("ssse3", 0, ecx, 9),
71 ("fma", 0, ecx, 12),
72 ("sse4.1", 0, ecx, 19),
73 ("sse4.2", 0, ecx, 20),
74 ("popcnt", 0, ecx, 23),
75 ("aes", 0, ecx, 25),
76 ("avx", 0, ecx, 28),
77 ("rdrand", 0, ecx, 30),
78 ("sgx", 1, ebx, 2),
79 ("bmi1", 1, ebx, 3),
80 ("avx2", 1, ebx, 5),
81 ("bmi2", 1, ebx, 8),
82 ("rdseed", 1, ebx, 18),
83 ("adx", 1, ebx, 19),
84 ("sha", 1, ebx, 29),
85 }
86
87 /// Check at runtime if CPU supports sequence of target features.
88 ///
89 /// During first execution this macro will use CPUID to check requested
90 /// target features, results will be cached and further calls will return
91 /// it instead.
92 #[macro_export]
93 macro_rules! cpuid_bool {
94 ($($tf:tt),+ $(,)? ) => {{
95 // CPUID is not available on SGX targets
96 #[cfg(all(not(target_env = "sgx"), not(all($(target_feature=$tf, )*))))]
97 let res = {
98 #[cfg(target_arch = "x86")]
99 use core::arch::x86::{__cpuid, __cpuid_count};
100 #[cfg(target_arch = "x86_64")]
101 use core::arch::x86_64::{__cpuid, __cpuid_count};
102
103 static CPUID_BOOL: cpuid_bool::LazyBool = cpuid_bool::LazyBool::new();
104 CPUID_BOOL.unsync_init(|| {
105 #[allow(unused_variables)]
106 let cr = unsafe {
107 [__cpuid(1), __cpuid_count(7, 0)]
108 };
109 // TODO: find how to remove `true`
110 $(cpuid_bool::check!(cr, $tf) & )+ true
111 })
112 };
113
114 #[cfg(all(target_env = "sgx", not(all($(target_feature=$tf, )*))))]
115 let res = false;
116 #[cfg(all($(target_feature=$tf, )*))]
117 let res = true;
118
119 res
120 }};
121 }