1#[cfg(asm_neon)]
2pub use neon::*;
3#[cfg(not(any(asm_x86_64, asm_neon)))]
4pub use rust::*;
5#[cfg(asm_x86_64)]
6pub use x86::*;
7
8#[cfg(not(any(asm_x86_64, asm_neon)))]
9mod rust {
10 use arg_enum_proc_macro::ArgEnum;
11
12 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, ArgEnum, Default)]
13 #[allow(clippy::upper_case_acronyms)]
14
15 pub enum CpuFeatureLevel {
16 #[default]
17 RUST,
18 }
19
20 impl CpuFeatureLevel {
21 #[cfg(test)]
22 #[allow(unused)]
23 #[inline]
24 pub const fn all() -> &'static [Self] {
25 use CpuFeatureLevel::*;
26 &[RUST]
27 }
28 }
29}
30
31#[cfg(asm_x86_64)]
32#[macro_use]
33mod x86 {
34 use std::{env, str::FromStr};
35
36 use arg_enum_proc_macro::ArgEnum;
37
38 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, ArgEnum)]
39 #[allow(clippy::upper_case_acronyms)]
40 pub enum CpuFeatureLevel {
41 RUST,
42 SSE2,
43 SSSE3,
44 #[arg_enum(alias = "sse4.1")]
45 SSE4_1,
46 AVX2,
47 AVX512,
48 #[arg_enum(alias = "avx512vpclmulqdq")]
49 AVX512ICL,
50 }
51
52 impl CpuFeatureLevel {
53 #[cfg(test)]
54 pub const fn all() -> &'static [Self] {
55 &[
56 CpuFeatureLevel::RUST,
57 CpuFeatureLevel::SSE2,
58 CpuFeatureLevel::SSSE3,
59 CpuFeatureLevel::SSE4_1,
60 CpuFeatureLevel::AVX2,
61 CpuFeatureLevel::AVX512,
62 CpuFeatureLevel::AVX512ICL,
63 ]
64 }
65
66 #[inline]
67 pub const fn len() -> usize {
68 CpuFeatureLevel::AVX512ICL as usize + 1
69 }
70
71 #[inline]
72 pub const fn as_index(self) -> usize {
73 self as usize
74 }
75 }
76
77 impl Default for CpuFeatureLevel {
78 #[inline]
79 fn default() -> CpuFeatureLevel {
80 fn avx512_detected() -> bool {
81 is_x86_feature_detected!("avx512bw")
82 && is_x86_feature_detected!("avx512cd")
83 && is_x86_feature_detected!("avx512dq")
84 && is_x86_feature_detected!("avx512f")
85 && is_x86_feature_detected!("avx512vl")
86 }
87 #[allow(deprecated)] fn avx512icl_detected() -> bool {
89 avx512_detected()
91 && is_x86_feature_detected!("avx512vnni")
92 && is_x86_feature_detected!("avx512ifma")
93 && is_x86_feature_detected!("avx512vbmi")
94 && is_x86_feature_detected!("avx512vbmi2")
95 && is_x86_feature_detected!("avx512vpopcntdq")
96 && is_x86_feature_detected!("avx512bitalg")
97 && is_x86_feature_detected!("avx512gfni")
98 && is_x86_feature_detected!("avx512vaes")
99 && is_x86_feature_detected!("avx512vpclmulqdq")
100 }
101
102 let detected: CpuFeatureLevel = if avx512icl_detected() {
103 CpuFeatureLevel::AVX512ICL
104 } else if avx512_detected() {
105 CpuFeatureLevel::AVX512
106 } else if is_x86_feature_detected!("avx2") {
107 CpuFeatureLevel::AVX2
108 } else if is_x86_feature_detected!("sse4.1") {
109 CpuFeatureLevel::SSE4_1
110 } else if is_x86_feature_detected!("ssse3") {
111 CpuFeatureLevel::SSSE3
112 } else if is_x86_feature_detected!("sse2") {
113 CpuFeatureLevel::SSE2
114 } else {
115 CpuFeatureLevel::RUST
116 };
117 let manual: CpuFeatureLevel = match env::var("CPU_TARGET") {
118 Ok(feature) => CpuFeatureLevel::from_str(&feature).unwrap_or(detected),
119 Err(_e) => detected,
120 };
121 if manual > detected {
122 detected
123 } else {
124 manual
125 }
126 }
127 }
128
129 macro_rules! cpu_function_lookup_table {
132 ($name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
134 static $name: [$type; crate::cpu::CpuFeatureLevel::len()] = {
135 use crate::cpu::CpuFeatureLevel;
136 #[allow(unused_mut)]
137 let mut out: [$type; CpuFeatureLevel::len()] = [$empty; CpuFeatureLevel::len()];
138
139 #[allow(unused_mut)]
141 let mut set: [bool; CpuFeatureLevel::len()] = [false; CpuFeatureLevel::len()];
142
143 #[allow(unused_imports)]
144 use CpuFeatureLevel::*;
145 $(
146 out[$key as usize] = $value;
147 set[$key as usize] = true;
148 )*
149 cpu_function_lookup_table!(waterfall_cpu_features(out, set, [SSE2, SSSE3, SSE4_1, AVX2, AVX512, AVX512ICL]));
150 out
151 };
152 };
153 ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
154 $pub cpu_function_lookup_table!($name: [$type], default: $empty, [$(($key, $value)),*]);
155 };
156
157 (waterfall_cpu_features($out:ident, $set:ident, [$($cpu:ident),*])) => {
162 #[allow(unused_assignments)]
166 let mut best = [$out[0], $out[0]];
167 $(
168 best[$set[$cpu as usize] as usize] = $out[$cpu as usize];
170 $out[$cpu as usize] = best[1];
172 )*
173 };
174
175 ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
177 pastey::item!{
178 cpu_function_lookup_table!(
179 $pub, $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
180 );
181 }
182 };
183
184 ($name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
186 pastey::item!{
187 cpu_function_lookup_table!(
188 $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
189 );
190 }
191 };
192 }
193}
194
195#[cfg(asm_neon)]
196#[macro_use]
197mod neon {
198 use std::{env, str::FromStr};
199
200 use arg_enum_proc_macro::ArgEnum;
201
202 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, ArgEnum)]
203 #[allow(clippy::upper_case_acronyms)]
204 pub enum CpuFeatureLevel {
205 RUST,
206 NEON,
207 }
208
209 impl CpuFeatureLevel {
210 #[cfg(test)]
211 #[inline]
212 pub const fn all() -> &'static [Self] {
213 use CpuFeatureLevel::*;
214 &[RUST, NEON]
215 }
216
217 #[inline]
218 pub const fn len() -> usize {
219 CpuFeatureLevel::NEON as usize + 1
220 }
221
222 #[inline]
223 pub fn as_index(self) -> usize {
224 self as usize
225 }
226 }
227
228 impl Default for CpuFeatureLevel {
229 #[inline]
230 fn default() -> CpuFeatureLevel {
231 let detected = CpuFeatureLevel::NEON;
232 let manual: CpuFeatureLevel = match env::var("CPU_TARGET") {
233 Ok(feature) => CpuFeatureLevel::from_str(&feature).unwrap_or(detected),
234 Err(_e) => detected,
235 };
236 if manual > detected {
237 detected
238 } else {
239 manual
240 }
241 }
242 }
243
244 macro_rules! cpu_function_lookup_table {
247 ($name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
249 static $name: [$type; crate::cpu::CpuFeatureLevel::len()] = {
250 use crate::cpu::CpuFeatureLevel;
251 #[allow(unused_mut)]
252 let mut out: [$type; CpuFeatureLevel::len()] = [$empty; CpuFeatureLevel::len()];
253
254 #[allow(unused_mut)]
256 let mut set: [bool; CpuFeatureLevel::len()] = [false; CpuFeatureLevel::len()];
257
258 #[allow(unused_imports)]
259 use CpuFeatureLevel::*;
260 $(
261 out[$key as usize] = $value;
262 set[$key as usize] = true;
263 )*
264 cpu_function_lookup_table!(waterfall_cpu_features(out, set, [NEON]));
265 out
266 };
267 };
268
269 ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$(($key:ident, $value:expr)),*]) => {
270 $pub cpu_function_lookup_table!($name: [$type], default: $empty, [$(($key, $value)),*]);
271 };
272 (waterfall_cpu_features($out:ident, $set:ident, [$($cpu:ident),*])) => {
277 #[allow(unused_assignments)]
281 let mut best = [$out[0], $out[0]];
282 $(
283 best[$set[$cpu as usize] as usize] = $out[$cpu as usize];
285 $out[$cpu as usize] = best[1];
287 )*
288 };
289
290
291 ($pub:vis, $name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
293 pastey::item!{
294 cpu_function_lookup_table!(
295 $pub, $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
296 );
297 }
298 };
299
300 ($name:ident: [$type:ty], default: $empty:expr, [$($key:ident),*]) => {
302 pastey::item!{
303 cpu_function_lookup_table!(
304 $name: [$type], default: $empty, [$(($key, [<$name _$key>])),*]
305 );
306 }
307 };
308 }
309}