@@ -259,35 +259,27 @@ unsafe fn aarch64_swizzle<const N: usize>(bytes: Simd<u8, N>, idxs: Simd<u8, N>)
259259#[ inline]
260260#[ allow( clippy:: let_and_return) ]
261261unsafe fn avx2_pshufb ( bytes : Simd < u8 , 32 > , idxs : Simd < u8 , 32 > ) -> Simd < u8 , 32 > {
262- use crate :: simd:: { Select , cmp:: SimdPartialOrd } ;
263262 #[ cfg( target_arch = "x86" ) ]
264263 use core:: arch:: x86;
265264 #[ cfg( target_arch = "x86_64" ) ]
266265 use core:: arch:: x86_64 as x86;
267266 use x86:: _mm256_permute2x128_si256 as avx2_cross_shuffle;
268267 use x86:: _mm256_shuffle_epi8 as avx2_half_pshufb;
269- let mid = Simd :: splat ( 16u8 ) ;
270- let high = mid + mid;
271268 // SAFETY: Caller promised AVX2
272269 unsafe {
273- // This is ordering sensitive, and LLVM will order these how you put them.
274- // Most AVX2 impls use ~5 "ports", and only 1 or 2 are capable of permutes.
275- // But the "compose" step will lower to ops that can also use at least 1 other port.
276- // So this tries to break up permutes so composition flows through "open" ports.
277- // Comparative benches should be done on multiple AVX2 CPUs before reordering this
278-
279- let hihi = avx2_cross_shuffle :: < 0x11 > ( bytes. into ( ) , bytes. into ( ) ) ;
280- let hi_shuf = Simd :: from ( avx2_half_pshufb (
281- hihi, // duplicate the vector's top half
282- idxs. into ( ) , // so that using only 4 bits of an index still picks bytes 16-31
283- ) ) ;
284- // A zero-fill during the compose step gives the "all-Neon-like" OOB-is-0 semantics
285- let compose = idxs. simd_lt ( high) . select ( hi_shuf, Simd :: splat ( 0 ) ) ;
286270 let lolo = avx2_cross_shuffle :: < 0x00 > ( bytes. into ( ) , bytes. into ( ) ) ;
287- let lo_shuf = Simd :: from ( avx2_half_pshufb ( lolo, idxs. into ( ) ) ) ;
288- // Repeat, then pick indices < 16, overwriting indices 0-15 from previous compose step
289- let compose = idxs. simd_lt ( mid) . select ( lo_shuf, compose) ;
290- compose
271+ let hihi = avx2_cross_shuffle :: < 0x11 > ( bytes. into ( ) , bytes. into ( ) ) ;
272+
273+ // Adding 0x60 preserves the low nibble and bit 4 for valid
274+ // indices 0..=31. Larger indices get their high bit set, so
275+ // VPSHUFB supplies the required out-of-bounds zeroing.
276+ let control = x86:: _mm256_adds_epu8 ( idxs. into ( ) , x86:: _mm256_set1_epi8 ( 0x60 ) ) ;
277+
278+ // Move index bit 4 into each byte's sign bit for VPBLENDVB.
279+ let select_high = x86:: _mm256_slli_epi16 :: < 3 > ( control) ;
280+ let from_low = avx2_half_pshufb ( lolo, control) ;
281+ let from_high = avx2_half_pshufb ( hihi, control) ;
282+ x86:: _mm256_blendv_epi8 ( from_low, from_high, select_high) . into ( )
291283 }
292284}
293285
0 commit comments