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)] |
132 | pub mod aarch64; |
133 | |
134 | #[cfg (not(miri))] |
135 | #[cfg (target_arch = "loongarch64" )] |
136 | #[doc (hidden)] |
137 | pub mod loongarch64; |
138 | |
139 | #[cfg (not(miri))] |
140 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
141 | mod x86; |
142 | |
143 | #[cfg (miri)] |
144 | mod miri; |
145 | |
146 | #[cfg (not(any( |
147 | target_arch = "aarch64" , |
148 | target_arch = "loongarch64" , |
149 | target_arch = "x86" , |
150 | target_arch = "x86_64" |
151 | )))] |
152 | compile_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 ] |
156 | macro_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 | |