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 v.raw.m256i_i32[i];
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));
250 return _mm256_maskload_epi32(ptr, x64_simd::kLoadMasksS8[n]);
253 template <
int kTupleCount = kSize>
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);
262 auto a = Simd::Load<kCount0>(ptr).raw;
264 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 8).raw;
266 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 16).raw;
268 auto d = _mm256_blend_epi32(a, b, 0b10010010);
269 auto e = _mm256_blend_epi32(d, c, 0b00100100);
270 auto f = _mm256_permute2x128_si256(e, e, 0x01);
271 auto g = _mm256_blend_epi32(e, f, 0b01000100);
272 out0.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(1, 2, 3, 0));
274 d = _mm256_blend_epi32(a, b, 0b00100100);
275 e = _mm256_blend_epi32(d, c, 0b01001001);
276 f = _mm256_permute2x128_si256(e, e, 0x01);
277 g = _mm256_blend_epi32(e, f, 0b10011001);
278 out1.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(2, 3, 0, 1));
280 d = _mm256_blend_epi32(a, b, 0b01001001);
281 e = _mm256_blend_epi32(d, c, 0b10010010);
282 f = _mm256_permute2x128_si256(e, e, 0x01);
283 g = _mm256_blend_epi32(e, f, 0b00100010);
284 out2.raw = _mm256_shuffle_epi32(g, _MM_SHUFFLE(3, 0, 1, 2));
287 template <
int N = kSize>
289 static_assert(N >= 0 && N <=
kSize);
290 if constexpr (N == 0) {
291 }
else if constexpr (N <
kSize) {
293 memcpy(ptr, &v,
sizeof(
Scalar) * N);
295 return _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), v.raw);
317 auto mask = _mm256_movemask_ps(_mm256_castsi256_ps(condition.raw));
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);
327 template <
int kTupleCount = kSize>
329 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
333 auto d = _mm256_shuffle_epi32(a.raw, _MM_SHUFFLE(1, 2, 3, 0));
334 auto e = _mm256_shuffle_epi32(b.raw, _MM_SHUFFLE(2, 3, 0, 1));
335 auto f = _mm256_shuffle_epi32(c.raw, _MM_SHUFFLE(3, 0, 1, 2));
336 __m256i g = _mm256_blend_epi32(d, e, 0b00100010);
337 g = _mm256_blend_epi32(g, f, 0b01000100);
338 __m256i h = _mm256_blend_epi32(d, e, 0b10011001);
339 h = _mm256_blend_epi32(h, f, 0b00100010);
340 h = _mm256_permute2x128_si256(h, h, 0x01);
341 __m256i i = _mm256_blend_epi32(d, e, 0b01000100);
342 i = _mm256_blend_epi32(i, f, 0b10011001);
343 d = _mm256_blend_epi32(g, h, 0b11110000);
344 e = _mm256_blend_epi32(i, g, 0b11110000);
345 f = _mm256_blend_epi32(h, i, 0b11110000);
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);
353 if constexpr (kCount2 > 0) {
354 Simd::Store<kCount2>(ptr + 16, f);
359 return _mm256_min_epi32(a.raw, b.raw);
363 return _mm256_max_epi32(a.raw, b.raw);
367 return _mm256_blendv_epi8(b.raw, a.raw, mask.raw);
371 return _mm256_setzero_si256();
375 return _mm256_cmpgt_epi32(rhs.raw, this->raw);
379 return _mm256_cmpgt_epi32(this->
raw, rhs.raw);
383 return ~(*
this > rhs);
387 return ~(*
this < rhs);
391 return _mm256_cmpeq_epi32(a.raw, b.raw);
399 auto mask = GetMSBitMask(
Equal(
raw, rhs.raw));
400 return mask == 0xFFFFFFFF;
409 auto ones = _mm256_set1_epi32(-1);
410 return _mm256_xor_si256(
raw, ones);
414 return Zero() - *
this;
418 return _mm256_add_epi32(
raw, rhs.raw);
422 return _mm256_sub_epi32(
raw, rhs.raw);
426 return _mm256_mullo_epi32(
raw, rhs.raw);
430#if MOCHI_ARCH_X64_SVML
431 return _mm256_div_epi32(
raw, rhs.raw);
447 return _mm256_and_si256(
raw, rhs.raw);
451 return _mm256_or_si256(
raw, rhs.raw);
455 return _mm256_xor_si256(
raw, rhs.raw);
459 return _mm256_slli_epi32(
raw, rhs);
462 template <
int kShift>
464 return _mm256_srli_epi32(a.raw, kShift);
470 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
#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)