21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
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)) {}
35 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
36 Simd(U a) :
raw(_mm256_set1_epi32(a)) {}
40 :
raw(_mm256_set_m128i(high.
raw, low.
raw)) {}
44 static_assert(i >= 0 && i < 8,
"Index out of range");
45 if constexpr (i == 0) {
46 return _mm256_cvtsi256_si32(v.raw);
47 }
else if constexpr (i == 1) {
48 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(v.raw, 0x01));
49 }
else if constexpr (i == 2) {
50 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(v.raw, 0x02));
51 }
else if constexpr (i == 3) {
52 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(v.raw, 0x03));
53 }
else if constexpr (i == 4) {
54 return _mm256_cvtsi256_si32(_mm256_permute2f128_si256(v.raw, v.raw, 0x01));
55 }
else if constexpr (i == 5) {
56 auto tmp = _mm256_permute2f128_si256(v.raw, v.raw, 0x01);
57 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(tmp, 0x01));
58 }
else if constexpr (i == 6) {
59 auto tmp = _mm256_permute2f128_si256(v.raw, v.raw, 0x01);
60 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(tmp, 0x02));
61 }
else if constexpr (i == 7) {
62 auto tmp = _mm256_permute2f128_si256(v.raw, v.raw, 0x01);
63 return _mm256_cvtsi256_si32(_mm256_shuffle_epi32(tmp, 0x03));
69#if MOCHI_COMPILER_MSVC
70 return raw.m256i_i32[i];
73 case 0:
return Get<0>(*
this);
74 case 1:
return Get<1>(*
this);
75 case 2:
return Get<2>(*
this);
76 case 3:
return Get<3>(*
this);
77 case 4:
return Get<4>(*
this);
78 case 5:
return Get<5>(*
this);
79 case 6:
return Get<6>(*
this);
80 case 7:
return Get<7>(*
this);
88 static_assert(iHalf == 0 || iHalf == 1);
89 return _mm256_extracti128_si256(a.raw, iHalf);
94#if MOCHI_COMPILER_MSVC
96 result.raw.m256i_i32[i] = value;
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)}};
110 return _mm256_blendv_epi8(v.raw, _mm256_set1_epi32(value), kMasks[i]);
116 static_assert(i >= 0 && i <
kSize,
"Index out of range");
117 return Set(v, i, value);
122 SetInt64(int64_t a, int64_t b, int64_t c, int64_t d) {
123 return _mm256_set_epi64x(d, c, b, a);
128 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
129 int mask = GetMSBitMask(v);
130 if constexpr (N ==
kSize) {
131 return mask == 0xFFFFFFFF;
133 int constexpr kNumBits = N *
sizeof(
Scalar);
134 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
135 return (mask & kMustBeSet) == kMustBeSet;
141 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
142 int mask = GetMSBitMask(v);
143 if constexpr (N ==
kSize) {
146 int constexpr kNumBits = N *
sizeof(
Scalar);
147 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
148 return (mask & kMayBeSet) != 0;
163 static_assert(N >= 2 && N <= 8,
"Unsupported N");
165 if constexpr (N >= 2 && N <= 4) {
170 if constexpr (N != 8) {
172 auto inf = HalfT{std::numeric_limits<Scalar>::max()};
173 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(inf, hi);
175 return HalfT::HMin<4>(HalfT::Min(lo, hi));
181 static_assert(N >= 2 && N <= 8,
"Unsupported N");
183 if constexpr (N >= 2 && N <= 4) {
188 if constexpr (N != 8) {
190 auto lowest = HalfT{std::numeric_limits<Scalar>::lowest()};
191 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(lowest, hi);
193 return HalfT::HMax<4>(HalfT::Max(lo, hi));
199 static_assert(N >= 2 && N <= 8,
"Unsupported N");
201 if constexpr (N >= 2 && N <= 4) {
203 }
else if constexpr (N == 5) {
206 }
else if constexpr (N == 6) {
208 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) +
Get<2>(a) +
Get<3>(a);
209 }
else if constexpr (N == 7) {
211 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) + HalfT::Get<2>(tmp) +
Get<3>(a);
212 }
else if constexpr (N == 8) {
217 template <
int N = kSize>
219 static_assert(N >= 0 && N <=
kSize);
220 if constexpr (N == 0) {
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);
226 return _mm256_maskload_epi32(ptr, mask);
227 }
else if constexpr (N == 3) {
228 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, -1, -1, -1);
229 return _mm256_maskload_epi32(ptr, mask);
230 }
else if constexpr (N == 4) {
231 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, -1, -1, -1, -1);
232 return _mm256_maskload_epi32(ptr, mask);
233 }
else if constexpr (N == 5) {
234 __m256i mask = _mm256_set_epi32(0, 0, 0, -1, -1, -1, -1, -1);
235 return _mm256_maskload_epi32(ptr, mask);
236 }
else if constexpr (N == 6) {
237 __m256i mask = _mm256_set_epi32(0, 0, -1, -1, -1, -1, -1, -1);
238 return _mm256_maskload_epi32(ptr, mask);
239 }
else if constexpr (N == 7) {
240 __m256i mask = _mm256_set_epi32(0, -1, -1, -1, -1, -1, -1, -1);
241 return _mm256_maskload_epi32(ptr, mask);
243 return _mm256_loadu_si256(
reinterpret_cast<__m256i const*
>(ptr));
249#if MOCHI_ARCH_X64_AVX512
250 return _mm256_maskz_loadu_epi32(x64_simd::kLaneMasksS8[n], ptr);
253 return _mm256_maskload_epi32(ptr, x64_simd::kLoadMasksS8[n]);
257 template <
int kTupleCount = kSize>
260 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
261 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 8);
262 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 8, 0, 8);
263 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 16, 0, 8);
266 auto a = Simd::Load<kCount0>(ptr).raw;
268 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 8).raw;
270 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 16).raw;
272 auto d = _mm256_blend_epi32(a, b, 0b10010010);
273 auto e = _mm256_blend_epi32(d, c, 0b00100100);
274 auto f = _mm256_permute2x128_si256(e, e, 0x01);
275 auto g = _mm256_blend_epi32(e, f, 0b01000100);
276 out0.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(1, 2, 3, 0));
278 d = _mm256_blend_epi32(a, b, 0b00100100);
279 e = _mm256_blend_epi32(d, c, 0b01001001);
280 f = _mm256_permute2x128_si256(e, e, 0x01);
281 g = _mm256_blend_epi32(e, f, 0b10011001);
282 out1.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(2, 3, 0, 1));
284 d = _mm256_blend_epi32(a, b, 0b01001001);
285 e = _mm256_blend_epi32(d, c, 0b10010010);
286 f = _mm256_permute2x128_si256(e, e, 0x01);
287 g = _mm256_blend_epi32(e, f, 0b00100010);
288 out2.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(3, 0, 1, 2));
291 template <
int N = kSize>
293 static_assert(N >= 0 && N <=
kSize);
294 if constexpr (N == 0) {
295 }
else if constexpr (N <
kSize) {
297 memcpy(ptr, &v,
sizeof(
Scalar) * N);
299 return _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), v.raw);
305#if MOCHI_ARCH_X64_AVX512
306 _mm256_mask_storeu_epi32(ptr, x64_simd::kLaneMasksS8[n], v.raw);
325 auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(condition.raw));
327 auto const* tableRow =
328 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableS8[mask]);
329 auto pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
330 auto packed = _mm256_permutevar8x32_ps(_mm256_castsi256_ps(values.raw), pattern);
331 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), _mm256_castps_si256(packed));
332 return _mm_popcnt_u32(mask);
335 template <
int kTupleCount = kSize>
337 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
341 auto d = _mm256_shuffle_epi32(a.raw, _MM_SHUFFLE(1, 2, 3, 0));
342 auto e = _mm256_shuffle_epi32(b.raw, _MM_SHUFFLE(2, 3, 0, 1));
343 auto f = _mm256_shuffle_epi32(c.raw, _MM_SHUFFLE(3, 0, 1, 2));
344 __m256i g = _mm256_blend_epi32(d, e, 0b00100010);
345 g = _mm256_blend_epi32(g, f, 0b01000100);
346 __m256i h = _mm256_blend_epi32(d, e, 0b10011001);
347 h = _mm256_blend_epi32(h, f, 0b00100010);
348 h = _mm256_permute2x128_si256(h, h, 0x01);
349 __m256i i = _mm256_blend_epi32(d, e, 0b01000100);
350 i = _mm256_blend_epi32(i, f, 0b10011001);
351 d = _mm256_blend_epi32(g, h, 0b11110000);
352 e = _mm256_blend_epi32(i, g, 0b11110000);
353 f = _mm256_blend_epi32(h, i, 0b11110000);
354 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 8);
355 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 8, 0, 8);
356 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 16, 0, 8);
357 Simd::Store<kCount0>(ptr, d);
358 if constexpr (kCount1 > 0) {
359 Simd::Store<kCount1>(ptr + 8, e);
361 if constexpr (kCount2 > 0) {
362 Simd::Store<kCount2>(ptr + 16, f);
367 return _mm256_min_epi32(a.raw, b.raw);
371 return _mm256_max_epi32(a.raw, b.raw);
375 return _mm256_blendv_epi8(b.raw, a.raw, mask.raw);
379 return _mm256_setzero_si256();
383 return _mm256_cmpgt_epi32(rhs.raw, this->raw);
387 return _mm256_cmpgt_epi32(this->
raw, rhs.raw);
391 return ~(*
this > rhs);
395 return ~(*
this < rhs);
399 return _mm256_cmpeq_epi32(a.raw, b.raw);
407 auto mask = GetMSBitMask(
Equal(
raw, rhs.raw));
408 return mask == 0xFFFFFFFF;
417 auto ones = _mm256_set1_epi32(-1);
418 return _mm256_xor_si256(
raw, ones);
422 return Zero() - *
this;
426 return _mm256_add_epi32(
raw, rhs.raw);
430 return _mm256_sub_epi32(
raw, rhs.raw);
434 return _mm256_mullo_epi32(
raw, rhs.raw);
438#if MOCHI_ARCH_X64_SVML
439 return _mm256_div_epi32(
raw, rhs.raw);
455 return _mm256_and_si256(
raw, rhs.raw);
459 return _mm256_or_si256(
raw, rhs.raw);
463 return _mm256_xor_si256(
raw, rhs.raw);
467 return _mm256_slli_epi32(
raw, rhs);
470 template <
int kShift>
472 static_assert(kShift >= 0 && kShift < 32,
"Shift amount out-of-range");
473 return _mm256_srai_epi32(a.raw, kShift);
479 return _mm256_movemask_epi8(a.raw);
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
Simd operator>(Simd rhs) const
Simd operator<<(int shift) const
Simd operator*(Simd rhs) const
Simd operator^(Simd rhs) 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
Simd operator+(Simd rhs) const
Simd operator/(Simd rhs) const
Simd operator<=(Simd rhs) const
Scalar operator[](int i) const
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
Simd< T, N > Set(Simd< T, N > a, T value)
constexpr auto NotEqual(T const &a, T const &b)
V Broadcast(typename V::Scalar a)
constexpr T Select(bool condition, T a, T b)
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
Simd< T, N/2 > GetHalf(Simd< T, N > a)
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)
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
void Store(T *ptr, Simd< T, N > a)
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
V Load(typename V::Scalar const *ptr)
Simd< T, N > ShiftRight(Simd< T, N > a)
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)