1#[cfg(target_arch = "x86_64")]
4use core::arch::x86_64;
5
6#[repr(u8)]
8#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9#[cfg(target_arch = "x86_64")]
10pub enum ISAExtension {
11 SSE2,
13
14 SSSE3,
16
17 SSE4_2,
19
20 AVX2,
22}
23
24#[repr(u8)]
26#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
27#[cfg(target_arch = "aarch64")]
28pub enum ISAExtension {
29 NEON,
31}
32
33#[inline(always)]
44#[cfg(target_arch = "aarch64")]
45pub fn detect_best_isa_extension() -> ISAExtension {
46 ISAExtension::NEON
57}
58
59#[inline(always)]
70#[cfg(target_arch = "x86_64")]
71pub fn detect_best_isa_extension() -> ISAExtension {
72 #[allow(unused_unsafe)]
78 let cpuid1 = unsafe { x86_64::__cpuid(1) };
79
80 let has_ssse3 = (cpuid1.ecx & (1 << 9)) != 0;
81 let has_sse4_2 = (cpuid1.ecx & (1 << 0x14)) != 0;
82
83 let osxsave = (cpuid1.ecx & (1 << 0x1B)) != 0;
84 if osxsave {
85 let xcr0 = unsafe { x86_64::_xgetbv(0) };
86 let xmm_ymm_enabled = (xcr0 & 0b110) == 0b110;
87
88 if xmm_ymm_enabled {
89 #[allow(unused_unsafe)]
95 let cpuid7 = unsafe { x86_64::__cpuid_count(7, 0) };
96
97 if (cpuid7.ebx & (1 << 5)) != 0 {
98 return ISAExtension::AVX2;
99 }
100 }
101 }
102
103 if has_sse4_2 {
104 return ISAExtension::SSE4_2;
105 }
106
107 if has_ssse3 {
108 return ISAExtension::SSSE3;
109 }
110
111 ISAExtension::SSE2
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 mod enum_invariants {
129 use super::*;
130 use core::mem::{align_of, size_of};
131
132 #[test]
133 fn ok_enum_representation_and_layout() {
134 assert_eq!(size_of::<ISAExtension>(), 1);
135 assert_eq!(align_of::<ISAExtension>(), 1);
136 }
137
138 #[test]
139 #[cfg(target_arch = "x86_64")]
140 fn ok_extension_monotonic_ordering() {
141 assert!(ISAExtension::SSE2 < ISAExtension::SSSE3);
142 assert!(ISAExtension::SSSE3 < ISAExtension::SSE4_2);
143 assert!(ISAExtension::SSE4_2 < ISAExtension::AVX2);
144 }
145
146 #[test]
147 fn ok_hashing_and_equality() {
148 use std::collections::hash_map::DefaultHasher;
149 use std::hash::{Hash, Hasher};
150
151 fn hash_val<T: Hash>(val: &T) -> u64 {
152 let mut hasher = DefaultHasher::new();
153 val.hash(&mut hasher);
154 hasher.finish()
155 }
156
157 let a = detect_best_isa_extension();
158 let b = detect_best_isa_extension();
159 assert_eq!(a, b);
160 assert_eq!(hash_val(&a), hash_val(&b));
161 }
162 }
163
164 mod detection {
165 use super::*;
166
167 #[test]
168 fn ok_detection_is_deterministic_and_idempotent() {
169 let first = detect_best_isa_extension();
170 for _ in 0..0x64 {
171 assert_eq!(detect_best_isa_extension(), first);
172 }
173 }
174
175 #[test]
176 fn ok_detection_across_threads() {
177 use std::thread;
178
179 let expected = detect_best_isa_extension();
180 let handles: Vec<_> = (0..8)
181 .map(|_| {
182 thread::spawn(move || {
183 for _ in 0..0x32 {
184 assert_eq!(detect_best_isa_extension(), expected);
185 }
186 })
187 })
188 .collect();
189
190 for handle in handles {
191 handle.join().unwrap();
192 }
193 }
194
195 #[test]
196 #[cfg(target_arch = "x86_64")]
197 fn ok_meets_x86_64_baseline() {
198 let detected = detect_best_isa_extension();
199 assert!(detected >= ISAExtension::SSE2);
200 }
201
202 #[test]
203 #[cfg(target_arch = "aarch64")]
204 fn ok_meets_aarch64_baseline() {
205 let detected = detect_best_isa_extension();
206 assert_eq!(detected, ISAExtension::NEON);
207 }
208
209 #[test]
210 #[cfg(target_arch = "x86_64")]
211 fn ok_cross_validate_with_std_feature_detection() {
212 let detected = detect_best_isa_extension();
213
214 assert!(std::is_x86_feature_detected!("sse2"));
215
216 match detected {
217 ISAExtension::AVX2 => {
218 assert!(std::is_x86_feature_detected!("avx2"));
219 assert!(std::is_x86_feature_detected!("sse4.2"));
220 assert!(std::is_x86_feature_detected!("ssse3"));
221 }
222 ISAExtension::SSE4_2 => {
223 assert!(std::is_x86_feature_detected!("sse4.2"));
224 assert!(std::is_x86_feature_detected!("ssse3"));
225 }
226 ISAExtension::SSSE3 => {
227 assert!(std::is_x86_feature_detected!("ssse3"));
228 }
229 ISAExtension::SSE2 => {}
230 }
231 }
232
233 #[test]
234 #[cfg(target_arch = "aarch64")]
235 fn ok_cross_validate_with_std_feature_detection() {
236 assert!(std::arch::is_aarch64_feature_detected!("neon"));
237 }
238 }
239
240 #[allow(unsafe_op_in_unsafe_fn)]
241 mod execution_smoke {
242 use super::*;
243
244 #[test]
245 #[cfg(target_arch = "x86_64")]
246 fn ok_execute_detected_isa_instructions() {
247 let detected = detect_best_isa_extension();
248
249 #[target_feature(enable = "sse2")]
250 unsafe fn exec_sse2() {
251 let a = core::arch::x86_64::_mm_set1_epi32(0x2A);
252 let b = core::arch::x86_64::_mm_set1_epi32(0x0A);
253 let c = core::arch::x86_64::_mm_add_epi32(a, b);
254
255 let mut out = [0i32; 4];
256 unsafe {
257 core::arch::x86_64::_mm_storeu_si128(
258 out.as_mut_ptr() as *mut _,
259 c,
260 );
261 }
262 assert_eq!(out, [0x34; 4]);
263 }
264
265 #[target_feature(enable = "ssse3")]
266 unsafe fn exec_ssse3() {
267 let val = core::arch::x86_64::_mm_set1_epi32(0x01020304);
268 let mask = core::arch::x86_64::_mm_setzero_si128();
269 let res = core::arch::x86_64::_mm_shuffle_epi8(val, mask);
270
271 let mut out = [0u8; 0x10];
272 unsafe {
273 core::arch::x86_64::_mm_storeu_si128(
274 out.as_mut_ptr() as *mut _,
275 res,
276 );
277 }
278 assert_eq!(out[0], 0x04);
279 }
280
281 #[target_feature(enable = "sse4.2")]
282 unsafe fn exec_sse4_2() {
283 let crc =
284 core::arch::x86_64::_mm_crc32_u64(!0, 0x123456789ABCDEF0);
285 assert_ne!(crc, 0);
286 }
287
288 #[target_feature(enable = "avx2")]
289 unsafe fn exec_avx2() {
290 let a = core::arch::x86_64::_mm256_set1_epi32(0x64);
291 let b = core::arch::x86_64::_mm256_set1_epi32(0xC8);
292 let c = core::arch::x86_64::_mm256_add_epi32(a, b);
293
294 let mut out = [0i32; 8];
295 unsafe {
296 core::arch::x86_64::_mm256_storeu_si256(
297 out.as_mut_ptr() as *mut _,
298 c,
299 );
300 }
301 assert_eq!(out, [0x12C; 8]);
302 }
303
304 unsafe {
305 if detected >= ISAExtension::SSE2 {
306 exec_sse2();
307 }
308
309 if detected >= ISAExtension::SSSE3 {
310 exec_ssse3();
311 }
312
313 if detected >= ISAExtension::SSE4_2 {
314 exec_sse4_2();
315 }
316
317 if detected >= ISAExtension::AVX2 {
318 exec_avx2();
319 }
320 }
321 }
322
323 #[test]
324 #[cfg(target_arch = "aarch64")]
325 fn ok_execute_detected_isa_instructions() {
326 let detected = detect_best_isa_extension();
327 assert_eq!(detected, ISAExtension::NEON);
328
329 #[target_feature(enable = "neon")]
330 unsafe fn exec_neon() {
331 let v = core::arch::aarch64::vdupq_n_u8(0x42);
332 let mut out = [0u8; 0x10];
333
334 unsafe {
335 core::arch::aarch64::vst1q_u8(out.as_mut_ptr(), v);
336 }
337 assert_eq!(out, [0x42; 0x10]);
338 }
339
340 unsafe {
341 exec_neon();
342 }
343 }
344 }
345}