1 //! Macro for checking CPU capabilities at runtime.
5 //! if cpuid_bool::cpuid_bool!("sha", "aes") {
6 //! println!("CPU supports both SHA and AES extensions");
8 //! println!("SHA and AES extensions are not supported");
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.
16 //! After first call macro caches result and returns it in subsequent
17 //! calls, thus runtime overhead for them is minimal.
19 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
20 compile_error
!("This crate works only on x86 and x86-64 targets.");
22 use core
::sync
::atomic
::{AtomicU8, Ordering::Relaxed}
;
24 /// This structure represents a lazily initialized static boolean value.
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
);
31 const UNINIT
: u8 = u8::max_value();
33 pub const fn new() -> Self {
34 Self(AtomicU8
::new(Self::UNINIT
))
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
{
45 self.0.store(val
as u8, Relaxed
);
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
)),* $
(,)?
) => {
58 ($cr
:expr
, $name
) => { ($cr[$i].$reg & (1 << $offset) != 0) }
;
69 ("pclmulqdq", 0, ecx
, 1),
72 ("sse4.1", 0, ecx
, 19),
73 ("sse4.2", 0, ecx
, 20),
74 ("popcnt", 0, ecx
, 23),
77 ("rdrand", 0, ecx
, 30),
82 ("rdseed", 1, ebx
, 18),
87 /// Check at runtime if CPU supports sequence of target features.
89 /// During first execution this macro will use CPUID to check requested
90 /// target features, results will be cached and further calls will return
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, )*))))]
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}
;
103 static CPUID_BOOL
: cpuid_bool
::LazyBool
= cpuid_bool
::LazyBool
::new();
104 CPUID_BOOL
.unsync_init(|| {
105 #[allow(unused_variables)]
107 [__cpuid(1), __cpuid_count(7, 0)]
109 // TODO: find how to remove `true`
110 $
(cpuid_bool
::check
!(cr
, $tf
) & )+ true
114 #[cfg(all(target_env = "sgx", not(all($(target_feature=$tf, )*))))]
116 #[cfg(all($(target_feature=$tf, )*))]