|
2 | 2 | // SPDX-FileCopyrightText: Copyright the Vortex contributors |
3 | 3 |
|
4 | 4 | use super::count_ones::align_offset_len; |
| 5 | +#[cfg(target_arch = "x86_64")] |
| 6 | +use crate::dispatch::CpuDispatch; |
5 | 7 |
|
6 | 8 | /// Returns the position of the `nth` set bit (0-indexed) within the logical range |
7 | 9 | /// `[offset, offset + len)` of the given byte slice. |
@@ -84,34 +86,43 @@ pub fn bit_select(bytes: &[u8], offset: usize, len: usize, nth: usize) -> Option |
84 | 86 | /// is the rank *within* that chunk. Otherwise all chunks were consumed. |
85 | 87 | #[inline] |
86 | 88 | fn scan_chunks(chunks: &[[u8; 64]], remaining: usize, pos: usize) -> (usize, usize, usize) { |
87 | | - scan_chunks_impl(chunks, remaining, pos) |
88 | | -} |
89 | | - |
90 | | -#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] |
91 | | -#[inline] |
92 | | -fn scan_chunks_impl(chunks: &[[u8; 64]], remaining: usize, pos: usize) -> (usize, usize, usize) { |
93 | | - scan_chunks_scalar(chunks, remaining, pos) |
94 | | -} |
95 | | - |
96 | | -#[cfg(target_arch = "x86_64")] |
97 | | -crate::cpu_dispatch! { |
98 | | - fn scan_chunks_impl(chunks: &[[u8; 64]], remaining: usize, pos: usize) -> (usize, usize, usize); |
99 | | - select { |
100 | | - if is_x86_feature_detected!("avx512f") && is_x86_feature_detected!("avx512vpopcntdq") { |
101 | | - // SAFETY: runtime detection guarantees the required target features. |
102 | | - return |chunks, remaining, pos| unsafe { |
103 | | - scan_chunks_avx512_vpopcnt(chunks, remaining, pos) |
| 89 | + #[cfg(target_arch = "x86_64")] |
| 90 | + { |
| 91 | + type ScanChunks = fn(&[[u8; 64]], usize, usize) -> (usize, usize, usize); |
| 92 | + static SELECTED: CpuDispatch<ScanChunks> = CpuDispatch::new(resolve); |
| 93 | + |
| 94 | + #[cold] |
| 95 | + fn resolve(chunks: &[[u8; 64]], remaining: usize, pos: usize) -> (usize, usize, usize) { |
| 96 | + let kernel: ScanChunks = if is_x86_feature_detected!("avx512f") |
| 97 | + && is_x86_feature_detected!("avx512vpopcntdq") |
| 98 | + { |
| 99 | + // SAFETY: runtime detection guarantees the required target features. |
| 100 | + |chunks, remaining, pos| unsafe { |
| 101 | + scan_chunks_avx512_vpopcnt(chunks, remaining, pos) |
| 102 | + } |
| 103 | + } else { |
| 104 | + scan_chunks_scalar |
104 | 105 | }; |
| 106 | + SELECTED.set(kernel); |
| 107 | + kernel(chunks, remaining, pos) |
105 | 108 | } |
106 | 109 |
|
107 | | - scan_chunks_scalar |
| 110 | + SELECTED.get()(chunks, remaining, pos) |
| 111 | + } |
| 112 | + #[cfg(target_arch = "aarch64")] |
| 113 | + { |
| 114 | + scan_chunks_neon(chunks, remaining, pos) |
| 115 | + } |
| 116 | + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] |
| 117 | + { |
| 118 | + scan_chunks_scalar(chunks, remaining, pos) |
108 | 119 | } |
109 | 120 | } |
110 | 121 |
|
111 | 122 | #[cfg(target_arch = "aarch64")] |
112 | 123 | #[allow(clippy::cast_possible_truncation)] // u64 → usize is lossless on aarch64 (64-bit) |
113 | 124 | #[inline] |
114 | | -fn scan_chunks_impl( |
| 125 | +fn scan_chunks_neon( |
115 | 126 | chunks: &[[u8; 64]], |
116 | 127 | mut remaining: usize, |
117 | 128 | mut pos: usize, |
@@ -285,26 +296,32 @@ fn scan_words_scalar( |
285 | 296 | // ── In-chunk select ───────────────────────────────────────────────────── |
286 | 297 |
|
287 | 298 | /// Position of the `nth` set bit inside a 64-byte chunk (0-indexed). |
288 | | -#[cfg(not(target_arch = "x86_64"))] |
289 | 299 | #[inline] |
290 | 300 | fn select_in_chunk(chunk: &[u8; 64], nth: usize) -> usize { |
291 | | - select_in_chunk_scalar(chunk, nth) |
292 | | -} |
293 | | - |
294 | | -#[cfg(target_arch = "x86_64")] |
295 | | -crate::cpu_dispatch! { |
296 | | - /// Position of the `nth` set bit inside a 64-byte chunk (0-indexed). |
297 | | - fn select_in_chunk(chunk: &[u8; 64], nth: usize) -> usize; |
298 | | - select { |
299 | | - if is_x86_feature_detected!("avx512f") |
300 | | - && is_x86_feature_detected!("avx512vpopcntdq") |
301 | | - && is_x86_feature_detected!("avx512vbmi2") |
302 | | - { |
303 | | - // SAFETY: runtime detection guarantees the required target features. |
304 | | - return |chunk, nth| unsafe { select_in_chunk_vbmi2(chunk, nth) }; |
| 301 | + #[cfg(target_arch = "x86_64")] |
| 302 | + { |
| 303 | + static SELECTED: CpuDispatch<fn(&[u8; 64], usize) -> usize> = CpuDispatch::new(resolve); |
| 304 | + |
| 305 | + #[cold] |
| 306 | + fn resolve(chunk: &[u8; 64], nth: usize) -> usize { |
| 307 | + let kernel: fn(&[u8; 64], usize) -> usize = if is_x86_feature_detected!("avx512f") |
| 308 | + && is_x86_feature_detected!("avx512vpopcntdq") |
| 309 | + && is_x86_feature_detected!("avx512vbmi2") |
| 310 | + { |
| 311 | + // SAFETY: runtime detection guarantees the required target features. |
| 312 | + |chunk, nth| unsafe { select_in_chunk_vbmi2(chunk, nth) } |
| 313 | + } else { |
| 314 | + select_in_chunk_scalar |
| 315 | + }; |
| 316 | + SELECTED.set(kernel); |
| 317 | + kernel(chunk, nth) |
305 | 318 | } |
306 | 319 |
|
307 | | - select_in_chunk_scalar |
| 320 | + SELECTED.get()(chunk, nth) |
| 321 | + } |
| 322 | + #[cfg(not(target_arch = "x86_64"))] |
| 323 | + { |
| 324 | + select_in_chunk_scalar(chunk, nth) |
308 | 325 | } |
309 | 326 | } |
310 | 327 |
|
@@ -371,22 +388,29 @@ fn count_ones_chunk(chunk: &[u8; 64]) -> usize { |
371 | 388 | // ── In-word select ────────────────────────────────────────────────────── |
372 | 389 |
|
373 | 390 | /// Position of the `nth` set bit inside a u64 (0-indexed, little-endian bit order). |
374 | | -#[cfg(not(target_arch = "x86_64"))] |
375 | 391 | #[inline] |
376 | 392 | fn select_in_word(word: u64, nth: usize) -> usize { |
377 | | - select_in_word_scalar(word, nth) |
378 | | -} |
379 | | - |
380 | | -#[cfg(target_arch = "x86_64")] |
381 | | -crate::cpu_dispatch! { |
382 | | - /// Position of the `nth` set bit inside a u64 (0-indexed, little-endian bit order). |
383 | | - fn select_in_word(word: u64, nth: usize) -> usize; |
384 | | - select { |
385 | | - if is_x86_feature_detected!("bmi2") { |
386 | | - // SAFETY: runtime detection guarantees the required target feature. |
387 | | - return |word, nth| unsafe { select_in_word_bmi2(word, nth) }; |
| 393 | + #[cfg(target_arch = "x86_64")] |
| 394 | + { |
| 395 | + static SELECTED: CpuDispatch<fn(u64, usize) -> usize> = CpuDispatch::new(resolve); |
| 396 | + |
| 397 | + #[cold] |
| 398 | + fn resolve(word: u64, nth: usize) -> usize { |
| 399 | + let kernel: fn(u64, usize) -> usize = if is_x86_feature_detected!("bmi2") { |
| 400 | + // SAFETY: runtime detection guarantees the required target feature. |
| 401 | + |word, nth| unsafe { select_in_word_bmi2(word, nth) } |
| 402 | + } else { |
| 403 | + select_in_word_scalar |
| 404 | + }; |
| 405 | + SELECTED.set(kernel); |
| 406 | + kernel(word, nth) |
388 | 407 | } |
389 | | - select_in_word_scalar |
| 408 | + |
| 409 | + SELECTED.get()(word, nth) |
| 410 | + } |
| 411 | + #[cfg(not(target_arch = "x86_64"))] |
| 412 | + { |
| 413 | + select_in_word_scalar(word, nth) |
390 | 414 | } |
391 | 415 | } |
392 | 416 |
|
|
0 commit comments