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(out.as_mut_ptr() as *mut _, c);
258 }
259 assert_eq!(out, [0x34; 4]);
260 }
261
262 #[target_feature(enable = "ssse3")]
263 unsafe fn exec_ssse3() {
264 let val = core::arch::x86_64::_mm_set1_epi32(0x01020304);
265 let mask = core::arch::x86_64::_mm_setzero_si128();
266 let res = core::arch::x86_64::_mm_shuffle_epi8(val, mask);
267
268 let mut out = [0u8; 0x10];
269 unsafe {
270 core::arch::x86_64::_mm_storeu_si128(out.as_mut_ptr() as *mut _, res);
271 }
272 assert_eq!(out[0], 0x04);
273 }
274
275 #[target_feature(enable = "sse4.2")]
276 unsafe fn exec_sse4_2() {
277 let crc = core::arch::x86_64::_mm_crc32_u64(!0, 0x123456789ABCDEF0);
278 assert_ne!(crc, 0);
279 }
280
281 #[target_feature(enable = "avx2")]
282 unsafe fn exec_avx2() {
283 let a = core::arch::x86_64::_mm256_set1_epi32(0x64);
284 let b = core::arch::x86_64::_mm256_set1_epi32(0xC8);
285 let c = core::arch::x86_64::_mm256_add_epi32(a, b);
286
287 let mut out = [0i32; 8];
288 unsafe {
289 core::arch::x86_64::_mm256_storeu_si256(out.as_mut_ptr() as *mut _, c);
290 }
291 assert_eq!(out, [0x12C; 8]);
292 }
293
294 unsafe {
295 if detected >= ISAExtension::SSE2 {
296 exec_sse2();
297 }
298
299 if detected >= ISAExtension::SSSE3 {
300 exec_ssse3();
301 }
302
303 if detected >= ISAExtension::SSE4_2 {
304 exec_sse4_2();
305 }
306
307 if detected >= ISAExtension::AVX2 {
308 exec_avx2();
309 }
310 }
311 }
312
313 #[test]
314 #[cfg(target_arch = "aarch64")]
315 fn ok_execute_detected_isa_instructions() {
316 let detected = detect_best_isa_extension();
317 assert_eq!(detected, ISAExtension::NEON);
318
319 #[target_feature(enable = "neon")]
320 unsafe fn exec_neon() {
321 let v = core::arch::aarch64::vdupq_n_u8(0x42);
322 let mut out = [0u8; 0x10];
323
324 unsafe {
325 core::arch::aarch64::vst1q_u8(out.as_mut_ptr(), v);
326 }
327 assert_eq!(out, [0x42; 0x10]);
328 }
329
330 unsafe {
331 exec_neon();
332 }
333 }
334 }
335}