1//! This crate provides macros for runtime CPU feature detection. It's intended
2//! as a stopgap until Rust [RFC 2725] adding first-class target feature detection
3//! macros to `libcore` is implemented.
4//!
5//! # Supported target architectures
6//!
7//! *NOTE: target features with an asterisk are unstable (nightly-only) and
8//! subject to change to match upstream name changes in the Rust standard
9//! library.
10//!
11//! ## `aarch64`
12//!
13//! Linux, iOS, and macOS/ARM only (ARM64 does not support OS-independent feature detection)
14//!
15//! Target features:
16//!
17//! - `aes`*
18//! - `sha2`*
19//! - `sha3`*
20//!
21//! ## `x86`/`x86_64`
22//!
23//! OS independent and `no_std`-friendly
24//!
25//! Target features:
26//!
27//! - `adx`
28//! - `aes`
29//! - `avx`
30//! - `avx2`
31//! - `avx512bw`*
32//! - `avx512cd`*
33//! - `avx512dq`*
34//! - `avx512er`*
35//! - `avx512f`*
36//! - `avx512ifma`*
37//! - `avx512pf`*
38//! - `avx512vl`*
39//! - `bmi1`
40//! - `bmi2`
41//! - `fma`,
42//! - `mmx`
43//! - `pclmulqdq`
44//! - `popcnt`
45//! - `rdrand`
46//! - `rdseed`
47//! - `sgx`
48//! - `sha`
49//! - `sse`
50//! - `sse2`
51//! - `sse3`
52//! - `sse4.1`
53//! - `sse4.2`
54//! - `ssse3`
55//!
56//! If you would like detection support for a target feature which is not on
57//! this list, please [open a GitHub issue][gh].
58//!
59//! # Example
60//! ```
61//! # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
62//! # {
63//! // This macro creates `cpuid_aes_sha` module
64//! cpufeatures::new!(cpuid_aes_sha, "aes", "sha");
65//!
66//! // `token` is a Zero Sized Type (ZST) value, which guarantees
67//! // that underlying static storage got properly initialized,
68//! // which allows to omit initialization branch
69//! let token: cpuid_aes_sha::InitToken = cpuid_aes_sha::init();
70//!
71//! if token.get() {
72//! println!("CPU supports both SHA and AES extensions");
73//! } else {
74//! println!("SHA and AES extensions are not supported");
75//! }
76//!
77//! // If stored value needed only once you can get stored value
78//! // omitting the token
79//! let val = cpuid_aes_sha::get();
80//! assert_eq!(val, token.get());
81//!
82//! // Additionally you can get both token and value
83//! let (token, val) = cpuid_aes_sha::init_get();
84//! assert_eq!(val, token.get());
85//! # }
86//! ```
87//!
88//! Note that if all tested target features are enabled via compiler options
89//! (e.g. by using `RUSTFLAGS`), the `get` method will always return `true`
90//! and `init` will not use CPUID instruction. Such behavior allows
91//! compiler to completely eliminate fallback code.
92//!
93//! After first call macro caches result and returns it in subsequent
94//! calls, thus runtime overhead for them is minimal.
95//!
96//! [RFC 2725]: https://github.com/rust-lang/rfcs/pull/2725
97//! [gh]: https://github.com/RustCrypto/utils/issues/new?title=cpufeatures:%20requesting%20support%20for%20CHANGEME%20target%20feature
98
99#![no_std]
100#![doc(
101 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
102 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
103)]
104
105#[cfg(not(miri))]
106#[cfg(all(target_arch = "aarch64"))]
107#[doc(hidden)]
108pub mod aarch64;
109
110#[cfg(not(miri))]
111#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
112mod x86;
113
114#[cfg(miri)]
115mod miri;
116
117#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
118compile_error!("This crate works only on `aarch64`, `x86`, and `x86-64` targets.");
119
120/// Create module with CPU feature detection code.
121#[macro_export]
122macro_rules! new {
123 ($mod_name:ident, $($tf:tt),+ $(,)?) => {
124 mod $mod_name {
125 use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
126
127 const UNINIT: u8 = u8::max_value();
128 static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);
129
130 /// Initialization token
131 #[derive(Copy, Clone, Debug)]
132 pub struct InitToken(());
133
134 impl InitToken {
135 /// Get initialized value
136 #[inline(always)]
137 pub fn get(&self) -> bool {
138 $crate::__unless_target_features! {
139 $($tf),+ => {
140 STORAGE.load(Relaxed) == 1
141 }
142 }
143 }
144 }
145
146 /// Initialize underlying storage if needed and get
147 /// stored value and initialization token.
148 #[inline]
149 pub fn init_get() -> (InitToken, bool) {
150 let res = $crate::__unless_target_features! {
151 $($tf),+ => {
152 // Relaxed ordering is fine, as we only have a single atomic variable.
153 let val = STORAGE.load(Relaxed);
154
155 if val == UNINIT {
156 let res = $crate::__detect_target_features!($($tf),+);
157 STORAGE.store(res as u8, Relaxed);
158 res
159 } else {
160 val == 1
161 }
162 }
163 };
164
165 (InitToken(()), res)
166 }
167
168 /// Initialize underlying storage if needed and get
169 /// initialization token.
170 #[inline]
171 pub fn init() -> InitToken {
172 init_get().0
173 }
174
175 /// Initialize underlying storage if needed and get
176 /// stored value.
177 #[inline]
178 pub fn get() -> bool {
179 init_get().1
180 }
181 }
182 };
183}
184