diff --git a/fearless_simd/src/generated/avx2.rs b/fearless_simd/src/generated/avx2.rs index 72aa5f25..1e16124e 100644 --- a/fearless_simd/src/generated/avx2.rs +++ b/fearless_simd/src/generated/avx2.rs @@ -240,6 +240,82 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_max_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_max_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_min_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_min_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { crate::kernel!( #[inline(always)] @@ -838,6 +914,50 @@ impl Simd for Avx2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { crate::kernel!( #[inline(always)] @@ -1340,6 +1460,50 @@ impl Simd for Avx2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -1935,6 +2099,46 @@ impl Simd for Avx2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { crate::kernel!( #[inline(always)] @@ -2393,6 +2597,46 @@ impl Simd for Avx2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { crate::kernel!( #[inline(always)] @@ -3024,6 +3268,42 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi32(reduced, shifted) }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi32(reduced, shifted) }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { crate::kernel!( #[inline(always)] @@ -3464,6 +3744,42 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu32(reduced, shifted) }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu32(reduced, shifted) }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { crate::kernel!( #[inline(always)] @@ -4039,6 +4355,66 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_max_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_min_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { + let intermediate = _mm_max_pd(reduced, shifted); + let b_is_nan = _mm_cmpunord_pd(shifted, shifted); + _mm_blendv_pd(intermediate, reduced, b_is_nan) + }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { + let intermediate = _mm_min_pd(reduced, shifted); + let b_is_nan = _mm_cmpunord_pd(shifted, shifted); + _mm_blendv_pd(intermediate, reduced, b_is_nan) + }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { crate::kernel!( #[inline(always)] @@ -4607,6 +4983,28 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { [ i64::max(a[0usize], b[0usize]), @@ -5021,6 +5419,28 @@ impl Simd for Avx2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx2, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { [ u64::max(a[0usize], b[0usize]), diff --git a/fearless_simd/src/generated/avx512.rs b/fearless_simd/src/generated/avx512.rs index 1803ac82..95b173b1 100644 --- a/fearless_simd/src/generated/avx512.rs +++ b/fearless_simd/src/generated/avx512.rs @@ -514,6 +514,66 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_range_ps::<5i32>(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_range_ps::<5i32>(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_range_ps::<4i32>(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_range_ps::<4i32>(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { crate::kernel!( #[inline(always)] @@ -1056,6 +1116,50 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { crate::kernel!( #[inline(always)] @@ -1544,6 +1648,50 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -2054,6 +2202,46 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { crate::kernel!( #[inline(always)] @@ -2480,6 +2668,46 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { crate::kernel!( #[inline(always)] @@ -3013,6 +3241,42 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi32(reduced, shifted) }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi32(reduced, shifted) }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { crate::kernel!( #[inline(always)] @@ -3433,6 +3697,42 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu32(reduced, shifted) }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu32(reduced, shifted) }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { crate::kernel!( #[inline(always)] @@ -3935,6 +4235,58 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_max_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_min_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_range_pd::<5i32>(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_range_pd::<4i32>(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { crate::kernel!( #[inline(always)] @@ -4436,6 +4788,28 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { crate::kernel!( #[inline(always)] @@ -4836,6 +5210,28 @@ impl Simd for Avx512 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Avx512, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { crate::kernel!( #[inline(always)] diff --git a/fearless_simd/src/generated/fallback.rs b/fearless_simd/src/generated/fallback.rs index f0b0bcde..38010a7f 100644 --- a/fearless_simd/src/generated/fallback.rs +++ b/fearless_simd/src/generated/fallback.rs @@ -297,6 +297,42 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + let reduced: [f32; 2usize] = [ + f32::max(a[0usize], a[1usize]), + f32::max(a[2usize], a[3usize]), + ]; + let reduced: [f32; 1usize] = [f32::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + let reduced: [f32; 2usize] = [ + f32::min(a[0usize], a[1usize]), + f32::min(a[2usize], a[3usize]), + ]; + let reduced: [f32; 1usize] = [f32::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + let reduced: [f32; 2usize] = [ + f32::max(a[0usize], a[1usize]), + f32::max(a[2usize], a[3usize]), + ]; + let reduced: [f32; 1usize] = [f32::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + let reduced: [f32; 2usize] = [ + f32::min(a[0usize], a[1usize]), + f32::min(a[2usize], a[3usize]), + ]; + let reduced: [f32; 1usize] = [f32::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { [ f32::max(a[0usize], b[0usize]), @@ -883,6 +919,56 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + let reduced: [i8; 8usize] = [ + i8::max(a[0usize], a[1usize]), + i8::max(a[2usize], a[3usize]), + i8::max(a[4usize], a[5usize]), + i8::max(a[6usize], a[7usize]), + i8::max(a[8usize], a[9usize]), + i8::max(a[10usize], a[11usize]), + i8::max(a[12usize], a[13usize]), + i8::max(a[14usize], a[15usize]), + ]; + let reduced: [i8; 4usize] = [ + i8::max(reduced[0usize], reduced[1usize]), + i8::max(reduced[2usize], reduced[3usize]), + i8::max(reduced[4usize], reduced[5usize]), + i8::max(reduced[6usize], reduced[7usize]), + ]; + let reduced: [i8; 2usize] = [ + i8::max(reduced[0usize], reduced[1usize]), + i8::max(reduced[2usize], reduced[3usize]), + ]; + let reduced: [i8; 1usize] = [i8::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + let reduced: [i8; 8usize] = [ + i8::min(a[0usize], a[1usize]), + i8::min(a[2usize], a[3usize]), + i8::min(a[4usize], a[5usize]), + i8::min(a[6usize], a[7usize]), + i8::min(a[8usize], a[9usize]), + i8::min(a[10usize], a[11usize]), + i8::min(a[12usize], a[13usize]), + i8::min(a[14usize], a[15usize]), + ]; + let reduced: [i8; 4usize] = [ + i8::min(reduced[0usize], reduced[1usize]), + i8::min(reduced[2usize], reduced[3usize]), + i8::min(reduced[4usize], reduced[5usize]), + i8::min(reduced[6usize], reduced[7usize]), + ]; + let reduced: [i8; 2usize] = [ + i8::min(reduced[0usize], reduced[1usize]), + i8::min(reduced[2usize], reduced[3usize]), + ]; + let reduced: [i8; 1usize] = [i8::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { [ i8::max(a[0usize], b[0usize]), @@ -1733,6 +1819,56 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + let reduced: [u8; 8usize] = [ + u8::max(a[0usize], a[1usize]), + u8::max(a[2usize], a[3usize]), + u8::max(a[4usize], a[5usize]), + u8::max(a[6usize], a[7usize]), + u8::max(a[8usize], a[9usize]), + u8::max(a[10usize], a[11usize]), + u8::max(a[12usize], a[13usize]), + u8::max(a[14usize], a[15usize]), + ]; + let reduced: [u8; 4usize] = [ + u8::max(reduced[0usize], reduced[1usize]), + u8::max(reduced[2usize], reduced[3usize]), + u8::max(reduced[4usize], reduced[5usize]), + u8::max(reduced[6usize], reduced[7usize]), + ]; + let reduced: [u8; 2usize] = [ + u8::max(reduced[0usize], reduced[1usize]), + u8::max(reduced[2usize], reduced[3usize]), + ]; + let reduced: [u8; 1usize] = [u8::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + let reduced: [u8; 8usize] = [ + u8::min(a[0usize], a[1usize]), + u8::min(a[2usize], a[3usize]), + u8::min(a[4usize], a[5usize]), + u8::min(a[6usize], a[7usize]), + u8::min(a[8usize], a[9usize]), + u8::min(a[10usize], a[11usize]), + u8::min(a[12usize], a[13usize]), + u8::min(a[14usize], a[15usize]), + ]; + let reduced: [u8; 4usize] = [ + u8::min(reduced[0usize], reduced[1usize]), + u8::min(reduced[2usize], reduced[3usize]), + u8::min(reduced[4usize], reduced[5usize]), + u8::min(reduced[6usize], reduced[7usize]), + ]; + let reduced: [u8; 2usize] = [ + u8::min(reduced[0usize], reduced[1usize]), + u8::min(reduced[2usize], reduced[3usize]), + ]; + let reduced: [u8; 1usize] = [u8::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { [ u8::max(a[0usize], b[0usize]), @@ -2717,6 +2853,36 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + let reduced: [i16; 4usize] = [ + i16::max(a[0usize], a[1usize]), + i16::max(a[2usize], a[3usize]), + i16::max(a[4usize], a[5usize]), + i16::max(a[6usize], a[7usize]), + ]; + let reduced: [i16; 2usize] = [ + i16::max(reduced[0usize], reduced[1usize]), + i16::max(reduced[2usize], reduced[3usize]), + ]; + let reduced: [i16; 1usize] = [i16::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + let reduced: [i16; 4usize] = [ + i16::min(a[0usize], a[1usize]), + i16::min(a[2usize], a[3usize]), + i16::min(a[4usize], a[5usize]), + i16::min(a[6usize], a[7usize]), + ]; + let reduced: [i16; 2usize] = [ + i16::min(reduced[0usize], reduced[1usize]), + i16::min(reduced[2usize], reduced[3usize]), + ]; + let reduced: [i16; 1usize] = [i16::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { [ i16::max(a[0usize], b[0usize]), @@ -3254,6 +3420,36 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + let reduced: [u16; 4usize] = [ + u16::max(a[0usize], a[1usize]), + u16::max(a[2usize], a[3usize]), + u16::max(a[4usize], a[5usize]), + u16::max(a[6usize], a[7usize]), + ]; + let reduced: [u16; 2usize] = [ + u16::max(reduced[0usize], reduced[1usize]), + u16::max(reduced[2usize], reduced[3usize]), + ]; + let reduced: [u16; 1usize] = [u16::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + let reduced: [u16; 4usize] = [ + u16::min(a[0usize], a[1usize]), + u16::min(a[2usize], a[3usize]), + u16::min(a[4usize], a[5usize]), + u16::min(a[6usize], a[7usize]), + ]; + let reduced: [u16; 2usize] = [ + u16::min(reduced[0usize], reduced[1usize]), + u16::min(reduced[2usize], reduced[3usize]), + ]; + let reduced: [u16; 1usize] = [u16::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { [ u16::max(a[0usize], b[0usize]), @@ -3951,6 +4147,24 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + let reduced: [i32; 2usize] = [ + i32::max(a[0usize], a[1usize]), + i32::max(a[2usize], a[3usize]), + ]; + let reduced: [i32; 1usize] = [i32::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + let reduced: [i32; 2usize] = [ + i32::min(a[0usize], a[1usize]), + i32::min(a[2usize], a[3usize]), + ]; + let reduced: [i32; 1usize] = [i32::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { [ i32::max(a[0usize], b[0usize]), @@ -4303,6 +4517,24 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + let reduced: [u32; 2usize] = [ + u32::max(a[0usize], a[1usize]), + u32::max(a[2usize], a[3usize]), + ]; + let reduced: [u32; 1usize] = [u32::max(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + let reduced: [u32; 2usize] = [ + u32::min(a[0usize], a[1usize]), + u32::min(a[2usize], a[3usize]), + ]; + let reduced: [u32; 1usize] = [u32::min(reduced[0usize], reduced[1usize])]; + reduced[0] + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { [ u32::max(a[0usize], b[0usize]), @@ -4724,6 +4956,26 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + let reduced: [f64; 1usize] = [f64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + let reduced: [f64; 1usize] = [f64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + let reduced: [f64; 1usize] = [f64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + let reduced: [f64; 1usize] = [f64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { [ f64::max(a[0usize], b[0usize]), @@ -5052,6 +5304,16 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + let reduced: [i64; 1usize] = [i64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + let reduced: [i64; 1usize] = [i64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { [ i64::max(a[0usize], b[0usize]), @@ -5319,6 +5581,16 @@ impl Simd for Fallback { .simd_into(self) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + let reduced: [u64; 1usize] = [u64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + let reduced: [u64; 1usize] = [u64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { [ u64::max(a[0usize], b[0usize]), diff --git a/fearless_simd/src/generated/neon.rs b/fearless_simd/src/generated/neon.rs index b93f7f36..dbb511ed 100644 --- a/fearless_simd/src/generated/neon.rs +++ b/fearless_simd/src/generated/neon.rs @@ -232,6 +232,46 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f32x4) -> f32 { + vmaxvq_f32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f32x4) -> f32 { + vminvq_f32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f32x4) -> f32 { + vmaxnmvq_f32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f32x4) -> f32 { + vminnmvq_f32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { crate::kernel!( #[inline(always)] @@ -675,6 +715,26 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i8x16) -> i8 { + vmaxvq_s8(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i8x16) -> i8 { + vminvq_s8(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { crate::kernel!( #[inline(always)] @@ -1038,6 +1098,26 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u8x16) -> u8 { + vmaxvq_u8(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u8x16) -> u8 { + vminvq_u8(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -1548,6 +1628,26 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i16x8) -> i16 { + vmaxvq_s16(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i16x8) -> i16 { + vminvq_s16(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { crate::kernel!( #[inline(always)] @@ -1910,6 +2010,26 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u16x8) -> u16 { + vmaxvq_u16(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u16x8) -> u16 { + vminvq_u16(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { crate::kernel!( #[inline(always)] @@ -2445,6 +2565,26 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i32x4) -> i32 { + vmaxvq_s32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: i32x4) -> i32 { + vminvq_s32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { crate::kernel!( #[inline(always)] @@ -2817,6 +2957,26 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u32x4) -> u32 { + vmaxvq_u32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: u32x4) -> u32 { + vminvq_u32(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { crate::kernel!( #[inline(always)] @@ -3325,6 +3485,46 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f64x2) -> f64 { + vmaxvq_f64(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f64x2) -> f64 { + vminvq_f64(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f64x2) -> f64 { + vmaxnmvq_f64(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Neon, a: f64x2) -> f64 { + vminnmvq_f64(a.into()) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { crate::kernel!( #[inline(always)] @@ -3774,6 +3974,16 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + let reduced: [i64; 1usize] = [i64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + let reduced: [i64; 1usize] = [i64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { [ i64::max(a[0usize], b[0usize]), @@ -4130,6 +4340,16 @@ impl Simd for Neon { kernel(self, a, b) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + let reduced: [u64; 1usize] = [u64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + let reduced: [u64; 1usize] = [u64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { [ u64::max(a[0usize], b[0usize]), diff --git a/fearless_simd/src/generated/simd_trait.rs b/fearless_simd/src/generated/simd_trait.rs index 899682e9..ec16cb2c 100644 --- a/fearless_simd/src/generated/simd_trait.rs +++ b/fearless_simd/src/generated/simd_trait.rs @@ -261,6 +261,14 @@ pub trait Simd: fn div_f32x4(self, a: f32x4, b: f32x4) -> f32x4; #[doc = "Return a vector with the magnitude of `a` and the sign of `b` for each element.\n\nThis operation copies the sign bit, so if an input element is NaN, the output element will be a NaN with the same payload and a copied sign bit."] fn copysign_f32x4(self, a: f32x4, b: f32x4) -> f32x4; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_f32x4(self, a: f32x4) -> f32; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_f32x4(self, a: f32x4) -> f32; + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32; + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -395,6 +403,10 @@ pub trait Simd: fn shr_i8x16(self, a: i8x16, shift: u32) -> i8x16; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_i8x16(self, a: i8x16, b: i8x16) -> i8x16; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_i8x16(self, a: i8x16) -> i8; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_i8x16(self, a: i8x16) -> i8; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -492,6 +504,10 @@ pub trait Simd: fn shr_u8x16(self, a: u8x16, shift: u32) -> u8x16; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_u8x16(self, a: u8x16, b: u8x16) -> u8x16; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_u8x16(self, a: u8x16) -> u8; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_u8x16(self, a: u8x16) -> u8; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -633,6 +649,10 @@ pub trait Simd: fn shr_i16x8(self, a: i16x8, shift: u32) -> i16x8; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_i16x8(self, a: i16x8, b: i16x8) -> i16x8; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_i16x8(self, a: i16x8) -> i16; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_i16x8(self, a: i16x8) -> i16; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -745,6 +765,10 @@ pub trait Simd: fn shr_u16x8(self, a: u16x8, shift: u32) -> u16x8; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_u16x8(self, a: u16x8, b: u16x8) -> u16x8; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_u16x8(self, a: u16x8) -> u16; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_u16x8(self, a: u16x8) -> u16; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -892,6 +916,10 @@ pub trait Simd: fn shr_i32x4(self, a: i32x4, shift: u32) -> i32x4; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_i32x4(self, a: i32x4, b: i32x4) -> i32x4; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_i32x4(self, a: i32x4) -> i32; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_i32x4(self, a: i32x4) -> i32; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -1006,6 +1034,10 @@ pub trait Simd: fn shr_u32x4(self, a: u32x4, shift: u32) -> u32x4; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_u32x4(self, a: u32x4, b: u32x4) -> u32x4; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_u32x4(self, a: u32x4) -> u32; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_u32x4(self, a: u32x4) -> u32; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -1147,6 +1179,14 @@ pub trait Simd: fn div_f64x2(self, a: f64x2, b: f64x2) -> f64x2; #[doc = "Return a vector with the magnitude of `a` and the sign of `b` for each element.\n\nThis operation copies the sign bit, so if an input element is NaN, the output element will be a NaN with the same payload and a copied sign bit."] fn copysign_f64x2(self, a: f64x2, b: f64x2) -> f64x2; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_f64x2(self, a: f64x2) -> f64; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_f64x2(self, a: f64x2) -> f64; + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64; + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -1285,6 +1325,10 @@ pub trait Simd: fn shr_i64x2(self, a: i64x2, shift: u32) -> i64x2; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_i64x2(self, a: i64x2, b: i64x2) -> i64x2; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_i64x2(self, a: i64x2) -> i64; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_i64x2(self, a: i64x2) -> i64; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -1397,6 +1441,10 @@ pub trait Simd: fn shr_u64x2(self, a: u64x2, shift: u32) -> u64x2; #[doc = "Shift each element right by the corresponding element in another vector.\n\nFor unsigned integers, zeros are shifted in on the left. For signed integers, the sign bit is replicated.\n\nWhen shifting out of bounds (e.g. shifting a 32-bit value by 32 or more), the result is implementation-defined and may vary by platform.\n\n# Performance\nThis per-lane shift is slower than shifting the entire vector by a scalar. It is not vectorized at all on WebAssembly, SSE4.2, and for `u8` and `i8` elements on AVX2. Prefer shifting by a single scalar value instead of by a vector whenever possible."] fn shrv_u64x2(self, a: u64x2, b: u64x2) -> u64x2; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max_u64x2(self, a: u64x2) -> u64; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min_u64x2(self, a: u64x2) -> u64; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] @@ -1593,6 +1641,30 @@ pub trait Simd: let (b0, b1) = self.split_f32x8(b); self.combine_f32x4(self.copysign_f32x4(a0, b0), self.copysign_f32x4(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_f32x8(self, a: f32x8) -> f32 { + let (a0, a1) = self.split_f32x8(a); + self.reduce_max_f32x4(self.max_f32x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_f32x8(self, a: f32x8) -> f32 { + let (a0, a1) = self.split_f32x8(a); + self.reduce_min_f32x4(self.min_f32x4(a0, a1)) + } + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_max_precise_f32x8(self, a: f32x8) -> f32 { + let (a0, a1) = self.split_f32x8(a); + self.reduce_max_precise_f32x4(self.max_precise_f32x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_min_precise_f32x8(self, a: f32x8) -> f32 { + let (a0, a1) = self.split_f32x8(a); + self.reduce_min_precise_f32x4(self.min_precise_f32x4(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_f32x8(self, a: f32x8, b: f32x8) -> f32x8 { @@ -1970,6 +2042,18 @@ pub trait Simd: let (b0, b1) = self.split_i8x32(b); self.combine_i8x16(self.shrv_i8x16(a0, b0), self.shrv_i8x16(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i8x32(self, a: i8x32) -> i8 { + let (a0, a1) = self.split_i8x32(a); + self.reduce_max_i8x16(self.max_i8x16(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i8x32(self, a: i8x32) -> i8 { + let (a0, a1) = self.split_i8x32(a); + self.reduce_min_i8x16(self.min_i8x16(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i8x32(self, a: i8x32, b: i8x32) -> i8x32 { @@ -2225,6 +2309,18 @@ pub trait Simd: let (b0, b1) = self.split_u8x32(b); self.combine_u8x16(self.shrv_u8x16(a0, b0), self.shrv_u8x16(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u8x32(self, a: u8x32) -> u8 { + let (a0, a1) = self.split_u8x32(a); + self.reduce_max_u8x16(self.max_u8x16(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u8x32(self, a: u8x32) -> u8 { + let (a0, a1) = self.split_u8x32(a); + self.reduce_min_u8x16(self.min_u8x16(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u8x32(self, a: u8x32, b: u8x32) -> u8x32 { @@ -2586,6 +2682,18 @@ pub trait Simd: let (b0, b1) = self.split_i16x16(b); self.combine_i16x8(self.shrv_i16x8(a0, b0), self.shrv_i16x8(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i16x16(self, a: i16x16) -> i16 { + let (a0, a1) = self.split_i16x16(a); + self.reduce_max_i16x8(self.max_i16x8(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i16x16(self, a: i16x16) -> i16 { + let (a0, a1) = self.split_i16x16(a); + self.reduce_min_i16x8(self.min_i16x8(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i16x16(self, a: i16x16, b: i16x16) -> i16x16 { @@ -2873,6 +2981,18 @@ pub trait Simd: let (b0, b1) = self.split_u16x16(b); self.combine_u16x8(self.shrv_u16x8(a0, b0), self.shrv_u16x8(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u16x16(self, a: u16x16) -> u16 { + let (a0, a1) = self.split_u16x16(a); + self.reduce_max_u16x8(self.max_u16x8(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u16x16(self, a: u16x16) -> u16 { + let (a0, a1) = self.split_u16x16(a); + self.reduce_min_u16x8(self.min_u16x8(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u16x16(self, a: u16x16, b: u16x16) -> u16x16 { @@ -3257,6 +3377,18 @@ pub trait Simd: let (b0, b1) = self.split_i32x8(b); self.combine_i32x4(self.shrv_i32x4(a0, b0), self.shrv_i32x4(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i32x8(self, a: i32x8) -> i32 { + let (a0, a1) = self.split_i32x8(a); + self.reduce_max_i32x4(self.max_i32x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i32x8(self, a: i32x8) -> i32 { + let (a0, a1) = self.split_i32x8(a); + self.reduce_min_i32x4(self.min_i32x4(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i32x8(self, a: i32x8, b: i32x8) -> i32x8 { @@ -3546,6 +3678,18 @@ pub trait Simd: let (b0, b1) = self.split_u32x8(b); self.combine_u32x4(self.shrv_u32x4(a0, b0), self.shrv_u32x4(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u32x8(self, a: u32x8) -> u32 { + let (a0, a1) = self.split_u32x8(a); + self.reduce_max_u32x4(self.max_u32x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u32x8(self, a: u32x8) -> u32 { + let (a0, a1) = self.split_u32x8(a); + self.reduce_min_u32x4(self.min_u32x4(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u32x8(self, a: u32x8, b: u32x8) -> u32x8 { @@ -3912,6 +4056,30 @@ pub trait Simd: let (b0, b1) = self.split_f64x4(b); self.combine_f64x2(self.copysign_f64x2(a0, b0), self.copysign_f64x2(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_f64x4(self, a: f64x4) -> f64 { + let (a0, a1) = self.split_f64x4(a); + self.reduce_max_f64x2(self.max_f64x2(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_f64x4(self, a: f64x4) -> f64 { + let (a0, a1) = self.split_f64x4(a); + self.reduce_min_f64x2(self.min_f64x2(a0, a1)) + } + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_max_precise_f64x4(self, a: f64x4) -> f64 { + let (a0, a1) = self.split_f64x4(a); + self.reduce_max_precise_f64x2(self.max_precise_f64x2(a0, a1)) + } + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_min_precise_f64x4(self, a: f64x4) -> f64 { + let (a0, a1) = self.split_f64x4(a); + self.reduce_min_precise_f64x2(self.min_precise_f64x2(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_f64x4(self, a: f64x4, b: f64x4) -> f64x4 { @@ -4308,6 +4476,18 @@ pub trait Simd: let (b0, b1) = self.split_i64x4(b); self.combine_i64x2(self.shrv_i64x2(a0, b0), self.shrv_i64x2(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i64x4(self, a: i64x4) -> i64 { + let (a0, a1) = self.split_i64x4(a); + self.reduce_max_i64x2(self.max_i64x2(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i64x4(self, a: i64x4) -> i64 { + let (a0, a1) = self.split_i64x4(a); + self.reduce_min_i64x2(self.min_i64x2(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i64x4(self, a: i64x4, b: i64x4) -> i64x4 { @@ -4589,6 +4769,18 @@ pub trait Simd: let (b0, b1) = self.split_u64x4(b); self.combine_u64x2(self.shrv_u64x2(a0, b0), self.shrv_u64x2(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u64x4(self, a: u64x4) -> u64 { + let (a0, a1) = self.split_u64x4(a); + self.reduce_max_u64x2(self.max_u64x2(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u64x4(self, a: u64x4) -> u64 { + let (a0, a1) = self.split_u64x4(a); + self.reduce_min_u64x2(self.min_u64x2(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u64x4(self, a: u64x4, b: u64x4) -> u64x4 { @@ -4951,6 +5143,30 @@ pub trait Simd: let (b0, b1) = self.split_f32x16(b); self.combine_f32x8(self.copysign_f32x8(a0, b0), self.copysign_f32x8(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_f32x16(self, a: f32x16) -> f32 { + let (a0, a1) = self.split_f32x16(a); + self.reduce_max_f32x8(self.max_f32x8(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_f32x16(self, a: f32x16) -> f32 { + let (a0, a1) = self.split_f32x16(a); + self.reduce_min_f32x8(self.min_f32x8(a0, a1)) + } + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_max_precise_f32x16(self, a: f32x16) -> f32 { + let (a0, a1) = self.split_f32x16(a); + self.reduce_max_precise_f32x8(self.max_precise_f32x8(a0, a1)) + } + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_min_precise_f32x16(self, a: f32x16) -> f32 { + let (a0, a1) = self.split_f32x16(a); + self.reduce_min_precise_f32x8(self.min_precise_f32x8(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_f32x16(self, a: f32x16, b: f32x16) -> f32x16 { @@ -5336,6 +5552,18 @@ pub trait Simd: let (b0, b1) = self.split_i8x64(b); self.combine_i8x32(self.shrv_i8x32(a0, b0), self.shrv_i8x32(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i8x64(self, a: i8x64) -> i8 { + let (a0, a1) = self.split_i8x64(a); + self.reduce_max_i8x32(self.max_i8x32(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i8x64(self, a: i8x64) -> i8 { + let (a0, a1) = self.split_i8x64(a); + self.reduce_min_i8x32(self.min_i8x32(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i8x64(self, a: i8x64, b: i8x64) -> i8x64 { @@ -5589,6 +5817,18 @@ pub trait Simd: let (b0, b1) = self.split_u8x64(b); self.combine_u8x32(self.shrv_u8x32(a0, b0), self.shrv_u8x32(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u8x64(self, a: u8x64) -> u8 { + let (a0, a1) = self.split_u8x64(a); + self.reduce_max_u8x32(self.max_u8x32(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u8x64(self, a: u8x64) -> u8 { + let (a0, a1) = self.split_u8x64(a); + self.reduce_min_u8x32(self.min_u8x32(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u8x64(self, a: u8x64, b: u8x64) -> u8x64 { @@ -5946,6 +6186,18 @@ pub trait Simd: let (b0, b1) = self.split_i16x32(b); self.combine_i16x16(self.shrv_i16x16(a0, b0), self.shrv_i16x16(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i16x32(self, a: i16x32) -> i16 { + let (a0, a1) = self.split_i16x32(a); + self.reduce_max_i16x16(self.max_i16x16(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i16x32(self, a: i16x32) -> i16 { + let (a0, a1) = self.split_i16x32(a); + self.reduce_min_i16x16(self.min_i16x16(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i16x32(self, a: i16x32, b: i16x32) -> i16x32 { @@ -6237,6 +6489,18 @@ pub trait Simd: let (b0, b1) = self.split_u16x32(b); self.combine_u16x16(self.shrv_u16x16(a0, b0), self.shrv_u16x16(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u16x32(self, a: u16x32) -> u16 { + let (a0, a1) = self.split_u16x32(a); + self.reduce_max_u16x16(self.max_u16x16(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u16x32(self, a: u16x32) -> u16 { + let (a0, a1) = self.split_u16x32(a); + self.reduce_min_u16x16(self.min_u16x16(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u16x32(self, a: u16x32, b: u16x32) -> u16x32 { @@ -6630,6 +6894,18 @@ pub trait Simd: let (b0, b1) = self.split_i32x16(b); self.combine_i32x8(self.shrv_i32x8(a0, b0), self.shrv_i32x8(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i32x16(self, a: i32x16) -> i32 { + let (a0, a1) = self.split_i32x16(a); + self.reduce_max_i32x8(self.max_i32x8(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i32x16(self, a: i32x16) -> i32 { + let (a0, a1) = self.split_i32x16(a); + self.reduce_min_i32x8(self.min_i32x8(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i32x16(self, a: i32x16, b: i32x16) -> i32x16 { @@ -6921,6 +7197,18 @@ pub trait Simd: let (b0, b1) = self.split_u32x16(b); self.combine_u32x8(self.shrv_u32x8(a0, b0), self.shrv_u32x8(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u32x16(self, a: u32x16) -> u32 { + let (a0, a1) = self.split_u32x16(a); + self.reduce_max_u32x8(self.max_u32x8(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u32x16(self, a: u32x16) -> u32 { + let (a0, a1) = self.split_u32x16(a); + self.reduce_min_u32x8(self.min_u32x8(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u32x16(self, a: u32x16, b: u32x16) -> u32x16 { @@ -7283,6 +7571,30 @@ pub trait Simd: let (b0, b1) = self.split_f64x8(b); self.combine_f64x4(self.copysign_f64x4(a0, b0), self.copysign_f64x4(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_f64x8(self, a: f64x8) -> f64 { + let (a0, a1) = self.split_f64x8(a); + self.reduce_max_f64x4(self.max_f64x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_f64x8(self, a: f64x8) -> f64 { + let (a0, a1) = self.split_f64x8(a); + self.reduce_min_f64x4(self.min_f64x4(a0, a1)) + } + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_max_precise_f64x8(self, a: f64x8) -> f64 { + let (a0, a1) = self.split_f64x8(a); + self.reduce_max_precise_f64x4(self.max_precise_f64x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + #[inline(always)] + fn reduce_min_precise_f64x8(self, a: f64x8) -> f64 { + let (a0, a1) = self.split_f64x8(a); + self.reduce_min_precise_f64x4(self.min_precise_f64x4(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_f64x8(self, a: f64x8, b: f64x8) -> f64x8 { @@ -7677,6 +7989,18 @@ pub trait Simd: let (b0, b1) = self.split_i64x8(b); self.combine_i64x4(self.shrv_i64x4(a0, b0), self.shrv_i64x4(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_i64x8(self, a: i64x8) -> i64 { + let (a0, a1) = self.split_i64x8(a); + self.reduce_max_i64x4(self.max_i64x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_i64x8(self, a: i64x8) -> i64 { + let (a0, a1) = self.split_i64x8(a); + self.reduce_min_i64x4(self.min_i64x4(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_i64x8(self, a: i64x8, b: i64x8) -> i64x8 { @@ -7956,6 +8280,18 @@ pub trait Simd: let (b0, b1) = self.split_u64x8(b); self.combine_u64x4(self.shrv_u64x4(a0, b0), self.shrv_u64x4(a1, b1)) } + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_max_u64x8(self, a: u64x8) -> u64 { + let (a0, a1) = self.split_u64x8(a); + self.reduce_max_u64x4(self.max_u64x4(a0, a1)) + } + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + #[inline(always)] + fn reduce_min_u64x8(self, a: u64x8) -> u64 { + let (a0, a1) = self.split_u64x8(a); + self.reduce_min_u64x4(self.min_u64x4(a0, a1)) + } #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] #[inline(always)] fn max_u64x8(self, a: u64x8, b: u64x8) -> u64x8 { @@ -8771,6 +9107,14 @@ pub trait SimdBase: fn swizzle_dyn(self, indices: impl SimdInto) -> Self; #[doc = "Dynamically swizzle this vector's bytes across the whole vector.\n\nThe `indices` operand is a same-width byte vector. For each output byte, index values within the vector's byte length select the corresponding byte from the input vector. Out-of-range indices produce zero."] fn swizzle_dyn_precise(self, indices: impl SimdInto) -> Self; + #[doc = "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\nFor floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_max(self) -> Self::Element; + #[doc = "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\nFor floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned."] + fn reduce_min(self) -> Self::Element; + #[doc = "Return the maximum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_max`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + fn reduce_max_precise(self) -> Self::Element; + #[doc = "Return the minimum element in the vector, ignoring quiet NaNs.\n\nFor integer vectors, this operation is the same as `reduce_min`.\n\nFor floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\nIf the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\nIf any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\nSignaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them."] + fn reduce_min_precise(self) -> Self::Element; #[doc = "Return the element-wise maximum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `max_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] fn max(self, rhs: impl SimdInto) -> Self; #[doc = "Return the element-wise minimum of two vectors.\n\nFor floating-point vectors, if either operand is NaN, the result for that lane is implementation-defined-- it could be either the first or second operand. See `min_precise` for a version that returns the non-NaN operand if only one is NaN.\n\nIf one floating-point operand is positive zero and the other is negative zero, the result is also implementation-defined, and it could be either one."] diff --git a/fearless_simd/src/generated/simd_types.rs b/fearless_simd/src/generated/simd_types.rs index 84650567..1b4daa72 100644 --- a/fearless_simd/src/generated/simd_types.rs +++ b/fearless_simd/src/generated/simd_types.rs @@ -159,6 +159,22 @@ impl SimdBase for f32x4 { self.simd.reverse_f32x4(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_f32x4(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_f32x4(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_precise_f32x4(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_precise_f32x4(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_f32x4(self, rhs.simd_into(self.simd)) } @@ -489,6 +505,22 @@ impl SimdBase for i8x16 { self.simd.reverse_i8x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i8x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i8x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i8x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i8x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i8x16(self, rhs.simd_into(self.simd)) } @@ -757,6 +789,22 @@ impl SimdBase for u8x16 { self.simd.reverse_u8x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u8x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u8x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u8x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u8x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u8x16(self, rhs.simd_into(self.simd)) } @@ -1115,6 +1163,22 @@ impl SimdBase for i16x8 { self.simd.reverse_i16x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i16x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i16x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i16x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i16x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i16x8(self, rhs.simd_into(self.simd)) } @@ -1390,6 +1454,22 @@ impl SimdBase for u16x8 { self.simd.reverse_u16x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u16x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u16x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u16x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u16x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u16x8(self, rhs.simd_into(self.simd)) } @@ -1751,6 +1831,22 @@ impl SimdBase for i32x4 { self.simd.reverse_i32x4(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i32x4(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i32x4(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i32x4(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i32x4(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i32x4(self, rhs.simd_into(self.simd)) } @@ -2026,6 +2122,22 @@ impl SimdBase for u32x4 { self.simd.reverse_u32x4(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u32x4(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u32x4(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u32x4(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u32x4(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u32x4(self, rhs.simd_into(self.simd)) } @@ -2399,6 +2511,22 @@ impl SimdBase for f64x2 { self.simd.reverse_f64x2(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_f64x2(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_f64x2(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_precise_f64x2(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_precise_f64x2(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_f64x2(self, rhs.simd_into(self.simd)) } @@ -2717,6 +2845,22 @@ impl SimdBase for i64x2 { self.simd.reverse_i64x2(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i64x2(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i64x2(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i64x2(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i64x2(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i64x2(self, rhs.simd_into(self.simd)) } @@ -2985,6 +3129,22 @@ impl SimdBase for u64x2 { self.simd.reverse_u64x2(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u64x2(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u64x2(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u64x2(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u64x2(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u64x2(self, rhs.simd_into(self.simd)) } @@ -3363,6 +3523,22 @@ impl SimdBase for f32x8 { self.simd.reverse_f32x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_f32x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_f32x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_precise_f32x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_precise_f32x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_f32x8(self, rhs.simd_into(self.simd)) } @@ -3704,6 +3880,22 @@ impl SimdBase for i8x32 { self.simd.reverse_i8x32(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i8x32(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i8x32(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i8x32(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i8x32(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i8x32(self, rhs.simd_into(self.simd)) } @@ -3983,6 +4175,22 @@ impl SimdBase for u8x32 { self.simd.reverse_u8x32(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u8x32(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u8x32(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u8x32(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u8x32(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u8x32(self, rhs.simd_into(self.simd)) } @@ -4344,6 +4552,22 @@ impl SimdBase for i16x16 { self.simd.reverse_i16x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i16x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i16x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i16x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i16x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i16x16(self, rhs.simd_into(self.simd)) } @@ -4623,6 +4847,22 @@ impl SimdBase for u16x16 { self.simd.reverse_u16x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u16x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u16x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u16x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u16x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u16x16(self, rhs.simd_into(self.simd)) } @@ -4992,6 +5232,22 @@ impl SimdBase for i32x8 { self.simd.reverse_i32x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i32x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i32x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i32x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i32x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i32x8(self, rhs.simd_into(self.simd)) } @@ -5274,6 +5530,22 @@ impl SimdBase for u32x8 { self.simd.reverse_u32x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u32x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u32x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u32x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u32x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u32x8(self, rhs.simd_into(self.simd)) } @@ -5642,6 +5914,22 @@ impl SimdBase for f64x4 { self.simd.reverse_f64x4(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_f64x4(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_f64x4(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_precise_f64x4(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_precise_f64x4(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_f64x4(self, rhs.simd_into(self.simd)) } @@ -5955,6 +6243,22 @@ impl SimdBase for i64x4 { self.simd.reverse_i64x4(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i64x4(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i64x4(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i64x4(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i64x4(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i64x4(self, rhs.simd_into(self.simd)) } @@ -6218,6 +6522,22 @@ impl SimdBase for u64x4 { self.simd.reverse_u64x4(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u64x4(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u64x4(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u64x4(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u64x4(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u64x4(self, rhs.simd_into(self.simd)) } @@ -6600,6 +6920,22 @@ impl SimdBase for f32x16 { self.simd.reverse_f32x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_f32x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_f32x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_precise_f32x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_precise_f32x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_f32x16(self, rhs.simd_into(self.simd)) } @@ -6968,6 +7304,22 @@ impl SimdBase for i8x64 { self.simd.reverse_i8x64(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i8x64(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i8x64(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i8x64(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i8x64(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i8x64(self, rhs.simd_into(self.simd)) } @@ -7273,6 +7625,22 @@ impl SimdBase for u8x64 { self.simd.reverse_u8x64(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u8x64(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u8x64(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u8x64(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u8x64(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u8x64(self, rhs.simd_into(self.simd)) } @@ -7644,6 +8012,22 @@ impl SimdBase for i16x32 { self.simd.reverse_i16x32(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i16x32(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i16x32(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i16x32(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i16x32(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i16x32(self, rhs.simd_into(self.simd)) } @@ -7933,6 +8317,22 @@ impl SimdBase for u16x32 { self.simd.reverse_u16x32(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u16x32(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u16x32(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u16x32(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u16x32(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u16x32(self, rhs.simd_into(self.simd)) } @@ -8304,6 +8704,22 @@ impl SimdBase for i32x16 { self.simd.reverse_i32x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i32x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i32x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i32x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i32x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i32x16(self, rhs.simd_into(self.simd)) } @@ -8589,6 +9005,22 @@ impl SimdBase for u32x16 { self.simd.reverse_u32x16(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u32x16(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u32x16(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u32x16(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u32x16(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u32x16(self, rhs.simd_into(self.simd)) } @@ -8964,6 +9396,22 @@ impl SimdBase for f64x8 { self.simd.reverse_f64x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_f64x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_f64x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_precise_f64x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_precise_f64x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_f64x8(self, rhs.simd_into(self.simd)) } @@ -9283,6 +9731,22 @@ impl SimdBase for i64x8 { self.simd.reverse_i64x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_i64x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_i64x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_i64x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_i64x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_i64x8(self, rhs.simd_into(self.simd)) } @@ -9552,6 +10016,22 @@ impl SimdBase for u64x8 { self.simd.reverse_u64x8(self) } #[inline(always)] + fn reduce_max(self) -> Self::Element { + self.simd.reduce_max_u64x8(self) + } + #[inline(always)] + fn reduce_min(self) -> Self::Element { + self.simd.reduce_min_u64x8(self) + } + #[inline(always)] + fn reduce_max_precise(self) -> Self::Element { + self.simd.reduce_max_u64x8(self) + } + #[inline(always)] + fn reduce_min_precise(self) -> Self::Element { + self.simd.reduce_min_u64x8(self) + } + #[inline(always)] fn max(self, rhs: impl SimdInto) -> Self { self.simd.max_u64x8(self, rhs.simd_into(self.simd)) } diff --git a/fearless_simd/src/generated/sse2.rs b/fearless_simd/src/generated/sse2.rs index 91b3cf05..c85a8565 100644 --- a/fearless_simd/src/generated/sse2.rs +++ b/fearless_simd/src/generated/sse2.rs @@ -334,6 +334,94 @@ impl Simd for Sse2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_max_ps(reduced, shifted); + let shifted_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_or_ps( + _mm_and_ps(shifted_is_nan, reduced), + _mm_andnot_ps(shifted_is_nan, intermediate), + ) + }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_max_ps(reduced, shifted); + let shifted_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_or_ps( + _mm_and_ps(shifted_is_nan, reduced), + _mm_andnot_ps(shifted_is_nan, intermediate), + ) + }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_min_ps(reduced, shifted); + let shifted_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_or_ps( + _mm_and_ps(shifted_is_nan, reduced), + _mm_andnot_ps(shifted_is_nan, intermediate), + ) + }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_min_ps(reduced, shifted); + let shifted_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_or_ps( + _mm_and_ps(shifted_is_nan, reduced), + _mm_andnot_ps(shifted_is_nan, intermediate), + ) + }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { crate::kernel!( #[inline(always)] @@ -927,6 +1015,106 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi8(a, b); + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { crate::kernel!( #[inline(always)] @@ -1563,6 +1751,50 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -2226,6 +2458,46 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { crate::kernel!( #[inline(always)] @@ -2669,6 +2941,118 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi16(0x8000u16.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi16(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi16(0x8000u16.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi16(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi16(0x8000u16.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi16(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi16(0x8000u16.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi16(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi16(0x8000u16.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi16(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi16(0x8000u16.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi16(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { crate::kernel!( #[inline(always)] @@ -3301,6 +3685,70 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi32(a, b); + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi32(a, b); + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi32(a, b); + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = _mm_cmpgt_epi32(a, b); + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { crate::kernel!( #[inline(always)] @@ -3752,6 +4200,90 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi32(0x80000000u32.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi32(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi32(0x80000000u32.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi32(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, a), _mm_andnot_si128(gt, b)) + } + }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi32(0x80000000u32.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi32(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { + { + let a = reduced; + let b = shifted; + let gt = { + let sign_bit = _mm_set1_epi32(0x80000000u32.cast_signed()); + let lhs_signed = _mm_xor_si128(a, sign_bit); + let rhs_signed = _mm_xor_si128(b, sign_bit); + _mm_cmpgt_epi32(lhs_signed, rhs_signed) + }; + _mm_or_si128(_mm_and_si128(gt, b), _mm_andnot_si128(gt, a)) + } + }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { crate::kernel!( #[inline(always)] @@ -4357,6 +4889,72 @@ impl Simd for Sse2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_max_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_min_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { + let intermediate = _mm_max_pd(reduced, shifted); + let shifted_is_nan = _mm_cmpunord_pd(shifted, shifted); + _mm_or_pd( + _mm_and_pd(shifted_is_nan, reduced), + _mm_andnot_pd(shifted_is_nan, intermediate), + ) + }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { + let intermediate = _mm_min_pd(reduced, shifted); + let shifted_is_nan = _mm_cmpunord_pd(shifted, shifted); + _mm_or_pd( + _mm_and_pd(shifted_is_nan, reduced), + _mm_andnot_pd(shifted_is_nan, intermediate), + ) + }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { crate::kernel!( #[inline(always)] @@ -4824,6 +5422,28 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { [ i64::max(a[0usize], b[0usize]), @@ -5209,6 +5829,28 @@ impl Simd for Sse2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse2, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { [ u64::max(a[0usize], b[0usize]), diff --git a/fearless_simd/src/generated/sse4_2.rs b/fearless_simd/src/generated/sse4_2.rs index 132a23d4..9ad40cb8 100644 --- a/fearless_simd/src/generated/sse4_2.rs +++ b/fearless_simd/src/generated/sse4_2.rs @@ -314,6 +314,82 @@ impl Simd for Sse4_2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_max_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { _mm_min_ps(reduced, shifted) }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_max_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_max_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f32x4) -> f32 { + let reduced: __m128 = a.into(); + let shifted = _mm_castsi128_ps(_mm_srli_si128::<8>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_min_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + let shifted = _mm_castsi128_ps(_mm_srli_si128::<4>(_mm_castps_si128(reduced))); + let reduced = { + let intermediate = _mm_min_ps(reduced, shifted); + let b_is_nan = _mm_cmpunord_ps(shifted, shifted); + _mm_blendv_ps(intermediate, reduced, b_is_nan) + }; + _mm_cvtss_f32(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { crate::kernel!( #[inline(always)] @@ -988,6 +1064,50 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epi8(reduced, shifted) }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i8x16) -> i8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epi8(reduced, shifted) }; + { + let lanes: [i8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { crate::kernel!( #[inline(always)] @@ -1495,6 +1615,50 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_max_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u8x16) -> u8 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + let shifted = _mm_srli_si128::<1>(reduced); + let reduced = { _mm_min_epu8(reduced, shifted) }; + { + let lanes: [u8; 16usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { crate::kernel!( #[inline(always)] @@ -2092,6 +2256,46 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i16x8) -> i16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epi16(reduced, shifted) }; + { + let lanes: [i16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { crate::kernel!( #[inline(always)] @@ -2551,6 +2755,46 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_max_epu16(reduced, shifted) }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u16x8) -> u16 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + let shifted = _mm_srli_si128::<2>(reduced); + let reduced = { _mm_min_epu16(reduced, shifted) }; + { + let lanes: [u16; 8usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { crate::kernel!( #[inline(always)] @@ -3180,6 +3424,42 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epi32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epi32(reduced, shifted) }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i32x4) -> i32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epi32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epi32(reduced, shifted) }; + { + let lanes: [i32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { crate::kernel!( #[inline(always)] @@ -3617,6 +3897,42 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_max_epu32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_max_epu32(reduced, shifted) }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u32x4) -> u32 { + let reduced: __m128i = a.into(); + let shifted = _mm_srli_si128::<8>(reduced); + let reduced = { _mm_min_epu32(reduced, shifted) }; + let shifted = _mm_srli_si128::<4>(reduced); + let reduced = { _mm_min_epu32(reduced, shifted) }; + { + let lanes: [u32; 4usize] = crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { crate::kernel!( #[inline(always)] @@ -4186,6 +4502,66 @@ impl Simd for Sse4_2 { kernel(self, a, b) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_max_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { _mm_min_pd(reduced, shifted) }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { + let intermediate = _mm_max_pd(reduced, shifted); + let b_is_nan = _mm_cmpunord_pd(shifted, shifted); + _mm_blendv_pd(intermediate, reduced, b_is_nan) + }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: f64x2) -> f64 { + let reduced: __m128d = a.into(); + let shifted = _mm_castsi128_pd(_mm_srli_si128::<8>(_mm_castpd_si128(reduced))); + let reduced = { + let intermediate = _mm_min_pd(reduced, shifted); + let b_is_nan = _mm_cmpunord_pd(shifted, shifted); + _mm_blendv_pd(intermediate, reduced, b_is_nan) + }; + _mm_cvtsd_f64(reduced) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { crate::kernel!( #[inline(always)] @@ -4770,6 +5146,28 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: i64x2) -> i64 { + let lanes: [i64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { [ i64::max(a[0usize], b[0usize]), @@ -5187,6 +5585,28 @@ impl Simd for Sse4_2 { .simd_into(self) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].max(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + crate::kernel!( + #[inline(always)] + fn kernel(token: Sse4_2, a: u64x2) -> u64 { + let lanes: [u64; 2] = a.into(); + lanes[0].min(lanes[1]) + } + ); + kernel(self, a) + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { [ u64::max(a[0usize], b[0usize]), diff --git a/fearless_simd/src/generated/wasm.rs b/fearless_simd/src/generated/wasm.rs index a0187ef3..95b555af 100644 --- a/fearless_simd/src/generated/wasm.rs +++ b/fearless_simd/src/generated/wasm.rs @@ -264,6 +264,42 @@ impl Simd for WasmSimd128 { v128_or(magnitude, sign_bits).simd_into(self) } #[inline(always)] + fn reduce_max_f32x4(self, a: f32x4) -> f32 { + let reduced = a; + let shuffled = i32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_f32x4(reduced, shuffled); + let shuffled = i32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_f32x4(reduced, shuffled); + f32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_f32x4(self, a: f32x4) -> f32 { + let reduced = a; + let shuffled = i32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_f32x4(reduced, shuffled); + let shuffled = i32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_f32x4(reduced, shuffled); + f32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_max_precise_f32x4(self, a: f32x4) -> f32 { + let reduced = a; + let shuffled = i32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_precise_f32x4(reduced, shuffled); + let shuffled = i32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_precise_f32x4(reduced, shuffled); + f32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_precise_f32x4(self, a: f32x4) -> f32 { + let reduced = a; + let shuffled = i32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_precise_f32x4(reduced, shuffled); + let shuffled = i32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_precise_f32x4(reduced, shuffled); + f32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_f32x4(self, a: f32x4, b: f32x4) -> f32x4 { #[cfg(target_feature = "relaxed-simd")] { @@ -608,6 +644,64 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_i8x16(self, a: i8x16) -> i8 { + let reduced = a; + let shuffled = i8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_i8x16(reduced, shuffled); + let shuffled = i8x16_shuffle::<4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_i8x16(reduced, shuffled); + let shuffled = i8x16_shuffle::<2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_i8x16(reduced, shuffled); + let shuffled = i8x16_shuffle::<1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_i8x16(reduced, shuffled); + i8x16_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_i8x16(self, a: i8x16) -> i8 { + let reduced = a; + let shuffled = i8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_i8x16(reduced, shuffled); + let shuffled = i8x16_shuffle::<4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_i8x16(reduced, shuffled); + let shuffled = i8x16_shuffle::<2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_i8x16(reduced, shuffled); + let shuffled = i8x16_shuffle::<1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_i8x16(reduced, shuffled); + i8x16_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_i8x16(self, a: i8x16, b: i8x16) -> i8x16 { i8x16_max(a.into(), b.into()).simd_into(self) } @@ -911,6 +1005,64 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_u8x16(self, a: u8x16) -> u8 { + let reduced = a; + let shuffled = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_u8x16(reduced, shuffled); + let shuffled = u8x16_shuffle::<4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_u8x16(reduced, shuffled); + let shuffled = u8x16_shuffle::<2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_u8x16(reduced, shuffled); + let shuffled = u8x16_shuffle::<1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.max_u8x16(reduced, shuffled); + u8x16_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_u8x16(self, a: u8x16) -> u8 { + let reduced = a; + let shuffled = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_u8x16(reduced, shuffled); + let shuffled = u8x16_shuffle::<4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_u8x16(reduced, shuffled); + let shuffled = u8x16_shuffle::<2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_u8x16(reduced, shuffled); + let shuffled = u8x16_shuffle::<1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0>( + reduced.into(), + reduced.into(), + ) + .simd_into(self); + let reduced = self.min_u8x16(reduced, shuffled); + u8x16_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_u8x16(self, a: u8x16, b: u8x16) -> u8x16 { u8x16_max(a.into(), b.into()).simd_into(self) } @@ -1264,6 +1416,34 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_i16x8(self, a: i16x8) -> i16 { + let reduced = a; + let shuffled = + i16x8_shuffle::<4, 5, 6, 7, 0, 1, 2, 3>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_i16x8(reduced, shuffled); + let shuffled = + i16x8_shuffle::<2, 3, 4, 5, 6, 7, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_i16x8(reduced, shuffled); + let shuffled = + i16x8_shuffle::<1, 2, 3, 4, 5, 6, 7, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_i16x8(reduced, shuffled); + i16x8_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_i16x8(self, a: i16x8) -> i16 { + let reduced = a; + let shuffled = + i16x8_shuffle::<4, 5, 6, 7, 0, 1, 2, 3>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_i16x8(reduced, shuffled); + let shuffled = + i16x8_shuffle::<2, 3, 4, 5, 6, 7, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_i16x8(reduced, shuffled); + let shuffled = + i16x8_shuffle::<1, 2, 3, 4, 5, 6, 7, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_i16x8(reduced, shuffled); + i16x8_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_i16x8(self, a: i16x8, b: i16x8) -> i16x8 { i16x8_max(a.into(), b.into()).simd_into(self) } @@ -1504,6 +1684,34 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_u16x8(self, a: u16x8) -> u16 { + let reduced = a; + let shuffled = + u16x8_shuffle::<4, 5, 6, 7, 0, 1, 2, 3>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_u16x8(reduced, shuffled); + let shuffled = + u16x8_shuffle::<2, 3, 4, 5, 6, 7, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_u16x8(reduced, shuffled); + let shuffled = + u16x8_shuffle::<1, 2, 3, 4, 5, 6, 7, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_u16x8(reduced, shuffled); + u16x8_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_u16x8(self, a: u16x8) -> u16 { + let reduced = a; + let shuffled = + u16x8_shuffle::<4, 5, 6, 7, 0, 1, 2, 3>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_u16x8(reduced, shuffled); + let shuffled = + u16x8_shuffle::<2, 3, 4, 5, 6, 7, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_u16x8(reduced, shuffled); + let shuffled = + u16x8_shuffle::<1, 2, 3, 4, 5, 6, 7, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_u16x8(reduced, shuffled); + u16x8_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_u16x8(self, a: u16x8, b: u16x8) -> u16x8 { u16x8_max(a.into(), b.into()).simd_into(self) } @@ -1832,6 +2040,24 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_i32x4(self, a: i32x4) -> i32 { + let reduced = a; + let shuffled = i32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_i32x4(reduced, shuffled); + let shuffled = i32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_i32x4(reduced, shuffled); + i32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_i32x4(self, a: i32x4) -> i32 { + let reduced = a; + let shuffled = i32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_i32x4(reduced, shuffled); + let shuffled = i32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_i32x4(reduced, shuffled); + i32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_i32x4(self, a: i32x4, b: i32x4) -> i32x4 { i32x4_max(a.into(), b.into()).simd_into(self) } @@ -2069,6 +2295,24 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_u32x4(self, a: u32x4) -> u32 { + let reduced = a; + let shuffled = u32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_u32x4(reduced, shuffled); + let shuffled = u32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_u32x4(reduced, shuffled); + u32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_u32x4(self, a: u32x4) -> u32 { + let reduced = a; + let shuffled = u32x4_shuffle::<2, 3, 0, 1>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_u32x4(reduced, shuffled); + let shuffled = u32x4_shuffle::<1, 2, 3, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_u32x4(reduced, shuffled); + u32x4_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_u32x4(self, a: u32x4, b: u32x4) -> u32x4 { u32x4_max(a.into(), b.into()).simd_into(self) } @@ -2375,6 +2619,34 @@ impl Simd for WasmSimd128 { v128_or(magnitude, sign_bits).simd_into(self) } #[inline(always)] + fn reduce_max_f64x2(self, a: f64x2) -> f64 { + let reduced = a; + let shuffled = i64x2_shuffle::<1, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_f64x2(reduced, shuffled); + f64x2_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_f64x2(self, a: f64x2) -> f64 { + let reduced = a; + let shuffled = i64x2_shuffle::<1, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_f64x2(reduced, shuffled); + f64x2_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_max_precise_f64x2(self, a: f64x2) -> f64 { + let reduced = a; + let shuffled = i64x2_shuffle::<1, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.max_precise_f64x2(reduced, shuffled); + f64x2_extract_lane::<0>(reduced.into()) + } + #[inline(always)] + fn reduce_min_precise_f64x2(self, a: f64x2) -> f64 { + let reduced = a; + let shuffled = i64x2_shuffle::<1, 0>(reduced.into(), reduced.into()).simd_into(self); + let reduced = self.min_precise_f64x2(reduced, shuffled); + f64x2_extract_lane::<0>(reduced.into()) + } + #[inline(always)] fn max_f64x2(self, a: f64x2, b: f64x2) -> f64x2 { #[cfg(target_feature = "relaxed-simd")] { @@ -2681,6 +2953,16 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_i64x2(self, a: i64x2) -> i64 { + let reduced: [i64; 1usize] = [i64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_i64x2(self, a: i64x2) -> i64 { + let reduced: [i64; 1usize] = [i64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_i64x2(self, a: i64x2, b: i64x2) -> i64x2 { [ i64::max(a[0usize], b[0usize]), @@ -2922,6 +3204,16 @@ impl Simd for WasmSimd128 { .simd_into(self) } #[inline(always)] + fn reduce_max_u64x2(self, a: u64x2) -> u64 { + let reduced: [u64; 1usize] = [u64::max(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] + fn reduce_min_u64x2(self, a: u64x2) -> u64 { + let reduced: [u64; 1usize] = [u64::min(a[0usize], a[1usize])]; + reduced[0] + } + #[inline(always)] fn max_u64x2(self, a: u64x2, b: u64x2) -> u64x2 { [ u64::max(a[0usize], b[0usize]), diff --git a/fearless_simd_gen/src/generic.rs b/fearless_simd_gen/src/generic.rs index 61d415d6..a6c03d65 100644 --- a/fearless_simd_gen/src/generic.rs +++ b/fearless_simd_gen/src/generic.rs @@ -222,6 +222,17 @@ pub(crate) fn generic_op(op: &Op, ty: &VecType) -> TokenStream { } } } + OpSig::Reduce { lane_op } => { + let combine_halves = generic_op_name(lane_op, &half); + // Combine corresponding lanes before reducing so vectors wider than 128 bits + // retain a fixed-depth tree while needing only one horizontal 128-bit leaf. + quote! { + #method_sig { + let (a0, a1) = self.#split(a); + self.#do_half(self.#combine_halves(a0, a1)) + } + } + } OpSig::Binary => { quote! { #method_sig { diff --git a/fearless_simd_gen/src/mk_fallback.rs b/fearless_simd_gen/src/mk_fallback.rs index 22fe36ac..fb957948 100644 --- a/fearless_simd_gen/src/mk_fallback.rs +++ b/fearless_simd_gen/src/mk_fallback.rs @@ -290,6 +290,7 @@ impl Level for Fallback { } } } + OpSig::Reduce { lane_op } => fallback_reduce_min_max(method_sig, vec_ty, lane_op), OpSig::Widen { target_ty } => { let scalar = target_ty.scalar.rust(target_ty.scalar_bits); let half_len = vec_ty.len / 2; @@ -783,6 +784,49 @@ fn lane(value: TokenStream, vec_ty: &VecType, idx: usize) -> TokenStream { } } +/// Build an adjacent balanced min/max reduction one horizontal level at a time. +fn fallback_reduce_min_max( + method_sig: TokenStream, + vec_ty: &VecType, + lane_op: &str, +) -> TokenStream { + assert_eq!( + vec_ty.n_bits(), + 128, + "wide reductions must use the generic 128-bit-grained implementation" + ); + + let scalar = vec_ty.scalar.rust(vec_ty.scalar_bits); + let mut statements = Vec::new(); + let mut previous = quote! { a }; + let mut previous_len = vec_ty.len; + + while previous_len > 1 { + let next_len = previous_len / 2; + let results = (0..next_len).map(|index| { + let left_index = index * 2; + let right_index = left_index + 1; + let args = [ + quote! { #previous[#left_index] }, + quote! { #previous[#right_index] }, + ]; + fallback::expr(lane_op, vec_ty, &args) + }); + statements.push(quote! { + let reduced: [#scalar; #next_len] = [#(#results),*]; + }); + previous = quote! { reduced }; + previous_len = next_len; + } + + quote! { + #method_sig { + #(#statements)* + #previous[0] + } + } +} + /// Whether the second argument of the function needs to be passed by reference. fn rhs_reference(method: &str) -> bool { !matches!( diff --git a/fearless_simd_gen/src/mk_neon.rs b/fearless_simd_gen/src/mk_neon.rs index c47d18b9..87e6dde3 100644 --- a/fearless_simd_gen/src/mk_neon.rs +++ b/fearless_simd_gen/src/mk_neon.rs @@ -207,6 +207,29 @@ impl Level for Neon { quote! { #expr.simd_into(#token) } }) } + OpSig::Reduce { lane_op } => { + assert_eq!( + vec_ty.n_bits(), + 128, + "wide reductions must use the generic 128-bit-grained implementation" + ); + + if vec_ty.scalar_bits == 64 + && matches!(vec_ty.scalar, ScalarType::Int | ScalarType::Unsigned) + { + return fallback_method(op, vec_ty); + } + + let intrinsic = match lane_op { + "min" => "vminv", + "max" => "vmaxv", + "min_precise" => "vminnmv", + "max_precise" => "vmaxnmv", + _ => unreachable!("unsupported min/max reduction lane operation"), + }; + let reduce = simple_intrinsic(intrinsic, vec_ty); + self.kernel_method(op, vec_ty, |_| quote! { #reduce(a.into()) }) + } OpSig::LoadInterleaved { block_size, block_count, diff --git a/fearless_simd_gen/src/mk_simd_types.rs b/fearless_simd_gen/src/mk_simd_types.rs index a27a0f39..db059627 100644 --- a/fearless_simd_gen/src/mk_simd_types.rs +++ b/fearless_simd_gen/src/mk_simd_types.rs @@ -483,6 +483,8 @@ fn simd_vec_impl(ty: &VecType) -> TokenStream { match method { "min_precise" => "min", "max_precise" => "max", + "reduce_min_precise" => "reduce_min", + "reduce_max_precise" => "reduce_max", _ => method, } } else { diff --git a/fearless_simd_gen/src/mk_wasm.rs b/fearless_simd_gen/src/mk_wasm.rs index d0039f73..e525e394 100644 --- a/fearless_simd_gen/src/mk_wasm.rs +++ b/fearless_simd_gen/src/mk_wasm.rs @@ -126,6 +126,47 @@ fn count_ones_method(op: Op, vec_ty: &VecType) -> TokenStream { } } +fn reduce_min_max(method_sig: TokenStream, vec_ty: &VecType, lane_op: &str) -> TokenStream { + assert_eq!( + vec_ty.n_bits(), + 128, + "wide reductions must use the generic 128-bit-grained implementation" + ); + + let combine = generic_op_name(lane_op, vec_ty); + let shuffle = match (vec_ty.scalar, vec_ty.scalar_bits) { + (ScalarType::Float, 32) => quote! { i32x4_shuffle }, + (ScalarType::Float, 64) => quote! { i64x2_shuffle }, + (ScalarType::Int | ScalarType::Unsigned, _) => { + let shuffle = simple_intrinsic("shuffle", vec_ty); + quote! { #shuffle } + } + _ => unreachable!("min/max reductions only operate on numeric vectors"), + }; + let extract = simple_intrinsic("extract_lane", vec_ty); + + let mut stages = Vec::new(); + let mut offset = vec_ty.len / 2; + while offset > 0 { + let indices = + (0..vec_ty.len).map(|index| Literal::usize_unsuffixed((index + offset) % vec_ty.len)); + stages.push(quote! { + let shuffled = #shuffle::<#(#indices),*>(reduced.into(), reduced.into()) + .simd_into(self); + let reduced = self.#combine(reduced, shuffled); + }); + offset /= 2; + } + + quote! { + #method_sig { + let reduced = a; + #(#stages)* + #extract::<0>(reduced.into()) + } + } +} + impl Level for WasmSimd128 { fn name(&self) -> &'static str { "WasmSimd128" @@ -248,6 +289,15 @@ impl Level for WasmSimd128 { } } } + OpSig::Reduce { lane_op } => { + if vec_ty.scalar_bits == 64 + && matches!(vec_ty.scalar, ScalarType::Int | ScalarType::Unsigned) + { + fallback_method(op, vec_ty) + } else { + reduce_min_max(method_sig, vec_ty, lane_op) + } + } OpSig::Widen { target_ty } => { if vec_ty.scalar == ScalarType::Float { return quote! { diff --git a/fearless_simd_gen/src/mk_x86.rs b/fearless_simd_gen/src/mk_x86.rs index 60f8f8d1..a44f616a 100644 --- a/fearless_simd_gen/src/mk_x86.rs +++ b/fearless_simd_gen/src/mk_x86.rs @@ -318,6 +318,7 @@ impl Level for X86 { OpSig::Splat => self.handle_splat(op, vec_ty), OpSig::Compare => self.handle_compare(op, method, vec_ty), OpSig::Unary => self.handle_unary(op, method_sig, method, vec_ty), + OpSig::Reduce { lane_op } => self.handle_reduce_min_max(op, vec_ty, lane_op), OpSig::Widen { target_ty } => self.handle_widen(op, vec_ty, target_ty), OpSig::Narrow { target_ty, mode } => self.handle_narrow(op, vec_ty, target_ty, mode), OpSig::Binary => self.handle_binary(op, method, vec_ty), @@ -820,14 +821,25 @@ fn sse2_select_expr( /// Other 8/16/32-bit integer min/max operations are synthesized with an SSE2 /// comparison and the bitwise select helper above. fn sse2_min_max_expr(method: &str, vec_ty: &VecType) -> TokenStream { + sse2_min_max_native_expr(method, vec_ty, quote! { a.into() }, quote! { b.into() }) +} + +/// Build an SSE2 integer min/max expression from operands that already evaluate +/// to native `__m128i` values. +fn sse2_min_max_native_expr( + method: &str, + vec_ty: &VecType, + a: TokenStream, + b: TokenStream, +) -> TokenStream { match (method, vec_ty.scalar, vec_ty.scalar_bits) { ("min", ScalarType::Unsigned, 8) | ("max", ScalarType::Unsigned, 8) => { let intrinsic = simple_intrinsic(method, vec_ty); - quote! { #intrinsic(a.into(), b.into()) } + quote! { #intrinsic(#a, #b) } } ("min", ScalarType::Int, 16) | ("max", ScalarType::Int, 16) => { let intrinsic = simple_intrinsic(method, vec_ty); - quote! { #intrinsic(a.into(), b.into()) } + quote! { #intrinsic(#a, #b) } } ("min" | "max", ScalarType::Int | ScalarType::Unsigned, 8 | 16 | 32) => { let gt = sse2_cmpgt_expr(vec_ty, quote! { a }, quote! { b }); @@ -838,8 +850,8 @@ fn sse2_min_max_expr(method: &str, vec_ty: &VecType) -> TokenStream { }; quote! { { - let a = a.into(); - let b = b.into(); + let a = #a; + let b = #b; let gt = #gt; #select } @@ -849,6 +861,60 @@ fn sse2_min_max_expr(method: &str, vec_ty: &VecType) -> TokenStream { } } +/// Build the native expression used to combine one stage of a min/max reduction. +/// +/// Keeping this in terms of the same helpers as the vertical operations ensures +/// that reductions inherit their NaN behavior. In particular, precise floating- +/// point reductions retain the explicit SSE2 NaN correction, use RANGE on +/// AVX-512, and use the regular x86 precise expression elsewhere. +fn x86_reduce_min_max_expr(level: X86, lane_op: &str, vec_ty: &VecType) -> TokenStream { + if level == X86::Avx512 + && vec_ty.scalar == ScalarType::Float + && matches!(lane_op, "min_precise" | "max_precise") + { + let suffix = op_suffix(vec_ty.scalar, vec_ty.scalar_bits, true); + let range = intrinsic_ident("range", suffix, vec_ty.n_bits()); + let imm = if lane_op == "max_precise" { + 0b0101 + } else { + 0b0100 + }; + return quote! { #range::<#imm>(reduced, shifted) }; + } + + if level == X86::Sse2 + && vec_ty.scalar == ScalarType::Float + && matches!(lane_op, "min_precise" | "max_precise") + { + let intrinsic = simple_intrinsic( + if lane_op == "max_precise" { + "max" + } else { + "min" + }, + vec_ty, + ); + let cmpunord = float_compare_method("unord", vec_ty); + let select = sse2_select_expr( + vec_ty, + quote! { shifted_is_nan }, + quote! { reduced }, + quote! { intermediate }, + ); + return quote! { + let intermediate = #intrinsic(reduced, shifted); + let shifted_is_nan = #cmpunord(shifted, shifted); + #select + }; + } + + if level == X86::Sse2 && matches!(vec_ty.scalar, ScalarType::Int | ScalarType::Unsigned) { + return sse2_min_max_native_expr(lane_op, vec_ty, quote! { reduced }, quote! { shifted }); + } + + x86::expr(lane_op, vec_ty, &[quote! { reduced }, quote! { shifted }]) +} + fn avx512_mask_register_bits(vec_ty: &VecType) -> usize { match vec_ty.len { 0..=8 => 8, @@ -1127,6 +1193,95 @@ impl X86 { } } + pub(crate) fn handle_reduce_min_max( + &self, + op: Op, + vec_ty: &VecType, + lane_op: &str, + ) -> TokenStream { + assert_eq!( + vec_ty.n_bits(), + 128, + "wide reductions must use the generic 128-bit-grained implementation" + ); + assert!( + matches!(lane_op, "min" | "max" | "min_precise" | "max_precise"), + "unexpected min/max reduction lane operation" + ); + assert!( + vec_ty.scalar != ScalarType::Mask, + "min/max reductions only operate on numeric vectors" + ); + + // x86 has no packed i64/u64 min/max before AVX-512, and a scalar pair + // is also the shortest fixed-depth reduction when AVX-512VL is present. + if matches!(vec_ty.scalar, ScalarType::Int | ScalarType::Unsigned) + && vec_ty.scalar_bits == 64 + { + assert!( + matches!(lane_op, "min" | "max"), + "precise integer reductions are forwarded to ordinary reductions" + ); + let scalar = vec_ty.scalar.rust(vec_ty.scalar_bits); + let scalar_op = Ident::new(lane_op, Span::call_site()); + return self.kernel_method(op, vec_ty, |_| { + quote! { + let lanes: [#scalar; 2] = a.into(); + lanes[0].#scalar_op(lanes[1]) + } + }); + } + + let mut stages = Vec::new(); + let mut shift_bytes = 8; + let scalar_bytes = vec_ty.scalar_bits / 8; + while shift_bytes >= scalar_bytes { + let shift = Literal::i32_unsuffixed(i32::try_from(shift_bytes).unwrap()); + let shifted = match (vec_ty.scalar, vec_ty.scalar_bits) { + (ScalarType::Float, 32) => quote! { + _mm_castsi128_ps(_mm_srli_si128::<#shift>(_mm_castps_si128(reduced))) + }, + (ScalarType::Float, 64) => quote! { + _mm_castsi128_pd(_mm_srli_si128::<#shift>(_mm_castpd_si128(reduced))) + }, + (ScalarType::Int | ScalarType::Unsigned, _) => { + quote! { _mm_srli_si128::<#shift>(reduced) } + } + _ => unreachable!("min/max reductions only operate on numeric vectors"), + }; + let combine = x86_reduce_min_max_expr(*self, lane_op, vec_ty); + stages.push(quote! { + let shifted = #shifted; + let reduced = { #combine }; + }); + shift_bytes /= 2; + } + + let scalar = vec_ty.scalar.rust(vec_ty.scalar_bits); + let len = vec_ty.len; + let arch_ty = self.arch_ty(vec_ty); + let result = match (vec_ty.scalar, vec_ty.scalar_bits) { + (ScalarType::Float, 32) => quote! { _mm_cvtss_f32(reduced) }, + (ScalarType::Float, 64) => quote! { _mm_cvtsd_f64(reduced) }, + (ScalarType::Int | ScalarType::Unsigned, _) => quote! { + { + let lanes: [#scalar; #len] = + crate::transmute::checked_transmute_copy(&reduced); + lanes[0] + } + }, + _ => unreachable!("min/max reductions only operate on numeric vectors"), + }; + + self.kernel_method(op, vec_ty, |_| { + quote! { + let reduced: #arch_ty = a.into(); + #(#stages)* + #result + } + }) + } + pub(crate) fn handle_splat(&self, op: Op, vec_ty: &VecType) -> TokenStream { if *self == Self::Avx512 && vec_ty.scalar == ScalarType::Mask { let lane_mask = avx512_mask_lane_bits(vec_ty); diff --git a/fearless_simd_gen/src/ops.rs b/fearless_simd_gen/src/ops.rs index 843e25b4..0c316a73 100644 --- a/fearless_simd_gen/src/ops.rs +++ b/fearless_simd_gen/src/ops.rs @@ -50,6 +50,9 @@ pub(crate) enum OpSig { Unary, /// Takes two argument of the vector type, and returns that same vector type. Binary, + /// Takes one numeric vector and reduces its elements to a scalar using the named + /// element-wise operation. + Reduce { lane_op: &'static str }, /// Takes three argument of the vector type, and returns that same vector type. Ternary, /// Takes two argument of the vector type, and returns the corresponding mask type. @@ -301,6 +304,10 @@ impl Op { (vec![vec.clone(), vec], quote! { #result<#simd_ty> }) } OpSig::Unary => (vec![vec.clone()], vec), + OpSig::Reduce { .. } => { + let scalar = vec_ty.scalar.rust(vec_ty.scalar_bits); + (vec![vec], scalar) + } OpSig::Binary | OpSig::Zip { .. } | OpSig::Unzip { .. } => { (vec![vec.clone(), vec.clone()], vec) } @@ -376,6 +383,10 @@ impl Op { let arg0 = &arg_names[0]; quote! { (#arg0) -> Self } } + OpSig::Reduce { .. } => { + let arg0 = &arg_names[0]; + quote! { (#arg0) -> Self::Element } + } OpSig::MaskReduce { .. } => { let arg0 = &arg_names[0]; quote! { (#arg0) -> bool } @@ -585,6 +596,48 @@ const BASE_OPS: &[Op] = &[ ]; const COMMON_BASE_OPS: &[Op] = &[ + Op::new( + "reduce_max", + OpKind::BaseTraitMethod, + OpSig::Reduce { lane_op: "max" }, + "Return the maximum element in the vector. Integer vectors always return the exact maximum.\n\n\ + For floating-point vectors with no NaNs, this returns the true maximum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true maximum. See `reduce_max_precise` for a version that ignores quiet NaNs.\n\n\ + If the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.", + ), + Op::new( + "reduce_min", + OpKind::BaseTraitMethod, + OpSig::Reduce { lane_op: "min" }, + "Return the minimum element in the vector. Integer vectors always return the exact minimum.\n\n\ + For floating-point vectors with no NaNs, this returns the true minimum. If any lane is NaN, the entire result is implementation-defined: it may be NaN or a numeric lane that is not the true minimum. See `reduce_min_precise` for a version that ignores quiet NaNs.\n\n\ + If the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.", + ), + Op::new( + "reduce_max_precise", + OpKind::BaseTraitMethod, + OpSig::Reduce { + lane_op: "max_precise", + }, + "Return the maximum element in the vector, ignoring quiet NaNs.\n\n\ + For integer vectors, this operation is the same as `reduce_max`.\n\n\ + For floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true maximum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\n\ + If the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\n\ + If any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true maximum.\n\ + Signaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them.", + ), + Op::new( + "reduce_min_precise", + OpKind::BaseTraitMethod, + OpSig::Reduce { + lane_op: "min_precise", + }, + "Return the minimum element in the vector, ignoring quiet NaNs.\n\n\ + For integer vectors, this operation is the same as `reduce_min`.\n\n\ + For floating-point vectors, quiet NaNs are ignored. If there is at least one numeric lane, this returns the true minimum of the numeric lanes. If all lanes are quiet NaNs, this returns NaN, with an unspecified payload and sign.\n\n\ + If the floating-point vector contains both positive zero and negative zero, either sign of zero may be returned.\n\n\ + If any lane is a *signaling* NaN, the result is fully non-deterministic: it may be NaN or a numeric lane and is not guaranteed to be the true minimum.\n\ + Signaling NaN values are not produced by floating-point math operations, only from manual initialization with specific bit patterns. You probably don't need to worry about them.", + ), Op::new( "max", OpKind::BaseTraitMethod, @@ -1299,11 +1352,14 @@ pub(crate) fn ops_for_type(ty: &VecType) -> Vec { ScalarType::Mask => false, }; if common_ops_follow { - // Integer precise min/max are exposed by `SimdBase`, but forward to - // the ordinary integer backend operations in `simd_vec_impl`. + // Integer precise min/max operations are exposed by `SimdBase`, but + // forward to the ordinary integer backend operations in `simd_vec_impl`. ops.extend(COMMON_BASE_OPS.iter().copied().filter(|op| { ty.scalar == ScalarType::Float - || !matches!(op.method, "min_precise" | "max_precise") + || !matches!( + op.method, + "min_precise" | "max_precise" | "reduce_min_precise" | "reduce_max_precise" + ) })); } } @@ -1605,6 +1661,13 @@ impl OpSig { return false; } + // Keep horizontal backend-specific implementations confined to 128-bit leaves. + // Wider vectors first combine corresponding lanes of their halves, then recursively + // reduce the result. This provides a bounded, balanced tree on every backend. + if matches!(self, Self::Reduce { .. }) && vec_ty.n_bits() > 128 { + return true; + } + // For a block-wise item slide/shift, defer to the non-block-wise version if the operand is 1 block wide anyway if let Self::Slide { granularity: SlideGranularity::WithinBlocks, @@ -1628,6 +1691,7 @@ impl OpSig { Self::MaskFromBitmask => &["bits"], Self::MaskSet => &["a", "index", "value"], Self::Unary + | Self::Reduce { .. } | Self::Split { .. } | Self::Cvt { .. } | Self::Widen { .. } @@ -1660,7 +1724,9 @@ impl OpSig { | Self::MaskFromBitmask | Self::MaskToBitmask | Self::MaskSet => &[], - Self::Unary | Self::Cvt { .. } | Self::MaskReduce { .. } => &["self"], + Self::Unary | Self::Reduce { .. } | Self::Cvt { .. } | Self::MaskReduce { .. } => { + &["self"] + } Self::Widen { .. } => &[], Self::Narrow { .. } => &[], Self::SwizzleDynWithinBlocks | Self::SwizzleDyn | Self::SwizzleDynPrecise => { @@ -1690,7 +1756,7 @@ impl OpSig { let arg1 = &arg_names[1]; quote! { #arg1 } } - Self::Unary | Self::MaskReduce { .. } => { + Self::Unary | Self::Reduce { .. } | Self::MaskReduce { .. } => { let arg0 = &arg_names[0]; quote! { #arg0 } } diff --git a/fearless_simd_tests/tests/generics.rs b/fearless_simd_tests/tests/generics.rs index 6177e528..a399897c 100644 --- a/fearless_simd_tests/tests/generics.rs +++ b/fearless_simd_tests/tests/generics.rs @@ -63,6 +63,23 @@ fn generic_first>(vector: V) -> V::Element { vector[0] } +// Ensure that horizontal min/max works generically +fn generic_reduce_min>(vector: V) -> V::Element { + vector.reduce_min() +} + +fn generic_reduce_max>(vector: V) -> V::Element { + vector.reduce_max() +} + +fn generic_reduce_min_precise>(vector: V) -> V::Element { + vector.reduce_min_precise() +} + +fn generic_reduce_max_precise>(vector: V) -> V::Element { + vector.reduce_max_precise() +} + // Ensure that a generic vector's 128-bit block is its own block fn generic_block_splat>(block: V::Block) -> V::Block { V::Block::block_splat(block) diff --git a/fearless_simd_tests/tests/harness/ops/mod.rs b/fearless_simd_tests/tests/harness/ops/mod.rs index e1d32642..9db8a45b 100644 --- a/fearless_simd_tests/tests/harness/ops/mod.rs +++ b/fearless_simd_tests/tests/harness/ops/mod.rs @@ -57,6 +57,10 @@ mod native_width; mod neg; mod not; mod or; +mod reduce_max; +mod reduce_max_precise; +mod reduce_min; +mod reduce_min_precise; mod reverse; mod rotate_elements_left; mod rotate_elements_right; diff --git a/fearless_simd_tests/tests/harness/ops/reduce_max.rs b/fearless_simd_tests/tests/harness/ops/reduce_max.rs new file mode 100644 index 00000000..1e9ec16e --- /dev/null +++ b/fearless_simd_tests/tests/harness/ops/reduce_max.rs @@ -0,0 +1,677 @@ +// Copyright 2026 the Fearless_SIMD Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use fearless_simd::*; +use fearless_simd_dev_macros::simd_test; + +// One concrete test row per supported vector type. + +#[simd_test] +fn reduce_max_f32x4(simd: S) { + let value = f32x4::from_slice(simd, &[3.0, f32::INFINITY, -2.0, f32::NEG_INFINITY]); + assert_eq!(value.reduce_max(), f32::INFINITY); + assert_eq!( + f32x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_max(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_i8x16(simd: S) { + let value = i8x16::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + i8::MIN, + 5, + 72, + ], + ); + assert_eq!(value.reduce_max(), i8::MAX); +} + +#[simd_test] +fn reduce_max_u8x16(simd: S) { + let value = u8x16::from_slice( + simd, + &[ + 12, + 87, + 3, + u8::MAX, + 144, + 0, + 63, + 201, + 8, + 91, + 1, + 37, + 164, + 222, + 5, + 72, + ], + ); + assert_eq!(value.reduce_max(), u8::MAX); +} + +#[simd_test] +fn reduce_max_i16x8(simd: S) { + let value = i16x8::from_slice(simd, &[1200, -700, i16::MAX, 0, -9999, 42, i16::MIN, 7321]); + assert_eq!(value.reduce_max(), i16::MAX); +} + +#[simd_test] +fn reduce_max_u16x8(simd: S) { + let value = u16x8::from_slice(simd, &[1200, 700, u16::MAX, 81, 9999, u16::MIN, 42, 7321]); + assert_eq!(value.reduce_max(), u16::MAX); +} + +#[simd_test] +fn reduce_max_i32x4(simd: S) { + let value = i32x4::from_slice(simd, &[42, i32::MIN, i32::MAX, -700_000]); + assert_eq!(value.reduce_max(), i32::MAX); +} + +#[simd_test] +fn reduce_max_u32x4(simd: S) { + let value = u32x4::from_slice(simd, &[42, u32::MAX, 700_000, u32::MIN]); + assert_eq!(value.reduce_max(), u32::MAX); +} + +#[simd_test] +fn reduce_max_f64x2(simd: S) { + let value = f64x2::from_slice(simd, &[f64::NEG_INFINITY, f64::INFINITY]); + assert_eq!(value.reduce_max(), f64::INFINITY); + assert_eq!(f64x2::from_slice(simd, &[0.0, -0.0]).reduce_max(), 0.0); +} + +#[simd_test] +fn reduce_max_i64x2(simd: S) { + let value = i64x2::from_slice(simd, &[i64::MIN, i64::MAX]); + assert_eq!(value.reduce_max(), i64::MAX); +} + +#[simd_test] +fn reduce_max_u64x2(simd: S) { + let value = u64x2::from_slice(simd, &[u64::MIN, u64::MAX]); + assert_eq!(value.reduce_max(), u64::MAX); +} + +#[simd_test] +fn reduce_max_f32x8(simd: S) { + let value = f32x8::from_slice( + simd, + &[ + 3.0, + -2.0, + f32::NEG_INFINITY, + 9.0, + 1.0, + f32::INFINITY, + 4.0, + 8.0, + ], + ); + assert_eq!(value.reduce_max(), f32::INFINITY); + assert_eq!( + f32x8::from_slice(simd, &[0.0, -0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0]).reduce_max(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_i8x32(simd: S) { + let value = i8x32::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + i8::MIN, + 5, + 72, + -18, + 26, + 100, + -55, + 3, + -120, + 84, + 16, + -33, + 49, + 7, + -76, + 110, + 67, + -2, + 31, + ], + ); + assert_eq!(value.reduce_max(), i8::MAX); +} + +#[simd_test] +fn reduce_max_u8x32(simd: S) { + let value = u8x32::from_slice( + simd, + &[ + 12, + 87, + 3, + u8::MAX, + 144, + 0, + 63, + 201, + 8, + 91, + 1, + 37, + 164, + 222, + 5, + 72, + 18, + 126, + 100, + 55, + 13, + 220, + 84, + 16, + 133, + 49, + 7, + 176, + 110, + 67, + 2, + 31, + ], + ); + assert_eq!(value.reduce_max(), u8::MAX); +} + +#[simd_test] +fn reduce_max_i16x16(simd: S) { + let value = i16x16::from_slice( + simd, + &[ + 1200, + -700, + i16::MAX, + 0, + -9999, + 42, + -16384, + 7321, + 81, + -2222, + 19000, + -5, + 640, + -12000, + i16::MIN, + 30001, + ], + ); + assert_eq!(value.reduce_max(), i16::MAX); +} + +#[simd_test] +fn reduce_max_u16x16(simd: S) { + let value = u16x16::from_slice( + simd, + &[ + 1200, + 700, + u16::MAX, + 81, + 9999, + 32000, + 42, + 7321, + 18, + u16::MIN, + 2222, + 19000, + 5, + 640, + 12000, + 30001, + ], + ); + assert_eq!(value.reduce_max(), u16::MAX); +} + +#[simd_test] +fn reduce_max_i32x8(simd: S) { + let value = i32x8::from_slice( + simd, + &[42, -7, i32::MAX, -700_000, 81, i32::MIN, 19_000_000, -5], + ); + assert_eq!(value.reduce_max(), i32::MAX); +} + +#[simd_test] +fn reduce_max_u32x8(simd: S) { + let value = u32x8::from_slice( + simd, + &[ + 42, + u32::MAX, + 700_000, + 81, + u32::MIN, + 19_000_000, + 5, + 3_000_000_000, + ], + ); + assert_eq!(value.reduce_max(), u32::MAX); +} + +#[simd_test] +fn reduce_max_f64x4(simd: S) { + let value = f64x4::from_slice(simd, &[4.0, f64::NEG_INFINITY, -7.0, f64::INFINITY]); + assert_eq!(value.reduce_max(), f64::INFINITY); + assert_eq!( + f64x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_max(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_i64x4(simd: S) { + let value = i64x4::from_slice(simd, &[42, i64::MAX, i64::MIN, -7_000_000_000]); + assert_eq!(value.reduce_max(), i64::MAX); +} + +#[simd_test] +fn reduce_max_u64x4(simd: S) { + let value = u64x4::from_slice(simd, &[42, u64::MIN, u64::MAX, 7_000_000_000]); + assert_eq!(value.reduce_max(), u64::MAX); +} + +#[simd_test] +fn reduce_max_f32x16(simd: S) { + let value = f32x16::from_fn(simd, |i| { + if i == 13 { + f32::INFINITY + } else if i == 2 { + f32::NEG_INFINITY + } else { + i as f32 - 4.0 + } + }); + assert_eq!(value.reduce_max(), f32::INFINITY); + let zeros = f32x16::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_max(), 0.0); +} + +#[simd_test] +fn reduce_max_i8x64(simd: S) { + let value = i8x64::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + 115, + 5, + 72, + -18, + 26, + 100, + -55, + 3, + -120, + 84, + 16, + -33, + 49, + 7, + -76, + 110, + 67, + -2, + 31, + 54, + -88, + 23, + 106, + -15, + 39, + -70, + 11, + 96, + -4, + 58, + -112, + 29, + 75, + -47, + 1, + 82, + -25, + 34, + 119, + -61, + 14, + 69, + -9, + 46, + -101, + 20, + 89, + -36, + i8::MIN, + 6, + 77, + ], + ); + assert_eq!(value.reduce_max(), i8::MAX); +} + +#[simd_test] +fn reduce_max_u8x64(simd: S) { + let value = u8x64::from_slice( + simd, + &[ + 12, + 87, + 3, + u8::MAX, + 144, + 90, + 63, + 201, + 8, + 91, + 1, + 37, + 164, + 222, + 5, + 72, + 18, + 126, + 100, + 55, + 13, + 220, + 84, + 16, + 133, + 49, + 7, + 176, + 110, + 67, + 2, + 31, + 154, + u8::MIN, + 23, + 206, + 15, + 139, + 70, + 11, + 196, + 4, + 158, + 212, + 29, + 175, + 47, + 81, + 182, + 25, + 134, + 219, + 61, + 14, + 169, + 9, + 146, + 101, + 20, + 189, + 36, + 128, + 6, + 77, + ], + ); + assert_eq!(value.reduce_max(), u8::MAX); +} + +#[simd_test] +fn reduce_max_i16x32(simd: S) { + let value = i16x32::from_slice( + simd, + &[ + 1200, + -700, + i16::MAX, + 0, + -9999, + 42, + -16384, + 7321, + 81, + -2222, + 19000, + -5, + 640, + -12000, + 30001, + -301, + 5400, + -8800, + 230, + 10600, + -1500, + 390, + -7000, + 1100, + 9600, + -400, + 5800, + i16::MIN, + 2900, + 750, + -4700, + 100, + ], + ); + assert_eq!(value.reduce_max(), i16::MAX); +} + +#[simd_test] +fn reduce_max_u16x32(simd: S) { + let value = u16x32::from_slice( + simd, + &[ + 1200, + 700, + u16::MAX, + 81, + 9999, + 32000, + 42, + 7321, + 18, + 2222, + 19000, + 5, + 640, + 12000, + 30001, + 301, + 5400, + 8800, + u16::MIN, + 10600, + 1500, + 390, + 7000, + 1100, + 9600, + 400, + 5800, + 32768, + 2900, + 750, + 4700, + 100, + ], + ); + assert_eq!(value.reduce_max(), u16::MAX); +} + +#[simd_test] +fn reduce_max_i32x16(simd: S) { + let value = i32x16::from_slice( + simd, + &[ + 42, + -7, + i32::MAX, + -700_000, + 81, + 19_000_000, + -5, + 640, + -12_000, + 30_001, + -301, + 5_400_000, + -8_800, + 230, + 10_600, + i32::MIN, + ], + ); + assert_eq!(value.reduce_max(), i32::MAX); +} + +#[simd_test] +fn reduce_max_u32x16(simd: S) { + let value = u32x16::from_slice( + simd, + &[ + 42, + u32::MAX, + 700_000, + 81, + 19_000_000, + 5, + 3_000_000_000, + 640, + u32::MIN, + 30_001, + 301, + 5_400_000, + 8_800, + 230, + 10_600, + 1_500_000_000, + ], + ); + assert_eq!(value.reduce_max(), u32::MAX); +} + +#[simd_test] +fn reduce_max_f64x8(simd: S) { + let value = f64x8::from_slice( + simd, + &[ + 4.0, + f64::NEG_INFINITY, + -7.0, + 2.0, + 8.0, + 1.0, + f64::INFINITY, + 5.0, + ], + ); + assert_eq!(value.reduce_max(), f64::INFINITY); + assert_eq!( + f64x8::from_slice(simd, &[0.0, -0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0]).reduce_max(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_i64x8(simd: S) { + let value = i64x8::from_slice( + simd, + &[ + 42, + -7, + i64::MAX, + -7_000_000_000, + 81, + 19_000_000, + i64::MIN, + 5_400_000_000, + ], + ); + assert_eq!(value.reduce_max(), i64::MAX); +} + +#[simd_test] +fn reduce_max_u64x8(simd: S) { + let value = u64x8::from_slice( + simd, + &[ + 42, + u64::MAX, + 7_000_000_000, + 81, + 19_000_000, + u64::MIN, + 5_400_000_000, + 12_345, + ], + ); + assert_eq!(value.reduce_max(), u64::MAX); +} diff --git a/fearless_simd_tests/tests/harness/ops/reduce_max_precise.rs b/fearless_simd_tests/tests/harness/ops/reduce_max_precise.rs new file mode 100644 index 00000000..aa5398b4 --- /dev/null +++ b/fearless_simd_tests/tests/harness/ops/reduce_max_precise.rs @@ -0,0 +1,252 @@ +// Copyright 2026 the Fearless_SIMD Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use fearless_simd::*; +use fearless_simd_dev_macros::simd_test; + +#[simd_test] +fn reduce_max_precise_f32x4(simd: S) { + let nan = f32::from_bits(0x7fc0_1234); + assert_eq!( + f32x4::from_slice(simd, &[nan, 4.0, 12.0, 7.0]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f32x4::from_slice(simd, &[4.0, 12.0, 7.0, nan]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f32x4::from_slice(simd, &[nan, 4.0, nan, 12.0]).reduce_max_precise(), + 12.0 + ); + assert!( + f32x4::from_slice(simd, &[nan; 4]) + .reduce_max_precise() + .is_nan() + ); + assert_eq!( + f32x4::from_slice(simd, &[f32::NEG_INFINITY, nan, 3.0, f32::INFINITY]).reduce_max_precise(), + f32::INFINITY + ); + assert_eq!( + f32x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_max_precise(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_precise_f64x2(simd: S) { + let nan = f64::from_bits(0x7ff8_0000_0000_1234); + assert_eq!( + f64x2::from_slice(simd, &[nan, 12.0]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f64x2::from_slice(simd, &[12.0, nan]).reduce_max_precise(), + 12.0 + ); + assert!( + f64x2::from_slice(simd, &[nan; 2]) + .reduce_max_precise() + .is_nan() + ); + assert_eq!( + f64x2::from_slice(simd, &[f64::NEG_INFINITY, f64::INFINITY]).reduce_max_precise(), + f64::INFINITY + ); + assert_eq!( + f64x2::from_slice(simd, &[0.0, -0.0]).reduce_max_precise(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_precise_f32x8(simd: S) { + let nan = f32::from_bits(0x7fc0_1234); + assert_eq!( + f32x8::from_slice(simd, &[nan, 4.0, 12.0, 7.0, 9.0, 3.0, 6.0, 8.0]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f32x8::from_slice(simd, &[4.0, 12.0, 7.0, 9.0, 3.0, 6.0, 8.0, nan]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f32x8::from_slice(simd, &[nan, 4.0, nan, 7.0, 12.0, nan, 6.0, 8.0]).reduce_max_precise(), + 12.0 + ); + assert!( + f32x8::from_slice(simd, &[nan; 8]) + .reduce_max_precise() + .is_nan() + ); + assert_eq!( + f32x8::from_slice( + simd, + &[ + f32::NEG_INFINITY, + nan, + 3.0, + 8.0, + 2.0, + f32::INFINITY, + 5.0, + 7.0 + ], + ) + .reduce_max_precise(), + f32::INFINITY + ); + let zeros = f32x8::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_max_precise(), 0.0); +} + +#[simd_test] +fn reduce_max_precise_f64x4(simd: S) { + let nan = f64::from_bits(0x7ff8_0000_0000_1234); + assert_eq!( + f64x4::from_slice(simd, &[nan, 4.0, 12.0, 7.0]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f64x4::from_slice(simd, &[4.0, 12.0, 7.0, nan]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f64x4::from_slice(simd, &[nan, 4.0, nan, 12.0]).reduce_max_precise(), + 12.0 + ); + assert!( + f64x4::from_slice(simd, &[nan; 4]) + .reduce_max_precise() + .is_nan() + ); + assert_eq!( + f64x4::from_slice(simd, &[f64::NEG_INFINITY, nan, 3.0, f64::INFINITY]).reduce_max_precise(), + f64::INFINITY + ); + assert_eq!( + f64x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_max_precise(), + 0.0 + ); +} + +#[simd_test] +fn reduce_max_precise_f32x16(simd: S) { + let nan = f32::from_bits(0x7fc0_1234); + let mut first = [4.0; 16]; + first[0] = nan; + first[13] = 12.0; + assert_eq!(f32x16::from_slice(simd, &first).reduce_max_precise(), 12.0); + + let mut last = [4.0; 16]; + last[15] = nan; + last[2] = 12.0; + assert_eq!(f32x16::from_slice(simd, &last).reduce_max_precise(), 12.0); + + let multiple = f32x16::from_fn(simd, |i| if i % 3 == 0 { nan } else { i as f32 }); + assert_eq!(multiple.reduce_max_precise(), 14.0); + assert!( + f32x16::from_slice(simd, &[nan; 16]) + .reduce_max_precise() + .is_nan() + ); + + let infinity = f32x16::from_fn(simd, |i| { + if i == 11 { + f32::INFINITY + } else if i == 3 { + f32::NEG_INFINITY + } else { + i as f32 + } + }); + assert_eq!(infinity.reduce_max_precise(), f32::INFINITY); + let zeros = f32x16::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_max_precise(), 0.0); +} + +#[simd_test] +fn reduce_max_precise_f64x8(simd: S) { + let nan = f64::from_bits(0x7ff8_0000_0000_1234); + assert_eq!( + f64x8::from_slice(simd, &[nan, 4.0, 12.0, 7.0, 9.0, 3.0, 6.0, 8.0]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f64x8::from_slice(simd, &[4.0, 12.0, 7.0, 9.0, 3.0, 6.0, 8.0, nan]).reduce_max_precise(), + 12.0 + ); + assert_eq!( + f64x8::from_slice(simd, &[nan, 4.0, nan, 7.0, 12.0, nan, 6.0, 8.0]).reduce_max_precise(), + 12.0 + ); + assert!( + f64x8::from_slice(simd, &[nan; 8]) + .reduce_max_precise() + .is_nan() + ); + assert_eq!( + f64x8::from_slice( + simd, + &[ + f64::NEG_INFINITY, + nan, + 3.0, + 8.0, + 2.0, + f64::INFINITY, + 5.0, + 7.0 + ], + ) + .reduce_max_precise(), + f64::INFINITY + ); + let zeros = f64x8::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_max_precise(), 0.0); +} + +#[simd_test] +fn reduce_max_precise_i8x16_alias(simd: S) { + let value = i8x16::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + i8::MIN, + 5, + 72, + ], + ); + assert_eq!(value.reduce_max_precise(), i8::MAX); +} + +#[simd_test] +fn reduce_max_precise_u64x8_alias(simd: S) { + let value = u64x8::from_slice( + simd, + &[ + 42, + u64::MAX, + 7_000_000_000, + 81, + 19_000_000, + u64::MIN, + 5_400_000_000, + 12_345, + ], + ); + assert_eq!(value.reduce_max_precise(), u64::MAX); +} diff --git a/fearless_simd_tests/tests/harness/ops/reduce_min.rs b/fearless_simd_tests/tests/harness/ops/reduce_min.rs new file mode 100644 index 00000000..68252aee --- /dev/null +++ b/fearless_simd_tests/tests/harness/ops/reduce_min.rs @@ -0,0 +1,677 @@ +// Copyright 2026 the Fearless_SIMD Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use fearless_simd::*; +use fearless_simd_dev_macros::simd_test; + +// One concrete test row per supported vector type. + +#[simd_test] +fn reduce_min_f32x4(simd: S) { + let value = f32x4::from_slice(simd, &[3.0, f32::NEG_INFINITY, -2.0, f32::INFINITY]); + assert_eq!(value.reduce_min(), f32::NEG_INFINITY); + assert_eq!( + f32x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_min(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_i8x16(simd: S) { + let value = i8x16::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + i8::MIN, + 5, + 72, + ], + ); + assert_eq!(value.reduce_min(), i8::MIN); +} + +#[simd_test] +fn reduce_min_u8x16(simd: S) { + let value = u8x16::from_slice( + simd, + &[ + 12, + 87, + 3, + u8::MAX, + 144, + 0, + 63, + 201, + 8, + 91, + 1, + 37, + 164, + 222, + 5, + 72, + ], + ); + assert_eq!(value.reduce_min(), u8::MIN); +} + +#[simd_test] +fn reduce_min_i16x8(simd: S) { + let value = i16x8::from_slice(simd, &[1200, -700, i16::MAX, 0, -9999, 42, i16::MIN, 7321]); + assert_eq!(value.reduce_min(), i16::MIN); +} + +#[simd_test] +fn reduce_min_u16x8(simd: S) { + let value = u16x8::from_slice(simd, &[1200, 700, u16::MAX, 81, 9999, u16::MIN, 42, 7321]); + assert_eq!(value.reduce_min(), u16::MIN); +} + +#[simd_test] +fn reduce_min_i32x4(simd: S) { + let value = i32x4::from_slice(simd, &[42, i32::MIN, i32::MAX, -700_000]); + assert_eq!(value.reduce_min(), i32::MIN); +} + +#[simd_test] +fn reduce_min_u32x4(simd: S) { + let value = u32x4::from_slice(simd, &[42, u32::MAX, 700_000, u32::MIN]); + assert_eq!(value.reduce_min(), u32::MIN); +} + +#[simd_test] +fn reduce_min_f64x2(simd: S) { + let value = f64x2::from_slice(simd, &[f64::INFINITY, f64::NEG_INFINITY]); + assert_eq!(value.reduce_min(), f64::NEG_INFINITY); + assert_eq!(f64x2::from_slice(simd, &[0.0, -0.0]).reduce_min(), 0.0); +} + +#[simd_test] +fn reduce_min_i64x2(simd: S) { + let value = i64x2::from_slice(simd, &[i64::MAX, i64::MIN]); + assert_eq!(value.reduce_min(), i64::MIN); +} + +#[simd_test] +fn reduce_min_u64x2(simd: S) { + let value = u64x2::from_slice(simd, &[u64::MAX, u64::MIN]); + assert_eq!(value.reduce_min(), u64::MIN); +} + +#[simd_test] +fn reduce_min_f32x8(simd: S) { + let value = f32x8::from_slice( + simd, + &[ + 3.0, + -2.0, + f32::INFINITY, + 9.0, + 1.0, + f32::NEG_INFINITY, + 4.0, + 8.0, + ], + ); + assert_eq!(value.reduce_min(), f32::NEG_INFINITY); + assert_eq!( + f32x8::from_slice(simd, &[0.0, -0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0]).reduce_min(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_i8x32(simd: S) { + let value = i8x32::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + i8::MIN, + 5, + 72, + -18, + 26, + 100, + -55, + 3, + -120, + 84, + 16, + -33, + 49, + 7, + -76, + 110, + 67, + -2, + 31, + ], + ); + assert_eq!(value.reduce_min(), i8::MIN); +} + +#[simd_test] +fn reduce_min_u8x32(simd: S) { + let value = u8x32::from_slice( + simd, + &[ + 12, + 87, + 3, + u8::MAX, + 144, + 0, + 63, + 201, + 8, + 91, + 1, + 37, + 164, + 222, + 5, + 72, + 18, + 126, + 100, + 55, + 13, + 220, + 84, + 16, + 133, + 49, + 7, + 176, + 110, + 67, + 2, + 31, + ], + ); + assert_eq!(value.reduce_min(), u8::MIN); +} + +#[simd_test] +fn reduce_min_i16x16(simd: S) { + let value = i16x16::from_slice( + simd, + &[ + 1200, + -700, + i16::MAX, + 0, + -9999, + 42, + -16384, + 7321, + 81, + -2222, + 19000, + -5, + 640, + -12000, + i16::MIN, + 30001, + ], + ); + assert_eq!(value.reduce_min(), i16::MIN); +} + +#[simd_test] +fn reduce_min_u16x16(simd: S) { + let value = u16x16::from_slice( + simd, + &[ + 1200, + 700, + u16::MAX, + 81, + 9999, + 32000, + 42, + 7321, + 18, + u16::MIN, + 2222, + 19000, + 5, + 640, + 12000, + 30001, + ], + ); + assert_eq!(value.reduce_min(), u16::MIN); +} + +#[simd_test] +fn reduce_min_i32x8(simd: S) { + let value = i32x8::from_slice( + simd, + &[42, -7, i32::MAX, -700_000, 81, i32::MIN, 19_000_000, -5], + ); + assert_eq!(value.reduce_min(), i32::MIN); +} + +#[simd_test] +fn reduce_min_u32x8(simd: S) { + let value = u32x8::from_slice( + simd, + &[ + 42, + u32::MAX, + 700_000, + 81, + u32::MIN, + 19_000_000, + 5, + 3_000_000_000, + ], + ); + assert_eq!(value.reduce_min(), u32::MIN); +} + +#[simd_test] +fn reduce_min_f64x4(simd: S) { + let value = f64x4::from_slice(simd, &[4.0, f64::INFINITY, -7.0, f64::NEG_INFINITY]); + assert_eq!(value.reduce_min(), f64::NEG_INFINITY); + assert_eq!( + f64x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_min(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_i64x4(simd: S) { + let value = i64x4::from_slice(simd, &[42, i64::MAX, i64::MIN, -7_000_000_000]); + assert_eq!(value.reduce_min(), i64::MIN); +} + +#[simd_test] +fn reduce_min_u64x4(simd: S) { + let value = u64x4::from_slice(simd, &[42, u64::MIN, u64::MAX, 7_000_000_000]); + assert_eq!(value.reduce_min(), u64::MIN); +} + +#[simd_test] +fn reduce_min_f32x16(simd: S) { + let value = f32x16::from_fn(simd, |i| { + if i == 13 { + f32::NEG_INFINITY + } else if i == 2 { + f32::INFINITY + } else { + i as f32 - 4.0 + } + }); + assert_eq!(value.reduce_min(), f32::NEG_INFINITY); + let zeros = f32x16::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_min(), 0.0); +} + +#[simd_test] +fn reduce_min_i8x64(simd: S) { + let value = i8x64::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + 115, + 5, + 72, + -18, + 26, + 100, + -55, + 3, + -120, + 84, + 16, + -33, + 49, + 7, + -76, + 110, + 67, + -2, + 31, + 54, + -88, + 23, + 106, + -15, + 39, + -70, + 11, + 96, + -4, + 58, + -112, + 29, + 75, + -47, + 1, + 82, + -25, + 34, + 119, + -61, + 14, + 69, + -9, + 46, + -101, + 20, + 89, + -36, + i8::MIN, + 6, + 77, + ], + ); + assert_eq!(value.reduce_min(), i8::MIN); +} + +#[simd_test] +fn reduce_min_u8x64(simd: S) { + let value = u8x64::from_slice( + simd, + &[ + 12, + 87, + 3, + u8::MAX, + 144, + 90, + 63, + 201, + 8, + 91, + 1, + 37, + 164, + 222, + 5, + 72, + 18, + 126, + 100, + 55, + 13, + 220, + 84, + 16, + 133, + 49, + 7, + 176, + 110, + 67, + 2, + 31, + 154, + u8::MIN, + 23, + 206, + 15, + 139, + 70, + 11, + 196, + 4, + 158, + 212, + 29, + 175, + 47, + 81, + 182, + 25, + 134, + 219, + 61, + 14, + 169, + 9, + 146, + 101, + 20, + 189, + 36, + 128, + 6, + 77, + ], + ); + assert_eq!(value.reduce_min(), u8::MIN); +} + +#[simd_test] +fn reduce_min_i16x32(simd: S) { + let value = i16x32::from_slice( + simd, + &[ + 1200, + -700, + i16::MAX, + 0, + -9999, + 42, + -16384, + 7321, + 81, + -2222, + 19000, + -5, + 640, + -12000, + 30001, + -301, + 5400, + -8800, + 230, + 10600, + -1500, + 390, + -7000, + 1100, + 9600, + -400, + 5800, + i16::MIN, + 2900, + 750, + -4700, + 100, + ], + ); + assert_eq!(value.reduce_min(), i16::MIN); +} + +#[simd_test] +fn reduce_min_u16x32(simd: S) { + let value = u16x32::from_slice( + simd, + &[ + 1200, + 700, + u16::MAX, + 81, + 9999, + 32000, + 42, + 7321, + 18, + 2222, + 19000, + 5, + 640, + 12000, + 30001, + 301, + 5400, + 8800, + u16::MIN, + 10600, + 1500, + 390, + 7000, + 1100, + 9600, + 400, + 5800, + 32768, + 2900, + 750, + 4700, + 100, + ], + ); + assert_eq!(value.reduce_min(), u16::MIN); +} + +#[simd_test] +fn reduce_min_i32x16(simd: S) { + let value = i32x16::from_slice( + simd, + &[ + 42, + -7, + i32::MAX, + -700_000, + 81, + 19_000_000, + -5, + 640, + -12_000, + 30_001, + -301, + 5_400_000, + -8_800, + 230, + 10_600, + i32::MIN, + ], + ); + assert_eq!(value.reduce_min(), i32::MIN); +} + +#[simd_test] +fn reduce_min_u32x16(simd: S) { + let value = u32x16::from_slice( + simd, + &[ + 42, + u32::MAX, + 700_000, + 81, + 19_000_000, + 5, + 3_000_000_000, + 640, + u32::MIN, + 30_001, + 301, + 5_400_000, + 8_800, + 230, + 10_600, + 1_500_000_000, + ], + ); + assert_eq!(value.reduce_min(), u32::MIN); +} + +#[simd_test] +fn reduce_min_f64x8(simd: S) { + let value = f64x8::from_slice( + simd, + &[ + 4.0, + f64::INFINITY, + -7.0, + 2.0, + 8.0, + 1.0, + f64::NEG_INFINITY, + 5.0, + ], + ); + assert_eq!(value.reduce_min(), f64::NEG_INFINITY); + assert_eq!( + f64x8::from_slice(simd, &[0.0, -0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0]).reduce_min(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_i64x8(simd: S) { + let value = i64x8::from_slice( + simd, + &[ + 42, + -7, + i64::MAX, + -7_000_000_000, + 81, + 19_000_000, + i64::MIN, + 5_400_000_000, + ], + ); + assert_eq!(value.reduce_min(), i64::MIN); +} + +#[simd_test] +fn reduce_min_u64x8(simd: S) { + let value = u64x8::from_slice( + simd, + &[ + 42, + u64::MAX, + 7_000_000_000, + 81, + 19_000_000, + u64::MIN, + 5_400_000_000, + 12_345, + ], + ); + assert_eq!(value.reduce_min(), u64::MIN); +} diff --git a/fearless_simd_tests/tests/harness/ops/reduce_min_precise.rs b/fearless_simd_tests/tests/harness/ops/reduce_min_precise.rs new file mode 100644 index 00000000..79411b68 --- /dev/null +++ b/fearless_simd_tests/tests/harness/ops/reduce_min_precise.rs @@ -0,0 +1,252 @@ +// Copyright 2026 the Fearless_SIMD Authors +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use fearless_simd::*; +use fearless_simd_dev_macros::simd_test; + +#[simd_test] +fn reduce_min_precise_f32x4(simd: S) { + let nan = f32::from_bits(0x7fc0_1234); + assert_eq!( + f32x4::from_slice(simd, &[nan, 4.0, -2.0, 7.0]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f32x4::from_slice(simd, &[4.0, -2.0, 7.0, nan]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f32x4::from_slice(simd, &[nan, 4.0, nan, -2.0]).reduce_min_precise(), + -2.0 + ); + assert!( + f32x4::from_slice(simd, &[nan; 4]) + .reduce_min_precise() + .is_nan() + ); + assert_eq!( + f32x4::from_slice(simd, &[f32::INFINITY, nan, 3.0, f32::NEG_INFINITY]).reduce_min_precise(), + f32::NEG_INFINITY + ); + assert_eq!( + f32x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_min_precise(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_precise_f64x2(simd: S) { + let nan = f64::from_bits(0x7ff8_0000_0000_1234); + assert_eq!( + f64x2::from_slice(simd, &[nan, -2.0]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f64x2::from_slice(simd, &[-2.0, nan]).reduce_min_precise(), + -2.0 + ); + assert!( + f64x2::from_slice(simd, &[nan; 2]) + .reduce_min_precise() + .is_nan() + ); + assert_eq!( + f64x2::from_slice(simd, &[f64::INFINITY, f64::NEG_INFINITY]).reduce_min_precise(), + f64::NEG_INFINITY + ); + assert_eq!( + f64x2::from_slice(simd, &[0.0, -0.0]).reduce_min_precise(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_precise_f32x8(simd: S) { + let nan = f32::from_bits(0x7fc0_1234); + assert_eq!( + f32x8::from_slice(simd, &[nan, 4.0, -2.0, 7.0, 9.0, 3.0, 6.0, 8.0]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f32x8::from_slice(simd, &[4.0, -2.0, 7.0, 9.0, 3.0, 6.0, 8.0, nan]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f32x8::from_slice(simd, &[nan, 4.0, nan, 7.0, -2.0, nan, 6.0, 8.0]).reduce_min_precise(), + -2.0 + ); + assert!( + f32x8::from_slice(simd, &[nan; 8]) + .reduce_min_precise() + .is_nan() + ); + assert_eq!( + f32x8::from_slice( + simd, + &[ + f32::INFINITY, + nan, + 3.0, + 8.0, + 2.0, + f32::NEG_INFINITY, + 5.0, + 7.0 + ], + ) + .reduce_min_precise(), + f32::NEG_INFINITY + ); + let zeros = f32x8::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_min_precise(), 0.0); +} + +#[simd_test] +fn reduce_min_precise_f64x4(simd: S) { + let nan = f64::from_bits(0x7ff8_0000_0000_1234); + assert_eq!( + f64x4::from_slice(simd, &[nan, 4.0, -2.0, 7.0]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f64x4::from_slice(simd, &[4.0, -2.0, 7.0, nan]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f64x4::from_slice(simd, &[nan, 4.0, nan, -2.0]).reduce_min_precise(), + -2.0 + ); + assert!( + f64x4::from_slice(simd, &[nan; 4]) + .reduce_min_precise() + .is_nan() + ); + assert_eq!( + f64x4::from_slice(simd, &[f64::INFINITY, nan, 3.0, f64::NEG_INFINITY]).reduce_min_precise(), + f64::NEG_INFINITY + ); + assert_eq!( + f64x4::from_slice(simd, &[0.0, -0.0, 0.0, -0.0]).reduce_min_precise(), + 0.0 + ); +} + +#[simd_test] +fn reduce_min_precise_f32x16(simd: S) { + let nan = f32::from_bits(0x7fc0_1234); + let mut first = [4.0; 16]; + first[0] = nan; + first[13] = -2.0; + assert_eq!(f32x16::from_slice(simd, &first).reduce_min_precise(), -2.0); + + let mut last = [4.0; 16]; + last[15] = nan; + last[2] = -2.0; + assert_eq!(f32x16::from_slice(simd, &last).reduce_min_precise(), -2.0); + + let multiple = f32x16::from_fn(simd, |i| if i % 3 == 0 { nan } else { i as f32 }); + assert_eq!(multiple.reduce_min_precise(), 1.0); + assert!( + f32x16::from_slice(simd, &[nan; 16]) + .reduce_min_precise() + .is_nan() + ); + + let infinity = f32x16::from_fn(simd, |i| { + if i == 11 { + f32::NEG_INFINITY + } else if i == 3 { + f32::INFINITY + } else { + i as f32 + } + }); + assert_eq!(infinity.reduce_min_precise(), f32::NEG_INFINITY); + let zeros = f32x16::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_min_precise(), 0.0); +} + +#[simd_test] +fn reduce_min_precise_f64x8(simd: S) { + let nan = f64::from_bits(0x7ff8_0000_0000_1234); + assert_eq!( + f64x8::from_slice(simd, &[nan, 4.0, -2.0, 7.0, 9.0, 3.0, 6.0, 8.0]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f64x8::from_slice(simd, &[4.0, -2.0, 7.0, 9.0, 3.0, 6.0, 8.0, nan]).reduce_min_precise(), + -2.0 + ); + assert_eq!( + f64x8::from_slice(simd, &[nan, 4.0, nan, 7.0, -2.0, nan, 6.0, 8.0]).reduce_min_precise(), + -2.0 + ); + assert!( + f64x8::from_slice(simd, &[nan; 8]) + .reduce_min_precise() + .is_nan() + ); + assert_eq!( + f64x8::from_slice( + simd, + &[ + f64::INFINITY, + nan, + 3.0, + 8.0, + 2.0, + f64::NEG_INFINITY, + 5.0, + 7.0 + ], + ) + .reduce_min_precise(), + f64::NEG_INFINITY + ); + let zeros = f64x8::from_fn(simd, |i| if i % 2 == 0 { 0.0 } else { -0.0 }); + assert_eq!(zeros.reduce_min_precise(), 0.0); +} + +#[simd_test] +fn reduce_min_precise_i8x16_alias(simd: S) { + let value = i8x16::from_slice( + simd, + &[ + 12, + -7, + 44, + i8::MAX, + -99, + 0, + 63, + -42, + 8, + 91, + -1, + 37, + -64, + i8::MIN, + 5, + 72, + ], + ); + assert_eq!(value.reduce_min_precise(), i8::MIN); +} + +#[simd_test] +fn reduce_min_precise_u64x8_alias(simd: S) { + let value = u64x8::from_slice( + simd, + &[ + 42, + u64::MAX, + 7_000_000_000, + 81, + 19_000_000, + u64::MIN, + 5_400_000_000, + 12_345, + ], + ); + assert_eq!(value.reduce_min_precise(), u64::MIN); +}