21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
29class Simd<int64_t, 2> {
31 static_assert(
sizeof(int64_t) ==
sizeof(
long long));
34 Simd(int64_t low, int64_t high)
35 :
raw(_mm_set_epi64x(static_cast<long long>(high), static_cast<long long>(low))) {}
36 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
37 Simd(U a) :
raw(_mm_set1_epi64x(static_cast<long long>(a))) {}
41 static_assert(i >= 0 && i <
kSize,
"Index out of range");
47#if MOCHI_COMPILER_MSVC
48 return raw.m128i_i64[i];
56 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
57 auto mask = GetMSBitMask(v);
58 if constexpr (N ==
kSize) {
59 return mask == 0x0000FFFF;
61 return (mask & 0x000000FF) == 0x000000FF;
65 template <
int x,
int y>
67 static_assert(x >= 0 && x < 2 && y >= 0 && y < 2,
"invalid blend index");
68 if constexpr (x == 0 && y == 0) {
70 }
else if constexpr (x == 1 && y == 1) {
73 return _mm_castpd_si128(
74 _mm_blend_pd(_mm_castsi128_pd(a.raw), _mm_castsi128_pd(b.raw), x | (y << 1)));
89 static_assert(N == 2,
"Unsupported N");
95 static_assert(N == 2,
"Unsupported N");
101 static_assert(N == 2,
"Unsupported N");
105 template <
int N = kSize>
107 static_assert(N >= 0 && N <=
kSize);
108 if constexpr (N == 0) {
110 }
else if constexpr (N == 1) {
111 return Simd{*ptr, 0};
112 }
else if constexpr (N == 2) {
113 return _mm_loadu_si128(
reinterpret_cast<__m128i const*
>(ptr));
119#if MOCHI_ARCH_X64_AVX512
120 return _mm_maskz_loadu_epi64(x64_simd::kLaneMasksS8[n], ptr);
130 template <
int kTupleCount = kSize>
133 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
134 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 2, 0, 2);
135 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 4, 0, 2);
136 auto a = _mm_castsi128_pd(Simd::Load<2>(ptr).
raw);
137 auto b = _mm_castsi128_pd(Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 2).
raw);
138 auto c = _mm_castsi128_pd(Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 4).
raw);
139 out0.raw = _mm_castpd_si128(_mm_shuffle_pd(a, b, 0b0010));
140 out1.raw = _mm_castpd_si128(_mm_shuffle_pd(a, c, 0b0001));
141 out2.raw = _mm_castpd_si128(_mm_shuffle_pd(b, c, 0b0010));
145#if MOCHI_ARCH_X64_AVX512
146 return _mm_min_epi64(a.raw, b.raw);
153#if MOCHI_ARCH_X64_AVX512
154 return _mm_max_epi64(a.raw, b.raw);
161 return _mm_blendv_epi8(b.raw, a.raw, mask.raw);
164 template <
int x = 0,
int y = 1>
166 static_assert(x >= 0 && x < 2,
"Invalid index");
167 static_assert(y >= 0 && y < 2,
"Invalid index");
168 if constexpr (x == 0 && y == 1) {
171 return _mm_castpd_si128(
172 _mm_shuffle_pd(_mm_castsi128_pd(v.raw), _mm_castsi128_pd(v.raw), x | (y << 1)));
176 template <
int N = kSize>
178 static_assert(N >= 0 && N <=
kSize);
179 if constexpr (N == 0) {
180 }
else if constexpr (N <
kSize) {
182 memcpy(ptr, &v.raw,
sizeof(
Scalar) * N);
184 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(ptr), v.raw);
190#if MOCHI_ARCH_X64_AVX512
191 _mm_mask_storeu_epi64(ptr, x64_simd::kLaneMasksS8[n], v.raw);
204#if MOCHI_ARCH_X64_AVX512
205 auto const mask = _mm_movepi64_mask(condition.raw);
206 _mm_mask_compressstoreu_epi64(ptr, mask, values.raw);
207 return _mm_popcnt_u32(mask);
209 auto mask = _mm_movemask_pd(_mm_castsi128_pd(condition.raw));
210 auto swapped = _mm_castpd_si128(_mm_shuffle_pd(
211 _mm_castsi128_pd(values.raw), _mm_castsi128_pd(values.raw), 1));
212 auto blendMask = _mm_set1_epi32((mask & 1) - 1);
213 auto packed = _mm_blendv_epi8(values.raw, swapped, blendMask);
214 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(ptr), packed);
215 return _mm_popcnt_u32(mask);
219 template <
int kTupleCount = kSize>
221 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
223 auto d = _mm_shuffle_pd(_mm_castsi128_pd(a.raw), _mm_castsi128_pd(b.raw), 0b00);
224 auto e = _mm_shuffle_pd(_mm_castsi128_pd(c.raw), _mm_castsi128_pd(a.raw), 0b10);
225 auto f = _mm_shuffle_pd(_mm_castsi128_pd(b.raw), _mm_castsi128_pd(c.raw), 0b11);
226 Simd::Store<2>(ptr, _mm_castpd_si128(d));
227 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 2, 0, 2);
228 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 4, 0, 2);
229 if constexpr (kCount1 > 0) {
230 Simd::Store<kCount1>(ptr + 2, _mm_castpd_si128(e));
232 if constexpr (kCount2 > 0) {
233 Simd::Store<kCount2>(ptr + 4, _mm_castpd_si128(f));
238 return _mm_setzero_si128();
242 return _mm_cmpgt_epi64(rhs.raw, this->raw);
246 return _mm_cmpgt_epi64(this->
raw, rhs.raw);
250 return ~(*
this > rhs);
254 return ~(*
this < rhs);
258 return _mm_cmpeq_epi64(a.raw, b.raw);
266 auto mask = GetMSBitMask(
Equal(*
this, rhs));
267 return mask == 0xFFFF;
271 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
276 __m128i ones = _mm_cmpeq_epi64(
raw,
raw);
277 return _mm_xor_si128(
raw, ones);
281 return _mm_sub_epi64(_mm_setzero_si128(),
raw);
285 return _mm_add_epi64(
raw, rhs.raw);
289 return _mm_sub_epi64(
raw, rhs.raw);
293#if MOCHI_ARCH_X64_AVX512
294 return _mm_mullo_epi64(
raw, rhs.raw);
301#if MOCHI_ARCH_X64_SVML
302 return _mm_div_epi64(
raw, rhs.raw);
310 return _mm_and_si128(
raw, rhs.raw);
314 return _mm_or_si128(
raw, rhs.raw);
318 return _mm_xor_si128(
raw, rhs.raw);
322 return _mm_slli_epi64(
raw, rhs);
325 template <
int kShift>
327 static_assert(kShift >= 0 && kShift < 64,
"Shift amount out-of-range");
328 if constexpr (kShift == 0) {
331#if MOCHI_ARCH_X64_AVX512
332 return _mm_srai_epi64(a.raw, kShift);
334 auto shifted = _mm_srli_epi64(a.raw, kShift);
335 auto signMask = _mm_cmpgt_epi64(_mm_setzero_si128(), a.raw);
336 auto signFill = _mm_slli_epi64(signMask, 64 - kShift);
337 return _mm_or_si128(shifted, signFill);
345 return _mm_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)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
constexpr T Select(bool condition, T a, T b)
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
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)