SuperDex Physics C++ API
Loading...
Searching...
No Matches
x64_simd_int_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_AVX2
22
23namespace superdex {
24
25/***********************************************************************************************
26 Simd<int, 8>
27*/
28template <>
29class Simd<int, 8> {
30 public:
32 Simd(int a, int b, int c = 0, int d = 0, int e = 0, int f = 0, int g = 0, int h = 0)
33 : raw(_mm256_set_epi32(h, g, f, e, d, c, b, a)) {} // AVX
34
35 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
36 Simd(U a) : raw(_mm256_set1_epi32(a)) {} // AVX
37
38 // Joint two Vec4i into one Vec8i
39 Simd(Simd<int, 4> const& low, Simd<int, 4> const& high)
40 : raw(_mm256_set_m128i(high.raw, low.raw)) {} // AVX
41
42 template <int i>
43 [[nodiscard]] static MOCHI_FORCE_INLINE int Get(Simd v) {
44 static_assert(i >= 0 && i < 8, "Index out of range");
45 if constexpr (i == 0) {
46 return _mm256_cvtsi256_si32(v.raw); // AVX
47 } else if constexpr (i == 1) {
48 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(v.raw, 0x01)); // AVX, AVX2
49 } else if constexpr (i == 2) {
50 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(v.raw, 0x02)); // AVX, AVX2
51 } else if constexpr (i == 3) {
52 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(v.raw, 0x03)); // AVX, AVX2
53 } else if constexpr (i == 4) {
54 return _mm256_cvtsi256_si32(_mm256_permute2f128_si256(v.raw, v.raw, 0x01)); // AVX, AVX
55 } else if constexpr (i == 5) {
56 auto tmp = _mm256_permute2f128_si256(v.raw, v.raw, 0x01); // AVX
57 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(tmp, 0x01)); // AVX, AVX2
58 } else if constexpr (i == 6) {
59 auto tmp = _mm256_permute2f128_si256(v.raw, v.raw, 0x01); // AVX
60 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(tmp, 0x02)); // AVX, AVX2
61 } else if constexpr (i == 7) {
62 auto tmp = _mm256_permute2f128_si256(v.raw, v.raw, 0x01); // AVX
63 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(tmp, 0x03)); // AVX, AVX2
64 }
65 }
66
67 [[nodiscard]] static MOCHI_FORCE_INLINE int Get(Simd v, int i) {
68 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
69#if MOCHI_COMPILER_MSVC
70 return v.raw.m256i_i32[i];
71#else
72 switch (i) { // clang-format off
73 case 0: return Get<0>(v);
74 case 1: return Get<1>(v);
75 case 2: return Get<2>(v);
76 case 3: return Get<3>(v);
77 case 4: return Get<4>(v);
78 case 5: return Get<5>(v);
79 case 6: return Get<6>(v);
80 case 7: return Get<7>(v);
81 MOCHI_UNLIKELY default: return 0;
82 } // clang-format on
83#endif
84 }
85
86 template <int iHalf>
87 [[nodiscard]] static MOCHI_FORCE_INLINE Simd<int, 4> GetHalf(Simd a) {
88 static_assert(iHalf == 0 || iHalf == 1);
89 return _mm256_extracti128_si256(a.raw, iHalf); // AVX
90 }
91
92 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Set(Simd v, int i, Scalar value) {
93 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
94#if MOCHI_COMPILER_MSVC
95 auto result = v;
96 result.raw.m256i_i32[i] = value;
97 return result;
98#else
99 // clang-format off
100 static constexpr __m256i kMasks[] = {
101 {static_cast<long long>(0x00000000FFFFFFFFLL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL)},
102 {static_cast<long long>(0xFFFFFFFF00000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL)},
103 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x00000000FFFFFFFFLL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL)},
104 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0xFFFFFFFF00000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL)},
105 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x00000000FFFFFFFFLL), static_cast<long long>(0x0000000000000000LL)},
106 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0xFFFFFFFF00000000LL), static_cast<long long>(0x0000000000000000LL)},
107 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x00000000FFFFFFFFLL)},
108 {static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0x0000000000000000LL), static_cast<long long>(0xFFFFFFFF00000000LL)}};
109 // clang-format on
110 return _mm256_blendv_epi8(v.raw, _mm256_set1_epi32(value), kMasks[i]); // AVX2
111#endif
112 }
113
114 template <int i>
115 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Set(Simd v, Scalar value) {
116 static_assert(i >= 0 && i < kSize, "Index out of range");
117 return Set(v, i, value);
118 }
119
120 // Set via 4 int64_t instead of 8 int
121 [[nodiscard]] static MOCHI_FORCE_INLINE Simd
122 SetInt64(int64_t a, int64_t b, int64_t c, int64_t d) {
123 return _mm256_set_epi64x(d, c, b, a); // AVX
124 }
125
126 template <int N>
127 [[nodiscard]] static MOCHI_FORCE_INLINE bool AllTrue(Simd v) {
128 static_assert(N >= 1 && N <= kSize, "Unsupported N");
129 int mask = GetMSBitMask(v); // One bit for each byte in the vector
130 if constexpr (N == kSize) {
131 return mask == 0xFFFFFFFF;
132 } else {
133 int constexpr kNumBits = N * sizeof(Scalar);
134 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
135 return (mask & kMustBeSet) == kMustBeSet;
136 }
137 }
138
139 template <int N>
140 [[nodiscard]] static MOCHI_FORCE_INLINE bool AnyTrue(Simd v) {
141 static_assert(N >= 1 && N <= kSize, "Unsupported N");
142 int mask = GetMSBitMask(v); // One bit for each byte in the vector
143 if constexpr (N == kSize) {
144 return mask != 0;
145 } else {
146 int constexpr kNumBits = N * sizeof(Scalar);
147 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
148 return (mask & kMayBeSet) != 0;
149 }
150 }
151
152 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Broadcast(Scalar const* p) {
153 return Simd{*p};
154 }
155
156 template <int i>
157 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Broadcast(Simd v) {
158 return Simd{Get<i>(v)}; // TODO: There is probably a faster way
159 }
160
161 template <int N = 8>
162 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HMin(Simd a) {
163 static_assert(N >= 2 && N <= 8, "Unsupported N");
164 using HalfT = Simd<Scalar, 4>;
165 if constexpr (N >= 2 && N <= 4) {
166 return HalfT::HMin<N>(GetHalf<0>(a));
167 } else {
168 auto lo = GetHalf<0>(a);
169 auto hi = GetHalf<1>(a);
170 if constexpr (N != 8) {
171 // Set the hi values we don't want to compare to the max int
172 auto inf = HalfT{std::numeric_limits<Scalar>::max()};
173 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(inf, hi);
174 }
175 return HalfT::HMin<4>(HalfT::Min(lo, hi));
176 }
177 }
178
179 template <int N = 8>
180 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HMax(Simd a) {
181 static_assert(N >= 2 && N <= 8, "Unsupported N");
182 using HalfT = Simd<Scalar, 4>;
183 if constexpr (N >= 2 && N <= 4) {
184 return HalfT::HMax<N>(GetHalf<0>(a));
185 } else {
186 auto lo = GetHalf<0>(a);
187 auto hi = GetHalf<1>(a);
188 if constexpr (N != 8) {
189 // Set the hi values we don't want to compare to the most negative int
190 auto lowest = HalfT{std::numeric_limits<Scalar>::lowest()};
191 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(lowest, hi);
192 }
193 return HalfT::HMax<4>(HalfT::Max(lo, hi));
194 }
195 }
196
197 template <int N>
198 [[nodiscard]] static MOCHI_FORCE_INLINE Scalar HSum(Simd a) {
199 static_assert(N >= 2 && N <= 8, "Unsupported N");
200 using HalfT = Simd<Scalar, kSize / 2>;
201 if constexpr (N >= 2 && N <= 4) {
202 return HalfT::HSum<N>(GetHalf<0>(a));
203 } else if constexpr (N == 5) {
204 auto tmp = GetHalf<0>(a) + GetHalf<1>(a);
205 return HalfT::Get<0>(tmp) + Get<1>(a) + Get<2>(a) + Get<3>(a);
206 } else if constexpr (N == 6) {
207 auto tmp = GetHalf<0>(a) + GetHalf<1>(a);
208 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) + Get<2>(a) + Get<3>(a);
209 } else if constexpr (N == 7) {
210 auto tmp = GetHalf<0>(a) + GetHalf<1>(a);
211 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) + HalfT::Get<2>(tmp) + Get<3>(a);
212 } else if constexpr (N == 8) {
213 return HalfT::HSum<4>(GetHalf<0>(a) + GetHalf<1>(a));
214 }
215 }
216
217 template <int N = kSize>
218 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Load([[maybe_unused]] Scalar const* ptr) {
219 static_assert(N >= 0 && N <= kSize);
220 if constexpr (N == 0) {
221 return Simd::Zero();
222 } else if constexpr (N == 1) {
223 return Simd{*ptr, 0};
224 } else if constexpr (N == 2) {
225 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, 0, -1, -1); // AVX
226 return _mm256_maskload_epi32(ptr, mask); // AVX2
227 } else if constexpr (N == 3) {
228 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, -1, -1, -1); // AVX
229 return _mm256_maskload_epi32(ptr, mask); // AVX2
230 } else if constexpr (N == 4) {
231 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, -1, -1, -1, -1); // AVX
232 return _mm256_maskload_epi32(ptr, mask); // AVX2
233 } else if constexpr (N == 5) {
234 __m256i mask = _mm256_set_epi32(0, 0, 0, -1, -1, -1, -1, -1); // AVX
235 return _mm256_maskload_epi32(ptr, mask); // AVX2
236 } else if constexpr (N == 6) {
237 __m256i mask = _mm256_set_epi32(0, 0, -1, -1, -1, -1, -1, -1); // AVX
238 return _mm256_maskload_epi32(ptr, mask); // AVX2
239 } else if constexpr (N == 7) {
240 __m256i mask = _mm256_set_epi32(0, -1, -1, -1, -1, -1, -1, -1); // AVX
241 return _mm256_maskload_epi32(ptr, mask); // AVX2
242 } else {
243 return _mm256_loadu_si256(reinterpret_cast<__m256i const*>(ptr)); // AVX
244 }
245 }
246
247 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Load(Scalar const* ptr, int n) {
248 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
249 // Use the same masks as Simd<float, 8>
250 return _mm256_maskload_epi32(ptr, x64_simd::kLoadMasksS8[n]); // AVX2
251 }
252
253 template <int kTupleCount = kSize>
254 MOCHI_FORCE_INLINE static void
255 LoadTransposed(Scalar const* ptr, Simd& out0, Simd& out1, Simd& out2) {
256 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
257 constexpr int kCount0 = Clamp(kTupleCount * 3 - 0, 0, 8);
258 constexpr int kCount1 = Clamp(kTupleCount * 3 - 8, 0, 8);
259 constexpr int kCount2 = Clamp(kTupleCount * 3 - 16, 0, 8);
260
261 // [ 0, 1, 2, 3, 4, 5, 6, 7]
262 auto a = Simd::Load<kCount0>(ptr).raw;
263 // [ 8, 9, 10, 11, 12, 13, 14, 15]
264 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 8).raw;
265 // [16, 17, 18, 19, 20, 21, 22, 23]
266 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 16).raw;
267
268 auto d = _mm256_blend_epi32(a, b, 0b10010010); // [0,9,_,3, 12,_,6,15]
269 auto e = _mm256_blend_epi32(d, c, 0b00100100); // [0,9,18,3, 12,21,6,15]
270 auto f = _mm256_permute2x128_si256(e, e, 0x01); // [12,21,6,15, 0,9,18,3]
271 auto g = _mm256_blend_epi32(e, f, 0b01000100); // [0,9,6,3, 12,21,18,15]
272 out0.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(1, 2, 3, 0)); // [0,3,6,9, 12,15,18,21]
273
274 d = _mm256_blend_epi32(a, b, 0b00100100); // [_,1,10,_, 4,13,_,7]
275 e = _mm256_blend_epi32(d, c, 0b01001001); // [16,1,10,19 4,13,22,7]
276 f = _mm256_permute2x128_si256(e, e, 0x01); // [4,13,22,7 16,1,10,19]
277 g = _mm256_blend_epi32(e, f, 0b10011001); // [4,1,10,7, 16,13,22,19]
278 out1.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(2, 3, 0, 1)); // [1,4,7,10, 13,16,19,22]
279
280 d = _mm256_blend_epi32(a, b, 0b01001001); // [8,_,2,11, _,5,14,_]
281 e = _mm256_blend_epi32(d, c, 0b10010010); // [8,17,2,11, 20,5,14,23]
282 f = _mm256_permute2x128_si256(e, e, 0x01); // [20,5,14,23, 8,17,2,11]
283 g = _mm256_blend_epi32(e, f, 0b00100010); // [8,5,2,11, 20,17,14,23]
284 out2.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(3, 0, 1, 2)); // [2,5,8,11, 14,17,20,23]
285 }
286
287 template <int N = kSize>
288 static MOCHI_FORCE_INLINE void Store(Scalar* ptr, Simd v) {
289 static_assert(N >= 0 && N <= kSize);
290 if constexpr (N == 0) {
291 } else if constexpr (N < kSize) {
292 // About 3X faster than a masked store on AMD. About the same on Intel.
293 memcpy(ptr, &v, sizeof(Scalar) * N);
294 } else {
295 return _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), v.raw); // AVX
296 }
297 }
298
299 static MOCHI_FORCE_INLINE void Store(Scalar* ptr, Simd v, int n) {
300 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
301 // Faster than masked store on AMD.
302 // clang-format off
303 switch (n) {
304 case 1: Store<1>(ptr, v); break;
305 case 2: Store<2>(ptr, v); break;
306 case 3: Store<3>(ptr, v); break;
307 case 4: Store<4>(ptr, v); break;
308 case 5: Store<5>(ptr, v); break;
309 case 6: Store<6>(ptr, v); break;
310 case 7: Store<7>(ptr, v); break;
311 case 8: Store<8>(ptr, v); break;
312 MOCHI_UNLIKELY default: break;
313 } // clang-format on
314 }
315
316 MOCHI_FORCE_INLINE static int StoreSelected(Scalar* ptr, Simd condition, Simd values) {
317 auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(condition.raw));
318 // Load 8 bytes from the table, then zero-exend to get the shuffle pattern.
319 auto const* tableRow =
320 reinterpret_cast<__m128i const*>(x64_simd::kStoreSelectedShuffleTableS8[mask]);
321 auto pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
322 auto packed = _mm256_permutevar8x32_ps(_mm256_castsi256_ps(values.raw), pattern);
323 _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), _mm256_castps_si256(packed));
324 return _mm_popcnt_u32(mask);
325 }
326
327 template <int kTupleCount = kSize>
328 MOCHI_FORCE_INLINE static void StoreTransposed(Scalar* ptr, Simd a, Simd b, Simd c) {
329 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
330 // a = [0,3,6,9, 12,15,18,21]
331 // b = [1,4,7,10, 13,16,19,22]
332 // c = [2,5,8,11, 14,17,20,23]
333 auto d = _mm256_shuffle_epi32(a.raw, _MM_SHUFFLE(1, 2, 3, 0)); // [0,9,6,3, 12,21,18,15]
334 auto e = _mm256_shuffle_epi32(b.raw, _MM_SHUFFLE(2, 3, 0, 1)); // [4,1,10,7, 16,13,22,19]
335 auto f = _mm256_shuffle_epi32(c.raw, _MM_SHUFFLE(3, 0, 1, 2)); // [8,5,2,11, 20,17,14,23]
336 __m256i g = _mm256_blend_epi32(d, e, 0b00100010); // [0,1,_,3, 12,13,_,15]
337 g = _mm256_blend_epi32(g, f, 0b01000100); // [0,1,2,3, 12,13,14,15]
338 __m256i h = _mm256_blend_epi32(d, e, 0b10011001); // [4,_,6,7, 16,_,18,19]
339 h = _mm256_blend_epi32(h, f, 0b00100010); // [4,5,6,7, 16,17,18,19]
340 h = _mm256_permute2x128_si256(h, h, 0x01); // [16,17,18,19, 4,5,6,7]
341 __m256i i = _mm256_blend_epi32(d, e, 0b01000100); // [_,9,10,_, _,21,22,_]
342 i = _mm256_blend_epi32(i, f, 0b10011001); // [8,9,10,11, 20,21,22,23]
343 d = _mm256_blend_epi32(g, h, 0b11110000); // [0,1,2,3, 4,5,6,7]
344 e = _mm256_blend_epi32(i, g, 0b11110000); // [8,9,10,11, 12,13,14,15]
345 f = _mm256_blend_epi32(h, i, 0b11110000); // [16,17,18,19, 20,21,22,23]
346 constexpr int kCount0 = Clamp(kTupleCount * 3 - 0, 0, 8);
347 constexpr int kCount1 = Clamp(kTupleCount * 3 - 8, 0, 8);
348 constexpr int kCount2 = Clamp(kTupleCount * 3 - 16, 0, 8);
349 Simd::Store<kCount0>(ptr, d);
350 if constexpr (kCount1 > 0) {
351 Simd::Store<kCount1>(ptr + 8, e);
352 }
353 if constexpr (kCount2 > 0) {
354 Simd::Store<kCount2>(ptr + 16, f);
355 }
356 }
357
358 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Min(Simd a, Simd b) {
359 return _mm256_min_epi32(a.raw, b.raw); // AVX2
360 }
361
362 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Max(Simd a, Simd b) {
363 return _mm256_max_epi32(a.raw, b.raw); // AVX2
364 }
365
366 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Select(Simd mask, Simd a, Simd b) {
367 return _mm256_blendv_epi8(b.raw, a.raw, mask.raw); // AVX2
368 }
369
370 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Zero() {
371 return _mm256_setzero_si256(); // AVX
372 }
373
374 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<(Simd rhs) const {
375 return _mm256_cmpgt_epi32(rhs.raw, this->raw); // AVX2
376 }
377
378 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>(Simd rhs) const {
379 return _mm256_cmpgt_epi32(this->raw, rhs.raw); // AVX2
380 }
381
382 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<=(Simd rhs) const {
383 return ~(*this > rhs);
384 }
385
386 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>=(Simd rhs) const {
387 return ~(*this < rhs);
388 }
389
390 [[nodiscard]] static MOCHI_FORCE_INLINE Simd Equal(Simd a, Simd b) {
391 return _mm256_cmpeq_epi32(a.raw, b.raw); // SSE2
392 }
393
394 [[nodiscard]] static MOCHI_FORCE_INLINE Simd NotEqual(Simd a, Simd b) {
395 return ~Equal(a, b); // No native support until AVX512
396 }
397
398 [[nodiscard]] MOCHI_FORCE_INLINE bool operator==(Simd rhs) const {
399 auto mask = GetMSBitMask(Equal(raw, rhs.raw));
400 return mask == 0xFFFFFFFF; // All values equal
401 }
402
403 [[nodiscard]] MOCHI_FORCE_INLINE bool operator!=(Simd rhs) const {
404 auto mask = GetMSBitMask(NotEqual(raw, rhs.raw));
405 return mask != 0; // Any values not equal
406 }
407
408 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator~() const {
409 auto ones = _mm256_set1_epi32(-1); // AVX
410 return _mm256_xor_si256(raw, ones); // AVX2
411 }
412
413 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-() const {
414 return Zero() - *this;
415 }
416
417 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator+(Simd rhs) const {
418 return _mm256_add_epi32(raw, rhs.raw); // AVX2
419 }
420
421 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-(Simd rhs) const {
422 return _mm256_sub_epi32(raw, rhs.raw); // AVX2
423 }
424
425 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator*(Simd rhs) const {
426 return _mm256_mullo_epi32(raw, rhs.raw); // AVX2
427 }
428
429 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator/(Simd rhs) const {
430#if MOCHI_ARCH_X64_SVML
431 return _mm256_div_epi32(raw, rhs.raw); // AVX2
432#else
433 // Fallback
434 return Simd{
435 Get<0>(*this) / Get<0>(rhs),
436 Get<1>(*this) / Get<1>(rhs),
437 Get<2>(*this) / Get<2>(rhs),
438 Get<3>(*this) / Get<3>(rhs),
439 Get<4>(*this) / Get<4>(rhs),
440 Get<5>(*this) / Get<5>(rhs),
441 Get<6>(*this) / Get<6>(rhs),
442 Get<7>(*this) / Get<7>(rhs)};
443#endif
444 }
445
446 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator&(Simd rhs) const {
447 return _mm256_and_si256(raw, rhs.raw); // AVX2
448 }
449
450 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator|(Simd rhs) const {
451 return _mm256_or_si256(raw, rhs.raw); // AVX2
452 }
453
454 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator^(Simd rhs) const {
455 return _mm256_xor_si256(raw, rhs.raw); // AVX2
456 }
457
458 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<<(int rhs) const {
459 return _mm256_slli_epi32(raw, rhs); // SSE2
460 }
461
462 template <int kShift>
463 [[nodiscard]] MOCHI_FORCE_INLINE static Simd ShiftRight(Simd a) {
464 return _mm256_srli_epi32(a.raw, kShift); // AVX2
465 }
466
467 private:
468 // Integer mask with the most significant bit of each byte in the vector
469 [[nodiscard]] static MOCHI_FORCE_INLINE int GetMSBitMask(Simd a) {
470 return _mm256_movemask_epi8(a.raw); // AVX
471 }
472};
473
474} // namespace superdex
475
476#endif // MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
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
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_UNLIKELY
#define MOCHI_FORCE_INLINE
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:379
T HMin(Simd< T, N > a)
Definition simd_inl.h:391
bool AllTrue(T const &a)
Definition basic_utils.h:60
Simd< T, N > Set(Simd< T, N > a, T value)
Definition simd_inl.h:315
constexpr auto NotEqual(T const &a, T const &b)
T HMax(Simd< T, N > a)
Definition simd_inl.h:397
V Broadcast(typename V::Scalar a)
Definition simd_inl.h:115
bool AnyTrue(T const &a)
Definition basic_utils.h:66
constexpr T Select(bool condition, T a, T b)
T Get(Simd< T, N > v)
Definition simd_inl.h:300
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
Simd< T, N/2 > GetHalf(Simd< T, N > a)
Definition simd_inl.h:310
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)