| //! This crate provides macros for runtime CPU feature detection. It's intended |
| //! as a stopgap until Rust [RFC 2725] adding first-class target feature detection |
| //! macros to `libcore` is implemented. |
| //! |
| //! Supported target architectures: |
| //! - `aarch64`: Linux and macOS/M4 only (ARM64 does not support OS-independent feature detection) |
| //! - Target features: `aes`, `sha2`, `sha3` |
| //! - `x86`/`x86_64`: OS independent and `no_std`-friendly |
| //! - Target features: `adx`, `aes`, `avx`, `avx2`, `bmi1`, `bmi2`, `fma`, |
| //! `mmx`, `pclmulqdq`, `popcnt`, `rdrand`, `rdseed`, `sgx`, `sha`, `sse`, |
| //! `sse2`, `sse3`, `sse4.1`, `sse4.2`, `ssse3` |
| //! |
| //! If you would like detection support for a target feature which is not on |
| //! this list, please [open a GitHub issue][gh]. |
| //! |
| //! # Example |
| //! ``` |
| //! # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| //! # { |
| //! // This macro creates `cpuid_aes_sha` module |
| //! cpufeatures::new!(cpuid_aes_sha, "aes", "sha"); |
| //! |
| //! // `token` is a Zero Sized Type (ZST) value, which guarantees |
| //! // that underlying static storage got properly initialized, |
| //! // which allows to omit initialization branch |
| //! let token: cpuid_aes_sha::InitToken = cpuid_aes_sha::init(); |
| //! |
| //! if token.get() { |
| //! println!("CPU supports both SHA and AES extensions"); |
| //! } else { |
| //! println!("SHA and AES extensions are not supported"); |
| //! } |
| //! |
| //! // If stored value needed only once you can get stored value |
| //! // omitting the token |
| //! let val = cpuid_aes_sha::get(); |
| //! assert_eq!(val, token.get()); |
| //! |
| //! // Additionally you can get both token and value |
| //! let (token, val) = cpuid_aes_sha::init_get(); |
| //! assert_eq!(val, token.get()); |
| //! # } |
| //! ``` |
| //! |
| //! Note that if all tested target features are enabled via compiler options |
| //! (e.g. by using `RUSTFLAGS`), the `get` method will always return `true` |
| //! and `init` will not use CPUID instruction. Such behavior allows |
| //! compiler to completely eliminate fallback code. |
| //! |
| //! After first call macro caches result and returns it in subsequent |
| //! calls, thus runtime overhead for them is minimal. |
| //! |
| //! [RFC 2725]: https://github.com/rust-lang/rfcs/pull/2725 |
| //! [gh]: https://github.com/RustCrypto/utils/issues/new?title=cpufeatures:%20requesting%20support%20for%20CHANGEME%20target%20feature |
| |
| #![no_std] |
| #![doc( |
| html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", |
| html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" |
| )] |
| |
| #[cfg(not(miri))] |
| #[cfg(all(target_arch = "aarch64"))] |
| #[doc(hidden)] |
| pub mod aarch64; |
| |
| #[cfg(not(miri))] |
| #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| mod x86; |
| |
| #[cfg(miri)] |
| mod miri; |
| |
| #[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))] |
| compile_error!("This crate works only on `aarch64`, `x86`, and `x86-64` targets."); |
| |
| /// Create module with CPU feature detection code. |
| #[macro_export] |
| macro_rules! new { |
| ($mod_name:ident, $($tf:tt),+ $(,)?) => { |
| mod $mod_name { |
| use core::sync::atomic::{AtomicU8, Ordering::Relaxed}; |
| |
| const UNINIT: u8 = u8::max_value(); |
| static STORAGE: AtomicU8 = AtomicU8::new(UNINIT); |
| |
| /// Initialization token |
| #[derive(Copy, Clone, Debug)] |
| pub struct InitToken(()); |
| |
| impl InitToken { |
| /// Get initialized value |
| #[inline(always)] |
| pub fn get(&self) -> bool { |
| $crate::__unless_target_features! { |
| $($tf),+ => { |
| STORAGE.load(Relaxed) == 1 |
| } |
| } |
| } |
| } |
| |
| /// Initialize underlying storage if needed and get |
| /// stored value and initialization token. |
| #[inline] |
| pub fn init_get() -> (InitToken, bool) { |
| let res = $crate::__unless_target_features! { |
| $($tf),+ => { |
| // Relaxed ordering is fine, as we only have a single atomic variable. |
| let val = STORAGE.load(Relaxed); |
| |
| if val == UNINIT { |
| let res = $crate::__detect_target_features!($($tf),+); |
| STORAGE.store(res as u8, Relaxed); |
| res |
| } else { |
| val == 1 |
| } |
| } |
| }; |
| |
| (InitToken(()), res) |
| } |
| |
| /// Initialize underlying storage if needed and get |
| /// initialization token. |
| #[inline] |
| pub fn init() -> InitToken { |
| init_get().0 |
| } |
| |
| /// Initialize underlying storage if needed and get |
| /// stored value. |
| #[inline] |
| pub fn get() -> bool { |
| init_get().1 |
| } |
| } |
| }; |
| } |