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 v.raw.m128i_i32[i];
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));
205 return _mm_maskload_epi32(ptr, x64_simd::kLoadMasksS4[n]);
208 template <
int kTupleCount = kSize>
211 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
212 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
213 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
214 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
215 auto a = Simd::Load<kCount0>(ptr).raw;
216 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).raw;
217 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).raw;
219 auto t0 = _mm_blend_epi32(a, b, 0b0100);
220 auto t1 = _mm_blend_epi32(t0, c, 0b0010);
221 out0 = _mm_shuffle_epi32(t1, _MM_SHUFFLE(1, 2, 3, 0));
223 t0 = _mm_blend_epi32(a, b, 0b1001);
224 t1 = _mm_blend_epi32(t0, c, 0b0100);
225 out1 = _mm_shuffle_epi32(t1, _MM_SHUFFLE(2, 3, 0, 1));
227 t0 = _mm_blend_epi32(a, b, 0b0010);
228 t1 = _mm_blend_epi32(c, t0, 0b0110);
229 out2 = _mm_shuffle_epi32(t1, _MM_SHUFFLE(3, 0, 1, 2));
233 return _mm_min_epi32(a.raw, b.raw);
237 return _mm_max_epi32(a.raw, b.raw);
241 return _mm_blendv_epi8(b.raw, a.raw, mask.raw);
245 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
248 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
249 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
252 return _mm_shuffle_epi32(v.raw, x | (y << 2) | (z << 4) | (w << 6));
257 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
260 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
261 return _mm_castps_si128(_mm_shuffle_ps(
262 _mm_castsi128_ps(a.raw),
263 _mm_castsi128_ps(b.raw),
264 x | (y << 2) | (z << 4) | (w << 6)));
267 template <
int N = kSize>
269 static_assert(N >= 0 && N <=
kSize);
270 if constexpr (N == 0) {
271 }
else if constexpr (N <
kSize) {
273 memcpy(ptr, &v,
sizeof(
Scalar) * N);
275 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(ptr), v.raw);
293 auto mask = _mm_movemask_ps(_mm_castsi128_ps(condition.raw));
295 auto const* tableRow =
296 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableS4[mask]);
297 auto pattern = _mm_load_si128(tableRow);
298 auto packed = _mm_castps_si128(_mm_permutevar_ps(_mm_castsi128_ps(values.raw), pattern));
299 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(ptr), packed);
300 return _mm_popcnt_u32(mask);
303 template <
int kTupleCount = kSize>
305 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
307 auto d = _mm_shuffle_epi32(a.raw, _MM_SHUFFLE(1, 2, 3, 0));
308 auto e = _mm_shuffle_epi32(b.raw, _MM_SHUFFLE(2, 3, 0, 1));
309 auto f = _mm_shuffle_epi32(c.raw, _MM_SHUFFLE(3, 0, 1, 2));
310 auto g = _mm_blend_epi32(_mm_blend_epi32(d, e, 0b0010), f, 0b0100);
311 auto h = _mm_blend_epi32(_mm_blend_epi32(d, e, 0b1001), f, 0b0010);
312 auto i = _mm_blend_epi32(_mm_blend_epi32(d, e, 0b0100), f, 0b1001);
313 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
314 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
315 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
316 Simd::Store<kCount0>(ptr, g);
317 if constexpr (kCount1 > 0) {
318 Simd::Store<kCount1>(ptr + 4, h);
320 if constexpr (kCount2 > 0) {
321 Simd::Store<kCount2>(ptr + 8, i);
326 return _mm_setzero_si128();
330 return _mm_cmplt_epi32(this->
raw, rhs.raw);
334 return _mm_cmpgt_epi32(this->
raw, rhs.raw);
338 return ~(*
this > rhs);
342 return ~(*
this < rhs);
346 return _mm_cmpeq_epi32(a.raw, b.raw);
354 auto mask = GetMSBitMask(
Equal(*
this, rhs));
355 return mask == 0xFFFF;
359 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
365 __m128i ones = _mm_cmpeq_epi32(
raw,
raw);
366 return _mm_xor_si128(
raw, ones);
370 return _mm_sub_epi32(_mm_setzero_si128(),
raw);
374 return _mm_add_epi32(
raw, rhs.raw);
378 return _mm_sub_epi32(
raw, rhs.raw);
382 return _mm_mullo_epi32(
raw, rhs.raw);
386#if MOCHI_ARCH_X64_SVML
387 return _mm_div_epi32(
raw, rhs.raw);
399 return _mm_and_si128(
raw, rhs.raw);
403 return _mm_or_si128(
raw, rhs.raw);
407 return _mm_xor_si128(
raw, rhs.raw);
411 return _mm_slli_epi32(
raw, rhs);
414 template <
int kShift>
416 return _mm_srli_epi32(a.raw, kShift);
422 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
#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)