ring/
cpu.rs

1// Copyright 2016 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15pub(crate) use self::features::Features;
16use core::mem::size_of;
17
18macro_rules! impl_get_feature {
19    {
20      features: [
21          $( { ( $( $arch:expr ),+ ) => $Name:ident }, )+
22      ],
23    } => {
24        $(
25            #[cfg(any( $( target_arch = $arch ),+ ))]
26            #[derive(Clone, Copy)]
27            pub(crate) struct $Name(crate::cpu::Features);
28
29            #[cfg(any( $( target_arch = $arch ),+ ))]
30            impl $Name {
31                const fn mask() -> u32 {
32                    1 << (Shift::$Name as u32)
33                }
34            }
35
36            #[cfg(any( $( target_arch = $arch ),+ ))]
37            impl crate::cpu::GetFeature<$Name> for super::features::Values {
38                #[inline(always)]
39                fn get_feature(&self) -> Option<$Name> {
40                    const MASK: u32 = $Name::mask();
41                    const STATICALLY_DETECTED: bool = (crate::cpu::CAPS_STATIC & MASK) == MASK;
42                    if STATICALLY_DETECTED { // TODO: `const`
43                        return Some($Name(self.cpu()));
44                    }
45
46                    if (self.values() & MASK) == MASK {
47                        Some($Name(self.cpu()))
48                    } else {
49                        None
50                    }
51                }
52            }
53        )+
54
55        #[repr(u32)]
56        enum Shift {
57            $(
58                #[cfg(any( $( target_arch = $arch ),+ ))]
59                $Name,
60            )+
61
62            #[cfg(target_arch = "x86_64")]
63            IntelCpu,
64
65            #[cfg(any(all(target_arch = "aarch64", target_endian = "little"),
66                     all(target_arch = "arm", target_endian = "little"),
67                     target_arch = "x86", target_arch = "x86_64"))]
68            // Synthesized to ensure the dynamic flag set is always non-zero.
69            //
70            // Keep this at the end as it is never checked except during init.
71            Initialized,
72        }
73    }
74}
75
76pub(crate) trait GetFeature<T> {
77    fn get_feature(&self) -> Option<T>;
78}
79
80impl GetFeature<()> for features::Values {
81    #[inline(always)]
82    fn get_feature(&self) -> Option<()> {
83        Some(())
84    }
85}
86
87impl<A, B> GetFeature<(A, B)> for features::Values
88where
89    features::Values: GetFeature<A>,
90    features::Values: GetFeature<B>,
91{
92    #[inline(always)]
93    fn get_feature(&self) -> Option<(A, B)> {
94        match (self.get_feature(), self.get_feature()) {
95            (Some(a), Some(b)) => Some((a, b)),
96            _ => None,
97        }
98    }
99}
100
101impl<A, B, C> GetFeature<(A, B, C)> for features::Values
102where
103    features::Values: GetFeature<A>,
104    features::Values: GetFeature<B>,
105    features::Values: GetFeature<C>,
106{
107    #[inline(always)]
108    fn get_feature(&self) -> Option<(A, B, C)> {
109        match (self.get_feature(), self.get_feature(), self.get_feature()) {
110            (Some(a), Some(b), Some(c)) => Some((a, b, c)),
111            _ => None,
112        }
113    }
114}
115
116impl<F> GetFeature<F> for Features
117where
118    features::Values: GetFeature<F>,
119{
120    #[inline(always)]
121    fn get_feature(&self) -> Option<F> {
122        self.values().get_feature()
123    }
124}
125
126#[inline(always)]
127pub(crate) fn features() -> Features {
128    featureflags::get_or_init()
129}
130
131mod features {
132    use crate::polyfill::NotSend;
133
134    /// A witness indicating that CPU features have been detected and cached.
135    ///
136    /// This is a zero-sized type so that it can be "stored" wherever convenient.
137    #[derive(Copy, Clone)]
138    pub(crate) struct Features(NotSend);
139
140    impl Features {
141        pub fn values(self) -> Values {
142            Values {
143                values: super::featureflags::get(self),
144                cpu: self,
145            }
146        }
147    }
148
149    cfg_if::cfg_if! {
150        if #[cfg(any(all(target_arch = "aarch64", target_endian = "little"), all(target_arch = "arm", target_endian = "little"),
151                     target_arch = "x86", target_arch = "x86_64"))] {
152            impl Features {
153                // SAFETY: This must only be called after CPU features have been written
154                // and synchronized.
155                pub(super) unsafe fn new_after_feature_flags_written_and_synced_unchecked() -> Self {
156                    Self(NotSend::VALUE)
157                }
158            }
159        } else {
160            impl Features {
161                pub(super) fn new_no_features_to_detect() -> Self {
162                    Self(NotSend::VALUE)
163                }
164            }
165        }
166    }
167
168    pub struct Values {
169        values: u32,
170        cpu: Features,
171    }
172
173    impl Values {
174        #[inline(always)]
175        pub(super) fn values(&self) -> u32 {
176            self.values
177        }
178
179        #[inline(always)]
180        pub(super) fn cpu(&self) -> Features {
181            self.cpu
182        }
183    }
184}
185
186const _: () = assert!(size_of::<Features>() == 0);
187
188cfg_if::cfg_if! {
189    if #[cfg(any(all(target_arch = "aarch64", target_endian = "little"), all(target_arch = "arm", target_endian = "little")))] {
190        pub mod arm;
191        use arm::featureflags;
192    } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
193        pub mod intel;
194        use intel::featureflags;
195    } else {
196        mod featureflags {
197            use super::Features;
198
199            #[inline(always)]
200            pub(super) fn get_or_init() -> Features {
201                Features::new_no_features_to_detect()
202            }
203
204            #[inline(always)]
205            pub(super) fn get(_cpu_features: Features) -> u32 {
206                STATIC_DETECTED
207            }
208
209            pub(super) const STATIC_DETECTED: u32 = 0;
210            pub(super) const FORCE_DYNAMIC_DETECTION: u32 = 0;
211        }
212    }
213}
214
215const CAPS_STATIC: u32 = featureflags::STATIC_DETECTED & !featureflags::FORCE_DYNAMIC_DETECTION;
216
217#[allow(clippy::assertions_on_constants, clippy::bad_bit_mask)]
218const _FORCE_DYNAMIC_DETECTION_HONORED: () =
219    assert!((CAPS_STATIC & featureflags::FORCE_DYNAMIC_DETECTION) == 0);
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    #[test]
226    fn test_static_is_subset_of_dynamic() {
227        let cpu = features();
228        let dynamic = featureflags::get(cpu);
229        assert_eq!(dynamic & CAPS_STATIC, CAPS_STATIC);
230    }
231}