libstdc++
simd.h
1// Definition of the public simd interfaces -*- C++ -*-
2
3// Copyright (C) 2020-2026 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25#ifndef _GLIBCXX_EXPERIMENTAL_SIMD_H
26#define _GLIBCXX_EXPERIMENTAL_SIMD_H
27
28#if __cplusplus >= 201703L
29
30#include "simd_detail.h"
31#include "numeric_traits.h"
32#include <bit>
33#include <bitset>
34#ifdef _GLIBCXX_DEBUG_UB
35#include <cstdio> // for stderr
36#endif
37#include <cstring>
38#include <cmath>
39#include <functional>
40#include <iosfwd>
41#include <utility>
42#include <algorithm>
43
44#if _GLIBCXX_SIMD_X86INTRIN
45#include <x86intrin.h>
46#elif _GLIBCXX_SIMD_HAVE_NEON
47#pragma GCC diagnostic push
48// narrowing conversion of '__a' from 'uint64_t' {aka 'long long unsigned int'} to
49// 'int64x1_t' {aka 'long long int'} [-Wnarrowing]
50#pragma GCC diagnostic ignored "-Wnarrowing"
51#include <arm_neon.h>
52#pragma GCC diagnostic pop
53#endif
54#if _GLIBCXX_SIMD_HAVE_SVE
55#include <arm_sve.h>
56#endif
57
58/** @namespace std::experimental::parallelism_v2
59 * @ingroup ts_simd
60 */
61_GLIBCXX_SIMD_BEGIN_NAMESPACE
62
63/** @ingroup ts_simd
64 * @{
65 */
66/* There are several closely related types, with the following naming
67 * convention:
68 * _Tp: vectorizable (arithmetic) type (or any type)
69 * _TV: __vector_type_t<_Tp, _Np>
70 * _TW: _SimdWrapper<_Tp, _Np>
71 * _TI: __intrinsic_type_t<_Tp, _Np>
72 * _TVT: _VectorTraits<_TV> or _VectorTraits<_TW>
73 * If one additional type is needed use _U instead of _T.
74 * Otherwise use _T\d, _TV\d, _TW\d, TI\d, _TVT\d.
75 *
76 * More naming conventions:
77 * _Ap or _Abi: An ABI tag from the simd_abi namespace
78 * _Ip: often used for integer types with sizeof(_Ip) == sizeof(_Tp),
79 * _IV, _IW as for _TV, _TW
80 * _Np: number of elements (not bytes)
81 * _Bytes: number of bytes
82 *
83 * Variable names:
84 * __k: mask object (vector- or bitmask)
85 */
86
87#if !_GLIBCXX_SIMD_X86INTRIN
88using __m128 [[__gnu__::__vector_size__(16)]] = float;
89using __m128d [[__gnu__::__vector_size__(16)]] = double;
90using __m128i [[__gnu__::__vector_size__(16)]] = long long;
91using __m256 [[__gnu__::__vector_size__(32)]] = float;
92using __m256d [[__gnu__::__vector_size__(32)]] = double;
93using __m256i [[__gnu__::__vector_size__(32)]] = long long;
94using __m512 [[__gnu__::__vector_size__(64)]] = float;
95using __m512d [[__gnu__::__vector_size__(64)]] = double;
96using __m512i [[__gnu__::__vector_size__(64)]] = long long;
97#endif
98
99#if _GLIBCXX_SIMD_HAVE_SVE
100constexpr inline int __sve_vectorized_size_bytes = __ARM_FEATURE_SVE_BITS / 8;
101#else
102constexpr inline int __sve_vectorized_size_bytes = 0;
103#endif
104
105namespace simd_abi {
106// simd_abi forward declarations {{{
107// implementation details:
108struct _Scalar;
109
110template <int _Np>
111 struct _Fixed;
112
113// There are two major ABIs that appear on different architectures.
114// Both have non-boolean values packed into an N Byte register
115// -> #elements = N / sizeof(T)
116// Masks differ:
117// 1. Use value vector registers for masks (all 0 or all 1)
118// 2. Use bitmasks (mask registers) with one bit per value in the corresponding
119// value vector
120//
121// Both can be partially used, masking off the rest when doing horizontal
122// operations or operations that can trap (e.g. FP_INVALID or integer division
123// by 0). This is encoded as the number of used bytes.
124template <int _UsedBytes>
125 struct _VecBuiltin;
126
127template <int _UsedBytes>
128 struct _VecBltnBtmsk;
129
130template <int _UsedBytes, int _TotalBytes = __sve_vectorized_size_bytes>
131 struct _SveAbi;
132
133template <typename _Tp, int _Np>
134 using _VecN = _VecBuiltin<sizeof(_Tp) * _Np>;
135
136template <int _UsedBytes = 16>
137 using _Sse = _VecBuiltin<_UsedBytes>;
138
139template <int _UsedBytes = 32>
140 using _Avx = _VecBuiltin<_UsedBytes>;
141
142template <int _UsedBytes = 64>
143 using _Avx512 = _VecBltnBtmsk<_UsedBytes>;
144
145template <int _UsedBytes = 16>
146 using _Neon = _VecBuiltin<_UsedBytes>;
147
148template <int _UsedBytes = __sve_vectorized_size_bytes>
149 using _Sve = _SveAbi<_UsedBytes, __sve_vectorized_size_bytes>;
150
151// implementation-defined:
152using __sse = _Sse<>;
153using __avx = _Avx<>;
154using __avx512 = _Avx512<>;
155using __neon = _Neon<>;
156using __neon128 = _Neon<16>;
157using __neon64 = _Neon<8>;
158using __sve = _Sve<>;
159
160// standard:
161template <typename _Tp, size_t _Np, typename...>
162 struct deduce;
163
164template <int _Np>
165 using fixed_size = _Fixed<_Np>;
166
167using scalar = _Scalar;
168
169// }}}
170} // namespace simd_abi
171// forward declarations is_simd(_mask), simd(_mask), simd_size {{{
172template <typename _Tp>
173 struct is_simd;
174
175template <typename _Tp>
176 struct is_simd_mask;
177
178template <typename _Tp, typename _Abi>
179 class simd;
180
181template <typename _Tp, typename _Abi>
182 class simd_mask;
183
184template <typename _Tp, typename _Abi>
185 struct simd_size;
186
187// }}}
188// load/store flags {{{
189struct element_aligned_tag
190{
191 template <typename _Tp, typename _Up = typename _Tp::value_type>
192 static constexpr size_t _S_alignment = alignof(_Up);
193
194 template <typename _Tp, typename _Up>
195 _GLIBCXX_SIMD_INTRINSIC static constexpr _Up*
196 _S_apply(_Up* __ptr)
197 { return __ptr; }
198};
199
200struct vector_aligned_tag
201{
202 template <typename _Tp, typename _Up = typename _Tp::value_type>
203 static constexpr size_t _S_alignment
204 = std::__bit_ceil(sizeof(_Up) * _Tp::size());
205
206 template <typename _Tp, typename _Up>
207 _GLIBCXX_SIMD_INTRINSIC static constexpr _Up*
208 _S_apply(_Up* __ptr)
209 { return static_cast<_Up*>(__builtin_assume_aligned(__ptr, _S_alignment<_Tp, _Up>)); }
210};
211
212template <size_t _Np> struct overaligned_tag
213{
214 template <typename _Tp, typename _Up = typename _Tp::value_type>
215 static constexpr size_t _S_alignment = _Np;
216
217 template <typename _Tp, typename _Up>
218 _GLIBCXX_SIMD_INTRINSIC static constexpr _Up*
219 _S_apply(_Up* __ptr)
220 { return static_cast<_Up*>(__builtin_assume_aligned(__ptr, _Np)); }
221};
222
223inline constexpr element_aligned_tag element_aligned = {};
224
225inline constexpr vector_aligned_tag vector_aligned = {};
226
227template <size_t _Np>
228 inline constexpr overaligned_tag<_Np> overaligned = {};
229
230// }}}
231template <size_t _Xp>
232 using _SizeConstant = integral_constant<size_t, _Xp>;
233// constexpr feature detection{{{
234constexpr inline bool __have_mmx = _GLIBCXX_SIMD_HAVE_MMX;
235constexpr inline bool __have_sse = _GLIBCXX_SIMD_HAVE_SSE;
236constexpr inline bool __have_sse2 = _GLIBCXX_SIMD_HAVE_SSE2;
237constexpr inline bool __have_sse3 = _GLIBCXX_SIMD_HAVE_SSE3;
238constexpr inline bool __have_ssse3 = _GLIBCXX_SIMD_HAVE_SSSE3;
239constexpr inline bool __have_sse4_1 = _GLIBCXX_SIMD_HAVE_SSE4_1;
240constexpr inline bool __have_sse4_2 = _GLIBCXX_SIMD_HAVE_SSE4_2;
241constexpr inline bool __have_xop = _GLIBCXX_SIMD_HAVE_XOP;
242constexpr inline bool __have_avx = _GLIBCXX_SIMD_HAVE_AVX;
243constexpr inline bool __have_avx2 = _GLIBCXX_SIMD_HAVE_AVX2;
244constexpr inline bool __have_bmi = _GLIBCXX_SIMD_HAVE_BMI1;
245constexpr inline bool __have_bmi2 = _GLIBCXX_SIMD_HAVE_BMI2;
246constexpr inline bool __have_lzcnt = _GLIBCXX_SIMD_HAVE_LZCNT;
247constexpr inline bool __have_sse4a = _GLIBCXX_SIMD_HAVE_SSE4A;
248constexpr inline bool __have_fma = _GLIBCXX_SIMD_HAVE_FMA;
249constexpr inline bool __have_fma4 = _GLIBCXX_SIMD_HAVE_FMA4;
250constexpr inline bool __have_f16c = _GLIBCXX_SIMD_HAVE_F16C;
251constexpr inline bool __have_popcnt = _GLIBCXX_SIMD_HAVE_POPCNT;
252constexpr inline bool __have_avx512f = _GLIBCXX_SIMD_HAVE_AVX512F;
253constexpr inline bool __have_avx512dq = _GLIBCXX_SIMD_HAVE_AVX512DQ;
254constexpr inline bool __have_avx512vl = _GLIBCXX_SIMD_HAVE_AVX512VL;
255constexpr inline bool __have_avx512bw = _GLIBCXX_SIMD_HAVE_AVX512BW;
256constexpr inline bool __have_avx512dq_vl = __have_avx512dq && __have_avx512vl;
257constexpr inline bool __have_avx512bw_vl = __have_avx512bw && __have_avx512vl;
258constexpr inline bool __have_avx512bitalg = _GLIBCXX_SIMD_HAVE_AVX512BITALG;
259constexpr inline bool __have_avx512vbmi2 = _GLIBCXX_SIMD_HAVE_AVX512VBMI2;
260constexpr inline bool __have_avx512vbmi = _GLIBCXX_SIMD_HAVE_AVX512VBMI;
261constexpr inline bool __have_avx512ifma = _GLIBCXX_SIMD_HAVE_AVX512IFMA;
262constexpr inline bool __have_avx512cd = _GLIBCXX_SIMD_HAVE_AVX512CD;
263constexpr inline bool __have_avx512vnni = _GLIBCXX_SIMD_HAVE_AVX512VNNI;
264constexpr inline bool __have_avx512vpopcntdq = _GLIBCXX_SIMD_HAVE_AVX512VPOPCNTDQ;
265constexpr inline bool __have_avx512vp2intersect = _GLIBCXX_SIMD_HAVE_AVX512VP2INTERSECT;
266
267constexpr inline bool __have_neon = _GLIBCXX_SIMD_HAVE_NEON;
268constexpr inline bool __have_neon_a32 = _GLIBCXX_SIMD_HAVE_NEON_A32;
269constexpr inline bool __have_neon_a64 = _GLIBCXX_SIMD_HAVE_NEON_A64;
270constexpr inline bool __support_neon_float =
271#if defined __GCC_IEC_559
272 __GCC_IEC_559 == 0;
273#elif defined __FAST_MATH__
274 true;
275#else
276 false;
277#endif
278
279constexpr inline bool __have_sve = _GLIBCXX_SIMD_HAVE_SVE;
280constexpr inline bool __have_sve2 = _GLIBCXX_SIMD_HAVE_SVE2;
281
282#ifdef _ARCH_PWR10
283constexpr inline bool __have_power10vec = true;
284#else
285constexpr inline bool __have_power10vec = false;
286#endif
287#ifdef __POWER9_VECTOR__
288constexpr inline bool __have_power9vec = true;
289#else
290constexpr inline bool __have_power9vec = false;
291#endif
292#if defined __POWER8_VECTOR__
293constexpr inline bool __have_power8vec = true;
294#else
295constexpr inline bool __have_power8vec = __have_power9vec;
296#endif
297#if defined __VSX__
298constexpr inline bool __have_power_vsx = true;
299#else
300constexpr inline bool __have_power_vsx = __have_power8vec;
301#endif
302#if defined __ALTIVEC__
303constexpr inline bool __have_power_vmx = true;
304#else
305constexpr inline bool __have_power_vmx = __have_power_vsx;
306#endif
307
308// }}}
309
310namespace __detail
311{
312#ifdef math_errhandling
313 // Determines _S_handle_fpexcept from math_errhandling if it is defined and expands to a constant
314 // expression. math_errhandling may expand to an extern symbol, in which case a constexpr value
315 // must be guessed.
316 template <int = math_errhandling>
317 constexpr bool
318 __handle_fpexcept_impl(int)
319 { return math_errhandling & MATH_ERREXCEPT; }
320#endif
321
322 // Fallback if math_errhandling doesn't work: with fast-math assume floating-point exceptions are
323 // ignored, otherwise implement correct exception behavior.
324 constexpr bool
325 __handle_fpexcept_impl(float)
326 {
327#if defined __FAST_MATH__
328 return false;
329#else
330 return true;
331#endif
332 }
333
334 /// True if math functions must raise floating-point exceptions as specified by C17.
335 static constexpr bool _S_handle_fpexcept = __handle_fpexcept_impl(0);
336
337 constexpr std::uint_least64_t
338 __floating_point_flags()
339 {
340 std::uint_least64_t __flags = 0;
341 if constexpr (_S_handle_fpexcept)
342 __flags |= 1;
343#ifdef __FAST_MATH__
344 __flags |= 1 << 1;
345#elif __FINITE_MATH_ONLY__
346 __flags |= 2 << 1;
347#elif __GCC_IEC_559 < 2
348 __flags |= 3 << 1;
349#endif
350 __flags |= (__FLT_EVAL_METHOD__ + 1) << 3;
351 return __flags;
352 }
353
354 constexpr std::uint_least64_t
355 __machine_flags()
356 {
357 if constexpr (__have_mmx || __have_sse)
358 return __have_mmx
359 | (__have_sse << 1)
360 | (__have_sse2 << 2)
361 | (__have_sse3 << 3)
362 | (__have_ssse3 << 4)
363 | (__have_sse4_1 << 5)
364 | (__have_sse4_2 << 6)
365 | (__have_xop << 7)
366 | (__have_avx << 8)
367 | (__have_avx2 << 9)
368 | (__have_bmi << 10)
369 | (__have_bmi2 << 11)
370 | (__have_lzcnt << 12)
371 | (__have_sse4a << 13)
372 | (__have_fma << 14)
373 | (__have_fma4 << 15)
374 | (__have_f16c << 16)
375 | (__have_popcnt << 17)
376 | (__have_avx512f << 18)
377 | (__have_avx512dq << 19)
378 | (__have_avx512vl << 20)
379 | (__have_avx512bw << 21)
380 | (__have_avx512bitalg << 22)
381 | (__have_avx512vbmi2 << 23)
382 | (__have_avx512vbmi << 24)
383 | (__have_avx512ifma << 25)
384 | (__have_avx512cd << 26)
385 | (__have_avx512vnni << 27)
386 | (__have_avx512vpopcntdq << 28)
387 | (__have_avx512vp2intersect << 29);
388 else if constexpr (__have_neon || __have_sve)
389 return __have_neon
390 | (__have_neon_a32 << 1)
391 | (__have_neon_a64 << 2)
392 | (__have_neon_a64 << 2)
393 | (__support_neon_float << 3)
394 | (__have_sve << 4)
395 | (__have_sve2 << 5);
396 else if constexpr (__have_power_vmx)
397 return __have_power_vmx
398 | (__have_power_vsx << 1)
399 | (__have_power8vec << 2)
400 | (__have_power9vec << 3)
401 | (__have_power10vec << 4);
402 else
403 return 0;
404 }
405
406 namespace
407 {
408 struct _OdrEnforcer {};
409 }
410
411 template <std::uint_least64_t...>
412 struct _MachineFlagsTemplate {};
413
414 /**@internal
415 * Use this type as default template argument to all function templates that
416 * are not declared always_inline. It ensures, that a function
417 * specialization, which the compiler decides not to inline, has a unique symbol
418 * (_OdrEnforcer) or a symbol matching the machine/architecture flags
419 * (_MachineFlagsTemplate). This helps to avoid ODR violations in cases where
420 * users link TUs compiled with different flags. This is especially important
421 * for using simd in libraries.
422 */
423 using __odr_helper
424 = conditional_t<__machine_flags() == 0, _OdrEnforcer,
425 _MachineFlagsTemplate<__machine_flags(), __floating_point_flags()>>;
426
427 struct _Minimum
428 {
429 template <typename _Tp>
430 _GLIBCXX_SIMD_INTRINSIC constexpr
431 _Tp
432 operator()(_Tp __a, _Tp __b) const
433 {
434 using std::min;
435 return min(__a, __b);
436 }
437 };
438
439 struct _Maximum
440 {
441 template <typename _Tp>
442 _GLIBCXX_SIMD_INTRINSIC constexpr
443 _Tp
444 operator()(_Tp __a, _Tp __b) const
445 {
446 using std::max;
447 return max(__a, __b);
448 }
449 };
450} // namespace __detail
451
452// unrolled/pack execution helpers
453// __execute_n_times{{{
454template <typename _Fp, size_t... _I>
455 [[__gnu__::__flatten__]] _GLIBCXX_SIMD_INTRINSIC constexpr
456 void
457 __execute_on_index_sequence(_Fp&& __f, index_sequence<_I...>)
458 { ((void)__f(_SizeConstant<_I>()), ...); }
459
460template <typename _Fp>
461 _GLIBCXX_SIMD_INTRINSIC constexpr void
462 __execute_on_index_sequence(_Fp&&, index_sequence<>)
463 { }
464
465template <size_t _Np, typename _Fp>
466 _GLIBCXX_SIMD_INTRINSIC constexpr void
467 __execute_n_times(_Fp&& __f)
468 {
469 __execute_on_index_sequence(static_cast<_Fp&&>(__f),
470 make_index_sequence<_Np>{});
471 }
472
473// }}}
474// __generate_from_n_evaluations{{{
475template <typename _R, typename _Fp, size_t... _I>
476 [[__gnu__::__flatten__]] _GLIBCXX_SIMD_INTRINSIC constexpr
477 _R
478 __execute_on_index_sequence_with_return(_Fp&& __f, index_sequence<_I...>)
479 { return _R{__f(_SizeConstant<_I>())...}; }
480
481template <size_t _Np, typename _R, typename _Fp>
482 _GLIBCXX_SIMD_INTRINSIC constexpr _R
483 __generate_from_n_evaluations(_Fp&& __f)
484 {
485 return __execute_on_index_sequence_with_return<_R>(
486 static_cast<_Fp&&>(__f), make_index_sequence<_Np>{});
487 }
488
489// }}}
490// __call_with_n_evaluations{{{
491template <size_t... _I, typename _F0, typename _FArgs>
492 [[__gnu__::__flatten__]] _GLIBCXX_SIMD_INTRINSIC constexpr
493 auto
494 __call_with_n_evaluations(index_sequence<_I...>, _F0&& __f0, _FArgs&& __fargs)
495 { return __f0(__fargs(_SizeConstant<_I>())...); }
496
497template <size_t _Np, typename _F0, typename _FArgs>
498 _GLIBCXX_SIMD_INTRINSIC constexpr auto
499 __call_with_n_evaluations(_F0&& __f0, _FArgs&& __fargs)
500 {
501 return __call_with_n_evaluations(make_index_sequence<_Np>{},
502 static_cast<_F0&&>(__f0),
503 static_cast<_FArgs&&>(__fargs));
504 }
505
506// }}}
507// __call_with_subscripts{{{
508template <size_t _First = 0, size_t... _It, typename _Tp, typename _Fp>
509 [[__gnu__::__flatten__]] _GLIBCXX_SIMD_INTRINSIC constexpr
510 auto
511 __call_with_subscripts(_Tp&& __x, index_sequence<_It...>, _Fp&& __fun)
512 { return __fun(__x[_First + _It]...); }
513
514template <size_t _Np, size_t _First = 0, typename _Tp, typename _Fp>
515 _GLIBCXX_SIMD_INTRINSIC constexpr auto
516 __call_with_subscripts(_Tp&& __x, _Fp&& __fun)
517 {
518 return __call_with_subscripts<_First>(static_cast<_Tp&&>(__x),
519 make_index_sequence<_Np>(),
520 static_cast<_Fp&&>(__fun));
521 }
522
523// }}}
524
525// vvv ---- type traits ---- vvv
526// integer type aliases{{{
527using _UChar = unsigned char;
528using _SChar = signed char;
529using _UShort = unsigned short;
530using _UInt = unsigned int;
531using _ULong = unsigned long;
532using _ULLong = unsigned long long;
533using _LLong = long long;
534
535//}}}
536// __first_of_pack{{{
537template <typename _T0, typename...>
538 struct __first_of_pack
539 { using type = _T0; };
540
541template <typename... _Ts>
542 using __first_of_pack_t = typename __first_of_pack<_Ts...>::type;
543
544//}}}
545// __value_type_or_identity_t {{{
546template <typename _Tp>
547 typename _Tp::value_type
548 __value_type_or_identity_impl(int);
549
550template <typename _Tp>
551 _Tp
552 __value_type_or_identity_impl(float);
553
554template <typename _Tp>
555 using __value_type_or_identity_t
556 = decltype(__value_type_or_identity_impl<_Tp>(int()));
557
558// }}}
559// __is_vectorizable {{{
560template <typename _Tp>
561 struct __is_vectorizable : public is_arithmetic<_Tp> {};
562
563template <>
564 struct __is_vectorizable<bool> : public false_type {};
565
566template <typename _Tp>
567 inline constexpr bool __is_vectorizable_v = __is_vectorizable<_Tp>::value;
568
569// Deduces to a vectorizable type
570template <typename _Tp, typename = enable_if_t<__is_vectorizable_v<_Tp>>>
571 using _Vectorizable = _Tp;
572
573// }}}
574// _LoadStorePtr / __is_possible_loadstore_conversion {{{
575template <typename _Ptr, typename _ValueType>
576 struct __is_possible_loadstore_conversion
577 : conjunction<__is_vectorizable<_Ptr>, __is_vectorizable<_ValueType>> {};
578
579template <>
580 struct __is_possible_loadstore_conversion<bool, bool> : true_type {};
581
582// Deduces to a type allowed for load/store with the given value type.
583template <typename _Ptr, typename _ValueType,
584 typename = enable_if_t<
585 __is_possible_loadstore_conversion<_Ptr, _ValueType>::value>>
586 using _LoadStorePtr = _Ptr;
587
588// }}}
589// __is_bitmask{{{
590template <typename _Tp, typename = void_t<>>
591 struct __is_bitmask : false_type {};
592
593template <typename _Tp>
594 inline constexpr bool __is_bitmask_v = __is_bitmask<_Tp>::value;
595
596// the __mmaskXX case:
597template <typename _Tp>
598 struct __is_bitmask<_Tp,
599 void_t<decltype(declval<unsigned&>() = declval<_Tp>() & 1u)>>
600 : true_type {};
601
602// }}}
603// __int_for_sizeof{{{
604#pragma GCC diagnostic push
605#pragma GCC diagnostic ignored "-Wpedantic"
606template <size_t _Bytes>
607 constexpr auto
608 __int_for_sizeof()
609 {
610 static_assert(_Bytes > 0);
611 if constexpr (_Bytes == sizeof(int32_t))
612 return int32_t();
613 else if constexpr (_Bytes == sizeof(int8_t))
614 return int8_t();
615 else if constexpr (_Bytes == sizeof(int16_t))
616 return int16_t();
617 else if constexpr (_Bytes == sizeof(int64_t))
618 return int64_t();
619 #ifdef __SIZEOF_INT128__
620 else if constexpr (_Bytes == sizeof(__int128))
621 return __int128();
622 #endif // __SIZEOF_INT128__
623 else if constexpr (_Bytes % sizeof(int) == 0)
624 {
625 constexpr size_t _Np = _Bytes / sizeof(int);
626 struct _Ip
627 {
628 int _M_data[_Np];
629
630 _GLIBCXX_SIMD_INTRINSIC constexpr _Ip
631 operator&(_Ip __rhs) const
632 {
633 return __generate_from_n_evaluations<_Np, _Ip>(
634 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
635 return __rhs._M_data[__i] & _M_data[__i];
636 });
637 }
638
639 _GLIBCXX_SIMD_INTRINSIC constexpr _Ip
640 operator|(_Ip __rhs) const
641 {
642 return __generate_from_n_evaluations<_Np, _Ip>(
643 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
644 return __rhs._M_data[__i] | _M_data[__i];
645 });
646 }
647
648 _GLIBCXX_SIMD_INTRINSIC constexpr _Ip
649 operator^(_Ip __rhs) const
650 {
651 return __generate_from_n_evaluations<_Np, _Ip>(
652 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
653 return __rhs._M_data[__i] ^ _M_data[__i];
654 });
655 }
656
657 _GLIBCXX_SIMD_INTRINSIC constexpr _Ip
658 operator~() const
659 {
660 return __generate_from_n_evaluations<_Np, _Ip>(
661 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return ~_M_data[__i]; });
662 }
663 };
664 return _Ip{};
665 }
666 else
667 static_assert(_Bytes == 0, "this should be unreachable");
668 }
669#pragma GCC diagnostic pop
670
671template <typename _Tp>
672 using __int_for_sizeof_t = decltype(__int_for_sizeof<sizeof(_Tp)>());
673
674template <size_t _Np>
675 using __int_with_sizeof_t = decltype(__int_for_sizeof<_Np>());
676
677// }}}
678// __is_fixed_size_abi{{{
679template <typename _Tp>
680 struct __is_fixed_size_abi : false_type {};
681
682template <int _Np>
683 struct __is_fixed_size_abi<simd_abi::fixed_size<_Np>> : true_type {};
684
685template <typename _Tp>
686 inline constexpr bool __is_fixed_size_abi_v = __is_fixed_size_abi<_Tp>::value;
687
688// }}}
689// __is_scalar_abi {{{
690template <typename _Abi>
691 constexpr bool
692 __is_scalar_abi()
693 { return is_same_v<simd_abi::scalar, _Abi>; }
694
695// }}}
696// __abi_bytes_v {{{
697template <template <int> class _Abi, int _Bytes>
698 constexpr int
699 __abi_bytes_impl(_Abi<_Bytes>*)
700 { return _Bytes; }
701
702template <typename _Tp>
703 constexpr int
704 __abi_bytes_impl(_Tp*)
705 { return -1; }
706
707template <typename _Abi>
708 inline constexpr int __abi_bytes_v
709 = __abi_bytes_impl(static_cast<_Abi*>(nullptr));
710
711// }}}
712// __is_builtin_bitmask_abi {{{
713template <typename _Abi>
714 constexpr bool
715 __is_builtin_bitmask_abi()
716 { return is_same_v<simd_abi::_VecBltnBtmsk<__abi_bytes_v<_Abi>>, _Abi>; }
717
718// }}}
719// __is_sse_abi {{{
720template <typename _Abi>
721 constexpr bool
722 __is_sse_abi()
723 {
724 constexpr auto _Bytes = __abi_bytes_v<_Abi>;
725 return _Bytes <= 16 && is_same_v<simd_abi::_VecBuiltin<_Bytes>, _Abi>;
726 }
727
728// }}}
729// __is_avx_abi {{{
730template <typename _Abi>
731 constexpr bool
732 __is_avx_abi()
733 {
734 constexpr auto _Bytes = __abi_bytes_v<_Abi>;
735 return _Bytes > 16 && _Bytes <= 32
736 && is_same_v<simd_abi::_VecBuiltin<_Bytes>, _Abi>;
737 }
738
739// }}}
740// __is_avx512_abi {{{
741template <typename _Abi>
742 constexpr bool
743 __is_avx512_abi()
744 {
745 constexpr auto _Bytes = __abi_bytes_v<_Abi>;
746 return _Bytes <= 64 && is_same_v<simd_abi::_Avx512<_Bytes>, _Abi>;
747 }
748
749// }}}
750// __is_neon_abi {{{
751template <typename _Abi>
752 constexpr bool
753 __is_neon_abi()
754 {
755 constexpr auto _Bytes = __abi_bytes_v<_Abi>;
756 return _Bytes <= 16 && is_same_v<simd_abi::_VecBuiltin<_Bytes>, _Abi>;
757 }
758
759// }}}
760// __is_sve_abi {{{
761template <typename _Abi>
762 constexpr bool
763 __is_sve_abi()
764 {
765 constexpr auto _Bytes = __abi_bytes_v<_Abi>;
766 return _Bytes <= __sve_vectorized_size_bytes && is_same_v<simd_abi::_Sve<_Bytes>, _Abi>;
767 }
768
769// }}}
770// __make_dependent_t {{{
771template <typename, typename _Up>
772 struct __make_dependent
773 { using type = _Up; };
774
775template <typename _Tp, typename _Up>
776 using __make_dependent_t = typename __make_dependent<_Tp, _Up>::type;
777
778// }}}
779// ^^^ ---- type traits ---- ^^^
780
781// __invoke_ub{{{
782template <typename... _Args>
783 [[noreturn]] _GLIBCXX_SIMD_ALWAYS_INLINE void
784 __invoke_ub([[maybe_unused]] const char* __msg, [[maybe_unused]] const _Args&... __args)
785 {
786#ifdef _GLIBCXX_DEBUG_UB
787 __builtin_fprintf(stderr, __msg, __args...);
788 __builtin_trap();
789#else
790 __builtin_unreachable();
791#endif
792 }
793
794// }}}
795// __assert_unreachable{{{
796template <typename _Tp>
797 struct __assert_unreachable
798 { static_assert(!is_same_v<_Tp, _Tp>, "this should be unreachable"); };
799
800// }}}
801// __size_or_zero_v {{{
802template <typename _Tp, typename _Ap, size_t _Np = simd_size<_Tp, _Ap>::value>
803 constexpr size_t
804 __size_or_zero_dispatch(int)
805 { return _Np; }
806
807template <typename _Tp, typename _Ap>
808 constexpr size_t
809 __size_or_zero_dispatch(float)
810 { return 0; }
811
812template <typename _Tp, typename _Ap>
813 inline constexpr size_t __size_or_zero_v
814 = __size_or_zero_dispatch<_Tp, _Ap>(0);
815
816// }}}
817// __div_roundup {{{
818inline constexpr size_t
819__div_roundup(size_t __a, size_t __b)
820{ return (__a + __b - 1) / __b; }
821
822// }}}
823// _ExactBool{{{
824class _ExactBool
825{
826 const bool _M_data;
827
828public:
829 _GLIBCXX_SIMD_INTRINSIC constexpr
830 _ExactBool(bool __b) : _M_data(__b) {}
831
832 _ExactBool(int) = delete;
833
834 _GLIBCXX_SIMD_INTRINSIC constexpr
835 operator bool() const
836 { return _M_data; }
837};
838
839// }}}
840// __may_alias{{{
841/**@internal
842 * Helper __may_alias<_Tp> that turns _Tp into the type to be used for an
843 * aliasing pointer. This adds the __may_alias attribute to _Tp (with compilers
844 * that support it).
845 */
846template <typename _Tp>
847 using __may_alias [[__gnu__::__may_alias__]] = _Tp;
848
849// }}}
850// _UnsupportedBase {{{
851// simd and simd_mask base for unsupported <_Tp, _Abi>
852struct _UnsupportedBase
853{
854 _UnsupportedBase() = delete;
855 _UnsupportedBase(const _UnsupportedBase&) = delete;
856 _UnsupportedBase& operator=(const _UnsupportedBase&) = delete;
857 ~_UnsupportedBase() = delete;
858};
859
860// }}}
861// _InvalidTraits {{{
862/**
863 * @internal
864 * Defines the implementation of __a given <_Tp, _Abi>.
865 *
866 * Implementations must ensure that only valid <_Tp, _Abi> instantiations are
867 * possible. Static assertions in the type definition do not suffice. It is
868 * important that SFINAE works.
869 */
870struct _InvalidTraits
871{
872 using _IsValid = false_type;
873 using _SimdBase = _UnsupportedBase;
874 using _MaskBase = _UnsupportedBase;
875
876 static constexpr size_t _S_full_size = 0;
877 static constexpr bool _S_is_partial = false;
878
879 static constexpr size_t _S_simd_align = 1;
880 struct _SimdImpl;
881 struct _SimdMember {};
882 struct _SimdCastType;
883
884 static constexpr size_t _S_mask_align = 1;
885 struct _MaskImpl;
886 struct _MaskMember {};
887 struct _MaskCastType;
888};
889
890// }}}
891// _SimdTraits {{{
892template <typename _Tp, typename _Abi, typename = void_t<>>
893 struct _SimdTraits : _InvalidTraits {};
894
895// }}}
896// __private_init, __bitset_init{{{
897/**
898 * @internal
899 * Tag used for private init constructor of simd and simd_mask
900 */
901inline constexpr struct _PrivateInit {} __private_init = {};
902
903inline constexpr struct _BitsetInit {} __bitset_init = {};
904
905// }}}
906// __is_narrowing_conversion<_From, _To>{{{
907template <typename _From, typename _To, bool = is_arithmetic_v<_From>,
908 bool = is_arithmetic_v<_To>>
909 struct __is_narrowing_conversion;
910
911// ignore "signed/unsigned mismatch" in the following trait.
912// The implicit conversions will do the right thing here.
913template <typename _From, typename _To>
914 struct __is_narrowing_conversion<_From, _To, true, true>
915 : public __bool_constant<(
916 __digits_v<_From> > __digits_v<_To>
917 || __finite_max_v<_From> > __finite_max_v<_To>
918 || __finite_min_v<_From> < __finite_min_v<_To>
919 || (is_signed_v<_From> && is_unsigned_v<_To>))> {};
920
921template <typename _Tp>
922 struct __is_narrowing_conversion<_Tp, bool, true, true>
923 : public true_type {};
924
925template <>
926 struct __is_narrowing_conversion<bool, bool, true, true>
927 : public false_type {};
928
929template <typename _Tp>
930 struct __is_narrowing_conversion<_Tp, _Tp, true, true>
931 : public false_type {};
932
933template <typename _From, typename _To>
934 struct __is_narrowing_conversion<_From, _To, false, true>
935 : public negation<is_convertible<_From, _To>> {};
936
937// }}}
938// __converts_to_higher_integer_rank{{{
939template <typename _From, typename _To, bool = (sizeof(_From) < sizeof(_To))>
940 struct __converts_to_higher_integer_rank : public true_type {};
941
942// this may fail for char -> short if sizeof(char) == sizeof(short)
943template <typename _From, typename _To>
944 struct __converts_to_higher_integer_rank<_From, _To, false>
945 : public is_same<decltype(declval<_From>() + declval<_To>()), _To> {};
946
947// }}}
948// __data(simd/simd_mask) {{{
949template <typename _Tp, typename _Ap>
950 _GLIBCXX_SIMD_INTRINSIC constexpr const auto&
951 __data(const simd<_Tp, _Ap>& __x);
952
953template <typename _Tp, typename _Ap>
954 _GLIBCXX_SIMD_INTRINSIC constexpr auto&
955 __data(simd<_Tp, _Ap>& __x);
956
957template <typename _Tp, typename _Ap>
958 _GLIBCXX_SIMD_INTRINSIC constexpr const auto&
959 __data(const simd_mask<_Tp, _Ap>& __x);
960
961template <typename _Tp, typename _Ap>
962 _GLIBCXX_SIMD_INTRINSIC constexpr auto&
963 __data(simd_mask<_Tp, _Ap>& __x);
964
965// }}}
966// _SimdConverter {{{
967template <typename _FromT, typename _FromA, typename _ToT, typename _ToA,
968 typename = void>
969 struct _SimdConverter;
970
971template <typename _Tp, typename _Ap>
972 struct _SimdConverter<_Tp, _Ap, _Tp, _Ap, void>
973 {
974 template <typename _Up>
975 _GLIBCXX_SIMD_INTRINSIC const _Up&
976 operator()(const _Up& __x)
977 { return __x; }
978 };
979
980// }}}
981// __to_value_type_or_member_type {{{
982template <typename _V>
983 _GLIBCXX_SIMD_INTRINSIC constexpr auto
984 __to_value_type_or_member_type(const _V& __x) -> decltype(__data(__x))
985 { return __data(__x); }
986
987template <typename _V>
988 _GLIBCXX_SIMD_INTRINSIC constexpr const typename _V::value_type&
989 __to_value_type_or_member_type(const typename _V::value_type& __x)
990 { return __x; }
991
992// }}}
993// __bool_storage_member_type{{{
994template <size_t _Size>
995 struct __bool_storage_member_type;
996
997template <size_t _Size>
998 using __bool_storage_member_type_t =
999 typename __bool_storage_member_type<_Size>::type;
1000
1001// }}}
1002// _SimdTuple {{{
1003// why not tuple?
1004// 1. tuple gives no guarantee about the storage order, but I require
1005// storage
1006// equivalent to array<_Tp, _Np>
1007// 2. direct access to the element type (first template argument)
1008// 3. enforces equal element type, only different _Abi types are allowed
1009template <typename _Tp, typename... _Abis>
1010 struct _SimdTuple;
1011
1012//}}}
1013// __fixed_size_storage_t {{{
1014template <typename _Tp, int _Np>
1015 struct __fixed_size_storage;
1016
1017template <typename _Tp, int _Np>
1018 using __fixed_size_storage_t = typename __fixed_size_storage<_Tp, _Np>::type;
1019
1020// }}}
1021// _SimdWrapper fwd decl{{{
1022template <typename _Tp, size_t _Size, typename = void_t<>>
1023 struct _SimdWrapper;
1024
1025template <typename _Tp>
1026 using _SimdWrapper8 = _SimdWrapper<_Tp, 8 / sizeof(_Tp)>;
1027template <typename _Tp>
1028 using _SimdWrapper16 = _SimdWrapper<_Tp, 16 / sizeof(_Tp)>;
1029template <typename _Tp>
1030 using _SimdWrapper32 = _SimdWrapper<_Tp, 32 / sizeof(_Tp)>;
1031template <typename _Tp>
1032 using _SimdWrapper64 = _SimdWrapper<_Tp, 64 / sizeof(_Tp)>;
1033
1034template <typename _Tp, size_t _Width>
1035 struct _SveSimdWrapper;
1036
1037// }}}
1038// __is_simd_wrapper {{{
1039template <typename _Tp>
1040 struct __is_simd_wrapper : false_type {};
1041
1042template <typename _Tp, size_t _Np>
1043 struct __is_simd_wrapper<_SimdWrapper<_Tp, _Np>> : true_type {};
1044
1045template <typename _Tp>
1046 inline constexpr bool __is_simd_wrapper_v = __is_simd_wrapper<_Tp>::value;
1047
1048// }}}
1049// _BitOps {{{
1050struct _BitOps
1051{
1052 // _S_bit_iteration {{{
1053 template <typename _Tp, typename _Fp>
1054 static void
1055 _S_bit_iteration(_Tp __mask, _Fp&& __f)
1056 {
1057 static_assert(sizeof(_ULLong) >= sizeof(_Tp));
1058 conditional_t<sizeof(_Tp) <= sizeof(_UInt), _UInt, _ULLong> __k;
1059 if constexpr (is_convertible_v<_Tp, decltype(__k)>)
1060 __k = __mask;
1061 else
1062 __k = __mask.to_ullong();
1063 while(__k)
1064 {
1065 __f(std::__countr_zero(__k));
1066 __k &= (__k - 1);
1067 }
1068 }
1069
1070 //}}}
1071};
1072
1073//}}}
1074// __increment, __decrement {{{
1075template <typename _Tp = void>
1076 struct __increment
1077 { constexpr _Tp operator()(_Tp __a) const { return ++__a; } };
1078
1079template <>
1080 struct __increment<void>
1081 {
1082 template <typename _Tp>
1083 constexpr _Tp
1084 operator()(_Tp __a) const
1085 { return ++__a; }
1086 };
1087
1088template <typename _Tp = void>
1089 struct __decrement
1090 { constexpr _Tp operator()(_Tp __a) const { return --__a; } };
1091
1092template <>
1093 struct __decrement<void>
1094 {
1095 template <typename _Tp>
1096 constexpr _Tp
1097 operator()(_Tp __a) const
1098 { return --__a; }
1099 };
1100
1101// }}}
1102// _ValuePreserving(OrInt) {{{
1103template <typename _From, typename _To,
1104 typename = enable_if_t<negation<
1105 __is_narrowing_conversion<__remove_cvref_t<_From>, _To>>::value>>
1106 using _ValuePreserving = _From;
1107
1108template <typename _From, typename _To,
1109 typename _DecayedFrom = __remove_cvref_t<_From>,
1110 typename = enable_if_t<conjunction<
1111 is_convertible<_From, _To>,
1112 disjunction<
1113 is_same<_DecayedFrom, _To>, is_same<_DecayedFrom, int>,
1114 conjunction<is_same<_DecayedFrom, _UInt>, is_unsigned<_To>>,
1115 negation<__is_narrowing_conversion<_DecayedFrom, _To>>>>::value>>
1116 using _ValuePreservingOrInt = _From;
1117
1118// }}}
1119// __intrinsic_type {{{
1120template <typename _Tp, size_t _Bytes, typename = void_t<>>
1121 struct __intrinsic_type;
1122
1123template <typename _Tp, size_t _Size>
1124 using __intrinsic_type_t =
1125 typename __intrinsic_type<_Tp, _Size * sizeof(_Tp)>::type;
1126
1127template <typename _Tp>
1128 using __intrinsic_type2_t = typename __intrinsic_type<_Tp, 2>::type;
1129template <typename _Tp>
1130 using __intrinsic_type4_t = typename __intrinsic_type<_Tp, 4>::type;
1131template <typename _Tp>
1132 using __intrinsic_type8_t = typename __intrinsic_type<_Tp, 8>::type;
1133template <typename _Tp>
1134 using __intrinsic_type16_t = typename __intrinsic_type<_Tp, 16>::type;
1135template <typename _Tp>
1136 using __intrinsic_type32_t = typename __intrinsic_type<_Tp, 32>::type;
1137template <typename _Tp>
1138 using __intrinsic_type64_t = typename __intrinsic_type<_Tp, 64>::type;
1139
1140// }}}
1141// _BitMask {{{
1142template <size_t _Np, bool _Sanitized = false>
1143 struct _BitMask;
1144
1145template <size_t _Np, bool _Sanitized>
1146 struct __is_bitmask<_BitMask<_Np, _Sanitized>, void> : true_type {};
1147
1148template <size_t _Np>
1149 using _SanitizedBitMask = _BitMask<_Np, true>;
1150
1151template <size_t _Np, bool _Sanitized>
1152 struct _BitMask
1153 {
1154 static_assert(_Np > 0);
1155
1156 static constexpr size_t _NBytes = __div_roundup(_Np, __CHAR_BIT__);
1157
1158 using _Tp = conditional_t<_Np == 1, bool,
1159 make_unsigned_t<__int_with_sizeof_t<std::min(
1160 sizeof(_ULLong), std::__bit_ceil(_NBytes))>>>;
1161
1162 static constexpr int _S_array_size = __div_roundup(_NBytes, sizeof(_Tp));
1163
1164 _Tp _M_bits[_S_array_size];
1165
1166 static constexpr int _S_unused_bits
1167 = _Np == 1 ? 0 : _S_array_size * sizeof(_Tp) * __CHAR_BIT__ - _Np;
1168
1169 static constexpr _Tp _S_bitmask = +_Tp(~_Tp()) >> _S_unused_bits;
1170
1171 constexpr _BitMask() noexcept = default;
1172
1173 constexpr _BitMask(unsigned long long __x) noexcept
1174 : _M_bits{static_cast<_Tp>(__x)} {}
1175
1176 _BitMask(bitset<_Np> __x) noexcept : _BitMask(__x.to_ullong()) {}
1177
1178 constexpr _BitMask(const _BitMask&) noexcept = default;
1179
1180 template <bool _RhsSanitized, typename = enable_if_t<_RhsSanitized == false
1181 && _Sanitized == true>>
1182 constexpr _BitMask(const _BitMask<_Np, _RhsSanitized>& __rhs) noexcept
1183 : _BitMask(__rhs._M_sanitized()) {}
1184
1185 constexpr operator _SimdWrapper<bool, _Np>() const noexcept
1186 {
1187 static_assert(_S_array_size == 1);
1188 return _M_bits[0];
1189 }
1190
1191 // precondition: is sanitized
1192 constexpr _Tp
1193 _M_to_bits() const noexcept
1194 {
1195 static_assert(_S_array_size == 1);
1196 return _M_bits[0];
1197 }
1198
1199 // precondition: is sanitized
1200 constexpr unsigned long long
1201 to_ullong() const noexcept
1202 {
1203 static_assert(_S_array_size == 1);
1204 return _M_bits[0];
1205 }
1206
1207 // precondition: is sanitized
1208 constexpr unsigned long
1209 to_ulong() const noexcept
1210 {
1211 static_assert(_S_array_size == 1);
1212 return _M_bits[0];
1213 }
1214
1215 constexpr bitset<_Np>
1216 _M_to_bitset() const noexcept
1217 {
1218 static_assert(_S_array_size == 1);
1219 return _M_bits[0];
1220 }
1221
1222 constexpr decltype(auto)
1223 _M_sanitized() const noexcept
1224 {
1225 if constexpr (_Sanitized)
1226 return *this;
1227 else if constexpr (_Np == 1)
1228 return _SanitizedBitMask<_Np>(_M_bits[0]);
1229 else
1230 {
1231 _SanitizedBitMask<_Np> __r = {};
1232 for (int __i = 0; __i < _S_array_size; ++__i)
1233 __r._M_bits[__i] = _M_bits[__i];
1234 if constexpr (_S_unused_bits > 0)
1235 __r._M_bits[_S_array_size - 1] &= _S_bitmask;
1236 return __r;
1237 }
1238 }
1239
1240 template <size_t _Mp, bool _LSanitized>
1241 constexpr _BitMask<_Np + _Mp, _Sanitized>
1242 _M_prepend(_BitMask<_Mp, _LSanitized> __lsb) const noexcept
1243 {
1244 constexpr size_t _RN = _Np + _Mp;
1245 using _Rp = _BitMask<_RN, _Sanitized>;
1246 if constexpr (_Rp::_S_array_size == 1)
1247 {
1248 _Rp __r{{_M_bits[0]}};
1249 __r._M_bits[0] <<= _Mp;
1250 __r._M_bits[0] |= __lsb._M_sanitized()._M_bits[0];
1251 return __r;
1252 }
1253 else
1254 __assert_unreachable<_Rp>();
1255 }
1256
1257 // Return a new _BitMask with size _NewSize while dropping _DropLsb least
1258 // significant bits. If the operation implicitly produces a sanitized bitmask,
1259 // the result type will have _Sanitized set.
1260 template <size_t _DropLsb, size_t _NewSize = _Np - _DropLsb>
1261 constexpr auto
1262 _M_extract() const noexcept
1263 {
1264 static_assert(_Np > _DropLsb);
1265 static_assert(_DropLsb + _NewSize <= sizeof(_ULLong) * __CHAR_BIT__,
1266 "not implemented for bitmasks larger than one ullong");
1267 if constexpr (_NewSize == 1)
1268 // must sanitize because the return _Tp is bool
1269 return _SanitizedBitMask<1>(_M_bits[0] & (_Tp(1) << _DropLsb));
1270 else
1271 return _BitMask<_NewSize,
1272 ((_NewSize + _DropLsb == sizeof(_Tp) * __CHAR_BIT__
1273 && _NewSize + _DropLsb <= _Np)
1274 || ((_Sanitized || _Np == sizeof(_Tp) * __CHAR_BIT__)
1275 && _NewSize + _DropLsb >= _Np))>(_M_bits[0]
1276 >> _DropLsb);
1277 }
1278
1279 // True if all bits are set. Implicitly sanitizes if _Sanitized == false.
1280 constexpr bool
1281 all() const noexcept
1282 {
1283 if constexpr (_Np == 1)
1284 return _M_bits[0];
1285 else if constexpr (!_Sanitized)
1286 return _M_sanitized().all();
1287 else
1288 {
1289 constexpr _Tp __allbits = ~_Tp();
1290 for (int __i = 0; __i < _S_array_size - 1; ++__i)
1291 if (_M_bits[__i] != __allbits)
1292 return false;
1293 return _M_bits[_S_array_size - 1] == _S_bitmask;
1294 }
1295 }
1296
1297 // True if at least one bit is set. Implicitly sanitizes if _Sanitized ==
1298 // false.
1299 constexpr bool
1300 any() const noexcept
1301 {
1302 if constexpr (_Np == 1)
1303 return _M_bits[0];
1304 else if constexpr (!_Sanitized)
1305 return _M_sanitized().any();
1306 else
1307 {
1308 for (int __i = 0; __i < _S_array_size - 1; ++__i)
1309 if (_M_bits[__i] != 0)
1310 return true;
1311 return _M_bits[_S_array_size - 1] != 0;
1312 }
1313 }
1314
1315 // True if no bit is set. Implicitly sanitizes if _Sanitized == false.
1316 constexpr bool
1317 none() const noexcept
1318 {
1319 if constexpr (_Np == 1)
1320 return !_M_bits[0];
1321 else if constexpr (!_Sanitized)
1322 return _M_sanitized().none();
1323 else
1324 {
1325 for (int __i = 0; __i < _S_array_size - 1; ++__i)
1326 if (_M_bits[__i] != 0)
1327 return false;
1328 return _M_bits[_S_array_size - 1] == 0;
1329 }
1330 }
1331
1332 // Returns the number of set bits. Implicitly sanitizes if _Sanitized ==
1333 // false.
1334 constexpr int
1335 count() const noexcept
1336 {
1337 if constexpr (_Np == 1)
1338 return _M_bits[0];
1339 else if constexpr (!_Sanitized)
1340 return _M_sanitized().none();
1341 else
1342 {
1343 int __result = __builtin_popcountll(_M_bits[0]);
1344 for (int __i = 1; __i < _S_array_size; ++__i)
1345 __result += __builtin_popcountll(_M_bits[__i]);
1346 return __result;
1347 }
1348 }
1349
1350 // Returns the bit at offset __i as bool.
1351 constexpr bool
1352 operator[](size_t __i) const noexcept
1353 {
1354 if constexpr (_Np == 1)
1355 return _M_bits[0];
1356 else if constexpr (_S_array_size == 1)
1357 return (_M_bits[0] >> __i) & 1;
1358 else
1359 {
1360 const size_t __j = __i / (sizeof(_Tp) * __CHAR_BIT__);
1361 const size_t __shift = __i % (sizeof(_Tp) * __CHAR_BIT__);
1362 return (_M_bits[__j] >> __shift) & 1;
1363 }
1364 }
1365
1366 template <size_t __i>
1367 constexpr bool
1368 operator[](_SizeConstant<__i>) const noexcept
1369 {
1370 static_assert(__i < _Np);
1371 constexpr size_t __j = __i / (sizeof(_Tp) * __CHAR_BIT__);
1372 constexpr size_t __shift = __i % (sizeof(_Tp) * __CHAR_BIT__);
1373 return static_cast<bool>(_M_bits[__j] & (_Tp(1) << __shift));
1374 }
1375
1376 // Set the bit at offset __i to __x.
1377 constexpr void
1378 set(size_t __i, bool __x) noexcept
1379 {
1380 if constexpr (_Np == 1)
1381 _M_bits[0] = __x;
1382 else if constexpr (_S_array_size == 1)
1383 {
1384 _M_bits[0] &= ~_Tp(_Tp(1) << __i);
1385 _M_bits[0] |= _Tp(_Tp(__x) << __i);
1386 }
1387 else
1388 {
1389 const size_t __j = __i / (sizeof(_Tp) * __CHAR_BIT__);
1390 const size_t __shift = __i % (sizeof(_Tp) * __CHAR_BIT__);
1391 _M_bits[__j] &= ~_Tp(_Tp(1) << __shift);
1392 _M_bits[__j] |= _Tp(_Tp(__x) << __shift);
1393 }
1394 }
1395
1396 template <size_t __i>
1397 constexpr void
1398 set(_SizeConstant<__i>, bool __x) noexcept
1399 {
1400 static_assert(__i < _Np);
1401 if constexpr (_Np == 1)
1402 _M_bits[0] = __x;
1403 else
1404 {
1405 constexpr size_t __j = __i / (sizeof(_Tp) * __CHAR_BIT__);
1406 constexpr size_t __shift = __i % (sizeof(_Tp) * __CHAR_BIT__);
1407 constexpr _Tp __mask = ~_Tp(_Tp(1) << __shift);
1408 _M_bits[__j] &= __mask;
1409 _M_bits[__j] |= _Tp(_Tp(__x) << __shift);
1410 }
1411 }
1412
1413 // Inverts all bits. Sanitized input leads to sanitized output.
1414 constexpr _BitMask
1415 operator~() const noexcept
1416 {
1417 if constexpr (_Np == 1)
1418 return !_M_bits[0];
1419 else
1420 {
1421 _BitMask __result{};
1422 for (int __i = 0; __i < _S_array_size - 1; ++__i)
1423 __result._M_bits[__i] = ~_M_bits[__i];
1424 if constexpr (_Sanitized)
1425 __result._M_bits[_S_array_size - 1]
1426 = _M_bits[_S_array_size - 1] ^ _S_bitmask;
1427 else
1428 __result._M_bits[_S_array_size - 1] = ~_M_bits[_S_array_size - 1];
1429 return __result;
1430 }
1431 }
1432
1433 constexpr _BitMask&
1434 operator^=(const _BitMask& __b) & noexcept
1435 {
1436 __execute_n_times<_S_array_size>(
1437 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { _M_bits[__i] ^= __b._M_bits[__i]; });
1438 return *this;
1439 }
1440
1441 constexpr _BitMask&
1442 operator|=(const _BitMask& __b) & noexcept
1443 {
1444 __execute_n_times<_S_array_size>(
1445 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { _M_bits[__i] |= __b._M_bits[__i]; });
1446 return *this;
1447 }
1448
1449 constexpr _BitMask&
1450 operator&=(const _BitMask& __b) & noexcept
1451 {
1452 __execute_n_times<_S_array_size>(
1453 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { _M_bits[__i] &= __b._M_bits[__i]; });
1454 return *this;
1455 }
1456
1457 friend constexpr _BitMask
1458 operator^(const _BitMask& __a, const _BitMask& __b) noexcept
1459 {
1460 _BitMask __r = __a;
1461 __r ^= __b;
1462 return __r;
1463 }
1464
1465 friend constexpr _BitMask
1466 operator|(const _BitMask& __a, const _BitMask& __b) noexcept
1467 {
1468 _BitMask __r = __a;
1469 __r |= __b;
1470 return __r;
1471 }
1472
1473 friend constexpr _BitMask
1474 operator&(const _BitMask& __a, const _BitMask& __b) noexcept
1475 {
1476 _BitMask __r = __a;
1477 __r &= __b;
1478 return __r;
1479 }
1480
1481 _GLIBCXX_SIMD_INTRINSIC
1482 constexpr bool
1483 _M_is_constprop() const
1484 {
1485 if constexpr (_S_array_size == 0)
1486 return __builtin_constant_p(_M_bits[0]);
1487 else
1488 {
1489 for (int __i = 0; __i < _S_array_size; ++__i)
1490 if (!__builtin_constant_p(_M_bits[__i]))
1491 return false;
1492 return true;
1493 }
1494 }
1495 };
1496
1497// }}}
1498
1499// vvv ---- builtin vector types [[gnu::vector_size(N)]] and operations ---- vvv
1500// __min_vector_size {{{
1501template <typename _Tp = void>
1502 static inline constexpr int __min_vector_size = 2 * sizeof(_Tp);
1503
1504#if _GLIBCXX_SIMD_HAVE_NEON
1505template <>
1506 inline constexpr int __min_vector_size<void> = 8;
1507#else
1508template <>
1509 inline constexpr int __min_vector_size<void> = 16;
1510#endif
1511
1512// }}}
1513// __vector_type {{{
1514template <typename _Tp, size_t _Np, typename = void>
1515 struct __vector_type_n {};
1516
1517// substitution failure for 0-element case
1518template <typename _Tp>
1519 struct __vector_type_n<_Tp, 0, void> {};
1520
1521// special case 1-element to be _Tp itself
1522template <typename _Tp>
1523 struct __vector_type_n<_Tp, 1, enable_if_t<__is_vectorizable_v<_Tp>>>
1524 { using type = _Tp; };
1525
1526// else, use GNU-style builtin vector types
1527template <typename _Tp, size_t _Np>
1528 struct __vector_type_n<_Tp, _Np, enable_if_t<__is_vectorizable_v<_Tp> && _Np >= 2>>
1529 {
1530 static constexpr size_t _S_Np2 = std::__bit_ceil(_Np * sizeof(_Tp));
1531
1532 static constexpr size_t _S_Bytes =
1533#ifdef __i386__
1534 // Using [[gnu::vector_size(8)]] would wreak havoc on the FPU because
1535 // those objects are passed via MMX registers and nothing ever calls EMMS.
1536 _S_Np2 == 8 ? 16 :
1537#endif
1538 _S_Np2 < __min_vector_size<_Tp> ? __min_vector_size<_Tp>
1539 : _S_Np2;
1540
1541 using type [[__gnu__::__vector_size__(_S_Bytes)]] = _Tp;
1542 };
1543
1544template <typename _Tp, size_t _Bytes, size_t = _Bytes % sizeof(_Tp)>
1545 struct __vector_type;
1546
1547template <typename _Tp, size_t _Bytes>
1548 struct __vector_type<_Tp, _Bytes, 0>
1549 : __vector_type_n<_Tp, _Bytes / sizeof(_Tp)> {};
1550
1551template <typename _Tp, size_t _Size>
1552 using __vector_type_t = typename __vector_type_n<_Tp, _Size>::type;
1553
1554template <typename _Tp>
1555 using __vector_type2_t = typename __vector_type<_Tp, 2>::type;
1556template <typename _Tp>
1557 using __vector_type4_t = typename __vector_type<_Tp, 4>::type;
1558template <typename _Tp>
1559 using __vector_type8_t = typename __vector_type<_Tp, 8>::type;
1560template <typename _Tp>
1561 using __vector_type16_t = typename __vector_type<_Tp, 16>::type;
1562template <typename _Tp>
1563 using __vector_type32_t = typename __vector_type<_Tp, 32>::type;
1564template <typename _Tp>
1565 using __vector_type64_t = typename __vector_type<_Tp, 64>::type;
1566
1567// }}}
1568// __is_vector_type {{{
1569template <typename _Tp, typename = void_t<>>
1570 struct __is_vector_type : false_type {};
1571
1572template <typename _Tp>
1573 struct __is_vector_type<
1574 _Tp, void_t<typename __vector_type<
1575 remove_reference_t<decltype(declval<_Tp>()[0])>, sizeof(_Tp)>::type>>
1576 : is_same<_Tp, typename __vector_type<
1577 remove_reference_t<decltype(declval<_Tp>()[0])>,
1578 sizeof(_Tp)>::type> {};
1579
1580template <typename _Tp>
1581 inline constexpr bool __is_vector_type_v = __is_vector_type<_Tp>::value;
1582
1583// }}}
1584// __is_intrinsic_type {{{
1585#if _GLIBCXX_SIMD_HAVE_SSE_ABI
1586template <typename _Tp>
1587 using __is_intrinsic_type = __is_vector_type<_Tp>;
1588#else // not SSE (x86)
1589template <typename _Tp, typename = void_t<>>
1590 struct __is_intrinsic_type : false_type {};
1591
1592template <typename _Tp>
1593 struct __is_intrinsic_type<
1594 _Tp, void_t<typename __intrinsic_type<
1595 remove_reference_t<decltype(declval<_Tp>()[0])>, sizeof(_Tp)>::type>>
1596 : is_same<_Tp, typename __intrinsic_type<
1597 remove_reference_t<decltype(declval<_Tp>()[0])>,
1598 sizeof(_Tp)>::type> {};
1599#endif
1600
1601template <typename _Tp>
1602 inline constexpr bool __is_intrinsic_type_v = __is_intrinsic_type<_Tp>::value;
1603
1604// }}}
1605// _VectorTraits{{{
1606template <typename _Tp, typename = void_t<>>
1607 struct _VectorTraitsImpl;
1608
1609template <typename _Tp>
1610 struct _VectorTraitsImpl<_Tp, enable_if_t<__is_vector_type_v<_Tp>
1611 || __is_intrinsic_type_v<_Tp>>>
1612 {
1613 using type = _Tp;
1614 using value_type = remove_reference_t<decltype(declval<_Tp>()[0])>;
1615 static constexpr int _S_full_size = sizeof(_Tp) / sizeof(value_type);
1616 using _Wrapper = _SimdWrapper<value_type, _S_full_size>;
1617 template <typename _Up, int _W = _S_full_size>
1618 static constexpr bool _S_is
1619 = is_same_v<value_type, _Up> && _W == _S_full_size;
1620 };
1621
1622template <typename _Tp, size_t _Np>
1623 struct _VectorTraitsImpl<_SimdWrapper<_Tp, _Np>,
1624 void_t<__vector_type_t<_Tp, _Np>>>
1625 {
1626 using type = __vector_type_t<_Tp, _Np>;
1627 using value_type = _Tp;
1628 static constexpr int _S_full_size = sizeof(type) / sizeof(value_type);
1629 using _Wrapper = _SimdWrapper<_Tp, _Np>;
1630 static constexpr bool _S_is_partial = (_Np == _S_full_size);
1631 static constexpr int _S_partial_width = _Np;
1632 template <typename _Up, int _W = _S_full_size>
1633 static constexpr bool _S_is
1634 = is_same_v<value_type, _Up>&& _W == _S_full_size;
1635 };
1636
1637template <typename _Tp, typename = typename _VectorTraitsImpl<_Tp>::type>
1638 using _VectorTraits = _VectorTraitsImpl<_Tp>;
1639
1640// }}}
1641// __as_vector{{{
1642template <typename _V>
1643 _GLIBCXX_SIMD_INTRINSIC constexpr auto
1644 __as_vector(_V __x)
1645 {
1646 if constexpr (__is_vector_type_v<_V>)
1647 return __x;
1648 else if constexpr (is_simd<_V>::value || is_simd_mask<_V>::value)
1649 {
1650 if constexpr (__is_fixed_size_abi_v<typename _V::abi_type>)
1651 {
1652 static_assert(is_simd<_V>::value);
1653 static_assert(_V::abi_type::template __traits<
1654 typename _V::value_type>::_SimdMember::_S_tuple_size == 1);
1655 return __as_vector(__data(__x).first);
1656 }
1657 else if constexpr (_V::size() > 1)
1658 return __data(__x)._M_data;
1659 else
1660 {
1661 static_assert(is_simd<_V>::value);
1662 using _Tp = typename _V::value_type;
1663#ifdef __i386__
1664 constexpr auto __bytes = sizeof(_Tp) == 8 ? 16 : sizeof(_Tp);
1665 using _RV [[__gnu__::__vector_size__(__bytes)]] = _Tp;
1666#else
1667 using _RV [[__gnu__::__vector_size__(sizeof(_Tp))]] = _Tp;
1668#endif
1669 return _RV{__data(__x)};
1670 }
1671 }
1672 else if constexpr (__is_vectorizable_v<_V>)
1673 return __vector_type_t<_V, 2>{__x};
1674 else
1675 return __x._M_data;
1676 }
1677
1678// }}}
1679// __as_wrapper{{{
1680template <size_t _Np = 0, typename _V>
1681 _GLIBCXX_SIMD_INTRINSIC constexpr auto
1682 __as_wrapper(_V __x)
1683 {
1684 if constexpr (__is_vector_type_v<_V>)
1685 return _SimdWrapper<typename _VectorTraits<_V>::value_type,
1686 (_Np > 0 ? _Np : _VectorTraits<_V>::_S_full_size)>(__x);
1687 else if constexpr (is_simd<_V>::value || is_simd_mask<_V>::value)
1688 {
1689 static_assert(_V::size() == _Np);
1690 return __data(__x);
1691 }
1692 else
1693 {
1694 static_assert(_V::_S_size == _Np);
1695 return __x;
1696 }
1697 }
1698
1699// }}}
1700// __intrin_bitcast{{{
1701template <typename _To, typename _From>
1702 _GLIBCXX_SIMD_INTRINSIC constexpr _To
1703 __intrin_bitcast(_From __v)
1704 {
1705 static_assert((__is_vector_type_v<_From> || __is_intrinsic_type_v<_From>)
1706 && (__is_vector_type_v<_To> || __is_intrinsic_type_v<_To>));
1707 if constexpr (sizeof(_To) == sizeof(_From))
1708 return reinterpret_cast<_To>(__v);
1709 else if constexpr (sizeof(_From) > sizeof(_To))
1710 if constexpr (sizeof(_To) >= 16)
1711 return reinterpret_cast<const __may_alias<_To>&>(__v);
1712 else
1713 {
1714 _To __r;
1715 __builtin_memcpy(&__r, &__v, sizeof(_To));
1716 return __r;
1717 }
1718#if _GLIBCXX_SIMD_X86INTRIN && !defined _GLIBCXX_CLANG
1719 else if constexpr (__have_avx && sizeof(_From) == 16 && sizeof(_To) == 32)
1720 return reinterpret_cast<_To>(__builtin_ia32_ps256_ps(
1721 reinterpret_cast<__vector_type_t<float, 4>>(__v)));
1722 else if constexpr (__have_avx512f && sizeof(_From) == 16
1723 && sizeof(_To) == 64)
1724 return reinterpret_cast<_To>(__builtin_ia32_ps512_ps(
1725 reinterpret_cast<__vector_type_t<float, 4>>(__v)));
1726 else if constexpr (__have_avx512f && sizeof(_From) == 32
1727 && sizeof(_To) == 64)
1728 return reinterpret_cast<_To>(__builtin_ia32_ps512_256ps(
1729 reinterpret_cast<__vector_type_t<float, 8>>(__v)));
1730#endif // _GLIBCXX_SIMD_X86INTRIN
1731 else if constexpr (sizeof(__v) <= 8)
1732 return reinterpret_cast<_To>(
1733 __vector_type_t<__int_for_sizeof_t<_From>, sizeof(_To) / sizeof(_From)>{
1734 reinterpret_cast<__int_for_sizeof_t<_From>>(__v)});
1735 else
1736 {
1737 static_assert(sizeof(_To) > sizeof(_From));
1738 _To __r = {};
1739 __builtin_memcpy(&__r, &__v, sizeof(_From));
1740 return __r;
1741 }
1742 }
1743
1744// }}}
1745// __vector_bitcast{{{
1746template <typename _To, size_t _NN = 0, typename _From,
1747 typename _FromVT = _VectorTraits<_From>,
1748 size_t _Np = _NN == 0 ? sizeof(_From) / sizeof(_To) : _NN>
1749 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_To, _Np>
1750 __vector_bitcast(_From __x)
1751 {
1752 using _R = __vector_type_t<_To, _Np>;
1753 return __intrin_bitcast<_R>(__x);
1754 }
1755
1756template <typename _To, size_t _NN = 0, typename _Tp, size_t _Nx,
1757 size_t _Np
1758 = _NN == 0 ? sizeof(_SimdWrapper<_Tp, _Nx>) / sizeof(_To) : _NN>
1759 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_To, _Np>
1760 __vector_bitcast(const _SimdWrapper<_Tp, _Nx>& __x)
1761 {
1762 static_assert(_Np > 1);
1763 return __intrin_bitcast<__vector_type_t<_To, _Np>>(__x._M_data);
1764 }
1765
1766// }}}
1767// __convert_x86 declarations {{{
1768#ifdef _GLIBCXX_SIMD_WORKAROUND_PR85048
1769template <typename _To, typename _Tp, typename _TVT = _VectorTraits<_Tp>>
1770 _To __convert_x86(_Tp);
1771
1772template <typename _To, typename _Tp, typename _TVT = _VectorTraits<_Tp>>
1773 _To __convert_x86(_Tp, _Tp);
1774
1775template <typename _To, typename _Tp, typename _TVT = _VectorTraits<_Tp>>
1776 _To __convert_x86(_Tp, _Tp, _Tp, _Tp);
1777
1778template <typename _To, typename _Tp, typename _TVT = _VectorTraits<_Tp>>
1779 _To __convert_x86(_Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp);
1780
1781template <typename _To, typename _Tp, typename _TVT = _VectorTraits<_Tp>>
1782 _To __convert_x86(_Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp, _Tp,
1783 _Tp, _Tp, _Tp, _Tp);
1784#endif // _GLIBCXX_SIMD_WORKAROUND_PR85048
1785
1786//}}}
1787// __bit_cast {{{
1788template <typename _To, typename _From>
1789 _GLIBCXX_SIMD_INTRINSIC constexpr _To
1790 __bit_cast(const _From __x)
1791 {
1792#if __has_builtin(__builtin_bit_cast)
1793 return __builtin_bit_cast(_To, __x);
1794#else
1795 static_assert(sizeof(_To) == sizeof(_From));
1796 constexpr bool __to_is_vectorizable
1797 = is_arithmetic_v<_To> || is_enum_v<_To>;
1798 constexpr bool __from_is_vectorizable
1799 = is_arithmetic_v<_From> || is_enum_v<_From>;
1800 if constexpr (__is_vector_type_v<_To> && __is_vector_type_v<_From>)
1801 return reinterpret_cast<_To>(__x);
1802 else if constexpr (__is_vector_type_v<_To> && __from_is_vectorizable)
1803 {
1804 using _FV [[__gnu__::__vector_size__(sizeof(_From))]] = _From;
1805 return reinterpret_cast<_To>(_FV{__x});
1806 }
1807 else if constexpr (__to_is_vectorizable && __from_is_vectorizable)
1808 {
1809 using _TV [[__gnu__::__vector_size__(sizeof(_To))]] = _To;
1810 using _FV [[__gnu__::__vector_size__(sizeof(_From))]] = _From;
1811 return reinterpret_cast<_TV>(_FV{__x})[0];
1812 }
1813 else if constexpr (__to_is_vectorizable && __is_vector_type_v<_From>)
1814 {
1815 using _TV [[__gnu__::__vector_size__(sizeof(_To))]] = _To;
1816 return reinterpret_cast<_TV>(__x)[0];
1817 }
1818 else
1819 {
1820 _To __r;
1821 __builtin_memcpy(reinterpret_cast<char*>(&__r),
1822 reinterpret_cast<const char*>(&__x), sizeof(_To));
1823 return __r;
1824 }
1825#endif
1826 }
1827
1828// }}}
1829// __to_intrin {{{
1830template <typename _Tp, typename _TVT = _VectorTraits<_Tp>,
1831 typename _R = __intrinsic_type_t<typename _TVT::value_type, _TVT::_S_full_size>>
1832 _GLIBCXX_SIMD_INTRINSIC constexpr _R
1833 __to_intrin(_Tp __x)
1834 {
1835 static_assert(sizeof(__x) <= sizeof(_R),
1836 "__to_intrin may never drop values off the end");
1837 if constexpr (sizeof(__x) == sizeof(_R))
1838 return reinterpret_cast<_R>(__as_vector(__x));
1839 else
1840 {
1841 using _Up = __int_for_sizeof_t<_Tp>;
1842 return reinterpret_cast<_R>(
1843 __vector_type_t<_Up, sizeof(_R) / sizeof(_Up)>{__bit_cast<_Up>(__x)});
1844 }
1845 }
1846
1847// }}}
1848// __make_vector{{{
1849template <typename _Tp, typename... _Args>
1850 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_Tp, sizeof...(_Args)>
1851 __make_vector(const _Args&... __args)
1852 { return __vector_type_t<_Tp, sizeof...(_Args)>{static_cast<_Tp>(__args)...}; }
1853
1854// }}}
1855// __vector_broadcast{{{
1856template <size_t _Np, typename _Tp, size_t... _I>
1857 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_Tp, _Np>
1858 __vector_broadcast_impl(_Tp __x, index_sequence<_I...>)
1859 { return __vector_type_t<_Tp, _Np>{((void)_I, __x)...}; }
1860
1861template <size_t _Np, typename _Tp>
1862 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_Tp, _Np>
1863 __vector_broadcast(_Tp __x)
1864 { return __vector_broadcast_impl<_Np, _Tp>(__x, make_index_sequence<_Np>()); }
1865
1866// }}}
1867// __generate_vector{{{
1868 template <typename _Tp, size_t _Np, typename _Gp, size_t... _I>
1869 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_Tp, _Np>
1870 __generate_vector_impl(_Gp&& __gen, index_sequence<_I...>)
1871 { return __vector_type_t<_Tp, _Np>{ static_cast<_Tp>(__gen(_SizeConstant<_I>()))...}; }
1872
1873template <typename _V, typename _VVT = _VectorTraits<_V>, typename _Gp>
1874 _GLIBCXX_SIMD_INTRINSIC constexpr _V
1875 __generate_vector(_Gp&& __gen)
1876 {
1877 if constexpr (__is_vector_type_v<_V>)
1878 return __generate_vector_impl<typename _VVT::value_type,
1879 _VVT::_S_full_size>(
1880 static_cast<_Gp&&>(__gen), make_index_sequence<_VVT::_S_full_size>());
1881 else
1882 return __generate_vector_impl<typename _VVT::value_type,
1883 _VVT::_S_partial_width>(
1884 static_cast<_Gp&&>(__gen),
1885 make_index_sequence<_VVT::_S_partial_width>());
1886 }
1887
1888template <typename _Tp, size_t _Np, typename _Gp>
1889 _GLIBCXX_SIMD_INTRINSIC constexpr __vector_type_t<_Tp, _Np>
1890 __generate_vector(_Gp&& __gen)
1891 {
1892 return __generate_vector_impl<_Tp, _Np>(static_cast<_Gp&&>(__gen),
1893 make_index_sequence<_Np>());
1894 }
1895
1896// }}}
1897// __xor{{{
1898template <typename _TW>
1899 _GLIBCXX_SIMD_INTRINSIC constexpr _TW
1900 __xor(_TW __a, _TW __b) noexcept
1901 {
1902 if constexpr (__is_vector_type_v<_TW> || __is_simd_wrapper_v<_TW>)
1903 {
1904 using _Tp = typename conditional_t<__is_simd_wrapper_v<_TW>, _TW,
1905 _VectorTraitsImpl<_TW>>::value_type;
1906 if constexpr (is_floating_point_v<_Tp>)
1907 {
1908 using _Ip = make_unsigned_t<__int_for_sizeof_t<_Tp>>;
1909 return __vector_bitcast<_Tp>(__vector_bitcast<_Ip>(__a)
1910 ^ __vector_bitcast<_Ip>(__b));
1911 }
1912 else if constexpr (__is_vector_type_v<_TW>)
1913 return __a ^ __b;
1914 else
1915 return __a._M_data ^ __b._M_data;
1916 }
1917 else
1918 return __a ^ __b;
1919 }
1920
1921// }}}
1922// __or{{{
1923template <typename _TW>
1924 _GLIBCXX_SIMD_INTRINSIC constexpr _TW
1925 __or(_TW __a, _TW __b) noexcept
1926 {
1927 if constexpr (__is_vector_type_v<_TW> || __is_simd_wrapper_v<_TW>)
1928 {
1929 using _Tp = typename conditional_t<__is_simd_wrapper_v<_TW>, _TW,
1930 _VectorTraitsImpl<_TW>>::value_type;
1931 if constexpr (is_floating_point_v<_Tp>)
1932 {
1933 using _Ip = make_unsigned_t<__int_for_sizeof_t<_Tp>>;
1934 return __vector_bitcast<_Tp>(__vector_bitcast<_Ip>(__a)
1935 | __vector_bitcast<_Ip>(__b));
1936 }
1937 else if constexpr (__is_vector_type_v<_TW>)
1938 return __a | __b;
1939 else
1940 return __a._M_data | __b._M_data;
1941 }
1942 else
1943 return __a | __b;
1944 }
1945
1946// }}}
1947// __and{{{
1948template <typename _TW>
1949 _GLIBCXX_SIMD_INTRINSIC constexpr _TW
1950 __and(_TW __a, _TW __b) noexcept
1951 {
1952 if constexpr (__is_vector_type_v<_TW> || __is_simd_wrapper_v<_TW>)
1953 {
1954 using _Tp = typename conditional_t<__is_simd_wrapper_v<_TW>, _TW,
1955 _VectorTraitsImpl<_TW>>::value_type;
1956 if constexpr (is_floating_point_v<_Tp>)
1957 {
1958 using _Ip = make_unsigned_t<__int_for_sizeof_t<_Tp>>;
1959 return __vector_bitcast<_Tp>(__vector_bitcast<_Ip>(__a)
1960 & __vector_bitcast<_Ip>(__b));
1961 }
1962 else if constexpr (__is_vector_type_v<_TW>)
1963 return __a & __b;
1964 else
1965 return __a._M_data & __b._M_data;
1966 }
1967 else
1968 return __a & __b;
1969 }
1970
1971// }}}
1972// __andnot{{{
1973#if _GLIBCXX_SIMD_X86INTRIN && !defined _GLIBCXX_CLANG
1974static constexpr struct
1975{
1976 _GLIBCXX_SIMD_INTRINSIC __v4sf
1977 operator()(__v4sf __a, __v4sf __b) const noexcept
1978 { return __builtin_ia32_andnps(__a, __b); }
1979
1980 _GLIBCXX_SIMD_INTRINSIC __v2df
1981 operator()(__v2df __a, __v2df __b) const noexcept
1982 { return __builtin_ia32_andnpd(__a, __b); }
1983
1984 _GLIBCXX_SIMD_INTRINSIC __v2di
1985 operator()(__v2di __a, __v2di __b) const noexcept
1986 { return __builtin_ia32_pandn128(__a, __b); }
1987
1988 _GLIBCXX_SIMD_INTRINSIC __v8sf
1989 operator()(__v8sf __a, __v8sf __b) const noexcept
1990 { return __builtin_ia32_andnps256(__a, __b); }
1991
1992 _GLIBCXX_SIMD_INTRINSIC __v4df
1993 operator()(__v4df __a, __v4df __b) const noexcept
1994 { return __builtin_ia32_andnpd256(__a, __b); }
1995
1996 _GLIBCXX_SIMD_INTRINSIC __v4di
1997 operator()(__v4di __a, __v4di __b) const noexcept
1998 {
1999 if constexpr (__have_avx2)
2000 return __builtin_ia32_andnotsi256(__a, __b);
2001 else
2002 return reinterpret_cast<__v4di>(
2003 __builtin_ia32_andnpd256(reinterpret_cast<__v4df>(__a),
2004 reinterpret_cast<__v4df>(__b)));
2005 }
2006
2007 _GLIBCXX_SIMD_INTRINSIC __v16sf
2008 operator()(__v16sf __a, __v16sf __b) const noexcept
2009 {
2010 if constexpr (__have_avx512dq)
2011 return _mm512_andnot_ps(__a, __b);
2012 else
2013 return reinterpret_cast<__v16sf>(
2014 _mm512_andnot_si512(reinterpret_cast<__v8di>(__a),
2015 reinterpret_cast<__v8di>(__b)));
2016 }
2017
2018 _GLIBCXX_SIMD_INTRINSIC __v8df
2019 operator()(__v8df __a, __v8df __b) const noexcept
2020 {
2021 if constexpr (__have_avx512dq)
2022 return _mm512_andnot_pd(__a, __b);
2023 else
2024 return reinterpret_cast<__v8df>(
2025 _mm512_andnot_si512(reinterpret_cast<__v8di>(__a),
2026 reinterpret_cast<__v8di>(__b)));
2027 }
2028
2029 _GLIBCXX_SIMD_INTRINSIC __v8di
2030 operator()(__v8di __a, __v8di __b) const noexcept
2031 { return _mm512_andnot_si512(__a, __b); }
2032} _S_x86_andnot;
2033#endif // _GLIBCXX_SIMD_X86INTRIN && !_GLIBCXX_CLANG
2034
2035template <typename _TW>
2036 _GLIBCXX_SIMD_INTRINSIC constexpr _TW
2037 __andnot(_TW __a, _TW __b) noexcept
2038 {
2039 if constexpr (__is_vector_type_v<_TW> || __is_simd_wrapper_v<_TW>)
2040 {
2041 using _TVT = conditional_t<__is_simd_wrapper_v<_TW>, _TW,
2042 _VectorTraitsImpl<_TW>>;
2043 using _Tp = typename _TVT::value_type;
2044#if _GLIBCXX_SIMD_X86INTRIN && !defined _GLIBCXX_CLANG
2045 if constexpr (sizeof(_TW) >= 16)
2046 {
2047 const auto __ai = __to_intrin(__a);
2048 const auto __bi = __to_intrin(__b);
2049 if (!__builtin_is_constant_evaluated()
2050 && !(__builtin_constant_p(__ai) && __builtin_constant_p(__bi)))
2051 {
2052 const auto __r = _S_x86_andnot(__ai, __bi);
2053 if constexpr (is_convertible_v<decltype(__r), _TW>)
2054 return __r;
2055 else
2056 return reinterpret_cast<typename _TVT::type>(__r);
2057 }
2058 }
2059#endif // _GLIBCXX_SIMD_X86INTRIN
2060 using _Ip = make_unsigned_t<__int_for_sizeof_t<_Tp>>;
2061 return __vector_bitcast<_Tp>(~__vector_bitcast<_Ip>(__a)
2062 & __vector_bitcast<_Ip>(__b));
2063 }
2064 else
2065 return ~__a & __b;
2066 }
2067
2068// }}}
2069// __not{{{
2070template <typename _Tp, typename _TVT = _VectorTraits<_Tp>>
2071 _GLIBCXX_SIMD_INTRINSIC constexpr _Tp
2072 __not(_Tp __a) noexcept
2073 {
2074 if constexpr (is_floating_point_v<typename _TVT::value_type>)
2075 return reinterpret_cast<typename _TVT::type>(
2076 ~__vector_bitcast<unsigned>(__a));
2077 else
2078 return ~__a;
2079 }
2080
2081// }}}
2082// __vec_shuffle{{{
2083template <typename _T0, typename _T1, typename _Fun, size_t... _Is>
2084 _GLIBCXX_SIMD_INTRINSIC constexpr
2085 __vector_type_t<remove_reference_t<decltype(declval<_T0>()[0])>, sizeof...(_Is)>
2086 __vec_shuffle(_T0 __x, _T1 __y, index_sequence<_Is...> __seq, _Fun __idx_perm)
2087 {
2088 constexpr int _N0 = sizeof(__x) / sizeof(__x[0]);
2089 constexpr int _N1 = sizeof(__y) / sizeof(__y[0]);
2090 using _Tp = remove_reference_t<decltype(declval<_T0>()[0])>;
2091 using _RV [[maybe_unused]] = __vector_type_t<_Tp, sizeof...(_Is)>;
2092#if __has_builtin(__builtin_shufflevector)
2093#ifdef _GLIBCXX_CLANG
2094 // Clang requires _T0 == _T1
2095 if constexpr (sizeof(__x) > sizeof(__y) and _N1 == 1)
2096 return __vec_shuffle(__x, _T0{__y[0]}, __seq, __idx_perm);
2097 else if constexpr (sizeof(__x) > sizeof(__y))
2098 return __vec_shuffle(__x, __intrin_bitcast<_T0>(__y), __seq, __idx_perm);
2099 else if constexpr (sizeof(__x) < sizeof(__y) and _N0 == 1)
2100 return __vec_shuffle(_T1{__x[0]}, __y, __seq, [=](int __i) {
2101 __i = __idx_perm(__i);
2102 return __i < _N0 ? __i : __i - _N0 + _N1;
2103 });
2104 else if constexpr (sizeof(__x) < sizeof(__y))
2105 return __vec_shuffle(__intrin_bitcast<_T1>(__x), __y, __seq, [=](int __i) {
2106 __i = __idx_perm(__i);
2107 return __i < _N0 ? __i : __i - _N0 + _N1;
2108 });
2109 else
2110#endif
2111 {
2112 const auto __r = __builtin_shufflevector(__x, __y, [=] {
2113 constexpr int __j = __idx_perm(_Is);
2114 static_assert(__j < _N0 + _N1);
2115 return __j;
2116 }()...);
2117#ifdef __i386__
2118 if constexpr (sizeof(__r) == sizeof(_RV))
2119 return __r;
2120 else
2121 return _RV {__r[_Is]...};
2122#else
2123 return __r;
2124#endif
2125 }
2126#else
2127 return _RV {
2128 [=]() -> _Tp {
2129 constexpr int __j = __idx_perm(_Is);
2130 static_assert(__j < _N0 + _N1);
2131 if constexpr (__j < 0)
2132 return 0;
2133 else if constexpr (__j < _N0)
2134 return __x[__j];
2135 else
2136 return __y[__j - _N0];
2137 }()...
2138 };
2139#endif
2140 }
2141
2142template <typename _T0, typename _Fun, typename _Seq>
2143 _GLIBCXX_SIMD_INTRINSIC constexpr auto
2144 __vec_shuffle(_T0 __x, _Seq __seq, _Fun __idx_perm)
2145 { return __vec_shuffle(__x, _T0(), __seq, __idx_perm); }
2146
2147// }}}
2148// __concat{{{
2149template <typename _Tp, typename _TVT = _VectorTraits<_Tp>,
2150 typename _R = __vector_type_t<typename _TVT::value_type, _TVT::_S_full_size * 2>>
2151 constexpr _R
2152 __concat(_Tp a_, _Tp b_)
2153 {
2154#ifdef _GLIBCXX_SIMD_WORKAROUND_XXX_1
2155 using _W
2156 = conditional_t<is_floating_point_v<typename _TVT::value_type>, double,
2157 conditional_t<(sizeof(_Tp) >= 2 * sizeof(long long)),
2158 long long, typename _TVT::value_type>>;
2159 constexpr int input_width = sizeof(_Tp) / sizeof(_W);
2160 const auto __a = __vector_bitcast<_W>(a_);
2161 const auto __b = __vector_bitcast<_W>(b_);
2162 using _Up = __vector_type_t<_W, sizeof(_R) / sizeof(_W)>;
2163#else
2164 constexpr int input_width = _TVT::_S_full_size;
2165 const _Tp& __a = a_;
2166 const _Tp& __b = b_;
2167 using _Up = _R;
2168#endif
2169 if constexpr (input_width == 2)
2170 return reinterpret_cast<_R>(_Up{__a[0], __a[1], __b[0], __b[1]});
2171 else if constexpr (input_width == 4)
2172 return reinterpret_cast<_R>(
2173 _Up{__a[0], __a[1], __a[2], __a[3], __b[0], __b[1], __b[2], __b[3]});
2174 else if constexpr (input_width == 8)
2175 return reinterpret_cast<_R>(
2176 _Up{__a[0], __a[1], __a[2], __a[3], __a[4], __a[5], __a[6], __a[7],
2177 __b[0], __b[1], __b[2], __b[3], __b[4], __b[5], __b[6], __b[7]});
2178 else if constexpr (input_width == 16)
2179 return reinterpret_cast<_R>(
2180 _Up{__a[0], __a[1], __a[2], __a[3], __a[4], __a[5], __a[6],
2181 __a[7], __a[8], __a[9], __a[10], __a[11], __a[12], __a[13],
2182 __a[14], __a[15], __b[0], __b[1], __b[2], __b[3], __b[4],
2183 __b[5], __b[6], __b[7], __b[8], __b[9], __b[10], __b[11],
2184 __b[12], __b[13], __b[14], __b[15]});
2185 else if constexpr (input_width == 32)
2186 return reinterpret_cast<_R>(
2187 _Up{__a[0], __a[1], __a[2], __a[3], __a[4], __a[5], __a[6],
2188 __a[7], __a[8], __a[9], __a[10], __a[11], __a[12], __a[13],
2189 __a[14], __a[15], __a[16], __a[17], __a[18], __a[19], __a[20],
2190 __a[21], __a[22], __a[23], __a[24], __a[25], __a[26], __a[27],
2191 __a[28], __a[29], __a[30], __a[31], __b[0], __b[1], __b[2],
2192 __b[3], __b[4], __b[5], __b[6], __b[7], __b[8], __b[9],
2193 __b[10], __b[11], __b[12], __b[13], __b[14], __b[15], __b[16],
2194 __b[17], __b[18], __b[19], __b[20], __b[21], __b[22], __b[23],
2195 __b[24], __b[25], __b[26], __b[27], __b[28], __b[29], __b[30],
2196 __b[31]});
2197 }
2198
2199// }}}
2200// __zero_extend {{{
2201template <typename _Tp, typename _TVT = _VectorTraits<_Tp>>
2202 struct _ZeroExtendProxy
2203 {
2204 using value_type = typename _TVT::value_type;
2205 static constexpr size_t _Np = _TVT::_S_full_size;
2206 const _Tp __x;
2207
2208 template <typename _To, typename _ToVT = _VectorTraits<_To>,
2209 typename
2210 = enable_if_t<is_same_v<typename _ToVT::value_type, value_type>>>
2211 _GLIBCXX_SIMD_INTRINSIC operator _To() const
2212 {
2213 constexpr size_t _ToN = _ToVT::_S_full_size;
2214 if constexpr (_ToN == _Np)
2215 return __x;
2216 else if constexpr (_ToN == 2 * _Np)
2217 {
2218#ifdef _GLIBCXX_SIMD_WORKAROUND_XXX_3
2219 if constexpr (__have_avx && _TVT::template _S_is<float, 4>)
2220 return __vector_bitcast<value_type>(
2221 _mm256_insertf128_ps(__m256(), __x, 0));
2222 else if constexpr (__have_avx && _TVT::template _S_is<double, 2>)
2223 return __vector_bitcast<value_type>(
2224 _mm256_insertf128_pd(__m256d(), __x, 0));
2225 else if constexpr (__have_avx2 && _Np * sizeof(value_type) == 16)
2226 return __vector_bitcast<value_type>(
2227 _mm256_insertf128_si256(__m256i(), __to_intrin(__x), 0));
2228 else if constexpr (__have_avx512f && _TVT::template _S_is<float, 8>)
2229 {
2230 if constexpr (__have_avx512dq)
2231 return __vector_bitcast<value_type>(
2232 _mm512_insertf32x8(__m512(), __x, 0));
2233 else
2234 return reinterpret_cast<__m512>(
2235 _mm512_insertf64x4(__m512d(),
2236 reinterpret_cast<__m256d>(__x), 0));
2237 }
2238 else if constexpr (__have_avx512f
2239 && _TVT::template _S_is<double, 4>)
2240 return __vector_bitcast<value_type>(
2241 _mm512_insertf64x4(__m512d(), __x, 0));
2242 else if constexpr (__have_avx512f && _Np * sizeof(value_type) == 32)
2243 return __vector_bitcast<value_type>(
2244 _mm512_inserti64x4(__m512i(), __to_intrin(__x), 0));
2245#endif
2246 return __concat(__x, _Tp());
2247 }
2248 else if constexpr (_ToN == 4 * _Np)
2249 {
2250#ifdef _GLIBCXX_SIMD_WORKAROUND_XXX_3
2251 if constexpr (__have_avx512dq && _TVT::template _S_is<double, 2>)
2252 {
2253 return __vector_bitcast<value_type>(
2254 _mm512_insertf64x2(__m512d(), __x, 0));
2255 }
2256 else if constexpr (__have_avx512f
2257 && is_floating_point_v<value_type>)
2258 {
2259 return __vector_bitcast<value_type>(
2260 _mm512_insertf32x4(__m512(), reinterpret_cast<__m128>(__x),
2261 0));
2262 }
2263 else if constexpr (__have_avx512f && _Np * sizeof(value_type) == 16)
2264 {
2265 return __vector_bitcast<value_type>(
2266 _mm512_inserti32x4(__m512i(), __to_intrin(__x), 0));
2267 }
2268#endif
2269 return __concat(__concat(__x, _Tp()),
2270 __vector_type_t<value_type, _Np * 2>());
2271 }
2272 else if constexpr (_ToN == 8 * _Np)
2273 return __concat(operator __vector_type_t<value_type, _Np * 4>(),
2274 __vector_type_t<value_type, _Np * 4>());
2275 else if constexpr (_ToN == 16 * _Np)
2276 return __concat(operator __vector_type_t<value_type, _Np * 8>(),
2277 __vector_type_t<value_type, _Np * 8>());
2278 else
2279 __assert_unreachable<_Tp>();
2280 }
2281 };
2282
2283template <typename _Tp, typename _TVT = _VectorTraits<_Tp>>
2284 _GLIBCXX_SIMD_INTRINSIC _ZeroExtendProxy<_Tp, _TVT>
2285 __zero_extend(_Tp __x)
2286 { return {__x}; }
2287
2288// }}}
2289// __extract<_Np, By>{{{
2290template <int _Offset,
2291 int _SplitBy,
2292 typename _Tp,
2293 typename _TVT = _VectorTraits<_Tp>,
2294 typename _R = __vector_type_t<typename _TVT::value_type, _TVT::_S_full_size / _SplitBy>>
2295 _GLIBCXX_SIMD_INTRINSIC constexpr _R
2296 __extract(_Tp __in)
2297 {
2298 using value_type = typename _TVT::value_type;
2299#if _GLIBCXX_SIMD_X86INTRIN // {{{
2300 if constexpr (sizeof(_Tp) == 64 && _SplitBy == 4 && _Offset > 0)
2301 {
2302 if constexpr (__have_avx512dq && is_same_v<double, value_type>)
2303 return _mm512_extractf64x2_pd(__to_intrin(__in), _Offset);
2304 else if constexpr (is_floating_point_v<value_type>)
2305 return __vector_bitcast<value_type>(
2306 _mm512_extractf32x4_ps(__intrin_bitcast<__m512>(__in), _Offset));
2307 else
2308 return reinterpret_cast<_R>(
2309 _mm512_extracti32x4_epi32(__intrin_bitcast<__m512i>(__in),
2310 _Offset));
2311 }
2312 else
2313#endif // _GLIBCXX_SIMD_X86INTRIN }}}
2314 {
2315#ifdef _GLIBCXX_SIMD_WORKAROUND_XXX_1
2316 using _W = conditional_t<
2317 is_floating_point_v<value_type>, double,
2318 conditional_t<(sizeof(_R) >= 16), long long, value_type>>;
2319 static_assert(sizeof(_R) % sizeof(_W) == 0);
2320 constexpr int __return_width = sizeof(_R) / sizeof(_W);
2321 using _Up = __vector_type_t<_W, __return_width>;
2322 const auto __x = __vector_bitcast<_W>(__in);
2323#else
2324 constexpr int __return_width = _TVT::_S_full_size / _SplitBy;
2325 using _Up = _R;
2326 const __vector_type_t<value_type, _TVT::_S_full_size>& __x
2327 = __in; // only needed for _Tp = _SimdWrapper<value_type, _Np>
2328#endif
2329 constexpr int _O = _Offset * __return_width;
2330 return __call_with_subscripts<__return_width, _O>(
2331 __x, [](auto... __entries) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
2332 return reinterpret_cast<_R>(_Up{__entries...});
2333 });
2334 }
2335 }
2336
2337// }}}
2338// __lo/__hi64[z]{{{
2339template <typename _Tp,
2340 typename _R = __vector_type8_t<typename _VectorTraits<_Tp>::value_type>>
2341 _GLIBCXX_SIMD_INTRINSIC constexpr _R
2342 __lo64(_Tp __x)
2343 {
2344 _R __r{};
2345 __builtin_memcpy(&__r, &__x, 8);
2346 return __r;
2347 }
2348
2349template <typename _Tp,
2350 typename _R = __vector_type8_t<typename _VectorTraits<_Tp>::value_type>>
2351 _GLIBCXX_SIMD_INTRINSIC constexpr _R
2352 __hi64(_Tp __x)
2353 {
2354 static_assert(sizeof(_Tp) == 16, "use __hi64z if you meant it");
2355 _R __r{};
2356 __builtin_memcpy(&__r, reinterpret_cast<const char*>(&__x) + 8, 8);
2357 return __r;
2358 }
2359
2360template <typename _Tp,
2361 typename _R = __vector_type8_t<typename _VectorTraits<_Tp>::value_type>>
2362 _GLIBCXX_SIMD_INTRINSIC constexpr _R
2363 __hi64z([[maybe_unused]] _Tp __x)
2364 {
2365 _R __r{};
2366 if constexpr (sizeof(_Tp) == 16)
2367 __builtin_memcpy(&__r, reinterpret_cast<const char*>(&__x) + 8, 8);
2368 return __r;
2369 }
2370
2371// }}}
2372// __lo/__hi128{{{
2373template <typename _Tp>
2374 _GLIBCXX_SIMD_INTRINSIC constexpr auto
2375 __lo128(_Tp __x)
2376 { return __extract<0, sizeof(_Tp) / 16>(__x); }
2377
2378template <typename _Tp>
2379 _GLIBCXX_SIMD_INTRINSIC constexpr auto
2380 __hi128(_Tp __x)
2381 {
2382 static_assert(sizeof(__x) == 32);
2383 return __extract<1, 2>(__x);
2384 }
2385
2386// }}}
2387// __lo/__hi256{{{
2388template <typename _Tp>
2389 _GLIBCXX_SIMD_INTRINSIC constexpr auto
2390 __lo256(_Tp __x)
2391 {
2392 static_assert(sizeof(__x) == 64);
2393 return __extract<0, 2>(__x);
2394 }
2395
2396template <typename _Tp>
2397 _GLIBCXX_SIMD_INTRINSIC constexpr auto
2398 __hi256(_Tp __x)
2399 {
2400 static_assert(sizeof(__x) == 64);
2401 return __extract<1, 2>(__x);
2402 }
2403
2404// }}}
2405// __auto_bitcast{{{
2406template <typename _Tp>
2407 struct _AutoCast
2408 {
2409 static_assert(__is_vector_type_v<_Tp>);
2410
2411 const _Tp __x;
2412
2413 template <typename _Up, typename _UVT = _VectorTraits<_Up>>
2414 _GLIBCXX_SIMD_INTRINSIC constexpr operator _Up() const
2415 { return __intrin_bitcast<typename _UVT::type>(__x); }
2416 };
2417
2418template <typename _Tp>
2419 _GLIBCXX_SIMD_INTRINSIC constexpr _AutoCast<_Tp>
2420 __auto_bitcast(const _Tp& __x)
2421 { return {__x}; }
2422
2423template <typename _Tp, size_t _Np>
2424 _GLIBCXX_SIMD_INTRINSIC constexpr
2425 _AutoCast<typename _SimdWrapper<_Tp, _Np>::_BuiltinType>
2426 __auto_bitcast(const _SimdWrapper<_Tp, _Np>& __x)
2427 { return {__x._M_data}; }
2428
2429// }}}
2430// ^^^ ---- builtin vector types [[gnu::vector_size(N)]] and operations ---- ^^^
2431
2432#if _GLIBCXX_SIMD_HAVE_SSE_ABI
2433// __bool_storage_member_type{{{
2434#if _GLIBCXX_SIMD_HAVE_AVX512F && _GLIBCXX_SIMD_X86INTRIN
2435template <size_t _Size>
2436 struct __bool_storage_member_type
2437 {
2438 static_assert((_Size & (_Size - 1)) != 0,
2439 "This trait may only be used for non-power-of-2 sizes. "
2440 "Power-of-2 sizes must be specialized.");
2441 using type =
2442 typename __bool_storage_member_type<std::__bit_ceil(_Size)>::type;
2443 };
2444
2445template <>
2446 struct __bool_storage_member_type<1> { using type = bool; };
2447
2448template <>
2449 struct __bool_storage_member_type<2> { using type = __mmask8; };
2450
2451template <>
2452 struct __bool_storage_member_type<4> { using type = __mmask8; };
2453
2454template <>
2455 struct __bool_storage_member_type<8> { using type = __mmask8; };
2456
2457template <>
2458 struct __bool_storage_member_type<16> { using type = __mmask16; };
2459
2460template <>
2461 struct __bool_storage_member_type<32> { using type = __mmask32; };
2462
2463template <>
2464 struct __bool_storage_member_type<64> { using type = __mmask64; };
2465#endif // _GLIBCXX_SIMD_HAVE_AVX512F
2466
2467// }}}
2468// __intrinsic_type (x86){{{
2469// the following excludes bool via __is_vectorizable
2470#if _GLIBCXX_SIMD_HAVE_SSE
2471template <typename _Tp, size_t _Bytes>
2472 struct __intrinsic_type<_Tp, _Bytes, enable_if_t<__is_vectorizable_v<_Tp> && _Bytes <= 64>>
2473 {
2474 // allow _Tp == long double with -mlong-double-64
2475 static_assert(!(is_same_v<_Tp, long double>
2476 && sizeof(long double) > sizeof(double)),
2477 "no __intrinsic_type support for long double on x86");
2478
2479 static constexpr size_t _S_VBytes = _Bytes <= 16 ? 16 : _Bytes <= 32 ? 32 : 64;
2480
2481 using type [[__gnu__::__vector_size__(_S_VBytes)]]
2482 = conditional_t<is_integral_v<_Tp>, long long int,
2483 conditional_t<is_same_v<_Tp, long double>, double, _Tp> >;
2484 };
2485#endif // _GLIBCXX_SIMD_HAVE_SSE
2486
2487// }}}
2488#endif // _GLIBCXX_SIMD_HAVE_SSE_ABI
2489// __intrinsic_type (ARM){{{
2490#if _GLIBCXX_SIMD_HAVE_NEON
2491template <>
2492 struct __intrinsic_type<float, 8, void>
2493 { using type = float32x2_t; };
2494
2495template <>
2496 struct __intrinsic_type<float, 16, void>
2497 { using type = float32x4_t; };
2498
2499template <>
2500 struct __intrinsic_type<double, 8, void>
2501 {
2502#if _GLIBCXX_SIMD_HAVE_NEON_A64
2503 using type = float64x1_t;
2504#endif
2505 };
2506
2507template <>
2508 struct __intrinsic_type<double, 16, void>
2509 {
2510#if _GLIBCXX_SIMD_HAVE_NEON_A64
2511 using type = float64x2_t;
2512#endif
2513 };
2514
2515#define _GLIBCXX_SIMD_ARM_INTRIN(_Bits, _Np) \
2516template <> \
2517 struct __intrinsic_type<__int_with_sizeof_t<_Bits / 8>, \
2518 _Np * _Bits / 8, void> \
2519 { using type = int##_Bits##x##_Np##_t; }; \
2520template <> \
2521 struct __intrinsic_type<make_unsigned_t<__int_with_sizeof_t<_Bits / 8>>, \
2522 _Np * _Bits / 8, void> \
2523 { using type = uint##_Bits##x##_Np##_t; }
2524_GLIBCXX_SIMD_ARM_INTRIN(8, 8);
2525_GLIBCXX_SIMD_ARM_INTRIN(8, 16);
2526_GLIBCXX_SIMD_ARM_INTRIN(16, 4);
2527_GLIBCXX_SIMD_ARM_INTRIN(16, 8);
2528_GLIBCXX_SIMD_ARM_INTRIN(32, 2);
2529_GLIBCXX_SIMD_ARM_INTRIN(32, 4);
2530_GLIBCXX_SIMD_ARM_INTRIN(64, 1);
2531_GLIBCXX_SIMD_ARM_INTRIN(64, 2);
2532#undef _GLIBCXX_SIMD_ARM_INTRIN
2533
2534template <typename _Tp, size_t _Bytes>
2535 struct __intrinsic_type<_Tp, _Bytes, enable_if_t<__is_vectorizable_v<_Tp> && _Bytes <= 16>>
2536 {
2537 static constexpr int _SVecBytes = _Bytes <= 8 ? 8 : 16;
2538
2539 using _Ip = __int_for_sizeof_t<_Tp>;
2540
2541 using _Up = conditional_t<
2542 is_floating_point_v<_Tp>, _Tp,
2543 conditional_t<is_unsigned_v<_Tp>, make_unsigned_t<_Ip>, _Ip>>;
2544
2545 static_assert(!is_same_v<_Tp, _Up> || _SVecBytes != _Bytes,
2546 "should use explicit specialization above");
2547
2548 using type = typename __intrinsic_type<_Up, _SVecBytes>::type;
2549 };
2550#endif // _GLIBCXX_SIMD_HAVE_NEON
2551
2552// }}}
2553// __intrinsic_type (PPC){{{
2554#ifdef __ALTIVEC__
2555template <typename _Tp>
2556 struct __intrinsic_type_impl;
2557
2558#define _GLIBCXX_SIMD_PPC_INTRIN(_Tp) \
2559 template <> \
2560 struct __intrinsic_type_impl<_Tp> { using type = __vector _Tp; }
2561_GLIBCXX_SIMD_PPC_INTRIN(float);
2562#ifdef __VSX__
2563_GLIBCXX_SIMD_PPC_INTRIN(double);
2564#endif
2565_GLIBCXX_SIMD_PPC_INTRIN(signed char);
2566_GLIBCXX_SIMD_PPC_INTRIN(unsigned char);
2567_GLIBCXX_SIMD_PPC_INTRIN(signed short);
2568_GLIBCXX_SIMD_PPC_INTRIN(unsigned short);
2569_GLIBCXX_SIMD_PPC_INTRIN(signed int);
2570_GLIBCXX_SIMD_PPC_INTRIN(unsigned int);
2571#if defined __VSX__ || __SIZEOF_LONG__ == 4
2572_GLIBCXX_SIMD_PPC_INTRIN(signed long);
2573_GLIBCXX_SIMD_PPC_INTRIN(unsigned long);
2574#endif
2575#ifdef __VSX__
2576_GLIBCXX_SIMD_PPC_INTRIN(signed long long);
2577_GLIBCXX_SIMD_PPC_INTRIN(unsigned long long);
2578#endif
2579#undef _GLIBCXX_SIMD_PPC_INTRIN
2580
2581template <typename _Tp, size_t _Bytes>
2582 struct __intrinsic_type<_Tp, _Bytes, enable_if_t<__is_vectorizable_v<_Tp> && _Bytes <= 16>>
2583 {
2584 static constexpr bool _S_is_ldouble = is_same_v<_Tp, long double>;
2585
2586 // allow _Tp == long double with -mlong-double-64
2587 static_assert(!(_S_is_ldouble && sizeof(long double) > sizeof(double)),
2588 "no __intrinsic_type support for 128-bit floating point on PowerPC");
2589
2590#ifndef __VSX__
2591 static_assert(!(is_same_v<_Tp, double>
2592 || (_S_is_ldouble && sizeof(long double) == sizeof(double))),
2593 "no __intrinsic_type support for 64-bit floating point on PowerPC w/o VSX");
2594#endif
2595
2596 static constexpr auto __element_type()
2597 {
2598 if constexpr (is_floating_point_v<_Tp>)
2599 {
2600 if constexpr (_S_is_ldouble)
2601 return double {};
2602 else
2603 return _Tp {};
2604 }
2605 else if constexpr (is_signed_v<_Tp>)
2606 {
2607 if constexpr (sizeof(_Tp) == sizeof(_SChar))
2608 return _SChar {};
2609 else if constexpr (sizeof(_Tp) == sizeof(short))
2610 return short {};
2611 else if constexpr (sizeof(_Tp) == sizeof(int))
2612 return int {};
2613 else if constexpr (sizeof(_Tp) == sizeof(_LLong))
2614 return _LLong {};
2615 }
2616 else
2617 {
2618 if constexpr (sizeof(_Tp) == sizeof(_UChar))
2619 return _UChar {};
2620 else if constexpr (sizeof(_Tp) == sizeof(_UShort))
2621 return _UShort {};
2622 else if constexpr (sizeof(_Tp) == sizeof(_UInt))
2623 return _UInt {};
2624 else if constexpr (sizeof(_Tp) == sizeof(_ULLong))
2625 return _ULLong {};
2626 }
2627 }
2628
2629 using type = typename __intrinsic_type_impl<decltype(__element_type())>::type;
2630 };
2631#endif // __ALTIVEC__
2632
2633// }}}
2634// _SimdWrapper<bool>{{{1
2635template <size_t _Width>
2636 struct _SimdWrapper<bool, _Width,
2637 void_t<typename __bool_storage_member_type<_Width>::type>>
2638 {
2639 using _BuiltinType = typename __bool_storage_member_type<_Width>::type;
2640 using value_type = bool;
2641
2642 static constexpr size_t _S_full_size = sizeof(_BuiltinType) * __CHAR_BIT__;
2643
2644 _GLIBCXX_SIMD_INTRINSIC constexpr _SimdWrapper<bool, _S_full_size>
2645 __as_full_vector() const
2646 { return _M_data; }
2647
2648 _GLIBCXX_SIMD_INTRINSIC constexpr
2649 _SimdWrapper() = default;
2650
2651 _GLIBCXX_SIMD_INTRINSIC constexpr
2652 _SimdWrapper(_BuiltinType __k) : _M_data(__k) {};
2653
2654 _GLIBCXX_SIMD_INTRINSIC
2655 operator const _BuiltinType&() const
2656 { return _M_data; }
2657
2658 _GLIBCXX_SIMD_INTRINSIC
2659 operator _BuiltinType&()
2660 { return _M_data; }
2661
2662 _GLIBCXX_SIMD_INTRINSIC _BuiltinType
2663 __intrin() const
2664 { return _M_data; }
2665
2666 _GLIBCXX_SIMD_INTRINSIC constexpr value_type
2667 operator[](size_t __i) const
2668 { return _M_data & (_BuiltinType(1) << __i); }
2669
2670 template <size_t __i>
2671 _GLIBCXX_SIMD_INTRINSIC constexpr value_type
2672 operator[](_SizeConstant<__i>) const
2673 { return _M_data & (_BuiltinType(1) << __i); }
2674
2675 _GLIBCXX_SIMD_INTRINSIC constexpr void
2676 _M_set(size_t __i, value_type __x)
2677 {
2678 if (__x)
2679 _M_data |= (_BuiltinType(1) << __i);
2680 else
2681 _M_data &= ~(_BuiltinType(1) << __i);
2682 }
2683
2684 _GLIBCXX_SIMD_INTRINSIC constexpr bool
2685 _M_is_constprop() const
2686 { return __builtin_constant_p(_M_data); }
2687
2688 _GLIBCXX_SIMD_INTRINSIC constexpr bool
2689 _M_is_constprop_none_of() const
2690 {
2691 if (__builtin_constant_p(_M_data))
2692 {
2693 constexpr int __nbits = sizeof(_BuiltinType) * __CHAR_BIT__;
2694 constexpr _BuiltinType __active_mask
2695 = ~_BuiltinType() >> (__nbits - _Width);
2696 return (_M_data & __active_mask) == 0;
2697 }
2698 return false;
2699 }
2700
2701 _GLIBCXX_SIMD_INTRINSIC constexpr bool
2702 _M_is_constprop_all_of() const
2703 {
2704 if (__builtin_constant_p(_M_data))
2705 {
2706 constexpr int __nbits = sizeof(_BuiltinType) * __CHAR_BIT__;
2707 constexpr _BuiltinType __active_mask
2708 = ~_BuiltinType() >> (__nbits - _Width);
2709 return (_M_data & __active_mask) == __active_mask;
2710 }
2711 return false;
2712 }
2713
2714 _BuiltinType _M_data;
2715 };
2716
2717// _SimdWrapperBase{{{1
2718template <bool _MustZeroInitPadding, typename _BuiltinType>
2719 struct _SimdWrapperBase;
2720
2721template <typename _BuiltinType>
2722 struct _SimdWrapperBase<false, _BuiltinType> // no padding or no SNaNs
2723 {
2724 _GLIBCXX_SIMD_INTRINSIC constexpr
2725 _SimdWrapperBase() = default;
2726
2727 _GLIBCXX_SIMD_INTRINSIC constexpr
2728 _SimdWrapperBase(_BuiltinType __init) : _M_data(__init) {}
2729
2730 _BuiltinType _M_data;
2731 };
2732
2733template <typename _BuiltinType>
2734 struct _SimdWrapperBase<true, _BuiltinType> // with padding that needs to
2735 // never become SNaN
2736 {
2737 _GLIBCXX_SIMD_INTRINSIC constexpr
2738 _SimdWrapperBase() : _M_data() {}
2739
2740 _GLIBCXX_SIMD_INTRINSIC constexpr
2741 _SimdWrapperBase(_BuiltinType __init) : _M_data(__init) {}
2742
2743 _BuiltinType _M_data;
2744 };
2745
2746// }}}
2747// _SimdWrapper{{{
2748struct _DisabledSimdWrapper;
2749
2750template <typename _Tp, size_t _Width>
2751 struct _SimdWrapper<
2752 _Tp, _Width,
2753 void_t<__vector_type_t<_Tp, _Width>, __intrinsic_type_t<_Tp, _Width>>>
2754 : _SimdWrapperBase<__has_iec559_behavior<__signaling_NaN, _Tp>::value
2755 && sizeof(_Tp) * _Width
2756 == sizeof(__vector_type_t<_Tp, _Width>),
2757 __vector_type_t<_Tp, _Width>>
2758 {
2759 static constexpr bool _S_need_default_init
2760 = __has_iec559_behavior<__signaling_NaN, _Tp>::value
2761 and sizeof(_Tp) * _Width == sizeof(__vector_type_t<_Tp, _Width>);
2762
2763 using _BuiltinType = __vector_type_t<_Tp, _Width>;
2764
2765 using _Base = _SimdWrapperBase<_S_need_default_init, _BuiltinType>;
2766
2767 static_assert(__is_vectorizable_v<_Tp>);
2768 static_assert(_Width >= 2); // 1 doesn't make sense, use _Tp directly then
2769
2770 using value_type = _Tp;
2771
2772 static inline constexpr size_t _S_full_size
2773 = sizeof(_BuiltinType) / sizeof(value_type);
2774 static inline constexpr int _S_size = _Width;
2775 static inline constexpr bool _S_is_partial = _S_full_size != _S_size;
2776
2777 using _Base::_M_data;
2778
2779 _GLIBCXX_SIMD_INTRINSIC constexpr _SimdWrapper<_Tp, _S_full_size>
2780 __as_full_vector() const
2781 { return _M_data; }
2782
2783 _GLIBCXX_SIMD_INTRINSIC constexpr
2784 _SimdWrapper(initializer_list<_Tp> __init)
2785 : _Base(__generate_from_n_evaluations<_Width, _BuiltinType>(
2786 [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
2787 return __init.begin()[__i.value];
2788 })) {}
2789
2790 _GLIBCXX_SIMD_INTRINSIC constexpr
2791 _SimdWrapper() = default;
2792
2793 _GLIBCXX_SIMD_INTRINSIC constexpr
2794 _SimdWrapper(const _SimdWrapper&) = default;
2795
2796 _GLIBCXX_SIMD_INTRINSIC constexpr
2797 _SimdWrapper(_SimdWrapper&&) = default;
2798
2799 _GLIBCXX_SIMD_INTRINSIC constexpr _SimdWrapper&
2800 operator=(const _SimdWrapper&) = default;
2801
2802 _GLIBCXX_SIMD_INTRINSIC constexpr _SimdWrapper&
2803 operator=(_SimdWrapper&&) = default;
2804
2805 // Convert from exactly matching __vector_type_t
2806 using _SimdWrapperBase<_S_need_default_init, _BuiltinType>::_SimdWrapperBase;
2807
2808 // Convert from __intrinsic_type_t if __intrinsic_type_t and __vector_type_t differ, otherwise
2809 // this ctor should not exist. Making the argument type unusable is our next best solution.
2810 _GLIBCXX_SIMD_INTRINSIC constexpr
2811 _SimdWrapper(conditional_t<is_same_v<_BuiltinType, __intrinsic_type_t<_Tp, _Width>>,
2812 _DisabledSimdWrapper, __intrinsic_type_t<_Tp, _Width>> __x)
2813 : _Base(__vector_bitcast<_Tp, _Width>(__x)) {}
2814
2815 // Convert from different __vector_type_t, but only if bit reinterpretation is a correct
2816 // conversion of the value_type
2817 template <typename _V, typename _TVT = _VectorTraits<_V>,
2818 typename = enable_if_t<sizeof(typename _TVT::value_type) == sizeof(_Tp)
2819 and sizeof(_V) == sizeof(_BuiltinType)
2820 and is_integral_v<_Tp>
2821 and is_integral_v<typename _TVT::value_type>>>
2822 _GLIBCXX_SIMD_INTRINSIC constexpr
2823 _SimdWrapper(_V __x)
2824 : _Base(reinterpret_cast<_BuiltinType>(__x)) {}
2825
2826 template <typename... _As,
2827 typename = enable_if_t<((is_same_v<simd_abi::scalar, _As> && ...)
2828 && sizeof...(_As) <= _Width)>>
2829 _GLIBCXX_SIMD_INTRINSIC constexpr
2830 operator _SimdTuple<_Tp, _As...>() const
2831 {
2832 return __generate_from_n_evaluations<sizeof...(_As), _SimdTuple<_Tp, _As...>>(
2833 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA
2834 { return _M_data[int(__i)]; });
2835 }
2836
2837 _GLIBCXX_SIMD_INTRINSIC constexpr
2838 operator const _BuiltinType&() const
2839 { return _M_data; }
2840
2841 _GLIBCXX_SIMD_INTRINSIC constexpr
2842 operator _BuiltinType&()
2843 { return _M_data; }
2844
2845 _GLIBCXX_SIMD_INTRINSIC constexpr _Tp
2846 operator[](size_t __i) const
2847 { return _M_data[__i]; }
2848
2849 template <size_t __i>
2850 _GLIBCXX_SIMD_INTRINSIC constexpr _Tp
2851 operator[](_SizeConstant<__i>) const
2852 { return _M_data[__i]; }
2853
2854 _GLIBCXX_SIMD_INTRINSIC constexpr void
2855 _M_set(size_t __i, _Tp __x)
2856 {
2857 if (__builtin_is_constant_evaluated())
2858 _M_data = __generate_from_n_evaluations<_Width, _BuiltinType>([&](auto __j) {
2859 return __j == __i ? __x : _M_data[__j()];
2860 });
2861 else
2862 _M_data[__i] = __x;
2863 }
2864
2865 _GLIBCXX_SIMD_INTRINSIC
2866 constexpr bool
2867 _M_is_constprop() const
2868 { return __builtin_constant_p(_M_data); }
2869
2870 _GLIBCXX_SIMD_INTRINSIC constexpr bool
2871 _M_is_constprop_none_of() const
2872 {
2873 if (__builtin_constant_p(_M_data))
2874 {
2875 bool __r = true;
2876 if constexpr (is_floating_point_v<_Tp>)
2877 {
2878 using _Ip = __int_for_sizeof_t<_Tp>;
2879 const auto __intdata = __vector_bitcast<_Ip>(_M_data);
2880 __execute_n_times<_Width>(
2881 [&](auto __i) { __r &= __intdata[__i.value] == _Ip(); });
2882 }
2883 else
2884 __execute_n_times<_Width>(
2885 [&](auto __i) { __r &= _M_data[__i.value] == _Tp(); });
2886 if (__builtin_constant_p(__r))
2887 return __r;
2888 }
2889 return false;
2890 }
2891
2892 _GLIBCXX_SIMD_INTRINSIC constexpr bool
2893 _M_is_constprop_all_of() const
2894 {
2895 if (__builtin_constant_p(_M_data))
2896 {
2897 bool __r = true;
2898 if constexpr (is_floating_point_v<_Tp>)
2899 {
2900 using _Ip = __int_for_sizeof_t<_Tp>;
2901 const auto __intdata = __vector_bitcast<_Ip>(_M_data);
2902 __execute_n_times<_Width>(
2903 [&](auto __i) { __r &= __intdata[__i.value] == ~_Ip(); });
2904 }
2905 else
2906 __execute_n_times<_Width>(
2907 [&](auto __i) { __r &= _M_data[__i.value] == ~_Tp(); });
2908 if (__builtin_constant_p(__r))
2909 return __r;
2910 }
2911 return false;
2912 }
2913 };
2914
2915// }}}
2916
2917// __vectorized_sizeof {{{
2918template <typename _Tp>
2919 constexpr size_t
2920 __vectorized_sizeof()
2921 {
2922 if constexpr (!__is_vectorizable_v<_Tp>)
2923 return 0;
2924
2925 if constexpr (sizeof(_Tp) <= 8)
2926 {
2927 // X86:
2928 if constexpr (__have_avx512bw)
2929 return 64;
2930 if constexpr (__have_avx512f && sizeof(_Tp) >= 4)
2931 return 64;
2932 if constexpr (__have_avx2)
2933 return 32;
2934 if constexpr (__have_avx && is_floating_point_v<_Tp>)
2935 return 32;
2936 if constexpr (__have_sse2)
2937 return 16;
2938 if constexpr (__have_sse && is_same_v<_Tp, float>)
2939 return 16;
2940 /* The following is too much trouble because of mixed MMX and x87 code.
2941 * While nothing here explicitly calls MMX instructions of registers,
2942 * they are still emitted but no EMMS cleanup is done.
2943 if constexpr (__have_mmx && sizeof(_Tp) <= 4 && is_integral_v<_Tp>)
2944 return 8;
2945 */
2946
2947 // PowerPC:
2948 if constexpr (__have_power8vec
2949 || (__have_power_vmx && (sizeof(_Tp) < 8))
2950 || (__have_power_vsx && is_floating_point_v<_Tp>) )
2951 return 16;
2952
2953 // ARM:
2954 if constexpr (__have_neon_a64)
2955 return 16;
2956 if constexpr (__have_neon_a32 and (not is_floating_point_v<_Tp>
2957 or is_same_v<_Tp, float>))
2958 return 16;
2959 if constexpr (__have_neon
2960 && sizeof(_Tp) < 8
2961 // Only allow fp if the user allows non-ICE559 fp (e.g.
2962 // via -ffast-math). ARMv7 NEON fp is not conforming to
2963 // IEC559.
2964 && (__support_neon_float || !is_floating_point_v<_Tp>))
2965 return 16;
2966 }
2967
2968 return sizeof(_Tp);
2969 }
2970
2971// }}}
2972namespace simd_abi {
2973// most of simd_abi is defined in simd_detail.h
2974template <typename _Tp>
2975 inline constexpr int max_fixed_size
2976 = ((__have_avx512bw && sizeof(_Tp) == 1)
2977 || (__have_sve && __sve_vectorized_size_bytes/sizeof(_Tp) >= 64)) ? 64 : 32;
2978
2979// compatible {{{
2980#if defined __x86_64__ || defined __aarch64__
2981template <typename _Tp>
2982 using compatible = conditional_t<(sizeof(_Tp) <= 8), _VecBuiltin<16>, scalar>;
2983#elif defined __ARM_NEON
2984// FIXME: not sure, probably needs to be scalar (or dependent on the hard-float
2985// ABI?)
2986template <typename _Tp>
2987 using compatible
2988 = conditional_t<(sizeof(_Tp) < 8
2989 && (__support_neon_float || !is_floating_point_v<_Tp>)),
2990 _VecBuiltin<16>, scalar>;
2991#else
2992template <typename>
2993 using compatible = scalar;
2994#endif
2995
2996// }}}
2997// native {{{
2998template <typename _Tp>
2999 constexpr auto
3000 __determine_native_abi()
3001 {
3002 constexpr size_t __bytes = __vectorized_sizeof<_Tp>();
3003 if constexpr (__bytes == sizeof(_Tp))
3004 return static_cast<scalar*>(nullptr);
3005 else if constexpr (__have_sve)
3006 return static_cast<_SveAbi<__sve_vectorized_size_bytes>*>(nullptr);
3007 else if constexpr (__have_avx512vl || (__have_avx512f && __bytes == 64))
3008 return static_cast<_VecBltnBtmsk<__bytes>*>(nullptr);
3009 else
3010 return static_cast<_VecBuiltin<__bytes>*>(nullptr);
3011 }
3012
3013template <typename _Tp, typename = enable_if_t<__is_vectorizable_v<_Tp>>>
3014 using native = remove_pointer_t<decltype(__determine_native_abi<_Tp>())>;
3015
3016// }}}
3017// __default_abi {{{
3018#if defined _GLIBCXX_SIMD_DEFAULT_ABI
3019template <typename _Tp>
3020 using __default_abi = _GLIBCXX_SIMD_DEFAULT_ABI<_Tp>;
3021#else
3022template <typename _Tp>
3023 using __default_abi = compatible<_Tp>;
3024#endif
3025
3026// }}}
3027} // namespace simd_abi
3028
3029// traits {{{1
3030template <typename _Tp>
3031 struct is_simd_flag_type
3032 : false_type
3033 {};
3034
3035template <>
3036 struct is_simd_flag_type<element_aligned_tag>
3037 : true_type
3038 {};
3039
3040template <>
3041 struct is_simd_flag_type<vector_aligned_tag>
3042 : true_type
3043 {};
3044
3045template <size_t _Np>
3046 struct is_simd_flag_type<overaligned_tag<_Np>>
3047 : __bool_constant<(_Np > 0) and __has_single_bit(_Np)>
3048 {};
3049
3050template <typename _Tp>
3051 inline constexpr bool is_simd_flag_type_v = is_simd_flag_type<_Tp>::value;
3052
3053template <typename _Tp, typename = enable_if_t<is_simd_flag_type_v<_Tp>>>
3054 using _IsSimdFlagType = _Tp;
3055
3056// is_abi_tag {{{2
3057template <typename _Tp, typename = void_t<>>
3058 struct is_abi_tag : false_type {};
3059
3060template <typename _Tp>
3061 struct is_abi_tag<_Tp, void_t<typename _Tp::_IsValidAbiTag>>
3062 : public _Tp::_IsValidAbiTag {};
3063
3064template <typename _Tp>
3065 inline constexpr bool is_abi_tag_v = is_abi_tag<_Tp>::value;
3066
3067// is_simd(_mask) {{{2
3068template <typename _Tp>
3069 struct is_simd : public false_type {};
3070
3071template <typename _Tp>
3072 inline constexpr bool is_simd_v = is_simd<_Tp>::value;
3073
3074template <typename _Tp>
3075 struct is_simd_mask : public false_type {};
3076
3077template <typename _Tp>
3078inline constexpr bool is_simd_mask_v = is_simd_mask<_Tp>::value;
3079
3080// simd_size {{{2
3081template <typename _Tp, typename _Abi, typename = void>
3082 struct __simd_size_impl {};
3083
3084template <typename _Tp, typename _Abi>
3085 struct __simd_size_impl<
3086 _Tp, _Abi,
3087 enable_if_t<conjunction_v<__is_vectorizable<_Tp>, is_abi_tag<_Abi>>>>
3088 : _SizeConstant<_Abi::template _S_size<_Tp>> {};
3089
3090template <typename _Tp, typename _Abi = simd_abi::__default_abi<_Tp>>
3091 struct simd_size : __simd_size_impl<_Tp, _Abi> {};
3092
3093template <typename _Tp, typename _Abi = simd_abi::__default_abi<_Tp>>
3094 inline constexpr size_t simd_size_v = simd_size<_Tp, _Abi>::value;
3095
3096// simd_abi::deduce {{{2
3097template <typename _Tp, size_t _Np, typename = void>
3098 struct __deduce_impl;
3099
3100template <typename _Tp, size_t _Np, typename = void>
3101 struct __no_sve_deduce_impl;
3102
3103namespace simd_abi {
3104/**
3105 * @tparam _Tp The requested `value_type` for the elements.
3106 * @tparam _Np The requested number of elements.
3107 * @tparam _Abis This parameter is ignored, since this implementation cannot
3108 * make any use of it. Either __a good native ABI is matched and used as `type`
3109 * alias, or the `fixed_size<_Np>` ABI is used, which internally is built from
3110 * the best matching native ABIs.
3111 */
3112template <typename _Tp, size_t _Np, typename...>
3113 struct deduce : __deduce_impl<_Tp, _Np> {};
3114
3115template <typename _Tp, size_t _Np, typename... _Abis>
3116 using deduce_t = typename deduce<_Tp, _Np, _Abis...>::type;
3117
3118template <typename _Tp, size_t _Np, typename...>
3119 struct __no_sve_deduce : __no_sve_deduce_impl<_Tp, _Np> {};
3120
3121template <typename _Tp, size_t _Np, typename... _Abis>
3122 using __no_sve_deduce_t = typename __no_sve_deduce<_Tp, _Np, _Abis...>::type;
3123} // namespace simd_abi
3124
3125// }}}2
3126// rebind_simd {{{2
3127template <typename _Tp, typename _V, typename = void>
3128 struct rebind_simd;
3129
3130template <typename _Tp, typename _Up, typename _Abi>
3131 struct rebind_simd<_Tp, simd<_Up, _Abi>,
3132 void_t<std::conditional_t<!__is_sve_abi<_Abi>(),
3133 simd_abi::__no_sve_deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>,
3134 simd_abi::deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>>>>
3135 {
3136 using type = simd<_Tp, std::conditional_t<
3137 !__is_sve_abi<_Abi>(),
3138 simd_abi::__no_sve_deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>,
3139 simd_abi::deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>>>;
3140 };
3141
3142template <typename _Tp, typename _Up, typename _Abi>
3143 struct rebind_simd<_Tp, simd_mask<_Up, _Abi>,
3144 void_t<std::conditional_t<!__is_sve_abi<_Abi>(),
3145 simd_abi::__no_sve_deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>,
3146 simd_abi::deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>>>>
3147 {
3148 using type = simd_mask<_Tp, std::conditional_t<
3149 !__is_sve_abi<_Abi>(),
3150 simd_abi::__no_sve_deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>,
3151 simd_abi::deduce_t<_Tp, simd_size_v<_Up, _Abi>, _Abi>>>;
3152 };
3153
3154template <typename _Tp, typename _V>
3155 using rebind_simd_t = typename rebind_simd<_Tp, _V>::type;
3156
3157// resize_simd {{{2
3158template <int _Np, typename _V, typename = void>
3159 struct resize_simd;
3160
3161template <int _Np, typename _Tp, typename _Abi>
3162 struct resize_simd<_Np, simd<_Tp, _Abi>, void_t<simd_abi::deduce_t<_Tp, _Np, _Abi>>>
3163 { using type = simd<_Tp, simd_abi::deduce_t<_Tp, _Np, _Abi>>; };
3164
3165template <int _Np, typename _Tp, typename _Abi>
3166 struct resize_simd<_Np, simd_mask<_Tp, _Abi>, void_t<simd_abi::deduce_t<_Tp, _Np, _Abi>>>
3167 { using type = simd_mask<_Tp, simd_abi::deduce_t<_Tp, _Np, _Abi>>; };
3168
3169template <int _Np, typename _V>
3170 using resize_simd_t = typename resize_simd<_Np, _V>::type;
3171
3172// }}}2
3173// memory_alignment {{{2
3174template <typename _Tp, typename _Up = typename _Tp::value_type>
3175 struct memory_alignment
3176 : public _SizeConstant<vector_aligned_tag::_S_alignment<_Tp, _Up>> {};
3177
3178template <typename _Tp, typename _Up = typename _Tp::value_type>
3179 inline constexpr size_t memory_alignment_v = memory_alignment<_Tp, _Up>::value;
3180
3181// class template simd [simd] {{{1
3182template <typename _Tp, typename _Abi = simd_abi::__default_abi<_Tp>>
3183 class simd;
3184
3185template <typename _Tp, typename _Abi>
3186 struct is_simd<simd<_Tp, _Abi>> : public true_type {};
3187
3188template <typename _Tp>
3189 using native_simd = simd<_Tp, simd_abi::native<_Tp>>;
3190
3191template <typename _Tp, int _Np>
3192 using fixed_size_simd = simd<_Tp, simd_abi::fixed_size<_Np>>;
3193
3194template <typename _Tp, size_t _Np>
3195 using __deduced_simd = simd<_Tp, simd_abi::deduce_t<_Tp, _Np>>;
3196
3197// class template simd_mask [simd_mask] {{{1
3198template <typename _Tp, typename _Abi = simd_abi::__default_abi<_Tp>>
3199 class simd_mask;
3200
3201template <typename _Tp, typename _Abi>
3202 struct is_simd_mask<simd_mask<_Tp, _Abi>> : public true_type {};
3203
3204template <typename _Tp>
3205 using native_simd_mask = simd_mask<_Tp, simd_abi::native<_Tp>>;
3206
3207template <typename _Tp, int _Np>
3208 using fixed_size_simd_mask = simd_mask<_Tp, simd_abi::fixed_size<_Np>>;
3209
3210template <typename _Tp, size_t _Np>
3211 using __deduced_simd_mask = simd_mask<_Tp, simd_abi::deduce_t<_Tp, _Np>>;
3212
3213// casts [simd.casts] {{{1
3214// static_simd_cast {{{2
3215template <typename _Tp, typename _Up, typename _Ap, bool = is_simd_v<_Tp>, typename = void>
3216 struct __static_simd_cast_return_type;
3217
3218template <typename _Tp, typename _A0, typename _Up, typename _Ap>
3219 struct __static_simd_cast_return_type<simd_mask<_Tp, _A0>, _Up, _Ap, false, void>
3220 : __static_simd_cast_return_type<simd<_Tp, _A0>, _Up, _Ap> {};
3221
3222template <typename _Tp, typename _Up, typename _Ap>
3223 struct __static_simd_cast_return_type<
3224 _Tp, _Up, _Ap, true, enable_if_t<_Tp::size() == simd_size_v<_Up, _Ap>>>
3225 { using type = _Tp; };
3226
3227template <typename _Tp, typename _Ap>
3228 struct __static_simd_cast_return_type<_Tp, _Tp, _Ap, false,
3229#ifdef _GLIBCXX_SIMD_FIX_P2TS_ISSUE66
3230 enable_if_t<__is_vectorizable_v<_Tp>>
3231#else
3232 void
3233#endif
3234 >
3235 { using type = simd<_Tp, _Ap>; };
3236
3237template <typename _Tp, typename = void>
3238 struct __safe_make_signed { using type = _Tp;};
3239
3240template <typename _Tp>
3241 struct __safe_make_signed<_Tp, enable_if_t<is_integral_v<_Tp>>>
3242 {
3243 // the extra make_unsigned_t is because of PR85951
3244 using type = make_signed_t<make_unsigned_t<_Tp>>;
3245 };
3246
3247template <typename _Tp>
3248 using safe_make_signed_t = typename __safe_make_signed<_Tp>::type;
3249
3250template <typename _Tp, typename _Up, typename _Ap>
3251 struct __static_simd_cast_return_type<_Tp, _Up, _Ap, false,
3252#ifdef _GLIBCXX_SIMD_FIX_P2TS_ISSUE66
3253 enable_if_t<__is_vectorizable_v<_Tp>>
3254#else
3255 void
3256#endif
3257 >
3258 {
3259 using type = conditional_t<
3260 (is_integral_v<_Up> && is_integral_v<_Tp> &&
3261#ifndef _GLIBCXX_SIMD_FIX_P2TS_ISSUE65
3262 is_signed_v<_Up> != is_signed_v<_Tp> &&
3263#endif
3264 is_same_v<safe_make_signed_t<_Up>, safe_make_signed_t<_Tp>>),
3265 simd<_Tp, _Ap>, fixed_size_simd<_Tp, simd_size_v<_Up, _Ap>>>;
3266 };
3267
3268template <typename _Tp, typename _Up, typename _Ap,
3269 typename _R
3270 = typename __static_simd_cast_return_type<_Tp, _Up, _Ap>::type>
3271 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _R
3272 static_simd_cast(const simd<_Up, _Ap>& __x)
3273 {
3274 if constexpr (is_same<_R, simd<_Up, _Ap>>::value)
3275 return __x;
3276 else
3277 {
3278 _SimdConverter<_Up, _Ap, typename _R::value_type, typename _R::abi_type>
3279 __c;
3280 return _R(__private_init, __c(__data(__x)));
3281 }
3282 }
3283
3284namespace __proposed {
3285template <typename _Tp, typename _Up, typename _Ap,
3286 typename _R
3287 = typename __static_simd_cast_return_type<_Tp, _Up, _Ap>::type>
3288 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR typename _R::mask_type
3289 static_simd_cast(const simd_mask<_Up, _Ap>& __x)
3290 {
3291 using _RM = typename _R::mask_type;
3292 return {__private_init, _RM::abi_type::_MaskImpl::template _S_convert<
3293 typename _RM::simd_type::value_type>(__x)};
3294 }
3295
3296template <typename _To, typename _Up, typename _Abi>
3297 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3298 _To
3299 simd_bit_cast(const simd<_Up, _Abi>& __x)
3300 {
3301 using _Tp = typename _To::value_type;
3302 using _ToMember = typename _SimdTraits<_Tp, typename _To::abi_type>::_SimdMember;
3303 using _From = simd<_Up, _Abi>;
3304 using _FromMember = typename _SimdTraits<_Up, _Abi>::_SimdMember;
3305 // with concepts, the following should be constraints
3306 static_assert(sizeof(_To) == sizeof(_From));
3307 static_assert(is_trivially_copyable_v<_Tp> && is_trivially_copyable_v<_Up>);
3308 static_assert(is_trivially_copyable_v<_ToMember> && is_trivially_copyable_v<_FromMember>);
3309#if __has_builtin(__builtin_bit_cast)
3310 return {__private_init, __builtin_bit_cast(_ToMember, __data(__x))};
3311#else
3312 return {__private_init, __bit_cast<_ToMember>(__data(__x))};
3313#endif
3314 }
3315
3316template <typename _To, typename _Up, typename _Abi>
3317 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3318 _To
3319 simd_bit_cast(const simd_mask<_Up, _Abi>& __x)
3320 {
3321 using _From = simd_mask<_Up, _Abi>;
3322 static_assert(sizeof(_To) == sizeof(_From));
3323 static_assert(is_trivially_copyable_v<_From>);
3324 // _To can be simd<T, A>, specifically simd<T, fixed_size<N>> in which case _To is not trivially
3325 // copyable.
3326 if constexpr (is_simd_v<_To>)
3327 {
3328 using _Tp = typename _To::value_type;
3329 using _ToMember = typename _SimdTraits<_Tp, typename _To::abi_type>::_SimdMember;
3330 static_assert(is_trivially_copyable_v<_ToMember>);
3331#if __has_builtin(__builtin_bit_cast)
3332 return {__private_init, __builtin_bit_cast(_ToMember, __x)};
3333#else
3334 return {__private_init, __bit_cast<_ToMember>(__x)};
3335#endif
3336 }
3337 else
3338 {
3339 static_assert(is_trivially_copyable_v<_To>);
3340#if __has_builtin(__builtin_bit_cast)
3341 return __builtin_bit_cast(_To, __x);
3342#else
3343 return __bit_cast<_To>(__x);
3344#endif
3345 }
3346 }
3347} // namespace __proposed
3348
3349// simd_cast {{{2
3350template <typename _Tp, typename _Up, typename _Ap,
3351 typename _To = __value_type_or_identity_t<_Tp>>
3352 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR auto
3353 simd_cast(const simd<_ValuePreserving<_Up, _To>, _Ap>& __x)
3354 -> decltype(static_simd_cast<_Tp>(__x))
3355 { return static_simd_cast<_Tp>(__x); }
3356
3357namespace __proposed {
3358template <typename _Tp, typename _Up, typename _Ap,
3359 typename _To = __value_type_or_identity_t<_Tp>>
3360 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR auto
3361 simd_cast(const simd_mask<_ValuePreserving<_Up, _To>, _Ap>& __x)
3362 -> decltype(static_simd_cast<_Tp>(__x))
3363 { return static_simd_cast<_Tp>(__x); }
3364} // namespace __proposed
3365
3366// }}}2
3367// resizing_simd_cast {{{
3368namespace __proposed {
3369/* Proposed spec:
3370
3371template <class T, class U, class Abi>
3372T resizing_simd_cast(const simd<U, Abi>& x)
3373
3374p1 Constraints:
3375 - is_simd_v<T> is true and
3376 - T::value_type is the same type as U
3377
3378p2 Returns:
3379 A simd object with the i^th element initialized to x[i] for all i in the
3380 range of [0, min(T::size(), simd_size_v<U, Abi>)). If T::size() is larger
3381 than simd_size_v<U, Abi>, the remaining elements are value-initialized.
3382
3383template <class T, class U, class Abi>
3384T resizing_simd_cast(const simd_mask<U, Abi>& x)
3385
3386p1 Constraints: is_simd_mask_v<T> is true
3387
3388p2 Returns:
3389 A simd_mask object with the i^th element initialized to x[i] for all i in
3390the range of [0, min(T::size(), simd_size_v<U, Abi>)). If T::size() is larger
3391 than simd_size_v<U, Abi>, the remaining elements are initialized to false.
3392
3393 */
3394
3395template <typename _Tp, typename _Up, typename _Ap>
3396 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR enable_if_t<
3397 conjunction_v<is_simd<_Tp>, is_same<typename _Tp::value_type, _Up>>, _Tp>
3398 resizing_simd_cast(const simd<_Up, _Ap>& __x)
3399 {
3400 if constexpr (is_same_v<typename _Tp::abi_type, _Ap>)
3401 return __x;
3402 else if (__builtin_is_constant_evaluated())
3403 return _Tp([&](auto __i) constexpr {
3404 return __i < simd_size_v<_Up, _Ap> ? __x[__i] : _Up();
3405 });
3406 else if constexpr (simd_size_v<_Up, _Ap> == 1)
3407 {
3408 _Tp __r{};
3409 __r[0] = __x[0];
3410 return __r;
3411 }
3412 else if constexpr (_Tp::size() == 1)
3413 return __x[0];
3414 else if constexpr (sizeof(_Tp) == sizeof(__x)
3415 && !__is_fixed_size_abi_v<_Ap> && !__is_sve_abi<_Ap>())
3416 return {__private_init,
3417 __vector_bitcast<typename _Tp::value_type, _Tp::size()>(
3418 _Ap::_S_masked(__data(__x))._M_data)};
3419 else
3420 {
3421 _Tp __r{};
3422 __builtin_memcpy(&__data(__r), &__data(__x),
3423 sizeof(_Up)
3424 * std::min(_Tp::size(), simd_size_v<_Up, _Ap>));
3425 return __r;
3426 }
3427 }
3428
3429template <typename _Tp, typename _Up, typename _Ap>
3430 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3431 enable_if_t<is_simd_mask_v<_Tp>, _Tp>
3432 resizing_simd_cast(const simd_mask<_Up, _Ap>& __x)
3433 {
3434 return {__private_init, _Tp::abi_type::_MaskImpl::template _S_convert<
3435 typename _Tp::simd_type::value_type>(__x)};
3436 }
3437} // namespace __proposed
3438
3439// }}}
3440// to_fixed_size {{{2
3441template <typename _Tp, int _Np>
3442 _GLIBCXX_SIMD_INTRINSIC fixed_size_simd<_Tp, _Np>
3443 to_fixed_size(const fixed_size_simd<_Tp, _Np>& __x)
3444 { return __x; }
3445
3446template <typename _Tp, int _Np>
3447 _GLIBCXX_SIMD_INTRINSIC fixed_size_simd_mask<_Tp, _Np>
3448 to_fixed_size(const fixed_size_simd_mask<_Tp, _Np>& __x)
3449 { return __x; }
3450
3451template <typename _Tp, typename _Ap>
3452 _GLIBCXX_SIMD_INTRINSIC fixed_size_simd<_Tp, simd_size_v<_Tp, _Ap>>
3453 to_fixed_size(const simd<_Tp, _Ap>& __x)
3454 {
3455 using _Rp = fixed_size_simd<_Tp, simd_size_v<_Tp, _Ap>>;
3456 return _Rp([&__x](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __x[__i]; });
3457 }
3458
3459template <typename _Tp, typename _Ap>
3460 _GLIBCXX_SIMD_INTRINSIC fixed_size_simd_mask<_Tp, simd_size_v<_Tp, _Ap>>
3461 to_fixed_size(const simd_mask<_Tp, _Ap>& __x)
3462 {
3463 return {__private_init,
3464 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __x[__i]; }};
3465 }
3466
3467// to_native {{{2
3468template <typename _Tp, int _Np>
3469 _GLIBCXX_SIMD_INTRINSIC
3470 enable_if_t<(_Np == native_simd<_Tp>::size()), native_simd<_Tp>>
3471 to_native(const fixed_size_simd<_Tp, _Np>& __x)
3472 {
3473 alignas(memory_alignment_v<native_simd<_Tp>>) _Tp __mem[_Np];
3474 __x.copy_to(__mem, vector_aligned);
3475 return {__mem, vector_aligned};
3476 }
3477
3478template <typename _Tp, int _Np>
3479 _GLIBCXX_SIMD_INTRINSIC
3480 enable_if_t<(_Np == native_simd_mask<_Tp>::size()), native_simd_mask<_Tp>>
3481 to_native(const fixed_size_simd_mask<_Tp, _Np>& __x)
3482 {
3483 return native_simd_mask<_Tp>(
3484 __private_init,
3485 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __x[__i]; });
3486 }
3487
3488// to_compatible {{{2
3489template <typename _Tp, int _Np>
3490 _GLIBCXX_SIMD_INTRINSIC enable_if_t<(_Np == simd<_Tp>::size()), simd<_Tp>>
3491 to_compatible(const simd<_Tp, simd_abi::fixed_size<_Np>>& __x)
3492 {
3493 alignas(memory_alignment_v<simd<_Tp>>) _Tp __mem[_Np];
3494 __x.copy_to(__mem, vector_aligned);
3495 return {__mem, vector_aligned};
3496 }
3497
3498template <typename _Tp, int _Np>
3499 _GLIBCXX_SIMD_INTRINSIC
3500 enable_if_t<(_Np == simd_mask<_Tp>::size()), simd_mask<_Tp>>
3501 to_compatible(const simd_mask<_Tp, simd_abi::fixed_size<_Np>>& __x)
3502 {
3503 return simd_mask<_Tp>(
3504 __private_init,
3505 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __x[__i]; });
3506 }
3507
3508// masked assignment [simd_mask.where] {{{1
3509
3510// where_expression {{{1
3511// const_where_expression<M, T> {{{2
3512template <typename _M, typename _Tp>
3513 class const_where_expression
3514 {
3515 using _V = _Tp;
3516 static_assert(is_same_v<_V, __remove_cvref_t<_Tp>>);
3517
3518 struct _Wrapper { using value_type = _V; };
3519
3520 protected:
3521 using _Impl = typename _V::_Impl;
3522
3523 using value_type =
3524 typename conditional_t<is_arithmetic_v<_V>, _Wrapper, _V>::value_type;
3525
3526 _GLIBCXX_SIMD_INTRINSIC friend const _M&
3527 __get_mask(const const_where_expression& __x)
3528 { return __x._M_k; }
3529
3530 _GLIBCXX_SIMD_INTRINSIC friend const _Tp&
3531 __get_lvalue(const const_where_expression& __x)
3532 { return __x._M_value; }
3533
3534 const _M& _M_k;
3535 _Tp& _M_value;
3536
3537 public:
3538 const_where_expression(const const_where_expression&) = delete;
3539
3540 const_where_expression& operator=(const const_where_expression&) = delete;
3541
3542 _GLIBCXX_SIMD_INTRINSIC constexpr
3543 const_where_expression(const _M& __kk, const _Tp& dd)
3544 : _M_k(__kk), _M_value(const_cast<_Tp&>(dd)) {}
3545
3546 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _V
3547 operator-() const&&
3548 {
3549 return {__private_init,
3550 _Impl::template _S_masked_unary<negate>(__data(_M_k),
3551 __data(_M_value))};
3552 }
3553
3554 template <typename _Up, typename _Flags>
3555 [[nodiscard]] _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _V
3556 copy_from(const _LoadStorePtr<_Up, value_type>* __mem, _IsSimdFlagType<_Flags>) const&&
3557 {
3558 return {__private_init,
3559 _Impl::_S_masked_load(__data(_M_value), __data(_M_k),
3560 _Flags::template _S_apply<_V>(__mem))};
3561 }
3562
3563 template <typename _Up, typename _Flags>
3564 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3565 copy_to(_LoadStorePtr<_Up, value_type>* __mem, _IsSimdFlagType<_Flags>) const&&
3566 {
3567 _Impl::_S_masked_store(__data(_M_value),
3568 _Flags::template _S_apply<_V>(__mem),
3569 __data(_M_k));
3570 }
3571 };
3572
3573// const_where_expression<bool, T> {{{2
3574template <typename _Tp>
3575 class const_where_expression<bool, _Tp>
3576 {
3577 using _M = bool;
3578 using _V = _Tp;
3579
3580 static_assert(is_same_v<_V, __remove_cvref_t<_Tp>>);
3581
3582 struct _Wrapper { using value_type = _V; };
3583
3584 protected:
3585 using value_type
3586 = typename conditional_t<is_arithmetic_v<_V>, _Wrapper, _V>::value_type;
3587
3588 _GLIBCXX_SIMD_INTRINSIC friend const _M&
3589 __get_mask(const const_where_expression& __x)
3590 { return __x._M_k; }
3591
3592 _GLIBCXX_SIMD_INTRINSIC friend const _Tp&
3593 __get_lvalue(const const_where_expression& __x)
3594 { return __x._M_value; }
3595
3596 const bool _M_k;
3597 _Tp& _M_value;
3598
3599 public:
3600 const_where_expression(const const_where_expression&) = delete;
3601 const_where_expression& operator=(const const_where_expression&) = delete;
3602
3603 _GLIBCXX_SIMD_INTRINSIC constexpr
3604 const_where_expression(const bool __kk, const _Tp& dd)
3605 : _M_k(__kk), _M_value(const_cast<_Tp&>(dd)) {}
3606
3607 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _V
3608 operator-() const&&
3609 { return _M_k ? -_M_value : _M_value; }
3610
3611 template <typename _Up, typename _Flags>
3612 [[nodiscard]] _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _V
3613 copy_from(const _LoadStorePtr<_Up, value_type>* __mem, _IsSimdFlagType<_Flags>) const&&
3614 { return _M_k ? static_cast<_V>(__mem[0]) : _M_value; }
3615
3616 template <typename _Up, typename _Flags>
3617 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3618 copy_to(_LoadStorePtr<_Up, value_type>* __mem, _IsSimdFlagType<_Flags>) const&&
3619 {
3620 if (_M_k)
3621 __mem[0] = _M_value;
3622 }
3623 };
3624
3625// where_expression<M, T> {{{2
3626template <typename _M, typename _Tp>
3627 class where_expression : public const_where_expression<_M, _Tp>
3628 {
3629 using _Impl = typename const_where_expression<_M, _Tp>::_Impl;
3630
3631 static_assert(!is_const<_Tp>::value,
3632 "where_expression may only be instantiated with __a non-const "
3633 "_Tp parameter");
3634
3635 using typename const_where_expression<_M, _Tp>::value_type;
3636 using const_where_expression<_M, _Tp>::_M_k;
3637 using const_where_expression<_M, _Tp>::_M_value;
3638
3639 static_assert(
3640 is_same<typename _M::abi_type, typename _Tp::abi_type>::value, "");
3641 static_assert(_M::size() == _Tp::size(), "");
3642
3643 _GLIBCXX_SIMD_INTRINSIC friend constexpr _Tp&
3644 __get_lvalue(where_expression& __x)
3645 { return __x._M_value; }
3646
3647 public:
3648 where_expression(const where_expression&) = delete;
3649 where_expression& operator=(const where_expression&) = delete;
3650
3651 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3652 where_expression(const _M& __kk, _Tp& dd)
3653 : const_where_expression<_M, _Tp>(__kk, dd) {}
3654
3655 template <typename _Up>
3656 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3657 operator=(_Up&& __x) &&
3658 {
3659 _Impl::_S_masked_assign(__data(_M_k), __data(_M_value),
3660 __to_value_type_or_member_type<_Tp>(
3661 static_cast<_Up&&>(__x)));
3662 }
3663
3664#define _GLIBCXX_SIMD_OP_(__op, __name) \
3665 template <typename _Up> \
3666 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void \
3667 operator __op##=(_Up&& __x)&& \
3668 { \
3669 _Impl::template _S_masked_cassign( \
3670 __data(_M_k), __data(_M_value), \
3671 __to_value_type_or_member_type<_Tp>(static_cast<_Up&&>(__x)), \
3672 [](auto __impl, auto __lhs, auto __rhs) \
3673 constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA \
3674 { return __impl.__name(__lhs, __rhs); }); \
3675 } \
3676 static_assert(true)
3677 _GLIBCXX_SIMD_OP_(+, _S_plus);
3678 _GLIBCXX_SIMD_OP_(-, _S_minus);
3679 _GLIBCXX_SIMD_OP_(*, _S_multiplies);
3680 _GLIBCXX_SIMD_OP_(/, _S_divides);
3681 _GLIBCXX_SIMD_OP_(%, _S_modulus);
3682 _GLIBCXX_SIMD_OP_(&, _S_bit_and);
3683 _GLIBCXX_SIMD_OP_(|, _S_bit_or);
3684 _GLIBCXX_SIMD_OP_(^, _S_bit_xor);
3685 _GLIBCXX_SIMD_OP_(<<, _S_shift_left);
3686 _GLIBCXX_SIMD_OP_(>>, _S_shift_right);
3687#undef _GLIBCXX_SIMD_OP_
3688
3689 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3690 operator++() &&
3691 {
3692 __data(_M_value)
3693 = _Impl::template _S_masked_unary<__increment>(__data(_M_k), __data(_M_value));
3694 }
3695
3696 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3697 operator++(int) &&
3698 {
3699 __data(_M_value)
3700 = _Impl::template _S_masked_unary<__increment>(__data(_M_k), __data(_M_value));
3701 }
3702
3703 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3704 operator--() &&
3705 {
3706 __data(_M_value)
3707 = _Impl::template _S_masked_unary<__decrement>(__data(_M_k), __data(_M_value));
3708 }
3709
3710 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3711 operator--(int) &&
3712 {
3713 __data(_M_value)
3714 = _Impl::template _S_masked_unary<__decrement>(__data(_M_k), __data(_M_value));
3715 }
3716
3717 // intentionally hides const_where_expression::copy_from
3718 template <typename _Up, typename _Flags>
3719 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3720 copy_from(const _LoadStorePtr<_Up, value_type>* __mem, _IsSimdFlagType<_Flags>) &&
3721 {
3722 __data(_M_value) = _Impl::_S_masked_load(__data(_M_value), __data(_M_k),
3723 _Flags::template _S_apply<_Tp>(__mem));
3724 }
3725 };
3726
3727// where_expression<bool, T> {{{2
3728template <typename _Tp>
3729 class where_expression<bool, _Tp>
3730 : public const_where_expression<bool, _Tp>
3731 {
3732 using _M = bool;
3733 using typename const_where_expression<_M, _Tp>::value_type;
3734 using const_where_expression<_M, _Tp>::_M_k;
3735 using const_where_expression<_M, _Tp>::_M_value;
3736
3737 public:
3738 where_expression(const where_expression&) = delete;
3739 where_expression& operator=(const where_expression&) = delete;
3740
3741 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3742 where_expression(const _M& __kk, _Tp& dd)
3743 : const_where_expression<_M, _Tp>(__kk, dd) {}
3744
3745#define _GLIBCXX_SIMD_OP_(__op) \
3746 template <typename _Up> \
3747 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void \
3748 operator __op(_Up&& __x)&& \
3749 { if (_M_k) _M_value __op static_cast<_Up&&>(__x); }
3750
3751 _GLIBCXX_SIMD_OP_(=)
3752 _GLIBCXX_SIMD_OP_(+=)
3753 _GLIBCXX_SIMD_OP_(-=)
3754 _GLIBCXX_SIMD_OP_(*=)
3755 _GLIBCXX_SIMD_OP_(/=)
3756 _GLIBCXX_SIMD_OP_(%=)
3757 _GLIBCXX_SIMD_OP_(&=)
3758 _GLIBCXX_SIMD_OP_(|=)
3759 _GLIBCXX_SIMD_OP_(^=)
3760 _GLIBCXX_SIMD_OP_(<<=)
3761 _GLIBCXX_SIMD_OP_(>>=)
3762 #undef _GLIBCXX_SIMD_OP_
3763
3764 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3765 operator++() &&
3766 { if (_M_k) ++_M_value; }
3767
3768 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3769 operator++(int) &&
3770 { if (_M_k) ++_M_value; }
3771
3772 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3773 operator--() &&
3774 { if (_M_k) --_M_value; }
3775
3776 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3777 operator--(int) &&
3778 { if (_M_k) --_M_value; }
3779
3780 // intentionally hides const_where_expression::copy_from
3781 template <typename _Up, typename _Flags>
3782 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR void
3783 copy_from(const _LoadStorePtr<_Up, value_type>* __mem, _IsSimdFlagType<_Flags>) &&
3784 { if (_M_k) _M_value = __mem[0]; }
3785 };
3786
3787// where {{{1
3788template <typename _Tp, typename _Ap>
3789 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3790 where_expression<simd_mask<_Tp, _Ap>, simd<_Tp, _Ap>>
3791 where(const typename simd<_Tp, _Ap>::mask_type& __k, simd<_Tp, _Ap>& __value)
3792 { return {__k, __value}; }
3793
3794template <typename _Tp, typename _Ap>
3795 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3796 const_where_expression<simd_mask<_Tp, _Ap>, simd<_Tp, _Ap>>
3797 where(const typename simd<_Tp, _Ap>::mask_type& __k, const simd<_Tp, _Ap>& __value)
3798 { return {__k, __value}; }
3799
3800template <typename _Tp, typename _Ap>
3801 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3802 where_expression<simd_mask<_Tp, _Ap>, simd_mask<_Tp, _Ap>>
3803 where(const remove_const_t<simd_mask<_Tp, _Ap>>& __k, simd_mask<_Tp, _Ap>& __value)
3804 { return {__k, __value}; }
3805
3806template <typename _Tp, typename _Ap>
3807 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3808 const_where_expression<simd_mask<_Tp, _Ap>, simd_mask<_Tp, _Ap>>
3809 where(const remove_const_t<simd_mask<_Tp, _Ap>>& __k, const simd_mask<_Tp, _Ap>& __value)
3810 { return {__k, __value}; }
3811
3812template <typename _Tp>
3813 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR where_expression<bool, _Tp>
3814 where(_ExactBool __k, _Tp& __value)
3815 { return {__k, __value}; }
3816
3817template <typename _Tp>
3818 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR const_where_expression<bool, _Tp>
3819 where(_ExactBool __k, const _Tp& __value)
3820 { return {__k, __value}; }
3821
3822template <typename _Tp, typename _Ap>
3823 _GLIBCXX_SIMD_CONSTEXPR void
3824 where(bool __k, simd<_Tp, _Ap>& __value) = delete;
3825
3826template <typename _Tp, typename _Ap>
3827 _GLIBCXX_SIMD_CONSTEXPR void
3828 where(bool __k, const simd<_Tp, _Ap>& __value) = delete;
3829
3830// proposed mask iterations {{{1
3831namespace __proposed {
3832template <size_t _Np>
3833 class where_range
3834 {
3835 const bitset<_Np> __bits;
3836
3837 public:
3838 where_range(bitset<_Np> __b) : __bits(__b) {}
3839
3840 class iterator
3841 {
3842 size_t __mask;
3843 size_t __bit;
3844
3845 _GLIBCXX_SIMD_INTRINSIC void
3846 __next_bit()
3847 { __bit = __builtin_ctzl(__mask); }
3848
3849 _GLIBCXX_SIMD_INTRINSIC void
3850 __reset_lsb()
3851 {
3852 // 01100100 - 1 = 01100011
3853 __mask &= (__mask - 1);
3854 // __asm__("btr %1,%0" : "+r"(__mask) : "r"(__bit));
3855 }
3856
3857 public:
3858 iterator(decltype(__mask) __m) : __mask(__m) { __next_bit(); }
3859 iterator(const iterator&) = default;
3860 iterator(iterator&&) = default;
3861
3862 _GLIBCXX_SIMD_ALWAYS_INLINE size_t
3863 operator->() const
3864 { return __bit; }
3865
3866 _GLIBCXX_SIMD_ALWAYS_INLINE size_t
3867 operator*() const
3868 { return __bit; }
3869
3870 _GLIBCXX_SIMD_ALWAYS_INLINE iterator&
3871 operator++()
3872 {
3873 __reset_lsb();
3874 __next_bit();
3875 return *this;
3876 }
3877
3878 _GLIBCXX_SIMD_ALWAYS_INLINE iterator
3879 operator++(int)
3880 {
3881 iterator __tmp = *this;
3882 __reset_lsb();
3883 __next_bit();
3884 return __tmp;
3885 }
3886
3887 _GLIBCXX_SIMD_ALWAYS_INLINE bool
3888 operator==(const iterator& __rhs) const
3889 { return __mask == __rhs.__mask; }
3890
3891 _GLIBCXX_SIMD_ALWAYS_INLINE bool
3892 operator!=(const iterator& __rhs) const
3893 { return __mask != __rhs.__mask; }
3894 };
3895
3896 iterator
3897 begin() const
3898 { return __bits.to_ullong(); }
3899
3900 iterator
3901 end() const
3902 { return 0; }
3903 };
3904
3905template <typename _Tp, typename _Ap>
3906 where_range<simd_size_v<_Tp, _Ap>>
3907 where(const simd_mask<_Tp, _Ap>& __k)
3908 { return __k.__to_bitset(); }
3909
3910} // namespace __proposed
3911
3912// }}}1
3913// reductions [simd.reductions] {{{1
3914template <typename _Tp, typename _Abi, typename _BinaryOperation = plus<>>
3915 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _Tp
3916 reduce(const simd<_Tp, _Abi>& __v, _BinaryOperation __binary_op = _BinaryOperation())
3917 { return _Abi::_SimdImpl::_S_reduce(__v, __binary_op); }
3918
3919template <typename _M, typename _V, typename _BinaryOperation = plus<>>
3920 _GLIBCXX_SIMD_INTRINSIC typename _V::value_type
3921 reduce(const const_where_expression<_M, _V>& __x,
3922 typename _V::value_type __identity_element, _BinaryOperation __binary_op)
3923 {
3924 if (__builtin_expect(none_of(__get_mask(__x)), false))
3925 return __identity_element;
3926
3927 _V __tmp = __identity_element;
3928 _V::_Impl::_S_masked_assign(__data(__get_mask(__x)), __data(__tmp),
3929 __data(__get_lvalue(__x)));
3930 return reduce(__tmp, __binary_op);
3931 }
3932
3933template <typename _M, typename _V>
3934 _GLIBCXX_SIMD_INTRINSIC typename _V::value_type
3935 reduce(const const_where_expression<_M, _V>& __x, plus<> __binary_op = {})
3936 { return reduce(__x, 0, __binary_op); }
3937
3938template <typename _M, typename _V>
3939 _GLIBCXX_SIMD_INTRINSIC typename _V::value_type
3940 reduce(const const_where_expression<_M, _V>& __x, multiplies<> __binary_op)
3941 { return reduce(__x, 1, __binary_op); }
3942
3943template <typename _M, typename _V>
3944 _GLIBCXX_SIMD_INTRINSIC typename _V::value_type
3945 reduce(const const_where_expression<_M, _V>& __x, bit_and<> __binary_op)
3946 { return reduce(__x, ~typename _V::value_type(), __binary_op); }
3947
3948template <typename _M, typename _V>
3949 _GLIBCXX_SIMD_INTRINSIC typename _V::value_type
3950 reduce(const const_where_expression<_M, _V>& __x, bit_or<> __binary_op)
3951 { return reduce(__x, 0, __binary_op); }
3952
3953template <typename _M, typename _V>
3954 _GLIBCXX_SIMD_INTRINSIC typename _V::value_type
3955 reduce(const const_where_expression<_M, _V>& __x, bit_xor<> __binary_op)
3956 { return reduce(__x, 0, __binary_op); }
3957
3958template <typename _Tp, typename _Abi>
3959 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _Tp
3960 hmin(const simd<_Tp, _Abi>& __v) noexcept
3961 { return _Abi::_SimdImpl::_S_reduce(__v, __detail::_Minimum()); }
3962
3963template <typename _Tp, typename _Abi>
3964 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR _Tp
3965 hmax(const simd<_Tp, _Abi>& __v) noexcept
3966 { return _Abi::_SimdImpl::_S_reduce(__v, __detail::_Maximum()); }
3967
3968template <typename _M, typename _V>
3969 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3970 typename _V::value_type
3971 hmin(const const_where_expression<_M, _V>& __x) noexcept
3972 {
3973 using _Tp = typename _V::value_type;
3974 constexpr _Tp __id_elem =
3975#ifdef __FINITE_MATH_ONLY__
3976 __finite_max_v<_Tp>;
3977#else
3978 __value_or<__infinity, _Tp>(__finite_max_v<_Tp>);
3979#endif
3980 _V __tmp = __id_elem;
3981 _V::_Impl::_S_masked_assign(__data(__get_mask(__x)), __data(__tmp),
3982 __data(__get_lvalue(__x)));
3983 return _V::abi_type::_SimdImpl::_S_reduce(__tmp, __detail::_Minimum());
3984 }
3985
3986template <typename _M, typename _V>
3987 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
3988 typename _V::value_type
3989 hmax(const const_where_expression<_M, _V>& __x) noexcept
3990 {
3991 using _Tp = typename _V::value_type;
3992 constexpr _Tp __id_elem =
3993#ifdef __FINITE_MATH_ONLY__
3994 __finite_min_v<_Tp>;
3995#else
3996 [] {
3997 if constexpr (__value_exists_v<__infinity, _Tp>)
3998 return -__infinity_v<_Tp>;
3999 else
4000 return __finite_min_v<_Tp>;
4001 }();
4002#endif
4003 _V __tmp = __id_elem;
4004 _V::_Impl::_S_masked_assign(__data(__get_mask(__x)), __data(__tmp),
4005 __data(__get_lvalue(__x)));
4006 return _V::abi_type::_SimdImpl::_S_reduce(__tmp, __detail::_Maximum());
4007 }
4008
4009// }}}1
4010// algorithms [simd.alg] {{{
4011template <typename _Tp, typename _Ap>
4012 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR simd<_Tp, _Ap>
4013 min(const simd<_Tp, _Ap>& __a, const simd<_Tp, _Ap>& __b)
4014 { return {__private_init, _Ap::_SimdImpl::_S_min(__data(__a), __data(__b))}; }
4015
4016template <typename _Tp, typename _Ap>
4017 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR simd<_Tp, _Ap>
4018 max(const simd<_Tp, _Ap>& __a, const simd<_Tp, _Ap>& __b)
4019 { return {__private_init, _Ap::_SimdImpl::_S_max(__data(__a), __data(__b))}; }
4020
4021template <typename _Tp, typename _Ap>
4022 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
4023 pair<simd<_Tp, _Ap>, simd<_Tp, _Ap>>
4024 minmax(const simd<_Tp, _Ap>& __a, const simd<_Tp, _Ap>& __b)
4025 {
4026 const auto pair_of_members
4027 = _Ap::_SimdImpl::_S_minmax(__data(__a), __data(__b));
4028 return {simd<_Tp, _Ap>(__private_init, pair_of_members.first),
4029 simd<_Tp, _Ap>(__private_init, pair_of_members.second)};
4030 }
4031
4032template <typename _Tp, typename _Ap>
4033 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR simd<_Tp, _Ap>
4034 clamp(const simd<_Tp, _Ap>& __v, const simd<_Tp, _Ap>& __lo, const simd<_Tp, _Ap>& __hi)
4035 {
4036 using _Impl = typename _Ap::_SimdImpl;
4037 return {__private_init,
4038 _Impl::_S_min(__data(__hi),
4039 _Impl::_S_max(__data(__lo), __data(__v)))};
4040 }
4041
4042// }}}
4043
4044template <size_t... _Sizes, typename _Tp, typename _Ap,
4045 typename = enable_if_t<((_Sizes + ...) == simd<_Tp, _Ap>::size())>>
4046 inline tuple<simd<_Tp, simd_abi::deduce_t<_Tp, _Sizes>>...>
4047 split(const simd<_Tp, _Ap>&);
4048
4049// __extract_part {{{
4050template <int _Index, int _Total, int _Combine = 1, typename _Tp, size_t _Np>
4051 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_CONST constexpr
4052 conditional_t<_Np == _Total and _Combine == 1, _Tp, _SimdWrapper<_Tp, _Np / _Total * _Combine>>
4053 __extract_part(const _SimdWrapper<_Tp, _Np> __x);
4054
4055template <int _Index, int _Parts, int _Combine = 1, typename _Tp, typename _A0, typename... _As>
4056 _GLIBCXX_SIMD_INTRINSIC constexpr auto
4057 __extract_part(const _SimdTuple<_Tp, _A0, _As...>& __x);
4058
4059// }}}
4060// _SizeList {{{
4061template <size_t _V0, size_t... _Values>
4062 struct _SizeList
4063 {
4064 template <size_t _I>
4065 static constexpr size_t
4066 _S_at(_SizeConstant<_I> = {})
4067 {
4068 if constexpr (_I == 0)
4069 return _V0;
4070 else
4071 return _SizeList<_Values...>::template _S_at<_I - 1>();
4072 }
4073
4074 template <size_t _I>
4075 static constexpr auto
4076 _S_before(_SizeConstant<_I> = {})
4077 {
4078 if constexpr (_I == 0)
4079 return _SizeConstant<0>();
4080 else
4081 return _SizeConstant<
4082 _V0 + _SizeList<_Values...>::template _S_before<_I - 1>()>();
4083 }
4084
4085 template <size_t _Np>
4086 static constexpr auto
4087 _S_pop_front(_SizeConstant<_Np> = {})
4088 {
4089 if constexpr (_Np == 0)
4090 return _SizeList();
4091 else
4092 return _SizeList<_Values...>::template _S_pop_front<_Np - 1>();
4093 }
4094 };
4095
4096// }}}
4097// __extract_center {{{
4098template <typename _Tp, size_t _Np>
4099 _GLIBCXX_SIMD_INTRINSIC _SimdWrapper<_Tp, _Np / 2>
4100 __extract_center(_SimdWrapper<_Tp, _Np> __x)
4101 {
4102 static_assert(_Np >= 4);
4103 static_assert(_Np % 4 == 0); // x0 - x1 - x2 - x3 -> return {x1, x2}
4104#if _GLIBCXX_SIMD_X86INTRIN // {{{
4105 if constexpr (__have_avx512f && sizeof(_Tp) * _Np == 64)
4106 {
4107 const auto __intrin = __to_intrin(__x);
4108 if constexpr (is_integral_v<_Tp>)
4109 return __vector_bitcast<_Tp>(_mm512_castsi512_si256(
4110 _mm512_shuffle_i32x4(__intrin, __intrin,
4111 1 + 2 * 0x4 + 2 * 0x10 + 3 * 0x40)));
4112 else if constexpr (sizeof(_Tp) == 4)
4113 return __vector_bitcast<_Tp>(_mm512_castps512_ps256(
4114 _mm512_shuffle_f32x4(__intrin, __intrin,
4115 1 + 2 * 0x4 + 2 * 0x10 + 3 * 0x40)));
4116 else if constexpr (sizeof(_Tp) == 8)
4117 return __vector_bitcast<_Tp>(_mm512_castpd512_pd256(
4118 _mm512_shuffle_f64x2(__intrin, __intrin,
4119 1 + 2 * 0x4 + 2 * 0x10 + 3 * 0x40)));
4120 else
4121 __assert_unreachable<_Tp>();
4122 }
4123 else if constexpr (sizeof(_Tp) * _Np == 32 && is_floating_point_v<_Tp>)
4124 return __vector_bitcast<_Tp>(
4125 _mm_shuffle_pd(__lo128(__vector_bitcast<double>(__x)),
4126 __hi128(__vector_bitcast<double>(__x)), 1));
4127 else if constexpr (sizeof(__x) == 32 && sizeof(_Tp) * _Np <= 32)
4128 return __vector_bitcast<_Tp>(
4129 _mm_alignr_epi8(__hi128(__vector_bitcast<_LLong>(__x)),
4130 __lo128(__vector_bitcast<_LLong>(__x)),
4131 sizeof(_Tp) * _Np / 4));
4132 else
4133#endif // _GLIBCXX_SIMD_X86INTRIN }}}
4134 {
4135 __vector_type_t<_Tp, _Np / 2> __r;
4136 __builtin_memcpy(&__r,
4137 reinterpret_cast<const char*>(&__x)
4138 + sizeof(_Tp) * _Np / 4,
4139 sizeof(_Tp) * _Np / 2);
4140 return __r;
4141 }
4142 }
4143
4144template <typename _Tp, typename _A0, typename... _As>
4145 _GLIBCXX_SIMD_INTRINSIC
4146 _SimdWrapper<_Tp, _SimdTuple<_Tp, _A0, _As...>::_S_size() / 2>
4147 __extract_center(const _SimdTuple<_Tp, _A0, _As...>& __x)
4148 {
4149 if constexpr (sizeof...(_As) == 0)
4150 return __extract_center(__x.first);
4151 else
4152 return __extract_part<1, 4, 2>(__x);
4153 }
4154
4155// }}}
4156// __split_wrapper {{{
4157template <size_t... _Sizes, typename _Tp, typename... _As>
4158 auto
4159 __split_wrapper(_SizeList<_Sizes...>, const _SimdTuple<_Tp, _As...>& __x)
4160 {
4161 return split<_Sizes...>(
4162 fixed_size_simd<_Tp, _SimdTuple<_Tp, _As...>::_S_size()>(__private_init,
4163 __x));
4164 }
4165
4166// }}}
4167
4168// split<simd>(simd) {{{
4169template <typename _V, typename _Ap,
4170 size_t _Parts = simd_size_v<typename _V::value_type, _Ap> / _V::size()>
4171 enable_if_t<simd_size_v<typename _V::value_type, _Ap> == _Parts * _V::size()
4172 && is_simd_v<_V>, array<_V, _Parts>>
4173 split(const simd<typename _V::value_type, _Ap>& __x)
4174 {
4175 using _Tp = typename _V::value_type;
4176
4177 auto __gen_fallback = [&]() constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4178 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4179 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4180 return _V([&](auto __j) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA
4181 { return __x[__i * _V::size() + __j]; });
4182 });
4183 };
4184
4185 if constexpr (_Parts == 1)
4186 {
4187 return {simd_cast<_V>(__x)};
4188 }
4189 else if (__x._M_is_constprop())
4190 {
4191 return __gen_fallback();
4192 }
4193#if _GLIBCXX_SIMD_HAVE_SVE
4194 else if constexpr(__is_sve_abi<_Ap>)
4195 {
4196 return __gen_fallback();
4197 }
4198#endif
4199 else if constexpr (
4200 __is_fixed_size_abi_v<_Ap>
4201 && (is_same_v<typename _V::abi_type, simd_abi::scalar>
4202 || (__is_fixed_size_abi_v<typename _V::abi_type>
4203 && sizeof(_V) == sizeof(_Tp) * _V::size() // _V doesn't have padding
4204 )))
4205 {
4206 // fixed_size -> fixed_size (w/o padding) or scalar
4207#ifdef _GLIBCXX_SIMD_USE_ALIASING_LOADS
4208 const __may_alias<_Tp>* const __element_ptr
4209 = reinterpret_cast<const __may_alias<_Tp>*>(&__data(__x));
4210 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4211 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA
4212 { return _V(__element_ptr + __i * _V::size(), vector_aligned); });
4213#else
4214 const auto& __xx = __data(__x);
4215 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4216 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4217 [[maybe_unused]] constexpr size_t __offset
4218 = decltype(__i)::value * _V::size();
4219 return _V([&](auto __j) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4220 constexpr _SizeConstant<__j + __offset> __k;
4221 return __xx[__k];
4222 });
4223 });
4224#endif
4225 }
4226 else if constexpr (is_same_v<typename _V::abi_type, simd_abi::scalar>)
4227 {
4228 // normally memcpy should work here as well
4229 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4230 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA { return __x[__i]; });
4231 }
4232 else
4233 {
4234 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4235 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4236 if constexpr (__is_fixed_size_abi_v<typename _V::abi_type>)
4237 return _V([&](auto __j) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4238 return __x[__i * _V::size() + __j];
4239 });
4240 else
4241 return _V(__private_init,
4242 __extract_part<decltype(__i)::value, _Parts>(__data(__x)));
4243 });
4244 }
4245 }
4246
4247// }}}
4248// split<simd_mask>(simd_mask) {{{
4249template <typename _V, typename _Ap,
4250 size_t _Parts = simd_size_v<typename _V::simd_type::value_type, _Ap> / _V::size()>
4251 enable_if_t<is_simd_mask_v<_V> && simd_size_v<typename
4252 _V::simd_type::value_type, _Ap> == _Parts * _V::size(), array<_V, _Parts>>
4253 split(const simd_mask<typename _V::simd_type::value_type, _Ap>& __x)
4254 {
4255 if constexpr (is_same_v<_Ap, typename _V::abi_type>)
4256 return {__x};
4257 else if constexpr (_Parts == 1)
4258 return {__proposed::static_simd_cast<_V>(__x)};
4259 else if constexpr (_Parts == 2 && __is_sse_abi<typename _V::abi_type>()
4260 && __is_avx_abi<_Ap>())
4261 return {_V(__private_init, __lo128(__data(__x))),
4262 _V(__private_init, __hi128(__data(__x)))};
4263 else if constexpr (_V::size() <= __CHAR_BIT__ * sizeof(_ULLong))
4264 {
4265 const bitset __bits = __x.__to_bitset();
4266 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4267 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4268 constexpr size_t __offset = __i * _V::size();
4269 return _V(__bitset_init, (__bits >> __offset).to_ullong());
4270 });
4271 }
4272 else
4273 {
4274 return __generate_from_n_evaluations<_Parts, array<_V, _Parts>>(
4275 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4276 constexpr size_t __offset = __i * _V::size();
4277 return _V(__private_init,
4278 [&](auto __j) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4279 return __x[__j + __offset];
4280 });
4281 });
4282 }
4283 }
4284
4285// }}}
4286// split<_Sizes...>(simd) {{{
4287template <size_t... _Sizes, typename _Tp, typename _Ap, typename>
4288 _GLIBCXX_SIMD_ALWAYS_INLINE
4289 tuple<simd<_Tp, simd_abi::deduce_t<_Tp, _Sizes>>...>
4290 split(const simd<_Tp, _Ap>& __x)
4291 {
4292 using _SL = _SizeList<_Sizes...>;
4293 using _Tuple = tuple<__deduced_simd<_Tp, _Sizes>...>;
4294 constexpr size_t _Np = simd_size_v<_Tp, _Ap>;
4295 constexpr size_t _N0 = _SL::template _S_at<0>();
4296 using _V = __deduced_simd<_Tp, _N0>;
4297
4298 auto __gen_fallback = [&]() constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA
4299 {
4300 return __generate_from_n_evaluations<sizeof...(_Sizes), _Tuple>(
4301 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4302 using _Vi = __deduced_simd<_Tp, _SL::_S_at(__i)>;
4303 constexpr size_t __offset = _SL::_S_before(__i);
4304 return _Vi([&](auto __j) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4305 return __x[__offset + __j];
4306 });
4307 });
4308 };
4309
4310 if (__x._M_is_constprop())
4311 __gen_fallback();
4312#if _GLIBCXX_SIMD_HAVE_SVE
4313 else if constexpr (__have_sve)
4314 __gen_fallback();
4315#endif
4316 else if constexpr (_Np == _N0)
4317 {
4318 static_assert(sizeof...(_Sizes) == 1);
4319 return {simd_cast<_V>(__x)};
4320 }
4321 else if constexpr // split from fixed_size, such that __x::first.size == _N0
4322 (__is_fixed_size_abi_v<
4323 _Ap> && __fixed_size_storage_t<_Tp, _Np>::_S_first_size == _N0)
4324 {
4325 static_assert(
4326 !__is_fixed_size_abi_v<typename _V::abi_type>,
4327 "How can <_Tp, _Np> be __a single _SimdTuple entry but __a "
4328 "fixed_size_simd "
4329 "when deduced?");
4330 // extract first and recurse (__split_wrapper is needed to deduce a new
4331 // _Sizes pack)
4332 return tuple_cat(make_tuple(_V(__private_init, __data(__x).first)),
4333 __split_wrapper(_SL::template _S_pop_front<1>(),
4334 __data(__x).second));
4335 }
4336 else if constexpr ((!__is_fixed_size_abi_v<simd_abi::deduce_t<_Tp, _Sizes>> && ...))
4337 {
4338 constexpr array<size_t, sizeof...(_Sizes)> __size = {_Sizes...};
4339 return __generate_from_n_evaluations<sizeof...(_Sizes), _Tuple>(
4340 [&](auto __i) constexpr {
4341 constexpr size_t __offset = [&]() {
4342 size_t __r = 0;
4343 for (unsigned __j = 0; __j < __i; ++__j)
4344 __r += __size[__j];
4345 return __r;
4346 }();
4347 return __deduced_simd<_Tp, __size[__i]>(
4348 __private_init,
4349 __extract_part<__offset, _Np, __size[__i]>(__data(__x)));
4350 });
4351 }
4352#ifdef _GLIBCXX_SIMD_USE_ALIASING_LOADS
4353 const __may_alias<_Tp>* const __element_ptr
4354 = reinterpret_cast<const __may_alias<_Tp>*>(&__x);
4355 return __generate_from_n_evaluations<sizeof...(_Sizes), _Tuple>(
4356 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4357 using _Vi = __deduced_simd<_Tp, _SL::_S_at(__i)>;
4358 constexpr size_t __offset = _SL::_S_before(__i);
4359 constexpr size_t __base_align = alignof(simd<_Tp, _Ap>);
4360 constexpr size_t __a
4361 = __base_align - ((__offset * sizeof(_Tp)) % __base_align);
4362 constexpr size_t __b = ((__a - 1) & __a) ^ __a;
4363 constexpr size_t __alignment = __b == 0 ? __a : __b;
4364 return _Vi(__element_ptr + __offset, overaligned<__alignment>);
4365 });
4366#else
4367 return __generate_from_n_evaluations<sizeof...(_Sizes), _Tuple>(
4368 [&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4369 using _Vi = __deduced_simd<_Tp, _SL::_S_at(__i)>;
4370 const auto& __xx = __data(__x);
4371 using _Offset = decltype(_SL::_S_before(__i));
4372 return _Vi([&](auto __j) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4373 constexpr _SizeConstant<_Offset::value + __j> __k;
4374 return __xx[__k];
4375 });
4376 });
4377#endif
4378 }
4379
4380// }}}
4381
4382// __subscript_in_pack {{{
4383template <size_t _I, typename _Tp, typename _Ap, typename... _As>
4384 _GLIBCXX_SIMD_INTRINSIC constexpr _Tp
4385 __subscript_in_pack(const simd<_Tp, _Ap>& __x, const simd<_Tp, _As>&... __xs)
4386 {
4387 if constexpr (_I < simd_size_v<_Tp, _Ap>)
4388 return __x[_I];
4389 else
4390 return __subscript_in_pack<_I - simd_size_v<_Tp, _Ap>>(__xs...);
4391 }
4392
4393// }}}
4394// __store_pack_of_simd {{{
4395template <typename _Tp, typename _A0, typename... _As>
4396 _GLIBCXX_SIMD_INTRINSIC void
4397 __store_pack_of_simd(char* __mem, const simd<_Tp, _A0>& __x0, const simd<_Tp, _As>&... __xs)
4398 {
4399 constexpr size_t __n_bytes = sizeof(_Tp) * simd_size_v<_Tp, _A0>;
4400 __builtin_memcpy(__mem, &__data(__x0), __n_bytes);
4401 if constexpr (sizeof...(__xs) > 0)
4402 __store_pack_of_simd(__mem + __n_bytes, __xs...);
4403 }
4404
4405// }}}
4406// concat(simd...) {{{
4407template <typename _Tp, typename... _As, typename = __detail::__odr_helper>
4408 inline _GLIBCXX_SIMD_CONSTEXPR
4409 simd<_Tp, simd_abi::deduce_t<_Tp, (simd_size_v<_Tp, _As> + ...)>>
4410 concat(const simd<_Tp, _As>&... __xs)
4411 {
4412 constexpr int _Np = (simd_size_v<_Tp, _As> + ...);
4413 using _Abi = simd_abi::deduce_t<_Tp, _Np>;
4414 using _Rp = simd<_Tp, _Abi>;
4415 using _RW = typename _SimdTraits<_Tp, _Abi>::_SimdMember;
4416 if constexpr (sizeof...(__xs) == 1)
4417 return simd_cast<_Rp>(__xs...);
4418 else if ((... && __xs._M_is_constprop()))
4419 return _Rp([&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA
4420 { return __subscript_in_pack<__i>(__xs...); });
4421 else if constexpr (__is_simd_wrapper_v<_RW> and sizeof...(__xs) == 2)
4422 {
4423 return {__private_init,
4424 __vec_shuffle(__as_vector(__xs)..., std::make_index_sequence<_RW::_S_full_size>(),
4425 [](int __i) {
4426 constexpr int __sizes[2] = {int(simd_size_v<_Tp, _As>)...};
4427 constexpr int __vsizes[2]
4428 = {int(sizeof(__as_vector(__xs)) / sizeof(_Tp))...};
4429 constexpr int __padding0 = __vsizes[0] - __sizes[0];
4430 return __i >= _Np ? -1 : __i < __sizes[0] ? __i : __i + __padding0;
4431 })};
4432 }
4433 else if constexpr (__is_simd_wrapper_v<_RW> and sizeof...(__xs) == 3)
4434 return [](const auto& __x0, const auto& __x1, const auto& __x2)
4435 _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4436 return concat(concat(__x0, __x1), __x2);
4437 }(__xs...);
4438 else if constexpr (__is_simd_wrapper_v<_RW> and sizeof...(__xs) > 3)
4439 return [](const auto& __x0, const auto& __x1, const auto&... __rest)
4440 _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4441 return concat(concat(__x0, __x1), concat(__rest...));
4442 }(__xs...);
4443 else
4444 {
4445 _Rp __r{};
4446 __store_pack_of_simd(reinterpret_cast<char*>(&__data(__r)), __xs...);
4447 return __r;
4448 }
4449 }
4450
4451// }}}
4452// concat(array<simd>) {{{
4453template <typename _Tp, typename _Abi, size_t _Np>
4454 _GLIBCXX_SIMD_ALWAYS_INLINE
4455 _GLIBCXX_SIMD_CONSTEXPR __deduced_simd<_Tp, simd_size_v<_Tp, _Abi> * _Np>
4456 concat(const array<simd<_Tp, _Abi>, _Np>& __x)
4457 {
4458 return __call_with_subscripts<_Np>(
4459 __x, [](const auto&... __xs) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
4460 return concat(__xs...);
4461 });
4462 }
4463
4464// }}}
4465
4466/// @cond undocumented
4467// _SmartReference {{{
4468template <typename _Up, typename _Accessor = _Up,
4469 typename _ValueType = typename _Up::value_type>
4470 class _SmartReference
4471 {
4472 friend _Accessor;
4473 int _M_index;
4474 _Up& _M_obj;
4475
4476 _GLIBCXX_SIMD_INTRINSIC constexpr _ValueType
4477 _M_read() const noexcept
4478 {
4479 if constexpr (is_arithmetic_v<_Up>)
4480 return _M_obj;
4481 else
4482 return _M_obj[_M_index];
4483 }
4484
4485 template <typename _Tp>
4486 _GLIBCXX_SIMD_INTRINSIC constexpr void
4487 _M_write(_Tp&& __x) const
4488 { _Accessor::_S_set(_M_obj, _M_index, static_cast<_Tp&&>(__x)); }
4489
4490 public:
4491 _GLIBCXX_SIMD_INTRINSIC constexpr
4492 _SmartReference(_Up& __o, int __i) noexcept
4493 : _M_index(__i), _M_obj(__o) {}
4494
4495 using value_type = _ValueType;
4496
4497 _GLIBCXX_SIMD_INTRINSIC
4498 _SmartReference(const _SmartReference&) = delete;
4499
4500 _GLIBCXX_SIMD_INTRINSIC constexpr
4501 operator value_type() const noexcept
4502 { return _M_read(); }
4503
4504 template <typename _Tp, typename = _ValuePreservingOrInt<__remove_cvref_t<_Tp>, value_type>>
4505 _GLIBCXX_SIMD_INTRINSIC constexpr _SmartReference
4506 operator=(_Tp&& __x) &&
4507 {
4508 _M_write(static_cast<_Tp&&>(__x));
4509 return {_M_obj, _M_index};
4510 }
4511
4512#define _GLIBCXX_SIMD_OP_(__op) \
4513 template <typename _Tp, \
4514 typename _TT = decltype(declval<value_type>() __op declval<_Tp>()), \
4515 typename = _ValuePreservingOrInt<__remove_cvref_t<_Tp>, _TT>, \
4516 typename = _ValuePreservingOrInt<_TT, value_type>> \
4517 _GLIBCXX_SIMD_INTRINSIC constexpr _SmartReference \
4518 operator __op##=(_Tp&& __x) && \
4519 { \
4520 const value_type& __lhs = _M_read(); \
4521 _M_write(__lhs __op __x); \
4522 return {_M_obj, _M_index}; \
4523 }
4524 _GLIBCXX_SIMD_ALL_ARITHMETICS(_GLIBCXX_SIMD_OP_);
4525 _GLIBCXX_SIMD_ALL_SHIFTS(_GLIBCXX_SIMD_OP_);
4526 _GLIBCXX_SIMD_ALL_BINARY(_GLIBCXX_SIMD_OP_);
4527#undef _GLIBCXX_SIMD_OP_
4528
4529 template <typename _Tp = void,
4530 typename = decltype(++declval<conditional_t<true, value_type, _Tp>&>())>
4531 _GLIBCXX_SIMD_INTRINSIC constexpr _SmartReference
4532 operator++() &&
4533 {
4534 value_type __x = _M_read();
4535 _M_write(++__x);
4536 return {_M_obj, _M_index};
4537 }
4538
4539 template <typename _Tp = void,
4540 typename = decltype(declval<conditional_t<true, value_type, _Tp>&>()++)>
4541 _GLIBCXX_SIMD_INTRINSIC constexpr value_type
4542 operator++(int) &&
4543 {
4544 const value_type __r = _M_read();
4545 value_type __x = __r;
4546 _M_write(++__x);
4547 return __r;
4548 }
4549
4550 template <typename _Tp = void,
4551 typename = decltype(--declval<conditional_t<true, value_type, _Tp>&>())>
4552 _GLIBCXX_SIMD_INTRINSIC constexpr _SmartReference
4553 operator--() &&
4554 {
4555 value_type __x = _M_read();
4556 _M_write(--__x);
4557 return {_M_obj, _M_index};
4558 }
4559
4560 template <typename _Tp = void,
4561 typename = decltype(declval<conditional_t<true, value_type, _Tp>&>()--)>
4562 _GLIBCXX_SIMD_INTRINSIC constexpr value_type
4563 operator--(int) &&
4564 {
4565 const value_type __r = _M_read();
4566 value_type __x = __r;
4567 _M_write(--__x);
4568 return __r;
4569 }
4570
4571 _GLIBCXX_SIMD_INTRINSIC friend void
4572 swap(_SmartReference&& __a, _SmartReference&& __b) noexcept(
4573 conjunction<
4574 is_nothrow_constructible<value_type, _SmartReference&&>,
4575 is_nothrow_assignable<_SmartReference&&, value_type&&>>::value)
4576 {
4577 value_type __tmp = static_cast<_SmartReference&&>(__a);
4578 static_cast<_SmartReference&&>(__a) = static_cast<value_type>(__b);
4579 static_cast<_SmartReference&&>(__b) = std::move(__tmp);
4580 }
4581
4582 _GLIBCXX_SIMD_INTRINSIC friend void
4583 swap(value_type& __a, _SmartReference&& __b) noexcept(
4584 conjunction<
4585 is_nothrow_constructible<value_type, value_type&&>,
4586 is_nothrow_assignable<value_type&, value_type&&>,
4587 is_nothrow_assignable<_SmartReference&&, value_type&&>>::value)
4588 {
4589 value_type __tmp(std::move(__a));
4590 __a = static_cast<value_type>(__b);
4591 static_cast<_SmartReference&&>(__b) = std::move(__tmp);
4592 }
4593
4594 _GLIBCXX_SIMD_INTRINSIC friend void
4595 swap(_SmartReference&& __a, value_type& __b) noexcept(
4596 conjunction<
4597 is_nothrow_constructible<value_type, _SmartReference&&>,
4598 is_nothrow_assignable<value_type&, value_type&&>,
4599 is_nothrow_assignable<_SmartReference&&, value_type&&>>::value)
4600 {
4601 value_type __tmp(__a);
4602 static_cast<_SmartReference&&>(__a) = std::move(__b);
4603 __b = std::move(__tmp);
4604 }
4605 };
4606
4607// }}}
4608// __scalar_abi_wrapper {{{
4609template <int _Bytes>
4610 struct __scalar_abi_wrapper
4611 {
4612 template <typename _Tp> static constexpr size_t _S_full_size = 1;
4613 template <typename _Tp> static constexpr size_t _S_size = 1;
4614 template <typename _Tp> static constexpr size_t _S_is_partial = false;
4615
4616 template <typename _Tp, typename _Abi = simd_abi::scalar>
4617 static constexpr bool _S_is_valid_v
4618 = _Abi::template _IsValid<_Tp>::value && sizeof(_Tp) == _Bytes;
4619 };
4620
4621// }}}
4622// __decay_abi metafunction {{{
4623template <typename _Tp>
4624 struct __decay_abi { using type = _Tp; };
4625
4626template <int _Bytes>
4627 struct __decay_abi<__scalar_abi_wrapper<_Bytes>>
4628 { using type = simd_abi::scalar; };
4629
4630// }}}
4631// __find_next_valid_abi metafunction {{{1
4632// Given an ABI tag A<N>, find an N2 < N such that A<N2>::_S_is_valid_v<_Tp> ==
4633// true, N2 is a power-of-2, and A<N2>::_S_is_partial<_Tp> is false. Break
4634// recursion at 2 elements in the resulting ABI tag. In this case
4635// type::_S_is_valid_v<_Tp> may be false.
4636template <template <int> class _Abi, int _Bytes, typename _Tp>
4637 struct __find_next_valid_abi
4638 {
4639 static constexpr auto
4640 _S_choose()
4641 {
4642 constexpr int _NextBytes = std::__bit_ceil((unsigned)_Bytes) / 2;
4643 using _NextAbi = _Abi<_NextBytes>;
4644 if constexpr (_NextBytes < sizeof(_Tp) * 2) // break recursion
4645 return _Abi<_Bytes>();
4646 else if constexpr (_NextAbi::template _S_is_partial<_Tp> == false
4647 && _NextAbi::template _S_is_valid_v<_Tp>)
4648 return _NextAbi();
4649 else
4650 return __find_next_valid_abi<_Abi, _NextBytes, _Tp>::_S_choose();
4651 }
4652
4653 using type = decltype(_S_choose());
4654 };
4655
4656template <int _Bytes, typename _Tp>
4657 struct __find_next_valid_abi<__scalar_abi_wrapper, _Bytes, _Tp>
4658 { using type = simd_abi::scalar; };
4659
4660// _AbiList {{{1
4661template <template <int> class...>
4662 struct _AbiList
4663 {
4664 template <typename, int> static constexpr bool _S_has_valid_abi = false;
4665 template <typename, int> using _FirstValidAbi = void;
4666 template <typename, int> using _BestAbi = void;
4667 };
4668
4669template <template <int> class _A0, template <int> class... _Rest>
4670 struct _AbiList<_A0, _Rest...>
4671 {
4672 template <typename _Tp, int _Np>
4673 static constexpr bool _S_has_valid_abi
4674 = _A0<sizeof(_Tp) * _Np>::template _S_is_valid_v<
4675 _Tp> || _AbiList<_Rest...>::template _S_has_valid_abi<_Tp, _Np>;
4676
4677 template <typename _Tp, int _Np>
4678 using _FirstValidAbi = conditional_t<
4679 _A0<sizeof(_Tp) * _Np>::template _S_is_valid_v<_Tp>,
4680 typename __decay_abi<_A0<sizeof(_Tp) * _Np>>::type,
4681 typename _AbiList<_Rest...>::template _FirstValidAbi<_Tp, _Np>>;
4682
4683 template <typename _Tp, int _Np>
4684 static constexpr auto
4685 _S_determine_best_abi()
4686 {
4687 static_assert(_Np >= 1);
4688 constexpr int _Bytes = sizeof(_Tp) * _Np;
4689 if constexpr (_Np == 1)
4690 return __make_dependent_t<_Tp, simd_abi::scalar>{};
4691 else
4692 {
4693 constexpr int __fullsize = _A0<_Bytes>::template _S_full_size<_Tp>;
4694 // _A0<_Bytes> is good if:
4695 // 1. The ABI tag is valid for _Tp
4696 // 2. The storage overhead is no more than padding to fill the next
4697 // power-of-2 number of bytes
4698 if constexpr (_A0<_Bytes>::template _S_is_valid_v<_Tp>
4699 && ((__is_sve_abi<_A0<_Bytes>>() && __have_sve
4700 && (_Np <= __sve_vectorized_size_bytes/sizeof(_Tp)))
4701 || (__fullsize / 2 < _Np))
4702 )
4703 return typename __decay_abi<_A0<_Bytes>>::type{};
4704 else
4705 {
4706 using _Bp =
4707 typename __find_next_valid_abi<_A0, _Bytes, _Tp>::type;
4708 if constexpr (_Bp::template _S_is_valid_v<
4709 _Tp> && _Bp::template _S_size<_Tp> <= _Np)
4710 return _Bp{};
4711 else
4712 return
4713 typename _AbiList<_Rest...>::template _BestAbi<_Tp, _Np>{};
4714 }
4715 }
4716 }
4717
4718 template <typename _Tp, int _Np>
4719 using _BestAbi = decltype(_S_determine_best_abi<_Tp, _Np>());
4720 };
4721
4722// }}}1
4723
4724// the following lists all native ABIs, which makes them accessible to
4725// simd_abi::deduce and select_best_vector_type_t (for fixed_size). Order
4726// matters: Whatever comes first has higher priority.
4727using _AllNativeAbis = _AbiList<
4728#if _GLIBCXX_SIMD_HAVE_SVE
4729 simd_abi::_SveAbi,
4730#endif
4731 simd_abi::_VecBltnBtmsk, simd_abi::_VecBuiltin, __scalar_abi_wrapper>;
4732
4733using _NoSveAllNativeAbis = _AbiList<simd_abi::_VecBltnBtmsk, simd_abi::_VecBuiltin,
4734 __scalar_abi_wrapper>;
4735
4736// valid _SimdTraits specialization {{{1
4737template <typename _Tp, typename _Abi>
4738 struct _SimdTraits<_Tp, _Abi, void_t<typename _Abi::template _IsValid<_Tp>>>
4739 : _Abi::template __traits<_Tp> {};
4740
4741// __deduce_impl specializations {{{1
4742// try all native ABIs (including scalar) first
4743template <typename _Tp, size_t _Np>
4744 struct __deduce_impl<
4745 _Tp, _Np, enable_if_t<_AllNativeAbis::template _S_has_valid_abi<_Tp, _Np>>>
4746 { using type = _AllNativeAbis::_FirstValidAbi<_Tp, _Np>; };
4747
4748template <typename _Tp, size_t _Np>
4749 struct __no_sve_deduce_impl<
4750 _Tp, _Np, enable_if_t<_NoSveAllNativeAbis::template _S_has_valid_abi<_Tp, _Np>>>
4751 { using type = _NoSveAllNativeAbis::_FirstValidAbi<_Tp, _Np>; };
4752
4753// fall back to fixed_size only if scalar and native ABIs don't match
4754template <typename _Tp, size_t _Np, typename = void>
4755 struct __deduce_fixed_size_fallback {};
4756
4757template <typename _Tp, size_t _Np>
4758 struct __deduce_fixed_size_fallback<_Tp, _Np,
4759 enable_if_t<simd_abi::fixed_size<_Np>::template _S_is_valid_v<_Tp>>>
4760 { using type = simd_abi::fixed_size<_Np>; };
4761
4762template <typename _Tp, size_t _Np, typename>
4763 struct __deduce_impl : public __deduce_fixed_size_fallback<_Tp, _Np> {};
4764
4765template <typename _Tp, size_t _Np, typename>
4766 struct __no_sve_deduce_impl
4767 : public __deduce_fixed_size_fallback<_Tp, _Np>
4768 {};
4769
4770
4771//}}}1
4772/// @endcond
4773
4774// simd_mask {{{
4775template <typename _Tp, typename _Abi>
4776 class simd_mask : public _SimdTraits<_Tp, _Abi>::_MaskBase
4777 {
4778 // types, tags, and friends {{{
4779 using _Traits = _SimdTraits<_Tp, _Abi>;
4780 using _MemberType = typename _Traits::_MaskMember;
4781
4782 // We map all masks with equal element sizeof to a single integer type, the
4783 // one given by __int_for_sizeof_t<_Tp>. This is the approach
4784 // [[gnu::vector_size(N)]] types take as well and it reduces the number of
4785 // template specializations in the implementation classes.
4786 using _Ip = __int_for_sizeof_t<_Tp>;
4787 static constexpr _Ip* _S_type_tag = nullptr;
4788
4789 friend typename _Traits::_MaskBase;
4790 friend class simd<_Tp, _Abi>; // to construct masks on return
4791 friend typename _Traits::_SimdImpl; // to construct masks on return and
4792 // inspect data on masked operations
4793 public:
4794 using _Impl = typename _Traits::_MaskImpl;
4795 friend _Impl;
4796
4797 // }}}
4798 // member types {{{
4799 using value_type = bool;
4800 using reference = _SmartReference<_MemberType, _Impl, value_type>;
4801 using simd_type = simd<_Tp, _Abi>;
4802 using abi_type = _Abi;
4803
4804 // }}}
4805 static constexpr size_t size() // {{{
4806 { return __size_or_zero_v<_Tp, _Abi>; }
4807
4808 // }}}
4809 // constructors & assignment {{{
4810 simd_mask() = default;
4811 simd_mask(const simd_mask&) = default;
4812 simd_mask(simd_mask&&) = default;
4813 simd_mask& operator=(const simd_mask&) = default;
4814 simd_mask& operator=(simd_mask&&) = default;
4815
4816 // }}}
4817 // access to internal representation (optional feature) {{{
4818 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR explicit
4819 simd_mask(typename _Traits::_MaskCastType __init)
4820 : _M_data{__init} {}
4821 // conversions to internal type is done in _MaskBase
4822
4823 // }}}
4824 // bitset interface (extension to be proposed) {{{
4825 // TS_FEEDBACK:
4826 // Conversion of simd_mask to and from bitset makes it much easier to
4827 // interface with other facilities. I suggest adding `static
4828 // simd_mask::from_bitset` and `simd_mask::to_bitset`.
4829 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR static simd_mask
4830 __from_bitset(bitset<size()> bs)
4831 { return {__bitset_init, bs}; }
4832
4833 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bitset<size()>
4834 __to_bitset() const
4835 { return _Impl::_S_to_bits(_M_data)._M_to_bitset(); }
4836
4837 // }}}
4838 // explicit broadcast constructor {{{
4839 _GLIBCXX_SIMD_ALWAYS_INLINE explicit _GLIBCXX_SIMD_CONSTEXPR
4840 simd_mask(value_type __x)
4841 : _M_data(_Impl::template _S_broadcast<_Ip>(__x)) {}
4842
4843 // }}}
4844 // implicit type conversion constructor {{{
4845 #ifdef _GLIBCXX_SIMD_ENABLE_IMPLICIT_MASK_CAST
4846 // proposed improvement
4847 template <typename _Up, typename _A2,
4848 typename = enable_if_t<simd_size_v<_Up, _A2> == size()>>
4849 _GLIBCXX_SIMD_ALWAYS_INLINE explicit(sizeof(_MemberType)
4850 != sizeof(typename _SimdTraits<_Up, _A2>::_MaskMember))
4851 simd_mask(const simd_mask<_Up, _A2>& __x)
4852 : simd_mask(__proposed::static_simd_cast<simd_mask>(__x)) {}
4853 #else
4854 // conforming to ISO/IEC 19570:2018
4855 template <typename _Up, typename = enable_if_t<conjunction<
4856 is_same<abi_type, simd_abi::fixed_size<size()>>,
4857 is_same<_Up, _Up>>::value>>
4858 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
4859 simd_mask(const simd_mask<_Up, simd_abi::fixed_size<size()>>& __x)
4860 : _M_data(_Impl::_S_from_bitmask(__data(__x), _S_type_tag)) {}
4861 #endif
4862
4863 // }}}
4864 // load constructor {{{
4865 template <typename _Flags>
4866 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
4867 simd_mask(const value_type* __mem, _IsSimdFlagType<_Flags>)
4868 : _M_data(_Impl::template _S_load<_Ip>(_Flags::template _S_apply<simd_mask>(__mem))) {}
4869
4870 template <typename _Flags>
4871 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
4872 simd_mask(const value_type* __mem, simd_mask __k, _IsSimdFlagType<_Flags>)
4873 : _M_data{}
4874 {
4875 _M_data = _Impl::_S_masked_load(_M_data, __k._M_data,
4876 _Flags::template _S_apply<simd_mask>(__mem));
4877 }
4878
4879 // }}}
4880 // loads [simd_mask.load] {{{
4881 template <typename _Flags>
4882 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR void
4883 copy_from(const value_type* __mem, _IsSimdFlagType<_Flags>)
4884 { _M_data = _Impl::template _S_load<_Ip>(_Flags::template _S_apply<simd_mask>(__mem)); }
4885
4886 // }}}
4887 // stores [simd_mask.store] {{{
4888 template <typename _Flags>
4889 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR void
4890 copy_to(value_type* __mem, _IsSimdFlagType<_Flags>) const
4891 { _Impl::_S_store(_M_data, _Flags::template _S_apply<simd_mask>(__mem)); }
4892
4893 // }}}
4894 // scalar access {{{
4895 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR reference
4896 operator[](size_t __i)
4897 {
4898 if (__i >= size())
4899 __invoke_ub("Subscript %d is out of range [0, %d]", __i, size() - 1);
4900 return {_M_data, int(__i)};
4901 }
4902
4903 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR value_type
4904 operator[](size_t __i) const
4905 {
4906 if (__i >= size())
4907 __invoke_ub("Subscript %d is out of range [0, %d]", __i, size() - 1);
4908 if constexpr (__is_scalar_abi<_Abi>())
4909 return _M_data;
4910 else
4911 return static_cast<bool>(_M_data[__i]);
4912 }
4913
4914 // }}}
4915 // negation {{{
4916 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd_mask
4917 operator!() const
4918 { return {__private_init, _Impl::_S_bit_not(_M_data)}; }
4919
4920 // }}}
4921 // simd_mask binary operators [simd_mask.binary] {{{
4922 #ifdef _GLIBCXX_SIMD_ENABLE_IMPLICIT_MASK_CAST
4923 // simd_mask<int> && simd_mask<uint> needs disambiguation
4924 template <typename _Up, typename _A2,
4925 typename = enable_if_t<is_convertible_v<simd_mask<_Up, _A2>, simd_mask>>>
4926 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4927 operator&&(const simd_mask& __x, const simd_mask<_Up, _A2>& __y)
4928 {
4929 return {__private_init,
4930 _Impl::_S_logical_and(__x._M_data, simd_mask(__y)._M_data)};
4931 }
4932
4933 template <typename _Up, typename _A2,
4934 typename = enable_if_t<is_convertible_v<simd_mask<_Up, _A2>, simd_mask>>>
4935 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4936 operator||(const simd_mask& __x, const simd_mask<_Up, _A2>& __y)
4937 {
4938 return {__private_init,
4939 _Impl::_S_logical_or(__x._M_data, simd_mask(__y)._M_data)};
4940 }
4941 #endif // _GLIBCXX_SIMD_ENABLE_IMPLICIT_MASK_CAST
4942
4943 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4944 operator&&(const simd_mask& __x, const simd_mask& __y)
4945 { return {__private_init, _Impl::_S_logical_and(__x._M_data, __y._M_data)}; }
4946
4947 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4948 operator||(const simd_mask& __x, const simd_mask& __y)
4949 { return {__private_init, _Impl::_S_logical_or(__x._M_data, __y._M_data)}; }
4950
4951 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4952 operator&(const simd_mask& __x, const simd_mask& __y)
4953 { return {__private_init, _Impl::_S_bit_and(__x._M_data, __y._M_data)}; }
4954
4955 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4956 operator|(const simd_mask& __x, const simd_mask& __y)
4957 { return {__private_init, _Impl::_S_bit_or(__x._M_data, __y._M_data)}; }
4958
4959 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4960 operator^(const simd_mask& __x, const simd_mask& __y)
4961 { return {__private_init, _Impl::_S_bit_xor(__x._M_data, __y._M_data)}; }
4962
4963 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask&
4964 operator&=(simd_mask& __x, const simd_mask& __y)
4965 {
4966 __x._M_data = _Impl::_S_bit_and(__x._M_data, __y._M_data);
4967 return __x;
4968 }
4969
4970 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask&
4971 operator|=(simd_mask& __x, const simd_mask& __y)
4972 {
4973 __x._M_data = _Impl::_S_bit_or(__x._M_data, __y._M_data);
4974 return __x;
4975 }
4976
4977 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask&
4978 operator^=(simd_mask& __x, const simd_mask& __y)
4979 {
4980 __x._M_data = _Impl::_S_bit_xor(__x._M_data, __y._M_data);
4981 return __x;
4982 }
4983
4984 // }}}
4985 // simd_mask compares [simd_mask.comparison] {{{
4986 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4987 operator==(const simd_mask& __x, const simd_mask& __y)
4988 { return !operator!=(__x, __y); }
4989
4990 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
4991 operator!=(const simd_mask& __x, const simd_mask& __y)
4992 { return {__private_init, _Impl::_S_bit_xor(__x._M_data, __y._M_data)}; }
4993
4994 // }}}
4995 // private_init ctor {{{
4996 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
4997 simd_mask(_PrivateInit, typename _Traits::_MaskMember __init)
4998 : _M_data(__init) {}
4999
5000 // }}}
5001 // private_init generator ctor {{{
5002 template <typename _Fp, typename = decltype(bool(declval<_Fp>()(size_t())))>
5003 _GLIBCXX_SIMD_INTRINSIC constexpr
5004 simd_mask(_PrivateInit, _Fp&& __gen)
5005 : _M_data()
5006 {
5007 __execute_n_times<size()>([&](auto __i) constexpr _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
5008 _Impl::_S_set(_M_data, __i, __gen(__i));
5009 });
5010 }
5011
5012 // }}}
5013 // bitset_init ctor {{{
5014 _GLIBCXX_SIMD_INTRINSIC constexpr
5015 simd_mask(_BitsetInit, bitset<size()> __init)
5016 : _M_data(_Impl::_S_from_bitmask(_SanitizedBitMask<size()>(__init), _S_type_tag))
5017 {}
5018
5019 // }}}
5020 // __cvt {{{
5021 // TS_FEEDBACK:
5022 // The conversion operator this implements should be a ctor on simd_mask.
5023 // Once you call .__cvt() on a simd_mask it converts conveniently.
5024 // A useful variation: add `explicit(sizeof(_Tp) != sizeof(_Up))`
5025 struct _CvtProxy
5026 {
5027 template <typename _Up, typename _A2,
5028 typename = enable_if_t<simd_size_v<_Up, _A2> == simd_size_v<_Tp, _Abi>>>
5029 _GLIBCXX_SIMD_ALWAYS_INLINE
5030 operator simd_mask<_Up, _A2>() &&
5031 {
5032 using namespace std::experimental::__proposed;
5033 return static_simd_cast<simd_mask<_Up, _A2>>(_M_data);
5034 }
5035
5036 const simd_mask<_Tp, _Abi>& _M_data;
5037 };
5038
5039 _GLIBCXX_SIMD_INTRINSIC _CvtProxy
5040 __cvt() const
5041 { return {*this}; }
5042
5043 // }}}
5044 // operator?: overloads (suggested extension) {{{
5045 #ifdef __GXX_CONDITIONAL_IS_OVERLOADABLE__
5046 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
5047 operator?:(const simd_mask& __k, const simd_mask& __where_true,
5048 const simd_mask& __where_false)
5049 {
5050 auto __ret = __where_false;
5051 _Impl::_S_masked_assign(__k._M_data, __ret._M_data, __where_true._M_data);
5052 return __ret;
5053 }
5054
5055 template <typename _U1, typename _U2,
5056 typename _Rp = simd<common_type_t<_U1, _U2>, _Abi>,
5057 typename = enable_if_t<conjunction_v<
5058 is_convertible<_U1, _Rp>, is_convertible<_U2, _Rp>,
5059 is_convertible<simd_mask, typename _Rp::mask_type>>>>
5060 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend _Rp
5061 operator?:(const simd_mask& __k, const _U1& __where_true,
5062 const _U2& __where_false)
5063 {
5064 _Rp __ret = __where_false;
5065 _Rp::_Impl::_S_masked_assign(
5066 __data(static_cast<typename _Rp::mask_type>(__k)), __data(__ret),
5067 __data(static_cast<_Rp>(__where_true)));
5068 return __ret;
5069 }
5070
5071 #ifdef _GLIBCXX_SIMD_ENABLE_IMPLICIT_MASK_CAST
5072 template <typename _Kp, typename _Ak, typename _Up, typename _Au,
5073 typename = enable_if_t<
5074 conjunction_v<is_convertible<simd_mask<_Kp, _Ak>, simd_mask>,
5075 is_convertible<simd_mask<_Up, _Au>, simd_mask>>>>
5076 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd_mask
5077 operator?:(const simd_mask<_Kp, _Ak>& __k, const simd_mask& __where_true,
5078 const simd_mask<_Up, _Au>& __where_false)
5079 {
5080 simd_mask __ret = __where_false;
5081 _Impl::_S_masked_assign(simd_mask(__k)._M_data, __ret._M_data,
5082 __where_true._M_data);
5083 return __ret;
5084 }
5085 #endif // _GLIBCXX_SIMD_ENABLE_IMPLICIT_MASK_CAST
5086 #endif // __GXX_CONDITIONAL_IS_OVERLOADABLE__
5087
5088 // }}}
5089 // _M_is_constprop {{{
5090 _GLIBCXX_SIMD_INTRINSIC constexpr bool
5091 _M_is_constprop() const
5092 {
5093 if constexpr (__is_scalar_abi<_Abi>())
5094 return __builtin_constant_p(_M_data);
5095 else
5096 return _M_data._M_is_constprop();
5097 }
5098
5099 // }}}
5100
5101 private:
5102 friend const auto& __data<_Tp, abi_type>(const simd_mask&);
5103 friend auto& __data<_Tp, abi_type>(simd_mask&);
5104 alignas(_Traits::_S_mask_align) _MemberType _M_data;
5105 };
5106
5107// }}}
5108
5109/// @cond undocumented
5110// __data(simd_mask) {{{
5111template <typename _Tp, typename _Ap>
5112 _GLIBCXX_SIMD_INTRINSIC constexpr const auto&
5113 __data(const simd_mask<_Tp, _Ap>& __x)
5114 { return __x._M_data; }
5115
5116template <typename _Tp, typename _Ap>
5117 _GLIBCXX_SIMD_INTRINSIC constexpr auto&
5118 __data(simd_mask<_Tp, _Ap>& __x)
5119 { return __x._M_data; }
5120
5121// }}}
5122/// @endcond
5123
5124// simd_mask reductions [simd_mask.reductions] {{{
5125template <typename _Tp, typename _Abi>
5126 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5127 all_of(const simd_mask<_Tp, _Abi>& __k) noexcept
5128 {
5129 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5130 {
5131 for (size_t __i = 0; __i < simd_size_v<_Tp, _Abi>; ++__i)
5132 if (!__k[__i])
5133 return false;
5134 return true;
5135 }
5136 else
5137 return _Abi::_MaskImpl::_S_all_of(__k);
5138 }
5139
5140template <typename _Tp, typename _Abi>
5141 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5142 any_of(const simd_mask<_Tp, _Abi>& __k) noexcept
5143 {
5144 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5145 {
5146 for (size_t __i = 0; __i < simd_size_v<_Tp, _Abi>; ++__i)
5147 if (__k[__i])
5148 return true;
5149 return false;
5150 }
5151 else
5152 return _Abi::_MaskImpl::_S_any_of(__k);
5153 }
5154
5155template <typename _Tp, typename _Abi>
5156 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5157 none_of(const simd_mask<_Tp, _Abi>& __k) noexcept
5158 {
5159 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5160 {
5161 for (size_t __i = 0; __i < simd_size_v<_Tp, _Abi>; ++__i)
5162 if (__k[__i])
5163 return false;
5164 return true;
5165 }
5166 else
5167 return _Abi::_MaskImpl::_S_none_of(__k);
5168 }
5169
5170template <typename _Tp, typename _Abi>
5171 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5172 some_of(const simd_mask<_Tp, _Abi>& __k) noexcept
5173 {
5174 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5175 {
5176 for (size_t __i = 1; __i < simd_size_v<_Tp, _Abi>; ++__i)
5177 if (__k[__i] != __k[__i - 1])
5178 return true;
5179 return false;
5180 }
5181 else
5182 return _Abi::_MaskImpl::_S_some_of(__k);
5183 }
5184
5185template <typename _Tp, typename _Abi>
5186 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR int
5187 popcount(const simd_mask<_Tp, _Abi>& __k) noexcept
5188 {
5189 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5190 {
5191 const int __r = __call_with_subscripts<simd_size_v<_Tp, _Abi>>(
5192 __k, [](auto... __elements) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
5193 return ((__elements != 0) + ...);
5194 });
5195 if (__builtin_is_constant_evaluated() || __builtin_constant_p(__r))
5196 return __r;
5197 }
5198 return _Abi::_MaskImpl::_S_popcount(__k);
5199 }
5200
5201template <typename _Tp, typename _Abi>
5202 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR int
5203 find_first_set(const simd_mask<_Tp, _Abi>& __k)
5204 {
5205 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5206 {
5207 constexpr size_t _Np = simd_size_v<_Tp, _Abi>;
5208 const size_t _Idx = __call_with_n_evaluations<_Np>(
5209 [](auto... __indexes) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
5210 return std::min({__indexes...});
5211 }, [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
5212 return __k[__i] ? +__i : _Np;
5213 });
5214 if (_Idx >= _Np)
5215 __invoke_ub("find_first_set(empty mask) is UB");
5216 if (__builtin_constant_p(_Idx))
5217 return _Idx;
5218 }
5219 return _Abi::_MaskImpl::_S_find_first_set(__k);
5220 }
5221
5222template <typename _Tp, typename _Abi>
5223 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR int
5224 find_last_set(const simd_mask<_Tp, _Abi>& __k)
5225 {
5226 if (__builtin_is_constant_evaluated() || __k._M_is_constprop())
5227 {
5228 constexpr size_t _Np = simd_size_v<_Tp, _Abi>;
5229 const int _Idx = __call_with_n_evaluations<_Np>(
5230 [](auto... __indexes) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
5231 return std::max({__indexes...});
5232 }, [&](auto __i) _GLIBCXX_SIMD_ALWAYS_INLINE_LAMBDA {
5233 return __k[__i] ? int(__i) : -1;
5234 });
5235 if (_Idx < 0)
5236 __invoke_ub("find_first_set(empty mask) is UB");
5237 if (__builtin_constant_p(_Idx))
5238 return _Idx;
5239 }
5240 return _Abi::_MaskImpl::_S_find_last_set(__k);
5241 }
5242
5243_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5244all_of(_ExactBool __x) noexcept
5245{ return __x; }
5246
5247_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5248any_of(_ExactBool __x) noexcept
5249{ return __x; }
5250
5251_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5252none_of(_ExactBool __x) noexcept
5253{ return !__x; }
5254
5255_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR bool
5256some_of(_ExactBool) noexcept
5257{ return false; }
5258
5259_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR int
5260popcount(_ExactBool __x) noexcept
5261{ return __x; }
5262
5263_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR int
5264find_first_set(_ExactBool)
5265{ return 0; }
5266
5267_GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR int
5268find_last_set(_ExactBool)
5269{ return 0; }
5270
5271// }}}
5272
5273/// @cond undocumented
5274// _SimdIntOperators{{{1
5275template <typename _V, typename _Tp, typename _Abi, bool>
5276 class _SimdIntOperators {};
5277
5278template <typename _V, typename _Tp, typename _Abi>
5279 class _SimdIntOperators<_V, _Tp, _Abi, true>
5280 {
5281 using _Impl = typename _SimdTraits<_Tp, _Abi>::_SimdImpl;
5282
5283 _GLIBCXX_SIMD_INTRINSIC constexpr const _V&
5284 __derived() const
5285 { return *static_cast<const _V*>(this); }
5286
5287 template <typename _Up>
5288 _GLIBCXX_SIMD_INTRINSIC static _GLIBCXX_SIMD_CONSTEXPR _V
5289 _S_make_derived(_Up&& __d)
5290 { return {__private_init, static_cast<_Up&&>(__d)}; }
5291
5292 public:
5293 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5294 _V&
5295 operator%=(_V& __lhs, const _V& __x)
5296 { return __lhs = __lhs % __x; }
5297
5298 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5299 _V&
5300 operator&=(_V& __lhs, const _V& __x)
5301 { return __lhs = __lhs & __x; }
5302
5303 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5304 _V&
5305 operator|=(_V& __lhs, const _V& __x)
5306 { return __lhs = __lhs | __x; }
5307
5308 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5309 _V&
5310 operator^=(_V& __lhs, const _V& __x)
5311 { return __lhs = __lhs ^ __x; }
5312
5313 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5314 _V&
5315 operator<<=(_V& __lhs, const _V& __x)
5316 { return __lhs = __lhs << __x; }
5317
5318 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5319 _V&
5320 operator>>=(_V& __lhs, const _V& __x)
5321 { return __lhs = __lhs >> __x; }
5322
5323 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5324 _V&
5325 operator<<=(_V& __lhs, int __x)
5326 { return __lhs = __lhs << __x; }
5327
5328 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5329 _V&
5330 operator>>=(_V& __lhs, int __x)
5331 { return __lhs = __lhs >> __x; }
5332
5333 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5334 _V
5335 operator%(const _V& __x, const _V& __y)
5336 {
5337 return _SimdIntOperators::_S_make_derived(
5338 _Impl::_S_modulus(__data(__x), __data(__y)));
5339 }
5340
5341 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5342 _V
5343 operator&(const _V& __x, const _V& __y)
5344 {
5345 return _SimdIntOperators::_S_make_derived(
5346 _Impl::_S_bit_and(__data(__x), __data(__y)));
5347 }
5348
5349 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5350 _V
5351 operator|(const _V& __x, const _V& __y)
5352 {
5353 return _SimdIntOperators::_S_make_derived(
5354 _Impl::_S_bit_or(__data(__x), __data(__y)));
5355 }
5356
5357 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5358 _V
5359 operator^(const _V& __x, const _V& __y)
5360 {
5361 return _SimdIntOperators::_S_make_derived(
5362 _Impl::_S_bit_xor(__data(__x), __data(__y)));
5363 }
5364
5365 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5366 _V
5367 operator<<(const _V& __x, const _V& __y)
5368 {
5369 return _SimdIntOperators::_S_make_derived(
5370 _Impl::_S_bit_shift_left(__data(__x), __data(__y)));
5371 }
5372
5373 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5374 _V
5375 operator>>(const _V& __x, const _V& __y)
5376 {
5377 return _SimdIntOperators::_S_make_derived(
5378 _Impl::_S_bit_shift_right(__data(__x), __data(__y)));
5379 }
5380
5381 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5382 _V
5383 operator<<(const _V& __x, int __y)
5384 {
5385 if (__y < 0)
5386 __invoke_ub("The behavior is undefined if the right operand of a "
5387 "shift operation is negative. [expr.shift]\nA shift by "
5388 "%d was requested",
5389 __y);
5390 if (size_t(__y) >= sizeof(declval<_Tp>() << __y) * __CHAR_BIT__)
5391 __invoke_ub(
5392 "The behavior is undefined if the right operand of a "
5393 "shift operation is greater than or equal to the width of the "
5394 "promoted left operand. [expr.shift]\nA shift by %d was requested",
5395 __y);
5396 return _SimdIntOperators::_S_make_derived(
5397 _Impl::_S_bit_shift_left(__data(__x), __y));
5398 }
5399
5400 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend
5401 _V
5402 operator>>(const _V& __x, int __y)
5403 {
5404 if (__y < 0)
5405 __invoke_ub(
5406 "The behavior is undefined if the right operand of a shift "
5407 "operation is negative. [expr.shift]\nA shift by %d was requested",
5408 __y);
5409 if (size_t(__y) >= sizeof(declval<_Tp>() << __y) * __CHAR_BIT__)
5410 __invoke_ub(
5411 "The behavior is undefined if the right operand of a shift "
5412 "operation is greater than or equal to the width of the promoted "
5413 "left operand. [expr.shift]\nA shift by %d was requested",
5414 __y);
5415 return _SimdIntOperators::_S_make_derived(
5416 _Impl::_S_bit_shift_right(__data(__x), __y));
5417 }
5418
5419 // unary operators (for integral _Tp)
5420 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
5421 _V
5422 operator~() const
5423 { return {__private_init, _Impl::_S_complement(__derived()._M_data)}; }
5424 };
5425
5426//}}}1
5427/// @endcond
5428
5429// simd {{{
5430template <typename _Tp, typename _Abi>
5431 class simd : public _SimdIntOperators<
5432 simd<_Tp, _Abi>, _Tp, _Abi,
5433 conjunction<is_integral<_Tp>,
5434 typename _SimdTraits<_Tp, _Abi>::_IsValid>::value>,
5435 public _SimdTraits<_Tp, _Abi>::_SimdBase
5436 {
5437 using _Traits = _SimdTraits<_Tp, _Abi>;
5438 using _MemberType = typename _Traits::_SimdMember;
5439 using _CastType = typename _Traits::_SimdCastType;
5440 static constexpr _Tp* _S_type_tag = nullptr;
5441 friend typename _Traits::_SimdBase;
5442
5443 public:
5444 using _Impl = typename _Traits::_SimdImpl;
5445 friend _Impl;
5446 friend _SimdIntOperators<simd, _Tp, _Abi, true>;
5447
5448 using value_type = _Tp;
5449 using reference = _SmartReference<_MemberType, _Impl, value_type>;
5450 using mask_type = simd_mask<_Tp, _Abi>;
5451 using abi_type = _Abi;
5452
5453 static constexpr size_t size()
5454 { return __size_or_zero_v<_Tp, _Abi>; }
5455
5456 _GLIBCXX_SIMD_CONSTEXPR simd() = default;
5457 _GLIBCXX_SIMD_CONSTEXPR simd(const simd&) = default;
5458 _GLIBCXX_SIMD_CONSTEXPR simd(simd&&) noexcept = default;
5459 _GLIBCXX_SIMD_CONSTEXPR simd& operator=(const simd&) = default;
5460 _GLIBCXX_SIMD_CONSTEXPR simd& operator=(simd&&) noexcept = default;
5461
5462 // implicit broadcast constructor
5463 template <typename _Up,
5464 typename = enable_if_t<!is_same_v<__remove_cvref_t<_Up>, bool>>>
5465 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
5466 simd(_ValuePreservingOrInt<_Up, value_type>&& __x)
5467 : _M_data(
5468 _Impl::_S_broadcast(static_cast<value_type>(static_cast<_Up&&>(__x))))
5469 {}
5470
5471 // implicit type conversion constructor (convert from fixed_size to
5472 // fixed_size)
5473 template <typename _Up>
5474 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
5475 simd(const simd<_Up, simd_abi::fixed_size<size()>>& __x,
5476 enable_if_t<
5477 conjunction<
5478 is_same<simd_abi::fixed_size<size()>, abi_type>,
5479 negation<__is_narrowing_conversion<_Up, value_type>>,
5480 __converts_to_higher_integer_rank<_Up, value_type>>::value,
5481 void*> = nullptr)
5482 : simd{static_cast<array<_Up, size()>>(__x).data(), vector_aligned} {}
5483
5484 // explicit type conversion constructor
5485#ifdef _GLIBCXX_SIMD_ENABLE_STATIC_CAST
5486 template <typename _Up, typename _A2,
5487 typename = decltype(static_simd_cast<simd>(
5488 declval<const simd<_Up, _A2>&>()))>
5489 _GLIBCXX_SIMD_ALWAYS_INLINE explicit _GLIBCXX_SIMD_CONSTEXPR
5490 simd(const simd<_Up, _A2>& __x)
5491 : simd(static_simd_cast<simd>(__x)) {}
5492#endif // _GLIBCXX_SIMD_ENABLE_STATIC_CAST
5493
5494 // generator constructor
5495 template <typename _Fp>
5496 _GLIBCXX_SIMD_ALWAYS_INLINE explicit _GLIBCXX_SIMD_CONSTEXPR
5497 simd(_Fp&& __gen, _ValuePreservingOrInt<decltype(declval<_Fp>()(
5498 declval<_SizeConstant<0>&>())),
5499 value_type>* = nullptr)
5500 : _M_data(_Impl::_S_generator(static_cast<_Fp&&>(__gen), _S_type_tag)) {}
5501
5502 // load constructor
5503 template <typename _Up, typename _Flags>
5504 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR
5505 simd(const _Up* __mem, _IsSimdFlagType<_Flags>)
5506 : _M_data(
5507 _Impl::_S_load(_Flags::template _S_apply<simd>(__mem), _S_type_tag))
5508 {}
5509
5510 // loads [simd.load]
5511 template <typename _Up, typename _Flags>
5512 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR void
5513 copy_from(const _Vectorizable<_Up>* __mem, _IsSimdFlagType<_Flags>)
5514 {
5515 _M_data = static_cast<decltype(_M_data)>(
5516 _Impl::_S_load(_Flags::template _S_apply<simd>(__mem), _S_type_tag));
5517 }
5518
5519 // stores [simd.store]
5520 template <typename _Up, typename _Flags>
5521 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR void
5522 copy_to(_Vectorizable<_Up>* __mem, _IsSimdFlagType<_Flags>) const
5523 {
5524 _Impl::_S_store(_M_data, _Flags::template _S_apply<simd>(__mem),
5525 _S_type_tag);
5526 }
5527
5528 // scalar access
5529 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR reference
5530 operator[](size_t __i)
5531 { return {_M_data, int(__i)}; }
5532
5533 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR value_type
5534 operator[]([[maybe_unused]] size_t __i) const
5535 {
5536 if constexpr (__is_scalar_abi<_Abi>())
5537 {
5538 _GLIBCXX_DEBUG_ASSERT(__i == 0);
5539 return _M_data;
5540 }
5541 else
5542 return _M_data[__i];
5543 }
5544
5545 // increment and decrement:
5546 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd&
5547 operator++()
5548 {
5549 _Impl::_S_increment(_M_data);
5550 return *this;
5551 }
5552
5553 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd
5554 operator++(int)
5555 {
5556 simd __r = *this;
5557 _Impl::_S_increment(_M_data);
5558 return __r;
5559 }
5560
5561 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd&
5562 operator--()
5563 {
5564 _Impl::_S_decrement(_M_data);
5565 return *this;
5566 }
5567
5568 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd
5569 operator--(int)
5570 {
5571 simd __r = *this;
5572 _Impl::_S_decrement(_M_data);
5573 return __r;
5574 }
5575
5576 // unary operators (for any _Tp)
5577 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR mask_type
5578 operator!() const
5579 { return {__private_init, _Impl::_S_negate(_M_data)}; }
5580
5581 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd
5582 operator+() const
5583 { return *this; }
5584
5585 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR simd
5586 operator-() const
5587 { return {__private_init, _Impl::_S_unary_minus(_M_data)}; }
5588
5589 // access to internal representation (suggested extension)
5590 _GLIBCXX_SIMD_ALWAYS_INLINE explicit _GLIBCXX_SIMD_CONSTEXPR
5591 simd(_CastType __init) : _M_data(__init) {}
5592
5593 // compound assignment [simd.cassign]
5594 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd&
5595 operator+=(simd& __lhs, const simd& __x)
5596 { return __lhs = __lhs + __x; }
5597
5598 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd&
5599 operator-=(simd& __lhs, const simd& __x)
5600 { return __lhs = __lhs - __x; }
5601
5602 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd&
5603 operator*=(simd& __lhs, const simd& __x)
5604 { return __lhs = __lhs * __x; }
5605
5606 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd&
5607 operator/=(simd& __lhs, const simd& __x)
5608 { return __lhs = __lhs / __x; }
5609
5610 // binary operators [simd.binary]
5611 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd
5612 operator+(const simd& __x, const simd& __y)
5613 { return {__private_init, _Impl::_S_plus(__x._M_data, __y._M_data)}; }
5614
5615 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd
5616 operator-(const simd& __x, const simd& __y)
5617 { return {__private_init, _Impl::_S_minus(__x._M_data, __y._M_data)}; }
5618
5619 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd
5620 operator*(const simd& __x, const simd& __y)
5621 { return {__private_init, _Impl::_S_multiplies(__x._M_data, __y._M_data)}; }
5622
5623 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd
5624 operator/(const simd& __x, const simd& __y)
5625 { return {__private_init, _Impl::_S_divides(__x._M_data, __y._M_data)}; }
5626
5627 // compares [simd.comparison]
5628 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend mask_type
5629 operator==(const simd& __x, const simd& __y)
5630 { return simd::_S_make_mask(_Impl::_S_equal_to(__x._M_data, __y._M_data)); }
5631
5632 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend mask_type
5633 operator!=(const simd& __x, const simd& __y)
5634 {
5635 return simd::_S_make_mask(
5636 _Impl::_S_not_equal_to(__x._M_data, __y._M_data));
5637 }
5638
5639 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend mask_type
5640 operator<(const simd& __x, const simd& __y)
5641 { return simd::_S_make_mask(_Impl::_S_less(__x._M_data, __y._M_data)); }
5642
5643 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend mask_type
5644 operator<=(const simd& __x, const simd& __y)
5645 {
5646 return simd::_S_make_mask(_Impl::_S_less_equal(__x._M_data, __y._M_data));
5647 }
5648
5649 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend mask_type
5650 operator>(const simd& __x, const simd& __y)
5651 { return simd::_S_make_mask(_Impl::_S_less(__y._M_data, __x._M_data)); }
5652
5653 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend mask_type
5654 operator>=(const simd& __x, const simd& __y)
5655 {
5656 return simd::_S_make_mask(_Impl::_S_less_equal(__y._M_data, __x._M_data));
5657 }
5658
5659 // operator?: overloads (suggested extension) {{{
5660#ifdef __GXX_CONDITIONAL_IS_OVERLOADABLE__
5661 _GLIBCXX_SIMD_ALWAYS_INLINE _GLIBCXX_SIMD_CONSTEXPR friend simd
5662 operator?:(const mask_type& __k, const simd& __where_true,
5663 const simd& __where_false)
5664 {
5665 auto __ret = __where_false;
5666 _Impl::_S_masked_assign(__data(__k), __data(__ret), __data(__where_true));
5667 return __ret;
5668 }
5669
5670#endif // __GXX_CONDITIONAL_IS_OVERLOADABLE__
5671 // }}}
5672
5673 // "private" because of the first arguments's namespace
5674 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
5675 simd(_PrivateInit, const _MemberType& __init)
5676 : _M_data(__init) {}
5677
5678 // "private" because of the first arguments's namespace
5679 _GLIBCXX_SIMD_INTRINSIC
5680 simd(_BitsetInit, bitset<size()> __init) : _M_data()
5681 { where(mask_type(__bitset_init, __init), *this) = ~*this; }
5682
5683 _GLIBCXX_SIMD_INTRINSIC constexpr bool
5684 _M_is_constprop() const
5685 {
5686 if constexpr (__is_scalar_abi<_Abi>())
5687 return __builtin_constant_p(_M_data);
5688 else
5689 return _M_data._M_is_constprop();
5690 }
5691
5692 private:
5693 _GLIBCXX_SIMD_INTRINSIC static constexpr mask_type
5694 _S_make_mask(typename mask_type::_MemberType __k)
5695 { return {__private_init, __k}; }
5696
5697 friend const auto& __data<value_type, abi_type>(const simd&);
5698 friend auto& __data<value_type, abi_type>(simd&);
5699 alignas(_Traits::_S_simd_align) _MemberType _M_data;
5700 };
5701
5702// }}}
5703/// @cond undocumented
5704// __data {{{
5705template <typename _Tp, typename _Ap>
5706 _GLIBCXX_SIMD_INTRINSIC constexpr const auto&
5707 __data(const simd<_Tp, _Ap>& __x)
5708 { return __x._M_data; }
5709
5710template <typename _Tp, typename _Ap>
5711 _GLIBCXX_SIMD_INTRINSIC constexpr auto&
5712 __data(simd<_Tp, _Ap>& __x)
5713 { return __x._M_data; }
5714
5715// }}}
5716namespace __float_bitwise_operators { //{{{
5717template <typename _Tp, typename _Ap>
5718 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR simd<_Tp, _Ap>
5719 operator^(const simd<_Tp, _Ap>& __a, const simd<_Tp, _Ap>& __b)
5720 { return {__private_init, _Ap::_SimdImpl::_S_bit_xor(__data(__a), __data(__b))}; }
5721
5722template <typename _Tp, typename _Ap>
5723 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR simd<_Tp, _Ap>
5724 operator|(const simd<_Tp, _Ap>& __a, const simd<_Tp, _Ap>& __b)
5725 { return {__private_init, _Ap::_SimdImpl::_S_bit_or(__data(__a), __data(__b))}; }
5726
5727template <typename _Tp, typename _Ap>
5728 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR simd<_Tp, _Ap>
5729 operator&(const simd<_Tp, _Ap>& __a, const simd<_Tp, _Ap>& __b)
5730 { return {__private_init, _Ap::_SimdImpl::_S_bit_and(__data(__a), __data(__b))}; }
5731
5732template <typename _Tp, typename _Ap>
5733 _GLIBCXX_SIMD_INTRINSIC _GLIBCXX_SIMD_CONSTEXPR
5734 enable_if_t<is_floating_point_v<_Tp>, simd<_Tp, _Ap>>
5735 operator~(const simd<_Tp, _Ap>& __a)
5736 { return {__private_init, _Ap::_SimdImpl::_S_complement(__data(__a))}; }
5737} // namespace __float_bitwise_operators }}}
5738/// @endcond
5739
5740/// @}
5741_GLIBCXX_SIMD_END_NAMESPACE
5742
5743#endif // __cplusplus >= 201703L
5744#endif // _GLIBCXX_EXPERIMENTAL_SIMD_H
5745
5746// vim: foldmethod=marker foldmarker={{{,}}}
constexpr bool operator<=(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
Definition chrono.h:863
constexpr bool operator>=(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
Definition chrono.h:877
constexpr duration< __common_rep_t< _Rep1, __disable_if_is_duration< _Rep2 > >, _Period > operator%(const duration< _Rep1, _Period > &__d, const _Rep2 &__s)
Definition chrono.h:787
constexpr bool operator<(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
Definition chrono.h:830
constexpr bool operator>(const duration< _Rep1, _Period1 > &__lhs, const duration< _Rep2, _Period2 > &__rhs)
Definition chrono.h:870
constexpr complex< _Tp > operator*(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x times y.
Definition complex:434
constexpr complex< _Tp > operator-(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x minus y.
Definition complex:404
constexpr complex< _Tp > operator+(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x plus y.
Definition complex:374
constexpr complex< _Tp > operator/(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x divided by y.
Definition complex:464
typename conditional< _Cond, _Iftrue, _Iffalse >::type conditional_t
Alias template for conditional.
Definition type_traits:2973
typename remove_reference< _Tp >::type remove_reference_t
Alias template for remove_reference.
Definition type_traits:1913
typename make_unsigned< _Tp >::type make_unsigned_t
Alias template for make_unsigned.
Definition type_traits:2273
typename enable_if< _Cond, _Tp >::type enable_if_t
Alias template for enable_if.
Definition type_traits:2969
__bool_constant< false > false_type
The type used as a compile-time boolean with false value.
Definition type_traits:122
constexpr auto tuple_cat(_Tpls &&... __tpls) -> typename __tuple_cat_result< _Tpls... >::__type
Create a tuple containing all elements from multiple tuple-like objects.
Definition tuple:2829
auto declval() noexcept -> decltype(__declval< _Tp >(0))
Definition type_traits:2741
constexpr tuple< typename __decay_and_strip< _Elements >::__type... > make_tuple(_Elements &&... __args)
Create a tuple containing copies of the arguments.
Definition tuple:2693
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
Convert a value to an rvalue.
Definition move.h:138
constexpr const _Tp & max(const _Tp &, const _Tp &)
This does what you think it does.
constexpr const _Tp & min(const _Tp &, const _Tp &)
This does what you think it does.
constexpr _Tp reduce(_InputIterator __first, _InputIterator __last, _Tp __init, _BinaryOperation __binary_op)
Calculate reduction of values in a range.
Definition numeric:294
void void_t
A metafunction that always yields void, used for detecting valid types.
ISO C++ entities toplevel namespace is std.
make_integer_sequence< size_t, _Num > make_index_sequence
Alias template make_index_sequence.
Definition utility.h:528
constexpr bitset< _Nb > operator^(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition bitset:1682
std::basic_istream< _CharT, _Traits > & operator>>(std::basic_istream< _CharT, _Traits > &__is, bitset< _Nb > &__x)
Global I/O operators for bitsets.
Definition bitset:1702
std::basic_ostream< _CharT, _Traits > & operator<<(std::basic_ostream< _CharT, _Traits > &__os, const bitset< _Nb > &__x)
Global I/O operators for bitsets.
Definition bitset:1798
constexpr bitset< _Nb > operator|(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition bitset:1672
constexpr bitset< _Nb > operator&(const bitset< _Nb > &__x, const bitset< _Nb > &__y) noexcept
Global bitwise operations on bitsets.
Definition bitset:1662