1pub(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 { 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 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 #[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 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}