SuperDex Physics C++ API
Loading...
Searching...
No Matches
x64_simd_int64_8_inl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include "x64_simd_inl.h" // for IntelliSense
20
21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX512
22
23namespace superdex {
24
25/***********************************************************************************************
26 Simd<int64_t, 8>
27*/
28template <>
29class Simd<int64_t, 8> {
30 public:
31 static_assert(sizeof(int64_t) == sizeof(long long));
32
33 MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(int64_t, 8, __m512i);
34
35 Simd(
36 int64_t a,
37 int64_t b,
38 int64_t c = 0,
39 int64_t d = 0,
40 int64_t e = 0,
41 int64_t f = 0,
42 int64_t g = 0,
43 int64_t h = 0)
44 : raw(_mm512_set_epi64(h, g, f, e, d, c, b, a)) {}
45
46 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
47 Simd(U a) : raw(_mm512_set1_epi64(static_cast<long long>(a))) {}
48
49 Simd(Simd<int64_t, 4> const& low, Simd<int64_t, 4> const& high)
50 : raw(_mm512_inserti64x4(_mm512_castsi256_si512(low.raw), high.raw, 1)) {}
51
52 template <int i>
53 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar Get(Simd v) {
54 static_assert(i >= 0 && i < kSize, "Index out of range");
55 return v[i];
56 }
57
58 [[nodiscard]] MOCHI_FORCE_INLINE Scalar operator[](int i) const {
59 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
60#if MOCHI_COMPILER_MSVC
61 return raw.m512i_i64[i];
62#else
63 return static_cast<Scalar>(raw[i]);
64#endif
65 }
66
67 template <int iHalf>
68 [[nodiscard]] static MOCHI_FORCE_INLINE Simd<int64_t, 4> GetHalf(Simd a) {
69 static_assert(iHalf == 0 || iHalf == 1);
70 if constexpr (iHalf == 0) {
71 return _mm512_castsi512_si256(a.raw);
72 } else {
73 return _mm512_extracti64x4_epi64(a.raw, 1);
74 }
75 }
76
77 template <int N>
78 [[nodiscard]] static MOCHI_FORCE_INLINE bool AllTrue(Simd v) {
79 static_assert(N >= 1 && N <= kSize, "Unsupported N");
80 auto const mask = ToMask(v);
81 if constexpr (N == kSize) {
82 return _kortestc_mask8_u8(mask, mask) != 0;
83 } else {
84 constexpr auto kLanes = LaneMask<N>();
85 return (mask & kLanes) == kLanes;
86 }
87 }
88
89 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Broadcast(Scalar const* p) {
90 return Simd{*p};
91 }
92
93 template <int i>
94 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Broadcast(Simd v) {
95 static_assert(i >= 0 && i < kSize, "Index out of range");
96 if constexpr (i == 0) {
97 return _mm512_broadcastq_epi64(_mm512_castsi512_si128(v.raw));
98 } else {
99 constexpr int kLane = i % 2;
100 constexpr int kGroup = i / 2;
101 auto const group =
102 _mm512_shuffle_i64x2(v.raw, v.raw, _MM_SHUFFLE(kGroup, kGroup, kGroup, kGroup));
103 return _mm512_shuffle_epi32(
104 group,
105 static_cast<_MM_PERM_ENUM>(
106 kLane == 0 ? _MM_SHUFFLE(1, 0, 1, 0) : _MM_SHUFFLE(3, 2, 3, 2)));
107 }
108 }
109
110 template <int N = kSize>
111 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HMin(Simd a) {
112 static_assert(N >= 2 && N <= kSize, "Unsupported N");
113 using HalfT = Simd<Scalar, 4>;
114 auto const lo = GetHalf<0>(a);
115 if constexpr (N <= 4) {
116 return HalfT::template HMin<N>(lo);
117 } else {
118 auto const hi = GetHalf<1>(a);
119 if constexpr (N == 5) {
120 return superdex::Min(HalfT::template HMin<4>(lo), HalfT::template Get<0>(hi));
121 } else if constexpr (N == kSize) {
122 return HalfT::template HMin<4>(HalfT::Min(lo, hi));
123 } else {
124 return _mm512_mask_reduce_min_epi64(LaneMask<N>(), a.raw);
125 }
126 }
127 }
128
129 template <int N = kSize>
130 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HMax(Simd a) {
131 static_assert(N >= 2 && N <= kSize, "Unsupported N");
132 using HalfT = Simd<Scalar, 4>;
133 auto const lo = GetHalf<0>(a);
134 if constexpr (N <= 4) {
135 return HalfT::template HMax<N>(lo);
136 } else {
137 auto const hi = GetHalf<1>(a);
138 if constexpr (N == 5) {
139 return superdex::Max(HalfT::template HMax<4>(lo), HalfT::template Get<0>(hi));
140 } else if constexpr (N == kSize) {
141 return HalfT::template HMax<4>(HalfT::Max(lo, hi));
142 } else {
143 return _mm512_mask_reduce_max_epi64(LaneMask<N>(), a.raw);
144 }
145 }
146 }
147
148 template <int N>
149 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HSum(Simd a) {
150 static_assert(N >= 2 && N <= kSize, "Unsupported N");
151 using HalfT = Simd<Scalar, 4>;
152 auto const lo = GetHalf<0>(a);
153 if constexpr (N <= 4) {
154 return HalfT::template HSum<N>(lo);
155 } else {
156 auto const hi = GetHalf<1>(a);
157 if constexpr (N == 5) {
158 return HalfT::template HSum<4>(lo) + HalfT::template Get<0>(hi);
159 } else if constexpr (N == kSize) {
160 return HalfT::template HSum<4>(lo + hi);
161 } else {
162 return _mm512_mask_reduce_add_epi64(LaneMask<N>(), a.raw);
163 }
164 }
165 }
166
167 template <int N = kSize>
168 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Load([[maybe_unused]] Scalar const* ptr) {
169 static_assert(N >= 0 && N <= kSize);
170 if constexpr (N == 0) {
171 return Zero();
172 } else if constexpr (N == 1) {
173 return _mm512_zextsi128_si512(_mm_loadl_epi64(reinterpret_cast<__m128i const*>(ptr)));
174 } else if constexpr (N == 2) {
175 return _mm512_zextsi128_si512(_mm_loadu_si128(reinterpret_cast<__m128i const*>(ptr)));
176 } else if constexpr (N < 4) {
177 return _mm512_zextsi256_si512(
178 _mm256_maskz_loadu_epi64(static_cast<__mmask8>((uint32_t{1} << N) - 1), ptr));
179 } else if constexpr (N == 4) {
180 return _mm512_zextsi256_si512(_mm256_loadu_si256(reinterpret_cast<__m256i const*>(ptr)));
181 } else if constexpr (N < kSize) {
182 return _mm512_maskz_loadu_epi64(LaneMask<N>(), ptr);
183 } else {
184 return _mm512_loadu_si512(ptr);
185 }
186 }
187
188 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Load(Scalar const* ptr, int n) {
189 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
190 auto const mask = static_cast<__mmask8>((uint32_t{1} << n) - 1);
191 return _mm512_maskz_loadu_epi64(mask, ptr);
192 }
193
194 template <int kTupleCount = kSize>
195 MOCHI_FORCE_INLINE static void
196 LoadTransposed(Scalar const* ptr, Simd& out0, Simd& out1, Simd& out2) {
197 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
198 if constexpr (kTupleCount == 1) {
199 out0 = Load<1>(ptr);
200 out1 = Load<1>(ptr + 1);
201 out2 = Load<1>(ptr + 2);
202 return;
203 }
204 constexpr int kTotalCount = kTupleCount * 3;
205 auto const x0 = Load<Clamp(kTotalCount, 0, kSize)>(ptr).raw;
206 if constexpr (kTupleCount <= 5) {
207 auto const index0 = LoadTransposeIndices<kTupleCount, 0>();
208 auto const index1 = LoadTransposeIndices<kTupleCount, 1>();
209 auto const index2 = LoadTransposeIndices<kTupleCount, 2>();
210 if constexpr (kTupleCount <= 2) {
211 out0.raw = _mm512_permutexvar_epi64(index0, x0);
212 out1.raw = _mm512_permutexvar_epi64(index1, x0);
213 out2.raw = _mm512_permutexvar_epi64(index2, x0);
214 } else {
215 auto const x1 = Load<kTotalCount - kSize>(ptr + kSize).raw;
216 out0.raw = _mm512_permutex2var_epi64(x0, index0, x1);
217 out1.raw = _mm512_permutex2var_epi64(x0, index1, x1);
218 out2.raw = _mm512_permutex2var_epi64(x0, index2, x1);
219 }
220 } else {
221 auto const x1 = Load<kSize>(ptr + kSize).raw;
222 constexpr int kCount2 = kTotalCount - 2 * kSize;
223 auto const x2 = Load<kCount2>(ptr + 2 * kSize).raw;
224 auto const index0 = _mm512_setr_epi64(0, 3, 6, 9, 12, 15, 0, 0);
225 auto const index1 = _mm512_setr_epi64(1, 4, 7, 10, 13, 0, 0, 0);
226 auto const index2 = _mm512_setr_epi64(2, 5, 8, 11, 14, 0, 0, 0);
227 if constexpr (kTupleCount == 6) {
228 out0.raw = _mm512_maskz_permutex2var_epi64(LaneMask<kTupleCount>(), x0, index0, x1);
229 constexpr int kZeroIndex = kSize + kCount2;
230 auto const partial1 = _mm512_permutex2var_epi64(x0, index1, x1);
231 auto const finalIndex1 = _mm512_setr_epi64(0, 1, 2, 3, 4, 8, kZeroIndex, kZeroIndex);
232 out1.raw = _mm512_permutex2var_epi64(partial1, finalIndex1, x2);
233 auto const partial2 = _mm512_permutex2var_epi64(x0, index2, x1);
234 auto const finalIndex2 = _mm512_setr_epi64(0, 1, 2, 3, 4, 9, kZeroIndex, kZeroIndex);
235 out2.raw = _mm512_permutex2var_epi64(partial2, finalIndex2, x2);
236 } else {
237 constexpr int kZeroIndex = kSize + kCount2;
238 auto const partial0 = _mm512_permutex2var_epi64(x0, index0, x1);
239 auto const finalIndex0 = _mm512_setr_epi64(
240 0, 1, 2, 3, 4, 5, kTupleCount > 6 ? 10 : kZeroIndex, kTupleCount > 7 ? 13 : kZeroIndex);
241 out0.raw = _mm512_permutex2var_epi64(partial0, finalIndex0, x2);
242 auto const partial1 = _mm512_permutex2var_epi64(x0, index1, x1);
243 auto const finalIndex1 = _mm512_setr_epi64(
244 0, 1, 2, 3, 4, 8, kTupleCount > 6 ? 11 : kZeroIndex, kTupleCount > 7 ? 14 : kZeroIndex);
245 out1.raw = _mm512_permutex2var_epi64(partial1, finalIndex1, x2);
246 auto const partial2 = _mm512_permutex2var_epi64(x0, index2, x1);
247 auto const finalIndex2 = _mm512_setr_epi64(
248 0, 1, 2, 3, 4, 9, kTupleCount > 6 ? 12 : kZeroIndex, kTupleCount > 7 ? 15 : kZeroIndex);
249 out2.raw = _mm512_permutex2var_epi64(partial2, finalIndex2, x2);
250 }
251 }
252 }
253
254 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Min(Simd a, Simd b) {
255 return _mm512_min_epi64(a.raw, b.raw);
256 }
257
258 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Max(Simd a, Simd b) {
259 return _mm512_max_epi64(a.raw, b.raw);
260 }
261
262 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Select(Simd mask, Simd a, Simd b) {
263 return _mm512_mask_blend_epi64(ToMask(mask), b.raw, a.raw);
264 }
265
266 template <int x0, int x1, int x2, int x3, int x4, int x5, int x6, int x7>
267 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Shuffle(Simd a) {
268 static_assert(
269 x0 >= 0 && x0 < kSize && x1 >= 0 && x1 < kSize && x2 >= 0 && x2 < kSize && x3 >= 0 &&
270 x3 < kSize && x4 >= 0 && x4 < kSize && x5 >= 0 && x5 < kSize && x6 >= 0 && x6 < kSize &&
271 x7 >= 0 && x7 < kSize,
272 "Invalid index");
273 if constexpr (
274 x0 == 0 && x1 == 1 && x2 == 2 && x3 == 3 && x4 == 4 && x5 == 5 && x6 == 6 && x7 == 7) {
275 return a;
276 } else {
277 auto const indices = _mm512_setr_epi64(x0, x1, x2, x3, x4, x5, x6, x7);
278 return _mm512_permutexvar_epi64(indices, a.raw);
279 }
280 }
281
282 template <int x0, int x1, int x2, int x3, int x4, int x5, int x6, int x7>
283 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Shuffle(Simd a, Simd b) {
284 static_assert(
285 x0 >= 0 && x0 < kSize && x1 >= 0 && x1 < kSize && x2 >= 0 && x2 < kSize && x3 >= 0 &&
286 x3 < kSize && x4 >= 0 && x4 < kSize && x5 >= 0 && x5 < kSize && x6 >= 0 && x6 < kSize &&
287 x7 >= 0 && x7 < kSize,
288 "Invalid index");
289 if constexpr (
290 x0 == 0 && x1 == 1 && x2 == 2 && x3 == 3 && x4 == 0 && x5 == 1 && x6 == 2 && x7 == 3) {
291 return _mm512_inserti64x4(_mm512_castsi256_si512(GetHalf<0>(a).raw), GetHalf<0>(b).raw, 1);
292 } else {
293 auto const indices =
294 _mm512_setr_epi64(x0, x1, x2, x3, x4 + kSize, x5 + kSize, x6 + kSize, x7 + kSize);
295 return _mm512_permutex2var_epi64(a.raw, indices, b.raw);
296 }
297 }
298
299 template <int N = kSize>
300 static MOCHI_FORCE_INLINE void Store([[maybe_unused]] Scalar* ptr, [[maybe_unused]] Simd v) {
301 static_assert(N >= 0 && N <= kSize);
302 if constexpr (N == 0) {
303 } else if constexpr (N == 1) {
304 _mm_storel_epi64(reinterpret_cast<__m128i*>(ptr), _mm512_castsi512_si128(v.raw));
305 } else if constexpr (N == 2) {
306 _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), _mm512_castsi512_si128(v.raw));
307 } else if constexpr (N < 4) {
308 _mm256_mask_storeu_epi64(
309 ptr, static_cast<__mmask8>((uint32_t{1} << N) - 1), _mm512_castsi512_si256(v.raw));
310 } else if constexpr (N == 4) {
311 _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), _mm512_castsi512_si256(v.raw));
312 } else if constexpr (N < kSize) {
313 _mm512_mask_storeu_epi64(ptr, LaneMask<N>(), v.raw);
314 } else {
315 _mm512_storeu_si512(ptr, v.raw);
316 }
317 }
318
319 static MOCHI_FORCE_INLINE void Store(Scalar* ptr, Simd v, int n) {
320 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
321 auto const mask = static_cast<__mmask8>((uint32_t{1} << n) - 1);
322 _mm512_mask_storeu_epi64(ptr, mask, v.raw);
323 }
324
325 MOCHI_FORCE_INLINE static int StoreSelected(Scalar* ptr, Simd condition, Simd values) {
326 auto const mask = ToMask(condition);
327 _mm512_mask_compressstoreu_epi64(ptr, mask, values.raw);
328 return _mm_popcnt_u32(mask);
329 }
330
331 template <int kTupleCount = kSize>
332 MOCHI_FORCE_INLINE static void StoreTransposed(Scalar* ptr, Simd a, Simd b, Simd c) {
333 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
334 if constexpr (kTupleCount == 1) {
335 Store<1>(ptr, a);
336 Store<1>(ptr + 1, b);
337 Store<1>(ptr + 2, c);
338 return;
339 }
340 constexpr int kTotalCount = kTupleCount * 3;
341 auto const ab0 =
342 _mm512_permutex2var_epi64(a.raw, _mm512_setr_epi64(0, 8, 0, 1, 9, 0, 2, 10), b.raw);
343 auto const x0 =
344 _mm512_permutex2var_epi64(ab0, _mm512_setr_epi64(0, 1, 8, 3, 4, 9, 6, 7), c.raw);
346 if constexpr (kTotalCount > kSize) {
347 auto const ab1 =
348 _mm512_permutex2var_epi64(a.raw, _mm512_setr_epi64(0, 3, 11, 0, 4, 12, 0, 5), b.raw);
349 auto const x1 =
350 _mm512_permutex2var_epi64(ab1, _mm512_setr_epi64(10, 1, 2, 11, 4, 5, 12, 7), c.raw);
351 Store<Clamp(kTotalCount - kSize, 0, kSize)>(ptr + kSize, x1);
352 }
353 if constexpr (kTotalCount > 2 * kSize) {
354 auto const ab2 =
355 _mm512_permutex2var_epi64(a.raw, _mm512_setr_epi64(13, 0, 6, 14, 0, 7, 15, 0), b.raw);
356 auto const x2 =
357 _mm512_permutex2var_epi64(ab2, _mm512_setr_epi64(0, 13, 2, 3, 14, 5, 6, 15), c.raw);
358 Store<kTotalCount - 2 * kSize>(ptr + 2 * kSize, x2);
359 }
360 }
361
362 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Zero() {
363 return _mm512_setzero_si512();
364 }
365
366 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<(Simd rhs) const {
367 return FromMask(_mm512_cmp_epi64_mask(raw, rhs.raw, _MM_CMPINT_LT));
368 }
369
370 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>(Simd rhs) const {
371 return FromMask(_mm512_cmp_epi64_mask(raw, rhs.raw, _MM_CMPINT_GT));
372 }
373
374 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<=(Simd rhs) const {
375 return FromMask(_mm512_cmp_epi64_mask(raw, rhs.raw, _MM_CMPINT_LE));
376 }
377
378 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>=(Simd rhs) const {
379 return FromMask(_mm512_cmp_epi64_mask(raw, rhs.raw, _MM_CMPINT_GE));
380 }
381
382 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Equal(Simd a, Simd b) {
383 return FromMask(_mm512_cmp_epi64_mask(a.raw, b.raw, _MM_CMPINT_EQ));
384 }
385
386 [[nodiscard]] static MOCHI_FORCE_INLINE Simd NotEqual(Simd a, Simd b) {
387 return FromMask(_mm512_cmp_epi64_mask(a.raw, b.raw, _MM_CMPINT_NE));
388 }
389
390 [[nodiscard]] MOCHI_FORCE_INLINE bool operator==(Simd rhs) const {
391 return _mm512_cmp_epi64_mask(raw, rhs.raw, _MM_CMPINT_EQ) == __mmask8{0xFF};
392 }
393
394 [[nodiscard]] MOCHI_FORCE_INLINE bool operator!=(Simd rhs) const {
395 return _mm512_cmp_epi64_mask(raw, rhs.raw, _MM_CMPINT_NE) != 0;
396 }
397
398 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator~() const {
399 return _mm512_xor_si512(raw, _mm512_set1_epi64(-1));
400 }
401
402 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-() const {
403 return _mm512_sub_epi64(_mm512_setzero_si512(), raw);
404 }
405
406 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator+(Simd rhs) const {
407 return _mm512_add_epi64(raw, rhs.raw);
408 }
409
410 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-(Simd rhs) const {
411 return _mm512_sub_epi64(raw, rhs.raw);
412 }
413
414 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator*(Simd rhs) const {
415 return _mm512_mullo_epi64(raw, rhs.raw);
416 }
417
418 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator/(Simd rhs) const {
419#if MOCHI_ARCH_X64_SVML
420 return _mm512_div_epi64(raw, rhs.raw);
421#else
422 return Simd{
423 Get<0>(*this) / Get<0>(rhs),
424 Get<1>(*this) / Get<1>(rhs),
425 Get<2>(*this) / Get<2>(rhs),
426 Get<3>(*this) / Get<3>(rhs),
427 Get<4>(*this) / Get<4>(rhs),
428 Get<5>(*this) / Get<5>(rhs),
429 Get<6>(*this) / Get<6>(rhs),
430 Get<7>(*this) / Get<7>(rhs)};
431#endif
432 }
433
434 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator&(Simd rhs) const {
435 return _mm512_and_si512(raw, rhs.raw);
436 }
437
438 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator|(Simd rhs) const {
439 return _mm512_or_si512(raw, rhs.raw);
440 }
441
442 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator^(Simd rhs) const {
443 return _mm512_xor_si512(raw, rhs.raw);
444 }
445
446 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<<(int rhs) const {
447 return _mm512_sll_epi64(raw, _mm_cvtsi32_si128(rhs));
448 }
449
450 template <int kShift>
451 [[nodiscard]] MOCHI_FORCE_INLINE static Simd ShiftRight(Simd a) {
452 static_assert(kShift >= 0 && kShift < 64, "Shift amount out-of-range");
453 if constexpr (kShift == 0) {
454 return a;
455 } else {
456 return _mm512_srai_epi64(a.raw, kShift);
457 }
458 }
459
460 private:
461 template <int kTupleCount, int kComponent>
462 [[nodiscard]] static MOCHI_FORCE_INLINE __m512i LoadTransposeIndices() {
463 constexpr int kZeroIndex = kTupleCount * 3;
464 return _mm512_setr_epi64(
465 kComponent,
466 kTupleCount > 1 ? 3 + kComponent : kZeroIndex,
467 kTupleCount > 2 ? 6 + kComponent : kZeroIndex,
468 kTupleCount > 3 ? 9 + kComponent : kZeroIndex,
469 kTupleCount > 4 ? 12 + kComponent : kZeroIndex,
470 kTupleCount > 5 ? 15 + kComponent : kZeroIndex,
471 kTupleCount > 6 ? 18 + kComponent : kZeroIndex,
472 kTupleCount > 7 ? 21 + kComponent : kZeroIndex);
473 }
474
475 // Returns a mask selecting the lowest N lanes.
476 template <int N>
477 [[nodiscard]] static constexpr __mmask8 LaneMask() {
478 static_assert(N >= 0 && N <= kSize);
479 if constexpr (N == kSize) {
480 return __mmask8{0xFF};
481 } else {
482 return static_cast<__mmask8>((uint32_t{1} << N) - 1);
483 }
484 }
485
486 // Converts a canonical logical vector (all-zero or all-one lanes) to a mask.
487 [[nodiscard]] static MOCHI_FORCE_INLINE __mmask8 ToMask(Simd a) {
488 auto const mask = _mm512_movepi64_mask(a.raw);
490 _mm512_cmpeq_epi64_mask(a.raw, _mm512_movm_epi64(mask)) == LaneMask<kSize>(),
491 "Expected a canonical logical mask");
492 return mask;
493 }
494
495 // Expands a mask into a canonical logical vector (all-zero or all-one lanes).
496 [[nodiscard]] static MOCHI_FORCE_INLINE Simd FromMask(__mmask8 mask) {
497 return _mm512_movm_epi64(mask);
498 }
499};
500
501} // namespace superdex
502
503#endif // MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX512
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
NativeType raw
Definition simd.h:174
Simd operator>(Simd rhs) const
Simd operator<<(int shift) const
Simd operator*(Simd rhs) const
Simd operator^(Simd rhs) const
Simd operator-() const
Simd operator>=(Simd rhs) const
Simd operator<(Simd rhs) const
bool operator!=(Simd rhs) const
Simd operator|(Simd rhs) const
static constexpr int kSize
Definition simd.h:96
Simd operator+(Simd rhs) const
Simd operator~() const
Simd operator/(Simd rhs) const
Simd operator<=(Simd rhs) const
Scalar operator[](int i) const
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_FORCE_INLINE
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
Definition simd_inl.h:273
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
T HSum(Simd< T, N > a)
Definition simd_inl.h:377
T HMin(Simd< T, N > a)
Definition simd_inl.h:389
bool AllTrue(T const &a)
Definition basic_utils.h:60
constexpr auto NotEqual(T const &a, T const &b)
T HMax(Simd< T, N > a)
Definition simd_inl.h:395
V Broadcast(typename V::Scalar a)
Definition simd_inl.h:115
constexpr T Select(bool condition, T a, T b)
T Get(Simd< T, N > v)
Definition simd_inl.h:303
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
Simd< T, N/2 > GetHalf(Simd< T, N > a)
Definition simd_inl.h:308
constexpr T const & Max(T const &a, T const &b)
void LoadTransposed(T const *ptr, Simd< T, N > &out0, Simd< T, N > &out1, Simd< T, N > &out2)
Definition simd_inl.h:207
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
Definition simd_inl.h:245
void Store(T *ptr, Simd< T, N > a)
Definition simd_inl.h:213
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
Definition simd_inl.h:225
V Load(typename V::Scalar const *ptr)
Definition simd_inl.h:184
Simd< T, N > ShiftRight(Simd< T, N > a)
Definition simd_inl.h:260
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)