1use std::error::Error;
4use std::fmt::{self, Display, Formatter};
5use std::str::FromStr;
6use std::sync::{LazyLock, OnceLock};
7
8#[macro_export]
51macro_rules! multiversion {
52 (
54 use {$($($path:ident::)+*),*};
55
56 #[dyn_dispatch = $dispatch:path]
57 $(#[$m:meta])* $v:vis fn $name:ident($($arg_name:ident: $arg_type:ty),*$(,)?) $(-> $ret:ty)? $body:block
58
59 $($tail:tt)*
60 ) => {
61 mod $name {
63 #[allow(clippy::allow_attributes, unused_imports)]
64 use {super::*, $crate::multiversion}; $crate::multiversion!{
67 use {$($($path::)+*),*};
68 $(#[$m])* pub fn $name($($arg_name: $arg_type),*) $(-> $ret)? $body
69 $($tail)*
70 }
71 }
72
73 #[inline]
75 $v fn $name($($arg_name: $arg_type),*) $(-> $ret)? {
76 use $crate::multiversion::Version::*;
77
78 match *$dispatch {
79 Scalar => $name::scalar::$name($($arg_name),*),
80 Array128 => $name::array128::$name($($arg_name),*),
81 Array256 => $name::array256::$name($($arg_name),*),
82 #[cfg(feature = "all-simd")]
83 Array4096 => $name::array4096::$name($($arg_name),*),
84 #[cfg(all(feature = "unsafe", any(target_arch = "x86", target_arch = "x86_64")))]
85 AVX2 => unsafe { $name::avx2::$name($($arg_name),*) },
86 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
87 AVX2x2 => unsafe { $name::avx2x2::$name($($arg_name),*) },
88 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
89 AVX2x4 => unsafe { $name::avx2x4::$name($($arg_name),*) },
90 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
91 AVX2x8 => unsafe { $name::avx2x8::$name($($arg_name),*) },
92 #[cfg(all(feature = "unsafe", target_arch = "aarch64"))]
93 Neon => $name::neon::$name($($arg_name),*),
94 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
95 Neonx2 => $name::neonx2::$name($($arg_name),*),
96 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
97 Neonx4 => $name::neonx4::$name($($arg_name),*),
98 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
99 Neonx8 => $name::neonx8::$name($($arg_name),*),
100 }
101 }
102 };
103
104 (
106 use {$($($path:ident::)+*),*};
107
108 $($tail:tt)+
109 ) => {
110 pub mod scalar {
112 #![allow(
113 clippy::reversed_empty_ranges,
114 clippy::range_plus_one,
115 clippy::modulo_one,
116 clippy::trivially_copy_pass_by_ref
117 )]
118
119 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
120 use {super::*, $($($path::)+scalar::*),*};
121
122 $($tail)*
123 }
124
125 pub mod array128 {
127 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
128 use {super::*, $($($path::)+array128::*),*};
129
130 $($tail)*
131 }
132
133 pub mod array256 {
135 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
136 use {super::*, $($($path::)+array256::*),*};
137
138 $($tail)*
139 }
140
141 #[cfg(feature="all-simd")]
143 pub mod array4096 {
144 #![allow(clippy::large_types_passed_by_value)]
145
146 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
147 use {super::*, $($($path::)+array4096::*),*};
148
149 $($tail)*
150 }
151
152 #[cfg(all(feature = "unsafe", any(target_arch = "x86", target_arch = "x86_64")))]
154 pub mod avx2 {
155 #![allow(clippy::missing_safety_doc)]
156
157 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
158 use {super::*, $($($path::)+avx2::*),*};
159
160 $crate::multiversion!{@helper target_feature(enable = "avx2") $($tail)*}
161 }
162
163 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
165 pub mod avx2x2 {
166 #![allow(clippy::missing_safety_doc)]
167
168 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
169 use {super::*, $($($path::)+avx2x2::*),*};
170
171 $crate::multiversion!{@helper target_feature(enable = "avx2") $($tail)*}
172 }
173
174 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
176 pub mod avx2x4 {
177 #![allow(clippy::missing_safety_doc)]
178
179 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
180 use {super::*, $($($path::)+avx2x4::*),*};
181
182 $crate::multiversion!{@helper target_feature(enable = "avx2") $($tail)*}
183 }
184
185 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
187 pub mod avx2x8 {
188 #![allow(clippy::missing_safety_doc)]
189
190 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
191 use {super::*, $($($path::)+avx2x8::*),*};
192
193 $crate::multiversion!{@helper target_feature(enable = "avx2") $($tail)*}
194 }
195
196 #[cfg(all(feature = "unsafe", target_arch = "aarch64"))]
198 pub mod neon {
199 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
200 use {super::*, $($($path::)+neon::*),*};
201
202 $($tail)*
203 }
204
205 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
207 pub mod neonx2 {
208 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
209 use {super::*, $($($path::)+neonx2::*),*};
210
211 $($tail)*
212 }
213
214 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
216 pub mod neonx4 {
217 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
218 use {super::*, $($($path::)+neonx4::*),*};
219
220 $($tail)*
221 }
222
223 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
225 pub mod neonx8 {
226 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
227 use {super::*, $($($path::)+neonx8::*),*};
228
229 $($tail)*
230 }
231 };
232
233 (fastest($name:ident())) => {
235 ::std::sync::LazyLock::new(#[cfg_attr(target_family = "wasm", expect(unreachable_code))] || {
236 use $crate::multiversion::Version::*;
237
238 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
240 return Array256;
241 #[cfg(all(target_family = "wasm"))]
242 return Array128;
243
244 if let Some(version) = $crate::multiversion::Version::get_override() {
245 return version;
246 }
247
248 $crate::multiversion::VERSIONS
249 .iter()
250 .map(|&x| {
251 let start = ::std::time::Instant::now();
252 ::std::hint::black_box(match x {
253 Scalar => scalar::$name(),
254 Array128 => array128::$name(),
255 Array256 => array256::$name(),
256 #[cfg(feature = "all-simd")]
257 Array4096 => array4096::$name(),
258 #[cfg(all(feature = "unsafe", any(target_arch = "x86", target_arch = "x86_64")))]
259 AVX2 => unsafe { avx2::$name() },
260 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
261 AVX2x2 => unsafe { avx2x2::$name() },
262 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
263 AVX2x4 => unsafe { avx2x4::$name() },
264 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
265 AVX2x8 => unsafe { avx2x8::$name() },
266 #[cfg(all(feature = "unsafe", target_arch = "aarch64"))]
267 Neon => neon::$name(),
268 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
269 Neonx2 => neonx2::$name(),
270 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
271 Neonx4 => neonx4::$name(),
272 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
273 Neonx8 => neonx8::$name(),
274 });
275 (start.elapsed(), x)
276 })
277 .min_by_key(|x| x.0)
279 .unwrap()
280 .1
281 })
282 };
283
284 (@helper $t:meta) => {};
287 (@helper $t:meta #[inline(always)] $($tail:tt)*) => {$crate::multiversion!{@helper $t #[inline] $($tail)*}};
289 (@helper $t:meta $(#[$m:meta])* $v:vis const $n:ident: $ty:ty = $e:expr; $($tail:tt)*) => {$(#[$m])* $v const $n: $ty = $e; $crate::multiversion!{@helper $t $($tail)*}};
290 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
291 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
292 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
293 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
294 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
295 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
296 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
297 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
298 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
299 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
300 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
301 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
302 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
303 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
304 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
305 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
306 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
307 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
308 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
309 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
310 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
311 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
312 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
313 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
314 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
315 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
316 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $t26:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 $t26 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
317 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $t26:tt $t27:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 $t26 $t27 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
318 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $t26:tt $t27:tt $t28:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 $t26 $t27 $t28 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
319 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $t26:tt $t27:tt $t28:tt $t29:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 $t26 $t27 $t28 $t29 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
320 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $t26:tt $t27:tt $t28:tt $t29:tt $t30:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 $t26 $t27 $t28 $t29 $t30 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
321 (@helper $t:meta $(#[$m:meta])* $v:vis fn $n:ident $t0:tt $t1:tt $t2:tt $t3:tt $t4:tt $t5:tt $t6:tt $t7:tt $t8:tt $t9:tt $t10:tt $t11:tt $t12:tt $t13:tt $t14:tt $t15:tt $t16:tt $t17:tt $t18:tt $t19:tt $t20:tt $t21:tt $t22:tt $t23:tt $t24:tt $t25:tt $t26:tt $t27:tt $t28:tt $t29:tt $t30:tt $t31:tt $b:block $($tail:tt)*) => {$(#[$m])* #[$t] $v unsafe fn $n $t0 $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 $t9 $t10 $t11 $t12 $t13 $t14 $t15 $t16 $t17 $t18 $t19 $t20 $t21 $t22 $t23 $t24 $t25 $t26 $t27 $t28 $t29 $t30 $t31 { #[allow(clippy::allow_attributes, clippy::macro_metavars_in_unsafe, unused_unsafe)] unsafe { $b } } $crate::multiversion!{@helper $t $($tail)*}};
322}
323
324#[macro_export]
336macro_rules! multiversion_test {
337 (
338 use {$($($path:ident::)+*),*};
339
340 #[test]
341 $(#[$m:meta])* $v:vis fn multiversion() $body:block
342 ) => {
343 #[test]
344 $(#[$m])*
345 fn scalar() {
346 #![allow(
347 clippy::reversed_empty_ranges,
348 clippy::range_plus_one,
349 clippy::modulo_one,
350 clippy::trivially_copy_pass_by_ref
351 )]
352
353 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
354 use {$($($path::)+scalar::*),*};
355
356 $body
357 }
358
359 #[test]
360 $(#[$m])*
361 fn array128() {
362 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
363 use {$($($path::)+array128::*),*};
364
365 $body
366 }
367
368 #[test]
369 $(#[$m])*
370 fn array256() {
371 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
372 use {$($($path::)+array256::*),*};
373
374 $body
375 }
376
377 #[test]
378 #[cfg(feature = "all-simd")]
379 $(#[$m])*
380 fn array4096() {
381 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
382 use {$($($path::)+array4096::*),*};
383
384 $body
385 }
386
387 #[test]
388 #[cfg(all(feature = "unsafe", any(target_arch = "x86", target_arch = "x86_64")))]
389 $(#[$m])*
390 fn avx2() {
391 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
392 use {$($($path::)+avx2::*),*};
393
394 if !$crate::multiversion::Version::AVX2.supported() {
395 use std::io::{stdout, Write};
396 let _ = writeln!(&mut stdout(), "warning: skipping test in {}::avx2 due to missing avx2 support", module_path!());
397 return;
398 }
399
400 unsafe { $body }
401 }
402
403 #[test]
404 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
405 $(#[$m])*
406 fn avx2x2() {
407 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
408 use {$($($path::)+avx2x2::*),*};
409
410 if !$crate::multiversion::Version::AVX2x2.supported() {
411 use std::io::{stdout, Write};
412 let _ = writeln!(&mut stdout(), "warning: skipping test in {}::avx2x2 due to missing avx2 support", module_path!());
413 return;
414 }
415
416 unsafe { $body }
417 }
418
419 #[test]
420 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
421 $(#[$m])*
422 fn avx2x4() {
423 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
424 use {$($($path::)+avx2x4::*),*};
425
426 if !$crate::multiversion::Version::AVX2x4.supported() {
427 use std::io::{stdout, Write};
428 let _ = writeln!(&mut stdout(), "warning: skipping test in {}::avx2x4 due to missing avx2 support", module_path!());
429 return;
430 }
431
432 unsafe { $body }
433 }
434
435 #[test]
436 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
437 $(#[$m])*
438 fn avx2x8() {
439 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
440 use {$($($path::)+avx2x8::*),*};
441
442 if !$crate::multiversion::Version::AVX2x8.supported() {
443 use std::io::{stdout, Write};
444 let _ = writeln!(&mut stdout(), "warning: skipping test in {}::avx2x8 due to missing avx2 support", module_path!());
445 return;
446 }
447
448 unsafe { $body }
449 }
450
451 #[test]
452 #[cfg(all(feature = "unsafe", target_arch = "aarch64"))]
453 $(#[$m])*
454 fn neon() {
455 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
456 use {$($($path::)+neon::*),*};
457
458 $body
459 }
460
461 #[test]
462 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
463 $(#[$m])*
464 fn neonx2() {
465 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
466 use {$($($path::)+neonx2::*),*};
467
468 $body
469 }
470
471 #[test]
472 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
473 $(#[$m])*
474 fn neonx4() {
475 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
476 use {$($($path::)+neonx4::*),*};
477
478 $body
479 }
480
481 #[test]
482 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
483 $(#[$m])*
484 fn neonx8() {
485 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
486 use {$($($path::)+neonx8::*),*};
487
488 $body
489 }
490 };
491
492 (
493 use {$($($path:ident::)+*),*};
494
495 { $($tail:tt)+ }
496 ) => {
497 #[allow(
498 clippy::reversed_empty_ranges,
499 clippy::range_plus_one,
500 clippy::modulo_one,
501 clippy::trivially_copy_pass_by_ref
502 )]
503 {
504 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
505 use {$($($path::)+scalar::*),*};
506
507 $crate::multiversion_test!(@expr { $($tail)+ });
508 }
509
510 {
511 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
512 use {$($($path::)+array128::*),*};
513
514 $crate::multiversion_test!(@expr { $($tail)+ });
515 }
516
517 {
518 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
519 use {$($($path::)+array256::*),*};
520
521 $crate::multiversion_test!(@expr { $($tail)+ });
522 }
523
524 #[cfg(feature = "all-simd")]
525 #[allow(clippy::large_types_passed_by_value)]
526 {
527 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
528 use {$($($path::)+array4096::*),*};
529
530 $crate::multiversion_test!(@expr { $($tail)+ });
531 }
532
533 #[cfg(all(feature = "unsafe", any(target_arch = "x86", target_arch = "x86_64")))]
534 if $crate::multiversion::Version::AVX2.supported() {
535 unsafe {
536 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
537 use {$($($path::)+avx2::*),*};
538
539 $crate::multiversion_test!(@expr { $($tail)+ });
540 }
541
542 #[cfg(feature = "all-simd")]
543 unsafe {
544 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
545 use {$($($path::)+avx2x2::*),*};
546
547 $crate::multiversion_test!(@expr { $($tail)+ });
548 }
549
550 #[cfg(feature = "all-simd")]
551 unsafe {
552 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
553 use {$($($path::)+avx2x4::*),*};
554
555 $crate::multiversion_test!(@expr { $($tail)+ });
556 }
557
558 #[cfg(feature = "all-simd")]
559 unsafe {
560 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
561 use {$($($path::)+avx2x8::*),*};
562
563 $crate::multiversion_test!(@expr { $($tail)+ });
564 }
565 }
566
567 #[cfg(all(feature = "unsafe", target_arch = "aarch64"))]
568 {
569 {
570 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
571 use {$($($path::)+neon::*),*};
572
573 $crate::multiversion_test!(@expr { $($tail)+ });
574 }
575
576 #[cfg(feature = "all-simd")]
577 {
578 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
579 use {$($($path::)+neonx2::*),*};
580
581 $crate::multiversion_test!(@expr { $($tail)+ });
582 }
583
584 #[cfg(feature = "all-simd")]
585 {
586 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
587 use {$($($path::)+neonx4::*),*};
588
589 $crate::multiversion_test!(@expr { $($tail)+ });
590 }
591
592 #[cfg(feature = "all-simd")]
593 {
594 #[allow(clippy::allow_attributes, unused_imports, clippy::wildcard_imports)]
595 use {$($($path::)+neonx8::*),*};
596
597 $crate::multiversion_test!(@expr { $($tail)+ });
598 }
599 }
600 };
601 (@expr $e:expr) => { $e }
602}
603
604macro_rules! versions_impl {
605 ($($
606 (#[$m:meta])*
607 $name:ident $(if $supported:expr)?,
608 )+) => {
609 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
611 pub enum Version {
612 #[default] $(
614 $(#[$m])*
615 $(#[doc = concat!("Requires `", stringify!($supported), "`.")])?
616 $name,
617 )+
618 }
619
620 impl Version {
621 #[must_use]
623 pub fn supported(self) -> bool {
624 match self {
625 $($(#[$m])* Version::$name => true $(&& $supported)?,)+
626 }
627 }
628 }
629
630 impl FromStr for Version {
631 type Err = UnknownVersion;
632
633 fn from_str(x: &str) -> Result<Self, Self::Err> {
635 $(
636 $(#[$m])*
637 if stringify!($name).eq_ignore_ascii_case(x) { return Ok(Version::$name); }
638 )+
639 Err(UnknownVersion(x.to_string()))
640 }
641 }
642
643 pub static VERSIONS: LazyLock<Vec<Version>> = LazyLock::new(|| {
645 let mut vec = vec![$($(#[$m])* Version::$name,)+];
646 vec.retain(|i| i.supported());
647 vec
648 });
649 };
650}
651versions_impl! {
652 Scalar,
653 Array128,
654 Array256,
655 #[cfg(feature = "all-simd")]
656 Array4096,
657 #[cfg(all(feature = "unsafe", any(target_arch = "x86", target_arch = "x86_64")))]
658 AVX2 if std::arch::is_x86_feature_detected!("avx2"),
659 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
660 AVX2x2 if std::arch::is_x86_feature_detected!("avx2"),
661 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
662 AVX2x4 if std::arch::is_x86_feature_detected!("avx2"),
663 #[cfg(all(feature = "unsafe", feature = "all-simd", any(target_arch = "x86", target_arch = "x86_64")))]
664 AVX2x8 if std::arch::is_x86_feature_detected!("avx2"),
665 #[cfg(all(feature = "unsafe", target_arch = "aarch64"))]
666 Neon,
667 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
668 Neonx2,
669 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
670 Neonx4,
671 #[cfg(all(feature = "unsafe", feature = "all-simd", target_arch = "aarch64"))]
672 Neonx8,
673}
674
675static OVERRIDE: OnceLock<Option<Version>> = OnceLock::new();
676
677impl Version {
678 pub fn get_override() -> Option<Version> {
685 *OVERRIDE.get_or_init(|| None)
686 }
687
688 pub fn set_override(version: Version) {
701 assert!(version.supported(), "{version:?} is not supported!");
702
703 if OVERRIDE.set(Some(version)).is_err() {
704 if Self::get_override().is_none() {
706 panic!("Version::set_override must be called before get_override");
707 } else {
708 panic!("Version::set_override called more than once");
709 }
710 }
711 }
712}
713
714#[derive(Debug)]
716pub struct UnknownVersion(String);
717
718impl Display for UnknownVersion {
719 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
720 write!(f, "unknown function version: {:#}", self.0)
721 }
722}
723
724impl Error for UnknownVersion {}