Skip to main content

cpufeatures/
lib.rs

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)]
7
8#[cfg(not(miri))]
9#[cfg(target_arch = "aarch64")]
10#[doc(hidden)]
11pub mod aarch64;
12
13#[cfg(not(miri))]
14#[cfg(target_arch = "loongarch64")]
15#[doc(hidden)]
16pub mod loongarch64;
17
18#[cfg(not(miri))]
19#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
20mod x86;
21
22#[cfg(miri)]
23mod miri;
24
25#[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.");
32
33/// Create module with CPU feature detection code.
34#[macro_export]
35macro_rules! new {
36    ($mod_name:ident, $($tf:tt),+ $(,)?) => {
37        mod $mod_name {
38            use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
39
40            const UNINIT: u8 = u8::MAX;
41            static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);
42
43            /// Initialization token
44            #[derive(Copy, Clone, Debug)]
45            pub struct InitToken(());
46
47            impl InitToken {
48                /// Initialize token, performing CPU feature detection.
49                pub fn init() -> Self {
50                    init()
51                }
52
53                /// Initialize token and return a `bool` indicating if the feature is supported.
54                pub fn init_get() -> (Self, bool) {
55                    init_get()
56                }
57
58                /// Get initialized value.
59                #[inline(always)]
60                pub fn get(&self) -> bool {
61                    $crate::__unless_target_features! {
62                        $($tf),+ => {
63                            STORAGE.load(Relaxed) == 1
64                        }
65                    }
66                }
67            }
68
69            /// Get stored value and initialization token,
70            /// initializing underlying storage if needed.
71            #[inline]
72            pub fn init_get() -> (InitToken, bool) {
73                let res = $crate::__unless_target_features! {
74                    $($tf),+ => {
75                        #[cold]
76                        fn init_inner() -> bool {
77                            let res = $crate::__detect_target_features!($($tf),+);
78                            STORAGE.store(res as u8, Relaxed);
79                            res
80                        }
81
82                        // Relaxed ordering is fine, as we only have a single atomic variable.
83                        let val = STORAGE.load(Relaxed);
84
85                        if val == UNINIT {
86                            init_inner()
87                        } else {
88                            val == 1
89                        }
90                    }
91                };
92
93                (InitToken(()), res)
94            }
95
96            /// Initialize underlying storage if needed and get initialization token.
97            #[inline]
98            pub fn init() -> InitToken {
99                init_get().0
100            }
101
102            /// Initialize underlying storage if needed and get stored value.
103            #[inline]
104            pub fn get() -> bool {
105                init_get().1
106            }
107        }
108    };
109}