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)] |
108 | pub mod aarch64; |
109 | |
110 | #[cfg (not(miri))] |
111 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
112 | mod x86; |
113 | |
114 | #[cfg (miri)] |
115 | mod miri; |
116 | |
117 | #[cfg (not(any(target_arch = "aarch64" , target_arch = "x86" , target_arch = "x86_64" )))] |
118 | compile_error!("This crate works only on `aarch64`, `x86`, and `x86-64` targets." ); |
119 | |
120 | /// Create module with CPU feature detection code. |
121 | #[macro_export ] |
122 | macro_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 | |