21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
29class Simd<double, 2> {
32 Simd(
double a,
double b) :
raw(_mm_set_pd(b, a)) {}
34 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
35 Simd(U a) :
raw(_mm_set_pd1(a)) {}
39 static_assert(i >= 0 && i < 2,
"Index out of range");
45#if MOCHI_COMPILER_MSVC
46 return raw.m128d_f64[i];
54#if MOCHI_COMPILER_MSVC
56 result.raw.m128d_f64[i] = value;
59 static constexpr __m128i kMasks[] = {{-1LL, 0LL}, {0LL, -1LL}};
60 return _mm_blendv_pd(v.raw, _mm_set_pd1(value), _mm_castsi128_pd(kMasks[i]));
66 static_assert(i >= 0 && i < 2,
"Index out of range");
67 if constexpr (i == 0) {
68 return _mm_shuffle_pd(_mm_set1_pd(value), v.raw, 0x02);
70 return _mm_shuffle_pd(v.raw, _mm_set1_pd(value), 0x02);
76 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
77 auto mask = GetMSBitMask(v);
78 if constexpr (N ==
kSize) {
79 return mask == 0x0000FFFF;
81 return (mask & 0x000000FF) == 0x000000FF;
87 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
88 int mask = GetMSBitMask(v);
89 if constexpr (N ==
kSize) {
92 return (mask & 0x000000FF) != 0;
96 template <
int x,
int y>
98 static_assert(x >= 0 && x < 2 && y >= 0 && y < 2,
"invalid blend index");
99 if constexpr (x == 0 && y == 0) {
101 }
else if constexpr (x == 1 && y == 1) {
104 return _mm_blend_pd(a.raw, b.raw, x | (y << 1));
118 return _mm_i64gather_pd(ptr, indices.raw,
sizeof(
double));
121 template <
int N = kSize>
123 static_assert(N >= 0 && N <=
kSize);
124 if constexpr (N == 0) {
126 }
else if constexpr (N == 1) {
127 return Simd{*ptr, 0.0};
129 return _mm_loadu_pd(ptr);
133 static_assert(
sizeof(
long long) == 8);
135#if !MOCHI_ARCH_X64_AVX512
136#if MOCHI_COMPILER_MSVC
139 static constexpr __m128i kLoadMasks[] = {
140 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
141 {-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0},
142 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};
146 static constexpr __m128i kLoadMasks[] = {{0LL, 0LL}, {-1LL, 0LL}, {-1LL, -1LL}};
152#if MOCHI_ARCH_X64_AVX512
153 return _mm_maskz_loadu_pd(x64_simd::kLaneMasksS8[n], ptr);
155 return _mm_maskload_pd(ptr, kLoadMasks[n]);
159 template <
int kTupleCount = kSize>
162 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
163 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 2, 0, 2);
164 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 4, 0, 2);
165 auto a = Simd::Load<2>(ptr).raw;
166 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 2).raw;
167 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 4).raw;
168 out0.raw = _mm_shuffle_pd(a, b, 0b0010);
169 out1.raw = _mm_shuffle_pd(a, c, 0b0001);
170 out2.raw = _mm_shuffle_pd(b, c, 0b0010);
173 template <
int N = kSize>
175 static_assert(N >= 0 && N <=
kSize);
176 if constexpr (N == 0) {
177 }
else if constexpr (N <
kSize) {
179 memcpy(ptr, &v,
sizeof(
Scalar) * N);
181 _mm_storeu_pd(ptr, v.raw);
187#if MOCHI_ARCH_X64_AVX512
188 _mm_mask_storeu_pd(ptr, x64_simd::kLaneMasksS8[n], v.raw);
199 auto mask = _mm_movemask_pd(condition.raw);
200 auto swapped = _mm_shuffle_pd(values.raw, values.raw, 1);
202 _mm_castsi128_pd(_mm_set1_epi32((mask & 1) - 1));
203 _mm_storeu_pd(ptr, _mm_blendv_pd(values.raw, swapped, blendMask));
204 return _mm_popcnt_u32(mask);
207 template <
int kTupleCount = kSize>
209 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
211 Simd::Store<2>(ptr, _mm_shuffle_pd(a.raw, b.raw, 0b00));
212 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 2, 0, 2);
213 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 4, 0, 2);
214 if constexpr (kCount1 > 0) {
215 Simd::Store<kCount1>(ptr + 2, _mm_shuffle_pd(c.raw, a.raw, 0b10));
217 if constexpr (kCount2 > 0) {
218 Simd::Store<kCount2>(ptr + 4, _mm_shuffle_pd(b.raw, c.raw, 0b11));
223 return _mm_blendv_pd(b.raw, a.raw, mask.raw);
227 template <
int x = 0,
int y = 1>
229 static_assert(x >= 0 && x < 2,
"Invalid index");
230 static_assert(y >= 0 && y < 2,
"Invalid index");
231 if constexpr (x == 0 && y == 1) {
234 return _mm_shuffle_pd(v.raw, v.raw, x | (y << 1));
239 return _mm_sqrt_pd(v.raw);
243 return Simd{1.0} / v;
252 return _mm_castsi128_pd(_mm_set1_epi64x(0x8000000000000000LL));
256 return _mm_andnot_pd(SignBitMask().
raw, v.raw);
260 return _mm_min_pd(a.raw, b.raw);
264 return _mm_max_pd(a.raw, b.raw);
268 return _mm_floor_pd(a.raw);
272 return _mm_round_pd(v.raw, _MM_FROUND_TO_NEAREST_INT);
275#if MOCHI_ARCH_X64_SVML
277 return _mm_cos_pd(a.raw);
281 return _mm_sin_pd(a.raw);
285 return _mm_tan_pd(a.raw);
289 return _mm_acos_pd(a.raw);
293 return _mm_asin_pd(a.raw);
297 return _mm_atan_pd(a.raw);
301 return _mm_exp_pd(a.raw);
305 return _mm_log_pd(a.raw);
309 return _mm_tanh_pd(a.raw);
314#if MOCHI_ARCH_X64_FMA
315 return {_mm_fmadd_pd(a.raw, b.raw, c.raw)};
322#if MOCHI_ARCH_X64_FMA
323 return _mm_fmsub_pd(a.raw, b.raw, c.raw);
330#if MOCHI_ARCH_X64_FMA
331 return _mm_fnmadd_pd(a.raw, b.raw, c.raw);
338#if MOCHI_ARCH_X64_FMA
339 return _mm_fnmsub_pd(a.raw, b.raw, c.raw);
347 static_assert(N == 2,
"Unsupported N");
353 static_assert(N == 2,
"Unsupported N");
359 static_assert(N == 2,
"Unsupported N");
365 static_assert(N == 2,
"Unsupported N");
371 static_assert(N == 2,
"Unsupported N");
372#if MOCHI_COMPILER_CLANG
373 return _mm_dp_pd(a.raw, b.raw, -1);
375 return _mm_dp_pd(a.raw, b.raw, 0xFF);
380 return _mm_cmplt_pd(this->
raw, rhs.raw);
384 return _mm_cmpgt_pd(this->
raw, rhs.raw);
388 return _mm_cmple_pd(this->
raw, rhs.raw);
392 return _mm_cmpge_pd(this->
raw, rhs.raw);
396 return _mm_cmpeq_pd(a.raw, b.raw);
400 return _mm_cmpneq_pd(a.raw, b.raw);
404 return _mm_setzero_pd();
408 auto mask = GetMSBitMask(
Equal(
raw, rhs.raw));
409 return mask == 0xFFFF;
420 __m128d ones = _mm_castsi128_pd(_mm_cmpeq_epi32(dummy, dummy));
421 return _mm_xor_pd(
raw, ones);
425 return _mm_xor_pd(
raw, SignBitMask().
raw);
429 return _mm_add_pd(
raw, rhs.raw);
433 return _mm_sub_pd(
raw, rhs.raw);
437 return _mm_mul_pd(
raw, rhs.raw);
441 return _mm_div_pd(
raw, rhs.raw);
445 return _mm_and_pd(
raw, rhs.raw);
449 return _mm_or_pd(
raw, rhs.raw);
453 return _mm_xor_pd(
raw, rhs.raw);
459 return _mm_movemask_epi8(_mm_castpd_si128(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
Scalar operator[](int i) 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)
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)