native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
native.x86.memory.ccm
1// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
2module;
3#include "native/isa_import.h"
4#include "native/config.h"
5#include "native/attributes.h"
6#include <array>
7#include <concepts>
8#include <cstddef>
9#include <cstdint>
10#include <limits>
11#if NATIVE_HOST_X86
12#include <immintrin.h>
13#endif
14export module native.x86.memory;
15export import native.x86.features;
16export import native.simd;
17
18#if NATIVE_HOST_X86 || defined(NATIVE_DOXYGEN)
19namespace native::detail::x86_memory {
20 // These checks belong solely to constant evaluation. Runtime addresses are
21 // formed by the gather/scatter instruction, including unaligned byte offsets.
22 inline void constant_offset_requires_complete_elements() noexcept { __builtin_trap(); }
23 inline void constant_offset_out_of_range() noexcept { __builtin_trap(); }
24
25 template<int Scale, class T, class I>
26 constexpr std::ptrdiff_t element_offset(I index) noexcept {
27 std::int64_t value = index;
28 if constexpr (Scale < sizeof(T)) {
29 constexpr auto divisor = static_cast<std::int64_t>(sizeof(T) / Scale);
30 if (value % divisor) constant_offset_requires_complete_elements();
31 value /= divisor;
32 } else {
33 constexpr auto multiplier = static_cast<std::int64_t>(Scale / sizeof(T));
34 if (value > std::numeric_limits<std::ptrdiff_t>::max() / multiplier ||
35 value < std::numeric_limits<std::ptrdiff_t>::min() / multiplier) {
36 constant_offset_out_of_range();
37 }
38 value *= multiplier;
39 }
40 if (value > std::numeric_limits<std::ptrdiff_t>::max() ||
41 value < std::numeric_limits<std::ptrdiff_t>::min()) constant_offset_out_of_range();
42 return static_cast<std::ptrdiff_t>(value);
43 }
44
45 template<class V>
46 constexpr auto array(V value) noexcept {
47 std::array<typename V::value_type, V::lanes> result{};
48 value.store(result.data());
49 return result;
50 }
51
52 template<class M>
53 constexpr bool active(M mask, std::size_t lane) noexcept {
54 if constexpr (std::same_as<M, std::uint64_t>) return (mask >> lane) & 1;
55 else return array(mask)[lane] < 0;
56 }
57
58 template<int Scale, class V, class I, class M>
59 constexpr V gather(typename V::value_type const * base, I indices, V source, M mask) noexcept {
60 auto result = array(source);
61 auto offsets = array(indices);
62 constexpr auto count = V::lanes < I::lanes ? V::lanes : I::lanes;
63 for (std::size_t lane = 0; lane < count; ++lane) {
64 if (active(mask, lane)) result[lane] = base[element_offset<Scale, typename V::value_type>(offsets[lane])];
65 }
66 for (std::size_t lane = count; lane < V::lanes; ++lane) result[lane] = {};
67 return V(result);
68 }
69
70 template<int Scale, class V, class I>
71 constexpr void scatter(typename V::value_type * base, I indices, V value, std::uint64_t mask) noexcept {
72 auto values = array(value);
73 auto offsets = array(indices);
74 constexpr auto count = V::lanes < I::lanes ? V::lanes : I::lanes;
75 for (std::size_t lane = 0; lane < count; ++lane) {
76 if (!active(mask, lane)) continue;
77 auto offset = element_offset<Scale, typename V::value_type>(offsets[lane]);
78 base[offset] = values[lane];
79 }
80 }
81}
82
83export namespace native {
97
99 template<int Scale, std::size_t N, isa<x86> Arch>
100 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4)
102 constexpr simd<float, 4, Arch> vgatherdps(float const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
103 if consteval {
104 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 4, Arch>{}, ~std::uint64_t{0});
105 } else {
107 _mm_i32gather_ps(base, indices.to_native(), Scale));
108 }
109 }
110
112 template<int Scale, isa<x86> Arch>
113 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
116 float const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
117 if consteval {
118 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
119 } else {
121 _mm_mask_i32gather_ps(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
122 }
123 }
124
126 template<int Scale, isa<x86> Arch>
127 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
130 float const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
131 if consteval {
132 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
133 } else {
135 _mm_mmask_i32gather_ps(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
136 }
137 }
138
140 template<int Scale, isa<x86> Arch>
141 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
142 native_inline native_target("avx512f,avx512vl")
143 constexpr void mask_vscatterdps(float * base, predicate<4, Arch> mask,
144 simd<std::int32_t, 4, Arch> indices, simd<float, 4, Arch> value) noexcept {
145 if consteval {
146 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
147 } else {
148 _mm_mask_i32scatter_ps(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
149 }
150 }
151
153 template<int Scale, std::size_t N, isa<x86> Arch>
154 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 8)
156 constexpr simd<float, 8, Arch> vgatherdps(float const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
157 if consteval {
158 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 8, Arch>{}, ~std::uint64_t{0});
159 } else {
161 _mm256_i32gather_ps(base, indices.to_native(), Scale));
162 }
163 }
164
166 template<int Scale, isa<x86> Arch>
167 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
170 float const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
171 if consteval {
172 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
173 } else {
175 _mm256_mask_i32gather_ps(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
176 }
177 }
178
180 template<int Scale, isa<x86> Arch>
181 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
184 float const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
185 if consteval {
186 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
187 } else {
189 _mm256_mmask_i32gather_ps(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
190 }
191 }
192
194 template<int Scale, isa<x86> Arch>
195 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
196 native_inline native_target("avx512f,avx512vl")
197 constexpr void mask_vscatterdps(float * base, predicate<8, Arch> mask,
198 simd<std::int32_t, 8, Arch> indices, simd<float, 8, Arch> value) noexcept {
199 if consteval {
200 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
201 } else {
202 _mm256_mask_i32scatter_ps(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
203 }
204 }
205
207 template<int Scale, isa<x86> Arch>
208 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
211 float const * base, simd<std::int32_t, 16, Arch> indices) noexcept {
212 if consteval {
213 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
214 } else {
216 _mm512_mask_i32gather_ps(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
217 }
218 }
219
221 template<int Scale, isa<x86> Arch>
222 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
224 constexpr void mask_vscatterdps(float * base, predicate<16, Arch> mask,
225 simd<std::int32_t, 16, Arch> indices, simd<float, 16, Arch> value) noexcept {
226 if consteval {
227 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
228 } else {
229 _mm512_mask_i32scatter_ps(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
230 }
231 }
232
234 template<int Scale, std::size_t N, isa<x86> Arch, class T>
235 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
237 constexpr simd<T, 4, Arch> vpgatherdd(T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
238 if consteval {
239 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
240 } else {
242 _mm_i32gather_epi32(reinterpret_cast<int const *>(base), indices.to_native(), Scale));
243 }
244 }
245
247 template<int Scale, isa<x86> Arch, class T>
248 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
251 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
252 if consteval {
253 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
254 } else {
256 _mm_mask_i32gather_epi32(source.to_native(), reinterpret_cast<int const *>(base), indices.to_native(), mask.to_native(), Scale));
257 }
258 }
259
261 template<int Scale, isa<x86> Arch, class T>
262 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
265 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
266 if consteval {
267 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
268 } else {
270 _mm_mmask_i32gather_epi32(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
271 }
272 }
273
275 template<int Scale, isa<x86> Arch, class T>
276 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
277 native_inline native_target("avx512f,avx512vl")
278 constexpr void mask_vpscatterdd(T * base, predicate<4, Arch> mask,
279 simd<std::int32_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
280 if consteval {
281 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
282 } else {
283 _mm_mask_i32scatter_epi32(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
284 }
285 }
286
288 template<int Scale, std::size_t N, isa<x86> Arch, class T>
289 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 8 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
291 constexpr simd<T, 8, Arch> vpgatherdd(T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
292 if consteval {
293 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 8, Arch>{}, ~std::uint64_t{0});
294 } else {
296 _mm256_i32gather_epi32(reinterpret_cast<int const *>(base), indices.to_native(), Scale));
297 }
298 }
299
301 template<int Scale, isa<x86> Arch, class T>
302 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
305 T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
306 if consteval {
307 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
308 } else {
310 _mm256_mask_i32gather_epi32(source.to_native(), reinterpret_cast<int const *>(base), indices.to_native(), mask.to_native(), Scale));
311 }
312 }
313
315 template<int Scale, isa<x86> Arch, class T>
316 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
319 T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
320 if consteval {
321 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
322 } else {
324 _mm256_mmask_i32gather_epi32(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
325 }
326 }
327
329 template<int Scale, isa<x86> Arch, class T>
330 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
331 native_inline native_target("avx512f,avx512vl")
332 constexpr void mask_vpscatterdd(T * base, predicate<8, Arch> mask,
333 simd<std::int32_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
334 if consteval {
335 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
336 } else {
337 _mm256_mask_i32scatter_epi32(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
338 }
339 }
340
342 template<int Scale, isa<x86> Arch, class T>
343 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
346 T const * base, simd<std::int32_t, 16, Arch> indices) noexcept {
347 if consteval {
348 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
349 } else {
351 _mm512_mask_i32gather_epi32(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
352 }
353 }
354
356 template<int Scale, isa<x86> Arch, class T>
357 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
359 constexpr void mask_vpscatterdd(T * base, predicate<16, Arch> mask,
360 simd<std::int32_t, 16, Arch> indices, simd<T, 16, Arch> value) noexcept {
361 if consteval {
362 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
363 } else {
364 _mm512_mask_i32scatter_epi32(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
365 }
366 }
367
369 template<int Scale, std::size_t N, isa<x86> Arch>
370 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2)
372 constexpr simd<double, 2, Arch> vgatherdpd(double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
373 if consteval {
374 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 2, Arch>{}, ~std::uint64_t{0});
375 } else {
377 _mm_i32gather_pd(base, indices.to_native(), Scale));
378 }
379 }
380
382 template<int Scale, isa<x86> Arch>
383 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
386 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
387 if consteval {
388 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
389 } else {
391 _mm_mask_i32gather_pd(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
392 }
393 }
394
396 template<int Scale, isa<x86> Arch>
397 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
400 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
401 if consteval {
402 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
403 } else {
405 _mm_mmask_i32gather_pd(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
406 }
407 }
408
410 template<int Scale, isa<x86> Arch>
411 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
412 native_inline native_target("avx512f,avx512vl")
413 constexpr void mask_vscatterdpd(double * base, predicate<2, Arch> mask,
414 simd<std::int32_t, 4, Arch> indices, simd<double, 2, Arch> value) noexcept {
415 if consteval {
416 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
417 } else {
418 _mm_mask_i32scatter_pd(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
419 }
420 }
421
423 template<int Scale, std::size_t N, isa<x86> Arch>
424 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4)
426 constexpr simd<double, 4, Arch> vgatherdpd(double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
427 if consteval {
428 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 4, Arch>{}, ~std::uint64_t{0});
429 } else {
431 _mm256_i32gather_pd(base, indices.to_native(), Scale));
432 }
433 }
434
436 template<int Scale, isa<x86> Arch>
437 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
440 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
441 if consteval {
442 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
443 } else {
445 _mm256_mask_i32gather_pd(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
446 }
447 }
448
450 template<int Scale, isa<x86> Arch>
451 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
454 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
455 if consteval {
456 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
457 } else {
459 _mm256_mmask_i32gather_pd(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
460 }
461 }
462
464 template<int Scale, isa<x86> Arch>
465 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
466 native_inline native_target("avx512f,avx512vl")
467 constexpr void mask_vscatterdpd(double * base, predicate<4, Arch> mask,
468 simd<std::int32_t, 4, Arch> indices, simd<double, 4, Arch> value) noexcept {
469 if consteval {
470 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
471 } else {
472 _mm256_mask_i32scatter_pd(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
473 }
474 }
475
477 template<int Scale, isa<x86> Arch>
478 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
481 double const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
482 if consteval {
483 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
484 } else {
486 _mm512_mask_i32gather_pd(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
487 }
488 }
489
491 template<int Scale, isa<x86> Arch>
492 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
494 constexpr void mask_vscatterdpd(double * base, predicate<8, Arch> mask,
495 simd<std::int32_t, 8, Arch> indices, simd<double, 8, Arch> value) noexcept {
496 if consteval {
497 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
498 } else {
499 _mm512_mask_i32scatter_pd(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
500 }
501 }
502
504 template<int Scale, std::size_t N, isa<x86> Arch, class T>
505 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
507 constexpr simd<T, 2, Arch> vpgatherdq(T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
508 if consteval {
509 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 2, Arch>{}, ~std::uint64_t{0});
510 } else {
512 _mm_i32gather_epi64(reinterpret_cast<long long const *>(base), indices.to_native(), Scale));
513 }
514 }
515
517 template<int Scale, isa<x86> Arch, class T>
518 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
521 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
522 if consteval {
523 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
524 } else {
526 _mm_mask_i32gather_epi64(source.to_native(), reinterpret_cast<long long const *>(base), indices.to_native(), mask.to_native(), Scale));
527 }
528 }
529
531 template<int Scale, isa<x86> Arch, class T>
532 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
535 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
536 if consteval {
537 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
538 } else {
540 _mm_mmask_i32gather_epi64(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
541 }
542 }
543
545 template<int Scale, isa<x86> Arch, class T>
546 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
547 native_inline native_target("avx512f,avx512vl")
548 constexpr void mask_vpscatterdq(T * base, predicate<2, Arch> mask,
549 simd<std::int32_t, 4, Arch> indices, simd<T, 2, Arch> value) noexcept {
550 if consteval {
551 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
552 } else {
553 _mm_mask_i32scatter_epi64(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
554 }
555 }
556
558 template<int Scale, std::size_t N, isa<x86> Arch, class T>
559 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
561 constexpr simd<T, 4, Arch> vpgatherdq(T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
562 if consteval {
563 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
564 } else {
566 _mm256_i32gather_epi64(reinterpret_cast<long long const *>(base), indices.to_native(), Scale));
567 }
568 }
569
571 template<int Scale, isa<x86> Arch, class T>
572 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
575 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
576 if consteval {
577 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
578 } else {
580 _mm256_mask_i32gather_epi64(source.to_native(), reinterpret_cast<long long const *>(base), indices.to_native(), mask.to_native(), Scale));
581 }
582 }
583
585 template<int Scale, isa<x86> Arch, class T>
586 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
589 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
590 if consteval {
591 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
592 } else {
594 _mm256_mmask_i32gather_epi64(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
595 }
596 }
597
599 template<int Scale, isa<x86> Arch, class T>
600 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
601 native_inline native_target("avx512f,avx512vl")
602 constexpr void mask_vpscatterdq(T * base, predicate<4, Arch> mask,
603 simd<std::int32_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
604 if consteval {
605 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
606 } else {
607 _mm256_mask_i32scatter_epi64(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
608 }
609 }
610
612 template<int Scale, isa<x86> Arch, class T>
613 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
616 T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
617 if consteval {
618 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
619 } else {
621 _mm512_mask_i32gather_epi64(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
622 }
623 }
624
626 template<int Scale, isa<x86> Arch, class T>
627 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
629 constexpr void mask_vpscatterdq(T * base, predicate<8, Arch> mask,
630 simd<std::int32_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
631 if consteval {
632 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
633 } else {
634 _mm512_mask_i32scatter_epi64(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
635 }
636 }
637
639 template<int Scale, std::size_t N, isa<x86> Arch>
640 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4)
642 constexpr simd<float, 4, Arch> vgatherqps(float const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
643 if consteval {
644 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 4, Arch>{}, ~std::uint64_t{0});
645 } else {
647 _mm_i64gather_ps(base, indices.to_native(), Scale));
648 }
649 }
650
652 template<int Scale, isa<x86> Arch>
653 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
656 float const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
657 if consteval {
658 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
659 } else {
661 _mm_mask_i64gather_ps(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
662 }
663 }
664
666 template<int Scale, isa<x86> Arch>
667 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
670 float const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
671 if consteval {
672 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
673 } else {
675 _mm_mmask_i64gather_ps(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
676 }
677 }
678
680 template<int Scale, isa<x86> Arch>
681 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
682 native_inline native_target("avx512f,avx512vl")
683 constexpr void mask_vscatterqps(float * base, predicate<4, Arch> mask,
684 simd<std::int64_t, 2, Arch> indices, simd<float, 4, Arch> value) noexcept {
685 if consteval {
686 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
687 } else {
688 _mm_mask_i64scatter_ps(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
689 }
690 }
691
693 template<int Scale, std::size_t N, isa<x86> Arch>
694 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4)
696 constexpr simd<float, 4, Arch> vgatherqps(float const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
697 if consteval {
698 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 4, Arch>{}, ~std::uint64_t{0});
699 } else {
701 _mm256_i64gather_ps(base, indices.to_native(), Scale));
702 }
703 }
704
706 template<int Scale, isa<x86> Arch>
707 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
710 float const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
711 if consteval {
712 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
713 } else {
715 _mm256_mask_i64gather_ps(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
716 }
717 }
718
720 template<int Scale, isa<x86> Arch>
721 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
724 float const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
725 if consteval {
726 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
727 } else {
729 _mm256_mmask_i64gather_ps(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
730 }
731 }
732
734 template<int Scale, isa<x86> Arch>
735 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
736 native_inline native_target("avx512f,avx512vl")
737 constexpr void mask_vscatterqps(float * base, predicate<4, Arch> mask,
738 simd<std::int64_t, 4, Arch> indices, simd<float, 4, Arch> value) noexcept {
739 if consteval {
740 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
741 } else {
742 _mm256_mask_i64scatter_ps(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
743 }
744 }
745
747 template<int Scale, isa<x86> Arch>
748 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
751 float const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
752 if consteval {
753 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
754 } else {
756 _mm512_mask_i64gather_ps(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
757 }
758 }
759
761 template<int Scale, isa<x86> Arch>
762 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
764 constexpr void mask_vscatterqps(float * base, predicate<8, Arch> mask,
765 simd<std::int64_t, 8, Arch> indices, simd<float, 8, Arch> value) noexcept {
766 if consteval {
767 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
768 } else {
769 _mm512_mask_i64scatter_ps(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
770 }
771 }
772
774 template<int Scale, std::size_t N, isa<x86> Arch, class T>
775 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
777 constexpr simd<T, 4, Arch> vpgatherqd(T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
778 if consteval {
779 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
780 } else {
782 _mm_i64gather_epi32(reinterpret_cast<int const *>(base), indices.to_native(), Scale));
783 }
784 }
785
787 template<int Scale, isa<x86> Arch, class T>
788 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
791 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
792 if consteval {
793 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
794 } else {
796 _mm_mask_i64gather_epi32(source.to_native(), reinterpret_cast<int const *>(base), indices.to_native(), mask.to_native(), Scale));
797 }
798 }
799
801 template<int Scale, isa<x86> Arch, class T>
802 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
805 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
806 if consteval {
807 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
808 } else {
810 _mm_mmask_i64gather_epi32(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
811 }
812 }
813
815 template<int Scale, isa<x86> Arch, class T>
816 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
817 native_inline native_target("avx512f,avx512vl")
818 constexpr void mask_vpscatterqd(T * base, predicate<4, Arch> mask,
819 simd<std::int64_t, 2, Arch> indices, simd<T, 4, Arch> value) noexcept {
820 if consteval {
821 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
822 } else {
823 _mm_mask_i64scatter_epi32(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
824 }
825 }
826
828 template<int Scale, std::size_t N, isa<x86> Arch, class T>
829 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
831 constexpr simd<T, 4, Arch> vpgatherqd(T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
832 if consteval {
833 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
834 } else {
836 _mm256_i64gather_epi32(reinterpret_cast<int const *>(base), indices.to_native(), Scale));
837 }
838 }
839
841 template<int Scale, isa<x86> Arch, class T>
842 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
845 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
846 if consteval {
847 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
848 } else {
850 _mm256_mask_i64gather_epi32(source.to_native(), reinterpret_cast<int const *>(base), indices.to_native(), mask.to_native(), Scale));
851 }
852 }
853
855 template<int Scale, isa<x86> Arch, class T>
856 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
859 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
860 if consteval {
861 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
862 } else {
864 _mm256_mmask_i64gather_epi32(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
865 }
866 }
867
869 template<int Scale, isa<x86> Arch, class T>
870 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
871 native_inline native_target("avx512f,avx512vl")
872 constexpr void mask_vpscatterqd(T * base, predicate<4, Arch> mask,
873 simd<std::int64_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
874 if consteval {
875 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
876 } else {
877 _mm256_mask_i64scatter_epi32(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
878 }
879 }
880
882 template<int Scale, isa<x86> Arch, class T>
883 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
886 T const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
887 if consteval {
888 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
889 } else {
891 _mm512_mask_i64gather_epi32(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
892 }
893 }
894
896 template<int Scale, isa<x86> Arch, class T>
897 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>))
899 constexpr void mask_vpscatterqd(T * base, predicate<8, Arch> mask,
900 simd<std::int64_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
901 if consteval {
902 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
903 } else {
904 _mm512_mask_i64scatter_epi32(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
905 }
906 }
907
909 template<int Scale, std::size_t N, isa<x86> Arch>
910 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2)
912 constexpr simd<double, 2, Arch> vgatherqpd(double const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
913 if consteval {
914 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 2, Arch>{}, ~std::uint64_t{0});
915 } else {
917 _mm_i64gather_pd(base, indices.to_native(), Scale));
918 }
919 }
920
922 template<int Scale, isa<x86> Arch>
923 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
926 double const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
927 if consteval {
928 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
929 } else {
931 _mm_mask_i64gather_pd(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
932 }
933 }
934
936 template<int Scale, isa<x86> Arch>
937 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
940 double const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
941 if consteval {
942 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
943 } else {
945 _mm_mmask_i64gather_pd(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
946 }
947 }
948
950 template<int Scale, isa<x86> Arch>
951 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
952 native_inline native_target("avx512f,avx512vl")
953 constexpr void mask_vscatterqpd(double * base, predicate<2, Arch> mask,
954 simd<std::int64_t, 2, Arch> indices, simd<double, 2, Arch> value) noexcept {
955 if consteval {
956 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
957 } else {
958 _mm_mask_i64scatter_pd(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
959 }
960 }
961
963 template<int Scale, std::size_t N, isa<x86> Arch>
964 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4)
966 constexpr simd<double, 4, Arch> vgatherqpd(double const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
967 if consteval {
968 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 4, Arch>{}, ~std::uint64_t{0});
969 } else {
971 _mm256_i64gather_pd(base, indices.to_native(), Scale));
972 }
973 }
974
976 template<int Scale, isa<x86> Arch>
977 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
980 double const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
981 if consteval {
982 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
983 } else {
985 _mm256_mask_i64gather_pd(source.to_native(), base, indices.to_native(), __builtin_bit_cast(typename decltype(source)::native_type, mask.to_native()), Scale));
986 }
987 }
988
990 template<int Scale, isa<x86> Arch>
991 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
994 double const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
995 if consteval {
996 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
997 } else {
999 _mm256_mmask_i64gather_pd(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
1000 }
1001 }
1002
1004 template<int Scale, isa<x86> Arch>
1005 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
1006 native_inline native_target("avx512f,avx512vl")
1007 constexpr void mask_vscatterqpd(double * base, predicate<4, Arch> mask,
1008 simd<std::int64_t, 4, Arch> indices, simd<double, 4, Arch> value) noexcept {
1009 if consteval {
1010 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1011 } else {
1012 _mm256_mask_i64scatter_pd(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
1013 }
1014 }
1015
1017 template<int Scale, isa<x86> Arch>
1018 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
1021 double const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
1022 if consteval {
1023 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1024 } else {
1026 _mm512_mask_i64gather_pd(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
1027 }
1028 }
1029
1031 template<int Scale, isa<x86> Arch>
1032 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8))
1033 native_inline native_target("avx512f")
1034 constexpr void mask_vscatterqpd(double * base, predicate<8, Arch> mask,
1035 simd<std::int64_t, 8, Arch> indices, simd<double, 8, Arch> value) noexcept {
1036 if consteval {
1037 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1038 } else {
1039 _mm512_mask_i64scatter_pd(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
1040 }
1041 }
1042
1044 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1045 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1047 constexpr simd<T, 2, Arch> vpgatherqq(T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1048 if consteval {
1049 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 2, Arch>{}, ~std::uint64_t{0});
1050 } else {
1052 _mm_i64gather_epi64(reinterpret_cast<long long const *>(base), indices.to_native(), Scale));
1053 }
1054 }
1055
1057 template<int Scale, isa<x86> Arch, class T>
1058 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1061 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1062 if consteval {
1063 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1064 } else {
1066 _mm_mask_i64gather_epi64(source.to_native(), reinterpret_cast<long long const *>(base), indices.to_native(), mask.to_native(), Scale));
1067 }
1068 }
1069
1071 template<int Scale, isa<x86> Arch, class T>
1072 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1073 native_nodiscard native_inline native_target("avx512f,avx512vl")
1075 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1076 if consteval {
1077 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1078 } else {
1080 _mm_mmask_i64gather_epi64(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
1081 }
1082 }
1083
1085 template<int Scale, isa<x86> Arch, class T>
1086 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1087 native_inline native_target("avx512f,avx512vl")
1088 constexpr void mask_vpscatterqq(T * base, predicate<2, Arch> mask,
1089 simd<std::int64_t, 2, Arch> indices, simd<T, 2, Arch> value) noexcept {
1090 if consteval {
1091 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1092 } else {
1093 _mm_mask_i64scatter_epi64(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
1094 }
1095 }
1096
1098 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1099 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1101 constexpr simd<T, 4, Arch> vpgatherqq(T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1102 if consteval {
1103 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
1104 } else {
1106 _mm256_i64gather_epi64(reinterpret_cast<long long const *>(base), indices.to_native(), Scale));
1107 }
1108 }
1109
1111 template<int Scale, isa<x86> Arch, class T>
1112 requires(Arch.has(x86_feature::avx2) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1115 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1116 if consteval {
1117 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1118 } else {
1120 _mm256_mask_i64gather_epi64(source.to_native(), reinterpret_cast<long long const *>(base), indices.to_native(), mask.to_native(), Scale));
1121 }
1122 }
1123
1125 template<int Scale, isa<x86> Arch, class T>
1126 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1127 native_nodiscard native_inline native_target("avx512f,avx512vl")
1129 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1130 if consteval {
1131 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1132 } else {
1134 _mm256_mmask_i64gather_epi64(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
1135 }
1136 }
1137
1139 template<int Scale, isa<x86> Arch, class T>
1140 requires(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1141 native_inline native_target("avx512f,avx512vl")
1142 constexpr void mask_vpscatterqq(T * base, predicate<4, Arch> mask,
1143 simd<std::int64_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
1144 if consteval {
1145 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1146 } else {
1147 _mm256_mask_i64scatter_epi64(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
1148 }
1149 }
1150
1152 template<int Scale, isa<x86> Arch, class T>
1153 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1156 T const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
1157 if consteval {
1158 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1159 } else {
1161 _mm512_mask_i64gather_epi64(source.to_native(), mask.to_bitset(), indices.to_native(), base, Scale));
1162 }
1163 }
1164
1166 template<int Scale, isa<x86> Arch, class T>
1167 requires(Arch.has(x86_feature::avx512f) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>))
1168 native_inline native_target("avx512f")
1169 constexpr void mask_vpscatterqq(T * base, predicate<8, Arch> mask,
1170 simd<std::int64_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
1171 if consteval {
1172 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1173 } else {
1174 _mm512_mask_i64scatter_epi64(base, mask.to_bitset(), indices.to_native(), value.to_native(), Scale);
1175 }
1176 }
1177
1179 template<int Scale, std::size_t N, isa<x86> Arch>
1180 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1181 native_nodiscard consteval simd<float, 4, Arch> vgatherdps(float const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1182 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 4, Arch>{}, ~std::uint64_t{0});
1183 }
1184
1185 template<int Scale, isa<x86> Arch>
1186 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1188 float const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1189 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1190 }
1191
1192 template<int Scale, isa<x86> Arch>
1193 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1194 native_nodiscard consteval simd<float, 4, Arch> mask_vgatherdps(simd<float, 4, Arch> source, predicate<4, Arch> mask,
1195 float const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1196 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1197 }
1198
1199 template<int Scale, isa<x86> Arch>
1200 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1201 consteval void mask_vscatterdps(float * base, predicate<4, Arch> mask,
1202 simd<std::int32_t, 4, Arch> indices, simd<float, 4, Arch> value) noexcept {
1203 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1204 }
1205
1206 template<int Scale, std::size_t N, isa<x86> Arch>
1207 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 8 && requires { sizeof(simd<float, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1208 native_nodiscard consteval simd<float, 8, Arch> vgatherdps(float const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1209 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 8, Arch>{}, ~std::uint64_t{0});
1210 }
1211
1212 template<int Scale, isa<x86> Arch>
1213 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1215 float const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1216 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1217 }
1218
1219 template<int Scale, isa<x86> Arch>
1220 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1221 native_nodiscard consteval simd<float, 8, Arch> mask_vgatherdps(simd<float, 8, Arch> source, predicate<8, Arch> mask,
1222 float const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1223 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1224 }
1225
1226 template<int Scale, isa<x86> Arch>
1227 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1228 consteval void mask_vscatterdps(float * base, predicate<8, Arch> mask,
1229 simd<std::int32_t, 8, Arch> indices, simd<float, 8, Arch> value) noexcept {
1230 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1231 }
1232
1233 template<int Scale, isa<x86> Arch>
1234 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 16, Arch>); sizeof(simd<std::int32_t, 16, Arch>); })
1236 float const * base, simd<std::int32_t, 16, Arch> indices) noexcept {
1237 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1238 }
1239
1240 template<int Scale, isa<x86> Arch>
1241 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 16, Arch>); sizeof(simd<std::int32_t, 16, Arch>); })
1242 consteval void mask_vscatterdps(float * base, predicate<16, Arch> mask,
1243 simd<std::int32_t, 16, Arch> indices, simd<float, 16, Arch> value) noexcept {
1244 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1245 }
1246
1247 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1248 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1249 native_nodiscard consteval simd<T, 4, Arch> vpgatherdd(T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1250 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
1251 }
1252
1253 template<int Scale, isa<x86> Arch, class T>
1254 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1256 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1257 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1258 }
1259
1260 template<int Scale, isa<x86> Arch, class T>
1261 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1262 native_nodiscard consteval simd<T, 4, Arch> mask_vpgatherdd(simd<T, 4, Arch> source, predicate<4, Arch> mask,
1263 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1264 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1265 }
1266
1267 template<int Scale, isa<x86> Arch, class T>
1268 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1269 consteval void mask_vpscatterdd(T * base, predicate<4, Arch> mask,
1270 simd<std::int32_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
1271 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1272 }
1273
1274 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1275 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 8 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1276 native_nodiscard consteval simd<T, 8, Arch> vpgatherdd(T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1277 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 8, Arch>{}, ~std::uint64_t{0});
1278 }
1279
1280 template<int Scale, isa<x86> Arch, class T>
1281 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1283 T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1284 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1285 }
1286
1287 template<int Scale, isa<x86> Arch, class T>
1288 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1289 native_nodiscard consteval simd<T, 8, Arch> mask_vpgatherdd(simd<T, 8, Arch> source, predicate<8, Arch> mask,
1290 T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1291 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1292 }
1293
1294 template<int Scale, isa<x86> Arch, class T>
1295 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1296 consteval void mask_vpscatterdd(T * base, predicate<8, Arch> mask,
1297 simd<std::int32_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
1298 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1299 }
1300
1301 template<int Scale, isa<x86> Arch, class T>
1302 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 16, Arch>); sizeof(simd<std::int32_t, 16, Arch>); })
1304 T const * base, simd<std::int32_t, 16, Arch> indices) noexcept {
1305 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1306 }
1307
1308 template<int Scale, isa<x86> Arch, class T>
1309 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 16, Arch>); sizeof(simd<std::int32_t, 16, Arch>); })
1311 simd<std::int32_t, 16, Arch> indices, simd<T, 16, Arch> value) noexcept {
1312 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1313 }
1314
1315 template<int Scale, std::size_t N, isa<x86> Arch>
1316 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2 && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1317 native_nodiscard consteval simd<double, 2, Arch> vgatherdpd(double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1318 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 2, Arch>{}, ~std::uint64_t{0});
1319 }
1320
1321 template<int Scale, isa<x86> Arch>
1322 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1324 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1325 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1326 }
1327
1328 template<int Scale, isa<x86> Arch>
1329 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1330 native_nodiscard consteval simd<double, 2, Arch> mask_vgatherdpd(simd<double, 2, Arch> source, predicate<2, Arch> mask,
1331 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1332 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1333 }
1334
1335 template<int Scale, isa<x86> Arch>
1336 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1337 consteval void mask_vscatterdpd(double * base, predicate<2, Arch> mask,
1338 simd<std::int32_t, 4, Arch> indices, simd<double, 2, Arch> value) noexcept {
1339 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1340 }
1341
1342 template<int Scale, std::size_t N, isa<x86> Arch>
1343 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1344 native_nodiscard consteval simd<double, 4, Arch> vgatherdpd(double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1345 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 4, Arch>{}, ~std::uint64_t{0});
1346 }
1347
1348 template<int Scale, isa<x86> Arch>
1349 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1351 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1352 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1353 }
1354
1355 template<int Scale, isa<x86> Arch>
1356 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1357 native_nodiscard consteval simd<double, 4, Arch> mask_vgatherdpd(simd<double, 4, Arch> source, predicate<4, Arch> mask,
1358 double const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1359 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1360 }
1361
1362 template<int Scale, isa<x86> Arch>
1363 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1364 consteval void mask_vscatterdpd(double * base, predicate<4, Arch> mask,
1365 simd<std::int32_t, 4, Arch> indices, simd<double, 4, Arch> value) noexcept {
1366 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1367 }
1368
1369 template<int Scale, isa<x86> Arch>
1370 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1372 double const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1373 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1374 }
1375
1376 template<int Scale, isa<x86> Arch>
1377 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1378 consteval void mask_vscatterdpd(double * base, predicate<8, Arch> mask,
1379 simd<std::int32_t, 8, Arch> indices, simd<double, 8, Arch> value) noexcept {
1380 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1381 }
1382
1383 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1384 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1385 native_nodiscard consteval simd<T, 2, Arch> vpgatherdq(T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1386 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 2, Arch>{}, ~std::uint64_t{0});
1387 }
1388
1389 template<int Scale, isa<x86> Arch, class T>
1390 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1392 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1393 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1394 }
1395
1396 template<int Scale, isa<x86> Arch, class T>
1397 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1398 native_nodiscard consteval simd<T, 2, Arch> mask_vpgatherdq(simd<T, 2, Arch> source, predicate<2, Arch> mask,
1399 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1400 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1401 }
1402
1403 template<int Scale, isa<x86> Arch, class T>
1404 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1405 consteval void mask_vpscatterdq(T * base, predicate<2, Arch> mask,
1406 simd<std::int32_t, 4, Arch> indices, simd<T, 2, Arch> value) noexcept {
1407 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1408 }
1409
1410 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1411 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1412 native_nodiscard consteval simd<T, 4, Arch> vpgatherdq(T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1413 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
1414 }
1415
1416 template<int Scale, isa<x86> Arch, class T>
1417 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1419 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1420 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1421 }
1422
1423 template<int Scale, isa<x86> Arch, class T>
1424 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1425 native_nodiscard consteval simd<T, 4, Arch> mask_vpgatherdq(simd<T, 4, Arch> source, predicate<4, Arch> mask,
1426 T const * base, simd<std::int32_t, 4, Arch> indices) noexcept {
1427 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1428 }
1429
1430 template<int Scale, isa<x86> Arch, class T>
1431 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); })
1432 consteval void mask_vpscatterdq(T * base, predicate<4, Arch> mask,
1433 simd<std::int32_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
1434 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1435 }
1436
1437 template<int Scale, isa<x86> Arch, class T>
1438 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1440 T const * base, simd<std::int32_t, 8, Arch> indices) noexcept {
1441 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1442 }
1443
1444 template<int Scale, isa<x86> Arch, class T>
1445 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int32_t, 8, Arch>); })
1446 consteval void mask_vpscatterdq(T * base, predicate<8, Arch> mask,
1447 simd<std::int32_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
1448 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1449 }
1450
1451 template<int Scale, std::size_t N, isa<x86> Arch>
1452 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1453 native_nodiscard consteval simd<float, 4, Arch> vgatherqps(float const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1454 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 4, Arch>{}, ~std::uint64_t{0});
1455 }
1456
1457 template<int Scale, isa<x86> Arch>
1458 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1460 float const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1461 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1462 }
1463
1464 template<int Scale, isa<x86> Arch>
1465 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1466 native_nodiscard consteval simd<float, 4, Arch> mask_vgatherqps(simd<float, 4, Arch> source, predicate<4, Arch> mask,
1467 float const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1468 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1469 }
1470
1471 template<int Scale, isa<x86> Arch>
1472 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1473 consteval void mask_vscatterqps(float * base, predicate<4, Arch> mask,
1474 simd<std::int64_t, 2, Arch> indices, simd<float, 4, Arch> value) noexcept {
1475 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1476 }
1477
1478 template<int Scale, std::size_t N, isa<x86> Arch>
1479 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1480 native_nodiscard consteval simd<float, 4, Arch> vgatherqps(float const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1481 return detail::x86_memory::gather<Scale>(base, indices, simd<float, 4, Arch>{}, ~std::uint64_t{0});
1482 }
1483
1484 template<int Scale, isa<x86> Arch>
1485 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1487 float const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1488 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1489 }
1490
1491 template<int Scale, isa<x86> Arch>
1492 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1493 native_nodiscard consteval simd<float, 4, Arch> mask_vgatherqps(simd<float, 4, Arch> source, predicate<4, Arch> mask,
1494 float const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1495 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1496 }
1497
1498 template<int Scale, isa<x86> Arch>
1499 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1500 consteval void mask_vscatterqps(float * base, predicate<4, Arch> mask,
1501 simd<std::int64_t, 4, Arch> indices, simd<float, 4, Arch> value) noexcept {
1502 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1503 }
1504
1505 template<int Scale, isa<x86> Arch>
1506 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1508 float const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
1509 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1510 }
1511
1512 template<int Scale, isa<x86> Arch>
1513 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<float, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1514 consteval void mask_vscatterqps(float * base, predicate<8, Arch> mask,
1515 simd<std::int64_t, 8, Arch> indices, simd<float, 8, Arch> value) noexcept {
1516 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1517 }
1518
1519 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1520 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1521 native_nodiscard consteval simd<T, 4, Arch> vpgatherqd(T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1522 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
1523 }
1524
1525 template<int Scale, isa<x86> Arch, class T>
1526 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1528 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1529 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1530 }
1531
1532 template<int Scale, isa<x86> Arch, class T>
1533 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1534 native_nodiscard consteval simd<T, 4, Arch> mask_vpgatherqd(simd<T, 4, Arch> source, predicate<4, Arch> mask,
1535 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1536 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1537 }
1538
1539 template<int Scale, isa<x86> Arch, class T>
1540 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1541 consteval void mask_vpscatterqd(T * base, predicate<4, Arch> mask,
1542 simd<std::int64_t, 2, Arch> indices, simd<T, 4, Arch> value) noexcept {
1543 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1544 }
1545
1546 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1547 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1548 native_nodiscard consteval simd<T, 4, Arch> vpgatherqd(T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1549 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
1550 }
1551
1552 template<int Scale, isa<x86> Arch, class T>
1553 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int32_t, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1555 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1556 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1557 }
1558
1559 template<int Scale, isa<x86> Arch, class T>
1560 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1561 native_nodiscard consteval simd<T, 4, Arch> mask_vpgatherqd(simd<T, 4, Arch> source, predicate<4, Arch> mask,
1562 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1563 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1564 }
1565
1566 template<int Scale, isa<x86> Arch, class T>
1567 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1568 consteval void mask_vpscatterqd(T * base, predicate<4, Arch> mask,
1569 simd<std::int64_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
1570 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1571 }
1572
1573 template<int Scale, isa<x86> Arch, class T>
1574 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1576 T const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
1577 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1578 }
1579
1580 template<int Scale, isa<x86> Arch, class T>
1581 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int32_t> || std::same_as<T, std::uint32_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1582 consteval void mask_vpscatterqd(T * base, predicate<8, Arch> mask,
1583 simd<std::int64_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
1584 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1585 }
1586
1587 template<int Scale, std::size_t N, isa<x86> Arch>
1588 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2 && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1589 native_nodiscard consteval simd<double, 2, Arch> vgatherqpd(double const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1590 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 2, Arch>{}, ~std::uint64_t{0});
1591 }
1592
1593 template<int Scale, isa<x86> Arch>
1594 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1596 double const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1597 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1598 }
1599
1600 template<int Scale, isa<x86> Arch>
1601 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1602 native_nodiscard consteval simd<double, 2, Arch> mask_vgatherqpd(simd<double, 2, Arch> source, predicate<2, Arch> mask,
1603 double const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1604 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1605 }
1606
1607 template<int Scale, isa<x86> Arch>
1608 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1609 consteval void mask_vscatterqpd(double * base, predicate<2, Arch> mask,
1610 simd<std::int64_t, 2, Arch> indices, simd<double, 2, Arch> value) noexcept {
1611 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1612 }
1613
1614 template<int Scale, std::size_t N, isa<x86> Arch>
1615 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1616 native_nodiscard consteval simd<double, 4, Arch> vgatherqpd(double const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1617 return detail::x86_memory::gather<Scale>(base, indices, simd<double, 4, Arch>{}, ~std::uint64_t{0});
1618 }
1619
1620 template<int Scale, isa<x86> Arch>
1621 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1623 double const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1624 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1625 }
1626
1627 template<int Scale, isa<x86> Arch>
1628 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1629 native_nodiscard consteval simd<double, 4, Arch> mask_vgatherqpd(simd<double, 4, Arch> source, predicate<4, Arch> mask,
1630 double const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1631 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1632 }
1633
1634 template<int Scale, isa<x86> Arch>
1635 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1636 consteval void mask_vscatterqpd(double * base, predicate<4, Arch> mask,
1637 simd<std::int64_t, 4, Arch> indices, simd<double, 4, Arch> value) noexcept {
1638 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1639 }
1640
1641 template<int Scale, isa<x86> Arch>
1642 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1644 double const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
1645 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1646 }
1647
1648 template<int Scale, isa<x86> Arch>
1649 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && requires { sizeof(simd<double, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1650 consteval void mask_vscatterqpd(double * base, predicate<8, Arch> mask,
1651 simd<std::int64_t, 8, Arch> indices, simd<double, 8, Arch> value) noexcept {
1652 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1653 }
1654
1655 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1656 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 2 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1657 native_nodiscard consteval simd<T, 2, Arch> vpgatherqq(T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1658 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 2, Arch>{}, ~std::uint64_t{0});
1659 }
1660
1661 template<int Scale, isa<x86> Arch, class T>
1662 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1664 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1665 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1666 }
1667
1668 template<int Scale, isa<x86> Arch, class T>
1669 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1670 native_nodiscard consteval simd<T, 2, Arch> mask_vpgatherqq(simd<T, 2, Arch> source, predicate<2, Arch> mask,
1671 T const * base, simd<std::int64_t, 2, Arch> indices) noexcept {
1672 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1673 }
1674
1675 template<int Scale, isa<x86> Arch, class T>
1676 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 2, Arch>); sizeof(simd<std::int64_t, 2, Arch>); })
1677 consteval void mask_vpscatterqq(T * base, predicate<2, Arch> mask,
1678 simd<std::int64_t, 2, Arch> indices, simd<T, 2, Arch> value) noexcept {
1679 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1680 }
1681
1682 template<int Scale, std::size_t N, isa<x86> Arch, class T>
1683 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && N == 4 && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1684 native_nodiscard consteval simd<T, 4, Arch> vpgatherqq(T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1685 return detail::x86_memory::gather<Scale>(base, indices, simd<T, 4, Arch>{}, ~std::uint64_t{0});
1686 }
1687
1688 template<int Scale, isa<x86> Arch, class T>
1689 requires(!(Arch.has(x86_feature::avx2)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1691 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1692 return detail::x86_memory::gather<Scale>(base, indices, source, mask);
1693 }
1694
1695 template<int Scale, isa<x86> Arch, class T>
1696 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1697 native_nodiscard consteval simd<T, 4, Arch> mask_vpgatherqq(simd<T, 4, Arch> source, predicate<4, Arch> mask,
1698 T const * base, simd<std::int64_t, 4, Arch> indices) noexcept {
1699 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1700 }
1701
1702 template<int Scale, isa<x86> Arch, class T>
1703 requires(!(Arch.has(x86_feature::avx512f) && Arch.has(x86_feature::avx512vl)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 4, Arch>); sizeof(simd<std::int64_t, 4, Arch>); })
1704 consteval void mask_vpscatterqq(T * base, predicate<4, Arch> mask,
1705 simd<std::int64_t, 4, Arch> indices, simd<T, 4, Arch> value) noexcept {
1706 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1707 }
1708
1709 template<int Scale, isa<x86> Arch, class T>
1710 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1712 T const * base, simd<std::int64_t, 8, Arch> indices) noexcept {
1713 return detail::x86_memory::gather<Scale>(base, indices, source, static_cast<std::uint64_t>(mask.to_bitset()));
1714 }
1715
1716 template<int Scale, isa<x86> Arch, class T>
1717 requires(!(Arch.has(x86_feature::avx512f)) && (Scale == 1 || Scale == 2 || Scale == 4 || Scale == 8) && (std::same_as<T, std::int64_t> || std::same_as<T, std::uint64_t>) && requires { sizeof(simd<T, 8, Arch>); sizeof(simd<std::int64_t, 8, Arch>); })
1718 consteval void mask_vpscatterqq(T * base, predicate<8, Arch> mask,
1719 simd<std::int64_t, 8, Arch> indices, simd<T, 8, Arch> value) noexcept {
1720 detail::x86_memory::scatter<Scale>(base, indices, value, static_cast<std::uint64_t>(mask.to_bitset()));
1721 }
1722
1724}
1725#endif
Compiler attributes for host code, with shader-safe shared modifiers.
#define native_inline
inline [[always_inline]]
Definition attributes.h:212
#define native_nodiscard
C++17 [[nodiscard]].
Definition attributes.h:189
#define native_target(x)
this indicates a required feature set for the current multiversioned function.
Definition attributes.h:476
typename mask_traits< std::remove_cvref_t< T > >::type mask
Definition mask_traits.h:22
constexpr simd< double, 2, Arch > vgatherqpd(double const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr simd< float, 4, Arch > mask_vgatherdps(simd< float, 4, Arch > source, simd< std::int32_t, 4, Arch > mask, float const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr simd< T, 2, Arch > vpgatherdq(T const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr void mask_vpscatterdq(T *base, predicate< 2, Arch > mask, simd< std::int32_t, 4, Arch > indices, simd< T, 2, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr simd< float, 4, Arch > vgatherqps(float const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr simd< float, 4, Arch > mask_vgatherqps(simd< float, 4, Arch > source, simd< std::int32_t, 4, Arch > mask, float const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr simd< T, 4, Arch > mask_vpgatherqd(simd< T, 4, Arch > source, simd< std::int32_t, 4, Arch > mask, T const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr void mask_vpscatterqq(T *base, predicate< 2, Arch > mask, simd< std::int64_t, 2, Arch > indices, simd< T, 2, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr simd< double, 2, Arch > vgatherdpd(double const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr void mask_vscatterdps(float *base, predicate< 4, Arch > mask, simd< std::int32_t, 4, Arch > indices, simd< float, 4, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr simd< float, 4, Arch > vgatherdps(float const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr simd< T, 4, Arch > vpgatherdd(T const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr simd< T, 2, Arch > mask_vpgatherqq(simd< T, 2, Arch > source, simd< std::int64_t, 2, Arch > mask, T const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr simd< double, 2, Arch > mask_vgatherdpd(simd< double, 2, Arch > source, simd< std::int64_t, 2, Arch > mask, double const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr simd< T, 4, Arch > mask_vpgatherdd(simd< T, 4, Arch > source, simd< std::int32_t, 4, Arch > mask, T const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr simd< T, 2, Arch > mask_vpgatherdq(simd< T, 2, Arch > source, simd< std::int64_t, 2, Arch > mask, T const *base, simd< std::int32_t, 4, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr simd< T, 2, Arch > vpgatherqq(T const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr simd< T, 4, Arch > vpgatherqd(T const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather all addressable lanes; N specifies the result lane count.
constexpr void mask_vpscatterqd(T *base, predicate< 4, Arch > mask, simd< std::int64_t, 2, Arch > indices, simd< T, 4, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr void mask_vscatterdpd(double *base, predicate< 2, Arch > mask, simd< std::int32_t, 4, Arch > indices, simd< double, 2, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr void mask_vpscatterdd(T *base, predicate< 4, Arch > mask, simd< std::int32_t, 4, Arch > indices, simd< T, 4, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr simd< double, 2, Arch > mask_vgatherqpd(simd< double, 2, Arch > source, simd< std::int64_t, 2, Arch > mask, double const *base, simd< std::int64_t, 2, Arch > indices) noexcept
Gather lanes whose full vector mask has its sign bit set; retain source otherwise.
constexpr void mask_vscatterqpd(double *base, predicate< 2, Arch > mask, simd< std::int64_t, 2, Arch > indices, simd< double, 2, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
constexpr void mask_vscatterqps(float *base, predicate< 4, Arch > mask, simd< std::int64_t, 2, Arch > indices, simd< float, 4, Arch > value) noexcept
Store predicate-selected lanes; inactive addresses are not accessed.
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
Standard-library adaptations documented here for SIMD value types.
Omitted architecture arguments use the native.simd provider's baseline.