25#ifndef _GLIBCXX_SIMD_VEC_H
26#define _GLIBCXX_SIMD_VEC_H 1
29#pragma GCC system_header
32#if __cplusplus >= 202400L
35#include "simd_flags.h"
42#pragma GCC diagnostic push
43#pragma GCC diagnostic ignored "-Wpsabi"
45namespace std _GLIBCXX_VISIBILITY(default)
47_GLIBCXX_BEGIN_NAMESPACE_VERSION
51 template <
typename _Tp,
typename _Ap>
55 using value_type = _Tp;
59 using mask_type = basic_mask<0, void>;
61#define _GLIBCXX_DELETE_SIMD "This specialization is disabled because of an invalid combination " \
62 "of template arguments to basic_vec."
64 basic_vec() =
delete(_GLIBCXX_DELETE_SIMD);
66 ~basic_vec() =
delete(_GLIBCXX_DELETE_SIMD);
68 basic_vec(
const basic_vec&) =
delete(_GLIBCXX_DELETE_SIMD);
70 basic_vec& operator=(
const basic_vec&) =
delete(_GLIBCXX_DELETE_SIMD);
72#undef _GLIBCXX_DELETE_SIMD
75 template <
typename _Tp,
typename _Ap>
78 using _Vp = basic_vec<_Tp, _Ap>;
81 using value_type = _Tp;
85 using mask_type = basic_mask<
sizeof(_Tp), abi_type>;
87 using iterator = __iterator<_Vp>;
89 using const_iterator = __iterator<const _Vp>;
93 {
return {
static_cast<_Vp&
>(*this), 0}; }
95 constexpr const_iterator
96 begin() const noexcept
99 constexpr const_iterator
100 cbegin() const noexcept
101 {
return {
static_cast<const _Vp&
>(*this), 0}; }
103 constexpr default_sentinel_t
107 constexpr default_sentinel_t
108 cend() const noexcept
111 static constexpr auto size = __simd_size_c<_Ap::_S_size>;
113 _VecBase() =
default;
116 template <
typename _Up,
typename _UAbi>
117 requires (_Ap::_S_size != _UAbi::_S_size)
118 _VecBase(
const basic_vec<_Up, _UAbi>&) =
delete(
"size mismatch");
120 template <
typename _Up,
typename _UAbi>
121 requires (_Ap::_S_size == _UAbi::_S_size) && (!__explicitly_convertible_to<_Up, _Tp>)
123 _VecBase(
const basic_vec<_Up, _UAbi>&)
124 =
delete(
"the value types are not convertible");
126 [[__gnu__::__always_inline__]]
128 operator+(
const _Vp& __x,
const _Vp& __y)
noexcept
135 [[__gnu__::__always_inline__]]
137 operator-(
const _Vp& __x,
const _Vp& __y)
noexcept
144 [[__gnu__::__always_inline__]]
146 operator*(
const _Vp& __x,
const _Vp& __y)
noexcept
153 [[__gnu__::__always_inline__]]
155 operator/(
const _Vp& __x,
const _Vp& __y)
noexcept
162 [[__gnu__::__always_inline__]]
164 operator%(
const _Vp& __x,
const _Vp& __y)
noexcept
165 requires requires (_Tp __a) { __a % __a; }
172 [[__gnu__::__always_inline__]]
174 operator&(
const _Vp& __x,
const _Vp& __y)
noexcept
175 requires requires (_Tp __a) { __a & __a; }
182 [[__gnu__::__always_inline__]]
184 operator|(
const _Vp& __x,
const _Vp& __y)
noexcept
185 requires requires (_Tp __a) { __a | __a; }
192 [[__gnu__::__always_inline__]]
194 operator^(
const _Vp& __x,
const _Vp& __y)
noexcept
195 requires requires (_Tp __a) { __a ^ __a; }
202 [[__gnu__::__always_inline__]]
204 operator<<(
const _Vp& __x,
const _Vp& __y) _GLIBCXX_SIMD_NOEXCEPT
205 requires requires (_Tp __a) { __a << __a; }
212 [[__gnu__::__always_inline__]]
214 operator<<(
const _Vp& __x, __simd_size_type __y) _GLIBCXX_SIMD_NOEXCEPT
215 requires requires (_Tp __a, __simd_size_type __b) { __a << __b; }
222 [[__gnu__::__always_inline__]]
224 operator>>(
const _Vp& __x,
const _Vp& __y) _GLIBCXX_SIMD_NOEXCEPT
225 requires requires (_Tp __a) { __a >> __a; }
232 [[__gnu__::__always_inline__]]
234 operator>>(
const _Vp& __x, __simd_size_type __y) _GLIBCXX_SIMD_NOEXCEPT
235 requires requires (_Tp __a, __simd_size_type __b) { __a >> __b; }
246 template <
integral _Tp>
247 inline constexpr _Tp __max_shift
248 = (
sizeof(_Tp) <
sizeof(int) ?
sizeof(int) :
sizeof(_Tp)) * __CHAR_BIT__;
250 template <__vectorizable _Tp, __abi_tag _Ap>
251 requires (_Ap::_S_nreg == 1)
252 && (!__complex_like<_Tp>)
253 class basic_vec<_Tp, _Ap>
254 :
public _VecBase<_Tp, _Ap>
256 template <
typename,
typename>
257 friend class basic_vec;
259 template <
size_t,
typename>
260 friend class basic_mask;
262 static constexpr int _S_size = _Ap::_S_size;
264 static constexpr int _S_full_size = __bit_ceil(
unsigned(_S_size));
266 static constexpr bool _S_is_scalar = _S_size == 1;
268 static constexpr bool _S_use_bitmask = _Ap::_S_is_bitmask && !_S_is_scalar;
270 using _DataType =
typename _Ap::template _DataType<_Tp>;
280 static constexpr bool _S_is_partial =
sizeof(_M_data) >
sizeof(_Tp) * _S_size;
282 using __canon_value_type = __canonical_vec_type_t<_Tp>;
285 using value_type = _Tp;
287 using mask_type = _VecBase<_Tp, _Ap>::mask_type;
290 [[__gnu__::__always_inline__]]
291 static constexpr basic_vec
292 _S_init(_DataType __x)
299 [[__gnu__::__always_inline__]]
304 [[__gnu__::__always_inline__]]
305 constexpr const _DataType&
306 _M_get() const noexcept
309 [[__gnu__::__always_inline__]]
310 friend constexpr bool
311 __is_const_known(
const basic_vec& __x)
312 {
return __builtin_constant_p(__x._M_data); }
314 [[__gnu__::__always_inline__]]
316 _M_concat_data([[maybe_unused]]
bool __do_sanitize =
false)
const
318 if constexpr (_S_is_scalar)
319 return __vec_builtin_type<__canon_value_type, 1>{_M_data};
324 template <
int _Size = _S_size,
int _Offset = 0,
typename _A0,
typename _Fp>
325 [[__gnu__::__always_inline__]]
326 static constexpr basic_vec
327 _S_static_permute(
const basic_vec<value_type, _A0>& __x, _Fp&& __idxmap)
329 using _Xp = basic_vec<value_type, _A0>;
331 if constexpr (_S_is_scalar)
333 constexpr __simd_size_type __j = [&]
consteval {
334 if constexpr (__index_permutation_function_sized<_Fp>)
335 return __idxmap(_Offset, _Size);
337 return __idxmap(_Offset);
339 if constexpr (__j == simd::zero_element || __j == simd::uninit_element)
342 static_assert(__j >= 0 && __j < _Xp::_S_size);
343 __r._M_data = __x[__j];
347 auto __idxmap2 = [=](
auto __i)
consteval {
348 if constexpr (int(__i + _Offset) >= _Size)
349 return __simd_size_c<simd::uninit_element>;
350 else if constexpr (__index_permutation_function_sized<_Fp>)
351 return __simd_size_c<__idxmap(__i + _Offset, _Size)>;
353 return __simd_size_c<__idxmap(__i + _Offset)>;
355 constexpr auto __adj_idx = [](
auto __i) {
356 constexpr int __j = __i;
357 if constexpr (__j == simd::zero_element)
358 return __simd_size_c<__bit_ceil(
unsigned(_Xp::_S_size))>;
359 else if constexpr (__j == simd::uninit_element)
360 return __simd_size_c<-1>;
363 static_assert(__j >= 0 && __j < _Xp::_S_size);
364 return __simd_size_c<__j>;
367 constexpr auto [...__is0] = _IotaArray<_S_size>;
368 constexpr bool __needs_zero_element
369 = ((__idxmap2(__simd_size_c<__is0>).value == simd::zero_element) || ...);
370 constexpr auto [...__is_full] = _IotaArray<_S_full_size>;
371 if constexpr (_A0::_S_nreg == 2 && !__needs_zero_element)
373 __r._M_data = __builtin_shufflevector(
374 __x._M_data0._M_data, __x._M_data1._M_data,
375 __adj_idx(__idxmap2(__simd_size_c<__is_full>)).value...);
379 __r._M_data = __builtin_shufflevector(
380 __x._M_concat_data(),
decltype(__x._M_concat_data())(),
381 __adj_idx(__idxmap2(__simd_size_c<__is_full>)).value...);
387 template <
typename _Vp>
388 [[__gnu__::__always_inline__]]
390 _M_chunk() const noexcept
392 constexpr int __n = _S_size / _Vp::_S_size;
393 constexpr int __rem = _S_size % _Vp::_S_size;
394 constexpr auto [...__is] = _IotaArray<__n>;
395 if constexpr (__rem == 0)
396 return array<_Vp, __n> {__extract_simd_at<_Vp>(cw<_Vp::_S_size * __is>, *
this)...};
399 using _Rest = resize_t<__rem, _Vp>;
400 return tuple(__extract_simd_at<_Vp>(cw<_Vp::_S_size * __is>, *
this)...,
401 __extract_simd_at<_Rest>(cw<_Vp::_S_size * __n>, *
this));
405 [[__gnu__::__always_inline__]]
406 static constexpr basic_vec
407 _S_concat(
const basic_vec& __x0)
noexcept
410 template <
typename... _As>
411 requires (
sizeof...(_As) > 1)
412 [[__gnu__::__always_inline__]]
413 static constexpr basic_vec
414 _S_concat(
const basic_vec<value_type, _As>&... __xs)
noexcept
416 static_assert(_S_size == (_As::_S_size + ...));
417 return __extract_simd_at<basic_vec>(cw<0>, __xs...);
444 template <
int _Shift, _ArchTraits _Traits = {}>
445 [[__gnu__::__always_inline__]]
447 _M_elements_shifted_to_front()
const
449 static_assert(_Shift < _S_size && -_Shift < _S_size);
450 if constexpr (_Shift == 0)
453 return _S_static_permute(*
this, [](
int __i)
consteval {
454 int __off = __i + _Shift;
455 return __off >= _S_size || __off < 0 ? zero_element : __off;
465 template <
typename _Vp, __canon_value_type __
id>
466 [[__gnu__::__always_inline__]]
468 _M_pad_to_T_with_value() const noexcept
470 static_assert(!_Vp::_S_is_partial);
471 static_assert(_Ap::_S_nreg == 1);
472 if constexpr (
sizeof(_Vp) == 32)
474 static_assert(
sizeof(_M_data) == 32);
475 constexpr auto __k = _Vp::mask_type::_S_partial_mask_of_n(_S_size);
476 return __select_impl(__k, _Vp::_S_init(_M_data), __id);
480 static_assert(
sizeof(_Vp) <= 16);
481 static_assert(
sizeof(_M_data) <=
sizeof(_Vp));
482 _Vp __v1 = __vec_zero_pad_to<sizeof(_Vp)>(_M_data);
483 if constexpr (__id == 0 && _S_is_partial)
487 __v1 = __v1.template _M_elements_shifted_to_front<-(_Vp::_S_size - _S_size)>();
488 else if constexpr (_Vp::_S_size - _S_size == 1)
490 __vec_set(__v1._M_data, _Vp::_S_size - 1, __id);
491 else if constexpr (__has_single_bit(
unsigned(_Vp::_S_size - _S_size)))
493 constexpr int __n = _Vp::_S_size - _S_size;
494 using _Ip = __integer_from<__n *
sizeof(__canon_value_type)>;
495 constexpr auto [...__is] = _IotaArray<__n>;
496 constexpr __canon_value_type __idn[__n] = {((void)__is, __id)...};
497 auto __vn = __vec_bit_cast<_Ip>(__v1._M_data);
498 __vec_set(__vn, _Vp::_S_size / __n - 1, __builtin_bit_cast(_Ip, __idn));
499 __v1._M_data =
reinterpret_cast<typename _Vp::_DataType
>(__vn);
501 else if constexpr (__id != 0 && !_S_is_partial)
505 constexpr _Vp __idn([](
int __i) {
506 return __i >= _S_size ? __id : __canon_value_type();
508 __v1._M_data = __vec_or(__v1._M_data, __idn._M_data);
510 else if constexpr (__id != 0 || _S_is_partial)
512 constexpr auto __k = _Vp::mask_type::_S_partial_mask_of_n(_S_size);
513 __v1 = __select_impl(__k, __v1, __id);
519 [[__gnu__::__always_inline__]]
521 _M_reduce_to_half(
auto __binary_op)
const
523 static_assert(__has_single_bit(
unsigned(_S_size)));
524 auto [__a, __b] = chunk<_S_size / 2>(*this);
525 return __binary_op(__a, __b);
528 template <
typename _Rest,
typename _BinaryOp>
529 [[__gnu__::__always_inline__]]
531 _M_reduce_tail(
const _Rest& __rest, _BinaryOp __binary_op)
const
533 if constexpr (_S_is_scalar)
534 return __binary_op(*
this, __rest)._M_data;
535 else if constexpr (_Rest::_S_size == _S_size)
536 return __binary_op(*
this, __rest)._M_reduce(__binary_op);
537 else if constexpr (_Rest::_S_size > _S_size)
539 auto [__a, __b] = __rest.template _M_chunk<basic_vec>();
540 return __binary_op(*
this, __a)._M_reduce_tail(__b, __binary_op);
542 else if constexpr (_Rest::_S_size == 1)
543 return __binary_op(_Rest(_M_reduce(__binary_op)), __rest)[0];
544 else if constexpr (
sizeof(_M_data) <= 16
545 &&
requires { __default_identity_element<__canon_value_type, _BinaryOp>(); })
547 constexpr __canon_value_type __id
548 = __default_identity_element<__canon_value_type, _BinaryOp>();
549 return __binary_op(_M_data, __rest.template _M_pad_to_T_with_value<basic_vec, __id>())
550 ._M_reduce(__binary_op);
553 return _M_reduce_to_half(__binary_op)._M_reduce_tail(__rest, __binary_op);
562 template <
typename _BinaryOp, _ArchTraits _Traits = {}>
563 [[__gnu__::__always_inline__]]
565 _M_reduce(_BinaryOp __binary_op)
const
567 constexpr bool __have_id_elem
568 =
requires { __default_identity_element<__canon_value_type, _BinaryOp>(); };
569 if constexpr (_S_size == 1)
570 return operator[](0);
571 else if constexpr (_Traits.template _M_eval_as_f32<value_type>()
572 && (is_same_v<_BinaryOp, plus<>>
573 || is_same_v<_BinaryOp, multiplies<>>))
574 return value_type(rebind_t<float, basic_vec>(*this)._M_reduce(__binary_op));
576 else if constexpr (is_integral_v<value_type> &&
sizeof(value_type) == 1
577 && is_same_v<
decltype(__binary_op), multiplies<>>)
592 if constexpr (!_S_is_partial)
594 using _V16 = resize_t<_S_size / 2, rebind_t<unsigned short, basic_vec>>;
595 auto __a = __builtin_bit_cast(_V16, *
this);
596 return __binary_op(__a, __a >> 8)._M_reduce(__binary_op);
600 using _V16 = rebind_t<unsigned short, basic_vec>;
601 return _V16(*this)._M_reduce(__binary_op);
605 else if constexpr (__has_single_bit(
unsigned(_S_size)))
607 if constexpr (
sizeof(_M_data) > 16)
608 return _M_reduce_to_half(__binary_op)._M_reduce(__binary_op);
609 else if constexpr (_S_size == 2)
610 return _M_reduce_to_half(__binary_op)[0];
613 static_assert(_S_size <= 16);
616 if constexpr (
sizeof(_M_data) <= 16 && is_integral_v<value_type>)
618 if constexpr (_S_size > 8)
619 __x = __binary_op(__x, __x.template _M_elements_shifted_to_front<8>());
620 if constexpr (_S_size > 4)
621 __x = __binary_op(__x, __x.template _M_elements_shifted_to_front<4>());
622 if constexpr (_S_size > 2)
623 __x = __binary_op(__x, __x.template _M_elements_shifted_to_front<2>());
626 return __binary_op(__x, __x.template _M_elements_shifted_to_front<1>())[0];
629 if constexpr (_S_size > 8)
630 __x = __binary_op(__x, _S_static_permute(__x, _SwapNeighbors<8>()));
631 if constexpr (_S_size > 4)
632 __x = __binary_op(__x, _S_static_permute(__x, _SwapNeighbors<4>()));
635 if constexpr (is_integral_v<value_type> &&
sizeof(value_type) <= 1)
636 return value_type(resize_t<4, rebind_t<int, basic_vec>>(chunk<4>(__x)[0])
637 ._M_reduce(__binary_op));
639 if constexpr (_S_size > 2)
640 __x = __binary_op(__x, _S_static_permute(__x, _SwapNeighbors<2>()));
641 if constexpr (is_integral_v<value_type> &&
sizeof(value_type) == 2)
642 return __binary_op(__x, _S_static_permute(__x, _SwapNeighbors<1>()))[0];
644 return __binary_op(vec<value_type, 1>(__x[0]), vec<value_type, 1>(__x[1]))[0];
647 else if constexpr (
sizeof(_M_data) == 32)
649 const auto [__lo, __hi] = chunk<__bit_floor(unsigned(_S_size))>(*
this);
650 return __lo._M_reduce_tail(__hi, __binary_op);
652 else if constexpr (
sizeof(_M_data) == 64)
656 auto __chunked = chunk<__bit_floor(
unsigned(_S_size)) / 2>(*this);
657 using _Cp =
decltype(__chunked);
658 if constexpr (tuple_size_v<_Cp> == 4)
660 const auto& [__a, __b, __c, __rest] = __chunked;
661 constexpr bool __amd_cpu = _Traits._M_have_sse4a();
662 if constexpr (__have_id_elem && __rest._S_size > 1 && __amd_cpu)
668 const auto& [__a, __rest] = chunk<__bit_floor(unsigned(_S_size))>(*
this);
669 using _Vp = remove_cvref_t<
decltype(__a)>;
670 constexpr __canon_value_type __id
671 = __default_identity_element<__canon_value_type, _BinaryOp>();
672 const _Vp __b = __rest.template _M_pad_to_T_with_value<_Vp, __id>();
673 return __binary_op(__a, __b)._M_reduce(__binary_op);
675 else if constexpr (__have_id_elem && __rest._S_size > 1)
681 using _Vp = remove_cvref_t<
decltype(__a)>;
682 constexpr __canon_value_type __id
683 = __default_identity_element<__canon_value_type, _BinaryOp>();
684 const _Vp __d = __rest.template _M_pad_to_T_with_value<_Vp, __id>();
685 return __binary_op(__binary_op(__a, __b), __binary_op(__c, __d))
686 ._M_reduce(__binary_op);
689 return __binary_op(__binary_op(__a, __b), __c)
690 ._M_reduce_tail(__rest, __binary_op);
692 else if constexpr (tuple_size_v<_Cp> == 3)
694 const auto& [__a, __b, __rest] = __chunked;
695 return __binary_op(__a, __b)._M_reduce_tail(__rest, __binary_op);
698 static_assert(
false);
700 else if constexpr (__have_id_elem)
702 constexpr __canon_value_type __id
703 = __default_identity_element<__canon_value_type, _BinaryOp>();
704 using _Vp = resize_t<__bit_ceil(
unsigned(_S_size)), basic_vec>;
705 return _M_pad_to_T_with_value<_Vp, __id>()._M_reduce(__binary_op);
709 const auto& [__a, __rest] = chunk<__bit_floor(unsigned(_S_size))>(*
this);
710 return __a._M_reduce_tail(__rest, __binary_op);
719 template <_OptTraits _Traits = {}>
720 [[__gnu__::__always_inline__]]
722 _M_isnan() const requires is_floating_point_v<value_type>
724 if constexpr (_Traits._M_finite_math_only())
725 return mask_type(
false);
726 else if constexpr (_S_is_scalar)
727 return mask_type(std::isnan(_M_data));
728 else if constexpr (_S_use_bitmask)
729 return _M_isunordered(*
this);
730 else if constexpr (!_Traits._M_support_snan())
731 return !(*
this == *
this);
732 else if (__is_const_known(_M_data))
733 return mask_type([&](
int __i) {
return std::isnan(_M_data[__i]); });
737 using _Ip = __integer_from<
sizeof(value_type)>;
739 < __builtin_bit_cast(rebind_t<_Ip, basic_vec>, _M_fabs());
743 template <_TargetTraits _Traits = {}>
744 [[__gnu__::__always_inline__]]
746 _M_isinf() const requires is_floating_point_v<value_type>
748 if constexpr (_Traits._M_finite_math_only())
749 return mask_type(
false);
750 else if constexpr (_S_is_scalar)
751 return mask_type(std::isinf(_M_data));
752 else if (__is_const_known(_M_data))
753 return mask_type([&](
int __i) {
return std::isinf(_M_data[__i]); });
755 else if constexpr (_S_use_bitmask)
756 return mask_type::_S_init(__x86_bitmask_isinf(_M_data));
757 else if constexpr (_Traits._M_have_avx512dq())
758 return __x86_bit_to_vecmask<typename mask_type::_DataType>(
759 __x86_bitmask_isinf(_M_data));
763 using _Ip = __integer_from<
sizeof(value_type)>;
764 return __vec_bit_cast<_Ip>(_M_fabs()._M_data)
769 [[__gnu__::__always_inline__]]
771 _M_abs() const requires signed_integral<value_type>
772 {
return _M_data < 0 ? -_M_data : _M_data; }
774 [[__gnu__::__always_inline__]]
776 _M_fabs() const requires floating_point<value_type>
778 if constexpr (_S_is_scalar)
781 return __vec_and(__vec_not(_S_signmask<_DataType>), _M_data);
784 template <_TargetTraits _Traits = {}>
785 [[__gnu__::__always_inline__]]
787 _M_isunordered(basic_vec __y)
const requires is_floating_point_v<value_type>
789 if constexpr (_Traits._M_finite_math_only())
790 return mask_type(
false);
791 else if constexpr (_S_is_scalar)
792 return mask_type(std::isunordered(_M_data, __y._M_data));
794 else if constexpr (_S_use_bitmask)
795 return _M_bitmask_cmp<_X86Cmp::_Unord>(__y._M_data);
798 return mask_type([&](
int __i) {
799 return std::isunordered(_M_data[__i], __y._M_data[__i]);
810 template <
typename _Up, _ArchTraits _Traits = {}>
811 static inline basic_vec
812 _S_partial_load(
const _Up* __mem,
size_t __n)
814 if constexpr (_S_is_scalar)
815 return __n == 0 ? basic_vec() : basic_vec(
static_cast<value_type
>(*__mem));
816 else if (__is_const_known_equal_to(__n >=
size_t(_S_size),
true))
817 return basic_vec(_LoadCtorTag(), __mem);
818 else if constexpr (!__converts_trivially<_Up, value_type>)
819 return static_cast<basic_vec
>(rebind_t<_Up, basic_vec>::_S_partial_load(__mem, __n));
823 if constexpr (_Traits._M_have_avx512f()
824 || (_Traits._M_have_avx() &&
sizeof(_Up) >= 4))
826 const auto __k = __n < _S_size ? mask_type::_S_partial_mask_of_n(
int(__n))
828 return _S_masked_load(__mem, mask_type::_S_partial_mask_of_n(
int(__n)));
831 if (__n >=
size_t(_S_size)) [[unlikely]]
832 return basic_vec(_LoadCtorTag(), __mem);
835 else if (__is_const_known_equal_to(
837 return __select_impl(mask_type::_S_partial_mask_of_n(
int(__n)),
838 basic_vec(_LoadCtorTag(), __mem), basic_vec());
840 else if constexpr (_S_size > 4)
842 alignas(_DataType)
byte __dst[
sizeof(_DataType)] = {};
843 const byte* __src =
reinterpret_cast<const byte*
>(__mem);
844 __memcpy_chunks<sizeof(_Up), sizeof(_DataType)>(__dst, __src, __n);
845 return __builtin_bit_cast(_DataType, __dst);
847 else if (__n == 0) [[unlikely]]
849 else if constexpr (_S_size == 2)
850 return _DataType {
static_cast<value_type
>(__mem[0]), 0};
853 constexpr auto [...__is] = _IotaArray<_S_size - 2>;
855 static_cast<value_type
>(__mem[0]),
856 static_cast<value_type
>(__is + 1 < __n ? __mem[__is + 1] : 0)...
874 template <
typename _Up, _ArchTraits _Traits = {}>
875 static inline basic_vec
876 _S_masked_load(
const _Up* __mem, mask_type __k)
878 if constexpr (_S_size == 1)
879 return __k[0] ?
static_cast<value_type
>(__mem[0]) : value_type();
881 else if constexpr (_Traits._M_have_avx512f())
882 return __x86_masked_load<_DataType>(__mem, __k._M_data);
883 else if constexpr (_Traits._M_have_avx() && (
sizeof(_Up) == 4 ||
sizeof(_Up) == 8))
885 if constexpr (__converts_trivially<_Up, value_type>)
886 return __x86_masked_load<_DataType>(__mem, __k._M_data);
889 using _UV = rebind_t<_Up, basic_vec>;
890 return basic_vec(_UV::_S_masked_load(__mem,
typename _UV::mask_type(__k)));
894 else if (__k._M_none_of()) [[unlikely]]
896 else if constexpr (_S_is_scalar)
897 return basic_vec(
static_cast<value_type
>(*__mem));
901 _Bitmask<_S_size < 32 ? 32 : _S_size> __bits = __k._M_to_uint();
902 [[assume(__bits != 0)]];
903 if constexpr (__converts_trivially<_Up, value_type>)
906 __bit_foreach(__bits, [&] [[__gnu__::__always_inline__]] (
int __i) {
907 __r[__i] = __mem[__i];
913 using _UV = rebind_t<_Up, basic_vec>;
914 alignas(_UV) _Up __tmp[
sizeof(_UV) /
sizeof(_Up)] = {};
915 __bit_foreach(__bits, [&] [[__gnu__::__always_inline__]] (
int __i) {
916 __tmp[__i] = __mem[__i];
918 return basic_vec(__builtin_bit_cast(_UV, __tmp));
923 template <
typename _Up>
924 [[__gnu__::__always_inline__]]
926 _M_store(_Up* __mem)
const
928 if constexpr (__converts_trivially<value_type, _Up>)
929 __builtin_memcpy(__mem, &_M_data,
sizeof(_Up) * _S_size);
931 rebind_t<_Up, basic_vec>(*this)._M_store(__mem);
943 template <
typename _Up, _ArchTraits _Traits = {}>
945 _S_partial_store(
const basic_vec __v, _Up* __mem,
size_t __n)
947 if (__is_const_known_equal_to(__n >= _S_size,
true))
950 else if constexpr (_Traits._M_have_avx512f() && !_S_is_scalar)
952 const auto __k = __n < _S_size ? mask_type::_S_partial_mask_of_n(
int(__n))
954 return _S_masked_store(__v, __mem, __k);
957 else if (__n >= _S_size) [[unlikely]]
959 else if (__n == 0) [[unlikely]]
961 else if constexpr (__converts_trivially<value_type, _Up>)
963 byte* __dst =
reinterpret_cast<byte*
>(__mem);
964 const byte* __src =
reinterpret_cast<const byte*
>(&__v._M_data);
965 __memcpy_chunks<sizeof(_Up), sizeof(_M_data)>(__dst, __src, __n);
969 using _UV = rebind_t<_Up, basic_vec>;
970 _UV::_S_partial_store(_UV(__v), __mem, __n);
987 template <
typename _Up, _ArchTraits _Traits = {}>
990 _S_masked_store(
const basic_vec __v, _Up* __mem,
const mask_type __k)
993 if constexpr (_Traits._M_have_avx512f())
995 __x86_masked_store(__v._M_data, __mem, __k._M_data);
998 else if constexpr (_Traits._M_have_avx() && (
sizeof(_Up) == 4 ||
sizeof(_Up) == 8))
1000 if constexpr (__converts_trivially<value_type, _Up>)
1001 __x86_masked_store(__v._M_data, __mem, __k._M_data);
1004 using _UV = rebind_t<_Up, basic_vec>;
1005 _UV::_S_masked_store(_UV(__v), __mem,
typename _UV::mask_type(__k));
1010 if (__k._M_none_of()) [[unlikely]]
1012 else if constexpr (_S_is_scalar)
1013 __mem[0] = __v._M_data;
1017 _Bitmask<_S_size < 32 ? 32 : _S_size> __bits = __k._M_to_uint();
1018 [[assume(__bits != 0)]];
1019 if constexpr (__converts_trivially<value_type, _Up>)
1021 __bit_foreach(__bits, [&] [[__gnu__::__always_inline__]] (
int __i) {
1022 __mem[__i] = __v[__i];
1027 const rebind_t<_Up, basic_vec> __cvted(__v);
1028 __bit_foreach(__bits, [&] [[__gnu__::__always_inline__]] (
int __i) {
1029 __mem[__i] = __cvted[__i];
1036 basic_vec() =
default;
1039 using _NativeVecType =
decltype([] {
1040 if constexpr (_S_is_scalar)
1041 return __vec_builtin_type<__canon_value_type, 1>();
1058 basic_vec(_NativeVecType __x)
1059 : _M_data([&] [[__gnu__::__always_inline__]] {
1060 if constexpr (_S_is_scalar)
1077 operator _NativeVecType()
const
1079 if constexpr (_S_is_scalar)
1080 return _NativeVecType{_M_data};
1089 template <__vec_builtin _IV>
1090 requires same_as<__x86_intel_intrin_value_type<value_type>, __vec_value_type<_IV>>
1091 && (
sizeof(_IV) ==
sizeof(_DataType) &&
sizeof(_IV) >= 16
1092 && !is_same_v<_IV, _DataType>)
1095 : _M_data(reinterpret_cast<_DataType>(__x))
1101 template <__vec_builtin _IV>
1102 requires same_as<__x86_intel_intrin_value_type<value_type>, __vec_value_type<_IV>>
1103 && (
sizeof(_IV) ==
sizeof(_DataType) &&
sizeof(_IV) >= 16
1104 && !is_same_v<_IV, _DataType>)
1106 operator _IV()
const
1107 {
return reinterpret_cast<_IV
>(_M_data); }
1122 template <__broadcast_constructible<value_type> _Up>
1123 [[__gnu__::__always_inline__]]
1125 basic_vec(_Up&& __x) noexcept
1126 : _M_data(_DataType() == _DataType() ?
static_cast<value_type
>(__x) : value_type())
1130 template <
typename _Up,
typename _UAbi, _TargetTraits _Traits = {}>
1131 requires (_S_size == _UAbi::_S_size)
1132 && __explicitly_convertible_to<_Up, value_type>
1133 [[__gnu__::__always_inline__]]
1135 explicit(!__value_preserving_convertible_to<_Up, value_type>
1136 || __higher_rank_than<_Up, value_type>)
1137 basic_vec(
const basic_vec<_Up, _UAbi>& __x) noexcept
1138 : _M_data([&] [[__gnu__::__always_inline__]] {
1139 if constexpr (_S_is_scalar)
1140 return static_cast<value_type
>(__x[0]);
1141 else if constexpr (_UAbi::_S_nreg >= 2)
1145 return _S_concat(resize_t<__x._N0, basic_vec>(__x._M_data0),
1146 resize_t<__x._N1, basic_vec>(__x._M_data1))._M_data;
1148 return __vec_cast<_DataType>(__x._M_concat_data());
1152 using _VecBase<_Tp, _Ap>::_VecBase;
1155 template <__simd_generator_invokable<value_type, _S_size> _Fp>
1156 [[__gnu__::__always_inline__]]
1158 basic_vec(_Fp&& __gen)
1159 : _M_data([&] [[__gnu__::__always_inline__]] {
1160 constexpr auto [...__is] = _IotaArray<_S_size>;
1161 return _DataType{
static_cast<value_type
>(__gen(__simd_size_c<__is>))...};
1166 template <
typename _Up>
1167 [[__gnu__::__always_inline__]]
1169 basic_vec(_LoadCtorTag,
const _Up* __ptr)
1172 if constexpr (_S_is_scalar)
1173 _M_data =
static_cast<value_type
>(__ptr[0]);
1176 constexpr auto [...__is] = _IotaArray<_S_size>;
1177 _M_data = _DataType{
static_cast<value_type
>(__ptr[__is])...};
1181 if constexpr (__converts_trivially<_Up, value_type>)
1183 __builtin_memcpy(&_M_data, __ptr,
sizeof(value_type) * _S_size);
1186 __vec_builtin_type<_Up, _S_full_size> __tmp = {};
1187 __builtin_memcpy(&__tmp, __ptr,
sizeof(_Up) * _S_size);
1188 _M_data = __vec_cast<_DataType>(__tmp);
1193 template <ranges::contiguous_range _Rg,
typename... _Flags>
1194 requires ranges::__static_sized_range<_Rg>
1195 && __vectorizable<ranges::range_value_t<_Rg>>
1196 && __explicitly_convertible_to<ranges::range_value_t<_Rg>, value_type>
1197 [[__gnu__::__always_inline__]]
1199 basic_vec(_Rg&& __range, flags<_Flags...> __flags = {})
1200 requires (ranges::size(__range) == _S_size)
1201 : basic_vec(_LoadCtorTag(), __flags.template _S_adjust_pointer<basic_vec>(
1202 ranges::
data(__range)))
1204 static_assert(__loadstore_convertible_to<ranges::range_value_t<_Rg>, value_type,
1214 [[__gnu__::__always_inline__]]
1215 constexpr value_type
1216 operator[](__simd_size_type __i)
const
1218 __glibcxx_simd_precondition(__i >= 0 && __i < _S_size,
"subscript is out of bounds");
1219 if constexpr (_S_is_scalar)
1222 return _M_data[__i];
1228 [[__gnu__::__always_inline__]]
1229 constexpr basic_vec&
1230 operator++() noexcept requires requires(value_type __a) { ++__a; }
1231 {
return *
this += value_type(1); }
1233 [[__gnu__::__always_inline__]]
1235 operator++(
int)
noexcept requires requires(value_type __a) { __a++; }
1237 basic_vec __r = *
this;
1238 *
this += value_type(1);
1242 [[__gnu__::__always_inline__]]
1243 constexpr basic_vec&
1244 operator--() noexcept requires requires(value_type __a) { --__a; }
1245 {
return *
this -= value_type(1); }
1247 [[__gnu__::__always_inline__]]
1249 operator--(
int)
noexcept requires requires(value_type __a) { __a--; }
1251 basic_vec __r = *
this;
1252 *
this -= value_type(1);
1256 [[__gnu__::__always_inline__]]
1258 operator!() const noexcept requires requires(value_type __a) { !__a; }
1259 {
return *
this == value_type(); }
1266 [[__gnu__::__always_inline__]]
1268 operator+() const noexcept requires requires(value_type __a) { +__a; }
1276 [[__gnu__::__always_inline__]]
1278 operator-() const noexcept requires requires(value_type __a) { -__a; }
1279 {
return _S_init(-_M_data); }
1286 [[__gnu__::__always_inline__]]
1288 operator~() const noexcept requires requires(value_type __a) { ~__a; }
1289 {
return _S_init(~_M_data); }
1297 [[__gnu__::__always_inline__]]
1298 friend constexpr basic_vec&
1299 operator&=(basic_vec& __x,
const basic_vec& __y)
noexcept
1300 requires requires(value_type __a) { __a & __a; }
1302 __x._M_data &= __y._M_data;
1311 [[__gnu__::__always_inline__]]
1312 friend constexpr basic_vec&
1313 operator|=(basic_vec& __x,
const basic_vec& __y)
noexcept
1314 requires requires(value_type __a) { __a | __a; }
1316 __x._M_data |= __y._M_data;
1325 [[__gnu__::__always_inline__]]
1326 friend constexpr basic_vec&
1327 operator^=(basic_vec& __x,
const basic_vec& __y)
noexcept
1328 requires requires(value_type __a) { __a ^ __a; }
1330 __x._M_data ^= __y._M_data;
1343 [[__gnu__::__always_inline__]]
1344 friend constexpr basic_vec&
1345 operator+=(basic_vec& __x,
const basic_vec& __y)
noexcept
1346 requires requires(value_type __a) { __a + __a; }
1348 if constexpr (_S_is_partial && is_integral_v<value_type> && is_signed_v<value_type>)
1363 using _UV =
typename _Ap::template _DataType<make_unsigned_t<value_type>>;
1364 const _DataType __result
1365 =
reinterpret_cast<_DataType
>(
reinterpret_cast<_UV
>(__x._M_data)
1366 +
reinterpret_cast<_UV
>(__y._M_data));
1367 const auto __positive = __y > value_type();
1368 const auto __overflow = __positive != (__result > __x);
1369 if (__overflow._M_any_of())
1370 __builtin_unreachable();
1371 __x._M_data = __result;
1373 else if constexpr (_TargetTraits()._M_eval_as_f32<value_type>())
1374 __x = basic_vec(rebind_t<float, basic_vec>(__x) + __y);
1376 __x._M_data += __y._M_data;
1382 [[__gnu__::__always_inline__]]
1383 friend constexpr basic_vec&
1384 operator-=(basic_vec& __x,
const basic_vec& __y)
noexcept
1385 requires requires(value_type __a) { __a - __a; }
1387 if constexpr (_S_is_partial && is_integral_v<value_type> && is_signed_v<value_type>)
1389 using _UV =
typename _Ap::template _DataType<make_unsigned_t<value_type>>;
1390 const _DataType __result
1391 =
reinterpret_cast<_DataType
>(
reinterpret_cast<_UV
>(__x._M_data)
1392 -
reinterpret_cast<_UV
>(__y._M_data));
1393 const auto __positive = __y > value_type();
1394 const auto __overflow = __positive != (__result < __x);
1395 if (__overflow._M_any_of())
1396 __builtin_unreachable();
1397 __x._M_data = __result;
1399 else if constexpr (_TargetTraits()._M_eval_as_f32<value_type>())
1400 __x = basic_vec(rebind_t<float, basic_vec>(__x) - __y);
1402 __x._M_data -= __y._M_data;
1408 [[__gnu__::__always_inline__]]
1409 friend constexpr basic_vec&
1410 operator*=(basic_vec& __x,
const basic_vec& __y)
noexcept
1411 requires requires(value_type __a) { __a * __a; }
1413 if constexpr (_S_is_partial && is_integral_v<value_type> && is_signed_v<value_type>)
1415 for (
int __i = 0; __i < _S_size; ++__i)
1417 if (__builtin_mul_overflow_p(__x._M_data[__i], __y._M_data[__i], value_type()))
1418 __builtin_unreachable();
1420 using _UV =
typename _Ap::template _DataType<make_unsigned_t<value_type>>;
1421 __x._M_data =
reinterpret_cast<_DataType
>(
reinterpret_cast<_UV
>(__x._M_data)
1422 *
reinterpret_cast<_UV
>(__y._M_data));
1428 else if constexpr (_S_is_scalar && is_unsigned_v<value_type>
1429 && is_signed_v<
decltype(value_type() * value_type())>)
1430 __x._M_data =
unsigned(__x._M_data) * unsigned(__y._M_data);
1432 else if constexpr (_TargetTraits()._M_eval_as_f32<value_type>())
1433 __x = basic_vec(rebind_t<float, basic_vec>(__x) * __y);
1436 __x._M_data *= __y._M_data;
1440 template <_TargetTraits _Traits = {}>
1441 [[__gnu__::__always_inline__]]
1442 friend constexpr basic_vec&
1443 operator/=(basic_vec& __x,
const basic_vec& __y)
noexcept
1444 requires requires(value_type __a) { __a / __a; }
1446 const basic_vec __result([&](
int __i) -> value_type {
return __x[__i] / __y[__i]; });
1447 if (__is_const_known(__result))
1449 return __x = __result;
1457 if constexpr (is_integral_v<value_type> && _S_size > 2
1458 && __value_preserving_convertible_to<value_type, double>)
1462 if (!__is_const_known(__y))
1465 if constexpr (_Traits._M_have_avx512fp16()
1466 && __value_preserving_convertible_to<value_type, _Float16>)
1467 return __x = basic_vec(rebind_t<_Float16, basic_vec>(__x) / __y);
1468 else if constexpr (__value_preserving_convertible_to<value_type, float>)
1469 return __x = basic_vec(rebind_t<float, basic_vec>(__x) / __y);
1471 return __x = basic_vec(rebind_t<double, basic_vec>(__x) / __y);
1475 if constexpr (_Traits._M_eval_as_f32<value_type>())
1476 return __x = basic_vec(rebind_t<float, basic_vec>(__x) / __y);
1478 basic_vec __y1 = __y;
1479 if constexpr (_S_is_partial)
1481 if constexpr (is_integral_v<value_type>)
1485 for (
int __i = 0; __i < _S_size; ++__i)
1486 __x._M_data[__i] /= __y._M_data[__i];
1490 __y1 = __select_impl(mask_type::_S_init(mask_type::_S_implicit_mask),
1491 __y, basic_vec(value_type(1)));
1493 __x._M_data /= __y1._M_data;
1497 [[__gnu__::__always_inline__]]
1498 friend constexpr basic_vec&
1499 operator%=(basic_vec& __x,
const basic_vec& __y)
noexcept
1500 requires requires(value_type __a) { __a % __a; }
1502 static_assert(is_integral_v<value_type>);
1503 if constexpr (_S_is_partial)
1505 const basic_vec __y1 = __select_impl(mask_type::_S_init(mask_type::_S_implicit_mask),
1506 __y, basic_vec(value_type(1)));
1507 if (__is_const_known(__y1))
1508 __x._M_data %= __y1._M_data;
1513 for (
int __i = 0; __i < _S_size; ++__i)
1514 __x._M_data[__i] %= __y._M_data[__i];
1518 __x._M_data %= __y._M_data;
1522 [[__gnu__::__always_inline__]]
1523 friend constexpr basic_vec&
1524 operator<<=(basic_vec& __x,
const basic_vec& __y) _GLIBCXX_SIMD_NOEXCEPT
1525 requires requires(value_type __a) { __a << __a; }
1527 __glibcxx_simd_precondition(is_unsigned_v<value_type> || all_of(__y >= value_type()),
1528 "negative shift is undefined behavior");
1529 __glibcxx_simd_precondition(all_of(__y < __max_shift<value_type>),
1530 "too large shift invokes undefined behavior");
1531 __x._M_data <<= __y._M_data;
1535 [[__gnu__::__always_inline__]]
1536 friend constexpr basic_vec&
1537 operator>>=(basic_vec& __x,
const basic_vec& __y) _GLIBCXX_SIMD_NOEXCEPT
1538 requires requires(value_type __a) { __a >> __a; }
1540 __glibcxx_simd_precondition(is_unsigned_v<value_type> || all_of(__y >= value_type()),
1541 "negative shift is undefined behavior");
1542 __glibcxx_simd_precondition(all_of(__y < __max_shift<value_type>),
1543 "too large shift invokes undefined behavior");
1544 __x._M_data >>= __y._M_data;
1548 [[__gnu__::__always_inline__]]
1549 friend constexpr basic_vec&
1550 operator<<=(basic_vec& __x, __simd_size_type __y) _GLIBCXX_SIMD_NOEXCEPT
1551 requires requires(value_type __a, __simd_size_type __b) { __a << __b; }
1553 __glibcxx_simd_precondition(__y >= 0,
"negative shift is undefined behavior");
1554 __glibcxx_simd_precondition(__y <
int(__max_shift<value_type>),
1555 "too large shift invokes undefined behavior");
1556 __x._M_data <<= __y;
1560 [[__gnu__::__always_inline__]]
1561 friend constexpr basic_vec&
1562 operator>>=(basic_vec& __x, __simd_size_type __y) _GLIBCXX_SIMD_NOEXCEPT
1563 requires requires(value_type __a, __simd_size_type __b) { __a >> __b; }
1565 __glibcxx_simd_precondition(__y >= 0,
"negative shift is undefined behavior");
1566 __glibcxx_simd_precondition(__y <
int(__max_shift<value_type>),
1567 "too large shift invokes undefined behavior");
1568 __x._M_data >>= __y;
1574 template <_X86Cmp _Cmp>
1575 [[__gnu__::__always_inline__]]
1577 _M_bitmask_cmp(_DataType __y)
const
1579 static_assert(_S_use_bitmask);
1580 if (__is_const_known(_M_data, __y))
1582 constexpr auto [...__is] = _IotaArray<_S_size>;
1583 constexpr auto __cmp_op = [] [[__gnu__::__always_inline__]]
1584 (value_type __a, value_type __b) {
1585 if constexpr (_Cmp == _X86Cmp::_Eq)
1587 else if constexpr (_Cmp == _X86Cmp::_Lt)
1589 else if constexpr (_Cmp == _X86Cmp::_Le)
1591 else if constexpr (_Cmp == _X86Cmp::_Unord)
1592 return std::isunordered(__a, __b);
1593 else if constexpr (_Cmp == _X86Cmp::_Neq)
1595 else if constexpr (_Cmp == _X86Cmp::_Nlt)
1596 return !(__a < __b);
1597 else if constexpr (_Cmp == _X86Cmp::_Nle)
1598 return !(__a <= __b);
1600 static_assert(
false);
1602 const _Bitmask<_S_size> __bits
1603 = ((__cmp_op(__vec_get(_M_data, __is), __vec_get(__y, __is))
1604 ? (1ULL << __is) : 0) | ...);
1605 return mask_type::_S_init(__bits);
1608 return mask_type::_S_init(__x86_bitmask_cmp<_Cmp>(_M_data, __y));
1612 [[__gnu__::__always_inline__]]
1613 friend constexpr mask_type
1614 operator==(
const basic_vec& __x,
const basic_vec& __y)
noexcept
1617 if constexpr (_S_use_bitmask)
1618 return __x._M_bitmask_cmp<_X86Cmp::_Eq>(__y._M_data);
1621 return mask_type::_S_init(__x._M_data == __y._M_data);
1624 [[__gnu__::__always_inline__]]
1625 friend constexpr mask_type
1626 operator!=(
const basic_vec& __x,
const basic_vec& __y)
noexcept
1629 if constexpr (_S_use_bitmask)
1630 return __x._M_bitmask_cmp<_X86Cmp::_Neq>(__y._M_data);
1633 return mask_type::_S_init(__x._M_data != __y._M_data);
1636 [[__gnu__::__always_inline__]]
1637 friend constexpr mask_type
1638 operator<(
const basic_vec& __x,
const basic_vec& __y)
noexcept
1641 if constexpr (_S_use_bitmask)
1642 return __x._M_bitmask_cmp<_X86Cmp::_Lt>(__y._M_data);
1645 return mask_type::_S_init(__x._M_data < __y._M_data);
1648 [[__gnu__::__always_inline__]]
1649 friend constexpr mask_type
1650 operator<=(
const basic_vec& __x,
const basic_vec& __y)
noexcept
1653 if constexpr (_S_use_bitmask)
1654 return __x._M_bitmask_cmp<_X86Cmp::_Le>(__y._M_data);
1657 return mask_type::_S_init(__x._M_data <= __y._M_data);
1660 [[__gnu__::__always_inline__]]
1661 friend constexpr mask_type
1662 operator>(
const basic_vec& __x,
const basic_vec& __y)
noexcept
1663 {
return __y < __x; }
1665 [[__gnu__::__always_inline__]]
1666 friend constexpr mask_type
1667 operator>=(
const basic_vec& __x,
const basic_vec& __y)
noexcept
1668 {
return __y <= __x; }
1671 template <_TargetTraits _Traits = {}>
1672 [[__gnu__::__always_inline__]]
1673 friend constexpr basic_vec
1674 __select_impl(
const mask_type& __k,
const basic_vec& __t,
const basic_vec& __f)
noexcept
1676 if constexpr (_S_size == 1)
1677 return __k[0] ? __t : __f;
1678 else if constexpr (_S_use_bitmask)
1681 if (__is_const_known(__k, __t, __f))
1682 return basic_vec([&](
int __i) {
return __k[__i] ? __t[__i] : __f[__i]; });
1684 return __x86_bitmask_blend(__k._M_data, __t._M_data, __f._M_data);
1686 static_assert(
false,
"TODO");
1691 return __k._M_data ? __t._M_data : __f._M_data;
1695 constexpr bool __uses_simd_register =
sizeof(_M_data) >= 8;
1696 using _VO = _VecOps<_DataType>;
1697 if (_VO::_S_is_const_known_equal_to(__f._M_data, 0))
1699 if (is_integral_v<value_type> && __uses_simd_register
1700 && _VO::_S_is_const_known_equal_to(__t._M_data, 1))
1705 return basic_vec((-__k)._M_abs());
1707 return __vec_and(
reinterpret_cast<_DataType
>(__k._M_data), __t._M_data);
1709 else if (_VecOps<_DataType>::_S_is_const_known_equal_to(__t._M_data, 0))
1711 if (is_integral_v<value_type> && __uses_simd_register
1712 && _VO::_S_is_const_known_equal_to(__f._M_data, 1))
1713 return value_type(1) + basic_vec(-__k);
1715 return __vec_and(
reinterpret_cast<_DataType
>(__vec_not(__k._M_data)), __f._M_data);
1724 return __k._M_data < 0 ? __t._M_data : __f._M_data;
1726 return __k._M_data ? __t._M_data : __f._M_data;
1732 template <__vectorizable _Tp, __abi_tag _Ap>
1733 requires (_Ap::_S_nreg > 1)
1734 && (!__complex_like<_Tp>)
1735 class basic_vec<_Tp, _Ap>
1736 :
public _VecBase<_Tp, _Ap>
1738 template <
typename,
typename>
1739 friend class basic_vec;
1741 template <
size_t,
typename>
1742 friend class basic_mask;
1744 static constexpr int _S_size = _Ap::_S_size;
1746 static constexpr int _N0 = __bit_ceil(
unsigned(_S_size)) / 2;
1748 static constexpr int _N1 = _S_size - _N0;
1750 using _DataType0 = __similar_vec<_Tp, _N0, _Ap>;
1753 static_assert(_N0 *
sizeof(_Tp) ==
sizeof(_DataType0));
1755 using _DataType1 = __similar_vec<_Tp, _N1, _Ap>;
1757 static_assert(_DataType0::abi_type::_S_nreg + _DataType1::abi_type::_S_nreg == _Ap::_S_nreg);
1759 static constexpr bool _S_is_scalar = _DataType0::_S_is_scalar;
1761 _DataType0 _M_data0;
1763 _DataType1 _M_data1;
1765 static constexpr bool _S_use_bitmask = _DataType0::_S_use_bitmask;
1767 static constexpr bool _S_is_partial = _DataType1::_S_is_partial;
1770 using value_type = _Tp;
1772 using mask_type = _VecBase<_Tp, _Ap>::mask_type;
1774 [[__gnu__::__always_inline__]]
1775 static constexpr basic_vec
1776 _S_init(
const _DataType0& __x,
const _DataType1& __y)
1784 [[__gnu__::__always_inline__]]
1785 constexpr _DataType0&
1786 _M_get_low() noexcept
1787 {
return _M_data0; }
1789 [[__gnu__::__always_inline__]]
1790 constexpr const _DataType0&
1791 _M_get_low() const noexcept
1792 {
return _M_data0; }
1794 [[__gnu__::__always_inline__]]
1795 constexpr _DataType1&
1796 _M_get_high() noexcept
1797 {
return _M_data1; }
1799 [[__gnu__::__always_inline__]]
1800 constexpr const _DataType1&
1801 _M_get_high() const noexcept
1802 {
return _M_data1; }
1804 [[__gnu__::__always_inline__]]
1805 friend constexpr bool
1806 __is_const_known(
const basic_vec& __x)
1807 {
return __is_const_known(__x._M_data0) && __is_const_known(__x._M_data1); }
1809 [[__gnu__::__always_inline__]]
1811 _M_concat_data([[maybe_unused]]
bool __do_sanitize =
false)
const
1813 return __vec_concat(_M_data0._M_concat_data(
false),
1814 __vec_zero_pad_to<
sizeof(_M_data0)>(
1815 _M_data1._M_concat_data(__do_sanitize)));
1818 template <
int _Size = _S_size,
int _Offset = 0,
typename _A0,
typename _Fp>
1819 [[__gnu__::__always_inline__]]
1820 static constexpr basic_vec
1821 _S_static_permute(
const basic_vec<value_type, _A0>& __x, _Fp&& __idxmap)
1824 _DataType0::template _S_static_permute<_Size, _Offset>(__x, __idxmap),
1825 _DataType1::template _S_static_permute<_Size, _Offset + _N0>(__x, __idxmap));
1828 template <
typename _Vp>
1829 [[__gnu__::__always_inline__]]
1831 _M_chunk() const noexcept
1833 constexpr int __n = _S_size / _Vp::_S_size;
1834 constexpr int __rem = _S_size % _Vp::_S_size;
1835 constexpr auto [...__is] = _IotaArray<__n>;
1836 if constexpr (__rem == 0)
1837 return array<_Vp, __n>{__extract_simd_at<_Vp>(cw<_Vp::_S_size * __is>,
1838 _M_data0, _M_data1)...};
1841 using _Rest = resize_t<__rem, _Vp>;
1842 return tuple(__extract_simd_at<_Vp>(cw<_Vp::_S_size * __is>, _M_data0, _M_data1)...,
1843 __extract_simd_at<_Rest>(cw<_Vp::_S_size * __n>, _M_data0, _M_data1));
1847 [[__gnu__::__always_inline__]]
1848 static constexpr const basic_vec&
1849 _S_concat(
const basic_vec& __x0)
noexcept
1852 template <
typename... _As>
1853 requires (
sizeof...(_As) >= 2)
1854 [[__gnu__::__always_inline__]]
1855 static constexpr basic_vec
1856 _S_concat(
const basic_vec<value_type, _As>&... __xs)
noexcept
1858 static_assert(_S_size == (_As::_S_size + ...));
1859 return _S_init(__extract_simd_at<_DataType0>(cw<0>, __xs...),
1860 __extract_simd_at<_DataType1>(cw<_N0>, __xs...));
1863 [[__gnu__::__always_inline__]]
1865 _M_reduce_to_half(
auto __binary_op)
const requires (_N0 == _N1)
1866 {
return __binary_op(_M_data0, _M_data1); }
1868 [[__gnu__::__always_inline__]]
1869 constexpr value_type
1870 _M_reduce_tail(
const auto& __rest,
auto __binary_op)
const
1872 if constexpr (__rest.size() > _S_size)
1874 auto [__a, __b] = __rest.template _M_chunk<basic_vec>();
1875 return __binary_op(*
this, __a)._M_reduce_tail(__b, __binary_op);
1877 else if constexpr (__rest.size() == _S_size)
1878 return __binary_op(*
this, __rest)._M_reduce(__binary_op);
1880 return _M_reduce_to_half(__binary_op)._M_reduce_tail(__rest, __binary_op);
1883 template <
typename _BinaryOp, _TargetTraits _Traits = {}>
1884 [[__gnu__::__always_inline__]]
1885 constexpr value_type
1886 _M_reduce(_BinaryOp __binary_op)
const
1888 if constexpr (_Traits.template _M_eval_as_f32<value_type>()
1889 && (is_same_v<_BinaryOp, plus<>>
1890 || is_same_v<_BinaryOp, multiplies<>>))
1891 return value_type(rebind_t<float, basic_vec>(*this)._M_reduce(__binary_op));
1893 else if constexpr (is_integral_v<value_type> &&
sizeof(value_type) == 1
1894 && is_same_v<
decltype(__binary_op), multiplies<>>)
1907 if constexpr (_DataType1::_S_is_scalar)
1908 return __binary_op(_DataType1(_M_data0._M_reduce(__binary_op)), _M_data1)[0];
1910 else if constexpr (_S_size % 2 == 0)
1912 using _V16 = resize_t<_S_size / 2, rebind_t<unsigned short, basic_vec>>;
1913 auto __a = __builtin_bit_cast(_V16, *
this);
1914 return __binary_op(__a, __a >> __CHAR_BIT__)._M_reduce(__binary_op);
1918 using _V16 = rebind_t<unsigned short, basic_vec>;
1919 return _V16(*this)._M_reduce(__binary_op);
1924 return _M_data0._M_reduce_tail(_M_data1, __binary_op);
1927 [[__gnu__::__always_inline__]]
1929 _M_isnan() const requires is_floating_point_v<value_type>
1930 {
return mask_type::_S_init(_M_data0._M_isnan(), _M_data1._M_isnan()); }
1932 [[__gnu__::__always_inline__]]
1934 _M_isinf() const requires is_floating_point_v<value_type>
1935 {
return mask_type::_S_init(_M_data0._M_isinf(), _M_data1._M_isinf()); }
1937 [[__gnu__::__always_inline__]]
1939 _M_isunordered(basic_vec __y)
const requires is_floating_point_v<value_type>
1941 return mask_type::_S_init(_M_data0._M_isunordered(__y._M_data0),
1942 _M_data1._M_isunordered(__y._M_data1));
1945 [[__gnu__::__always_inline__]]
1947 _M_abs() const requires signed_integral<value_type>
1948 {
return _S_init(_M_data0._M_abs(), _M_data1._M_abs()); }
1950 [[__gnu__::__always_inline__]]
1952 _M_fabs() const requires floating_point<value_type>
1953 {
return _S_init(_M_data0._M_fabs(), _M_data1._M_fabs()); }
1955 template <
typename _Up>
1956 [[__gnu__::__always_inline__]]
1957 static inline basic_vec
1958 _S_partial_load(
const _Up* __mem,
size_t __n)
1961 return _S_init(_DataType0(_LoadCtorTag(), __mem),
1962 _DataType1::_S_partial_load(__mem + _N0, __n - _N0));
1964 return _S_init(_DataType0::_S_partial_load(__mem, __n),
1968 template <
typename _Up, _ArchTraits _Traits = {}>
1969 static inline basic_vec
1970 _S_masked_load(
const _Up* __mem, mask_type __k)
1972 return _S_init(_DataType0::_S_masked_load(__mem, __k._M_data0),
1973 _DataType1::_S_masked_load(__mem + _N0, __k._M_data1));
1976 template <
typename _Up>
1977 [[__gnu__::__always_inline__]]
1979 _M_store(_Up* __mem)
const
1981 _M_data0._M_store(__mem);
1982 _M_data1._M_store(__mem + _N0);
1985 template <
typename _Up>
1986 [[__gnu__::__always_inline__]]
1988 _S_partial_store(
const basic_vec& __v, _Up* __mem,
size_t __n)
1992 __v._M_data0._M_store(__mem);
1993 _DataType1::_S_partial_store(__v._M_data1, __mem + _N0, __n - _N0);
1997 _DataType0::_S_partial_store(__v._M_data0, __mem, __n);
2001 template <
typename _Up>
2002 [[__gnu__::__always_inline__]]
2004 _S_masked_store(
const basic_vec& __v, _Up* __mem,
const mask_type& __k)
2006 _DataType0::_S_masked_store(__v._M_data0, __mem, __k._M_data0);
2007 _DataType1::_S_masked_store(__v._M_data1, __mem + _N0, __k._M_data1);
2010 basic_vec() =
default;
2013 using _NativeVecType = __vec_builtin_type<value_type, __bit_ceil(
unsigned(_S_size))>;
2015 [[__gnu__::__always_inline__]]
2017 basic_vec(
const _NativeVecType& __x)
2018 : _M_data0(_VecOps<__vec_builtin_type<value_type, _N0>>::_S_extract(__x)),
2019 _M_data1(_VecOps<__vec_builtin_type<value_type, __bit_ceil(unsigned(_N1))>>
2020 ::_S_extract(__x, integral_constant<int, _N0>()))
2023 [[__gnu__::__always_inline__]]
2025 operator _NativeVecType()
const
2026 {
return _M_concat_data(); }
2029 template <__broadcast_constructible<value_type> _Up>
2030 [[__gnu__::__always_inline__]]
2032 basic_vec(_Up&& __x) noexcept
2033 : _M_data0(
static_cast<value_type
>(__x)), _M_data1(
static_cast<value_type
>(__x))
2037 template <
typename _Up,
typename _UAbi>
2038 requires (_S_size == _UAbi::_S_size)
2039 && __explicitly_convertible_to<_Up, value_type>
2040 [[__gnu__::__always_inline__]]
2042 explicit(!__value_preserving_convertible_to<_Up, value_type>
2043 || __higher_rank_than<_Up, value_type>)
2044 basic_vec(
const basic_vec<_Up, _UAbi>& __x) noexcept
2045 : _M_data0(get<0>(chunk<_N0>(__x))),
2046 _M_data1(get<1>(chunk<_N0>(__x)))
2049 using _VecBase<_Tp, _Ap>::_VecBase;
2052 template <__simd_generator_invokable<value_type, _S_size> _Fp>
2053 [[__gnu__::__always_inline__]]
2055 basic_vec(_Fp&& __gen)
2056 : _M_data0(__gen), _M_data1([&] [[__gnu__::__always_inline__]] (auto __i) {
2057 return __gen(__simd_size_c<__i + _N0>);
2062 template <
typename _Up>
2063 [[__gnu__::__always_inline__]]
2065 basic_vec(_LoadCtorTag,
const _Up* __ptr)
2066 : _M_data0(_LoadCtorTag(), __ptr),
2067 _M_data1(_LoadCtorTag(), __ptr + _N0)
2070 template <ranges::contiguous_range _Rg,
typename... _Flags>
2071 requires ranges::__static_sized_range<_Rg>
2072 && __vectorizable<ranges::range_value_t<_Rg>>
2073 && __explicitly_convertible_to<ranges::range_value_t<_Rg>, value_type>
2075 basic_vec(_Rg&& __range, flags<_Flags...> __flags = {})
2076 requires (ranges::size(__range) == _S_size)
2077 : basic_vec(_LoadCtorTag(),
2078 __flags.template _S_adjust_pointer<basic_vec>(ranges::
data(__range)))
2080 static_assert(__loadstore_convertible_to<ranges::range_value_t<_Rg>, value_type,
2085 [[__gnu__::__always_inline__]]
2086 constexpr value_type
2087 operator[](__simd_size_type __i)
const
2089 __glibcxx_simd_precondition(__i >= 0 && __i < _S_size,
"subscript is out of bounds");
2090 if (__is_const_known(__i))
2091 return __i < _N0 ? _M_data0[__i] : _M_data1[__i - _N0];
2094 using _AliasingT [[__gnu__::__may_alias__]] = value_type;
2095 return reinterpret_cast<const _AliasingT*
>(
this)[__i];
2100 [[__gnu__::__always_inline__]]
2101 constexpr basic_vec&
2102 operator++() noexcept requires requires(value_type __a) { ++__a; }
2109 [[__gnu__::__always_inline__]]
2111 operator++(
int)
noexcept requires requires(value_type __a) { __a++; }
2113 basic_vec __r = *
this;
2119 [[__gnu__::__always_inline__]]
2120 constexpr basic_vec&
2121 operator--() noexcept requires requires(value_type __a) { --__a; }
2128 [[__gnu__::__always_inline__]]
2130 operator--(
int)
noexcept requires requires(value_type __a) { __a--; }
2132 basic_vec __r = *
this;
2138 [[__gnu__::__always_inline__]]
2140 operator!() const noexcept requires requires(value_type __a) { !__a; }
2141 {
return mask_type::_S_init(!_M_data0, !_M_data1); }
2143 [[__gnu__::__always_inline__]]
2145 operator+() const noexcept requires requires(value_type __a) { +__a; }
2148 [[__gnu__::__always_inline__]]
2150 operator-() const noexcept requires requires(value_type __a) { -__a; }
2151 {
return _S_init(-_M_data0, -_M_data1); }
2153 [[__gnu__::__always_inline__]]
2155 operator~() const noexcept requires requires(value_type __a) { ~__a; }
2156 {
return _S_init(~_M_data0, ~_M_data1); }
2159#define _GLIBCXX_SIMD_DEFINE_OP(sym) \
2160 [[__gnu__::__always_inline__]] \
2161 friend constexpr basic_vec& \
2162 operator sym##=(basic_vec& __x, const basic_vec& __y) _GLIBCXX_SIMD_NOEXCEPT \
2164 __x._M_data0 sym##= __y._M_data0; \
2165 __x._M_data1 sym##= __y._M_data1; \
2169 _GLIBCXX_SIMD_DEFINE_OP(+)
2170 _GLIBCXX_SIMD_DEFINE_OP(-)
2171 _GLIBCXX_SIMD_DEFINE_OP(*)
2172 _GLIBCXX_SIMD_DEFINE_OP(/)
2173 _GLIBCXX_SIMD_DEFINE_OP(%)
2174 _GLIBCXX_SIMD_DEFINE_OP(&)
2175 _GLIBCXX_SIMD_DEFINE_OP(|)
2176 _GLIBCXX_SIMD_DEFINE_OP(^)
2177 _GLIBCXX_SIMD_DEFINE_OP(<<)
2178 _GLIBCXX_SIMD_DEFINE_OP(>>)
2180#undef _GLIBCXX_SIMD_DEFINE_OP
2182 [[__gnu__::__always_inline__]]
2183 friend constexpr basic_vec&
2184 operator<<=(basic_vec& __x, __simd_size_type __y) _GLIBCXX_SIMD_NOEXCEPT
2185 requires requires(value_type __a, __simd_size_type __b) { __a << __b; }
2187 __x._M_data0 <<= __y;
2188 __x._M_data1 <<= __y;
2192 [[__gnu__::__always_inline__]]
2193 friend constexpr basic_vec&
2194 operator>>=(basic_vec& __x, __simd_size_type __y) _GLIBCXX_SIMD_NOEXCEPT
2195 requires requires(value_type __a, __simd_size_type __b) { __a >> __b; }
2197 __x._M_data0 >>= __y;
2198 __x._M_data1 >>= __y;
2203 [[__gnu__::__always_inline__]]
2204 friend constexpr mask_type
2205 operator==(
const basic_vec& __x,
const basic_vec& __y)
noexcept
2206 {
return mask_type::_S_init(__x._M_data0 == __y._M_data0, __x._M_data1 == __y._M_data1); }
2208 [[__gnu__::__always_inline__]]
2209 friend constexpr mask_type
2210 operator!=(
const basic_vec& __x,
const basic_vec& __y)
noexcept
2211 {
return mask_type::_S_init(__x._M_data0 != __y._M_data0, __x._M_data1 != __y._M_data1); }
2213 [[__gnu__::__always_inline__]]
2214 friend constexpr mask_type
2215 operator<(
const basic_vec& __x,
const basic_vec& __y)
noexcept
2216 {
return mask_type::_S_init(__x._M_data0 < __y._M_data0, __x._M_data1 < __y._M_data1); }
2218 [[__gnu__::__always_inline__]]
2219 friend constexpr mask_type
2220 operator<=(
const basic_vec& __x,
const basic_vec& __y)
noexcept
2221 {
return mask_type::_S_init(__x._M_data0 <= __y._M_data0, __x._M_data1 <= __y._M_data1); }
2223 [[__gnu__::__always_inline__]]
2224 friend constexpr mask_type
2225 operator>(
const basic_vec& __x,
const basic_vec& __y)
noexcept
2226 {
return mask_type::_S_init(__x._M_data0 > __y._M_data0, __x._M_data1 > __y._M_data1); }
2228 [[__gnu__::__always_inline__]]
2229 friend constexpr mask_type
2230 operator>=(
const basic_vec& __x,
const basic_vec& __y)
noexcept
2231 {
return mask_type::_S_init(__x._M_data0 >= __y._M_data0, __x._M_data1 >= __y._M_data1); }
2234 [[__gnu__::__always_inline__]]
2235 friend constexpr basic_vec
2236 __select_impl(
const mask_type& __k,
const basic_vec& __t,
const basic_vec& __f)
noexcept
2238 return _S_init(__select_impl(__k._M_data0, __t._M_data0, __f._M_data0),
2239 __select_impl(__k._M_data1, __t._M_data1, __f._M_data1));
2244 template <ranges::contiguous_range _Rg,
typename... _Ts>
2245 requires ranges::__static_sized_range<_Rg>
2246 basic_vec(_Rg&& __r, _Ts...)
2247 -> basic_vec<ranges::range_value_t<_Rg>,
2248 __deduce_abi_t<ranges::range_value_t<_Rg>,
2250 static_cast<__simd_size_type
>(ranges::size(__r))>>;
2252 static_cast<__simd_size_type
>(
decltype(std::span(__r))::extent)>>;
2255 template <
size_t _Bytes,
typename _Ap>
2256 basic_vec(basic_mask<_Bytes, _Ap>)
2257 -> basic_vec<__integer_from<_Bytes>,
2258 decltype(__abi_rebind<__integer_from<_Bytes>, basic_mask<_Bytes, _Ap>::size.value,
2262 template <__vectorizable _Tp>
2263 requires is_arithmetic_v<_Tp>
2264 inline constexpr _Tp
2265 __iota<_Tp> = _Tp();
2267 template <
typename _Tp,
typename _Ap>
2268 inline constexpr basic_vec<_Tp, _Ap>
2269 __iota<basic_vec<_Tp, _Ap>> = basic_vec<_Tp, _Ap>([](_Tp __i) -> _Tp {
2271 "iota object would overflow");
2275_GLIBCXX_END_NAMESPACE_VERSION
2278#pragma GCC diagnostic pop
constexpr bool operator<=(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
constexpr bool operator>=(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
constexpr bool operator<(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
constexpr bool operator>(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
constexpr complex< _Tp > operator-(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x minus y.
constexpr complex< _Tp > operator+(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x plus y.
bool is_sufficiently_aligned(_Tp *__ptr)
Is __ptr aligned to an _Align byte boundary?
ISO C++ entities toplevel namespace is std.
_Tp fabs(const std::complex< _Tp > &__z)
fabs(__z) TR1 8.1.8 [tr.c99.cmplx.fabs]
constexpr auto data(_Container &__cont) noexcept(noexcept(__cont.data())) -> decltype(__cont.data())
Return the data pointer of a container.
static constexpr _Tp max() noexcept
static constexpr _Tp infinity() noexcept