libstdc++
simd_details.h
1 // Implementation of <simd> -*- C++ -*-
2 
3 // Copyright The GNU Toolchain Authors.
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_SIMD_DETAILS_H
26 #define _GLIBCXX_SIMD_DETAILS_H 1
27 
28 #ifdef _GLIBCXX_SYSHDR
29 #pragma GCC system_header
30 #endif
31 
32 #if __cplusplus >= 202400L
33 
34 #include <bit>
35 #include <bits/c++config.h> // _GLIBCXX_FLOAT_IS_IEEE_BINARY32
36 #include <bits/stl_function.h> // plus, minus, multiplies, ...
37 #include <bits/utility.h> // integer_sequence, etc.
38 #include <cmath> // for math_errhandling :(
39 #include <concepts>
40 #include <cstdint>
41 #include <limits>
42 #include <span> // for dynamic_extent
43 
44 #if __CHAR_BIT__ != 8
45 // There are simply too many constants and bit operators that currently depend on CHAR_BIT == 8.
46 // Generalization to CHAR_BIT != 8 does not make sense without testability (i.e. a test target).
47 #error "<simd> is not supported for CHAR_BIT != 8"
48 #endif
49 
50 // psabi warnings are bogus because the ABI of the internal types never leaks into user code
51 #pragma GCC diagnostic push
52 #pragma GCC diagnostic ignored "-Wpsabi"
53 
54 #if defined __x86_64__ || defined __i386__
55 #define _GLIBCXX_X86 1
56 #else
57 #define _GLIBCXX_X86 0
58 #endif
59 
60 #ifndef _GLIBCXX_SIMD_NOEXCEPT
61 /** @internal
62  * For unit-testing preconditions, use this macro to remove noexcept.
63  */
64 #define _GLIBCXX_SIMD_NOEXCEPT noexcept
65 #endif
66 
67 #define _GLIBCXX_SIMD_TOSTRING_IMPL(x) #x
68 #define _GLIBCXX_SIMD_TOSTRING(x) _GLIBCXX_SIMD_TOSTRING_IMPL(x)
69 
70 // This is used for unit-testing precondition checking
71 #define __glibcxx_simd_precondition(expr, msg, ...) \
72  __glibcxx_assert(expr)
73 
74 namespace std _GLIBCXX_VISIBILITY(default)
75 {
76 _GLIBCXX_BEGIN_NAMESPACE_VERSION
77 
78 namespace simd
79 {
80  template <typename _Tp>
81  inline constexpr _Tp
82  __iota = [] { static_assert(false, "invalid __iota specialization"); }();
83 
84  // [simd.general] vectorizable types
85 
86  template <typename _Tp>
87  concept __vectorizable_scalar
88  = same_as<remove_cv_t<_Tp>, _Tp>
89 #ifdef __STDCPP_BFLOAT16_T__
90  && !same_as<_Tp, __gnu_cxx::__bfloat16_t>
91 #endif
92  && ((integral<_Tp> && sizeof(_Tp) <= sizeof(0ULL) && !same_as<_Tp, bool>)
93  || (floating_point<_Tp> && sizeof(_Tp) <= sizeof(double)));
94 
95  // [simd.general] p2
96  template <typename _Tp>
97  concept __vectorizable = __vectorizable_scalar<_Tp>;
98 
99  /** @internal
100  * Describes variants of _Abi.
101  */
102  enum class _AbiVariant : unsigned long long
103  {
104  _BitMask = 0x01, // AVX512 bit-masks
105  _MaskVariants = 0x0f, // vector masks if bits [0:3] are 0
106  };
107 
108  /** @internal
109  * Return @p __in with only bits set that are set in any of @p __to_keep.
110  */
111  consteval _AbiVariant
112  __filter_abi_variant(_AbiVariant __in, same_as<_AbiVariant> auto... __to_keep)
113  {
114  using _Up = underlying_type_t<_AbiVariant>;
115  return static_cast<_AbiVariant>(static_cast<_Up>(__in) & (static_cast<_Up>(__to_keep) | ...));
116  }
117 
118  /** @internal
119  * Type used whenever no valid integer/value type exists.
120  */
121  struct _InvalidInteger
122  {};
123 
124  /** @internal
125  * Alias for a signed integer type T such that sizeof(T) equals _Bytes.
126  *
127  * C++26 [simd.expos.defn]
128  */
129  template <size_t _Bytes>
130  using __integer_from
131  = decltype([] consteval {
132  if constexpr (sizeof(signed char) == _Bytes)
133  return static_cast<signed char>(0);
134  else if constexpr (sizeof(signed short) == _Bytes)
135  return static_cast<signed short>(0);
136  else if constexpr (sizeof(signed int) == _Bytes)
137  return static_cast<signed int>(0);
138  else if constexpr (sizeof(signed long long) == _Bytes)
139  return static_cast<signed long long>(0);
140  else
141  return _InvalidInteger();
142  }());
143 
144  /** @internal
145  * Alias for an unsigned integer type T such that sizeof(T) equals _Bytes.
146  */
147  template <size_t _Bytes>
148  using _UInt = make_unsigned_t<__integer_from<_Bytes>>;
149 
150  /** @internal
151  * Divide @p __x by @p __y while rounding up instead of down.
152  *
153  * Preconditions: __x >= 0 && __y > 0.
154  */
155  template <typename _Tp>
156  consteval _Tp
157  __div_ceil(_Tp __x, _Tp __y)
158  { return (__x + __y - 1) / __y; }
159 
160  /** @internal
161  * Alias for an unsigned integer type that can store at least @p _NBits bits.
162  */
163  template <int _NBits>
164  requires (_NBits > 0 && _NBits <= numeric_limits<unsigned long long>::digits)
165  using _Bitmask = _UInt<__div_ceil(__bit_ceil(unsigned(_NBits)), unsigned(__CHAR_BIT__))>;
166 
167  /** @internal
168  * Map a given type @p _Tp to an equivalent type.
169  *
170  * This helps with reducing the necessary branches && casts in the implementation as well as
171  * reducing the number of template instantiations.
172  */
173  template <typename _Tp>
174  struct __canonical_vec_type
175  { using type = _Tp; };
176 
177  template <typename _Tp>
178  using __canonical_vec_type_t = typename __canonical_vec_type<_Tp>::type;
179 
180 #if __SIZEOF_INT__ == __SIZEOF_LONG__
181  template <>
182  struct __canonical_vec_type<long>
183  { using type = int; };
184 
185  template <>
186  struct __canonical_vec_type<unsigned long>
187  { using type = unsigned int; };
188 #elif __SIZEOF_LONG_LONG__ == __SIZEOF_LONG__
189  template <>
190  struct __canonical_vec_type<long>
191  { using type = long long; };
192 
193  template <>
194  struct __canonical_vec_type<unsigned long>
195  { using type = unsigned long long; };
196 #endif
197 
198  template <typename _Tp>
199  requires std::is_enum_v<_Tp>
200  struct __canonical_vec_type<_Tp>
201  { using type = __canonical_vec_type<std::underlying_type_t<_Tp>>::type; };
202 
203  template <>
204  struct __canonical_vec_type<char>
205 #if __CHAR_UNSIGNED__
206  { using type = unsigned char; };
207 #else
208  { using type = signed char; };
209 #endif
210 
211  template <>
212  struct __canonical_vec_type<char8_t>
213  { using type = unsigned char; };
214 
215  template <>
216  struct __canonical_vec_type<char16_t>
217  { using type = uint_least16_t; };
218 
219  template <>
220  struct __canonical_vec_type<char32_t>
221  { using type = uint_least32_t; };
222 
223  template <>
224  struct __canonical_vec_type<wchar_t>
225  {
226  using type = std::__conditional_t<std::is_signed_v<wchar_t>,
227  simd::__integer_from<sizeof(wchar_t)>,
228  simd::_UInt<sizeof(wchar_t)>>;
229  };
230 
231 #if defined(__FLT64_DIG__) && defined(_GLIBCXX_DOUBLE_IS_IEEE_BINARY64)
232  template <>
233  struct __canonical_vec_type<_Float64>
234  { using type = double; };
235 #endif
236 
237 #if defined(__FLT32_DIG__) && defined(_GLIBCXX_FLOAT_IS_IEEE_BINARY32)
238  template <>
239  struct __canonical_vec_type<_Float32>
240  { using type = float; };
241 #endif
242 
243  /** @internal
244  * @brief This ABI tag determines the data member(s) of basic_vec and basic_mask.
245  *
246  * `_Nreg` determines the number of recursive basic_vec/basic_mask data members where `_Nreg` is
247  * equal to 1. With `_Nreg` equal to 1, the basic_vec/basic_mask holds one vector builtin ( `_Np`
248  * greater than 1) or a scalar (`_Np` equal to 1).
249  * @f$\lceil\frac{\mathtt{Np}}{\mathtt{Nreg}}\rceil@f$ therefore determines the number of elements
250  * in a register (except for a remainder where it can be smaller). If `_Np` equals `_Nreg`, (the
251  * aforementioned quotient is 1), then basic_vec (recursively) holds non-vector data members and
252  * basic_mask holds bools.
253  *
254  * The `_Var` parameter determines details about the data member in the one register case. Masks
255  * can be represented as vector masks (the default comparison result of GNU vector builtins),
256  * bit-masks as used by AVX-512, bit-masks as used by ARM SVE (not yet implemented), or a single
257  * bool (for the `_Np` equals 1 case). For basic_mask it determines the actual data layout and
258  * for basic_mask it determines the result of compares.
259  *
260  * @tparam _Np The number of elements.
261  * @tparam _Nreg The number of registers needed to store `_Np` elements.
262  * @tparam _Var Determines how complex value-types are laid out and whether mask types use
263  * bit-masks or vector-masks.
264  */
265  template <int _Np, int _Nreg, underlying_type_t<_AbiVariant> _Var>
266  struct _Abi
267  {
268  static constexpr int _S_size = _Np;
269 
270  /** @internal
271  * The number of registers needed to represent one basic_vec for the element type that was
272  * used on ABI deduction.
273  *
274  * Examples:
275  * - '_Abi< 8, 2>' for 'int' is 2x 128-bit
276  * - '_Abi< 9, 3>' for 'int' is 2x 128-bit and 1x 32-bit
277  * - '_Abi<10, 3>' for 'int' is 2x 128-bit and 1x 64-bit
278  * - '_Abi<10, 1>' for 'int' is 1x 512-bit
279  * - '_Abi<10, 2>' for 'int' is 1x 256-bit and 1x 64-bit
280  */
281  static constexpr int _S_nreg = _Nreg;
282 
283  static_assert(_S_size > 0);
284  static_assert(_S_nreg > 0);
285 
286  static constexpr _AbiVariant _S_variant = static_cast<_AbiVariant>(_Var);
287 
288  static constexpr bool _S_is_bitmask
289  = __filter_abi_variant(_S_variant, _AbiVariant::_BitMask) == _AbiVariant::_BitMask;
290 
291  static constexpr bool _S_is_vecmask = !_S_is_bitmask;
292 
293  template <typename _Tp>
294  using _DataType = decltype([] {
295  static_assert(_S_nreg == 1);
296  if constexpr (_S_size == 1)
297  return __canonical_vec_type_t<_Tp>();
298  else
299  {
300  constexpr int __n = __bit_ceil(unsigned(_S_size));
301  using _Vp [[__gnu__::__vector_size__(sizeof(_Tp) * __n)]]
302  = __canonical_vec_type_t<_Tp>;
303  return _Vp();
304  }
305  }());
306 
307  template <size_t _Bytes>
308  using _MaskDataType
309  = decltype([] {
310  static_assert(_S_nreg == 1);
311  if constexpr (_S_size == 1)
312  return bool();
313  else if constexpr (_S_is_vecmask)
314  {
315  constexpr unsigned __vbytes = _Bytes * __bit_ceil(unsigned(_S_size));
316  using _Vp [[__gnu__::__vector_size__(__vbytes)]] = __integer_from<_Bytes>;
317  return _Vp();
318  }
319  else if constexpr (_Nreg > 1)
320  return _InvalidInteger();
321  else
322  return _Bitmask<_S_size>();
323  }());
324 
325  template <int _N2, int _Nreg2 = __div_ceil(_N2, _S_size)>
326  static consteval auto
327  _S_resize()
328  {
329  if constexpr (_N2 == 1)
330  return _Abi<1, 1, _Var>();
331  else
332  return _Abi<_N2, _Nreg2, _Var>();
333  }
334  };
335 
336  /** @internal
337  * Alias for an _Abi specialization where the _AbiVariant bits are combined into a single integer
338  * value.
339  *
340  * Rationale: Consider diagnostic output and mangling of e.g. vec<int, 4> with AVX512. That's an
341  * alias for std::simd::basic_vec<int, std::simd::_Abi<4, 1, 1ull>>. If _AbiVariant were the
342  * template argument type of _Abi, the diagnostic output would be 'std::simd::basic_vec<int,
343  * std::simd::_Abi<4, 1, (std::simd::_AbiVariant)std::simd::_AbiVariant::_BitMask>>'. That's a lot
344  * longer, requires longer mangled names, and bakes the names of the enumerators into the ABI. As
345  * soon as bits of multiple _AbiVariants are combined, this becomes hard to parse for humans
346  * anyway.
347  */
348  template <int _Np, int _Nreg, _AbiVariant... _Vs>
349  using _Abi_t = _Abi<_Np, _Nreg, (static_cast<underlying_type_t<_AbiVariant>>(_Vs) | ... | 0)>;
350 
351  /** @internal
352  * This type is used whenever ABI tag deduction can't give a useful answer.
353  */
354  struct _InvalidAbi
355  { static constexpr int _S_size = 0; };
356 
357  /** @internal
358  * Satisfied if @p _Tp is a valid simd ABI tag. This is a necessary but not sufficient condition
359  * for an enabled basic_vec/basic_mask specialization.
360  */
361  template <typename _Tp>
362  concept __abi_tag
363  = same_as<decltype(_Tp::_S_variant), const _AbiVariant>
364  && (_Tp::_S_size >= _Tp::_S_nreg) && (_Tp::_S_nreg >= 1)
365  && requires(_Tp __x) {
366  { __x.template _S_resize<_Tp::_S_size, _Tp::_S_nreg>() } -> same_as<_Tp>;
367  };
368 
369  /** @internal
370  * Satisfied if `_Tp` is a valid simd ABI tag and one element is stored per register (number of
371  * registers equals size).
372  */
373  template <typename _Tp>
374  concept __scalar_abi_tag
375  = same_as<_Tp, _Abi_t<_Tp::_S_size, _Tp::_S_size, _Tp::_S_variant>> && __abi_tag<_Tp>;
376 
377  // Determine if math functions must *raise* floating-point exceptions.
378  // math_errhandling may expand to an extern symbol, in which case we must assume fp exceptions
379  // need to be considered. A conforming C library must define math_errhandling, but in case it
380  // isn't defined we simply use the fallback.
381 #ifdef math_errhandling
382  template <int = 0>
383  requires requires { typename bool_constant<0 != (math_errhandling & MATH_ERREXCEPT)>; }
384  consteval bool
385  __handle_fpexcept_impl(int)
386  { return 0 != (math_errhandling & MATH_ERREXCEPT); }
387 #endif
388 
389  // Fallback if math_errhandling doesn't work: implement correct exception behavior.
390  consteval bool
391  __handle_fpexcept_impl(float)
392  { return true; }
393 
394  /** @internal
395  * This type can be used as a template parameter for avoiding ODR violations, where code needs to
396  * differ depending on optimization flags (mostly fp-math related).
397  */
398  struct _OptTraits
399  {
400  consteval bool
401  _M_test(int __bit) const
402  { return ((_M_build_flags >> __bit) & 1) == 1; }
403 
404  // true iff floating-point operations can signal an exception (allow non-default handler)
405  consteval bool
406  _M_fp_may_signal() const
407  { return _M_test(0); }
408 
409  // true iff floating-point operations can raise an exception flag
410  consteval bool
411  _M_fp_may_raise() const
412  { return _M_test(12); }
413 
414  consteval bool
415  _M_fast_math() const
416  { return _M_test(1); }
417 
418  consteval bool
419  _M_finite_math_only() const
420  { return _M_test(2); }
421 
422  consteval bool
423  _M_no_signed_zeros() const
424  { return _M_test(3); }
425 
426  consteval bool
427  _M_signed_zeros() const
428  { return !_M_test(3); }
429 
430  consteval bool
431  _M_reciprocal_math() const
432  { return _M_test(4); }
433 
434  consteval bool
435  _M_no_math_errno() const
436  { return _M_test(5); }
437 
438  consteval bool
439  _M_math_errno() const
440  { return !_M_test(5); }
441 
442  consteval bool
443  _M_associative_math() const
444  { return _M_test(6); }
445 
446  consteval bool
447  _M_conforming_to_STDC_annex_G() const
448  { return _M_test(10) && !_M_finite_math_only(); }
449 
450  consteval bool
451  _M_support_snan() const
452  { return _M_test(11); }
453 
454  __UINT64_TYPE__ _M_build_flags
455  = 0
456 #if !__NO_TRAPPING_MATH__
457  + (1 << 0)
458 #endif
459  + (__handle_fpexcept_impl(0) << 12)
460 #if __FAST_MATH__
461  + (1 << 1)
462 #endif
463 #if __FINITE_MATH_ONLY__
464  + (1 << 2)
465 #endif
466 #if __NO_SIGNED_ZEROS__
467  + (1 << 3)
468 #endif
469 #if __RECIPROCAL_MATH__
470  + (1 << 4)
471 #endif
472 #if __NO_MATH_ERRNO__
473  + (1 << 5)
474 #endif
475 #if __ASSOCIATIVE_MATH__
476  + (1 << 6)
477 #endif
478  // bits 7, 8, and 9 reserved for __FLT_EVAL_METHOD__
479 #if __FLT_EVAL_METHOD__ == 1
480  + (1 << 7)
481 #elif __FLT_EVAL_METHOD__ == 2
482  + (2 << 7)
483 #elif __FLT_EVAL_METHOD__ != 0
484  + (3 << 7)
485 #endif
486 
487  // C Annex G defines the behavior of complex<T> where T is IEC60559 floating-point. If
488  // __STDC_IEC_60559_COMPLEX__ is defined then Annex G is implemented - and simd<complex>
489  // will do so as well. However, Clang never defines the macro.
490 #if defined __STDC_IEC_60559_COMPLEX__ || defined __STDC_IEC_559_COMPLEX__ || defined _GLIBCXX_CLANG
491  + (1 << 10)
492 #endif
493 #if __SUPPORT_SNAN__
494  + (1 << 11)
495 #endif
496  ;
497  };
498 
499  /** @internal
500  * Return true iff @p __s equals "1".
501  */
502  consteval bool
503  __streq_to_1(const char* __s)
504  { return __s != nullptr && __s[0] == '1' && __s[1] == '\0'; }
505 
506  /** @internal
507  * If the macro given as @p feat is defined to 1, expands to a bit set at position @p off.
508  * Otherwise, expand to zero.
509  */
510 #define _GLIBCXX_SIMD_ARCH_FLAG(off, feat) \
511  (static_cast<__UINT64_TYPE__>(std::simd::__streq_to_1(_GLIBCXX_SIMD_TOSTRING_IMPL(feat))) << off)
512 
513 #if _GLIBCXX_X86
514 
515 #define _GLIBCXX_SIMD_ARCH_TRAITS_INIT { \
516  _GLIBCXX_SIMD_ARCH_FLAG(0, __MMX__) \
517  | _GLIBCXX_SIMD_ARCH_FLAG( 1, __SSE__) \
518  | _GLIBCXX_SIMD_ARCH_FLAG( 2, __SSE2__) \
519  | _GLIBCXX_SIMD_ARCH_FLAG( 3, __SSE3__) \
520  | _GLIBCXX_SIMD_ARCH_FLAG( 4, __SSSE3__) \
521  | _GLIBCXX_SIMD_ARCH_FLAG( 5, __SSE4_1__) \
522  | _GLIBCXX_SIMD_ARCH_FLAG( 6, __SSE4_2__) \
523  | _GLIBCXX_SIMD_ARCH_FLAG( 7, __POPCNT__) \
524  | _GLIBCXX_SIMD_ARCH_FLAG( 8, __AVX__) \
525  | _GLIBCXX_SIMD_ARCH_FLAG( 9, __F16C__) \
526  | _GLIBCXX_SIMD_ARCH_FLAG(10, __BMI__) \
527  | _GLIBCXX_SIMD_ARCH_FLAG(11, __BMI2__) \
528  | _GLIBCXX_SIMD_ARCH_FLAG(12, __LZCNT__) \
529  | _GLIBCXX_SIMD_ARCH_FLAG(13, __AVX2__) \
530  | _GLIBCXX_SIMD_ARCH_FLAG(14, __FMA__) \
531  | _GLIBCXX_SIMD_ARCH_FLAG(15, __AVX512F__) \
532  | _GLIBCXX_SIMD_ARCH_FLAG(16, __AVX512CD__) \
533  | _GLIBCXX_SIMD_ARCH_FLAG(17, __AVX512DQ__) \
534  | _GLIBCXX_SIMD_ARCH_FLAG(18, __AVX512BW__) \
535  | _GLIBCXX_SIMD_ARCH_FLAG(19, __AVX512VL__) \
536  | _GLIBCXX_SIMD_ARCH_FLAG(20, __AVX512BITALG__) \
537  | _GLIBCXX_SIMD_ARCH_FLAG(21, __AVX512VBMI__) \
538  | _GLIBCXX_SIMD_ARCH_FLAG(22, __AVX512VBMI2__) \
539  | _GLIBCXX_SIMD_ARCH_FLAG(23, __AVX512IFMA__) \
540  | _GLIBCXX_SIMD_ARCH_FLAG(24, __AVX512VNNI__) \
541  | _GLIBCXX_SIMD_ARCH_FLAG(25, __AVX512VPOPCNTDQ__) \
542  | _GLIBCXX_SIMD_ARCH_FLAG(26, __AVX512FP16__) \
543  | _GLIBCXX_SIMD_ARCH_FLAG(27, __AVX512BF16__) \
544  | _GLIBCXX_SIMD_ARCH_FLAG(28, __AVXIFMA__) \
545  | _GLIBCXX_SIMD_ARCH_FLAG(29, __AVXNECONVERT__) \
546  | _GLIBCXX_SIMD_ARCH_FLAG(30, __AVXVNNI__) \
547  | _GLIBCXX_SIMD_ARCH_FLAG(31, __AVXVNNIINT8__) \
548  | _GLIBCXX_SIMD_ARCH_FLAG(32, __AVXVNNIINT16__) \
549  | _GLIBCXX_SIMD_ARCH_FLAG(33, __AVX10_1__) \
550  | _GLIBCXX_SIMD_ARCH_FLAG(34, __AVX10_2__) \
551  | _GLIBCXX_SIMD_ARCH_FLAG(35, __AVX512VP2INTERSECT__) \
552  | _GLIBCXX_SIMD_ARCH_FLAG(36, __SSE4A__) \
553  | _GLIBCXX_SIMD_ARCH_FLAG(37, __FMA4__) \
554  | _GLIBCXX_SIMD_ARCH_FLAG(38, __XOP__) \
555  }
556  // Should this include __APX_F__? I don't think it's relevant for use in constexpr-if branches =>
557  // no ODR issue? The same could be said about several other flags above that are not checked
558  // anywhere.
559 
560  struct _ArchTraits
561  {
562  __UINT64_TYPE__ _M_flags = _GLIBCXX_SIMD_ARCH_TRAITS_INIT;
563 
564  consteval bool
565  _M_test(int __bit) const
566  { return ((_M_flags >> __bit) & 1) == 1; }
567 
568  consteval bool
569  _M_have_mmx() const
570  { return _M_test(0); }
571 
572  consteval bool
573  _M_have_sse() const
574  { return _M_test(1); }
575 
576  consteval bool
577  _M_have_sse2() const
578  { return _M_test(2); }
579 
580  consteval bool
581  _M_have_sse3() const
582  { return _M_test(3); }
583 
584  consteval bool
585  _M_have_ssse3() const
586  { return _M_test(4); }
587 
588  consteval bool
589  _M_have_sse4_1() const
590  { return _M_test(5); }
591 
592  consteval bool
593  _M_have_sse4_2() const
594  { return _M_test(6); }
595 
596  consteval bool
597  _M_have_popcnt() const
598  { return _M_test(7); }
599 
600  consteval bool
601  _M_have_avx() const
602  { return _M_test(8); }
603 
604  consteval bool
605  _M_have_f16c() const
606  { return _M_test(9); }
607 
608  consteval bool
609  _M_have_bmi() const
610  { return _M_test(10); }
611 
612  consteval bool
613  _M_have_bmi2() const
614  { return _M_test(11); }
615 
616  consteval bool
617  _M_have_lzcnt() const
618  { return _M_test(12); }
619 
620  consteval bool
621  _M_have_avx2() const
622  { return _M_test(13); }
623 
624  consteval bool
625  _M_have_fma() const
626  { return _M_test(14); }
627 
628  consteval bool
629  _M_have_avx512f() const
630  { return _M_test(15); }
631 
632  consteval bool
633  _M_have_avx512cd() const
634  { return _M_test(16); }
635 
636  consteval bool
637  _M_have_avx512dq() const
638  { return _M_test(17); }
639 
640  consteval bool
641  _M_have_avx512bw() const
642  { return _M_test(18); }
643 
644  consteval bool
645  _M_have_avx512vl() const
646  { return _M_test(19); }
647 
648  consteval bool
649  _M_have_avx512bitalg() const
650  { return _M_test(20); }
651 
652  consteval bool
653  _M_have_avx512vbmi() const
654  { return _M_test(21); }
655 
656  consteval bool
657  _M_have_avx512vbmi2() const
658  { return _M_test(22); }
659 
660  consteval bool
661  _M_have_avx512ifma() const
662  { return _M_test(23); }
663 
664  consteval bool
665  _M_have_avx512vnni() const
666  { return _M_test(24); }
667 
668  consteval bool
669  _M_have_avx512vpopcntdq() const
670  { return _M_test(25); }
671 
672  consteval bool
673  _M_have_avx512fp16() const
674  { return _M_test(26); }
675 
676  consteval bool
677  _M_have_avx512bf16() const
678  { return _M_test(27); }
679 
680  consteval bool
681  _M_have_avxifma() const
682  { return _M_test(28); }
683 
684  consteval bool
685  _M_have_avxneconvert() const
686  { return _M_test(29); }
687 
688  consteval bool
689  _M_have_avxvnni() const
690  { return _M_test(30); }
691 
692  consteval bool
693  _M_have_avxvnniint8() const
694  { return _M_test(31); }
695 
696  consteval bool
697  _M_have_avxvnniint16() const
698  { return _M_test(32); }
699 
700  consteval bool
701  _M_have_avx10_1() const
702  { return _M_test(33); }
703 
704  consteval bool
705  _M_have_avx10_2() const
706  { return _M_test(34); }
707 
708  consteval bool
709  _M_have_avx512vp2intersect() const
710  { return _M_test(35); }
711 
712  consteval bool
713  _M_have_sse4a() const
714  { return _M_test(36); }
715 
716  consteval bool
717  _M_have_fma4() const
718  { return _M_test(37); }
719 
720  consteval bool
721  _M_have_xop() const
722  { return _M_test(38); }
723 
724  template <typename _Tp>
725  consteval bool
726  _M_eval_as_f32() const
727  { return is_same_v<_Tp, _Float16> && !_M_have_avx512fp16(); }
728  };
729 
730  template <typename _Tp, _ArchTraits _Traits = {}>
731  consteval auto
732  __native_abi()
733  {
734  constexpr int __adj_sizeof = sizeof(_Tp) * (1 + is_same_v<_Tp, _Float16>);
735  if constexpr (!__vectorizable<_Tp>)
736  return _InvalidAbi();
737  else if constexpr (_Traits._M_have_avx512fp16())
738  return _Abi_t<64 / sizeof(_Tp), 1, _AbiVariant::_BitMask>();
739  else if constexpr (_Traits._M_have_avx512f())
740  return _Abi_t<64 / __adj_sizeof, 1, _AbiVariant::_BitMask>();
741  else if constexpr (is_same_v<_Tp, _Float16> && !_Traits._M_have_f16c())
742  return _Abi_t<1, 1>();
743  else if constexpr (_Traits._M_have_avx2())
744  return _Abi_t<32 / __adj_sizeof, 1>();
745  else if constexpr (_Traits._M_have_avx() && is_floating_point_v<_Tp>)
746  return _Abi_t<32 / __adj_sizeof, 1>();
747  else if constexpr (_Traits._M_have_sse2())
748  return _Abi_t<16 / __adj_sizeof, 1>();
749  else if constexpr (_Traits._M_have_sse() && is_floating_point_v<_Tp>
750  && sizeof(_Tp) == sizeof(float))
751  return _Abi_t<16 / __adj_sizeof, 1>();
752  // no MMX: we can't emit EMMS where it would be necessary
753  else
754  return _Abi_t<1, 1>();
755  }
756 
757 #else
758 
759  // scalar fallback
760  struct _ArchTraits
761  {
762  __UINT64_TYPE__ _M_flags = 0;
763 
764  constexpr bool
765  _M_test(int __bit) const
766  { return ((_M_flags >> __bit) & 1) == 1; }
767  };
768 
769  template <typename _Tp>
770  consteval auto
771  __native_abi()
772  {
773  if constexpr (!__vectorizable<_Tp>)
774  return _InvalidAbi();
775  else
776  return _Abi_t<1, 1>();
777  }
778 
779 #endif
780 
781  /** @internal
782  * You must use this type as template argument to function templates that are not declared
783  * always_inline (to avoid issues when linking code compiled with different compiler flags).
784  */
785  struct _TargetTraits
786  : _ArchTraits, _OptTraits
787  {};
788 
789  /** @internal
790  * Alias for an ABI tag such that basic_vec<_Tp, __native_abi_t_<_Tp>> stores one SIMD register of
791  * optimal width.
792  *
793  * @tparam _Tp A vectorizable type.
794  *
795  * C++26 [simd.expos.abi]
796  */
797  template <typename _Tp>
798  using __native_abi_t = decltype(std::simd::__native_abi<_Tp>());
799 
800  template <typename _Tp, int _Np, _TargetTraits _Target = {}>
801  consteval auto
802  __deduce_abi()
803  {
804  constexpr auto __native = std::simd::__native_abi<_Tp>();
805  if constexpr (0 == __native._S_size || _Np <= 0)
806  return _InvalidAbi();
807  else if constexpr (_Np == __native._S_size)
808  return __native;
809  else
810  return __native.template _S_resize<_Np>();
811  }
812 
813  /** @internal
814  * Alias for an ABI tag @c A such that `basic_vec<_Tp, A>` stores @p _Np elements.
815  *
816  * C++26 [simd.expos.abi]
817  */
818  template <typename _Tp, int _Np>
819  using __deduce_abi_t = decltype(std::simd::__deduce_abi<_Tp, _Np>());
820 
821  /** @internal
822  * \c rebind implementation detail for basic_vec, and basic_mask where we know the destination
823  * value-type
824  */
825  template <typename _Tp, int _Np, __abi_tag _A0, _ArchTraits = {}>
826  consteval auto
827  __abi_rebind()
828  {
829  if constexpr (_Np <= 0 || !__vectorizable<_Tp>)
830  return _InvalidAbi();
831 
832  else
833  {
834  using _Native = remove_const_t<decltype(std::simd::__native_abi<_Tp>())>;
835  static_assert(0 != _Native::_S_size);
836  constexpr int __nreg = __div_ceil(_Np, _Native::_S_size);
837 
838  // __scalar_abi_tag is sticky (unless we reach size 1, where we can't know whether it was
839  // an explicit __scalar_abi_tag before some resize_t)
840  if constexpr (__scalar_abi_tag<_Native> || (__scalar_abi_tag<_A0> && _A0::_S_size >= 2))
841  {
842  return _A0::template _S_resize<_Np, _Np>();
843  }
844 
845  else
846  return _Abi_t<_Native::_S_size, 1, __filter_abi_variant(_A0::_S_variant,
847  _AbiVariant::_MaskVariants)
848  >::template _S_resize<_Np, __nreg>();
849  }
850  }
851 
852  /** @internal
853  * @c rebind implementation detail for basic_mask.
854  *
855  * The important difference here is that we have no information about the actual value-type other
856  * than its @c sizeof. So `_Bytes == 8` could mean `complex<float>`, @c double, or @c int64_t.
857  * E.g. `_Np == 4` with AVX w/o AVX2 that's `vector(4) int`, `vector(4) long long`, or `2x
858  * vector(2) long long`.
859  * That's why this overload has the additional @p _IsOnlyResize parameter, which tells us that the
860  * value-type doesn't change.
861  */
862  template <size_t _Bytes, int _Np, __abi_tag _A0, bool _IsOnlyResize, _ArchTraits _Traits = {}>
863  consteval auto
864  __abi_rebind()
865  {
866  if constexpr (_Bytes == 0 || _Np <= 0)
867  return _InvalidAbi();
868 
869 #if _GLIBCXX_X86
870  // AVX w/o AVX2:
871  // e.g. resize_t<8, mask<float, Whatever>> needs to be _Abi<8, 1> not _Abi<8, 2>
872  // We determine whether _A0 identifies an AVX vector by looking at the size of a native
873  // register. If it's 32, it's a YMM register, otherwise it's 16 or less.
874  else if constexpr (_IsOnlyResize
875  && _Traits._M_have_avx() && !_Traits._M_have_avx2()
876  && __bit_ceil(__div_ceil<unsigned>(
877  _A0::_S_size, _A0::_S_nreg)) * _Bytes == 32)
878  {
879  if constexpr (_Bytes == sizeof(double))
880  return __abi_rebind<double, _Np, _A0>();
881  else if constexpr (_Bytes == sizeof(float))
882  return __abi_rebind<float, _Np, _A0>();
883  else if constexpr (_Traits._M_have_f16c() && _Bytes == sizeof(_Float16))
884  return __abi_rebind<_Float16, _Np, _A0>();
885  else // impossible
886  static_assert(false);
887  }
888 #endif
889 
890  else
891  return __abi_rebind<__integer_from<_Bytes>, _Np, _A0>();
892  }
893 
894  /** @internal
895  * Returns true unless _GLIBCXX_SIMD_COND_EXPLICIT_MASK_CONVERSION is defined.
896  *
897  * On IvyBridge, (vec<float> == 0.f) == (rebind_t<int, vec<float>> == 0) does not compile. It does
898  * compile on basically every other target, though. This is due to the difference in ABI tag:
899  * _Abi<8, 1, [...]> vs. _Abi<8, 2, [...]> (8 elements, 1 vs. 2 registers).
900  * I know how to define this function for libstdc++ to avoid interconvertible masks. The question
901  * is whether we can specify this in general for C++29.
902  *
903  * Idea: Is rebind_t<integer-from<...>, mask>::abi_type the same type as
904  * deduce-t<integer-from<...>, mask::size()>? If yes, it's the "better" ABI tag. However, this
905  * makes the conversion behavior dependent on compiler flags. Probably not what we want.
906  */
907  template <typename _To, typename _From>
908  consteval bool
909  __is_mask_conversion_explicit([[maybe_unused]] size_t __b0, [[maybe_unused]] size_t __b1)
910  {
911  constexpr int __n = _To::_S_size;
912  static_assert(__n == _From::_S_size);
913 #ifndef _GLIBCXX_SIMD_COND_EXPLICIT_MASK_CONVERSION
914  /// C++26 [simd.mask.ctor] uses unconditional explicit
915  return true;
916 #else
917  if (__b0 != __b1)
918  return true;
919 
920  // converting to a bit-mask is better
921  else if constexpr (_To::_S_is_vecmask != _From::_S_is_vecmask)
922  return _To::_S_is_vecmask; // to vector-mask is explicit
923 
924  // with vec-masks, fewer registers is better
925  else if constexpr (_From::_S_nreg != _To::_S_nreg)
926  return _From::_S_nreg < _To::_S_nreg;
927 
928  else
929  __builtin_unreachable();
930 #endif
931  }
932 
933  /** @internal
934  * An alias for a signed integer type.
935  *
936  * libstdc++ unconditionally uses @c int here, since it matches the return type of
937  * 'Bit Operation Builtins' in GCC.
938  *
939  * C++26 [simd.expos.defn]
940  */
941  using __simd_size_type = int;
942 
943  // integral_constant shortcut
944  template <__simd_size_type _Xp>
945  inline constexpr integral_constant<__simd_size_type, _Xp> __simd_size_c = {};
946 
947  // [simd.syn]
948  template <typename _Tp, typename _Ap = __native_abi_t<_Tp>>
949  class basic_vec;
950 
951  template <typename _Tp, __simd_size_type _Np = __native_abi_t<_Tp>::_S_size>
952  using vec = basic_vec<_Tp, __deduce_abi_t<_Tp, _Np>>;
953 
954  template <size_t _Bytes, typename _Ap = __native_abi_t<__integer_from<_Bytes>>>
955  class basic_mask;
956 
957  template <typename _Tp, __simd_size_type _Np = __native_abi_t<_Tp>::_S_size>
958  using mask = basic_mask<sizeof(_Tp), __deduce_abi_t<_Tp, _Np>>;
959 
960  // [simd.ctor] load constructor constraints
961  template <typename _Tp, size_t _Np = -1uz>
962  concept __static_sized_range
963  = ranges::sized_range<_Tp> && requires(_Tp&& __r) {
964  typename integral_constant<size_t, ranges::size(__r)>;
965  requires (_Np == -1uz || ranges::size(__r) == _Np);
966  };
967 
968  template <typename _Rg>
969  consteval size_t
970  __static_range_size(_Rg& __r)
971  {
972  if constexpr (requires { typename integral_constant<size_t, ranges::size(__r)>; })
973  return ranges::size(__r);
974  else
975  return dynamic_extent;
976  }
977 
978  // [simd.general] value-preserving
979  template <typename _From, typename _To>
980  concept __arithmetic_only_value_preserving_convertible_to
981  = convertible_to<_From, _To> && is_arithmetic_v<_From> && is_arithmetic_v<_To>
982  && !(is_signed_v<_From> && is_unsigned_v<_To>)
986 
987  /** @internal
988  * Satisfied if the conversion from @p _From to @p _To is a value-preserving conversion.
989  *
990  * C++26 [simd.general]
991  */
992  template <typename _From, typename _To>
993  concept __value_preserving_convertible_to
994  = __arithmetic_only_value_preserving_convertible_to<_From, _To>;
995 
996  // LWG4420
997  template <typename _From, typename _To>
998  concept __explicitly_convertible_to = requires {
999  static_cast<_To>(declval<_From>());
1000  };
1001 
1002  /** @internal
1003  * C++26 [simd.expos]
1004  */
1005  template<typename _Tp>
1006  concept __constexpr_wrapper_like
1007  = convertible_to<_Tp, decltype(_Tp::value)>
1008  && equality_comparable_with<_Tp, decltype(_Tp::value)>
1009  && bool_constant<_Tp() == _Tp::value>::value
1010  && bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) == _Tp::value>::value;
1011 
1012  // [simd.ctor] explicit(...) of broadcast ctor
1013  template <auto _From, typename _To>
1014  concept __non_narrowing_constexpr_conversion
1015  = is_arithmetic_v<decltype(_From)>
1016  && static_cast<decltype(_From)>(static_cast<_To>(_From)) == _From
1017  && !(unsigned_integral<_To> && _From < decltype(_From)())
1018  && _From <= std::numeric_limits<_To>::max()
1019  && _From >= std::numeric_limits<_To>::lowest();
1020 
1021  // [simd.ctor] p4
1022  // This implements LWG4436 (submitted on 2025-10-28)
1023  template <typename _From, typename _To>
1024  concept __broadcast_constructible
1025  = ((convertible_to<_From, _To> && !is_arithmetic_v<remove_cvref_t<_From>>
1026  && !__constexpr_wrapper_like<remove_cvref_t<_From>>) // 4.1
1027  || __value_preserving_convertible_to<remove_cvref_t<_From>, _To> // 4.2
1028  || (__constexpr_wrapper_like<remove_cvref_t<_From>> // 4.3
1029  && __non_narrowing_constexpr_conversion<auto(remove_cvref_t<_From>::value),
1030  _To>));
1031 
1032  // __higher_floating_point_rank_than<_Tp, U> (_Tp has higher or equal floating point rank than U)
1033  template <typename _From, typename _To>
1034  consteval bool
1035  __higher_floating_point_rank_than()
1036  {
1037  return floating_point<_From> && floating_point<_To>
1038  && is_same_v<common_type_t<_From, _To>, _From> && !is_same_v<_From, _To>;
1039  }
1040 
1041  // __higher_integer_rank_than<_Tp, U> (_Tp has higher or equal integer rank than U)
1042  template <typename _From, typename _To>
1043  consteval bool
1044  __higher_integer_rank_than()
1045  {
1046  return integral<_From> && integral<_To>
1047  && (sizeof(_From) > sizeof(_To) || is_same_v<common_type_t<_From, _To>, _From>)
1048  && !is_same_v<_From, _To>;
1049  }
1050 
1051  template <typename _From, typename _To>
1052  concept __higher_rank_than
1053  = __higher_floating_point_rank_than<_From, _To>() || __higher_integer_rank_than<_From, _To>();
1054 
1055  struct __convert_flag;
1056 
1057  template <typename _From, typename _To, typename... _Flags>
1058  concept __loadstore_convertible_to
1059  = same_as<_From, _To>
1060  || (__vectorizable<_From> && __vectorizable<_To>
1061  && (__value_preserving_convertible_to<_From, _To>
1062  || (__explicitly_convertible_to<_From, _To>
1063  && (std::is_same_v<_Flags, __convert_flag> || ...))));
1064 
1065  template <typename _From, typename _To>
1066  concept __simd_generator_convertible_to
1067  = std::convertible_to<_From, _To>
1068  && (!is_arithmetic_v<_From> || __value_preserving_convertible_to<_From, _To>);
1069 
1070  template <typename _Fp, typename _Tp, __simd_size_type... _Is>
1071  requires (__simd_generator_convertible_to<
1072  decltype(declval<_Fp>()(__simd_size_c<_Is>)), _Tp> && ...)
1073  constexpr void
1074  __simd_generator_invokable_impl(integer_sequence<__simd_size_type, _Is...>);
1075 
1076  template <typename _Fp, typename _Tp, __simd_size_type _Np>
1077  concept __simd_generator_invokable = requires {
1078  __simd_generator_invokable_impl<_Fp, _Tp>(make_integer_sequence<__simd_size_type, _Np>());
1079  };
1080 
1081  template <typename _Fp>
1082  concept __index_permutation_function_sized = requires(_Fp const& __f)
1083  {
1084  { __f(0, 0) } -> std::integral;
1085  };
1086 
1087  template <typename _Fp, typename _Simd>
1088  concept __index_permutation_function
1089  = __index_permutation_function_sized<_Fp> || requires(_Fp const& __f) {
1090  { __f(0) } -> std::integral;
1091  };
1092 
1093  /** @internal
1094  * The value of the @c _Bytes template argument to a @c basic_mask specialization.
1095  *
1096  * C++26 [simd.expos.defn]
1097  */
1098  template <typename _Tp>
1099  constexpr size_t __mask_element_size = 0;
1100 
1101  template <size_t _Bytes, __abi_tag _Ap>
1102  constexpr size_t __mask_element_size<basic_mask<_Bytes, _Ap>> = _Bytes;
1103 
1104  // [simd.expos]
1105  template <typename _Vp>
1106  concept __simd_vec_type
1107  = same_as<_Vp, basic_vec<typename _Vp::value_type, typename _Vp::abi_type>>
1108  && is_default_constructible_v<_Vp>;
1109 
1110  template <typename _Vp>
1111  concept __simd_mask_type
1112  = same_as<_Vp, basic_mask<__mask_element_size<_Vp>, typename _Vp::abi_type>>
1113  && is_default_constructible_v<_Vp>;
1114 
1115  /** @internal
1116  * Satisfied if @p _Tp is a data-parallel type.
1117  */
1118  template <typename _Vp>
1119  concept __simd_vec_or_mask_type = __simd_vec_type<_Vp> || __simd_mask_type<_Vp>;
1120 
1121  template <typename _Vp>
1122  concept __simd_floating_point
1123  = __simd_vec_type<_Vp> && floating_point<typename _Vp::value_type>;
1124 
1125  template <typename _Vp>
1126  concept __simd_integral
1127  = __simd_vec_type<_Vp> && integral<typename _Vp::value_type>;
1128 
1129  template <typename _Tp>
1130  concept __converts_to_vec
1131  = __simd_vec_type<decltype(declval<const _Tp&>() + declval<const _Tp&>())>;
1132 
1133  template <__converts_to_vec _Tp>
1134  using __deduced_vec_t = decltype(declval<const _Tp&>() + declval<const _Tp&>());
1135 
1136  template <typename _Vp, typename _Tp>
1137  using __make_compatible_simd_t
1138  = decltype([] {
1139  using _Up = decltype(declval<const _Tp&>() + declval<const _Tp&>());
1140  if constexpr (__simd_vec_type<_Up>)
1141  return _Up();
1142  else
1143  return vec<_Up, _Vp::size()>();
1144  }());
1145 
1146  template <typename _Tp>
1147  concept __math_floating_point = __simd_floating_point<__deduced_vec_t<_Tp>>;
1148 
1149  template <typename _BinaryOperation, typename _Tp>
1150  concept __reduction_binary_operation
1151  = requires (const _BinaryOperation __binary_op, const vec<_Tp, 1> __v) {
1152  { __binary_op(__v, __v) } -> same_as<vec<_Tp, 1>>;
1153  };
1154 
1155  /** @internal
1156  * Returns the highest index @c i where `(__bits >> i) & 1` equals @c 1.
1157  */
1158  [[__gnu__::__always_inline__]]
1159  constexpr __simd_size_type
1160  __highest_bit(std::unsigned_integral auto __bits)
1161  {
1163  constexpr auto _Nd = __int_traits<decltype(__bits)>::__digits;
1164  return _Nd - 1 - __countl_zero(__bits);
1165  }
1166 
1167  template <__vectorizable _Tp, __simd_size_type _Np, __abi_tag _Ap>
1168  using __similar_mask = basic_mask<sizeof(_Tp), decltype(__abi_rebind<_Tp, _Np, _Ap>())>;
1169 
1170  // Allow _Tp to be _InvalidInteger for __integer_from<16>
1171  template <typename _Tp, __simd_size_type _Np, __abi_tag _Ap>
1172  using __similar_vec = basic_vec<_Tp, decltype(__abi_rebind<_Tp, _Np, _Ap>())>;
1173 
1174  // LWG4470 [simd.expos]
1175  template <size_t _Bytes, typename _Ap>
1176  using __simd_vec_from_mask_t = __similar_vec<__integer_from<_Bytes>, _Ap::_S_size, _Ap>;
1177 
1178 #if _GLIBCXX_SIMD_THROW_ON_BAD_VALUE // used for unit tests (also see P3844)
1179  class __bad_value_preserving_cast
1180  {};
1181 
1182 #define __glibcxx_on_bad_value_preserving_cast throw __bad_value_preserving_cast
1183 #else
1184  void __bad_value_preserving_cast(); // not defined
1185 
1186 #define __glibcxx_on_bad_value_preserving_cast __bad_value_preserving_cast
1187 #endif
1188 
1189  template <typename _To, typename _From>
1190 #if _GLIBCXX_SIMD_THROW_ON_BAD_VALUE // see P3844
1191  [[__gnu__::__optimize__("exceptions")]] // work around potential -fno-exceptions
1192 #endif
1193  consteval _To
1194  __value_preserving_cast(const _From& __x)
1195  {
1196  static_assert(is_arithmetic_v<_From>);
1197  if constexpr (!__value_preserving_convertible_to<_From, _To>)
1198  {
1199  using _Up = typename __make_unsigned<_From>::__type;
1200  if (static_cast<_Up>(static_cast<_To>(__x)) != static_cast<_Up>(__x))
1201  __glibcxx_on_bad_value_preserving_cast();
1202  else if constexpr (is_signed_v<_From> && is_unsigned_v<_To>)
1203  {
1204  if (__x < _From())
1205  __glibcxx_on_bad_value_preserving_cast();
1206  }
1207  else if constexpr (unsigned_integral<_From> && signed_integral<_To>)
1208  {
1209  if (__x > numeric_limits<_To>::max())
1210  __glibcxx_on_bad_value_preserving_cast();
1211  }
1212  }
1213  return static_cast<_To>(__x);
1214  }
1215 
1216  /** @internal
1217  * std::pair is not trivially copyable, this one is
1218  */
1219  template <typename _T0, typename _T1>
1220  struct __trivial_pair
1221  {
1222  _T0 _M_first;
1223  _T1 _M_second;
1224  };
1225 
1226  template <typename _From, typename _To>
1227  concept __converts_trivially = convertible_to<_From, _To>
1228  && sizeof(_From) == sizeof(_To)
1229  && is_integral_v<_From> == is_integral_v<_To>
1230  && is_floating_point_v<_From> == is_floating_point_v<_To>;
1231 
1232  [[__gnu__::__always_inline__]]
1233  constexpr void
1234  __bit_foreach(unsigned_integral auto __bits, auto&& __fun)
1235  {
1236  static_assert(sizeof(__bits) >= sizeof(int)); // avoid promotion to int
1237  while (__bits)
1238  {
1239  __fun(__countr_zero(__bits));
1240  __bits &= (__bits - 1);
1241  }
1242  }
1243 
1244  /** @internal
1245  * Optimized @c memcpy for use in partial loads and stores.
1246  *
1247  * The implementation uses at most two fixed-size power-of-2 @c memcpy calls and reduces the
1248  * number of branches to a minimum. The variable size is achieved by overlapping two @c memcpy
1249  * calls.
1250  *
1251  * @tparam _Chunk Copies @p __n times @p _Chunk bytes.
1252  * @tparam _Max Copy no more than @p _Max bytes.
1253  *
1254  * @param __dst The destination pointer.
1255  * @param __src The source pointer.
1256  * @param __n Thu number of chunks that need to be copied.
1257  */
1258  template <size_t _Chunk, size_t _Max>
1259  inline void
1260  __memcpy_chunks(byte* __restrict__ __dst, const byte* __restrict__ __src,
1261  size_t __n)
1262  {
1263  static_assert(_Max <= 64);
1264  static_assert(__has_single_bit(_Chunk) && _Chunk <= 8);
1265  size_t __bytes = _Chunk * __n;
1266  if (__builtin_constant_p(__bytes))
1267  { // If __n is known via constant propagation use a single memcpy call. Since this is still
1268  // a fixed-size memcpy to the compiler, this leaves more room for optimization.
1269  __builtin_memcpy(__dst, __src, __bytes);
1270  }
1271  else if (__bytes > 32 && _Max > 32)
1272  {
1273  __builtin_memcpy(__dst, __src, 32);
1274  __bytes -= 32;
1275  __builtin_memcpy(__dst + __bytes, __src + __bytes, 32);
1276  }
1277  else if (__bytes > 16 && _Max > 16)
1278  {
1279  __builtin_memcpy(__dst, __src, 16);
1280  if constexpr (_Chunk == 8)
1281  {
1282  __bytes -= 8;
1283  __builtin_memcpy(__dst + __bytes, __src + __bytes, 8);
1284  }
1285  else
1286  {
1287  __bytes -= 16;
1288  __builtin_memcpy(__dst + __bytes, __src + __bytes, 16);
1289  }
1290  }
1291  else if (__bytes > 8 && _Max > 8)
1292  {
1293  __builtin_memcpy(__dst, __src, 8);
1294  if constexpr (_Chunk == 4)
1295  {
1296  __bytes -= 4;
1297  __builtin_memcpy(__dst + __bytes, __src + __bytes, 4);
1298  }
1299  else if constexpr (_Chunk < 4)
1300  {
1301  __bytes -= 8;
1302  __builtin_memcpy(__dst + __bytes, __src + __bytes, 8);
1303  }
1304  }
1305  else if (__bytes > 4 && _Max > 4)
1306  {
1307  __builtin_memcpy(__dst, __src, 4);
1308  if constexpr (_Chunk == 2)
1309  {
1310  __bytes -= 2;
1311  __builtin_memcpy(__dst + __bytes, __src + __bytes, 2);
1312  }
1313  else if constexpr (_Chunk == 1)
1314  {
1315  __bytes -= 4;
1316  __builtin_memcpy(__dst + __bytes, __src + __bytes, 4);
1317  }
1318  }
1319  else if (__bytes >= 2)
1320  {
1321  __builtin_memcpy(__dst, __src, 2);
1322  if constexpr (_Chunk == 2)
1323  {
1324  __bytes -= 2;
1325  __builtin_memcpy(__dst + __bytes, __src + __bytes, 2);
1326  }
1327  else if constexpr (_Chunk == 1)
1328  {
1329  __bytes -= 1;
1330  __builtin_memcpy(__dst + __bytes, __src + __bytes, 1);
1331  }
1332  }
1333  else if (__bytes == 1)
1334  __builtin_memcpy(__dst, __src, 1);
1335  }
1336 
1337  // [simd.reductions] identity_element = *see below*
1338  template <typename _Tp, typename _BinaryOperation>
1339  requires __is_one_of<_BinaryOperation,
1340  plus<>, multiplies<>, bit_and<>, bit_or<>, bit_xor<>>::value
1341  consteval _Tp
1342  __default_identity_element()
1343  {
1344  if constexpr (same_as<_BinaryOperation, multiplies<>>)
1345  return _Tp(1);
1346  else if constexpr (same_as<_BinaryOperation, bit_and<>>)
1347  return _Tp(~_Tp());
1348  else
1349  return _Tp(0);
1350  }
1351 } // namespace simd
1352 _GLIBCXX_END_NAMESPACE_VERSION
1353 } // namespace std
1354 
1355 #pragma GCC diagnostic pop
1356 #endif // C++26
1357 #endif // _GLIBCXX_SIMD_DETAILS_H
requires requires
Definition: complex:1948
typename underlying_type< _Tp >::type underlying_type_t
Alias template for underlying_type.
Definition: type_traits:2958
ISO C++ entities toplevel namespace is std.
concept same_as
[concept.same], concept same_as
Definition: concepts:65
concept convertible_to
[concept.convertible], concept convertible_to
Definition: concepts:81
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits<integer-type>.
static constexpr int digits
Definition: limits:218
Properties of fundamental types.
Definition: limits:320
static constexpr _Tp max() noexcept
Definition: limits:328
static constexpr _Tp lowest() noexcept
Definition: limits:334