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