1#![no_std]
2#![doc = "# [RustCrypto]: CPU Feature Detection\n\n[![crate][crate-image]][crate-link]\n[![Docs][docs-image]][docs-link]\n[![Build Status][build-image]][build-link]\n![Apache2/MIT licensed][license-image]\n![Rust Version][rustc-image]\n[![Project Chat][chat-image]][chat-link]\n\nLightweight and efficient runtime CPU feature detection for `aarch64`, `loongarch64`, and\n`x86`/`x86_64` targets.\n\nSupports `no_std` as well as mobile targets including iOS and Android,\nproviding an alternative to the `std`-dependent `is_x86_feature_detected!`\nmacro.\n\nThis crate is intended as a stopgap until Rust [RFC 2725] adding first-class\ntarget feature detection macros to `libcore` is implemented.\n\n## Example\n```rust\n#[cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))]\npub mod x86_backend {\n // This macro creates `cpuid_aes_sha` module\n cpufeatures::new!(cpuid_aes_sha, \"aes\", \"sha\");\n\n pub fn run() {\n // `token` is a Zero Sized Type (ZST) value, which guarantees\n // that underlying static storage got properly initialized,\n // which allows to omit initialization branch\n let token: cpuid_aes_sha::InitToken = cpuid_aes_sha::init();\n\n if token.get() {\n println!(\"CPU supports both SHA and AES extensions\");\n } else {\n println!(\"SHA and AES extensions are not supported\");\n }\n\n // If stored value needed only once you can get stored value\n // omitting the token\n let val = cpuid_aes_sha::get();\n assert_eq!(val, token.get());\n \n // Additionally you can get both token and value\n let (token, val) = cpuid_aes_sha::init_get();\n assert_eq!(val, token.get());\n }\n}\n```\n\nNote that if all tested target features are enabled via compiler options\n(e.g. by using `RUSTFLAGS`), the `get` method will always return `true`\nand `init` will not use CPUID instruction. Such behavior allows\ncompiler to completely eliminate fallback code.\n\nAfter first call macro caches result and returns it in subsequent\ncalls, thus runtime overhead for them is minimal.\n\n## Supported target architectures\n\n*NOTE: target features with an asterisk are unstable (nightly-only) and subject\nto change to match upstream name changes in the Rust standard library.*\n\n### `aarch64`\n\nLinux, iOS, and macOS/ARM only (ARM64 does not support OS-independent feature detection)\n\nTarget features:\n\n- `aes`*\n- `sha2`*\n- `sha3`*\n\n### `loongarch64`\n\nLinux only (`loongarch64` does not support OS-independent feature detection)\n\nTarget features:\n\n- `32s`*\n- `f`*\n- `d`*\n- `frecipe`*\n- `div32`*\n- `lsx`*\n- `lasx`*\n- `lam-bh`*\n- `lamcas`*\n- `ld-seq-sa`*\n- `scq`*\n- `lbt`*\n- `lvz`*\n- `ual`*\n\n### `x86`/`x86_64`\n\nOS independent and `no_std`-friendly\n\nTarget features:\n\n- `adx`\n- `aes`\n- `avx`\n- `avx2`\n- `avx512bw`*\n- `avx512cd`*\n- `avx512dq`*\n- `avx512er`*\n- `avx512f`*\n- `avx512ifma`*\n- `avx512pf`*\n- `avx512vl`*\n- `avx512vbmi`*\n- `avx512vbmi2`*\n- `bmi1`\n- `bmi2`\n- `fma`\n- `mmx`\n- `pclmulqdq`\n- `popcnt`\n- `rdrand`\n- `rdseed`\n- `sgx`\n- `sha`\n- `sse`\n- `sse2`\n- `sse3`\n- `sse4.1`\n- `sse4.2`\n- `ssse3`\n- `sha512`\n- `sm3`\n- `sm4`\n\nIf you would like detection support for a target feature which is not on\nthis list, please [open a GitHub issue][github-issue].\n\n## License\n\nLicensed under either of:\n\n * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)\n * [MIT license](http://opensource.org/licenses/MIT)\n\nat your option.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n[//]: # (badges)\n\n[crate-image]: https://img.shields.io/crates/v/cpufeatures.svg\n[crate-link]: https://crates.io/crates/cpufeatures\n[docs-image]: https://docs.rs/cpufeatures/badge.svg\n[docs-link]: https://docs.rs/cpufeatures/\n[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg\n[rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg\n[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg\n[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils\n[build-image]: https://github.com/RustCrypto/utils/actions/workflows/cpufeatures.yml/badge.svg?branch=master\n[build-link]: https://github.com/RustCrypto/utils/actions/workflows/cpufeatures.yml?query=branch:master\n\n[//]: # (general links)\n\n[RustCrypto]: https://github.com/RustCrypto\n[RustCrypto/utils#378]: https://github.com/RustCrypto/utils/issues/378\n[RFC 2725]: https://github.com/rust-lang/rfcs/pull/2725\n[github-issue]: https://github.com/RustCrypto/utils/issues/new?title=cpufeatures:%20requesting%20support%20for%20CHANGEME%20target%20feature\n"include_str!("../README.md")]
3#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
5 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
6)]
78#[cfg(not(miri))]
9#[cfg(target_arch = "aarch64")]
10#[doc(hidden)]
11pub mod aarch64;
1213#[cfg(not(miri))]
14#[cfg(target_arch = "loongarch64")]
15#[doc(hidden)]
16pub mod loongarch64;
1718#[cfg(not(miri))]
19#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
20mod x86;
2122#[cfg(miri)]
23mod miri;
2425#[cfg(not(any(
26 target_arch = "aarch64",
27 target_arch = "loongarch64",
28 target_arch = "x86",
29 target_arch = "x86_64"
30)))]
31compile_error!("This crate works only on `aarch64`, `loongarch64`, `x86`, and `x86-64` targets.");
3233/// Create module with CPU feature detection code.
34#[macro_export]
35macro_rules!new {
36 ($mod_name:ident, $($tf:tt),+ $(,)?) => {
37mod $mod_name {
38use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
3940const UNINIT: u8 = u8::MAX;
41static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);
4243/// Initialization token
44#[derive(Copy, Clone, Debug)]
45pub struct InitToken(());
4647impl InitToken {
48/// Initialize token, performing CPU feature detection.
49pub fn init() -> Self {
50 init()
51 }
5253/// Initialize token and return a `bool` indicating if the feature is supported.
54pub fn init_get() -> (Self, bool) {
55 init_get()
56 }
5758/// Get initialized value.
59#[inline(always)]
60pub fn get(&self) -> bool {
61$crate::__unless_target_features! {
62 $($tf),+ => {
63 STORAGE.load(Relaxed) == 1
64}
65 }
66 }
67 }
6869/// Get stored value and initialization token,
70 /// initializing underlying storage if needed.
71#[inline]
72pub fn init_get() -> (InitToken, bool) {
73let res = $crate::__unless_target_features! {
74 $($tf),+ => {
75#[cold]
76fn init_inner() -> bool {
77let res = $crate::__detect_target_features!($($tf),+);
78 STORAGE.store(res as u8, Relaxed);
79 res
80 }
8182// Relaxed ordering is fine, as we only have a single atomic variable.
83let val = STORAGE.load(Relaxed);
8485if val == UNINIT {
86 init_inner()
87 } else {
88 val == 1
89}
90 }
91 };
9293 (InitToken(()), res)
94 }
9596/// Initialize underlying storage if needed and get initialization token.
97#[inline]
98pub fn init() -> InitToken {
99 init_get().0
100}
101102/// Initialize underlying storage if needed and get stored value.
103#[inline]
104pub fn get() -> bool {
105 init_get().1
106}
107 }
108 };
109}