// Simd x86 specific implementations -*- C++ -*- // Copyright (C) 2020-2022 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . #ifndef _GLIBCXX_EXPERIMENTAL_SIMD_X86_H_ #define _GLIBCXX_EXPERIMENTAL_SIMD_X86_H_ #if __cplusplus >= 201703L #if !_GLIBCXX_SIMD_X86INTRIN #error \ "simd_x86.h may only be included when MMX or SSE on x86(_64) are available" #endif _GLIBCXX_SIMD_BEGIN_NAMESPACE // __to_masktype {{{ // Given return <__int_for_sizeof_t, N>. For _SimdWrapper and // __vector_type_t. template _GLIBCXX_SIMD_INTRINSIC constexpr _SimdWrapper<__int_for_sizeof_t<_Tp>, _Np> __to_masktype(_SimdWrapper<_Tp, _Np> __x) { return reinterpret_cast<__vector_type_t<__int_for_sizeof_t<_Tp>, _Np>>(__x._M_data); } template , _VectorTraits<_TV>>, typename _Up = __int_for_sizeof_t> _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_Up, _TVT::_S_full_size> __to_masktype(_TV __x) { return reinterpret_cast<__vector_type_t<_Up, _TVT::_S_full_size>>(__x); } // }}} // __interleave128_lo {{{ template , typename _Trait = _VectorTraits<_Tp>> _GLIBCXX_SIMD_INTRINSIC constexpr _Tp __interleave128_lo(const _Ap& __av, const _Bp& __bv) { const _Tp __a(__av); const _Tp __b(__bv); if constexpr (sizeof(_Tp) == 16 && _Trait::_S_full_size == 2) return _Tp{__a[0], __b[0]}; else if constexpr (sizeof(_Tp) == 16 && _Trait::_S_full_size == 4) return _Tp{__a[0], __b[0], __a[1], __b[1]}; else if constexpr (sizeof(_Tp) == 16 && _Trait::_S_full_size == 8) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[2], __b[2], __a[3], __b[3]}; else if constexpr (sizeof(_Tp) == 16 && _Trait::_S_full_size == 16) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[2], __b[2], __a[3], __b[3], __a[4], __b[4], __a[5], __b[5], __a[6], __b[6], __a[7], __b[7]}; else if constexpr (sizeof(_Tp) == 32 && _Trait::_S_full_size == 4) return _Tp{__a[0], __b[0], __a[2], __b[2]}; else if constexpr (sizeof(_Tp) == 32 && _Trait::_S_full_size == 8) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[4], __b[4], __a[5], __b[5]}; else if constexpr (sizeof(_Tp) == 32 && _Trait::_S_full_size == 16) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[2], __b[2], __a[3], __b[3], __a[8], __b[8], __a[9], __b[9], __a[10], __b[10], __a[11], __b[11]}; else if constexpr (sizeof(_Tp) == 32 && _Trait::_S_full_size == 32) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[2], __b[2], __a[3], __b[3], __a[4], __b[4], __a[5], __b[5], __a[6], __b[6], __a[7], __b[7], __a[16], __b[16], __a[17], __b[17], __a[18], __b[18], __a[19], __b[19], __a[20], __b[20], __a[21], __b[21], __a[22], __b[22], __a[23], __b[23]}; else if constexpr (sizeof(_Tp) == 64 && _Trait::_S_full_size == 8) return _Tp{__a[0], __b[0], __a[2], __b[2], __a[4], __b[4], __a[6], __b[6]}; else if constexpr (sizeof(_Tp) == 64 && _Trait::_S_full_size == 16) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[4], __b[4], __a[5], __b[5], __a[8], __b[8], __a[9], __b[9], __a[12], __b[12], __a[13], __b[13]}; else if constexpr (sizeof(_Tp) == 64 && _Trait::_S_full_size == 32) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[2], __b[2], __a[3], __b[3], __a[8], __b[8], __a[9], __b[9], __a[10], __b[10], __a[11], __b[11], __a[16], __b[16], __a[17], __b[17], __a[18], __b[18], __a[19], __b[19], __a[24], __b[24], __a[25], __b[25], __a[26], __b[26], __a[27], __b[27]}; else if constexpr (sizeof(_Tp) == 64 && _Trait::_S_full_size == 64) return _Tp{__a[0], __b[0], __a[1], __b[1], __a[2], __b[2], __a[3], __b[3], __a[4], __b[4], __a[5], __b[5], __a[6], __b[6], __a[7], __b[7], __a[16], __b[16], __a[17], __b[17], __a[18], __b[18], __a[19], __b[19], __a[20], __b[20], __a[21], __b[21], __a[22], __b[22], __a[23], __b[23], __a[32], __b[32], __a[33], __b[33], __a[34], __b[34], __a[35], __b[35], __a[36], __b[36], __a[37], __b[37], __a[38], __b[38], __a[39], __b[39], __a[48], __b[48], __a[49], __b[49], __a[50], __b[50], __a[51], __b[51], __a[52], __b[52], __a[53], __b[53], __a[54], __b[54], __a[55], __b[55]}; else __assert_unreachable<_Tp>(); } // }}} // __is_zero{{{ template > _GLIBCXX_SIMD_INTRINSIC constexpr bool __is_zero(_Tp __a) { if (!__builtin_is_constant_evaluated()) { if constexpr (__have_avx) { if constexpr (_TVT::template _S_is) return _mm256_testz_ps(__a, __a); else if constexpr (_TVT::template _S_is) return _mm256_testz_pd(__a, __a); else if constexpr (sizeof(_Tp) == 32) return _mm256_testz_si256(__to_intrin(__a), __to_intrin(__a)); else if constexpr (_TVT::template _S_is) return _mm_testz_ps(__to_intrin(__a), __to_intrin(__a)); else if constexpr (_TVT::template _S_is) return _mm_testz_pd(__a, __a); else return _mm_testz_si128(__to_intrin(__a), __to_intrin(__a)); } else if constexpr (__have_sse4_1) return _mm_testz_si128(__intrin_bitcast<__m128i>(__a), __intrin_bitcast<__m128i>(__a)); } else if constexpr (sizeof(_Tp) <= 8) return reinterpret_cast<__int_for_sizeof_t<_Tp>>(__a) == 0; else { const auto __b = __vector_bitcast<_LLong>(__a); if constexpr (sizeof(__b) == 16) return (__b[0] | __b[1]) == 0; else if constexpr (sizeof(__b) == 32) return __is_zero(__lo128(__b) | __hi128(__b)); else if constexpr (sizeof(__b) == 64) return __is_zero(__lo256(__b) | __hi256(__b)); else __assert_unreachable<_Tp>(); } } // }}} // __movemask{{{ template > _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_CONST int __movemask(_Tp __a) { if constexpr (sizeof(_Tp) == 32) { if constexpr (_TVT::template _S_is) return _mm256_movemask_ps(__to_intrin(__a)); else if constexpr (_TVT::template _S_is) return _mm256_movemask_pd(__to_intrin(__a)); else return _mm256_movemask_epi8(__to_intrin(__a)); } else if constexpr (_TVT::template _S_is) return _mm_movemask_ps(__to_intrin(__a)); else if constexpr (_TVT::template _S_is) return _mm_movemask_pd(__to_intrin(__a)); else return _mm_movemask_epi8(__to_intrin(__a)); } // }}} // __testz{{{ template > _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_CONST constexpr int __testz(_TI __a, _TI __b) { static_assert(is_same_v<_TI, __intrinsic_type_t>); if (!__builtin_is_constant_evaluated()) { if constexpr (sizeof(_TI) == 32) { if constexpr (_TVT::template _S_is) return _mm256_testz_ps(__to_intrin(__a), __to_intrin(__b)); else if constexpr (_TVT::template _S_is) return _mm256_testz_pd(__to_intrin(__a), __to_intrin(__b)); else return _mm256_testz_si256(__to_intrin(__a), __to_intrin(__b)); } else if constexpr (_TVT::template _S_is && __have_avx) return _mm_testz_ps(__to_intrin(__a), __to_intrin(__b)); else if constexpr (_TVT::template _S_is && __have_avx) return _mm_testz_pd(__to_intrin(__a), __to_intrin(__b)); else if constexpr (__have_sse4_1) return _mm_testz_si128(__intrin_bitcast<__m128i>(__to_intrin(__a)), __intrin_bitcast<__m128i>(__to_intrin(__b))); else return __movemask(0 == __and(__a, __b)) != 0; } else return __is_zero(__and(__a, __b)); } // }}} // __testc{{{ // requires SSE4.1 or above template > _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_CONST constexpr int __testc(_TI __a, _TI __b) { static_assert(is_same_v<_TI, __intrinsic_type_t>); if (__builtin_is_constant_evaluated()) return __is_zero(__andnot(__a, __b)); if constexpr (sizeof(_TI) == 32) { if constexpr (_TVT::template _S_is) return _mm256_testc_ps(__a, __b); else if constexpr (_TVT::template _S_is) return _mm256_testc_pd(__a, __b); else return _mm256_testc_si256(__to_intrin(__a), __to_intrin(__b)); } else if constexpr (_TVT::template _S_is && __have_avx) return _mm_testc_ps(__to_intrin(__a), __to_intrin(__b)); else if constexpr (_TVT::template _S_is && __have_avx) return _mm_testc_pd(__to_intrin(__a), __to_intrin(__b)); else { static_assert(is_same_v<_TI, _TI> && __have_sse4_1); return _mm_testc_si128(__intrin_bitcast<__m128i>(__to_intrin(__a)), __intrin_bitcast<__m128i>(__to_intrin(__b))); } } // }}} // __testnzc{{{ template > _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_CONST constexpr int __testnzc(_TI __a, _TI __b) { static_assert(is_same_v<_TI, __intrinsic_type_t>); if (!__builtin_is_constant_evaluated()) { if constexpr (sizeof(_TI) == 32) { if constexpr (_TVT::template _S_is) return _mm256_testnzc_ps(__a, __b); else if constexpr (_TVT::template _S_is) return _mm256_testnzc_pd(__a, __b); else return _mm256_testnzc_si256(__to_intrin(__a), __to_intrin(__b)); } else if constexpr (_TVT::template _S_is && __have_avx) return _mm_testnzc_ps(__to_intrin(__a), __to_intrin(__b)); else if constexpr (_TVT::template _S_is && __have_avx) return _mm_testnzc_pd(__to_intrin(__a), __to_intrin(__b)); else if constexpr (__have_sse4_1) return _mm_testnzc_si128(__intrin_bitcast<__m128i>(__to_intrin(__a)), __intrin_bitcast<__m128i>(__to_intrin(__b))); else return __movemask(0 == __and(__a, __b)) == 0 && __movemask(0 == __andnot(__a, __b)) == 0; } else return !(__is_zero(__and(__a, __b)) || __is_zero(__andnot(__a, __b))); } // }}} // __xzyw{{{ // shuffles the complete vector, swapping the inner two quarters. Often useful // for AVX for fixing up a shuffle result. template > _GLIBCXX_SIMD_INTRINSIC _Tp __xzyw(_Tp __a) { if constexpr (sizeof(_Tp) == 16) { const auto __x = __vector_bitcast, float, int>>(__a); return reinterpret_cast<_Tp>( decltype(__x){__x[0], __x[2], __x[1], __x[3]}); } else if constexpr (sizeof(_Tp) == 32) { const auto __x = __vector_bitcast, double, _LLong>>(__a); return reinterpret_cast<_Tp>( decltype(__x){__x[0], __x[2], __x[1], __x[3]}); } else if constexpr (sizeof(_Tp) == 64) { const auto __x = __vector_bitcast, double, _LLong>>(__a); return reinterpret_cast<_Tp>(decltype(__x){__x[0], __x[1], __x[4], __x[5], __x[2], __x[3], __x[6], __x[7]}); } else __assert_unreachable<_Tp>(); } // }}} // __maskload_epi32{{{ template _GLIBCXX_SIMD_INTRINSIC auto __maskload_epi32(const int* __ptr, _Tp __k) { if constexpr (sizeof(__k) == 16) return _mm_maskload_epi32(__ptr, __k); else return _mm256_maskload_epi32(__ptr, __k); } // }}} // __maskload_epi64{{{ template _GLIBCXX_SIMD_INTRINSIC auto __maskload_epi64(const _LLong* __ptr, _Tp __k) { if constexpr (sizeof(__k) == 16) return _mm_maskload_epi64(__ptr, __k); else return _mm256_maskload_epi64(__ptr, __k); } // }}} // __maskload_ps{{{ template _GLIBCXX_SIMD_INTRINSIC auto __maskload_ps(const float* __ptr, _Tp __k) { if constexpr (sizeof(__k) == 16) return _mm_maskload_ps(__ptr, __k); else return _mm256_maskload_ps(__ptr, __k); } // }}} // __maskload_pd{{{ template _GLIBCXX_SIMD_INTRINSIC auto __maskload_pd(const double* __ptr, _Tp __k) { if constexpr (sizeof(__k) == 16) return _mm_maskload_pd(__ptr, __k); else return _mm256_maskload_pd(__ptr, __k); } // }}} #ifdef __clang__ template _GLIBCXX_SIMD_INTRINSIC constexpr auto __movm(_Kp __k) noexcept { static_assert(is_unsigned_v<_Kp>); if constexpr (sizeof(_Tp) == 1 && __have_avx512bw) { if constexpr (_Np <= 16 && __have_avx512vl) return __builtin_ia32_cvtmask2b128(__k); else if constexpr (_Np <= 32 && __have_avx512vl) return __builtin_ia32_cvtmask2b256(__k); else return __builtin_ia32_cvtmask2b512(__k); } else if constexpr (sizeof(_Tp) == 2 && __have_avx512bw) { if constexpr (_Np <= 8 && __have_avx512vl) return __builtin_ia32_cvtmask2w128(__k); else if constexpr (_Np <= 16 && __have_avx512vl) return __builtin_ia32_cvtmask2w256(__k); else return __builtin_ia32_cvtmask2w512(__k); } else if constexpr (sizeof(_Tp) == 4 && __have_avx512dq) { if constexpr (_Np <= 4 && __have_avx512vl) return __builtin_ia32_cvtmask2d128(__k); else if constexpr (_Np <= 8 && __have_avx512vl) return __builtin_ia32_cvtmask2d256(__k); else return __builtin_ia32_cvtmask2d512(__k); } else if constexpr (sizeof(_Tp) == 8 && __have_avx512dq) { if constexpr (_Np <= 2 && __have_avx512vl) return __builtin_ia32_cvtmask2q128(__k); else if constexpr (_Np <= 4 && __have_avx512vl) return __builtin_ia32_cvtmask2q256(__k); else return __builtin_ia32_cvtmask2q512(__k); } else __assert_unreachable<_Tp>(); } #endif // __clang__ #ifdef _GLIBCXX_SIMD_WORKAROUND_PR85048 #include "simd_x86_conversions.h" #endif // ISA & type detection {{{ template constexpr bool __is_sse_ps() { return __have_sse && is_same_v<_Tp, float> && sizeof(__intrinsic_type_t<_Tp, _Np>) == 16; } template constexpr bool __is_sse_pd() { return __have_sse2 && is_same_v<_Tp, double> && sizeof(__intrinsic_type_t<_Tp, _Np>) == 16; } template constexpr bool __is_avx_ps() { return __have_avx && is_same_v<_Tp, float> && sizeof(__intrinsic_type_t<_Tp, _Np>) == 32; } template constexpr bool __is_avx_pd() { return __have_avx && is_same_v<_Tp, double> && sizeof(__intrinsic_type_t<_Tp, _Np>) == 32; } template constexpr bool __is_avx512_ps() { return __have_avx512f && is_same_v<_Tp, float> && sizeof(__intrinsic_type_t<_Tp, _Np>) == 64; } template constexpr bool __is_avx512_pd() { return __have_avx512f && is_same_v<_Tp, double> && sizeof(__intrinsic_type_t<_Tp, _Np>) == 64; } // }}} struct _MaskImplX86Mixin; // _CommonImplX86 {{{ struct _CommonImplX86 : _CommonImplBuiltin { #ifdef _GLIBCXX_SIMD_WORKAROUND_PR85048 // _S_converts_via_decomposition {{{ template static constexpr bool _S_converts_via_decomposition() { if constexpr (is_integral_v< _From> && is_integral_v<_To> && sizeof(_From) == 8 && _ToSize == 16) return (sizeof(_To) == 2 && !__have_ssse3) || (sizeof(_To) == 1 && !__have_avx512f); else if constexpr (is_floating_point_v<_From> && is_integral_v<_To>) return ((sizeof(_From) == 4 || sizeof(_From) == 8) && sizeof(_To) == 8 && !__have_avx512dq) || (sizeof(_From) == 8 && sizeof(_To) == 4 && !__have_sse4_1 && _ToSize == 16); else if constexpr ( is_integral_v<_From> && is_floating_point_v<_To> && sizeof(_From) == 8 && !__have_avx512dq) return (sizeof(_To) == 4 && _ToSize == 16) || (sizeof(_To) == 8 && _ToSize < 64); else return false; } template static inline constexpr bool __converts_via_decomposition_v = _S_converts_via_decomposition<_From, _To, _ToSize>(); // }}} #endif // _S_store {{{ using _CommonImplBuiltin::_S_store; template _GLIBCXX_SIMD_INTRINSIC static constexpr void _S_store(_SimdWrapper<_Tp, _Np> __x, void* __addr) { constexpr size_t _Bytes = _Np * sizeof(_Tp); if (__builtin_is_constant_evaluated()) _CommonImplBuiltin::_S_store(__x, __addr); else if constexpr ((_Bytes & (_Bytes - 1)) != 0 && __have_avx512bw_vl) { const auto __v = __to_intrin(__x); if constexpr (_Bytes & 1) { if constexpr (_Bytes < 16) _mm_mask_storeu_epi8(__addr, 0xffffu >> (16 - _Bytes), __intrin_bitcast<__m128i>(__v)); else if constexpr (_Bytes < 32) _mm256_mask_storeu_epi8(__addr, 0xffffffffu >> (32 - _Bytes), __intrin_bitcast<__m256i>(__v)); else _mm512_mask_storeu_epi8(__addr, 0xffffffffffffffffull >> (64 - _Bytes), __intrin_bitcast<__m512i>(__v)); } else if constexpr (_Bytes & 2) { if constexpr (_Bytes < 16) _mm_mask_storeu_epi16(__addr, 0xffu >> (8 - _Bytes / 2), __intrin_bitcast<__m128i>(__v)); else if constexpr (_Bytes < 32) _mm256_mask_storeu_epi16(__addr, 0xffffu >> (16 - _Bytes / 2), __intrin_bitcast<__m256i>(__v)); else _mm512_mask_storeu_epi16(__addr, 0xffffffffull >> (32 - _Bytes / 2), __intrin_bitcast<__m512i>(__v)); } else if constexpr (_Bytes & 4) { if constexpr (_Bytes < 16) _mm_mask_storeu_epi32(__addr, 0xfu >> (4 - _Bytes / 4), __intrin_bitcast<__m128i>(__v)); else if constexpr (_Bytes < 32) _mm256_mask_storeu_epi32(__addr, 0xffu >> (8 - _Bytes / 4), __intrin_bitcast<__m256i>(__v)); else _mm512_mask_storeu_epi32(__addr, 0xffffull >> (16 - _Bytes / 4), __intrin_bitcast<__m512i>(__v)); } else { static_assert( _Bytes > 16, "_Bytes < 16 && (_Bytes & 7) == 0 && (_Bytes & (_Bytes " "- 1)) != 0 is impossible"); if constexpr (_Bytes < 32) _mm256_mask_storeu_epi64(__addr, 0xfu >> (4 - _Bytes / 8), __intrin_bitcast<__m256i>(__v)); else _mm512_mask_storeu_epi64(__addr, 0xffull >> (8 - _Bytes / 8), __intrin_bitcast<__m512i>(__v)); } } else _CommonImplBuiltin::_S_store(__x, __addr); } // }}} // _S_store_bool_array(_BitMask) {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr void _S_store_bool_array(const _BitMask<_Np, _Sanitized> __x, bool* __mem) { if (__builtin_is_constant_evaluated()) _CommonImplBuiltin::_S_store_bool_array(__x, __mem); else if constexpr (__have_avx512bw_vl) // don't care for BW w/o VL _S_store<_Np>(1 & __vector_bitcast<_UChar, _Np>( [=]() constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { if constexpr (_Np <= 16) return _mm_movm_epi8(__x._M_to_bits()); else if constexpr (_Np <= 32) return _mm256_movm_epi8(__x._M_to_bits()); else if constexpr (_Np <= 64) return _mm512_movm_epi8(__x._M_to_bits()); else __assert_unreachable<_SizeConstant<_Np>>(); }()), __mem); else if constexpr (__have_bmi2) { if constexpr (_Np <= 4) _S_store<_Np>(_pdep_u32(__x._M_to_bits(), 0x01010101U), __mem); else __execute_n_times<__div_roundup(_Np, sizeof(size_t))>( [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { constexpr size_t __offset = __i * sizeof(size_t); constexpr int __todo = std::min(sizeof(size_t), _Np - __offset); if constexpr (__todo == 1) __mem[__offset] = __x[__offset]; else { const auto __bools = #ifdef __x86_64__ _pdep_u64(__x.template _M_extract<__offset>().to_ullong(), 0x0101010101010101ULL); #else // __x86_64__ _pdep_u32( __x.template _M_extract<__offset>()._M_to_bits(), 0x01010101U); #endif // __x86_64__ _S_store<__todo>(__bools, __mem + __offset); } }); } else if constexpr (__have_sse2 && _Np > 7) __execute_n_times<__div_roundup(_Np, 16)>([&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { constexpr int __offset = __i * 16; constexpr int __todo = std::min(16, int(_Np) - __offset); const int __bits = __x.template _M_extract<__offset>()._M_to_bits(); __vector_type16_t<_UChar> __bools; if constexpr (__have_avx512f) { auto __as32bits = _mm512_maskz_mov_epi32(__bits, __to_intrin( __vector_broadcast<16>(1))); auto __as16bits = __xzyw(_mm256_packs_epi32(__lo256(__as32bits), __todo > 8 ? __hi256(__as32bits) : __m256i())); __bools = __vector_bitcast<_UChar>( _mm_packs_epi16(__lo128(__as16bits), __hi128(__as16bits))); } else { using _V = __vector_type_t<_UChar, 16>; auto __tmp = _mm_cvtsi32_si128(__bits); __tmp = _mm_unpacklo_epi8(__tmp, __tmp); __tmp = _mm_unpacklo_epi16(__tmp, __tmp); __tmp = _mm_unpacklo_epi32(__tmp, __tmp); _V __tmp2 = reinterpret_cast<_V>(__tmp); __tmp2 &= _V{1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128}; // mask bit index __bools = (__tmp2 == 0) + 1; // 0xff -> 0x00 | 0x00 -> 0x01 } _S_store<__todo>(__bools, __mem + __offset); }); else _CommonImplBuiltin::_S_store_bool_array(__x, __mem); } // }}} // _S_blend_avx512 {{{ // Returns: __k ? __b : __a // TODO: reverse __a and __b to match COND_EXPR // Requires: _TV to be a __vector_type_t matching valuetype for the bitmask // __k template _GLIBCXX_SIMD_INTRINSIC static _TV _S_blend_avx512(const _Kp __k, const _TV __a, const _TV __b) noexcept { static_assert(__is_vector_type_v<_TV>); using _Tp = typename _VectorTraits<_TV>::value_type; static_assert(sizeof(_TV) >= 16); static_assert(sizeof(_Tp) <= 8); #ifdef __clang__ return __movm<_VectorTraits<_TV>::_S_full_size, _Tp>(__k) ? __b : __a; #else using _IntT = conditional_t<(sizeof(_Tp) > 2), conditional_t, conditional_t>; [[maybe_unused]] const auto __aa = __vector_bitcast<_IntT>(__a); [[maybe_unused]] const auto __bb = __vector_bitcast<_IntT>(__b); if constexpr (sizeof(_TV) == 64) { if constexpr (sizeof(_Tp) == 1) return reinterpret_cast<_TV>( __builtin_ia32_blendmb_512_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 2) return reinterpret_cast<_TV>( __builtin_ia32_blendmw_512_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 4 && is_floating_point_v<_Tp>) return __builtin_ia32_blendmps_512_mask(__a, __b, __k); else if constexpr (sizeof(_Tp) == 4) return reinterpret_cast<_TV>( __builtin_ia32_blendmd_512_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 8 && is_floating_point_v<_Tp>) return __builtin_ia32_blendmpd_512_mask(__a, __b, __k); else if constexpr (sizeof(_Tp) == 8) return reinterpret_cast<_TV>( __builtin_ia32_blendmq_512_mask(__aa, __bb, __k)); } else if constexpr (sizeof(_TV) == 32) { if constexpr (sizeof(_Tp) == 1) return reinterpret_cast<_TV>( __builtin_ia32_blendmb_256_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 2) return reinterpret_cast<_TV>( __builtin_ia32_blendmw_256_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 4 && is_floating_point_v<_Tp>) return __builtin_ia32_blendmps_256_mask(__a, __b, __k); else if constexpr (sizeof(_Tp) == 4) return reinterpret_cast<_TV>( __builtin_ia32_blendmd_256_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 8 && is_floating_point_v<_Tp>) return __builtin_ia32_blendmpd_256_mask(__a, __b, __k); else if constexpr (sizeof(_Tp) == 8) return reinterpret_cast<_TV>( __builtin_ia32_blendmq_256_mask(__aa, __bb, __k)); } else if constexpr (sizeof(_TV) == 16) { if constexpr (sizeof(_Tp) == 1) return reinterpret_cast<_TV>( __builtin_ia32_blendmb_128_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 2) return reinterpret_cast<_TV>( __builtin_ia32_blendmw_128_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 4 && is_floating_point_v<_Tp>) return __builtin_ia32_blendmps_128_mask(__a, __b, __k); else if constexpr (sizeof(_Tp) == 4) return reinterpret_cast<_TV>( __builtin_ia32_blendmd_128_mask(__aa, __bb, __k)); else if constexpr (sizeof(_Tp) == 8 && is_floating_point_v<_Tp>) return __builtin_ia32_blendmpd_128_mask(__a, __b, __k); else if constexpr (sizeof(_Tp) == 8) return reinterpret_cast<_TV>( __builtin_ia32_blendmq_128_mask(__aa, __bb, __k)); } #endif } // }}} // _S_blend_intrin {{{ // Returns: __k ? __b : __a // TODO: reverse __a and __b to match COND_EXPR // Requires: _Tp to be an intrinsic type (integers blend per byte) and 16/32 // Bytes wide template _GLIBCXX_SIMD_INTRINSIC static _Tp _S_blend_intrin(_Tp __k, _Tp __a, _Tp __b) noexcept { static_assert(is_same_v); constexpr struct { _GLIBCXX_SIMD_INTRINSIC __m128 operator()(__m128 __a, __m128 __b, __m128 __k) const noexcept { return __builtin_ia32_blendvps(__a, __b, __k); } _GLIBCXX_SIMD_INTRINSIC __m128d operator()(__m128d __a, __m128d __b, __m128d __k) const noexcept { return __builtin_ia32_blendvpd(__a, __b, __k); } _GLIBCXX_SIMD_INTRINSIC __m128i operator()(__m128i __a, __m128i __b, __m128i __k) const noexcept { return reinterpret_cast<__m128i>( __builtin_ia32_pblendvb128(reinterpret_cast<__v16qi>(__a), reinterpret_cast<__v16qi>(__b), reinterpret_cast<__v16qi>(__k))); } _GLIBCXX_SIMD_INTRINSIC __m256 operator()(__m256 __a, __m256 __b, __m256 __k) const noexcept { return __builtin_ia32_blendvps256(__a, __b, __k); } _GLIBCXX_SIMD_INTRINSIC __m256d operator()(__m256d __a, __m256d __b, __m256d __k) const noexcept { return __builtin_ia32_blendvpd256(__a, __b, __k); } _GLIBCXX_SIMD_INTRINSIC __m256i operator()(__m256i __a, __m256i __b, __m256i __k) const noexcept { if constexpr (__have_avx2) return reinterpret_cast<__m256i>( __builtin_ia32_pblendvb256(reinterpret_cast<__v32qi>(__a), reinterpret_cast<__v32qi>(__b), reinterpret_cast<__v32qi>(__k))); else return reinterpret_cast<__m256i>( __builtin_ia32_blendvps256(reinterpret_cast<__v8sf>(__a), reinterpret_cast<__v8sf>(__b), reinterpret_cast<__v8sf>(__k))); } } __eval; return __eval(__a, __b, __k); } // }}} // _S_blend {{{ // Returns: __k ? __at1 : __at0 // TODO: reverse __at0 and __at1 to match COND_EXPR template _GLIBCXX_SIMD_INTRINSIC static constexpr _SimdWrapper<_Tp, _Np> _S_blend(_SimdWrapper __k, _SimdWrapper<_Tp, _Np> __at0, _SimdWrapper<_Tp, _Np> __at1) { static_assert(is_same_v<_Tp, _Tp> && __have_avx512f); if (__k._M_is_constprop() && __at0._M_is_constprop() && __at1._M_is_constprop()) return __generate_from_n_evaluations<_Np, __vector_type_t<_Tp, _Np>>( [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __k[__i] ? __at1[__i] : __at0[__i]; }); else if constexpr (sizeof(__at0) == 64 || (__have_avx512vl && sizeof(__at0) >= 16)) return _S_blend_avx512(__k._M_data, __at0._M_data, __at1._M_data); else { static_assert((__have_avx512vl && sizeof(__at0) < 16) || !__have_avx512vl); constexpr size_t __size = (__have_avx512vl ? 16 : 64) / sizeof(_Tp); return __vector_bitcast<_Tp, _Np>( _S_blend_avx512(__k._M_data, __vector_bitcast<_Tp, __size>(__at0), __vector_bitcast<_Tp, __size>(__at1))); } } template _GLIBCXX_SIMD_INTRINSIC static constexpr _SimdWrapper<_Tp, _Np> _S_blend(_SimdWrapper<__int_for_sizeof_t<_Tp>, _Np> __k, _SimdWrapper<_Tp, _Np> __at0, _SimdWrapper<_Tp, _Np> __at1) { const auto __kk = __wrapper_bitcast<_Tp>(__k); if (__builtin_is_constant_evaluated() || (__kk._M_is_constprop() && __at0._M_is_constprop() && __at1._M_is_constprop())) { auto __r = __or(__andnot(__kk, __at0), __and(__kk, __at1)); if (__r._M_is_constprop()) return __r; } if constexpr (((__have_avx512f && sizeof(__at0) == 64) || __have_avx512vl) && (sizeof(_Tp) >= 4 || __have_avx512bw)) // convert to bitmask and call overload above return _S_blend( _SimdWrapper( __make_dependent_t<_Tp, _MaskImplX86Mixin>::_S_to_bits(__k) ._M_to_bits()), __at0, __at1); else { // Since GCC does not assume __k to be a mask, using the builtin // conditional operator introduces an extra compare against 0 before // blending. So we rather call the intrinsic here. if constexpr (__have_sse4_1) return _S_blend_intrin(__to_intrin(__kk), __to_intrin(__at0), __to_intrin(__at1)); else return __or(__andnot(__kk, __at0), __and(__kk, __at1)); } } // }}} }; // }}} // _SimdImplX86 {{{ template struct _SimdImplX86 : _SimdImplBuiltin<_Abi> { using _Base = _SimdImplBuiltin<_Abi>; template using _MaskMember = typename _Base::template _MaskMember<_Tp>; template static constexpr size_t _S_full_size = _Abi::template _S_full_size<_Tp>; template static constexpr size_t _S_size = _Abi::template _S_size<_Tp>; template static constexpr size_t _S_max_store_size = (sizeof(_Tp) >= 4 && __have_avx512f) || __have_avx512bw ? 64 : (is_floating_point_v<_Tp>&& __have_avx) || __have_avx2 ? 32 : 16; using _MaskImpl = typename _Abi::_MaskImpl; // _S_masked_load {{{ template static inline _SimdWrapper<_Tp, _Np> _S_masked_load(_SimdWrapper<_Tp, _Np> __merge, _MaskMember<_Tp> __k, const _Up* __mem) noexcept { static_assert(_Np == _S_size<_Tp>); if constexpr (is_same_v<_Tp, _Up> || // no conversion (sizeof(_Tp) == sizeof(_Up) && is_integral_v< _Tp> == is_integral_v<_Up>) // conversion via bit // reinterpretation ) { [[maybe_unused]] const auto __intrin = __to_intrin(__merge); if constexpr ((__is_avx512_abi<_Abi>() || __have_avx512bw_vl) && sizeof(_Tp) == 1) { const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(__intrin) == 16) __merge = __vector_bitcast<_Tp, _Np>( _mm_mask_loadu_epi8(__intrin, __kk, __mem)); else if constexpr (sizeof(__merge) == 32) __merge = __vector_bitcast<_Tp, _Np>( _mm256_mask_loadu_epi8(__intrin, __kk, __mem)); else if constexpr (sizeof(__merge) == 64) __merge = __vector_bitcast<_Tp, _Np>( _mm512_mask_loadu_epi8(__intrin, __kk, __mem)); else __assert_unreachable<_Tp>(); } else if constexpr ((__is_avx512_abi<_Abi>() || __have_avx512bw_vl) && sizeof(_Tp) == 2) { const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(__intrin) == 16) __merge = __vector_bitcast<_Tp, _Np>( _mm_mask_loadu_epi16(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 32) __merge = __vector_bitcast<_Tp, _Np>( _mm256_mask_loadu_epi16(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 64) __merge = __vector_bitcast<_Tp, _Np>( _mm512_mask_loadu_epi16(__intrin, __kk, __mem)); else __assert_unreachable<_Tp>(); } else if constexpr ((__is_avx512_abi<_Abi>() || __have_avx512vl) && sizeof(_Tp) == 4 && is_integral_v<_Up>) { const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(__intrin) == 16) __merge = __vector_bitcast<_Tp, _Np>( _mm_mask_loadu_epi32(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 32) __merge = __vector_bitcast<_Tp, _Np>( _mm256_mask_loadu_epi32(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 64) __merge = __vector_bitcast<_Tp, _Np>( _mm512_mask_loadu_epi32(__intrin, __kk, __mem)); else __assert_unreachable<_Tp>(); } else if constexpr ((__is_avx512_abi<_Abi>() || __have_avx512vl) && sizeof(_Tp) == 4 && is_floating_point_v<_Up>) { const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(__intrin) == 16) __merge = __vector_bitcast<_Tp, _Np>( _mm_mask_loadu_ps(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 32) __merge = __vector_bitcast<_Tp, _Np>( _mm256_mask_loadu_ps(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 64) __merge = __vector_bitcast<_Tp, _Np>( _mm512_mask_loadu_ps(__intrin, __kk, __mem)); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx2 && sizeof(_Tp) == 4 && is_integral_v<_Up>) { static_assert(sizeof(__intrin) == 16 || sizeof(__intrin) == 32); __merge = __or(__andnot(__vector_bitcast<_Tp>(__k), __merge._M_data), __vector_bitcast<_Tp, _Np>( __maskload_epi32(reinterpret_cast(__mem), __to_intrin(__k)))); } else if constexpr (__have_avx && sizeof(_Tp) == 4) { static_assert(sizeof(__intrin) == 16 || sizeof(__intrin) == 32); __merge = __or(__andnot(__vector_bitcast<_Tp>(__k), __merge._M_data), __vector_bitcast<_Tp, _Np>( __maskload_ps(reinterpret_cast(__mem), __to_intrin(__k)))); } else if constexpr ((__is_avx512_abi<_Abi>() || __have_avx512vl) && sizeof(_Tp) == 8 && is_integral_v<_Up>) { const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(__intrin) == 16) __merge = __vector_bitcast<_Tp, _Np>( _mm_mask_loadu_epi64(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 32) __merge = __vector_bitcast<_Tp, _Np>( _mm256_mask_loadu_epi64(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 64) __merge = __vector_bitcast<_Tp, _Np>( _mm512_mask_loadu_epi64(__intrin, __kk, __mem)); else __assert_unreachable<_Tp>(); } else if constexpr ((__is_avx512_abi<_Abi>() || __have_avx512vl) && sizeof(_Tp) == 8 && is_floating_point_v<_Up>) { const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(__intrin) == 16) __merge = __vector_bitcast<_Tp, _Np>( _mm_mask_loadu_pd(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 32) __merge = __vector_bitcast<_Tp, _Np>( _mm256_mask_loadu_pd(__intrin, __kk, __mem)); else if constexpr (sizeof(__intrin) == 64) __merge = __vector_bitcast<_Tp, _Np>( _mm512_mask_loadu_pd(__intrin, __kk, __mem)); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx2 && sizeof(_Tp) == 8 && is_integral_v<_Up>) { static_assert(sizeof(__intrin) == 16 || sizeof(__intrin) == 32); __merge = __or(__andnot(__vector_bitcast<_Tp>(__k), __merge._M_data), __vector_bitcast<_Tp, _Np>(__maskload_epi64( reinterpret_cast(__mem), __to_intrin(__k)))); } else if constexpr (__have_avx && sizeof(_Tp) == 8) { static_assert(sizeof(__intrin) == 16 || sizeof(__intrin) == 32); __merge = __or(__andnot(__vector_bitcast<_Tp>(__k), __merge._M_data), __vector_bitcast<_Tp, _Np>( __maskload_pd(reinterpret_cast(__mem), __to_intrin(__k)))); } else _BitOps::_S_bit_iteration(_MaskImpl::_S_to_bits(__k), [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { __merge._M_set(__i, static_cast<_Tp>(__mem[__i])); }); } /* Very uncertain, that the following improves anything. Needs benchmarking * before it's activated. else if constexpr (sizeof(_Up) <= 8 && // no long double !__converts_via_decomposition_v< _Up, _Tp, sizeof(__merge)> // conversion via decomposition // is better handled via the // bit_iteration fallback below ) { // TODO: copy pattern from _S_masked_store, which doesn't resort to // fixed_size using _Ap = simd_abi::deduce_t<_Up, _Np>; using _ATraits = _SimdTraits<_Up, _Ap>; using _AImpl = typename _ATraits::_SimdImpl; typename _ATraits::_SimdMember __uncvted{}; typename _ATraits::_MaskMember __kk = _Ap::_MaskImpl::template _S_convert<_Up>(__k); __uncvted = _AImpl::_S_masked_load(__uncvted, __kk, __mem); _SimdConverter<_Up, _Ap, _Tp, _Abi> __converter; _Base::_S_masked_assign(__k, __merge, __converter(__uncvted)); } */ else __merge = _Base::_S_masked_load(__merge, __k, __mem); return __merge; } // }}} // _S_masked_store_nocvt {{{ template _GLIBCXX_SIMD_INTRINSIC static void _S_masked_store_nocvt(_SimdWrapper<_Tp, _Np> __v, _Tp* __mem, _SimdWrapper __k) { [[maybe_unused]] const auto __vi = __to_intrin(__v); if constexpr (sizeof(__vi) == 64) { static_assert(sizeof(__v) == 64 && __have_avx512f); if constexpr (__have_avx512bw && sizeof(_Tp) == 1) _mm512_mask_storeu_epi8(__mem, __k, __vi); else if constexpr (__have_avx512bw && sizeof(_Tp) == 2) _mm512_mask_storeu_epi16(__mem, __k, __vi); else if constexpr (__have_avx512f && sizeof(_Tp) == 4) { if constexpr (is_integral_v<_Tp>) _mm512_mask_storeu_epi32(__mem, __k, __vi); else _mm512_mask_storeu_ps(__mem, __k, __vi); } else if constexpr (__have_avx512f && sizeof(_Tp) == 8) { if constexpr (is_integral_v<_Tp>) _mm512_mask_storeu_epi64(__mem, __k, __vi); else _mm512_mask_storeu_pd(__mem, __k, __vi); } else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__vi) == 32) { if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 1) _mm256_mask_storeu_epi8(__mem, __k, __vi); else if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 2) _mm256_mask_storeu_epi16(__mem, __k, __vi); else if constexpr (__have_avx512vl && sizeof(_Tp) == 4) { if constexpr (is_integral_v<_Tp>) _mm256_mask_storeu_epi32(__mem, __k, __vi); else _mm256_mask_storeu_ps(__mem, __k, __vi); } else if constexpr (__have_avx512vl && sizeof(_Tp) == 8) { if constexpr (is_integral_v<_Tp>) _mm256_mask_storeu_epi64(__mem, __k, __vi); else _mm256_mask_storeu_pd(__mem, __k, __vi); } else if constexpr (__have_avx512f && (sizeof(_Tp) >= 4 || __have_avx512bw)) { // use a 512-bit maskstore, using zero-extension of the bitmask _S_masked_store_nocvt( _SimdWrapper64<_Tp>( __intrin_bitcast<__vector_type64_t<_Tp>>(__v._M_data)), __mem, _SimdWrapper(__k._M_data)); } else _S_masked_store_nocvt(__v, __mem, _MaskImpl::template _S_to_maskvector< __int_for_sizeof_t<_Tp>, _Np>(__k)); } else if constexpr (sizeof(__vi) == 16) { if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 1) _mm_mask_storeu_epi8(__mem, __k, __vi); else if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 2) _mm_mask_storeu_epi16(__mem, __k, __vi); else if constexpr (__have_avx512vl && sizeof(_Tp) == 4) { if constexpr (is_integral_v<_Tp>) _mm_mask_storeu_epi32(__mem, __k, __vi); else _mm_mask_storeu_ps(__mem, __k, __vi); } else if constexpr (__have_avx512vl && sizeof(_Tp) == 8) { if constexpr (is_integral_v<_Tp>) _mm_mask_storeu_epi64(__mem, __k, __vi); else _mm_mask_storeu_pd(__mem, __k, __vi); } else if constexpr (__have_avx512f && (sizeof(_Tp) >= 4 || __have_avx512bw)) { // use a 512-bit maskstore, using zero-extension of the bitmask _S_masked_store_nocvt( _SimdWrapper64<_Tp>( __intrin_bitcast<__intrinsic_type64_t<_Tp>>(__v._M_data)), __mem, _SimdWrapper(__k._M_data)); } else _S_masked_store_nocvt(__v, __mem, _MaskImpl::template _S_to_maskvector< __int_for_sizeof_t<_Tp>, _Np>(__k)); } else __assert_unreachable<_Tp>(); } template _GLIBCXX_SIMD_INTRINSIC static void _S_masked_store_nocvt(_SimdWrapper<_Tp, _Np> __v, _Tp* __mem, _SimdWrapper<__int_for_sizeof_t<_Tp>, _Np> __k) { if constexpr (sizeof(__v) <= 16) { [[maybe_unused]] const auto __vi = __intrin_bitcast<__m128i>(__as_vector(__v)); [[maybe_unused]] const auto __ki = __intrin_bitcast<__m128i>(__as_vector(__k)); if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 1) _mm_mask_storeu_epi8(__mem, _mm_movepi8_mask(__ki), __vi); else if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 2) _mm_mask_storeu_epi16(__mem, _mm_movepi16_mask(__ki), __vi); else if constexpr (__have_avx2 && sizeof(_Tp) == 4 && is_integral_v<_Tp>) _mm_maskstore_epi32(reinterpret_cast(__mem), __ki, __vi); else if constexpr (__have_avx && sizeof(_Tp) == 4) _mm_maskstore_ps(reinterpret_cast(__mem), __ki, __vector_bitcast(__vi)); else if constexpr (__have_avx2 && sizeof(_Tp) == 8 && is_integral_v<_Tp>) _mm_maskstore_epi64(reinterpret_cast<_LLong*>(__mem), __ki, __vi); else if constexpr (__have_avx && sizeof(_Tp) == 8) _mm_maskstore_pd(reinterpret_cast(__mem), __ki, __vector_bitcast(__vi)); else _Base::_S_masked_store_nocvt(__v, __mem, __k); } else if constexpr (sizeof(__v) == 32) { [[maybe_unused]] const auto __vi = __intrin_bitcast<__m256i>(__as_vector(__v)); [[maybe_unused]] const auto __ki = __intrin_bitcast<__m256i>(__as_vector(__k)); if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 1) _mm256_mask_storeu_epi8(__mem, _mm256_movepi8_mask(__ki), __vi); else if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 2) _mm256_mask_storeu_epi16(__mem, _mm256_movepi16_mask(__ki), __vi); else if constexpr (__have_avx2 && sizeof(_Tp) == 4 && is_integral_v<_Tp>) _mm256_maskstore_epi32(reinterpret_cast(__mem), __ki, __vi); else if constexpr (sizeof(_Tp) == 4) _mm256_maskstore_ps(reinterpret_cast(__mem), __ki, __vector_bitcast(__v)); else if constexpr (__have_avx2 && sizeof(_Tp) == 8 && is_integral_v<_Tp>) _mm256_maskstore_epi64(reinterpret_cast<_LLong*>(__mem), __ki, __vi); else if constexpr (__have_avx && sizeof(_Tp) == 8) _mm256_maskstore_pd(reinterpret_cast(__mem), __ki, __vector_bitcast(__v)); else _Base::_S_masked_store_nocvt(__v, __mem, __k); } else __assert_unreachable<_Tp>(); } // }}} // _S_masked_store {{{ template _GLIBCXX_SIMD_INTRINSIC static void _S_masked_store(const _SimdWrapper<_Tp, _Np> __v, _Up* __mem, const _MaskMember<_Tp> __k) noexcept { if constexpr (is_integral_v< _Tp> && is_integral_v<_Up> && sizeof(_Tp) > sizeof(_Up) && __have_avx512f && (sizeof(_Tp) >= 4 || __have_avx512bw) && (sizeof(__v) == 64 || __have_avx512vl)) { // truncating store const auto __vi = __to_intrin(__v); const auto __kk = _MaskImpl::_S_to_bits(__k)._M_to_bits(); if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 4 && sizeof(__vi) == 64) _mm512_mask_cvtepi64_storeu_epi32(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 4 && sizeof(__vi) == 32) _mm256_mask_cvtepi64_storeu_epi32(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 4 && sizeof(__vi) == 16) _mm_mask_cvtepi64_storeu_epi32(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 2 && sizeof(__vi) == 64) _mm512_mask_cvtepi64_storeu_epi16(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 2 && sizeof(__vi) == 32) _mm256_mask_cvtepi64_storeu_epi16(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 2 && sizeof(__vi) == 16) _mm_mask_cvtepi64_storeu_epi16(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 1 && sizeof(__vi) == 64) _mm512_mask_cvtepi64_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 1 && sizeof(__vi) == 32) _mm256_mask_cvtepi64_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 8 && sizeof(_Up) == 1 && sizeof(__vi) == 16) _mm_mask_cvtepi64_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 4 && sizeof(_Up) == 2 && sizeof(__vi) == 64) _mm512_mask_cvtepi32_storeu_epi16(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 4 && sizeof(_Up) == 2 && sizeof(__vi) == 32) _mm256_mask_cvtepi32_storeu_epi16(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 4 && sizeof(_Up) == 2 && sizeof(__vi) == 16) _mm_mask_cvtepi32_storeu_epi16(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 4 && sizeof(_Up) == 1 && sizeof(__vi) == 64) _mm512_mask_cvtepi32_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 4 && sizeof(_Up) == 1 && sizeof(__vi) == 32) _mm256_mask_cvtepi32_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 4 && sizeof(_Up) == 1 && sizeof(__vi) == 16) _mm_mask_cvtepi32_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 2 && sizeof(_Up) == 1 && sizeof(__vi) == 64) _mm512_mask_cvtepi16_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 2 && sizeof(_Up) == 1 && sizeof(__vi) == 32) _mm256_mask_cvtepi16_storeu_epi8(__mem, __kk, __vi); else if constexpr (sizeof(_Tp) == 2 && sizeof(_Up) == 1 && sizeof(__vi) == 16) _mm_mask_cvtepi16_storeu_epi8(__mem, __kk, __vi); else __assert_unreachable<_Tp>(); } else _Base::_S_masked_store(__v, __mem, __k); } // }}} // _S_multiplies {{{ template > _GLIBCXX_SIMD_INTRINSIC static constexpr _V _S_multiplies(_V __x, _V __y) { using _Tp = typename _VVT::value_type; if (__builtin_is_constant_evaluated() || __x._M_is_constprop() || __y._M_is_constprop()) return __as_vector(__x) * __as_vector(__y); else if constexpr (sizeof(_Tp) == 1) { if constexpr (sizeof(_V) == 2) { const auto __xs = reinterpret_cast(__x._M_data); const auto __ys = reinterpret_cast(__y._M_data); return reinterpret_cast<__vector_type_t<_Tp, 2>>(short( ((__xs * __ys) & 0xff) | ((__xs >> 8) * (__ys & 0xff00)))); } else if constexpr (sizeof(_V) == 4 && _VVT::_S_partial_width == 3) { const auto __xi = reinterpret_cast(__x._M_data); const auto __yi = reinterpret_cast(__y._M_data); return reinterpret_cast<__vector_type_t<_Tp, 3>>( ((__xi * __yi) & 0xff) | (((__xi >> 8) * (__yi & 0xff00)) & 0xff00) | ((__xi >> 16) * (__yi & 0xff0000))); } else if constexpr (sizeof(_V) == 4) { const auto __xi = reinterpret_cast(__x._M_data); const auto __yi = reinterpret_cast(__y._M_data); return reinterpret_cast<__vector_type_t<_Tp, 4>>( ((__xi * __yi) & 0xff) | (((__xi >> 8) * (__yi & 0xff00)) & 0xff00) | (((__xi >> 16) * (__yi & 0xff0000)) & 0xff0000) | ((__xi >> 24) * (__yi & 0xff000000u))); } else if constexpr (sizeof(_V) == 8 && __have_avx2 && is_signed_v<_Tp>) return __convert( __vector_bitcast(_mm_cvtepi8_epi16(__to_intrin(__x))) * __vector_bitcast(_mm_cvtepi8_epi16(__to_intrin(__y)))); else if constexpr (sizeof(_V) == 8 && __have_avx2 && is_unsigned_v<_Tp>) return __convert( __vector_bitcast(_mm_cvtepu8_epi16(__to_intrin(__x))) * __vector_bitcast(_mm_cvtepu8_epi16(__to_intrin(__y)))); else { // codegen of `x*y` is suboptimal (as of GCC 9.0.1) constexpr size_t __full_size = _VVT::_S_full_size; constexpr int _Np = sizeof(_V) >= 16 ? __full_size / 2 : 8; using _ShortW = _SimdWrapper; const _ShortW __even = __vector_bitcast(__x) * __vector_bitcast(__y); _ShortW __high_byte = _ShortW()._M_data - 256; //[&]() { asm("" : "+x"(__high_byte._M_data)); }(); const _ShortW __odd = (__vector_bitcast(__x) >> 8) * (__vector_bitcast(__y) & __high_byte._M_data); if constexpr (__have_avx512bw && sizeof(_V) > 2) return _CommonImplX86::_S_blend_avx512( 0xaaaa'aaaa'aaaa'aaaaLL, __vector_bitcast<_Tp>(__even), __vector_bitcast<_Tp>(__odd)); else if constexpr (__have_sse4_1 && sizeof(_V) > 2) return _CommonImplX86::_S_blend_intrin(__to_intrin( __high_byte), __to_intrin(__even), __to_intrin(__odd)); else return __to_intrin( __or(__andnot(__high_byte, __even), __odd)); } } else return _Base::_S_multiplies(__x, __y); } // }}} // _S_divides {{{ #ifdef _GLIBCXX_SIMD_WORKAROUND_PR90993 template _GLIBCXX_SIMD_INTRINSIC static constexpr _SimdWrapper<_Tp, _Np> _S_divides(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { if (!__builtin_is_constant_evaluated() && !__builtin_constant_p(__y._M_data)) if constexpr (is_integral_v<_Tp> && sizeof(_Tp) <= 4) { // use divps - codegen of `x/y` is suboptimal (as of GCC 9.0.1) // Note that using floating-point division is likely to raise the // *Inexact* exception flag and thus appears like an invalid // "as-if" transformation. However, C++ doesn't specify how the // fpenv can be observed and points to C. C says that function // calls are assumed to potentially raise fp exceptions, unless // documented otherwise. Consequently, operator/, which is a // function call, may raise fp exceptions. /*const struct _CsrGuard { const unsigned _M_data = _mm_getcsr(); _CsrGuard() { _mm_setcsr(0x9f80); // turn off FP exceptions and flush-to-zero } ~_CsrGuard() { _mm_setcsr(_M_data); } } __csr;*/ using _Float = conditional_t; constexpr size_t __n_intermediate = std::min(_Np, (__have_avx512f ? 64 : __have_avx ? 32 : 16) / sizeof(_Float)); using _FloatV = __vector_type_t<_Float, __n_intermediate>; constexpr size_t __n_floatv = __div_roundup(_Np, __n_intermediate); using _R = __vector_type_t<_Tp, _Np>; const auto __xf = __convert_all<_FloatV, __n_floatv>(__x); const auto __yf = __convert_all<_FloatV, __n_floatv>( _Abi::__make_padding_nonzero(__as_vector(__y))); return __call_with_n_evaluations<__n_floatv>( [](auto... __quotients) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __vector_convert<_R>(__quotients...); }, [&__xf, &__yf](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA -> _SimdWrapper<_Float, __n_intermediate> { #if __RECIPROCAL_MATH__ // If -freciprocal-math is active, using the `/` operator is // incorrect because it may be translated to an imprecise // multiplication with reciprocal. We need to use inline // assembly to force a real division. _FloatV __r; if constexpr (__have_avx) // -mno-sse2avx is irrelevant // because once -mavx is given, GCC // emits VEX encoded vdivp[sd] { if constexpr (sizeof(_Tp) == 4) asm("vdivpd\t{%2, %1, %0|%0, %1, %2}" : "=x"(__r) : "x"(__xf[__i]), "x"(__yf[__i])); else asm("vdivps\t{%2, %1, %0|%0, %1, %2}" : "=x"(__r) : "x"(__xf[__i]), "x"(__yf[__i])); } else { __r = __xf[__i]; if constexpr (sizeof(_Tp) == 4) asm("divpd\t{%1, %0|%0, %1}" : "=x"(__r) : "x"(__yf[__i])); else asm("divps\t{%1, %0|%0, %1}" : "=x"(__r) : "x"(__yf[__i])); } return __r; #else return __xf[__i] / __yf[__i]; #endif }); } /* 64-bit int division is potentially optimizable via double division if * the value in __x is small enough and the conversion between * int<->double is efficient enough: else if constexpr (is_integral_v<_Tp> && is_unsigned_v<_Tp> && sizeof(_Tp) == 8) { if constexpr (__have_sse4_1 && sizeof(__x) == 16) { if (_mm_test_all_zeros(__x, __m128i{0xffe0'0000'0000'0000ull, 0xffe0'0000'0000'0000ull})) { __x._M_data | 0x __vector_convert<__m128d>(__x._M_data) } } } */ return _Base::_S_divides(__x, __y); } #else using _Base::_S_divides; #endif // _GLIBCXX_SIMD_WORKAROUND_PR90993 // }}} // _S_modulus {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr _SimdWrapper<_Tp, _Np> _S_modulus(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { if (__builtin_is_constant_evaluated() || __builtin_constant_p(__y._M_data) || sizeof(_Tp) >= 8) return _Base::_S_modulus(__x, __y); else return _Base::_S_minus(__x, _S_multiplies(__y, _S_divides(__x, __y))); } // }}} // _S_bit_shift_left {{{ // Notes on UB. C++2a [expr.shift] says: // -1- [...] The operands shall be of integral or unscoped enumeration type // and integral promotions are performed. The type of the result is that // of the promoted left operand. The behavior is undefined if the right // operand is negative, or greater than or equal to the width of the // promoted left operand. // -2- The value of E1 << E2 is the unique value congruent to E1×2^E2 modulo // 2^N, where N is the width of the type of the result. // // C++17 [expr.shift] says: // -2- The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated // bits are zero-filled. If E1 has an unsigned type, the value of the // result is E1 × 2^E2 , reduced modulo one more than the maximum value // representable in the result type. Otherwise, if E1 has a signed type // and non-negative value, and E1 × 2^E2 is representable in the // corresponding unsigned type of the result type, then that value, // converted to the result type, is the resulting value; otherwise, the // behavior is undefined. // // Consequences: // With C++2a signed and unsigned types have the same UB // characteristics: // - left shift is not UB for 0 <= RHS < max(32, #bits(T)) // // With C++17 there's little room for optimizations because the standard // requires all shifts to happen on promoted integrals (i.e. int). Thus, // short and char shifts must assume shifts affect bits of neighboring // values. #ifndef _GLIBCXX_SIMD_NO_SHIFT_OPT template > constexpr inline _GLIBCXX_CONST static typename _TVT::type _S_bit_shift_left(_Tp __xx, int __y) { using _V = typename _TVT::type; using _Up = typename _TVT::value_type; _V __x = __xx; [[maybe_unused]] const auto __ix = __to_intrin(__x); if (__builtin_is_constant_evaluated()) return __x << __y; #if __cplusplus > 201703 // after C++17, signed shifts have no UB, and behave just like unsigned // shifts else if constexpr (sizeof(_Up) == 1 && is_signed_v<_Up>) return __vector_bitcast<_Up>( _S_bit_shift_left(__vector_bitcast>(__x), __y)); #endif else if constexpr (sizeof(_Up) == 1) { // (cf. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83894) if (__builtin_constant_p(__y)) { if (__y == 0) return __x; else if (__y == 1) return __x + __x; else if (__y == 2) { __x = __x + __x; return __x + __x; } else if (__y > 2 && __y < 8) { if constexpr (sizeof(__x) > sizeof(unsigned)) { const _UChar __mask = 0xff << __y; // precomputed vector return __vector_bitcast<_Up>( __vector_bitcast<_UChar>( __vector_bitcast(__x) << __y) & __mask); } else { const unsigned __mask = (0xff & (0xff << __y)) * 0x01010101u; return reinterpret_cast<_V>( static_cast<__int_for_sizeof_t<_V>>( unsigned( reinterpret_cast<__int_for_sizeof_t<_V>>(__x) << __y) & __mask)); } } else if (__y >= 8 && __y < 32) return _V(); else __builtin_unreachable(); } // general strategy in the following: use an sllv instead of sll // instruction, because it's 2 to 4 times faster: else if constexpr (__have_avx512bw_vl && sizeof(__x) == 16) return __vector_bitcast<_Up>(_mm256_cvtepi16_epi8( _mm256_sllv_epi16(_mm256_cvtepi8_epi16(__ix), _mm256_set1_epi16(__y)))); else if constexpr (__have_avx512bw && sizeof(__x) == 32) return __vector_bitcast<_Up>(_mm512_cvtepi16_epi8( _mm512_sllv_epi16(_mm512_cvtepi8_epi16(__ix), _mm512_set1_epi16(__y)))); else if constexpr (__have_avx512bw && sizeof(__x) == 64) { const auto __shift = _mm512_set1_epi16(__y); return __vector_bitcast<_Up>( __concat(_mm512_cvtepi16_epi8(_mm512_sllv_epi16( _mm512_cvtepi8_epi16(__lo256(__ix)), __shift)), _mm512_cvtepi16_epi8(_mm512_sllv_epi16( _mm512_cvtepi8_epi16(__hi256(__ix)), __shift)))); } else if constexpr (__have_avx2 && sizeof(__x) == 32) { #if 1 const auto __shift = _mm_cvtsi32_si128(__y); auto __k = _mm256_sll_epi16(_mm256_slli_epi16(~__m256i(), 8), __shift); __k |= _mm256_srli_epi16(__k, 8); return __vector_bitcast<_Up>(_mm256_sll_epi32(__ix, __shift) & __k); #else const _Up __k = 0xff << __y; return __vector_bitcast<_Up>(__vector_bitcast(__x) << __y) & __k; #endif } else { const auto __shift = _mm_cvtsi32_si128(__y); auto __k = _mm_sll_epi16(_mm_slli_epi16(~__m128i(), 8), __shift); __k |= _mm_srli_epi16(__k, 8); return __intrin_bitcast<_V>(_mm_sll_epi16(__ix, __shift) & __k); } } return __x << __y; } template > constexpr inline _GLIBCXX_CONST static typename _TVT::type _S_bit_shift_left(_Tp __xx, typename _TVT::type __y) { using _V = typename _TVT::type; using _Up = typename _TVT::value_type; _V __x = __xx; [[maybe_unused]] const auto __ix = __to_intrin(__x); [[maybe_unused]] const auto __iy = __to_intrin(__y); if (__builtin_is_constant_evaluated()) return __x << __y; #if __cplusplus > 201703 // after C++17, signed shifts have no UB, and behave just like unsigned // shifts else if constexpr (is_signed_v<_Up>) return __vector_bitcast<_Up>( _S_bit_shift_left(__vector_bitcast>(__x), __vector_bitcast>(__y))); #endif else if constexpr (sizeof(_Up) == 1) { if constexpr (sizeof __ix == 64 && __have_avx512bw) return __vector_bitcast<_Up>(__concat( _mm512_cvtepi16_epi8( _mm512_sllv_epi16(_mm512_cvtepu8_epi16(__lo256(__ix)), _mm512_cvtepu8_epi16(__lo256(__iy)))), _mm512_cvtepi16_epi8( _mm512_sllv_epi16(_mm512_cvtepu8_epi16(__hi256(__ix)), _mm512_cvtepu8_epi16(__hi256(__iy)))))); else if constexpr (sizeof __ix == 32 && __have_avx512bw) return __vector_bitcast<_Up>(_mm512_cvtepi16_epi8( _mm512_sllv_epi16(_mm512_cvtepu8_epi16(__ix), _mm512_cvtepu8_epi16(__iy)))); else if constexpr (sizeof __x <= 8 && __have_avx512bw_vl) return __intrin_bitcast<_V>( _mm_cvtepi16_epi8(_mm_sllv_epi16(_mm_cvtepu8_epi16(__ix), _mm_cvtepu8_epi16(__iy)))); else if constexpr (sizeof __ix == 16 && __have_avx512bw_vl) return __intrin_bitcast<_V>(_mm256_cvtepi16_epi8( _mm256_sllv_epi16(_mm256_cvtepu8_epi16(__ix), _mm256_cvtepu8_epi16(__iy)))); else if constexpr (sizeof __ix == 16 && __have_avx512bw) return __intrin_bitcast<_V>( __lo128(_mm512_cvtepi16_epi8(_mm512_sllv_epi16( _mm512_cvtepu8_epi16(_mm256_castsi128_si256(__ix)), _mm512_cvtepu8_epi16(_mm256_castsi128_si256(__iy)))))); else if constexpr (__have_sse4_1 && sizeof(__x) == 16) { auto __mask = __vector_bitcast<_Up>(__vector_bitcast(__y) << 5); auto __x4 = __vector_bitcast<_Up>(__vector_bitcast(__x) << 4); __x4 &= char(0xf0); __x = reinterpret_cast<_V>(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__x), __to_intrin(__x4))); __mask += __mask; auto __x2 = __vector_bitcast<_Up>(__vector_bitcast(__x) << 2); __x2 &= char(0xfc); __x = reinterpret_cast<_V>(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__x), __to_intrin(__x2))); __mask += __mask; auto __x1 = __x + __x; __x = reinterpret_cast<_V>(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__x), __to_intrin(__x1))); return __x & ((__y & char(0xf8)) == 0); // y > 7 nulls the result } else if constexpr (sizeof(__x) == 16) { auto __mask = __vector_bitcast<_UChar>(__vector_bitcast(__y) << 5); auto __x4 = __vector_bitcast<_Up>(__vector_bitcast(__x) << 4); __x4 &= char(0xf0); __x = __vector_bitcast<_SChar>(__mask) < 0 ? __x4 : __x; __mask += __mask; auto __x2 = __vector_bitcast<_Up>(__vector_bitcast(__x) << 2); __x2 &= char(0xfc); __x = __vector_bitcast<_SChar>(__mask) < 0 ? __x2 : __x; __mask += __mask; auto __x1 = __x + __x; __x = __vector_bitcast<_SChar>(__mask) < 0 ? __x1 : __x; return __x & ((__y & char(0xf8)) == 0); // y > 7 nulls the result } else return __x << __y; } else if constexpr (sizeof(_Up) == 2) { if constexpr (sizeof __ix == 64 && __have_avx512bw) return __vector_bitcast<_Up>(_mm512_sllv_epi16(__ix, __iy)); else if constexpr (sizeof __ix == 32 && __have_avx512bw_vl) return __vector_bitcast<_Up>(_mm256_sllv_epi16(__ix, __iy)); else if constexpr (sizeof __ix == 32 && __have_avx512bw) return __vector_bitcast<_Up>( __lo256(_mm512_sllv_epi16(_mm512_castsi256_si512(__ix), _mm512_castsi256_si512(__iy)))); else if constexpr (sizeof __ix == 32 && __have_avx2) { const auto __ux = __vector_bitcast(__x); const auto __uy = __vector_bitcast(__y); return __vector_bitcast<_Up>(_mm256_blend_epi16( __auto_bitcast(__ux << (__uy & 0x0000ffffu)), __auto_bitcast((__ux & 0xffff0000u) << (__uy >> 16)), 0xaa)); } else if constexpr (sizeof __ix == 16 && __have_avx512bw_vl) return __intrin_bitcast<_V>(_mm_sllv_epi16(__ix, __iy)); else if constexpr (sizeof __ix == 16 && __have_avx512bw) return __intrin_bitcast<_V>( __lo128(_mm512_sllv_epi16(_mm512_castsi128_si512(__ix), _mm512_castsi128_si512(__iy)))); else if constexpr (sizeof __ix == 16 && __have_avx2) { const auto __ux = __vector_bitcast(__ix); const auto __uy = __vector_bitcast(__iy); return __intrin_bitcast<_V>(_mm_blend_epi16( __auto_bitcast(__ux << (__uy & 0x0000ffffu)), __auto_bitcast((__ux & 0xffff0000u) << (__uy >> 16)), 0xaa)); } else if constexpr (sizeof __ix == 16) { using _Float4 = __vector_type_t; using _Int4 = __vector_type_t; using _UInt4 = __vector_type_t; const _UInt4 __yu = reinterpret_cast<_UInt4>(__to_intrin(__y + (0x3f8 >> 3))); return __x * __intrin_bitcast<_V>( __vector_convert<_Int4>(_SimdWrapper( reinterpret_cast<_Float4>(__yu << 23))) | (__vector_convert<_Int4>(_SimdWrapper( reinterpret_cast<_Float4>((__yu >> 16) << 23))) << 16)); } else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(_Up) == 4 && sizeof __ix == 16 && !__have_avx2) // latency is suboptimal, but throughput is at full speedup return __intrin_bitcast<_V>( __vector_bitcast(__ix) * __vector_convert<__vector_type16_t>( _SimdWrapper(__vector_bitcast( (__vector_bitcast(__y) << 23) + 0x3f80'0000)))); else if constexpr (sizeof(_Up) == 8 && sizeof __ix == 16 && !__have_avx2) { const auto __lo = _mm_sll_epi64(__ix, __iy); const auto __hi = _mm_sll_epi64(__ix, _mm_unpackhi_epi64(__iy, __iy)); if constexpr (__have_sse4_1) return __vector_bitcast<_Up>(_mm_blend_epi16(__lo, __hi, 0xf0)); else return __vector_bitcast<_Up>( _mm_move_sd(__vector_bitcast(__hi), __vector_bitcast(__lo))); } else return __x << __y; } #endif // _GLIBCXX_SIMD_NO_SHIFT_OPT // }}} // _S_bit_shift_right {{{ #ifndef _GLIBCXX_SIMD_NO_SHIFT_OPT template > constexpr inline _GLIBCXX_CONST static typename _TVT::type _S_bit_shift_right(_Tp __xx, int __y) { using _V = typename _TVT::type; using _Up = typename _TVT::value_type; _V __x = __xx; [[maybe_unused]] const auto __ix = __to_intrin(__x); if (__builtin_is_constant_evaluated()) return __x >> __y; else if (__builtin_constant_p(__y) && is_unsigned_v< _Up> && __y >= int(sizeof(_Up) * __CHAR_BIT__)) return _V(); else if constexpr (sizeof(_Up) == 1 && is_unsigned_v<_Up>) //{{{ return __intrin_bitcast<_V>(__vector_bitcast<_UShort>(__ix) >> __y) & _Up(0xff >> __y); //}}} else if constexpr (sizeof(_Up) == 1 && is_signed_v<_Up>) //{{{ return __intrin_bitcast<_V>( (__vector_bitcast<_UShort>(__vector_bitcast(__ix) >> (__y + 8)) << 8) | (__vector_bitcast<_UShort>( __vector_bitcast(__vector_bitcast<_UShort>(__ix) << 8) >> __y) >> 8)); //}}} // GCC optimizes sizeof == 2, 4, and unsigned 8 as expected else if constexpr (sizeof(_Up) == 8 && is_signed_v<_Up>) //{{{ { if (__y > 32) return (__intrin_bitcast<_V>(__vector_bitcast(__ix) >> 32) & _Up(0xffff'ffff'0000'0000ull)) | __vector_bitcast<_Up>( __vector_bitcast(__vector_bitcast<_ULLong>(__ix) >> 32) >> (__y - 32)); else return __intrin_bitcast<_V>(__vector_bitcast<_ULLong>(__ix) >> __y) | __vector_bitcast<_Up>( __vector_bitcast(__ix & -0x8000'0000'0000'0000ll) >> __y); } //}}} else return __x >> __y; } template > constexpr inline _GLIBCXX_CONST static typename _TVT::type _S_bit_shift_right(_Tp __xx, typename _TVT::type __y) { using _V = typename _TVT::type; using _Up = typename _TVT::value_type; _V __x = __xx; [[maybe_unused]] const auto __ix = __to_intrin(__x); [[maybe_unused]] const auto __iy = __to_intrin(__y); if (__builtin_is_constant_evaluated() || (__builtin_constant_p(__x) && __builtin_constant_p(__y))) return __x >> __y; else if constexpr (sizeof(_Up) == 1) //{{{ { if constexpr (sizeof(__x) <= 8 && __have_avx512bw_vl) return __intrin_bitcast<_V>(_mm_cvtepi16_epi8( is_signed_v<_Up> ? _mm_srav_epi16(_mm_cvtepi8_epi16(__ix), _mm_cvtepi8_epi16(__iy)) : _mm_srlv_epi16(_mm_cvtepu8_epi16(__ix), _mm_cvtepu8_epi16(__iy)))); if constexpr (sizeof(__x) == 16 && __have_avx512bw_vl) return __intrin_bitcast<_V>(_mm256_cvtepi16_epi8( is_signed_v<_Up> ? _mm256_srav_epi16(_mm256_cvtepi8_epi16(__ix), _mm256_cvtepi8_epi16(__iy)) : _mm256_srlv_epi16(_mm256_cvtepu8_epi16(__ix), _mm256_cvtepu8_epi16(__iy)))); else if constexpr (sizeof(__x) == 32 && __have_avx512bw) return __vector_bitcast<_Up>(_mm512_cvtepi16_epi8( is_signed_v<_Up> ? _mm512_srav_epi16(_mm512_cvtepi8_epi16(__ix), _mm512_cvtepi8_epi16(__iy)) : _mm512_srlv_epi16(_mm512_cvtepu8_epi16(__ix), _mm512_cvtepu8_epi16(__iy)))); else if constexpr (sizeof(__x) == 64 && is_signed_v<_Up>) return __vector_bitcast<_Up>(_mm512_mask_mov_epi8( _mm512_srav_epi16(__ix, _mm512_srli_epi16(__iy, 8)), 0x5555'5555'5555'5555ull, _mm512_srav_epi16( _mm512_slli_epi16(__ix, 8), _mm512_maskz_add_epi8(0x5555'5555'5555'5555ull, __iy, _mm512_set1_epi16(8))))); else if constexpr (sizeof(__x) == 64 && is_unsigned_v<_Up>) return __vector_bitcast<_Up>(_mm512_mask_mov_epi8( _mm512_srlv_epi16(__ix, _mm512_srli_epi16(__iy, 8)), 0x5555'5555'5555'5555ull, _mm512_srlv_epi16( _mm512_maskz_mov_epi8(0x5555'5555'5555'5555ull, __ix), _mm512_maskz_mov_epi8(0x5555'5555'5555'5555ull, __iy)))); /* This has better throughput but higher latency than the impl below else if constexpr (__have_avx2 && sizeof(__x) == 16 && is_unsigned_v<_Up>) { const auto __shorts = __to_intrin(_S_bit_shift_right( __vector_bitcast<_UShort>(_mm256_cvtepu8_epi16(__ix)), __vector_bitcast<_UShort>(_mm256_cvtepu8_epi16(__iy)))); return __vector_bitcast<_Up>( _mm_packus_epi16(__lo128(__shorts), __hi128(__shorts))); } */ else if constexpr (__have_avx2 && sizeof(__x) > 8) // the following uses vpsr[al]vd, which requires AVX2 if constexpr (is_signed_v<_Up>) { const auto r3 = __vector_bitcast<_UInt>( (__vector_bitcast(__x) >> (__vector_bitcast<_UInt>(__y) >> 24))) & 0xff000000u; const auto r2 = __vector_bitcast<_UInt>( ((__vector_bitcast(__x) << 8) >> ((__vector_bitcast<_UInt>(__y) << 8) >> 24))) & 0xff000000u; const auto r1 = __vector_bitcast<_UInt>( ((__vector_bitcast(__x) << 16) >> ((__vector_bitcast<_UInt>(__y) << 16) >> 24))) & 0xff000000u; const auto r0 = __vector_bitcast<_UInt>( (__vector_bitcast(__x) << 24) >> ((__vector_bitcast<_UInt>(__y) << 24) >> 24)); return __vector_bitcast<_Up>(r3 | (r2 >> 8) | (r1 >> 16) | (r0 >> 24)); } else { const auto r3 = (__vector_bitcast<_UInt>(__x) >> (__vector_bitcast<_UInt>(__y) >> 24)) & 0xff000000u; const auto r2 = ((__vector_bitcast<_UInt>(__x) << 8) >> ((__vector_bitcast<_UInt>(__y) << 8) >> 24)) & 0xff000000u; const auto r1 = ((__vector_bitcast<_UInt>(__x) << 16) >> ((__vector_bitcast<_UInt>(__y) << 16) >> 24)) & 0xff000000u; const auto r0 = (__vector_bitcast<_UInt>(__x) << 24) >> ((__vector_bitcast<_UInt>(__y) << 24) >> 24); return __vector_bitcast<_Up>(r3 | (r2 >> 8) | (r1 >> 16) | (r0 >> 24)); } else if constexpr (__have_sse4_1 && is_unsigned_v<_Up> && sizeof(__x) > 2) { auto __x128 = __vector_bitcast<_Up>(__ix); auto __mask = __vector_bitcast<_Up>(__vector_bitcast<_UShort>(__iy) << 5); auto __x4 = __vector_bitcast<_Up>( (__vector_bitcast<_UShort>(__x128) >> 4) & _UShort(0xff0f)); __x128 = __vector_bitcast<_Up>(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__x128), __to_intrin(__x4))); __mask += __mask; auto __x2 = __vector_bitcast<_Up>( (__vector_bitcast<_UShort>(__x128) >> 2) & _UShort(0xff3f)); __x128 = __vector_bitcast<_Up>(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__x128), __to_intrin(__x2))); __mask += __mask; auto __x1 = __vector_bitcast<_Up>( (__vector_bitcast<_UShort>(__x128) >> 1) & _UShort(0xff7f)); __x128 = __vector_bitcast<_Up>(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__x128), __to_intrin(__x1))); return __intrin_bitcast<_V>( __x128 & ((__vector_bitcast<_Up>(__iy) & char(0xf8)) == 0)); // y > 7 nulls the result } else if constexpr (__have_sse4_1 && is_signed_v<_Up> && sizeof(__x) > 2) { auto __mask = __vector_bitcast<_UChar>( __vector_bitcast<_UShort>(__iy) << 5); auto __maskl = [&]() _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __to_intrin(__vector_bitcast<_UShort>(__mask) << 8); }; auto __xh = __vector_bitcast(__ix); auto __xl = __vector_bitcast(__ix) << 8; auto __xh4 = __xh >> 4; auto __xl4 = __xl >> 4; __xh = __vector_bitcast(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__xh), __to_intrin(__xh4))); __xl = __vector_bitcast( _CommonImplX86::_S_blend_intrin(__maskl(), __to_intrin(__xl), __to_intrin(__xl4))); __mask += __mask; auto __xh2 = __xh >> 2; auto __xl2 = __xl >> 2; __xh = __vector_bitcast(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__xh), __to_intrin(__xh2))); __xl = __vector_bitcast( _CommonImplX86::_S_blend_intrin(__maskl(), __to_intrin(__xl), __to_intrin(__xl2))); __mask += __mask; auto __xh1 = __xh >> 1; auto __xl1 = __xl >> 1; __xh = __vector_bitcast(_CommonImplX86::_S_blend_intrin( __to_intrin(__mask), __to_intrin(__xh), __to_intrin(__xh1))); __xl = __vector_bitcast( _CommonImplX86::_S_blend_intrin(__maskl(), __to_intrin(__xl), __to_intrin(__xl1))); return __intrin_bitcast<_V>( (__vector_bitcast<_Up>((__xh & short(0xff00))) | __vector_bitcast<_Up>(__vector_bitcast<_UShort>(__xl) >> 8)) & ((__vector_bitcast<_Up>(__iy) & char(0xf8)) == 0)); // y > 7 nulls the result } else if constexpr (is_unsigned_v<_Up> && sizeof(__x) > 2) // SSE2 { auto __mask = __vector_bitcast<_Up>(__vector_bitcast<_UShort>(__y) << 5); auto __x4 = __vector_bitcast<_Up>( (__vector_bitcast<_UShort>(__x) >> 4) & _UShort(0xff0f)); __x = __mask > 0x7f ? __x4 : __x; __mask += __mask; auto __x2 = __vector_bitcast<_Up>( (__vector_bitcast<_UShort>(__x) >> 2) & _UShort(0xff3f)); __x = __mask > 0x7f ? __x2 : __x; __mask += __mask; auto __x1 = __vector_bitcast<_Up>( (__vector_bitcast<_UShort>(__x) >> 1) & _UShort(0xff7f)); __x = __mask > 0x7f ? __x1 : __x; return __x & ((__y & char(0xf8)) == 0); // y > 7 nulls the result } else if constexpr (sizeof(__x) > 2) // signed SSE2 { static_assert(is_signed_v<_Up>); auto __maskh = __vector_bitcast<_UShort>(__y) << 5; auto __maskl = __vector_bitcast<_UShort>(__y) << (5 + 8); auto __xh = __vector_bitcast(__x); auto __xl = __vector_bitcast(__x) << 8; auto __xh4 = __xh >> 4; auto __xl4 = __xl >> 4; __xh = __maskh > 0x7fff ? __xh4 : __xh; __xl = __maskl > 0x7fff ? __xl4 : __xl; __maskh += __maskh; __maskl += __maskl; auto __xh2 = __xh >> 2; auto __xl2 = __xl >> 2; __xh = __maskh > 0x7fff ? __xh2 : __xh; __xl = __maskl > 0x7fff ? __xl2 : __xl; __maskh += __maskh; __maskl += __maskl; auto __xh1 = __xh >> 1; auto __xl1 = __xl >> 1; __xh = __maskh > 0x7fff ? __xh1 : __xh; __xl = __maskl > 0x7fff ? __xl1 : __xl; __x = __vector_bitcast<_Up>((__xh & short(0xff00))) | __vector_bitcast<_Up>(__vector_bitcast<_UShort>(__xl) >> 8); return __x & ((__y & char(0xf8)) == 0); // y > 7 nulls the result } else return __x >> __y; } //}}} else if constexpr (sizeof(_Up) == 2 && sizeof(__x) >= 4) //{{{ { [[maybe_unused]] auto __blend_0xaa = [](auto __a, auto __b) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { if constexpr (sizeof(__a) == 16) return _mm_blend_epi16(__to_intrin(__a), __to_intrin(__b), 0xaa); else if constexpr (sizeof(__a) == 32) return _mm256_blend_epi16(__to_intrin(__a), __to_intrin(__b), 0xaa); else if constexpr (sizeof(__a) == 64) return _mm512_mask_blend_epi16(0xaaaa'aaaaU, __to_intrin(__a), __to_intrin(__b)); else __assert_unreachable(); }; if constexpr (__have_avx512bw_vl && sizeof(_Tp) <= 16) return __intrin_bitcast<_V>(is_signed_v<_Up> ? _mm_srav_epi16(__ix, __iy) : _mm_srlv_epi16(__ix, __iy)); else if constexpr (__have_avx512bw_vl && sizeof(_Tp) == 32) return __vector_bitcast<_Up>(is_signed_v<_Up> ? _mm256_srav_epi16(__ix, __iy) : _mm256_srlv_epi16(__ix, __iy)); else if constexpr (__have_avx512bw && sizeof(_Tp) == 64) return __vector_bitcast<_Up>(is_signed_v<_Up> ? _mm512_srav_epi16(__ix, __iy) : _mm512_srlv_epi16(__ix, __iy)); else if constexpr (__have_avx2 && is_signed_v<_Up>) return __intrin_bitcast<_V>( __blend_0xaa(((__vector_bitcast(__ix) << 16) >> (__vector_bitcast(__iy) & 0xffffu)) >> 16, __vector_bitcast(__ix) >> (__vector_bitcast(__iy) >> 16))); else if constexpr (__have_avx2 && is_unsigned_v<_Up>) return __intrin_bitcast<_V>( __blend_0xaa((__vector_bitcast<_UInt>(__ix) & 0xffffu) >> (__vector_bitcast<_UInt>(__iy) & 0xffffu), __vector_bitcast<_UInt>(__ix) >> (__vector_bitcast<_UInt>(__iy) >> 16))); else if constexpr (__have_sse4_1) { auto __mask = __vector_bitcast<_UShort>(__iy); auto __x128 = __vector_bitcast<_Up>(__ix); //__mask *= 0x0808; __mask = (__mask << 3) | (__mask << 11); // do __x128 = 0 where __y[4] is set __x128 = __vector_bitcast<_Up>( _mm_blendv_epi8(__to_intrin(__x128), __m128i(), __to_intrin(__mask))); // do __x128 =>> 8 where __y[3] is set __x128 = __vector_bitcast<_Up>( _mm_blendv_epi8(__to_intrin(__x128), __to_intrin(__x128 >> 8), __to_intrin(__mask += __mask))); // do __x128 =>> 4 where __y[2] is set __x128 = __vector_bitcast<_Up>( _mm_blendv_epi8(__to_intrin(__x128), __to_intrin(__x128 >> 4), __to_intrin(__mask += __mask))); // do __x128 =>> 2 where __y[1] is set __x128 = __vector_bitcast<_Up>( _mm_blendv_epi8(__to_intrin(__x128), __to_intrin(__x128 >> 2), __to_intrin(__mask += __mask))); // do __x128 =>> 1 where __y[0] is set return __intrin_bitcast<_V>( _mm_blendv_epi8(__to_intrin(__x128), __to_intrin(__x128 >> 1), __to_intrin(__mask + __mask))); } else { auto __k = __vector_bitcast<_UShort>(__iy) << 11; auto __x128 = __vector_bitcast<_Up>(__ix); auto __mask = [](__vector_type16_t<_UShort> __kk) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __vector_bitcast(__kk) < 0; }; // do __x128 = 0 where __y[4] is set __x128 = __mask(__k) ? decltype(__x128)() : __x128; // do __x128 =>> 8 where __y[3] is set __x128 = __mask(__k += __k) ? __x128 >> 8 : __x128; // do __x128 =>> 4 where __y[2] is set __x128 = __mask(__k += __k) ? __x128 >> 4 : __x128; // do __x128 =>> 2 where __y[1] is set __x128 = __mask(__k += __k) ? __x128 >> 2 : __x128; // do __x128 =>> 1 where __y[0] is set return __intrin_bitcast<_V>(__mask(__k + __k) ? __x128 >> 1 : __x128); } } //}}} else if constexpr (sizeof(_Up) == 4 && !__have_avx2) //{{{ { if constexpr (is_unsigned_v<_Up>) { // x >> y == x * 2^-y == (x * 2^(31-y)) >> 31 const __m128 __factor_f = reinterpret_cast<__m128>( 0x4f00'0000u - (__vector_bitcast(__y) << 23)); const __m128i __factor = __builtin_constant_p(__factor_f) ? __to_intrin( __make_vector(__factor_f[0], __factor_f[1], __factor_f[2], __factor_f[3])) : _mm_cvttps_epi32(__factor_f); const auto __r02 = _mm_srli_epi64(_mm_mul_epu32(__ix, __factor), 31); const auto __r13 = _mm_mul_epu32(_mm_srli_si128(__ix, 4), _mm_srli_si128(__factor, 4)); if constexpr (__have_sse4_1) return __intrin_bitcast<_V>( _mm_blend_epi16(_mm_slli_epi64(__r13, 1), __r02, 0x33)); else return __intrin_bitcast<_V>( __r02 | _mm_slli_si128(_mm_srli_epi64(__r13, 31), 4)); } else { auto __shift = [](auto __a, auto __b) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { if constexpr (is_signed_v<_Up>) return _mm_sra_epi32(__a, __b); else return _mm_srl_epi32(__a, __b); }; const auto __r0 = __shift(__ix, _mm_unpacklo_epi32(__iy, __m128i())); const auto __r1 = __shift(__ix, _mm_srli_epi64(__iy, 32)); const auto __r2 = __shift(__ix, _mm_unpackhi_epi32(__iy, __m128i())); const auto __r3 = __shift(__ix, _mm_srli_si128(__iy, 12)); if constexpr (__have_sse4_1) return __intrin_bitcast<_V>( _mm_blend_epi16(_mm_blend_epi16(__r1, __r0, 0x3), _mm_blend_epi16(__r3, __r2, 0x30), 0xf0)); else return __intrin_bitcast<_V>(_mm_unpacklo_epi64( _mm_unpacklo_epi32(__r0, _mm_srli_si128(__r1, 4)), _mm_unpackhi_epi32(__r2, _mm_srli_si128(__r3, 4)))); } } //}}} else return __x >> __y; } #endif // _GLIBCXX_SIMD_NO_SHIFT_OPT // }}} // compares {{{ // _S_equal_to {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr _MaskMember<_Tp> _S_equal_to(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { if constexpr (__is_avx512_abi<_Abi>()) // {{{ { if (__builtin_is_constant_evaluated() || (__x._M_is_constprop() && __y._M_is_constprop())) return _MaskImpl::_S_to_bits( __as_wrapper<_Np>(__x._M_data == __y._M_data)); constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); [[maybe_unused]] const auto __xi = __to_intrin(__x); [[maybe_unused]] const auto __yi = __to_intrin(__y); if constexpr (is_floating_point_v<_Tp>) { if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_EQ_OQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_EQ_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_EQ_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_EQ_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_EQ_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_EQ_OQ); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmpeq_epi64_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmpeq_epi32_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 2) return _mm512_mask_cmpeq_epi16_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 1) return _mm512_mask_cmpeq_epi8_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmpeq_epi64_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmpeq_epi32_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 2) return _mm256_mask_cmpeq_epi16_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 1) return _mm256_mask_cmpeq_epi8_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmpeq_epi64_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmpeq_epi32_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 2) return _mm_mask_cmpeq_epi16_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 1) return _mm_mask_cmpeq_epi8_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } // }}} else if (__builtin_is_constant_evaluated()) return _Base::_S_equal_to(__x, __y); else if constexpr (sizeof(__x) == 8) { const auto __r128 = __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__x) == __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__y); _MaskMember<_Tp> __r64{}; __builtin_memcpy(&__r64._M_data, &__r128, sizeof(__r64)); return __r64; } else return _Base::_S_equal_to(__x, __y); } // }}} // _S_not_equal_to {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr _MaskMember<_Tp> _S_not_equal_to(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { if constexpr (__is_avx512_abi<_Abi>()) // {{{ { if (__builtin_is_constant_evaluated() || (__x._M_is_constprop() && __y._M_is_constprop())) return _MaskImpl::_S_to_bits( __as_wrapper<_Np>(__x._M_data != __y._M_data)); constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); [[maybe_unused]] const auto __xi = __to_intrin(__x); [[maybe_unused]] const auto __yi = __to_intrin(__y); if constexpr (is_floating_point_v<_Tp>) { if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_NEQ_UQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_NEQ_UQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_NEQ_UQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_NEQ_UQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_NEQ_UQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_NEQ_UQ); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return ~_mm512_mask_cmpeq_epi64_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return ~_mm512_mask_cmpeq_epi32_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 2) return ~_mm512_mask_cmpeq_epi16_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 1) return ~_mm512_mask_cmpeq_epi8_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return ~_mm256_mask_cmpeq_epi64_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return ~_mm256_mask_cmpeq_epi32_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 2) return ~_mm256_mask_cmpeq_epi16_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 1) return ~_mm256_mask_cmpeq_epi8_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return ~_mm_mask_cmpeq_epi64_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return ~_mm_mask_cmpeq_epi32_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 2) return ~_mm_mask_cmpeq_epi16_mask(__k1, __xi, __yi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 1) return ~_mm_mask_cmpeq_epi8_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } // }}} else if (__builtin_is_constant_evaluated()) return _Base::_S_not_equal_to(__x, __y); else if constexpr (sizeof(__x) == 8) { const auto __r128 = __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__x) != __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__y); _MaskMember<_Tp> __r64{}; __builtin_memcpy(&__r64._M_data, &__r128, sizeof(__r64)); return __r64; } else return _Base::_S_not_equal_to(__x, __y); } // }}} // _S_less {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr _MaskMember<_Tp> _S_less(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { if constexpr (__is_avx512_abi<_Abi>()) // {{{ { if (__builtin_is_constant_evaluated() || (__x._M_is_constprop() && __y._M_is_constprop())) return _MaskImpl::_S_to_bits( __as_wrapper<_Np>(__x._M_data < __y._M_data)); constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); [[maybe_unused]] const auto __xi = __to_intrin(__x); [[maybe_unused]] const auto __yi = __to_intrin(__y); if constexpr (sizeof(__xi) == 64) { if constexpr (is_same_v<_Tp, float>) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LT_OS); else if constexpr (is_same_v<_Tp, double>) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LT_OS); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 1) return _mm512_mask_cmplt_epi8_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 2) return _mm512_mask_cmplt_epi16_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 4) return _mm512_mask_cmplt_epi32_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 8) return _mm512_mask_cmplt_epi64_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 1) return _mm512_mask_cmplt_epu8_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 2) return _mm512_mask_cmplt_epu16_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 4) return _mm512_mask_cmplt_epu32_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 8) return _mm512_mask_cmplt_epu64_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 32) { if constexpr (is_same_v<_Tp, float>) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LT_OS); else if constexpr (is_same_v<_Tp, double>) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LT_OS); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 1) return _mm256_mask_cmplt_epi8_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 2) return _mm256_mask_cmplt_epi16_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 4) return _mm256_mask_cmplt_epi32_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 8) return _mm256_mask_cmplt_epi64_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 1) return _mm256_mask_cmplt_epu8_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 2) return _mm256_mask_cmplt_epu16_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 4) return _mm256_mask_cmplt_epu32_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 8) return _mm256_mask_cmplt_epu64_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 16) { if constexpr (is_same_v<_Tp, float>) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LT_OS); else if constexpr (is_same_v<_Tp, double>) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LT_OS); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 1) return _mm_mask_cmplt_epi8_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 2) return _mm_mask_cmplt_epi16_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 4) return _mm_mask_cmplt_epi32_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 8) return _mm_mask_cmplt_epi64_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 1) return _mm_mask_cmplt_epu8_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 2) return _mm_mask_cmplt_epu16_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 4) return _mm_mask_cmplt_epu32_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 8) return _mm_mask_cmplt_epu64_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } else __assert_unreachable<_Tp>(); } // }}} else if (__builtin_is_constant_evaluated()) return _Base::_S_less(__x, __y); else if constexpr (sizeof(__x) == 8) { const auto __r128 = __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__x) < __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__y); _MaskMember<_Tp> __r64{}; __builtin_memcpy(&__r64._M_data, &__r128, sizeof(__r64)); return __r64; } else return _Base::_S_less(__x, __y); } // }}} // _S_less_equal {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr _MaskMember<_Tp> _S_less_equal(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { if constexpr (__is_avx512_abi<_Abi>()) // {{{ { if (__builtin_is_constant_evaluated() || (__x._M_is_constprop() && __y._M_is_constprop())) return _MaskImpl::_S_to_bits( __as_wrapper<_Np>(__x._M_data <= __y._M_data)); constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); [[maybe_unused]] const auto __xi = __to_intrin(__x); [[maybe_unused]] const auto __yi = __to_intrin(__y); if constexpr (sizeof(__xi) == 64) { if constexpr (is_same_v<_Tp, float>) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LE_OS); else if constexpr (is_same_v<_Tp, double>) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LE_OS); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 1) return _mm512_mask_cmple_epi8_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 2) return _mm512_mask_cmple_epi16_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 4) return _mm512_mask_cmple_epi32_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 8) return _mm512_mask_cmple_epi64_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 1) return _mm512_mask_cmple_epu8_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 2) return _mm512_mask_cmple_epu16_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 4) return _mm512_mask_cmple_epu32_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 8) return _mm512_mask_cmple_epu64_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 32) { if constexpr (is_same_v<_Tp, float>) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LE_OS); else if constexpr (is_same_v<_Tp, double>) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LE_OS); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 1) return _mm256_mask_cmple_epi8_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 2) return _mm256_mask_cmple_epi16_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 4) return _mm256_mask_cmple_epi32_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 8) return _mm256_mask_cmple_epi64_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 1) return _mm256_mask_cmple_epu8_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 2) return _mm256_mask_cmple_epu16_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 4) return _mm256_mask_cmple_epu32_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 8) return _mm256_mask_cmple_epu64_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 16) { if constexpr (is_same_v<_Tp, float>) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LE_OS); else if constexpr (is_same_v<_Tp, double>) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LE_OS); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 1) return _mm_mask_cmple_epi8_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 2) return _mm_mask_cmple_epi16_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 4) return _mm_mask_cmple_epi32_mask(__k1, __xi, __yi); else if constexpr (is_signed_v<_Tp> && sizeof(_Tp) == 8) return _mm_mask_cmple_epi64_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 1) return _mm_mask_cmple_epu8_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 2) return _mm_mask_cmple_epu16_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 4) return _mm_mask_cmple_epu32_mask(__k1, __xi, __yi); else if constexpr (is_unsigned_v<_Tp> && sizeof(_Tp) == 8) return _mm_mask_cmple_epu64_mask(__k1, __xi, __yi); else __assert_unreachable<_Tp>(); } else __assert_unreachable<_Tp>(); } // }}} else if (__builtin_is_constant_evaluated()) return _Base::_S_less_equal(__x, __y); else if constexpr (sizeof(__x) == 8) { const auto __r128 = __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__x) <= __vector_bitcast<_Tp, 16 / sizeof(_Tp)>(__y); _MaskMember<_Tp> __r64{}; __builtin_memcpy(&__r64._M_data, &__r128, sizeof(__r64)); return __r64; } else return _Base::_S_less_equal(__x, __y); } // }}} }}} // negation {{{ template _GLIBCXX_SIMD_INTRINSIC static constexpr _MaskMember<_Tp> _S_negate(_SimdWrapper<_Tp, _Np> __x) noexcept { if constexpr (__is_avx512_abi<_Abi>()) return _S_equal_to(__x, _SimdWrapper<_Tp, _Np>()); else return _Base::_S_negate(__x); } // }}} // math {{{ using _Base::_S_abs; // _S_sqrt {{{ template _GLIBCXX_SIMD_INTRINSIC static _SimdWrapper<_Tp, _Np> _S_sqrt(_SimdWrapper<_Tp, _Np> __x) { if constexpr (__is_sse_ps<_Tp, _Np>()) return __auto_bitcast(_mm_sqrt_ps(__to_intrin(__x))); else if constexpr (__is_sse_pd<_Tp, _Np>()) return _mm_sqrt_pd(__x); else if constexpr (__is_avx_ps<_Tp, _Np>()) return _mm256_sqrt_ps(__x); else if constexpr (__is_avx_pd<_Tp, _Np>()) return _mm256_sqrt_pd(__x); else if constexpr (__is_avx512_ps<_Tp, _Np>()) return _mm512_sqrt_ps(__x); else if constexpr (__is_avx512_pd<_Tp, _Np>()) return _mm512_sqrt_pd(__x); else __assert_unreachable<_Tp>(); } // }}} // _S_ldexp {{{ template _GLIBCXX_SIMD_INTRINSIC static _SimdWrapper<_Tp, _Np> _S_ldexp(_SimdWrapper<_Tp, _Np> __x, __fixed_size_storage_t __exp) { if constexpr (sizeof(__x) == 64 || __have_avx512vl) { const auto __xi = __to_intrin(__x); constexpr _SimdConverter, _Tp, _Abi> __cvt; const auto __expi = __to_intrin(__cvt(__exp)); using _Up = __bool_storage_member_type_t<_Np>; constexpr _Up __k1 = _Np < sizeof(_Up) * __CHAR_BIT__ ? _Up((1ULL << _Np) - 1) : ~_Up(); if constexpr (sizeof(__xi) == 16) { if constexpr (sizeof(_Tp) == 8) return _mm_maskz_scalef_pd(__k1, __xi, __expi); else return _mm_maskz_scalef_ps(__k1, __xi, __expi); } else if constexpr (sizeof(__xi) == 32) { if constexpr (sizeof(_Tp) == 8) return _mm256_maskz_scalef_pd(__k1, __xi, __expi); else return _mm256_maskz_scalef_ps(__k1, __xi, __expi); } else { static_assert(sizeof(__xi) == 64); if constexpr (sizeof(_Tp) == 8) return _mm512_maskz_scalef_pd(__k1, __xi, __expi); else return _mm512_maskz_scalef_ps(__k1, __xi, __expi); } } else return _Base::_S_ldexp(__x, __exp); } // }}} // _S_trunc {{{ template _GLIBCXX_SIMD_INTRINSIC static _SimdWrapper<_Tp, _Np> _S_trunc(_SimdWrapper<_Tp, _Np> __x) { if constexpr (__is_avx512_ps<_Tp, _Np>()) return _mm512_roundscale_ps(__x, 0x0b); else if constexpr (__is_avx512_pd<_Tp, _Np>()) return _mm512_roundscale_pd(__x, 0x0b); else if constexpr (__is_avx_ps<_Tp, _Np>()) return _mm256_round_ps(__x, 0xb); else if constexpr (__is_avx_pd<_Tp, _Np>()) return _mm256_round_pd(__x, 0xb); else if constexpr (__have_sse4_1 && __is_sse_ps<_Tp, _Np>()) return __auto_bitcast(_mm_round_ps(__to_intrin(__x), 0xb)); else if constexpr (__have_sse4_1 && __is_sse_pd<_Tp, _Np>()) return _mm_round_pd(__x, 0xb); else if constexpr (__is_sse_ps<_Tp, _Np>()) { auto __truncated = _mm_cvtepi32_ps(_mm_cvttps_epi32(__to_intrin(__x))); const auto __no_fractional_values = __vector_bitcast(__vector_bitcast<_UInt>(__to_intrin(__x)) & 0x7f800000u) < 0x4b000000; // the exponent is so large that no mantissa bits // signify fractional values (0x3f8 + 23*8 = // 0x4b0) return __no_fractional_values ? __truncated : __to_intrin(__x); } else return _Base::_S_trunc(__x); } // }}} // _S_round {{{ template _GLIBCXX_SIMD_INTRINSIC static _SimdWrapper<_Tp, _Np> _S_round(_SimdWrapper<_Tp, _Np> __x) { // Note that _MM_FROUND_TO_NEAREST_INT rounds ties to even, not away // from zero as required by std::round. Therefore this function is more // complicated. using _V = __vector_type_t<_Tp, _Np>; _V __truncated; if constexpr (__is_avx512_ps<_Tp, _Np>()) __truncated = _mm512_roundscale_ps(__x._M_data, 0x0b); else if constexpr (__is_avx512_pd<_Tp, _Np>()) __truncated = _mm512_roundscale_pd(__x._M_data, 0x0b); else if constexpr (__is_avx_ps<_Tp, _Np>()) __truncated = _mm256_round_ps(__x._M_data, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC); else if constexpr (__is_avx_pd<_Tp, _Np>()) __truncated = _mm256_round_pd(__x._M_data, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC); else if constexpr (__have_sse4_1 && __is_sse_ps<_Tp, _Np>()) __truncated = __auto_bitcast( _mm_round_ps(__to_intrin(__x), _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC)); else if constexpr (__have_sse4_1 && __is_sse_pd<_Tp, _Np>()) __truncated = _mm_round_pd(__x._M_data, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC); else if constexpr (__is_sse_ps<_Tp, _Np>()) __truncated = __auto_bitcast( _mm_cvtepi32_ps(_mm_cvttps_epi32(__to_intrin(__x)))); else return _Base::_S_round(__x); // x < 0 => truncated <= 0 && truncated >= x => x - truncated <= 0 // x > 0 => truncated >= 0 && truncated <= x => x - truncated >= 0 const _V __rounded = __truncated + (__and(_S_absmask<_V>, __x._M_data - __truncated) >= _Tp(.5) ? __or(__and(_S_signmask<_V>, __x._M_data), _V() + 1) : _V()); if constexpr (__have_sse4_1) return __rounded; else // adjust for missing range in cvttps_epi32 return __and(_S_absmask<_V>, __x._M_data) < 0x1p23f ? __rounded : __x._M_data; } // }}} // _S_nearbyint {{{ template > _GLIBCXX_SIMD_INTRINSIC static _Tp _S_nearbyint(_Tp __x) noexcept { if constexpr (_TVT::template _S_is) return _mm512_roundscale_ps(__x, 0x0c); else if constexpr (_TVT::template _S_is) return _mm512_roundscale_pd(__x, 0x0c); else if constexpr (_TVT::template _S_is) return _mm256_round_ps(__x, _MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC); else if constexpr (_TVT::template _S_is) return _mm256_round_pd(__x, _MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC); else if constexpr (__have_sse4_1 && _TVT::template _S_is) return _mm_round_ps(__x, _MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC); else if constexpr (__have_sse4_1 && _TVT::template _S_is) return _mm_round_pd(__x, _MM_FROUND_CUR_DIRECTION | _MM_FROUND_NO_EXC); else return _Base::_S_nearbyint(__x); } // }}} // _S_rint {{{ template > _GLIBCXX_SIMD_INTRINSIC static _Tp _S_rint(_Tp __x) noexcept { if constexpr (_TVT::template _S_is) return _mm512_roundscale_ps(__x, 0x04); else if constexpr (_TVT::template _S_is) return _mm512_roundscale_pd(__x, 0x04); else if constexpr (_TVT::template _S_is) return _mm256_round_ps(__x, _MM_FROUND_CUR_DIRECTION); else if constexpr (_TVT::template _S_is) return _mm256_round_pd(__x, _MM_FROUND_CUR_DIRECTION); else if constexpr (__have_sse4_1 && _TVT::template _S_is) return _mm_round_ps(__x, _MM_FROUND_CUR_DIRECTION); else if constexpr (__have_sse4_1 && _TVT::template _S_is) return _mm_round_pd(__x, _MM_FROUND_CUR_DIRECTION); else return _Base::_S_rint(__x); } // }}} // _S_floor {{{ template _GLIBCXX_SIMD_INTRINSIC static _SimdWrapper<_Tp, _Np> _S_floor(_SimdWrapper<_Tp, _Np> __x) { if constexpr (__is_avx512_ps<_Tp, _Np>()) return _mm512_roundscale_ps(__x, 0x09); else if constexpr (__is_avx512_pd<_Tp, _Np>()) return _mm512_roundscale_pd(__x, 0x09); else if constexpr (__is_avx_ps<_Tp, _Np>()) return _mm256_round_ps(__x, 0x9); else if constexpr (__is_avx_pd<_Tp, _Np>()) return _mm256_round_pd(__x, 0x9); else if constexpr (__have_sse4_1 && __is_sse_ps<_Tp, _Np>()) return __auto_bitcast(_mm_round_ps(__to_intrin(__x), 0x9)); else if constexpr (__have_sse4_1 && __is_sse_pd<_Tp, _Np>()) return _mm_round_pd(__x, 0x9); else return _Base::_S_floor(__x); } // }}} // _S_ceil {{{ template _GLIBCXX_SIMD_INTRINSIC static _SimdWrapper<_Tp, _Np> _S_ceil(_SimdWrapper<_Tp, _Np> __x) { if constexpr (__is_avx512_ps<_Tp, _Np>()) return _mm512_roundscale_ps(__x, 0x0a); else if constexpr (__is_avx512_pd<_Tp, _Np>()) return _mm512_roundscale_pd(__x, 0x0a); else if constexpr (__is_avx_ps<_Tp, _Np>()) return _mm256_round_ps(__x, 0xa); else if constexpr (__is_avx_pd<_Tp, _Np>()) return _mm256_round_pd(__x, 0xa); else if constexpr (__have_sse4_1 && __is_sse_ps<_Tp, _Np>()) return __auto_bitcast(_mm_round_ps(__to_intrin(__x), 0xa)); else if constexpr (__have_sse4_1 && __is_sse_pd<_Tp, _Np>()) return _mm_round_pd(__x, 0xa); else return _Base::_S_ceil(__x); } // }}} // _S_signbit {{{ template _GLIBCXX_SIMD_INTRINSIC static _MaskMember<_Tp> _S_signbit(_SimdWrapper<_Tp, _Np> __x) { if constexpr (__is_avx512_abi<_Abi>() && __have_avx512dq) { if constexpr (sizeof(__x) == 64 && sizeof(_Tp) == 4) return _mm512_movepi32_mask( __intrin_bitcast<__m512i>(__x._M_data)); else if constexpr (sizeof(__x) == 64 && sizeof(_Tp) == 8) return _mm512_movepi64_mask( __intrin_bitcast<__m512i>(__x._M_data)); else if constexpr (sizeof(__x) == 32 && sizeof(_Tp) == 4) return _mm256_movepi32_mask( __intrin_bitcast<__m256i>(__x._M_data)); else if constexpr (sizeof(__x) == 32 && sizeof(_Tp) == 8) return _mm256_movepi64_mask( __intrin_bitcast<__m256i>(__x._M_data)); else if constexpr (sizeof(__x) <= 16 && sizeof(_Tp) == 4) return _mm_movepi32_mask(__intrin_bitcast<__m128i>(__x._M_data)); else if constexpr (sizeof(__x) <= 16 && sizeof(_Tp) == 8) return _mm_movepi64_mask(__intrin_bitcast<__m128i>(__x._M_data)); } else if constexpr (__is_avx512_abi<_Abi>()) { const auto __xi = __to_intrin(__x); [[maybe_unused]] constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_movemask_ps(__xi); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_movemask_pd(__xi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_movemask_ps(__xi); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_movemask_pd(__xi); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmplt_epi32_mask( __k1, __intrin_bitcast<__m512i>(__xi), __m512i()); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmplt_epi64_mask( __k1, __intrin_bitcast<__m512i>(__xi), __m512i()); else __assert_unreachable<_Tp>(); } else return _Base::_S_signbit(__x); /*{ using _I = __int_for_sizeof_t<_Tp>; if constexpr (sizeof(__x) == 64) return _S_less(__vector_bitcast<_I>(__x), _I()); else { const auto __xx = __vector_bitcast<_I>(__x._M_data); [[maybe_unused]] constexpr _I __signmask = __finite_min_v<_I>; if constexpr ((sizeof(_Tp) == 4 && (__have_avx2 || sizeof(__x) == 16)) || __have_avx512vl) { return __vector_bitcast<_Tp>(__xx >> __digits_v<_I>); } else if constexpr ((__have_avx2 || (__have_ssse3 && sizeof(__x) == 16))) { return __vector_bitcast<_Tp>((__xx & __signmask) == __signmask); } else { // SSE2/3 or AVX (w/o AVX2) constexpr auto __one = __vector_broadcast<_Np, _Tp>(1); return __vector_bitcast<_Tp>( __vector_bitcast<_Tp>( (__xx & __signmask) | __vector_bitcast<_I>(__one)) // -1 or 1 != __one); } } }*/ } // }}} // _S_isnonzerovalue_mask {{{ // (isnormal | is subnormal == !isinf & !isnan & !is zero) template _GLIBCXX_SIMD_INTRINSIC static auto _S_isnonzerovalue_mask(_Tp __x) { using _Traits = _VectorTraits<_Tp>; if constexpr (__have_avx512dq_vl) { if constexpr (_Traits::template _S_is< float, 2> || _Traits::template _S_is) return _knot_mask8(_mm_fpclass_ps_mask(__to_intrin(__x), 0x9f)); else if constexpr (_Traits::template _S_is) return _knot_mask8(_mm256_fpclass_ps_mask(__x, 0x9f)); else if constexpr (_Traits::template _S_is) return _knot_mask16(_mm512_fpclass_ps_mask(__x, 0x9f)); else if constexpr (_Traits::template _S_is) return _knot_mask8(_mm_fpclass_pd_mask(__x, 0x9f)); else if constexpr (_Traits::template _S_is) return _knot_mask8(_mm256_fpclass_pd_mask(__x, 0x9f)); else if constexpr (_Traits::template _S_is) return _knot_mask8(_mm512_fpclass_pd_mask(__x, 0x9f)); else __assert_unreachable<_Tp>(); } else { using _Up = typename _Traits::value_type; constexpr size_t _Np = _Traits::_S_full_size; const auto __a = __x * __infinity_v<_Up>; // NaN if __x == 0 const auto __b = __x * _Up(); // NaN if __x == inf if constexpr (__have_avx512vl && __is_sse_ps<_Up, _Np>()) return _mm_cmp_ps_mask(__to_intrin(__a), __to_intrin(__b), _CMP_ORD_Q); else if constexpr (__have_avx512f && __is_sse_ps<_Up, _Np>()) return __mmask8(0xf & _mm512_cmp_ps_mask(__auto_bitcast(__a), __auto_bitcast(__b), _CMP_ORD_Q)); else if constexpr (__have_avx512vl && __is_sse_pd<_Up, _Np>()) return _mm_cmp_pd_mask(__a, __b, _CMP_ORD_Q); else if constexpr (__have_avx512f && __is_sse_pd<_Up, _Np>()) return __mmask8(0x3 & _mm512_cmp_pd_mask(__auto_bitcast(__a), __auto_bitcast(__b), _CMP_ORD_Q)); else if constexpr (__have_avx512vl && __is_avx_ps<_Up, _Np>()) return _mm256_cmp_ps_mask(__a, __b, _CMP_ORD_Q); else if constexpr (__have_avx512f && __is_avx_ps<_Up, _Np>()) return __mmask8(_mm512_cmp_ps_mask(__auto_bitcast(__a), __auto_bitcast(__b), _CMP_ORD_Q)); else if constexpr (__have_avx512vl && __is_avx_pd<_Up, _Np>()) return _mm256_cmp_pd_mask(__a, __b, _CMP_ORD_Q); else if constexpr (__have_avx512f && __is_avx_pd<_Up, _Np>()) return __mmask8(0xf & _mm512_cmp_pd_mask(__auto_bitcast(__a), __auto_bitcast(__b), _CMP_ORD_Q)); else if constexpr (__is_avx512_ps<_Up, _Np>()) return _mm512_cmp_ps_mask(__a, __b, _CMP_ORD_Q); else if constexpr (__is_avx512_pd<_Up, _Np>()) return _mm512_cmp_pd_mask(__a, __b, _CMP_ORD_Q); else __assert_unreachable<_Tp>(); } } // }}} // _S_isfinite {{{ template _GLIBCXX_SIMD_INTRINSIC static _MaskMember<_Tp> _S_isfinite(_SimdWrapper<_Tp, _Np> __x) { static_assert(is_floating_point_v<_Tp>); #if !__FINITE_MATH_ONLY__ if constexpr (__is_avx512_abi<_Abi>() && __have_avx512dq) { const auto __xi = __to_intrin(__x); constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return __k1 ^ _mm512_mask_fpclass_ps_mask(__k1, __xi, 0x99); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return __k1 ^ _mm512_mask_fpclass_pd_mask(__k1, __xi, 0x99); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __k1 ^ _mm256_mask_fpclass_ps_mask(__k1, __xi, 0x99); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __k1 ^ _mm256_mask_fpclass_pd_mask(__k1, __xi, 0x99); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __k1 ^ _mm_mask_fpclass_ps_mask(__k1, __xi, 0x99); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __k1 ^ _mm_mask_fpclass_pd_mask(__k1, __xi, 0x99); } else if constexpr (__is_avx512_abi<_Abi>()) { // if all exponent bits are set, __x is either inf or NaN using _I = __int_for_sizeof_t<_Tp>; const auto __inf = __vector_bitcast<_I>( __vector_broadcast<_Np>(__infinity_v<_Tp>)); return _S_less<_I, _Np>(__vector_bitcast<_I>(__x) & __inf, __inf); } else #endif return _Base::_S_isfinite(__x); } // }}} // _S_isinf {{{ template _GLIBCXX_SIMD_INTRINSIC static _MaskMember<_Tp> _S_isinf(_SimdWrapper<_Tp, _Np> __x) { #if !__FINITE_MATH_ONLY__ if constexpr (__is_avx512_abi<_Abi>() && __have_avx512dq) { const auto __xi = __to_intrin(__x); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_fpclass_ps_mask(__xi, 0x18); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_fpclass_pd_mask(__xi, 0x18); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_fpclass_ps_mask(__xi, 0x18); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_fpclass_pd_mask(__xi, 0x18); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_fpclass_ps_mask(__xi, 0x18); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_fpclass_pd_mask(__xi, 0x18); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx512dq_vl) { if constexpr (__is_sse_pd<_Tp, _Np>()) return _mm_movm_epi64(_mm_fpclass_pd_mask(__x, 0x18)); else if constexpr (__is_avx_pd<_Tp, _Np>()) return _mm256_movm_epi64(_mm256_fpclass_pd_mask(__x, 0x18)); else if constexpr (__is_sse_ps<_Tp, _Np>()) return _mm_movm_epi32( _mm_fpclass_ps_mask(__to_intrin(__x), 0x18)); else if constexpr (__is_avx_ps<_Tp, _Np>()) return _mm256_movm_epi32(_mm256_fpclass_ps_mask(__x, 0x18)); else __assert_unreachable<_Tp>(); } else #endif return _Base::_S_isinf(__x); } // }}} // _S_isnormal {{{ template _GLIBCXX_SIMD_INTRINSIC static _MaskMember<_Tp> _S_isnormal(_SimdWrapper<_Tp, _Np> __x) { #if __FINITE_MATH_ONLY__ [[maybe_unused]] constexpr int __mode = 0x26; #else [[maybe_unused]] constexpr int __mode = 0xbf; #endif if constexpr (__is_avx512_abi<_Abi>() && __have_avx512dq) { const auto __xi = __to_intrin(__x); const auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return __k1 ^ _mm512_mask_fpclass_ps_mask(__k1, __xi, __mode); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return __k1 ^ _mm512_mask_fpclass_pd_mask(__k1, __xi, __mode); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __k1 ^ _mm256_mask_fpclass_ps_mask(__k1, __xi, __mode); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __k1 ^ _mm256_mask_fpclass_pd_mask(__k1, __xi, __mode); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __k1 ^ _mm_mask_fpclass_ps_mask(__k1, __xi, __mode); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __k1 ^ _mm_mask_fpclass_pd_mask(__k1, __xi, __mode); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx512dq) { if constexpr (__have_avx512vl && __is_sse_ps<_Tp, _Np>()) return _mm_movm_epi32( _knot_mask8(_mm_fpclass_ps_mask(__to_intrin(__x), __mode))); else if constexpr (__have_avx512vl && __is_avx_ps<_Tp, _Np>()) return _mm256_movm_epi32( _knot_mask8(_mm256_fpclass_ps_mask(__x, __mode))); else if constexpr (__is_avx512_ps<_Tp, _Np>()) return _knot_mask16(_mm512_fpclass_ps_mask(__x, __mode)); else if constexpr (__have_avx512vl && __is_sse_pd<_Tp, _Np>()) return _mm_movm_epi64( _knot_mask8(_mm_fpclass_pd_mask(__x, __mode))); else if constexpr (__have_avx512vl && __is_avx_pd<_Tp, _Np>()) return _mm256_movm_epi64( _knot_mask8(_mm256_fpclass_pd_mask(__x, __mode))); else if constexpr (__is_avx512_pd<_Tp, _Np>()) return _knot_mask8(_mm512_fpclass_pd_mask(__x, __mode)); else __assert_unreachable<_Tp>(); } else if constexpr (__is_avx512_abi<_Abi>()) { using _I = __int_for_sizeof_t<_Tp>; const auto absn = __vector_bitcast<_I>(_S_abs(__x)); const auto minn = __vector_bitcast<_I>( __vector_broadcast<_Np>(__norm_min_v<_Tp>)); #if __FINITE_MATH_ONLY__ return _S_less_equal<_I, _Np>(minn, absn); #else const auto infn = __vector_bitcast<_I>(__vector_broadcast<_Np>(__infinity_v<_Tp>)); return __and(_S_less_equal<_I, _Np>(minn, absn), _S_less<_I, _Np>(absn, infn)); #endif } else return _Base::_S_isnormal(__x); } // }}} // _S_isnan {{{ template _GLIBCXX_SIMD_INTRINSIC static _MaskMember<_Tp> _S_isnan(_SimdWrapper<_Tp, _Np> __x) { return _S_isunordered(__x, __x); } // }}} // _S_isunordered {{{ template _GLIBCXX_SIMD_INTRINSIC static _MaskMember<_Tp> _S_isunordered([[maybe_unused]] _SimdWrapper<_Tp, _Np> __x, [[maybe_unused]] _SimdWrapper<_Tp, _Np> __y) { #if __FINITE_MATH_ONLY__ return {}; // false #else const auto __xi = __to_intrin(__x); const auto __yi = __to_intrin(__y); if constexpr (__is_avx512_abi<_Abi>()) { constexpr auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_UNORD_Q); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_UNORD_Q); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_UNORD_Q); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_UNORD_Q); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_UNORD_Q); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_UNORD_Q); } else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __to_masktype(_mm256_cmp_ps(__xi, __yi, _CMP_UNORD_Q)); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __to_masktype(_mm256_cmp_pd(__xi, __yi, _CMP_UNORD_Q)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast(_mm_cmpunord_ps(__xi, __yi)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype(_mm_cmpunord_pd(__xi, __yi)); else __assert_unreachable<_Tp>(); #endif } // }}} // _S_isgreater {{{ template static constexpr _MaskMember<_Tp> _S_isgreater(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { const auto __xi = __to_intrin(__x); const auto __yi = __to_intrin(__y); if constexpr (__is_avx512_abi<_Abi>()) { const auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_GT_OQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_GT_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_GT_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_GT_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_GT_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_GT_OQ); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx) { if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __to_masktype(_mm256_cmp_ps(__xi, __yi, _CMP_GT_OQ)); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __to_masktype(_mm256_cmp_pd(__xi, __yi, _CMP_GT_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast(_mm_cmp_ps(__xi, __yi, _CMP_GT_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype(_mm_cmp_pd(__xi, __yi, _CMP_GT_OQ)); else __assert_unreachable<_Tp>(); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 4) { const auto __xn = __vector_bitcast(__xi); const auto __yn = __vector_bitcast(__yi); const auto __xp = __xn < 0 ? -(__xn & 0x7fff'ffff) : __xn; const auto __yp = __yn < 0 ? -(__yn & 0x7fff'ffff) : __yn; return __auto_bitcast( __and(__to_masktype(_mm_cmpord_ps(__xi, __yi)), __xp > __yp)); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __vector_type_t<__int_with_sizeof_t<8>, 2>{ -_mm_ucomigt_sd(__xi, __yi), -_mm_ucomigt_sd(_mm_unpackhi_pd(__xi, __xi), _mm_unpackhi_pd(__yi, __yi))}; else return _Base::_S_isgreater(__x, __y); } // }}} // _S_isgreaterequal {{{ template static constexpr _MaskMember<_Tp> _S_isgreaterequal(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { const auto __xi = __to_intrin(__x); const auto __yi = __to_intrin(__y); if constexpr (__is_avx512_abi<_Abi>()) { const auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_GE_OQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_GE_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_GE_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_GE_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_GE_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_GE_OQ); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx) { if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __to_masktype(_mm256_cmp_ps(__xi, __yi, _CMP_GE_OQ)); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __to_masktype(_mm256_cmp_pd(__xi, __yi, _CMP_GE_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast(_mm_cmp_ps(__xi, __yi, _CMP_GE_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype(_mm_cmp_pd(__xi, __yi, _CMP_GE_OQ)); else __assert_unreachable<_Tp>(); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 4) { const auto __xn = __vector_bitcast(__xi); const auto __yn = __vector_bitcast(__yi); const auto __xp = __xn < 0 ? -(__xn & 0x7fff'ffff) : __xn; const auto __yp = __yn < 0 ? -(__yn & 0x7fff'ffff) : __yn; return __auto_bitcast( __and(__to_masktype(_mm_cmpord_ps(__xi, __yi)), __xp >= __yp)); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __vector_type_t<__int_with_sizeof_t<8>, 2>{ -_mm_ucomige_sd(__xi, __yi), -_mm_ucomige_sd(_mm_unpackhi_pd(__xi, __xi), _mm_unpackhi_pd(__yi, __yi))}; else return _Base::_S_isgreaterequal(__x, __y); } // }}} // _S_isless {{{ template static constexpr _MaskMember<_Tp> _S_isless(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { const auto __xi = __to_intrin(__x); const auto __yi = __to_intrin(__y); if constexpr (__is_avx512_abi<_Abi>()) { const auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LT_OQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LT_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LT_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LT_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LT_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LT_OQ); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx) { if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __to_masktype(_mm256_cmp_ps(__xi, __yi, _CMP_LT_OQ)); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __to_masktype(_mm256_cmp_pd(__xi, __yi, _CMP_LT_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast(_mm_cmp_ps(__xi, __yi, _CMP_LT_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype(_mm_cmp_pd(__xi, __yi, _CMP_LT_OQ)); else __assert_unreachable<_Tp>(); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 4) { const auto __xn = __vector_bitcast(__xi); const auto __yn = __vector_bitcast(__yi); const auto __xp = __xn < 0 ? -(__xn & 0x7fff'ffff) : __xn; const auto __yp = __yn < 0 ? -(__yn & 0x7fff'ffff) : __yn; return __auto_bitcast( __and(__to_masktype(_mm_cmpord_ps(__xi, __yi)), __xp < __yp)); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __vector_type_t<__int_with_sizeof_t<8>, 2>{ -_mm_ucomigt_sd(__yi, __xi), -_mm_ucomigt_sd(_mm_unpackhi_pd(__yi, __yi), _mm_unpackhi_pd(__xi, __xi))}; else return _Base::_S_isless(__x, __y); } // }}} // _S_islessequal {{{ template static constexpr _MaskMember<_Tp> _S_islessequal(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { const auto __xi = __to_intrin(__x); const auto __yi = __to_intrin(__y); if constexpr (__is_avx512_abi<_Abi>()) { const auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LE_OQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LE_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LE_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LE_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_LE_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_LE_OQ); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx) { if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __to_masktype(_mm256_cmp_ps(__xi, __yi, _CMP_LE_OQ)); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __to_masktype(_mm256_cmp_pd(__xi, __yi, _CMP_LE_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast(_mm_cmp_ps(__xi, __yi, _CMP_LE_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype(_mm_cmp_pd(__xi, __yi, _CMP_LE_OQ)); else __assert_unreachable<_Tp>(); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 4) { const auto __xn = __vector_bitcast(__xi); const auto __yn = __vector_bitcast(__yi); const auto __xp = __xn < 0 ? -(__xn & 0x7fff'ffff) : __xn; const auto __yp = __yn < 0 ? -(__yn & 0x7fff'ffff) : __yn; return __auto_bitcast( __and(__to_masktype(_mm_cmpord_ps(__xi, __yi)), __xp <= __yp)); } else if constexpr (__have_sse2 && sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __vector_type_t<__int_with_sizeof_t<8>, 2>{ -_mm_ucomige_sd(__yi, __xi), -_mm_ucomige_sd(_mm_unpackhi_pd(__yi, __yi), _mm_unpackhi_pd(__xi, __xi))}; else return _Base::_S_islessequal(__x, __y); } // }}} // _S_islessgreater {{{ template static constexpr _MaskMember<_Tp> _S_islessgreater(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y) { const auto __xi = __to_intrin(__x); const auto __yi = __to_intrin(__y); if constexpr (__is_avx512_abi<_Abi>()) { const auto __k1 = _Abi::template _S_implicit_mask_intrin<_Tp>(); if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4) return _mm512_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_NEQ_OQ); else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8) return _mm512_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_NEQ_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return _mm256_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_NEQ_OQ); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return _mm256_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_NEQ_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return _mm_mask_cmp_ps_mask(__k1, __xi, __yi, _CMP_NEQ_OQ); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return _mm_mask_cmp_pd_mask(__k1, __xi, __yi, _CMP_NEQ_OQ); else __assert_unreachable<_Tp>(); } else if constexpr (__have_avx) { if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4) return __to_masktype(_mm256_cmp_ps(__xi, __yi, _CMP_NEQ_OQ)); else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8) return __to_masktype(_mm256_cmp_pd(__xi, __yi, _CMP_NEQ_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast(_mm_cmp_ps(__xi, __yi, _CMP_NEQ_OQ)); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype(_mm_cmp_pd(__xi, __yi, _CMP_NEQ_OQ)); else __assert_unreachable<_Tp>(); } else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4) return __auto_bitcast( __and(_mm_cmpord_ps(__xi, __yi), _mm_cmpneq_ps(__xi, __yi))); else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8) return __to_masktype( __and(_mm_cmpord_pd(__xi, __yi), _mm_cmpneq_pd(__xi, __yi))); else __assert_unreachable<_Tp>(); } //}}} }}} template