21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
32 Simd(
int a,
int b,
int c = 0,
int d = 0) :
raw(_mm_set_epi32(d, c, b, a)) {}
33 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
34 Simd(U a) :
raw(_mm_set1_epi32(a)) {}
38 static_assert(i >= 0 && i < 4,
"Index out of range");
39 if constexpr (i == 0) {
40 return _mm_cvtsi128_si32(v.raw);
41 }
else if constexpr (i == 1) {
42 return _mm_cvtsi128_si32(_mm_shuffle_epi32(v.raw, _MM_SHUFFLE(1, 1, 1, 1)));
43 }
else if constexpr (i == 2) {
44 return _mm_cvtsi128_si32(_mm_shuffle_epi32(v.raw, _MM_SHUFFLE(2, 2, 2, 2)));
45 }
else if constexpr (i == 3) {
46 return _mm_cvtsi128_si32(_mm_shuffle_epi32(v.raw, _MM_SHUFFLE(3, 3, 3, 3)));
52#if MOCHI_COMPILER_MSVC
53 return raw.m128i_i32[i];
56 case 0:
return Get<0>(*
this);
57 case 1:
return Get<1>(*
this);
58 case 2:
return Get<2>(*
this);
59 case 3:
return Get<3>(*
this);
67#if MOCHI_COMPILER_MSVC
69 result.raw.m128i_i32[i] = value;
72 static constexpr __m128i kMasks[] = {
74 {
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL)},
75 {
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL)},
76 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL)},
77 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL)}
79 return _mm_blendv_epi8(v.raw, _mm_set1_epi32(value), kMasks[i]);
85 static_assert(i >= 0 && i <
kSize,
"Index out of range");
88 return _mm_castps_si128(_mm_insert_ps(
89 _mm_castsi128_ps(v.raw), _mm_castsi128_ps(_mm_set1_epi32(value)), i << 4));
94 return _mm_set_epi64x(b, a);
97 template <
int x,
int y,
int z,
int w>
100 x >= 0 && x < 2 && y >= 0 && y < 2 && z >= 0 && z < 2 && w >= 0 && w < 2,
101 "invalid blend index");
102 if constexpr (x == 0 && y == 0 && z == 0 && w == 0) {
104 }
else if constexpr (x == 1 && y == 1 && z == 1 && w == 1) {
107 return _mm_blend_epi32(a.raw, b.raw, x | (y << 1) | (z << 2) | (w << 3));
113 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
114 int mask = GetMSBitMask(v);
115 if constexpr (N ==
kSize) {
116 return mask == 0x0000FFFF;
118 int constexpr kNumBits = N *
sizeof(
Scalar);
119 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
120 return (mask & kMustBeSet) == kMustBeSet;
126 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
127 int mask = GetMSBitMask(v);
128 if constexpr (N ==
kSize) {
131 int constexpr kNumBits = N *
sizeof(
Scalar);
132 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
133 return (mask & kMayBeSet) != 0;
148 static_assert(N >= 2 && N <= 4,
"Unsupported N");
149 if constexpr (N == 2) {
151 }
else if constexpr (N == 3) {
161 static_assert(N >= 2 && N <= 4,
"Unsupported N");
162 if constexpr (N == 2) {
164 }
else if constexpr (N == 3) {
174 static_assert(N >= 2 && N <= 4,
"Unsupported N");
175 if constexpr (N == 2) {
177 }
else if constexpr (N == 3) {
184 template <
int N = kSize>
186 static_assert(N >= 0 && N <= 4);
187 if constexpr (N == 0) {
189 }
else if constexpr (N == 1) {
190 return Simd{*ptr, 0};
191 }
else if constexpr (N == 2) {
192 __m128i mask = _mm_set_epi32(0, 0, -1, -1);
193 return _mm_maskload_epi32(ptr, mask);
194 }
else if constexpr (N == 3) {
195 __m128i mask = _mm_set_epi32(0, -1, -1, -1);
196 return _mm_maskload_epi32(ptr, mask);
197 }
else if constexpr (N == 4) {
198 return _mm_loadu_si128(
reinterpret_cast<__m128i const*
>(ptr));
204#if MOCHI_ARCH_X64_AVX512
205 return _mm_maskz_loadu_epi32(x64_simd::kLaneMasksS8[n], ptr);
208 return _mm_maskload_epi32(ptr, x64_simd::kLoadMasksS4[n]);
212 template <
int kTupleCount = kSize>
215 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
216 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
217 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
218 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
219 auto a = Simd::Load<kCount0>(ptr).raw;
220 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).raw;
221 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).raw;
223 auto t0 = _mm_blend_epi32(a, b, 0b0100);
224 auto t1 = _mm_blend_epi32(t0, c, 0b0010);
225 out0 = _mm_shuffle_epi32(t1, _MM_SHUFFLE(1, 2, 3, 0));
227 t0 = _mm_blend_epi32(a, b, 0b1001);
228 t1 = _mm_blend_epi32(t0, c, 0b0100);
229 out1 = _mm_shuffle_epi32(t1, _MM_SHUFFLE(2, 3, 0, 1));
231 t0 = _mm_blend_epi32(a, b, 0b0010);
232 t1 = _mm_blend_epi32(c, t0, 0b0110);
233 out2 = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3, 0, 1, 2));
237 return _mm_min_epi32(a.raw, b.raw);
241 return _mm_max_epi32(a.raw, b.raw);
245 return _mm_blendv_epi8(b.raw, a.raw, mask.raw);
249 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
252 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
253 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
256 return _mm_shuffle_epi32(v.raw, x | (y << 2) | (z << 4) | (w << 6));
261 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
264 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
265 return _mm_castps_si128(_mm_shuffle_ps(
266 _mm_castsi128_ps(a.raw),
267 _mm_castsi128_ps(b.raw),
268 x | (y << 2) | (z << 4) | (w << 6)));
271 template <
int N = kSize>
273 static_assert(N >= 0 && N <=
kSize);
274 if constexpr (N == 0) {
275 }
else if constexpr (N <
kSize) {
277 memcpy(ptr, &v,
sizeof(
Scalar) * N);
279 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(ptr), v.raw);
285#if MOCHI_ARCH_X64_AVX512
286 _mm_mask_storeu_epi32(ptr, x64_simd::kLaneMasksS8[n], v.raw);
301 auto mask = _mm_movemask_ps(_mm_castsi128_ps(condition.raw));
303 auto const* tableRow =
304 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableS4[mask]);
305 auto pattern = _mm_load_si128(tableRow);
306 auto packed = _mm_castps_si128(_mm_permutevar_ps(_mm_castsi128_ps(values.raw), pattern));
307 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(ptr), packed);
308 return _mm_popcnt_u32(mask);
311 template <
int kTupleCount = kSize>
313 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
315 auto d = _mm_shuffle_epi32(a.raw, _MM_SHUFFLE(1, 2, 3, 0));
316 auto e = _mm_shuffle_epi32(b.raw, _MM_SHUFFLE(2, 3, 0, 1));
317 auto f = _mm_shuffle_epi32(c.raw, _MM_SHUFFLE(3, 0, 1, 2));
318 auto g = _mm_blend_epi32(_mm_blend_epi32(d, e, 0b0010), f, 0b0100);
319 auto h = _mm_blend_epi32(_mm_blend_epi32(d, e, 0b1001), f, 0b0010);
320 auto i = _mm_blend_epi32(_mm_blend_epi32(d, e, 0b0100), f, 0b1001);
321 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
322 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
323 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
324 Simd::Store<kCount0>(ptr, g);
325 if constexpr (kCount1 > 0) {
326 Simd::Store<kCount1>(ptr + 4, h);
328 if constexpr (kCount2 > 0) {
329 Simd::Store<kCount2>(ptr + 8, i);
334 return _mm_setzero_si128();
338 return _mm_cmplt_epi32(this->
raw, rhs.raw);
342 return _mm_cmpgt_epi32(this->
raw, rhs.raw);
346 return ~(*
this > rhs);
350 return ~(*
this < rhs);
354 return _mm_cmpeq_epi32(a.raw, b.raw);
362 auto mask = GetMSBitMask(
Equal(*
this, rhs));
363 return mask == 0xFFFF;
367 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
373 __m128i ones = _mm_cmpeq_epi32(
raw,
raw);
374 return _mm_xor_si128(
raw, ones);
378 return _mm_sub_epi32(_mm_setzero_si128(),
raw);
382 return _mm_add_epi32(
raw, rhs.raw);
386 return _mm_sub_epi32(
raw, rhs.raw);
390 return _mm_mullo_epi32(
raw, rhs.raw);
394#if MOCHI_ARCH_X64_SVML
395 return _mm_div_epi32(
raw, rhs.raw);
407 return _mm_and_si128(
raw, rhs.raw);
411 return _mm_or_si128(
raw, rhs.raw);
415 return _mm_xor_si128(
raw, rhs.raw);
419 return _mm_slli_epi32(
raw, rhs);
422 template <
int kShift>
424 static_assert(kShift >= 0 && kShift < 32,
"Shift amount out-of-range");
425 return _mm_srai_epi32(a.raw, kShift);
431 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)
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)
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)