| // Implementation of <simd> -*- C++ -*- |
| |
| // Copyright The GNU Toolchain Authors. |
| // |
| // 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 |
| // <http://www.gnu.org/licenses/>. |
| |
| #ifndef _GLIBCXX_SIMD_COMPLEX_H |
| #define _GLIBCXX_SIMD_COMPLEX_H 1 |
| |
| #ifdef _GLIBCXX_SYSHDR |
| #pragma GCC system_header |
| #endif |
| |
| #if __cplusplus >= 202400L |
| |
| #include "simd_vec.h" |
| #include <complex> |
| |
| // psabi warnings are bogus because the ABI of the internal types never leaks into user code |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wpsabi" |
| |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| namespace simd |
| { |
| /** @internal |
| * @brief Return a _CxIleav mask that holds @p __k as its data member. |
| * |
| * @note If the resulting mask type has size 1, then it will actually store a single bool, rather |
| * than the given mask object. |
| */ |
| template <size_t _Bytes, typename _Ap, __abi_tag _Aret |
| = decltype(__abi_rebind<complex<__float_from<_Bytes>>, _Ap::_S_size / 2, _Ap>())> |
| [[__gnu__::__always_inline__]] |
| constexpr basic_mask<_Bytes * 2, _Aret> |
| __to_cx_ileav(const basic_mask<_Bytes, _Ap>& __k) |
| { |
| static_assert(_Ap::_S_size % 2 == 0 |
| && (__filter_abi_variant(_Ap::_S_variant, _AbiVariant::_CxVariants) |
| == _AbiVariant())); |
| if constexpr (_Aret::_S_size == 1) |
| return basic_mask<_Bytes * 2, _Aret>(__k[0]); |
| else |
| return basic_mask<_Bytes * 2, _Aret>::_S_init(__k); |
| } |
| |
| constexpr void |
| __check_hi_bits_for_zero(unsigned_integral auto __x) |
| { |
| __glibcxx_simd_precondition(__x == 0, |
| "to_ullong called on mask with 'true' elements at indices" |
| "higher than 64"); |
| } |
| |
| template <typename _T0, typename _T1> |
| constexpr void |
| __check_hi_bits_for_zero(const __trivial_pair<_T0, _T1>& __p) |
| { |
| std::simd::__check_hi_bits_for_zero(__p._M_first); |
| std::simd::__check_hi_bits_for_zero(__p._M_second); |
| } |
| |
| constexpr unsigned long long |
| __unwrap_pairs_to_ullong(unsigned_integral auto __x) |
| { return __x; } |
| |
| template <typename _T0, typename _T1> |
| constexpr unsigned long long |
| __unwrap_pairs_to_ullong(const __trivial_pair<_T0, _T1>& __p) |
| { |
| std::simd::__check_hi_bits_for_zero(__p._M_second); |
| return std::simd::__unwrap_pairs_to_ullong(__p._M_first); |
| } |
| |
| template <int _Np> |
| constexpr bitset<_Np> |
| __unwrap_pairs_to_bitset(unsigned_integral auto __x) |
| { |
| static_assert(_Np <= 64); |
| return __x; |
| } |
| |
| template <size_t _Np, typename _T0, typename _T1> |
| constexpr bitset<_Np> |
| __unwrap_pairs_to_bitset(const __trivial_pair<_T0, _T1>& __p) |
| { |
| constexpr size_t _N0 = __bit_floor(_Np); |
| constexpr size_t _N1 = _Np - _N0; |
| static_assert(_N0 % 64 == 0); |
| struct _Tmp |
| { |
| bitset<__bit_floor(_Np)> _M_lo; |
| bitset<_Np - __bit_floor(_Np)> _M_hi; |
| }; |
| _Tmp __tmp = {std::simd::__unwrap_pairs_to_bitset<_N0>(__p._M_first), |
| std::simd::__unwrap_pairs_to_bitset<_N1>(__p._M_second)}; |
| return __builtin_bit_cast(bitset<_Np>, __tmp); |
| } |
| |
| template <size_t _Bytes> |
| consteval auto |
| __tree_of_ulong() |
| { |
| static constexpr size_t _N0 = __bit_floor(_Bytes - 1); |
| static constexpr size_t _N1 = _Bytes - _N0; |
| if constexpr (_Bytes <= sizeof(unsigned long)) |
| return 0ul; |
| else |
| return __trivial_pair {__tree_of_ulong<_N0>(), __tree_of_ulong<_N1>()}; |
| } |
| |
| template <size_t _Bytes> |
| using __tree_of_ulong_t = decltype(__tree_of_ulong<_Bytes>()); |
| |
| template <size_t _Np> |
| constexpr auto |
| __bitset_to_pairs(const bitset<_Np>& __b) noexcept |
| { |
| if constexpr (_Np <= 64) |
| return __b.to_ullong(); |
| else |
| return __builtin_bit_cast(__tree_of_ulong_t<__div_ceil(_Np, size_t(__CHAR_BIT__))>, __b); |
| } |
| |
| // complex interleaved (_CxIleav) ------------------------------------------- |
| |
| /** @internal |
| * @brief Functions acting on / recursing into the non-complex fp vec objects, interpreting even |
| * elements as real and odd elements as imaginary. |
| */ |
| namespace __cxileav |
| { |
| /** @internal |
| * @brief Set even (real) elements in @p __x to the values in @p __re. |
| */ |
| template <typename _Tp, typename _Ap> |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| __set_real(basic_vec<_Tp, _Ap>& __x, |
| const __similar_vec<_Tp, _Ap::_S_size / 2, _Ap>& __re) noexcept |
| { |
| if constexpr (__scalar_abi_tag<_Ap> && _Ap::_S_size == 2) |
| __x._M_get_low() = __re; |
| else if constexpr (_Ap::_S_nreg >= 2) |
| { // recurse |
| constexpr int __n0 = __x._M_get_low().size(); |
| const auto& [__lo, __hi] = __re.template _M_chunk< |
| __similar_vec<_Tp, __n0 / 2, _Ap>>(); |
| __set_real(__x._M_get_low(), __lo); |
| __set_real(__x._M_get_high(), __hi); |
| } |
| else |
| { |
| using _DataType = typename _Ap::template _DataType<_Tp>; |
| _DataType& __xv = __x._M_get(); |
| const auto __rv = __re._M_get(); |
| if constexpr (_Ap::_S_size == 2) |
| __vec_set(__xv, 0, __rv); |
| else if (__is_const_known(__x, __re)) |
| { |
| constexpr auto [...__is] = _IotaArray<_Ap::_S_size>; |
| __xv = _DataType {((__is & 1) == 0 ? __rv[__is / 2] : __xv[__is])...}; |
| } |
| else |
| _VecOps<_DataType>::_S_overwrite_even_elements(__xv, __rv); |
| } |
| } |
| |
| /** @internal |
| * @brief Set odd (imaginary) elements in @p __x to the values in @p __im. |
| */ |
| template <typename _Tp, typename _Ap> |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| __set_imag(basic_vec<_Tp, _Ap>& __x, |
| const __similar_vec<_Tp, _Ap::_S_size / 2, _Ap>& __im) noexcept |
| { |
| if constexpr (__scalar_abi_tag<_Ap> && _Ap::_S_size == 2) |
| __x._M_get_high() = __im; |
| else if constexpr (_Ap::_S_nreg >= 2) |
| { // recurse |
| constexpr int __n0 = __x._M_get_low().size(); |
| const auto& [__lo, __hi] = __im.template _M_chunk< |
| __similar_vec<_Tp, __n0 / 2, _Ap>>(); |
| __set_imag(__x._M_get_low(), __lo); |
| __set_imag(__x._M_get_high(), __hi); |
| } |
| else |
| { |
| using _DataType = typename _Ap::template _DataType<_Tp>; |
| _DataType& __xv = __x._M_get(); |
| const auto __iv = __im._M_get(); |
| if constexpr (_Ap::_S_size == 2) |
| __vec_set(__xv, 1, __iv); |
| else if (__is_const_known(__x, __im)) |
| { |
| constexpr auto [...__is] = _IotaArray<_Ap::_S_size>; |
| __xv = _DataType {((__is & 1) == 1 ? __iv[__is / 2] : __xv[__is])...}; |
| } |
| else |
| _VecOps<_DataType>::_S_overwrite_odd_elements(__xv, __iv); |
| } |
| } |
| |
| /** @internal |
| * @brief Return @p __x after flipping the sign of odd (imaginary) elements. |
| */ |
| template <typename _Tp, typename _Ap> |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec<_Tp, _Ap> |
| __negate_imag(const basic_vec<_Tp, _Ap>& __x) |
| { |
| if constexpr (__scalar_abi_tag<_Ap> && _Ap::_S_size == 2) |
| return basic_vec<_Tp, _Ap>::_S_init(__x._M_get_low(), -__x._M_get_high()); |
| else if constexpr (_Ap::_S_nreg >= 2) // recurse |
| return basic_vec<_Tp, _Ap>::_S_init(__negate_imag(__x._M_get_low()), |
| __negate_imag(__x._M_get_high())); |
| else |
| return _VecOps<typename _Ap::template _DataType<_Tp>> |
| ::_S_complex_negate_imag(__x._M_get()); |
| } |
| |
| /** @internal |
| * @brief Recompute all complex multiplications where @p __nan is true using @p _Cx's |
| * multiplication operator. |
| * |
| * @todo use coarser _TargetTraits and move into .so |
| */ |
| template <typename _Cx, _TargetTraits, __vec_builtin _TV> |
| [[__gnu__::__cold__]] |
| constexpr _TV |
| __redo_mul(_TV __r, const _TV __x, const _TV __y, const auto __nan, const int __n) |
| { |
| // redo multiplication using scalar complex-mul on (NaN, NaN) results |
| for (int __i = 0; __i < __n; __i += 2) |
| { |
| if (__nan[__i] && __nan[__i + 1]) |
| { |
| using _Tc = typename _Cx::value_type; |
| const _Cx __cx(_Tc(__x[__i]), _Tc(__x[__i + 1])); |
| const _Cx __cy(_Tc(__y[__i]), _Tc(__y[__i + 1])); |
| const _Cx __cr = __cx * __cy; |
| __vec_set(__r, __i, __cr.real()); |
| __vec_set(__r, __i + 1, __cr.imag()); |
| } |
| } |
| return __r; |
| } |
| |
| /** @internal |
| * @brief Complex multiplication of @p __x and @p __y, returning the result in @p __x. |
| */ |
| template <typename _Cx, _TargetTraits _Traits, typename _Tp, typename _Ap> |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| __mul(basic_vec<_Tp, _Ap>& __x, const basic_vec<_Tp, _Ap>& __y) |
| { |
| static_assert(__complex_like<_Cx>); |
| if constexpr (__scalar_abi_tag<_Ap> && _Ap::_S_size == 2) |
| { |
| const _Cx __c = _Cx(__x[0], __x[1]) * _Cx(__y[0], __y[1]); |
| __x._M_get_low() = __c.real(); |
| __x._M_get_high() = __c.imag(); |
| } |
| else if constexpr (_Ap::_S_nreg >= 2) |
| { // recurse |
| __mul<_Cx, _Traits>(__x._M_get_low(), __y._M_get_low()); |
| __mul<_Cx, _Traits>(__x._M_get_high(), __y._M_get_high()); |
| } |
| else if constexpr (_Traits.template _M_eval_as_f32<_Tp>()) |
| { // eval float16_t as float |
| using _Vf32 = rebind_t<float, basic_vec<_Tp, _Ap>>; |
| _Vf32 __xf32(__x); |
| __mul<_Cx, _Traits>(__xf32, _Vf32(__y)); |
| __x = static_cast<basic_vec<_Tp, _Ap>>(__xf32); |
| } |
| else |
| { |
| using _DataType = typename _Ap::template _DataType<_Tp>; |
| const _DataType __xv = __x._M_get(); |
| const _DataType __yv = __y._M_get(); |
| using _VO = _VecOps<_DataType>; // don't care for actual numer of elements |
| using _VOS = _VecOps<_DataType, _Ap::_S_size>; // to check for const-prop values |
| if (_VOS::_S_complex_imag_is_const_known_zero(__xv)) |
| { |
| if (_VOS::_S_complex_imag_is_const_known_zero(__yv)) |
| __x = __xv * __yv; |
| else |
| { |
| if (_Traits._M_conforming_to_STDC_annex_G()) |
| { // handle negative zero (0 * y can be -0) |
| auto __a = _VO::_S_dup_even(__xv) * __yv; |
| auto __b = _DataType() * _VO::_S_swap_neighbors(__yv); |
| __x = _VO::_S_addsub(__a, __b); |
| } |
| else |
| __x = _VO::_S_dup_even(__xv) * __yv; |
| } |
| } |
| else if (_VOS::_S_complex_imag_is_const_known_zero(__yv)) |
| { |
| if (_Traits._M_conforming_to_STDC_annex_G()) |
| __x = _VO::_S_addsub(_VO::_S_dup_even(__yv) * __xv, |
| _DataType() * _VO::_S_swap_neighbors(__xv)); |
| else |
| __x = _VO::_S_dup_even(__yv) * __xv; |
| } |
| else if (_VOS::_S_complex_real_is_const_known_zero(__yv)) |
| { |
| if (_Traits._M_conforming_to_STDC_annex_G()) |
| __x = _VO::_S_addsub(_DataType(), _VO::_S_dup_odd(__yv) |
| * _VO::_S_swap_neighbors(__xv)); |
| else |
| __x = _VO::_S_dup_odd(__yv) |
| * _VO::_S_complex_negate_real(_VO::_S_swap_neighbors(__xv)); |
| } |
| else if (_VOS::_S_complex_real_is_const_known_zero(__xv)) |
| { |
| if (_Traits._M_conforming_to_STDC_annex_G()) |
| __x = _VO::_S_addsub(_DataType(), _VO::_S_dup_odd(__xv) |
| * _VO::_S_swap_neighbors(__yv)); |
| else |
| __x = _VO::_S_dup_odd(__xv) |
| * _VO::_S_complex_negate_real(_VO::_S_swap_neighbors(__yv)); |
| } |
| else |
| { |
| #if _GLIBCXX_X86 |
| if (_Traits._M_have_fma() && !__is_const_known(__xv, __yv)) |
| { |
| if constexpr (_Traits._M_have_fma()) |
| __x = __x86_complex_multiplies(__xv, __yv); |
| } |
| else |
| #endif |
| __x = _VO::_S_addsub(_VO::_S_dup_even(__xv) * __yv, |
| _VO::_S_dup_odd(__xv) * _VO::_S_swap_neighbors(__yv)); |
| const auto __nan = __x._M_isnan(); |
| if (_Traits._M_conforming_to_STDC_annex_G() && __nan._M_any_of()) |
| __x = __redo_mul<_Cx, _Traits>(__x._M_get(), __xv, __yv, __nan, _Ap::_S_size); |
| } |
| } |
| } |
| } |
| |
| template <size_t _Bytes, __abi_tag _Ap> |
| requires _Ap::_S_is_cx_ileav && (_Ap::_S_size >= 2) // size 1 is in simd_mask.h |
| class basic_mask<_Bytes, _Ap> |
| : public _MaskBase<_Bytes, _Ap> |
| { |
| using _Base = _MaskBase<_Bytes, _Ap>; |
| |
| using _VecType = _Base::_VecType; |
| |
| template <size_t, typename> |
| friend class basic_mask; |
| |
| template <typename, typename> |
| friend class basic_vec; |
| |
| static constexpr int _S_size = _Ap::_S_size; |
| |
| using _DataType = __component_mask_for_ileav<_Bytes, _Ap>; |
| |
| static constexpr bool _S_is_scalar = _DataType::_S_is_scalar; |
| |
| static constexpr bool _S_use_bitmask = _DataType::_S_use_bitmask; |
| |
| static constexpr int _S_full_size = _DataType::_S_full_size / 2; |
| |
| static constexpr bool _S_is_partial = _DataType::_S_is_partial; |
| |
| static constexpr bool _S_has_bool_member = _DataType::_S_has_bool_member; |
| |
| static constexpr size_t _S_padding_bytes = _DataType::_S_padding_bytes; |
| |
| _DataType _M_data; |
| |
| public: |
| using value_type = bool; |
| |
| using abi_type = _Ap; |
| |
| using iterator = _Base::iterator; |
| |
| using const_iterator = _Base::const_iterator; |
| |
| // internal but public API ---------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_init(const _DataType& __x) |
| { |
| basic_mask __r; |
| __r._M_data = __x; |
| return __r; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_concat_data() const |
| { return _M_data._M_concat_data(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr const _DataType& |
| _M_get_ileav_data() const |
| { return _M_data; } |
| |
| template <_ArchTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_partial_mask_of_n(int __n) |
| { return _S_init(_DataType::_S_partial_mask_of_n(__n * 2)); } |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_and_neighbors(_DataType __k) |
| { return _S_init(__k._M_and_neighbors()); } |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_or_neighbors(_DataType __k) |
| { return _S_init(__k._M_or_neighbors()); } |
| |
| template <typename _Mp> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_chunk() const noexcept |
| { |
| if constexpr (_Mp::abi_type::_S_variant != _Ap::_S_variant) |
| { |
| using _M2 = resize_t<_S_size, _Mp>; |
| static_assert(!is_same_v<_M2, basic_mask>); |
| return static_cast<_M2>(*this).template _M_chunk<_Mp>(); |
| } |
| else if constexpr (_Mp::_S_size == 1) |
| { |
| constexpr auto [...__is] = _IotaArray<_S_size>; |
| return array{_Mp(_M_data[__is])...}; |
| } |
| else // _Mp is the same partial specialization |
| { |
| constexpr int __rem = _S_size % _Mp::_S_size; |
| const auto [...__xs] = _M_data.template _M_chunk<typename _Mp::_DataType>(); |
| static_assert(is_same_v<decltype(__to_cx_ileav(__xs...[0])), _Mp>); |
| if constexpr (__rem == 0) |
| return array{__to_cx_ileav(__xs)...}; |
| else |
| return tuple(__to_cx_ileav(__xs)...); |
| } |
| } |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr const basic_mask& |
| _S_concat(const basic_mask& __x0) noexcept |
| { return __x0; } |
| |
| template <typename... _As> |
| requires (sizeof...(_As) > 1) |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_concat(const basic_mask<_Bytes, _As>&... __xs) noexcept |
| { return basic_mask::_S_init(_DataType::_S_concat(__xs._M_get_ileav_data()...)); } |
| |
| // [simd.mask.overview] default constructor ----------------------------- |
| basic_mask() = default; |
| |
| // [simd.mask.overview] conversion extensions --------------------------- |
| template <__vec_builtin _TV> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_mask(const _TV& __x) requires convertible_to<_TV, _DataType> |
| : _M_data(__x) |
| {} |
| |
| template <__vec_builtin _TV> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| operator _TV() requires convertible_to<_DataType, _TV> |
| { return _M_data; } |
| |
| // [simd.mask.ctor] broadcast constructor ------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_mask(same_as<bool> auto __x) noexcept // LWG 4382. |
| : _M_data(__x) |
| {} |
| |
| // [simd.mask.ctor] conversion constructor ------------------------------ |
| template <size_t _UBytes, typename _UAbi> |
| requires (_S_size == _UAbi::_S_size) |
| [[__gnu__::__always_inline__]] |
| constexpr explicit(__is_mask_conversion_explicit<_Ap, _UAbi>(_Bytes, _UBytes)) |
| basic_mask(const basic_mask<_UBytes, _UAbi>& __x) noexcept |
| : _M_data([&] { |
| using _UV = basic_mask<_UBytes, _UAbi>; |
| if constexpr (_UAbi::_S_is_cx_ileav) |
| // _CxIleav -> _CxIleav => we can simply convert the contained mask |
| return __x._M_data; // calls conversion ctor on _DataType |
| |
| // __x is not _CxIleav from here on |
| else if constexpr (_S_use_bitmask || _UV::_S_use_bitmask) |
| return _DataType::_S_init(__duplicate_each_bit<_S_size>(__x._M_to_uint())); |
| |
| // vec-mask to vec-mask from here on |
| else if constexpr (_UAbi::_S_is_cx_ctgus) |
| // unwrap _CxCtgus mask and recurse |
| return basic_mask(__x._M_data)._M_data; |
| |
| else if constexpr (_UV::_S_is_scalar || _S_is_scalar) |
| // need to duplicate & convert one vector element into two bools |
| return _DataType([&](int __i) { return __x[__i / 2]; }); // TODO: optimize |
| |
| else if constexpr (_Bytes == _UBytes) |
| return _DataType::_S_recursive_bit_cast(__x); |
| else if constexpr (_Bytes <= sizeof(0ll)) |
| { |
| using _U2 = __similar_mask<__integer_from<_Bytes>, _S_size, _UAbi>; |
| return _DataType::_S_recursive_bit_cast(_U2(__x)); |
| } |
| else if constexpr (_UBytes > 1) |
| { |
| using _U2 = __similar_mask<__integer_from<_UBytes / 2>, _S_size * 2, _UAbi>; |
| return _U2::_S_recursive_bit_cast(__x); // calls conversion ctor on _DataType |
| } |
| else // _Bytes == 16 && _UBytes == 1 |
| // convert twice (1 -> 2 -> 16) |
| // The conversion to short keeps the intermediate mask as small as possible and thus |
| // requires fewer across-128bit boundary shuffles. |
| return basic_mask(__similar_mask<short, _UV::_S_size, _UAbi>(__x))._M_data; |
| }()) |
| {} |
| |
| using _Base::_MaskBase; |
| |
| // [simd.mask.ctor] generator constructor ------------------------------- |
| template <__simd_generator_invokable<bool, _S_size> _Fp> |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_mask(_Fp&& __gen) |
| : _M_data([&] [[__gnu__::__always_inline__]] { |
| // for _CxIleav, the results of each __gen call need to initialize two |
| // neighboring elements |
| constexpr auto [...__is] = _IotaArray<_S_size>; |
| bool __tmp[_S_size] = {__gen(__simd_size_c<__is>)...}; |
| return _DataType([&] [[__gnu__::__always_inline__]] (size_t __i) { |
| return __tmp[__i / 2]; |
| }); |
| }()) |
| {} |
| |
| // [simd.mask.ctor] bitset constructor ---------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_mask(const same_as<bitset<_S_size>> auto& __b) noexcept // LWG 4382. |
| : _M_data(_DataType::_S_init(__duplicate_each_bit<_S_size>( |
| std::simd::__bitset_to_pairs(__b)))) |
| {} |
| |
| // [simd.mask.ctor] uint constructor ------------------------------------ |
| template <unsigned_integral _Tp> |
| requires (!same_as<_Tp, bool>) // LWG 4382. |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_mask(_Tp __val) noexcept |
| : _M_data(__duplicate_each_bit<_S_size>(__val)) |
| {} |
| |
| // [simd.mask.subscr] --------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr value_type |
| operator[](__simd_size_type __i) const |
| { return _M_data[__i * 2]; } |
| |
| // [simd.mask.unary] ---------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr basic_mask |
| operator!() const noexcept |
| { return _S_init(!_M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _VecType |
| operator+() const noexcept requires destructible<_VecType> |
| { return operator _VecType(); } |
| |
| constexpr _VecType |
| operator+() const noexcept = delete; |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _VecType |
| operator-() const noexcept requires destructible<_VecType> |
| { |
| using _Ip = typename _VecType::value_type; |
| if constexpr (_S_use_bitmask) |
| return __select_impl(*this, _Ip(-1), _Ip()); |
| else |
| return __builtin_bit_cast(_VecType, -_M_data); |
| } |
| |
| constexpr _VecType |
| operator-() const noexcept = delete; |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _VecType |
| operator~() const noexcept requires destructible<_VecType> |
| { |
| using _Ip = typename _VecType::value_type; |
| if constexpr (_S_use_bitmask) |
| return __select_impl(*this, _Ip(-2), _Ip(-1)); |
| else |
| return __builtin_bit_cast(_VecType, _M_data) - _Ip(1); |
| } |
| |
| constexpr _VecType |
| operator~() const noexcept = delete; |
| |
| // [simd.mask.conv] ----------------------------------------------------- |
| template <typename _Up, typename _UAbi> |
| requires (_UAbi::_S_size == _S_size) |
| [[__gnu__::__always_inline__]] |
| constexpr explicit(sizeof(_Up) != _Bytes) |
| operator basic_vec<_Up, _UAbi>() const noexcept |
| { |
| using _Mp = typename basic_vec<_Up, _UAbi>::mask_type; |
| return __select_impl(_Mp(*this), basic_vec<_Up, _UAbi>(1), basic_vec<_Up, _UAbi>(0)); |
| } |
| |
| using _Base::operator basic_vec; |
| |
| // [simd.mask.namedconv] ------------------------------------------------ |
| [[__gnu__::__always_inline__]] |
| constexpr bitset<_S_size> |
| to_bitset() const noexcept |
| { return std::simd::__unwrap_pairs_to_bitset<_S_size>(_M_to_uint()); } |
| |
| template <int _Offset = 0, _ArchTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_to_uint() const |
| { return _M_data.template _M_to_uint<_Offset, true>(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr unsigned long long |
| to_ullong() const |
| { return std::simd::__unwrap_pairs_to_ullong(_M_to_uint()); } |
| |
| // [simd.mask.binary] --------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator&&(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data & __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator||(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data | __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator&(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data & __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator|(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data | __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator^(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data ^ __y._M_data); } |
| |
| // [simd.mask.cassign] -------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask& |
| operator&=(basic_mask& __x, const basic_mask& __y) noexcept |
| { |
| __x._M_data &= __y._M_data; |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask& |
| operator|=(basic_mask& __x, const basic_mask& __y) noexcept |
| { |
| __x._M_data |= __y._M_data; |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask& |
| operator^=(basic_mask& __x, const basic_mask& __y) noexcept |
| { |
| __x._M_data ^= __y._M_data; |
| return __x; |
| } |
| |
| // [simd.mask.comparison] ----------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator==(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data == __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator!=(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data != __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator>=(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data >= __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator<=(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data <= __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator>(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data > __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator<(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data < __y._M_data); } |
| |
| // [simd.mask.cond] ----------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| __select_impl(const basic_mask& __k, const basic_mask& __t, const basic_mask& __f) noexcept |
| { return _S_init(__select_impl(__k._M_data, __t._M_data, __f._M_data)); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| __select_impl(const basic_mask& __k, same_as<bool> auto __t, same_as<bool> auto __f) noexcept |
| { return _S_init(__select_impl(__k._M_data, __t, __f)); } |
| |
| template <__vectorizable _T0, same_as<_T0> _T1> |
| requires (sizeof(_T0) == _Bytes) |
| [[__gnu__::__always_inline__]] |
| friend constexpr vec<_T0, _S_size> |
| __select_impl(const basic_mask& __k, const _T0& __t, const _T1& __f) noexcept |
| { |
| using _Vp = vec<_T0, _S_size>; |
| return __select_impl(static_cast<typename _Vp::mask_type>(__k), _Vp(__t), _Vp(__f)); |
| } |
| |
| // [simd.mask.reductions] implementation -------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr bool |
| _M_all_of() const noexcept |
| { return _M_data._M_all_of(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr bool |
| _M_any_of() const noexcept |
| { return _M_data._M_any_of(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr bool |
| _M_none_of() const noexcept |
| { return _M_data._M_none_of(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr __simd_size_type |
| _M_reduce_count() const noexcept |
| { return _M_data._M_reduce_count() / 2; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr __simd_size_type |
| _M_reduce_min_index() const |
| { return _M_data._M_reduce_min_index() / 2; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr __simd_size_type |
| _M_reduce_max_index() const |
| { return _M_data._M_reduce_max_index() / 2; } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr bool |
| __is_const_known(const basic_mask& __x) |
| { return __is_const_known(__x._M_data); } |
| }; |
| |
| template <__vectorizable _Tp, __abi_tag _Ap> |
| requires __complex_like<_Tp> && _Ap::_S_is_cx_ileav && (_Ap::_S_size >= 2) // size 1 is below |
| class basic_vec<_Tp, _Ap> |
| : public _VecBase<_Tp, _Ap> |
| { |
| template <typename, typename> |
| friend class basic_vec; |
| |
| static constexpr int _S_size = _Ap::_S_size; |
| |
| static constexpr int _S_full_size = __bit_ceil(unsigned(_S_size)); |
| |
| using _T0 = typename _Tp::value_type; |
| |
| using _TSimd = __similar_vec<_T0, 2 * _S_size, _Ap>; |
| |
| using _RealSimd = __similar_vec<_T0, _S_size, _Ap>; |
| |
| _TSimd _M_data = {}; |
| |
| static constexpr bool _S_use_bitmask = _TSimd::_S_use_bitmask; |
| |
| static constexpr bool _S_is_partial = sizeof(_M_data) > sizeof(_Tp) * _S_size; |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_vec |
| _S_init(const _TSimd& __x) |
| { |
| basic_vec __r; |
| __r._M_data = __x; |
| return __r; |
| } |
| |
| public: |
| using value_type = _Tp; |
| |
| using mask_type = _VecBase<_Tp, _Ap>::mask_type; |
| |
| // internal but public API ---------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr const _TSimd& |
| _M_get_ileav_data() const |
| { return _M_data; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr const auto& |
| _M_get_low() const requires (_Ap::_S_nreg >= 2) |
| { return _M_data._M_get_low(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr const auto& |
| _M_get_high() const requires (_Ap::_S_nreg >= 2) |
| { return _M_data._M_get_high(); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr bool |
| __is_const_known(const basic_vec& __x) |
| { return __is_const_known(__x._M_data); } |
| |
| template <typename _Vp> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_chunk() const noexcept |
| { |
| if constexpr (_Vp::abi_type::_S_is_cx_ileav) |
| { |
| constexpr int __n = _S_size / _Vp::_S_size; |
| constexpr int __rem = _S_size % _Vp::_S_size; |
| const auto __chunked = _M_data.template _M_chunk<resize_t<_Vp::_S_size * 2, |
| _TSimd>>(); |
| constexpr auto [...__is] = _IotaArray<__n>; |
| if constexpr (__rem == 0) |
| return array<_Vp, __n> {_Vp::_S_init(__chunked[__is])...}; |
| else |
| { |
| using _Rest = resize_t<__rem, _Vp>; |
| return tuple(_Vp::_S_init(get<__is>(__chunked))..., |
| _Rest::_S_init(get<__n>(__chunked))); |
| } |
| } |
| else |
| return resize_t<_S_size, _Vp>(*this).template _M_chunk<_Vp>(); |
| } |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr const basic_vec& |
| _S_concat(const basic_vec& __x0) noexcept |
| { return __x0; } |
| |
| template <typename... _As> |
| requires (sizeof...(_As) > 1) |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_vec |
| _S_concat(const basic_vec<value_type, _As>&... __xs) noexcept |
| { return basic_vec::_S_init(_TSimd::_S_concat(__xs._M_get_ileav_data()...)); } |
| |
| template <typename _BinaryOp> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_reduce_to_register(_BinaryOp __binary_op) const |
| { |
| if constexpr (_TSimd::abi_type::_S_nreg == 1) |
| return *this; |
| else |
| { |
| auto [__lo, __hi] = _M_chunk<resize_t<__bit_ceil(unsigned(_S_size)) / 2, |
| basic_vec>>(); |
| auto __a = __lo._M_reduce_to_register(__binary_op); |
| auto __b = __hi._M_reduce_to_register(__binary_op); |
| if constexpr (__a._S_size == __b._S_size) |
| return __binary_op(__a, __b); |
| else |
| { |
| using _V1 = resize_t<1, basic_vec>; |
| return __binary_op(_V1(__a._M_reduce(__binary_op)), |
| _V1(__b._M_reduce(__binary_op))); |
| } |
| } |
| } |
| |
| template <typename _BinaryOp, _ArchTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| constexpr value_type |
| _M_reduce(_BinaryOp __binary_op) const |
| { |
| if constexpr (_S_size == 1) |
| return operator[](0); |
| else if constexpr (_Traits.template _M_eval_as_f32<_T0>()) |
| return value_type(rebind_t<complex<float>, basic_vec>(*this)._M_reduce(__binary_op)); |
| else if constexpr (_TSimd::abi_type::_S_nreg >= 2) |
| return _M_reduce_to_register(__binary_op)._M_reduce(__binary_op); |
| else if constexpr (__has_single_bit(unsigned(_S_size))) |
| { |
| const auto [__a, __b] = _M_chunk<resize_t<_S_size / 2, basic_vec>>(); |
| return __binary_op(__a, __b)._M_reduce(__binary_op); |
| } |
| else |
| { |
| const auto [__a, __b, __c, ...__rest] |
| = _M_chunk<resize_t<__bit_floor(unsigned(_S_size)) / 2, basic_vec>>(); |
| const auto __ab = __binary_op(__a, __b); |
| static_assert(sizeof...(__rest) <= 1); |
| if constexpr (__a._S_size != __c._S_size) |
| return cat(__ab, __c)._M_reduce(__binary_op); |
| else |
| return cat(__binary_op(__ab, __c), __rest...)._M_reduce(__binary_op); |
| } |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| static inline basic_vec |
| _S_partial_load(const _Up* __mem, size_t __n) |
| { |
| if constexpr (__complex_like<_Up>) |
| return _S_init(_TSimd::_S_partial_load( |
| reinterpret_cast<const typename _Up::value_type*>(__mem), __n * 2)); |
| else |
| return basic_vec(_RealSimd::_S_partial_load(__mem, __n)); |
| } |
| |
| template <typename _Up, _ArchTraits _Traits = {}> |
| static inline basic_vec |
| _S_masked_load(const _Up* __mem, mask_type __k) |
| { |
| if constexpr (__complex_like<_Up>) |
| return _S_init(_TSimd::_S_masked_load( |
| reinterpret_cast<const typename _Up::value_type*>(__mem), |
| __k._M_data)); |
| else |
| return basic_vec(_RealSimd::_S_masked_load(__mem, typename _RealSimd::mask_type(__k))); |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| inline void |
| _M_store(_Up* __mem) const |
| { |
| static_assert(__complex_like<_Up>); |
| _M_data._M_store(reinterpret_cast<typename _Up::value_type*>(__mem)); |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| static inline void |
| _S_partial_store(const basic_vec& __v, _Up* __mem, size_t __n) |
| { |
| static_assert(__complex_like<_Up>); |
| _TSimd::_S_partial_store(__v._M_data, reinterpret_cast<typename _Up::value_type*>(__mem), |
| __n * 2); |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| static inline void |
| _S_masked_store(const basic_vec& __v, _Up* __mem, const mask_type& __k) |
| { |
| static_assert(__complex_like<_Up>); |
| _TSimd::_S_masked_store(__v._M_data, reinterpret_cast<typename _Up::value_type*>(__mem), |
| __k._M_data); |
| } |
| |
| basic_vec() = default; |
| |
| // TODO: conversion extensions |
| |
| // [simd.ctor] broadcast constructor ------------------------------------ |
| template <__broadcast_constructible<value_type> _Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_Up&& __x) noexcept |
| : _M_data([&](int __i) { |
| if constexpr (__complex_like<_Up>) |
| return (__i & 1) == 0 ? __x.real() : __x.imag(); |
| else |
| return (__i & 1) == 0 ? __x : _T0(); |
| }) |
| {} |
| |
| // [simd.ctor] conversion constructor ----------------------------------- |
| template <__complex_like _Up, typename _UAbi> |
| requires (_S_size == _UAbi::_S_size) |
| && __explicitly_convertible_to<_Up, value_type> |
| && _UAbi::_S_is_cx_ileav |
| [[__gnu__::__always_inline__]] |
| constexpr |
| explicit(!convertible_to<_Up, value_type>) |
| basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept |
| : _M_data(__x._M_data) |
| {} |
| |
| template <__complex_like _Up, typename _UAbi> |
| requires (_S_size == _UAbi::_S_size) |
| && __explicitly_convertible_to<_Up, value_type> |
| && (!_UAbi::_S_is_cx_ileav) |
| [[__gnu__::__always_inline__]] |
| constexpr |
| explicit(!convertible_to<_Up, value_type>) |
| basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept |
| : basic_vec(static_cast<_RealSimd>(__x._M_real), static_cast<_RealSimd>(__x._M_imag)) |
| {} |
| |
| template <typename _Up, typename _UAbi> |
| requires (!__complex_like<_Up>) |
| && (_S_size == _UAbi::_S_size) |
| && __explicitly_convertible_to<_Up, value_type> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| explicit(!convertible_to<_Up, value_type>) |
| basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept |
| : basic_vec(_RealSimd(__x)) |
| {} |
| |
| using _VecBase<_Tp, _Ap>::_VecBase; |
| |
| // [simd.ctor] generator constructor ------------------------------------ |
| template <__simd_generator_invokable<value_type, _S_size> _Fp> |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_vec(_Fp&& __gen) |
| : _M_data([&] { |
| using _Arr = std::array<value_type, sizeof(_TSimd) / sizeof(value_type)>; |
| constexpr auto [...__is] = _IotaArray<_S_size>; |
| const _Arr __tmp = { static_cast<value_type>(__gen(__simd_size_c<__is>))... }; |
| return __builtin_bit_cast(_TSimd, __tmp); |
| }()) |
| {} |
| |
| // [simd.ctor] load constructor ----------------------------------------- |
| template <__complex_like _Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_LoadCtorTag, const _Up* __ptr) |
| : _M_data([&] { |
| if consteval |
| { |
| return _TSimd([&](int __i) { |
| const _Up& __cx = __ptr[__i / 2]; |
| return static_cast<_T0>(__i % 2 == 0 ? __cx.real() : __cx.imag()); |
| }); |
| } |
| else |
| { |
| return _TSimd(_LoadCtorTag(), |
| reinterpret_cast<const typename _Up::value_type*>(__ptr)); |
| } |
| }()) |
| {} |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_LoadCtorTag, const _Up* __ptr) |
| : basic_vec(_RealSimd(_LoadCtorTag(), __ptr)) |
| {} |
| |
| template <ranges::contiguous_range _Rg, typename... _Flags> |
| requires ranges::__static_sized_range<_Rg> && (ranges::__static_size<_Rg>() == _S_size) |
| && __vectorizable<ranges::range_value_t<_Rg>> |
| && __explicitly_convertible_to<ranges::range_value_t<_Rg>, value_type> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_Rg&& __range, flags<_Flags...> __flags = {}) |
| : basic_vec(_LoadCtorTag(), __flags.template _S_adjust_pointer<basic_vec>( |
| ranges::data(__range))) |
| { |
| static_assert(__loadstore_convertible_to<ranges::range_value_t<_Rg>, value_type, |
| _Flags...>); |
| } |
| |
| // [simd.ctor] complex init --------------------------------------------- |
| // This uses _RealSimd as proposed in LWG4230 |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(const _RealSimd& __re, const _RealSimd& __im = {}) noexcept |
| { |
| __cxileav::__set_real(_M_data, __re); |
| __cxileav::__set_imag(_M_data, __im); |
| } |
| |
| // [simd.subscr] -------------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr value_type |
| operator[](__simd_size_type __i) const |
| { return value_type(_M_data[__i * 2], _M_data[__i * 2 + 1]); } |
| |
| // [simd.unary] unary operators ----------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec& |
| operator++() noexcept requires requires(value_type __a) { ++__a; } |
| { |
| _M_data += value_type(_T0(1)); |
| return *this; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator++(int) noexcept requires requires(value_type __a) { __a++; } |
| { |
| basic_vec __r = *this; |
| _M_data += value_type(_T0(1)); |
| return __r; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec& |
| operator--() noexcept requires requires(value_type __a) { --__a; } |
| { |
| _M_data -= value_type(_T0(1)); |
| return *this; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator--(int) noexcept requires requires(value_type __a) { __a--; } |
| { |
| basic_vec __r = *this; |
| _M_data -= value_type(_T0(1)); |
| return __r; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr mask_type |
| operator!() const noexcept requires requires(value_type __a) { !__a; } |
| { return _S_init(!_M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator+() const noexcept requires requires(value_type __a) { +__a; } |
| { return *this; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator-() const noexcept requires requires(value_type __a) { -__a; } |
| { |
| basic_vec __r = *this; |
| __r._M_data = -_M_data; |
| return __r; |
| } |
| |
| // [simd.cassign] compound assignment ----------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator+=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a + __a; } |
| { |
| __x._M_data += __y._M_data; |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator-=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a - __a; } |
| { |
| __x._M_data -= __y._M_data; |
| return __x; |
| } |
| |
| template <_TargetTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator*=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a * __a; } |
| { |
| __cxileav::__mul<value_type, _Traits>(__x._M_data, __y._M_data); |
| return __x; |
| } |
| |
| template <int _RemoveMe = 0> |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator/=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a / __a; } |
| { |
| static_assert(false, "TODO"); |
| } |
| |
| // [simd.comparison] compare operators ---------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr mask_type |
| operator==(const basic_vec& __x, const basic_vec& __y) noexcept |
| { return mask_type::_S_and_neighbors(__x._M_data == __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr mask_type |
| operator!=(const basic_vec& __x, const basic_vec& __y) noexcept |
| { return mask_type::_S_or_neighbors(__x._M_data != __y._M_data); } |
| |
| // [simd.complex.access] complex-value accessors ------------------------ |
| // LWG4230: returns _RealSimd instead of auto |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| real() const noexcept |
| { return permute<_S_size>(_M_data, [](int __i) { return __i * 2; }); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| imag() const noexcept |
| { return permute<_S_size>(_M_data, [](int __i) { return __i * 2 + 1; }); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| real(const _RealSimd& __x) noexcept |
| { __cxileav::__set_real(_M_data, __x); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| imag(const _RealSimd& __x) noexcept |
| { __cxileav::__set_imag(_M_data, __x); } |
| |
| // [simd.cond] --------------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec |
| __select_impl(const mask_type& __k, const basic_vec& __t, const basic_vec& __f) noexcept |
| { return _S_init(__select_impl(__k._M_data, __t._M_data, __f._M_data)); } |
| |
| // [simd.complex.math] internals --------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| _M_abs() const; // TODO: depends on [simd.math] |
| |
| // associated functions |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| _M_norm() const |
| { |
| auto __re = real(); |
| auto __im = imag(); |
| return __re * __re + __im * __im; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| _M_conj() const |
| { return _S_init(__cxileav::__negate_imag(_M_data)); } |
| }; |
| |
| // complex contiguous (_CxCtgus) -------------------------------------------- |
| // (and _CxIleav basic_vec with size 1) |
| |
| /** @internal |
| * @brief Functions acting on / recursing into the non-complex fp vec objects, where real and |
| * imaginary parts are stored in separate vec objects. |
| */ |
| namespace __cxctgus |
| { |
| /** @internal |
| * @brief Recompute all complex multiplications where @p __nan is true using @p _Cx's |
| * multiplication operator. |
| * |
| * @todo use coarser _TargetTraits and move into .so |
| */ |
| template <typename _Cx, _TargetTraits, __vec_builtin _TV, typename _Kp> |
| [[__gnu__::__cold__, __gnu__::__noinline__]] |
| constexpr void |
| __redo_mul(_TV& __re, _TV& __im, const _TV __re0, const _TV __im0, |
| const _TV __re1, const _TV __im1, const _Kp __nan, int __n) |
| { |
| for (int __i = 0; __i < __n; ++__i) |
| { |
| bool __isnan; |
| if constexpr (is_integral_v<_Kp>) |
| __isnan = (__nan & (_Kp(1) << __i)) != 0; |
| else |
| __isnan = __nan[__i] != 0; |
| if (__isnan) |
| { |
| const _Cx __c0(__re0[__i], __im0[__i]); |
| const _Cx __c1(__re1[__i], __im1[__i]); |
| const _Cx __cr = __c0 * __c1; |
| __vec_set(__re, __i, __cr.real()); |
| __vec_set(__im, __i, __cr.imag()); |
| } |
| } |
| } |
| |
| /** @internal |
| * @brief Complex multiplication of (@p __re0, @p __im0) and (@p __re1, @p __im1), returning the |
| * result in @p __re0 and @p __im0. |
| */ |
| template <typename _Cx, _TargetTraits _Traits, typename _Tp, typename _Ap> |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| __mul(basic_vec<_Tp, _Ap>& __re0, basic_vec<_Tp, _Ap>& __im0, |
| const basic_vec<_Tp, _Ap>& __re1, const basic_vec<_Tp, _Ap>& __im1) |
| { |
| static_assert(__complex_like<_Cx>); |
| if constexpr (_Ap::_S_nreg >= 2) |
| { |
| __mul<_Cx, _Traits>(__re0._M_get_low(), __im0._M_get_low(), |
| __re1._M_get_low(), __im1._M_get_low()); |
| __mul<_Cx, _Traits>(__re0._M_get_high(), __im0._M_get_high(), |
| __re1._M_get_high(), __im1._M_get_high()); |
| } |
| else if constexpr (_Ap::_S_size == 1) |
| { // use _Cx::operator* |
| const _Cx __c0(__re0._M_get(), __im0._M_get()); |
| const _Cx __c1(__re1._M_get(), __im1._M_get()); |
| const _Cx __cr = __c0 * __c1; |
| __re0._M_get() = __cr.real(); |
| __im0._M_get() = __cr.imag(); |
| } |
| else if constexpr (_Traits.template _M_eval_as_f32<_Tp>()) |
| { |
| using _Vf = rebind_t<float, basic_vec<_Tp, _Ap>>; |
| using _Cf = complex<float>; |
| _Vf __re0f = __re0; |
| _Vf __im0f = __im0; |
| __mul<_Cf, _Traits, float, typename _Vf::abi_type>(__re0f, __im0f, __re1, __im1); |
| __re0 = static_cast<basic_vec<_Tp, _Ap>>(__re0f); |
| __im0 = static_cast<basic_vec<_Tp, _Ap>>(__im0f); |
| } |
| else |
| { |
| basic_vec<_Tp, _Ap> __re = __re0 * __re1 - __im0 * __im1; |
| basic_vec<_Tp, _Ap> __im = __re0 * __im1 + __im0 * __re1; |
| const auto __nan = __re._M_isunordered(__im); |
| if (__nan._M_any_of()) [[unlikely]] |
| __redo_mul<_Cx, _Traits>(__re._M_get(), __im._M_get(), __re0._M_get(), __im0._M_get(), |
| __re1._M_get(), __im1._M_get(), |
| __nan._M_concat_data(), _Ap::_S_size); |
| __re0 = __re; |
| __im0 = __im; |
| } |
| } |
| } |
| |
| template <size_t _Bytes, __abi_tag _Ap> |
| requires _Ap::_S_is_cx_ctgus && (_Ap::_S_size >= 2) // size 1 is in simd_mask.h |
| class basic_mask<_Bytes, _Ap> |
| : public _MaskBase<_Bytes, _Ap> |
| { |
| using _Base = _MaskBase<_Bytes, _Ap>; |
| |
| using _VecType = _Base::_VecType; |
| |
| template <size_t, typename> |
| friend class basic_mask; |
| |
| template <typename, typename> |
| friend class basic_vec; |
| |
| static constexpr int _S_size = _Ap::_S_size; |
| |
| using _DataType = __component_mask_for_ctgus<_Bytes, _Ap>; |
| |
| static_assert(_DataType::abi_type::_S_nreg == _Ap::_S_nreg); |
| |
| static constexpr bool _S_is_scalar = _DataType::_S_is_scalar; |
| |
| static constexpr bool _S_use_bitmask = _DataType::_S_use_bitmask; |
| |
| static constexpr int _S_full_size = _DataType::_S_full_size; |
| |
| static constexpr bool _S_is_partial = _DataType::_S_is_partial; |
| |
| static constexpr bool _S_has_bool_member = _DataType::_S_has_bool_member; |
| |
| static constexpr size_t _S_padding_bytes = _DataType::_S_padding_bytes; |
| |
| _DataType _M_data; |
| |
| public: |
| using value_type = bool; |
| |
| using abi_type = _Ap; |
| |
| // internal but public API ---------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_init(const _DataType& __x) |
| { |
| basic_mask __r; |
| __r._M_data = __x; |
| return __r; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr const _DataType& |
| _M_get() const |
| { return _M_data; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_concat_data() const |
| { return _M_data._M_concat_data(); } |
| |
| template <_ArchTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_partial_mask_of_n(int __n) |
| { return _S_init(_DataType::_S_partial_mask_of_n(__n)); } |
| |
| template <typename _Mp> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_chunk() const noexcept |
| { |
| if constexpr (_Mp::abi_type::_S_variant != _Ap::_S_variant) |
| { |
| using _M2 = resize_t<_S_size, _Mp>; |
| static_assert(!is_same_v<_M2, basic_mask>); |
| return static_cast<_M2>(*this).template _M_chunk<_Mp>(); |
| } |
| else if constexpr (_Mp::_S_size == 1) |
| { |
| constexpr auto [...__is] = _IotaArray<_S_size>; |
| return array{_Mp(_M_data[__is])...}; |
| } |
| else // _Mp is the same partial specialization |
| { |
| constexpr int __rem = _S_size % _Mp::_S_size; |
| const auto [...__xs, __last] = _M_data.template _M_chunk<typename _Mp::_DataType>(); |
| if constexpr (__rem == 0) |
| return array{_Mp::_S_init(__xs)..., _Mp::_S_init(__last)}; |
| else |
| return tuple(_Mp::_S_init(__xs)..., resize_t<__rem, _Mp>(__last)); |
| } |
| } |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr const basic_mask& |
| _S_concat(const basic_mask& __x0) noexcept |
| { return __x0; } |
| |
| /** @internal |
| * @brief Adjust the mask type to match _RealSimd. |
| * |
| * This is a trivial unwrap for this partial specialization of basic_mask. However, for |
| * _Abi<1, 1, _CxCtgus> _M_data is the bool object and needs to be converted. |
| */ |
| [[__gnu__::__always_inline__]] |
| constexpr const _DataType& |
| _M_get_ctgus_data() const noexcept |
| { return _M_data; } |
| |
| template <typename... _As> |
| requires (sizeof...(_As) > 1) |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_mask |
| _S_concat(const basic_mask<_Bytes, _As>&... __xs) noexcept |
| { return basic_mask::_S_init(_DataType::_S_concat(__xs._M_get_ctgus_data()...)); } |
| |
| // [simd.mask.overview] default constructor ----------------------------- |
| basic_mask() = default; |
| |
| // [simd.mask.overview] conversion extensions --------------------------- |
| template <__vec_builtin _TV> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_mask(const _TV& __x) requires convertible_to<_TV, _DataType> |
| : _M_data(__x) |
| {} |
| |
| template <__vec_builtin _TV> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| operator _TV() requires convertible_to<_DataType, _TV> |
| { return _M_data; } |
| |
| // [simd.mask.ctor] broadcast constructor ------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_mask(same_as<bool> auto __x) noexcept // LWG 4382. |
| : _M_data(__x) |
| {} |
| |
| // [simd.mask.ctor] conversion constructor ------------------------------ |
| template <size_t _UBytes, typename _UAbi> |
| requires (_S_size == _UAbi::_S_size) |
| [[__gnu__::__always_inline__]] |
| constexpr explicit(__is_mask_conversion_explicit<_Ap, _UAbi>(_Bytes, _UBytes)) |
| basic_mask(const basic_mask<_UBytes, _UAbi>& __x) noexcept |
| : _M_data(__x) |
| {} |
| |
| using _Base::_MaskBase; |
| |
| // [simd.mask.ctor] generator constructor ------------------------------- |
| template <__simd_generator_invokable<bool, _S_size> _Fp> |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_mask(_Fp&& __gen) |
| : _M_data(__gen) |
| {} |
| |
| // [simd.mask.ctor] bitset constructor ---------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_mask(const same_as<bitset<_S_size>> auto& __b) noexcept // LWG 4382. |
| : _M_data(__b) |
| {} |
| |
| // [simd.mask.ctor] uint constructor ------------------------------------ |
| template <unsigned_integral _Tp> |
| requires (!same_as<_Tp, bool>) // LWG 4382. |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_mask(_Tp __val) noexcept |
| : _M_data(__val) |
| {} |
| |
| // [simd.mask.subscr] --------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr value_type |
| operator[](__simd_size_type __i) const |
| { return _M_data[__i]; } |
| |
| // [simd.mask.unary] ---------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr basic_mask |
| operator!() const noexcept |
| { return _S_init(!_M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _VecType |
| operator+() const noexcept requires destructible<_VecType> |
| { return static_cast<_VecType>(_M_data); } |
| |
| constexpr _VecType |
| operator+() const noexcept = delete; |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _VecType |
| operator-() const noexcept requires destructible<_VecType> |
| { |
| using _Ip = typename _VecType::value_type; |
| if constexpr (_S_use_bitmask) |
| return __select_impl(*this, _Ip(-1), _Ip()); |
| else |
| return -_M_data; // sign-extends |
| } |
| |
| constexpr _VecType |
| operator-() const noexcept = delete; |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _VecType |
| operator~() const noexcept requires destructible<_VecType> |
| { |
| using _Ip = typename _VecType::value_type; |
| if constexpr (_S_use_bitmask) |
| return __select_impl(*this, _Ip(-2), _Ip(-1)); |
| else |
| return ~_M_data; // sign-extends |
| } |
| |
| constexpr _VecType |
| operator~() const noexcept = delete; |
| |
| // [simd.mask.conv] ----------------------------------------------------- |
| template <typename _Up, typename _UAbi> |
| requires (_UAbi::_S_size == _S_size) |
| [[__gnu__::__always_inline__]] |
| constexpr explicit(sizeof(_Up) != _Bytes) |
| operator basic_vec<_Up, _UAbi>() const noexcept |
| { |
| using _UV = basic_vec<_Up, _UAbi>; |
| using _Mp = typename _UV::mask_type; |
| return __select_impl(static_cast<_Mp>(_M_data), _UV(1), _UV(0)); |
| } |
| |
| using _Base::operator basic_vec; |
| |
| // [simd.mask.namedconv] ------------------------------------------------ |
| [[__gnu__::__always_inline__]] |
| constexpr bitset<_S_size> |
| to_bitset() const noexcept |
| { return _M_data.to_bitset(); } |
| |
| template <int _Offset = 0, _ArchTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_to_uint() const |
| { return _M_data.template _M_to_uint<_Offset>(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr unsigned long long |
| to_ullong() const |
| { return _M_data.to_ullong(); } |
| |
| // [simd.mask.binary] --------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator&&(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data & __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator||(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data | __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator&(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data & __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator|(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data | __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator^(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data ^ __y._M_data); } |
| |
| // [simd.mask.cassign] -------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask& |
| operator&=(basic_mask& __x, const basic_mask& __y) noexcept |
| { |
| __x._M_data &= __y._M_data; |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask& |
| operator|=(basic_mask& __x, const basic_mask& __y) noexcept |
| { |
| __x._M_data |= __y._M_data; |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask& |
| operator^=(basic_mask& __x, const basic_mask& __y) noexcept |
| { |
| __x._M_data ^= __y._M_data; |
| return __x; |
| } |
| |
| // [simd.mask.comparison] ----------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator==(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data == __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator!=(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data != __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator>=(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data >= __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator<=(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data <= __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator>(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data > __y._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| operator<(const basic_mask& __x, const basic_mask& __y) noexcept |
| { return _S_init(__x._M_data < __y._M_data); } |
| |
| // [simd.mask.cond] ----------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| __select_impl(const basic_mask& __k, const basic_mask& __t, const basic_mask& __f) noexcept |
| { return __select_impl(__k._M_data, __t._M_data, __f._M_data); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_mask |
| __select_impl(const basic_mask& __k, same_as<bool> auto __t, same_as<bool> auto __f) noexcept |
| { return _S_init(__select_impl(__k._M_data, __t, __f)); } |
| |
| template <__vectorizable _T0, same_as<_T0> _T1> |
| requires (sizeof(_T0) == _Bytes) |
| [[__gnu__::__always_inline__]] |
| friend constexpr vec<_T0, _S_size> |
| __select_impl(const basic_mask& __k, const _T0& __t, const _T1& __f) noexcept |
| { |
| using _Vp = vec<_T0, _S_size>; |
| return __select_impl(static_cast<typename _Vp::mask_type>(__k), _Vp(__t), _Vp(__f)); |
| } |
| |
| // [simd.mask.reductions] implementation -------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr bool |
| _M_all_of() const noexcept |
| { return _M_data._M_all_of(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr bool |
| _M_any_of() const noexcept |
| { return _M_data._M_any_of(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr bool |
| _M_none_of() const noexcept |
| { return _M_data._M_none_of(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr __simd_size_type |
| _M_reduce_count() const noexcept |
| { return _M_data._M_reduce_count(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr __simd_size_type |
| _M_reduce_min_index() const |
| { return _M_data._M_reduce_min_index(); } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr __simd_size_type |
| _M_reduce_max_index() const |
| { return _M_data._M_reduce_max_index(); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr bool |
| __is_const_known(const basic_mask& __x) |
| { return __is_const_known(__x._M_data); } |
| }; |
| |
| template <__vectorizable _Tp, __abi_tag _Ap> |
| requires __complex_like<_Tp> && (_Ap::_S_is_cx_ctgus || _Ap::_S_size == 1) |
| class basic_vec<_Tp, _Ap> |
| : public _VecBase<_Tp, _Ap> |
| { |
| template <typename, typename> |
| friend class basic_vec; |
| |
| static constexpr int _S_size = _Ap::_S_size; |
| |
| static constexpr int _S_full_size = __bit_ceil(unsigned(_S_size)); |
| |
| using _T0 = typename _Tp::value_type; |
| |
| using _RealSimd = __similar_vec<_T0, _S_size, _Ap>; |
| |
| _RealSimd _M_real = {}; |
| |
| _RealSimd _M_imag = {}; |
| |
| static constexpr bool _S_is_scalar = _RealSimd::_S_is_scalar; |
| |
| static constexpr bool _S_use_bitmask = _RealSimd::_S_use_bitmask; |
| |
| static constexpr bool _S_is_partial = _RealSimd::_S_is_partial; |
| |
| public: |
| using value_type = _Tp; |
| |
| using mask_type = _VecBase<_Tp, _Ap>::mask_type; |
| |
| // internal but public API ---------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd& |
| _M_get_real() noexcept |
| { return _M_real; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr const _RealSimd& |
| _M_get_real() const noexcept |
| { return _M_real; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd& |
| _M_get_imag() noexcept |
| { return _M_imag; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr const _RealSimd& |
| _M_get_imag() const noexcept |
| { return _M_imag; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_get_low() const requires (_Ap::_S_nreg >= 2) |
| { |
| return resize_t<_M_real._N0, basic_vec>( |
| _M_real._M_get_low(), _M_imag._M_get_low()); |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_get_high() const requires (_Ap::_S_nreg >= 2) |
| { |
| return resize_t<_M_real._N1, basic_vec>( |
| _M_real._M_get_high(), _M_imag._M_get_high()); |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_concat_data(bool /*do_sanitize*/ = false) const |
| requires (_S_size == 1) // only for _CxCtgus of size 1 |
| { |
| return __vec_builtin_type<__canonical_vec_type_t<_T0>, 2>{ |
| _M_real._M_data, _M_imag._M_data |
| }; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_get_ileav_data() const |
| requires (_S_size == 1 && _Ap::_S_is_cx_ileav) |
| { return __builtin_bit_cast(__similar_vec<_T0, 2, _Ap>, *this); } |
| |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_vec |
| _S_init(const __similar_vec<_T0, 2, _Ap>& __x) |
| requires (_S_size == 1 && _Ap::_S_is_cx_ileav) |
| { return __builtin_bit_cast(basic_vec, __x); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr bool |
| __is_const_known(const basic_vec& __x) |
| { return __is_const_known(__x._M_real) && __is_const_known(__x._M_imag); } |
| |
| template <typename _Vp> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_chunk() const noexcept |
| { |
| constexpr int __n = _S_size / _Vp::_S_size; |
| constexpr int __rem = _S_size % _Vp::_S_size; |
| const auto [...__rs, __rN] = _M_real.template _M_chunk<typename _Vp::_RealSimd>(); |
| const auto [...__is, __iN] = _M_imag.template _M_chunk<typename _Vp::_RealSimd>(); |
| if constexpr (__rem == 0) |
| return array<_Vp, __n>{_Vp(__rs, __is)..., _Vp(__rN, __iN)}; |
| else |
| return tuple(_Vp(__rs, __is)..., resize_t<__rem, _Vp>(__rN, __iN)); |
| } |
| |
| template <typename _A0> |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_vec |
| _S_concat(const basic_vec<value_type, _A0>& __x0) noexcept |
| { return static_cast<basic_vec>(__x0); } |
| |
| template <typename... _As> |
| requires (sizeof...(_As) > 1) |
| [[__gnu__::__always_inline__]] |
| static constexpr basic_vec |
| _S_concat(const basic_vec<value_type, _As>&... __xs) noexcept |
| { return {_RealSimd::_S_concat(__xs._M_real...), _RealSimd::_S_concat(__xs._M_imag...) }; } |
| |
| template <typename _BinaryOp> |
| [[__gnu__::__always_inline__]] |
| constexpr auto |
| _M_reduce_to_register(_BinaryOp __binary_op) const |
| { |
| if constexpr (_RealSimd::abi_type::_S_nreg == 1) |
| return *this; |
| else |
| { |
| auto [__lo, __hi] = _M_chunk<resize_t<_RealSimd::_N0, basic_vec>>(); |
| auto __a = __lo._M_reduce_to_register(__binary_op); |
| auto __b = __hi._M_reduce_to_register(__binary_op); |
| if constexpr (__a._S_size == __b._S_size) |
| return __binary_op(__a, __b); |
| else |
| { |
| using _V1 = resize_t<1, basic_vec>; |
| return __binary_op(_V1(__a._M_reduce(__binary_op)), |
| _V1(__b._M_reduce(__binary_op))); |
| } |
| } |
| } |
| |
| template <typename _BinaryOp, _ArchTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| constexpr value_type |
| _M_reduce(_BinaryOp __binary_op) const |
| { |
| if constexpr (_S_size == 1) |
| return operator[](0); |
| else if constexpr (_Traits.template _M_eval_as_f32<_T0>()) |
| return value_type(rebind_t<complex<float>, basic_vec>(*this)._M_reduce(__binary_op)); |
| else if constexpr (_RealSimd::abi_type::_S_nreg >= 2) |
| return _M_reduce_to_register(__binary_op)._M_reduce(__binary_op); |
| else if constexpr (__has_single_bit(unsigned(_S_size))) |
| { |
| const auto [__a, __b] = _M_chunk<resize_t<_S_size / 2, basic_vec>>(); |
| return __binary_op(__a, __b)._M_reduce(__binary_op); |
| } |
| else |
| { |
| const auto [__a, __b, __c, ...__rest] |
| = _M_chunk<resize_t<__bit_floor(unsigned(_S_size)) / 2, basic_vec>>(); |
| const auto __ab = __binary_op(__a, __b); |
| static_assert(sizeof...(__rest) <= 1); |
| if constexpr (__a._S_size != __c._S_size) |
| return cat(__ab, __c)._M_reduce(__binary_op); |
| else |
| return cat(__binary_op(__ab, __c), __rest...)._M_reduce(__binary_op); |
| } |
| } |
| |
| /** @internal |
| * Implementation of @ref partial_load. |
| * |
| * If @p __mem stores complex numbers, this needs to load @c abcdefgh from memory into two |
| * basic_vec: @c aceg and @c bdfh. |
| * |
| * @param __mem A pointer to an array of @p __n values. Can be complex or real. |
| * @param __n Read no more than @p __n values from memory. |
| * |
| * @todo Optimize with deinterleaving loads or loads + deinterleaving fixup. |
| */ |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| static inline basic_vec |
| _S_partial_load(const _Up* __mem, size_t __n) |
| { |
| if constexpr (__complex_like<_Up>) |
| return basic_vec( |
| _RealSimd([&](size_t __i) -> _T0 { |
| return __i < __n ? __mem[__i].real() : _T0(); |
| }), |
| _RealSimd([&](size_t __i) -> _T0 { |
| return __i < __n ? __mem[__i].imag() : _T0(); |
| })); |
| else |
| return basic_vec(_RealSimd::_S_partial_load(__mem, __n)); |
| } |
| |
| /** @internal |
| * |
| * @todo Optimize with deinterleaving loads or loads + deinterleaving fixup. |
| */ |
| template <typename _Up, _ArchTraits _Traits = {}> |
| static inline basic_vec |
| _S_masked_load(const _Up* __mem, mask_type __k) |
| { |
| if constexpr (__complex_like<_Up>) |
| { // TODO: optimize |
| return basic_vec(_RealSimd([&](int __i) { |
| return __k[__i] ? __mem[__i].real() : _T0(); |
| }), |
| _RealSimd([&](int __i) { |
| return __k[__i] ? __mem[__i].imag() : _T0(); |
| })); |
| } |
| else |
| return basic_vec(_RealSimd::_S_masked_load(__mem, typename _RealSimd::mask_type(__k))); |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| inline void |
| _M_store(_Up* __mem) const |
| { |
| static_assert(__complex_like<_Up>); |
| for (int __i = 0; __i < _S_size; ++__i) |
| { |
| __mem[__i].real(_M_real[__i]); |
| __mem[__i].imag(_M_imag[__i]); |
| } |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| static inline void |
| _S_partial_store(const basic_vec& __v, _Up* __mem, size_t __n) |
| { |
| static_assert(__complex_like<_Up>); |
| for (size_t __i = 0; __i < std::min(__n, size_t(_S_size)); ++__i) |
| { |
| __mem[__i].real(__v._M_real[__i]); |
| __mem[__i].imag(__v._M_imag[__i]); |
| } |
| } |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| static inline void |
| _S_masked_store(const basic_vec& __v, _Up* __mem, const mask_type& __k) |
| { |
| // TODO: optimize |
| static_assert(__complex_like<_Up>); |
| for (int __i = 0; __i < _S_size; ++__i) |
| { |
| if (__k[__i]) |
| __mem[__i] = __v[__i]; |
| } |
| } |
| |
| basic_vec() = default; |
| |
| // TODO: conversion extensions |
| |
| // [simd.ctor] broadcast constructor ------------------------------------ |
| template <__broadcast_constructible<value_type> _Up> |
| requires __complex_like<_Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_Up&& __x) noexcept |
| : _M_real(__x.real()), _M_imag(__x.imag()) |
| {} |
| |
| template <__broadcast_constructible<value_type> _Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_Up&& __x) noexcept |
| : _M_real(__x), _M_imag() |
| {} |
| |
| // [simd.ctor] conversion constructor ----------------------------------- |
| template <__complex_like _Up, typename _UAbi> |
| requires (_S_size == _UAbi::_S_size) |
| && __explicitly_convertible_to<_Up, value_type> |
| && _UAbi::_S_is_cx_ileav |
| [[__gnu__::__always_inline__]] |
| constexpr |
| explicit(!convertible_to<_Up, value_type>) |
| basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept |
| : _M_real(__x.real()), _M_imag(__x.imag()) |
| {} |
| |
| template <__complex_like _Up, typename _UAbi> |
| requires (_S_size == _UAbi::_S_size) |
| && __explicitly_convertible_to<_Up, value_type> |
| && (!_UAbi::_S_is_cx_ileav) |
| [[__gnu__::__always_inline__]] |
| constexpr |
| explicit(!convertible_to<_Up, value_type>) |
| basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept |
| : _M_real(__x._M_real), _M_imag(__x._M_imag) // using real() instead of _M_real is possible |
| // but potentially leads to memcpy because of oversized _M_real (likewise for imag) |
| {} |
| |
| template <typename _Up, typename _UAbi> // _Up is not complex! |
| requires (!__complex_like<_Up>) |
| && (_S_size == _UAbi::_S_size) |
| && __explicitly_convertible_to<_Up, value_type> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| explicit(!convertible_to<_Up, value_type>) |
| basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept |
| : _M_real(__x), _M_imag() |
| {} |
| |
| using _VecBase<_Tp, _Ap>::_VecBase; |
| |
| // [simd.ctor] generator constructor ------------------------------------ |
| template <__simd_generator_invokable<value_type, _S_size> _Fp> |
| [[__gnu__::__always_inline__]] |
| constexpr explicit |
| basic_vec(_Fp&& __gen) |
| : _M_real(), |
| _M_imag([&] { |
| _T0 __re[sizeof(_RealSimd) / sizeof(_T0)] = {}; |
| _T0 __im[sizeof(_RealSimd) / sizeof(_T0)] = {}; |
| template for (constexpr int __i : _IotaArray<_S_size>) |
| { |
| const value_type __c = static_cast<value_type>(__gen(__simd_size_c<__i>)); |
| __re[__i] = __c.real(); |
| __im[__i] = __c.imag(); |
| } |
| _M_real = __builtin_bit_cast(_RealSimd, __re); |
| return __builtin_bit_cast(_RealSimd, __im); |
| }()) |
| {} |
| |
| // [simd.ctor] load constructor ----------------------------------------- |
| template <__complex_like _Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_LoadCtorTag, const _Up* __ptr) |
| : _M_real([&](int __i) -> _T0 { return __ptr[__i].real(); }), |
| _M_imag([&](int __i) -> _T0 { return __ptr[__i].imag(); }) |
| {} |
| |
| template <typename _Up> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_LoadCtorTag, const _Up* __ptr) |
| : _M_real(_LoadCtorTag(), __ptr), _M_imag() |
| {} |
| |
| template <ranges::contiguous_range _Rg, typename... _Flags> |
| requires ranges::__static_sized_range<_Rg> && (ranges::__static_size<_Rg>() == _S_size) |
| && __vectorizable<ranges::range_value_t<_Rg>> |
| && __explicitly_convertible_to<ranges::range_value_t<_Rg>, value_type> |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(_Rg&& __range, flags<_Flags...> __flags = {}) |
| : basic_vec(_LoadCtorTag(), __flags.template _S_adjust_pointer<basic_vec>( |
| ranges::data(__range))) |
| { |
| static_assert(__loadstore_convertible_to<ranges::range_value_t<_Rg>, value_type, |
| _Flags...>); |
| } |
| |
| // [simd.ctor] complex init --------------------------------------------- |
| // This uses _RealSimd as proposed in LWG4230 |
| [[__gnu__::__always_inline__]] |
| constexpr |
| basic_vec(const _RealSimd& __re, const _RealSimd& __im = {}) noexcept |
| : _M_real(__re), _M_imag(__im) |
| {} |
| |
| // [simd.subscr] -------------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr value_type |
| operator[](__simd_size_type __i) const |
| { return value_type(_M_real[__i], _M_imag[__i]); } |
| |
| // [simd.unary] unary operators ----------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec& |
| operator++() noexcept requires requires(value_type __a) { ++__a; } |
| { |
| ++_M_real; |
| return *this; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator++(int) noexcept requires requires(value_type __a) { __a++; } |
| { |
| basic_vec __r = *this; |
| ++_M_real; |
| return __r; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec& |
| operator--() noexcept requires requires(value_type __a) { --__a; } |
| { |
| --_M_real; |
| return *this; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator--(int) noexcept requires requires(value_type __a) { __a--; } |
| { |
| basic_vec __r = *this; |
| --_M_real; |
| return __r; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr mask_type |
| operator!() const noexcept requires requires(value_type __a) { !__a; } |
| { return !_M_real && !_M_imag; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator+() const noexcept requires requires(value_type __a) { +__a; } |
| { return *this; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| operator-() const noexcept requires requires(value_type __a) { -__a; } |
| { return basic_vec(-_M_real, -_M_imag); } |
| |
| // [simd.cassign] compound assignment ----------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator+=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a + __a; } |
| { |
| __x._M_real += __y._M_real; |
| __x._M_imag += __y._M_imag; |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator-=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a - __a; } |
| { |
| __x._M_real -= __y._M_real; |
| __x._M_imag -= __y._M_imag; |
| return __x; |
| } |
| |
| |
| template <_TargetTraits _Traits = {}> |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator*=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a * __a; } |
| { |
| __cxctgus::__mul<value_type, _Traits>(__x._M_real, __x._M_imag, __y._M_real, __y._M_imag); |
| return __x; |
| } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec& |
| operator/=(basic_vec& __x, const basic_vec& __y) noexcept |
| requires requires(value_type __a) { __a / __a; } |
| { |
| const _RealSimd __r = __x._M_real * __y._M_real + __x._M_imag * __y._M_imag; |
| const _RealSimd __n = __y._M_norm(); |
| __x._M_imag = (__x._M_imag * __y._M_real - __x._M_real * __y._M_imag) / __n; |
| __x._M_real = __r / __n; |
| return __x; |
| } |
| |
| // [simd.comparison] compare operators ---------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr mask_type |
| operator==(const basic_vec& __x, const basic_vec& __y) noexcept |
| { return mask_type(__x._M_real == __y._M_real && __x._M_imag == __y._M_imag); } |
| |
| [[__gnu__::__always_inline__]] |
| friend constexpr mask_type |
| operator!=(const basic_vec& __x, const basic_vec& __y) noexcept |
| { return mask_type(__x._M_real != __y._M_real || __x._M_imag != __y._M_imag); } |
| |
| // [simd.complex.access] complex-value accessors ------------------------ |
| // LWG4230: returns _RealSimd instead of auto |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| real() const noexcept |
| { return _M_real; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| imag() const noexcept |
| { return _M_imag; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| real(const _RealSimd& __x) noexcept |
| { _M_real = __x; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr void |
| imag(const _RealSimd& __x) noexcept |
| { _M_imag = __x; } |
| |
| // [simd.cond] --------------------------------------------------------- |
| [[__gnu__::__always_inline__]] |
| friend constexpr basic_vec |
| __select_impl(const mask_type& __k, const basic_vec& __t, const basic_vec& __f) noexcept |
| { |
| typename basic_vec::_RealSimd::mask_type __kk(__k); |
| return basic_vec(__select_impl(__kk, __t._M_real, __f._M_real), |
| __select_impl(__kk, __t._M_imag, __f._M_imag)); |
| } |
| |
| // [simd.complex.math] internals --------------------------------------- |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| _M_abs() const; // TODO: depends on [simd.math] |
| |
| // associated functions |
| [[__gnu__::__always_inline__]] |
| constexpr _RealSimd |
| _M_norm() const |
| { return _M_real * _M_real + _M_imag * _M_imag; } |
| |
| [[__gnu__::__always_inline__]] |
| constexpr basic_vec |
| _M_conj() const |
| { return basic_vec(_M_real, -_M_imag); } |
| }; |
| |
| // [P3319R5] (extension) ---------------------------------------------------- |
| template <__complex_like _Tp, typename _Ap> |
| inline constexpr basic_vec<_Tp, _Ap> |
| __iota<basic_vec<_Tp, _Ap>> = basic_vec<_Tp, _Ap>([](typename _Tp::value_type __i) |
| -> typename _Tp::value_type { |
| static_assert(_Ap::_S_size - 1 <= numeric_limits<typename _Tp::value_type>::max(), |
| "iota object would overflow"); |
| return __i; |
| }); |
| } // namespace simd |
| _GLIBCXX_END_NAMESPACE_VERSION |
| } // namespace std |
| |
| #pragma GCC diagnostic pop |
| #endif // C++26 |
| #endif // _GLIBCXX_SIMD_COMPLEX_H |