21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
29class Simd<int64_t, 4> {
31 static_assert(
sizeof(int64_t) ==
sizeof(
long long));
34 Simd(int64_t a, int64_t b, int64_t c = 0, int64_t d = 0)
35 :
raw(_mm256_set_epi64x(
36 static_cast<long long>(d),
37 static_cast<long long>(c),
38 static_cast<long long>(b),
39 static_cast<long long>(a))) {}
40 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
41 Simd(U a) :
raw(_mm256_set1_epi64x(static_cast<long long>(a))) {}
45 static_assert(i >= 0 && i <
kSize,
"Index out of range");
46#if MOCHI_COMPILER_MSVC
51 if constexpr (i < 2) {
52 return _mm_extract_epi64(_mm256_castsi256_si128(v.raw), i);
54 return _mm_extract_epi64(_mm256_extracti128_si256(v.raw, 1), i - 2);
57 return _mm256_extract_epi64(v.raw, i);
63#if MOCHI_COMPILER_MSVC
64 return v.raw.m256i_i64[i];
78 static_assert(iHalf == 0 || iHalf == 1);
79 return _mm256_extracti128_si256(a.raw, iHalf);
84 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
85 int mask = GetMSBitMask(v);
86 if constexpr (N ==
kSize) {
87 return mask == 0xFFFFFFFF;
89 int constexpr kNumBits = N *
sizeof(
Scalar);
90 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
91 return (mask & kMustBeSet) == kMustBeSet;
106 static_assert(N >= 2 && N <= 4,
"Unsupported N");
108 if constexpr (N == 2) {
110 }
else if constexpr (N == 3) {
113 return HalfT::Get<0>(HalfT::Min(HalfT::Min(lo, HalfT::Broadcast<1>(lo)), hi));
121 static_assert(N >= 2 && N <= 4,
"Unsupported N");
123 if constexpr (N == 2) {
125 }
else if constexpr (N == 3) {
128 return HalfT::Get<0>(HalfT::Max(HalfT::Max(lo, HalfT::Broadcast<1>(lo)), hi));
136 static_assert(N >= 2 && N <= 4,
"Unsupported N");
137 if constexpr (N == 2) {
139 }
else if constexpr (N == 3) {
142 return HalfT::Get<0>(tmp) +
Get<1>(a);
143 }
else if constexpr (N == 4) {
146 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp);
150 template <
int N = kSize>
152 static_assert(N >= 0 && N <=
kSize);
153 if constexpr (N == 0) {
155 }
else if constexpr (N == 1) {
156 return Simd{*ptr, 0, 0, 0};
157 }
else if constexpr (N == 2) {
158 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, -1, -1, -1, -1);
159 return _mm256_maskload_epi64(
reinterpret_cast<long long const*
>(ptr), mask);
160 }
else if constexpr (N == 3) {
161 __m256i mask = _mm256_set_epi32(0, 0, -1, -1, -1, -1, -1, -1);
162 return _mm256_maskload_epi64(
reinterpret_cast<long long const*
>(ptr), mask);
164 return _mm256_loadu_si256(
reinterpret_cast<__m256i const*
>(ptr));
179 template <
int kTupleCount = kSize>
182 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
183 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
184 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
185 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
186 auto a = _mm256_castsi256_pd(Simd::Load<kCount0>(ptr).
raw);
188 _mm256_castsi256_pd(Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).
raw);
190 _mm256_castsi256_pd(Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).
raw);
192 auto d = _mm256_blend_pd(a, b, 0b0100);
193 d = _mm256_blend_pd(d, c, 0b0010);
194 auto e = _mm256_permute2f128_pd(d, d, 0x01);
195 out0.raw = _mm256_castpd_si256(_mm256_blend_pd(d, e, 0b1010));
197 d = _mm256_blend_pd(a, b, 0b1001);
198 d = _mm256_blend_pd(d, c, 0b0100);
199 out1.raw = _mm256_castpd_si256(_mm256_shuffle_pd(d, d, 0b0101));
201 d = _mm256_blend_pd(a, b, 0b0010);
202 d = _mm256_blend_pd(d, c, 0b1001);
203 e = _mm256_permute2f128_pd(d, d, 0x01);
204 out2.raw = _mm256_castpd_si256(_mm256_blend_pd(d, e, 0b0101));
226 return _mm256_blendv_epi8(b.raw, a.raw, mask.raw);
229 template <
int x,
int y,
int z,
int w>
231 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
234 return _mm256_permute4x64_epi64(a.raw, _MM_SHUFFLE(w, z, y, x));
238 template <
int x,
int y,
int z,
int w>
243 template <
int N = kSize>
245 static_assert(N >= 0 && N <=
kSize);
246 if constexpr (N == 0) {
247 }
else if constexpr (N <
kSize) {
248 memcpy(ptr, &v.raw,
sizeof(
Scalar) * N);
250 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), v.raw);
268 auto mask = _mm256_movemask_pd(_mm256_castsi256_pd(condition.raw));
270 auto const* tableRow =
271 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableD4[mask]);
272 auto pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
273 auto packed = _mm256_permutevar8x32_epi32(values.raw, pattern);
274 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), packed);
275 return _mm_popcnt_u32(mask);
278 template <
int kTupleCount = kSize>
280 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
282 auto a_ = _mm256_castsi256_pd(a.raw);
283 auto b_ = _mm256_castsi256_pd(b.raw);
284 auto c_ = _mm256_castsi256_pd(c.raw);
285 auto d = _mm256_shuffle_pd(b_, b_, 0b0101);
286 auto e = _mm256_blend_pd(a_, c_, 0b0101);
287 e = _mm256_permute2f128_pd(e, e, 0x01);
288 auto f = _mm256_blend_pd(a_, d, 0b1010);
289 auto g = _mm256_blend_pd(d, c_, 0b1010);
290 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
291 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
292 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
293 Simd::Store<kCount0>(
294 ptr,
Simd{_mm256_castpd_si256(_mm256_blend_pd(f, e, 0b1100))});
295 if constexpr (kCount1 > 0) {
296 Simd::Store<kCount1>(
297 ptr + 4,
Simd{_mm256_castpd_si256(_mm256_blend_pd(g, f, 0b1100))});
299 if constexpr (kCount2 > 0) {
300 Simd::Store<kCount2>(
301 ptr + 8,
Simd{_mm256_castpd_si256(_mm256_blend_pd(e, g, 0b1100))});
306 return _mm256_setzero_si256();
310 return _mm256_cmpgt_epi64(rhs.raw, this->raw);
314 return _mm256_cmpgt_epi64(this->
raw, rhs.raw);
318 return ~(*
this > rhs);
322 return ~(*
this < rhs);
326 return _mm256_cmpeq_epi64(a.raw, b.raw);
334 auto mask = GetMSBitMask(
Equal(*
this, rhs));
335 return mask == 0xFFFFFFFF;
339 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
344 auto ones = _mm256_cmpeq_epi64(
raw,
raw);
345 return _mm256_xor_si256(
raw, ones);
349 return _mm256_sub_epi64(_mm256_setzero_si256(),
raw);
353 return _mm256_add_epi64(
raw, rhs.raw);
357 return _mm256_sub_epi64(
raw, rhs.raw);
371#if MOCHI_ARCH_X64_SVML
372 return _mm256_div_epi64(
raw, rhs.raw);
384 return _mm256_and_si256(
raw, rhs.raw);
388 return _mm256_or_si256(
raw, rhs.raw);
392 return _mm256_xor_si256(
raw, rhs.raw);
396 return _mm256_slli_epi64(
raw, rhs);
399 template <
int kShift>
401 return _mm256_srli_epi64(a.raw, kShift);
407 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,...)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
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)