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 AUTHORS DISCLAIM ALL WARRANTIES |
8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 | |
15 | pub(crate) use self::features::Features; |
16 | use core::mem::size_of; |
17 | |
18 | macro_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 | |
76 | pub(crate) trait GetFeature<T> { |
77 | fn get_feature(&self) -> Option<T>; |
78 | } |
79 | |
80 | impl GetFeature<()> for features::Values { |
81 | #[inline (always)] |
82 | fn get_feature(&self) -> Option<()> { |
83 | Some(()) |
84 | } |
85 | } |
86 | |
87 | impl<A, B> GetFeature<(A, B)> for features::Values |
88 | where |
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: A), Some(b: B)) => Some((a, b)), |
96 | _ => None, |
97 | } |
98 | } |
99 | } |
100 | |
101 | impl<A, B, C> GetFeature<(A, B, C)> for features::Values |
102 | where |
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: A), Some(b: B), Some(c: C)) => Some((a, b, c)), |
111 | _ => None, |
112 | } |
113 | } |
114 | } |
115 | |
116 | impl<F> GetFeature<F> for Features |
117 | where |
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)] |
127 | pub(crate) fn features() -> Features { |
128 | featureflags::get_or_init() |
129 | } |
130 | |
131 | mod 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 | |
186 | const _: () = assert!(size_of::<Features>() == 0); |
187 | |
188 | cfg_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 | |
215 | const CAPS_STATIC: u32 = featureflags::STATIC_DETECTED & !featureflags::FORCE_DYNAMIC_DETECTION; |
216 | |
217 | #[allow (clippy::assertions_on_constants, clippy::bad_bit_mask)] |
218 | const _FORCE_DYNAMIC_DETECTION_HONORED: () = |
219 | assert!((CAPS_STATIC & featureflags::FORCE_DYNAMIC_DETECTION) == 0); |
220 | |
221 | #[cfg (test)] |
222 | mod 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 | } |
232 | |