Skip to main content

cpufeatures/
lib.rs

1#![no_std]
2#![doc = 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_value();
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}