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
74namespace std _GLIBCXX_VISIBILITY(default)
75{
76_GLIBCXX_BEGIN_NAMESPACE_VERSION
77
78namespace 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 {
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>
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()
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
typename underlying_type< _Tp >::type underlying_type_t
Alias template for underlying_type.
Definition type_traits:2956
typename make_unsigned< _Tp >::type make_unsigned_t
Alias template for make_unsigned.
Definition type_traits:2250
auto declval() noexcept -> decltype(__declval< _Tp >(0))
Definition type_traits:2718
ISO C++ entities toplevel namespace is std.
__make_integer_seq< integer_sequence, _Tp, _Num > make_integer_sequence
Alias template make_integer_sequence.
Definition utility.h:516
__numeric_traits_integer< _Tp > __int_traits
Convenience alias for __numeric_traits<integer-type>.
static constexpr int digits
Definition limits:218
static constexpr _Tp max() noexcept
Definition limits:328
static constexpr _Tp lowest() noexcept
Definition limits:334