/rust/registry/src/index.crates.io-6f17d22bba15001f/cpufeatures-0.2.17/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
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 | 0 | pub fn get(&self) -> bool { |
172 | 0 | $crate::__unless_target_features! { |
173 | 0 | $($tf),+ => { |
174 | 0 | STORAGE.load(Relaxed) == 1 |
175 | 0 | } |
176 | 0 | } |
177 | 0 | } |
178 | | } |
179 | | |
180 | | /// Get stored value and initialization token, |
181 | | /// initializing underlying storage if needed. |
182 | | #[inline] |
183 | 0 | pub fn init_get() -> (InitToken, bool) { |
184 | 0 | let res = $crate::__unless_target_features! { |
185 | | $($tf),+ => { |
186 | | #[cold] |
187 | 0 | fn init_inner() -> bool { |
188 | 0 | let res = $crate::__detect_target_features!($($tf),+); |
189 | 0 | STORAGE.store(res as u8, Relaxed); |
190 | 0 | res |
191 | 0 | } |
192 | | |
193 | | // Relaxed ordering is fine, as we only have a single atomic variable. |
194 | 0 | let val = STORAGE.load(Relaxed); |
195 | 0 |
|
196 | 0 | if val == UNINIT { |
197 | 0 | init_inner() |
198 | | } else { |
199 | 0 | val == 1 |
200 | | } |
201 | | } |
202 | | }; |
203 | | |
204 | 0 | (InitToken(()), res) |
205 | 0 | } |
206 | | |
207 | | /// Initialize underlying storage if needed and get initialization token. |
208 | | #[inline] |
209 | 0 | pub fn init() -> InitToken { |
210 | 0 | init_get().0 |
211 | 0 | } |
212 | | |
213 | | /// Initialize underlying storage if needed and get stored value. |
214 | | #[inline] |
215 | 0 | pub fn get() -> bool { |
216 | 0 | init_get().1 |
217 | 0 | } |
218 | | } |
219 | | }; |
220 | | } |