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");
51#if MOCHI_COMPILER_MSVC
52 return raw.m256i_i64[i];
60 static_assert(iHalf == 0 || iHalf == 1);
61 return _mm256_extracti128_si256(a.raw, iHalf);
66 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
67 int mask = GetMSBitMask(v);
68 if constexpr (N ==
kSize) {
69 return mask == 0xFFFFFFFF;
71 int constexpr kNumBits = N *
sizeof(
Scalar);
72 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
73 return (mask & kMustBeSet) == kMustBeSet;
88 static_assert(N >= 2 && N <= 4,
"Unsupported N");
90 if constexpr (N == 2) {
92 }
else if constexpr (N == 3) {
95 return HalfT::Get<0>(HalfT::Min(HalfT::Min(lo, HalfT::Broadcast<1>(lo)), hi));
103 static_assert(N >= 2 && N <= 4,
"Unsupported N");
105 if constexpr (N == 2) {
107 }
else if constexpr (N == 3) {
110 return HalfT::Get<0>(HalfT::Max(HalfT::Max(lo, HalfT::Broadcast<1>(lo)), hi));
118 static_assert(N >= 2 && N <= 4,
"Unsupported N");
119 if constexpr (N == 2) {
121 }
else if constexpr (N == 3) {
124 return HalfT::Get<0>(tmp) +
Get<1>(a);
125 }
else if constexpr (N == 4) {
128 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp);
132 template <
int N = kSize>
134 static_assert(N >= 0 && N <=
kSize);
135 if constexpr (N == 0) {
137 }
else if constexpr (N == 1) {
138 return Simd{*ptr, 0, 0, 0};
139 }
else if constexpr (N == 2) {
140 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, -1, -1, -1, -1);
141 return _mm256_maskload_epi64(
reinterpret_cast<long long const*
>(ptr), mask);
142 }
else if constexpr (N == 3) {
143 __m256i mask = _mm256_set_epi32(0, 0, -1, -1, -1, -1, -1, -1);
144 return _mm256_maskload_epi64(
reinterpret_cast<long long const*
>(ptr), mask);
146 return _mm256_loadu_si256(
reinterpret_cast<__m256i const*
>(ptr));
152#if MOCHI_ARCH_X64_AVX512
153 return _mm256_maskz_loadu_epi64(x64_simd::kLaneMasksS8[n], ptr);
165 template <
int kTupleCount = kSize>
168 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
169 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
170 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
171 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
172 auto a = _mm256_castsi256_pd(Simd::Load<kCount0>(ptr).
raw);
174 _mm256_castsi256_pd(Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).
raw);
176 _mm256_castsi256_pd(Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).
raw);
178 auto d = _mm256_blend_pd(a, b, 0b0100);
179 d = _mm256_blend_pd(d, c, 0b0010);
180 auto e = _mm256_permute2f128_pd(d, d, 0x01);
181 out0.raw = _mm256_castpd_si256(_mm256_blend_pd(d, e, 0b1010));
183 d = _mm256_blend_pd(a, b, 0b1001);
184 d = _mm256_blend_pd(d, c, 0b0100);
185 out1.raw = _mm256_castpd_si256(_mm256_shuffle_pd(d, d, 0b0101));
187 d = _mm256_blend_pd(a, b, 0b0010);
188 d = _mm256_blend_pd(d, c, 0b1001);
189 e = _mm256_permute2f128_pd(d, d, 0x01);
190 out2.raw = _mm256_castpd_si256(_mm256_blend_pd(d, e, 0b0101));
194#if MOCHI_ARCH_X64_AVX512
195 return _mm256_min_epi64(a.raw, b.raw);
206#if MOCHI_ARCH_X64_AVX512
207 return _mm256_max_epi64(a.raw, b.raw);
218 return _mm256_blendv_epi8(b.raw, a.raw, mask.raw);
221 template <
int x,
int y,
int z,
int w>
223 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
226 return _mm256_permute4x64_epi64(a.raw, _MM_SHUFFLE(w, z, y, x));
230 template <
int x,
int y,
int z,
int w>
235 template <
int N = kSize>
237 static_assert(N >= 0 && N <=
kSize);
238 if constexpr (N == 0) {
239 }
else if constexpr (N <
kSize) {
240 memcpy(ptr, &v.raw,
sizeof(
Scalar) * N);
242 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), v.raw);
248#if MOCHI_ARCH_X64_AVX512
249 _mm256_mask_storeu_epi64(ptr, x64_simd::kLaneMasksS8[n], v.raw);
264#if MOCHI_ARCH_X64_AVX512
265 auto const mask = _mm256_movepi64_mask(condition.raw);
266 _mm256_mask_compressstoreu_epi64(ptr, mask, values.raw);
267 return _mm_popcnt_u32(mask);
269 auto mask = _mm256_movemask_pd(_mm256_castsi256_pd(condition.raw));
271 auto const* tableRow =
272 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableD4[mask]);
273 auto pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
274 auto packed = _mm256_permutevar8x32_epi32(values.raw, pattern);
275 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(ptr), packed);
276 return _mm_popcnt_u32(mask);
280 template <
int kTupleCount = kSize>
282 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
284 auto a_ = _mm256_castsi256_pd(a.raw);
285 auto b_ = _mm256_castsi256_pd(b.raw);
286 auto c_ = _mm256_castsi256_pd(c.raw);
287 auto d = _mm256_shuffle_pd(b_, b_, 0b0101);
288 auto e = _mm256_blend_pd(a_, c_, 0b0101);
289 e = _mm256_permute2f128_pd(e, e, 0x01);
290 auto f = _mm256_blend_pd(a_, d, 0b1010);
291 auto g = _mm256_blend_pd(d, c_, 0b1010);
292 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
293 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
294 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
295 Simd::Store<kCount0>(
296 ptr,
Simd{_mm256_castpd_si256(_mm256_blend_pd(f, e, 0b1100))});
297 if constexpr (kCount1 > 0) {
298 Simd::Store<kCount1>(
299 ptr + 4,
Simd{_mm256_castpd_si256(_mm256_blend_pd(g, f, 0b1100))});
301 if constexpr (kCount2 > 0) {
302 Simd::Store<kCount2>(
303 ptr + 8,
Simd{_mm256_castpd_si256(_mm256_blend_pd(e, g, 0b1100))});
308 return _mm256_setzero_si256();
312 return _mm256_cmpgt_epi64(rhs.raw, this->raw);
316 return _mm256_cmpgt_epi64(this->
raw, rhs.raw);
320 return ~(*
this > rhs);
324 return ~(*
this < rhs);
328 return _mm256_cmpeq_epi64(a.raw, b.raw);
336 auto mask = GetMSBitMask(
Equal(*
this, rhs));
337 return mask == 0xFFFFFFFF;
341 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
346 auto ones = _mm256_cmpeq_epi64(
raw,
raw);
347 return _mm256_xor_si256(
raw, ones);
351 return _mm256_sub_epi64(_mm256_setzero_si256(),
raw);
355 return _mm256_add_epi64(
raw, rhs.raw);
359 return _mm256_sub_epi64(
raw, rhs.raw);
363#if MOCHI_ARCH_X64_AVX512
364 return _mm256_mullo_epi64(
raw, rhs.raw);
375#if MOCHI_ARCH_X64_SVML
376 return _mm256_div_epi64(
raw, rhs.raw);
388 return _mm256_and_si256(
raw, rhs.raw);
392 return _mm256_or_si256(
raw, rhs.raw);
396 return _mm256_xor_si256(
raw, rhs.raw);
400 return _mm256_slli_epi64(
raw, rhs);
403 template <
int kShift>
405 static_assert(kShift >= 0 && kShift < 64,
"Shift amount out-of-range");
406 if constexpr (kShift == 0) {
409#if MOCHI_ARCH_X64_AVX512
410 return _mm256_srai_epi64(a.raw, kShift);
412 auto shifted = _mm256_srli_epi64(a.raw, kShift);
413 auto signMask = _mm256_cmpgt_epi64(_mm256_setzero_si256(), a.raw);
414 auto signFill = _mm256_slli_epi64(signMask, 64 - kShift);
415 return _mm256_or_si256(shifted, signFill);
423 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,...)
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)