21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
29class Simd<double, 4> {
32 Simd(
double a,
double b,
double c = 0.0,
double d = 0.0)
33 :
raw(_mm256_set_pd(d, c, b, a)) {}
34 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
35 Simd(U a) :
raw(_mm256_set1_pd(a)) {}
39 :
raw(_mm256_set_m128d(high.
raw, low.
raw)) {}
43 static_assert(i >= 0 && i < 4,
"Index out of range");
44 if constexpr (i == 0) {
45 return _mm256_cvtsd_f64(v.raw);
46 }
else if constexpr (i == 1) {
47 return _mm256_cvtsd_f64(_mm256_shuffle_pd(v.raw, v.raw, 0x01));
48 }
else if constexpr (i == 2) {
49 return _mm256_cvtsd_f64(_mm256_permute2f128_pd(v.raw, v.raw, 0x01));
50 }
else if constexpr (i == 3) {
51 auto tmp = _mm256_permute2f128_pd(v.raw, v.raw, 0x01);
52 return _mm256_cvtsd_f64(_mm256_shuffle_pd(tmp, tmp, 0x01));
58#if MOCHI_COMPILER_MSVC
59 return v.raw.m256d_f64[i];
73#if MOCHI_COMPILER_MSVC
75 result.raw.m256d_f64[i] = value;
78 static constexpr __m256i kMasks[] = {
79 {-1LL, 0LL, 0LL, 0LL}, {0LL, -1LL, 0LL, 0LL}, {0LL, 0LL, -1LL, 0LL}, {0LL, 0LL, 0LL, -1LL}};
80 return _mm256_blendv_pd(v.raw, _mm256_set1_pd(value), _mm256_castsi256_pd(kMasks[i]));
86 static_assert(i >= 0 && i <
kSize,
"Index out of range");
87 return Set(v, i, value);
92 static_assert(iHalf == 0 || iHalf == 1);
93 return _mm256_extractf128_pd(a.raw, iHalf);
98 auto araw = _mm256_castpd_si256(a.raw);
99 auto v = _mm256_insert_epi64(araw, 0x3FF0000000000000LL, 3);
100 return _mm256_castsi256_pd(v);
105 auto araw = _mm256_castpd_si256(a.raw);
106 auto v = _mm256_insert_epi64(araw, 0, 3);
107 return _mm256_castsi256_pd(v);
110 template <
int x,
int y,
int z,
int w>
113 x >= 0 && x < 2 && y >= 0 && y < 2 && z >= 0 && z < 2 && w >= 0 && w < 2,
114 "invalid blend index");
115 if constexpr (x == 0 && y == 0 && z == 0 && w == 0) {
117 }
else if constexpr (x == 1 && y == 1 && z == 1 && w == 1) {
120 return _mm256_blend_pd(a.raw, b.raw, x | (y << 1) | (z << 2) | (w << 3));
126 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
127 int mask = GetMSBitMask(v);
128 if constexpr (N ==
kSize) {
129 return mask == 0xFFFFFFFF;
131 int constexpr kNumBits = N *
sizeof(
Scalar);
132 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
133 return (mask & kMustBeSet) == kMustBeSet;
139 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
140 int mask = GetMSBitMask(v);
141 if constexpr (N ==
kSize) {
144 int constexpr kNumBits = N *
sizeof(
Scalar);
145 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
146 return (mask & kMayBeSet) != 0;
151 return _mm256_broadcast_sd(p);
159 template <
int N = kSize>
161 static_assert(N >= 0 && N <= 4);
162 if constexpr (N == 0) {
164 }
else if constexpr (N == 1) {
165 return Simd{*ptr, 0.0};
166 }
else if constexpr (N == 2) {
167 __m256i mask = _mm256_set_epi64x(0, 0, -1, -1);
168 return _mm256_maskload_pd(ptr, mask);
169 }
else if constexpr (N == 3) {
170 __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);
171 return _mm256_maskload_pd(ptr, mask);
173 return _mm256_loadu_pd(ptr);
177#if MOCHI_COMPILER_MSVC
180 static constexpr __m256i kLoadMasks[] = {
181 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
182 {-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
183 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
184 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0},
185 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
189 static constexpr __m256i kLoadMasks[] = {
190 {0LL, 0LL, 0LL, 0LL},
191 {-1LL, 0LL, 0LL, 0LL},
192 {-1LL, -1LL, 0LL, 0LL},
193 {-1LL, -1LL, -1LL, 0LL},
194 {-1LL, -1LL, -1LL, -1LL}};
199 return _mm256_maskload_pd(ptr, kLoadMasks[n]);
203 return _mm256_i32gather_pd(ptr, indices.raw,
sizeof(
double));
207 return _mm256_i64gather_pd(ptr, indices.raw,
sizeof(
double));
210 template <
int kTupleCount = kSize>
213 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
214 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
215 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
216 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
217 auto a = Simd::Load<kCount0>(ptr).raw;
218 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).raw;
219 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).raw;
221 auto d = _mm256_blend_pd(a, b, 0b0100);
222 d = _mm256_blend_pd(d, c, 0b0010);
223 auto e = _mm256_permute2f128_pd(d, d, 0x01);
224 out0.raw = _mm256_blend_pd(d, e, 0b1010);
226 d = _mm256_blend_pd(a, b, 0b1001);
227 d = _mm256_blend_pd(d, c, 0b0100);
228 out1.raw = _mm256_shuffle_pd(d, d, 0b0101);
230 d = _mm256_blend_pd(a, b, 0b0010);
231 d = _mm256_blend_pd(d, c, 0b1001);
232 e = _mm256_permute2f128_pd(d, d, 0x01);
233 out2.raw = _mm256_blend_pd(d, e, 0b0101);
238 static_assert(i >= 0 && i <= 3,
"Invalid component index");
239 auto zero = _mm256_setzero_si256();
240 auto v = _mm256_insert_epi64(zero, 0x3FF0000000000000LL, i);
241 return _mm256_castsi256_pd(v);
244 template <
int N = kSize>
246 static_assert(N >= 0 && N <=
kSize);
247 if constexpr (N == 0) {
248 }
else if constexpr (N <
kSize) {
250 memcpy(ptr, &v,
sizeof(
Scalar) * N);
252 _mm256_storeu_pd(ptr, v.raw);
269 auto mask = _mm256_movemask_pd(condition.raw);
271 auto const* tableRow =
272 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableD4[mask]);
273 __m256i pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
274 __m256i packed = _mm256_permutevar8x32_epi32(_mm256_castpd_si256(values.raw), pattern);
275 _mm256_storeu_pd(ptr, _mm256_castsi256_pd(packed));
276 return _mm_popcnt_u32(mask);
279 template <
int kTupleCount = kSize>
281 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
283 auto d = _mm256_shuffle_pd(b.raw, b.raw, 0b0101);
284 auto e = _mm256_blend_pd(a.raw, c.raw, 0b0101);
285 e = _mm256_permute2f128_pd(e, e, 0x01);
286 auto f = _mm256_blend_pd(a.raw, d, 0b1010);
287 auto g = _mm256_blend_pd(d, c.raw, 0b1010);
288 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
289 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
290 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
291 Simd::Store<kCount0>(ptr, _mm256_blend_pd(f, e, 0b1100));
292 if constexpr (kCount1 > 0) {
293 Simd::Store<kCount1>(ptr + 4, _mm256_blend_pd(g, f, 0b1100));
295 if constexpr (kCount2 > 0) {
296 Simd::Store<kCount2>(ptr + 8, _mm256_blend_pd(e, g, 0b1100));
301 return _mm256_blendv_pd(b.raw, a.raw, mask.raw);
305 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
307 static_assert(x >= 0 && x < 4,
"Invalid index");
308 static_assert(y >= 0 && y < 4,
"Invalid index");
309 static_assert(z >= 0 && z < 4,
"Invalid index");
310 static_assert(w >= 0 && w < 4,
"Invalid index");
311 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
314 return _mm256_permute4x64_pd(v.raw, x | (y << 2) | (z << 4) | (w << 6));
319 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
321 static_assert(x >= 0 && x < 4,
"Invalid index");
322 static_assert(y >= 0 && y < 4,
"Invalid index");
323 static_assert(z >= 0 && z < 4,
"Invalid index");
324 static_assert(w >= 0 && w < 4,
"Invalid index");
329 return _mm256_sqrt_pd(v.raw);
333 return Simd{1.0} / v;
342 return _mm256_castsi256_pd(_mm256_set1_epi64x(0x8000000000000000LL));
346 return _mm256_andnot_pd(SignBitMask().
raw, v.raw);
350 return _mm256_min_pd(a.raw, b.raw);
354 return _mm256_max_pd(a.raw, b.raw);
358 return _mm256_floor_pd(a.raw);
362 return _mm256_round_pd(v.raw, _MM_FROUND_TO_NEAREST_INT);
365#if MOCHI_ARCH_X64_SVML
367 return _mm256_cos_pd(a.raw);
371 return _mm256_sin_pd(a.raw);
375 return _mm256_tan_pd(a.raw);
379 return _mm256_acos_pd(a.raw);
383 return _mm256_asin_pd(a.raw);
387 return _mm256_atan_pd(a.raw);
391 return _mm256_exp_pd(a.raw);
395 return _mm256_log_pd(a.raw);
399 return _mm256_tanh_pd(a.raw);
404#if MOCHI_ARCH_X64_FMA
405 return {_mm256_fmadd_pd(a.raw, b.raw, c.raw)};
412#if MOCHI_ARCH_X64_FMA
413 return _mm256_fmsub_pd(a.raw, b.raw, c.raw);
420#if MOCHI_ARCH_X64_FMA
421 return _mm256_fnmadd_pd(a.raw, b.raw, c.raw);
428#if MOCHI_ARCH_X64_FMA
429 return _mm256_fnmsub_pd(a.raw, b.raw, c.raw);
437 static_assert(N >= 2 && N <= 4,
"Unsupported N");
439 if constexpr (N == 2) {
441 }
else if constexpr (N == 3) {
444 return HalfT::Get<0>(HalfT::Min(HalfT::Min(lo, HalfT::Broadcast<1>(lo)), hi));
452 static_assert(N >= 2 && N <= 4,
"Unsupported N");
454 if constexpr (N == 2) {
456 }
else if constexpr (N == 3) {
459 return HalfT::Get<0>(HalfT::Max(HalfT::Max(lo, HalfT::Broadcast<1>(lo)), hi));
467 static_assert(N >= 2 && N <= 4,
"Unsupported N");
468 if constexpr (N == 2) {
470 }
else if constexpr (N == 3) {
473 return HalfT::Get<0>(tmp) +
Get<1>(a);
474 }
else if constexpr (N == 4) {
480 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp);
486 static_assert(N >= 2 && N <= 4,
"Unsupported N");
489 if constexpr (N == 2) {
490 return buf[0] * buf[1];
491 }
else if constexpr (N == 3) {
492 return buf[0] * buf[1] * buf[2];
493 }
else if constexpr (N == 4) {
494 return buf[0] * buf[1] * buf[2] * buf[3];
500 static_assert(N >= 2 && N <= 4,
"Unsupported N");
505 return _mm256_cmp_pd(this->
raw, rhs.raw, _CMP_LT_OQ);
509 return _mm256_cmp_pd(this->
raw, rhs.raw, _CMP_GT_OQ);
513 return _mm256_cmp_pd(this->
raw, rhs.raw, _CMP_LE_OQ);
517 return _mm256_cmp_pd(this->
raw, rhs.raw, _CMP_GE_OQ);
521 return _mm256_cmp_pd(a.raw, b.raw, _CMP_EQ_OQ);
525 return _mm256_cmp_pd(a.raw, b.raw, _CMP_NEQ_UQ);
529 return _mm256_setzero_pd();
533 auto mask = GetMSBitMask(
Equal(*
this, rhs));
534 return mask == 0xFFFFFFFF;
538 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
543 auto ones = _mm256_castsi256_pd(_mm256_set1_epi64x(-1));
544 return _mm256_xor_pd(
raw, ones);
548 return _mm256_xor_pd(
raw, SignBitMask().
raw);
552 return _mm256_add_pd(
raw, rhs.raw);
556 return _mm256_sub_pd(
raw, rhs.raw);
560 return _mm256_mul_pd(
raw, rhs.raw);
564 return _mm256_div_pd(
raw, rhs.raw);
568 return _mm256_and_pd(
raw, rhs.raw);
572 return _mm256_or_pd(
raw, rhs.raw);
576 return _mm256_xor_pd(
raw, rhs.raw);
582 return _mm256_movemask_epi8(_mm256_castpd_si256(a.raw));
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
Simd operator>(Simd rhs) 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,...)
T Dot(Simd< T, N > a, Simd< T, N > b)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
V LoadIndexed(typename V::Scalar const *ptr, Simd< I, V::kSize > indices)
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
constexpr auto MulAdd(A a, B b, C c)
Simd< T, N > Tanh(Simd< T, N > a)
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)
constexpr auto MulSub(A a, B b, C c)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
constexpr T Select(bool condition, T a, T b)
Simd< T, N > Ln(Simd< T, N > a)
constexpr auto NegMulAdd(A a, B b, C c)
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)
Simd< T, N > FastRound(Simd< T, N > a)
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
void Store(T *ptr, Simd< T, N > a)
Simd< T, N > RcpSqrtApprox(Simd< T, N > a)
constexpr T RcpApprox(T a)
constexpr auto NegMulSub(A a, B b, C c)
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
V Load(typename V::Scalar const *ptr)
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)