21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
32 Simd(
float a,
float b,
float c = 0.0f,
float d = 0.0f) :
raw(_mm_set_ps(d, c, b, a)) {}
33 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
34 Simd(U a) :
raw(_mm_set_ps1(a)) {}
38 static_assert(i >= 0 && i < 4,
"Index out of range");
39 if constexpr (i == 0) {
40 return _mm_cvtss_f32(v.raw);
41 }
else if constexpr (i == 1) {
42 return _mm_cvtss_f32(_mm_shuffle_ps(v.raw, v.raw, _MM_SHUFFLE(1, 1, 1, 1)));
43 }
else if constexpr (i == 2) {
44 return _mm_cvtss_f32(_mm_shuffle_ps(v.raw, v.raw, _MM_SHUFFLE(2, 2, 2, 2)));
45 }
else if constexpr (i == 3) {
46 return _mm_cvtss_f32(_mm_shuffle_ps(v.raw, v.raw, _MM_SHUFFLE(3, 3, 3, 3)));
52#if MOCHI_COMPILER_MSVC
53 return v.raw.m128_f32[i];
67#if MOCHI_COMPILER_MSVC
69 result.raw.m128_f32[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_ps(v.raw, _mm_set1_ps(value), _mm_castsi128_ps(kMasks[i]));
85 return _mm_insert_ps(v.raw, _mm_set1_ps(value), i << 4);
90 auto araw = _mm_castps_si128(a.raw);
91 auto v = _mm_insert_epi32(araw, 0x3f800000, 3);
92 return _mm_castsi128_ps(v);
97 auto araw = _mm_castps_si128(a.raw);
98 auto v = _mm_insert_epi32(araw, 0x00000000, 3);
99 return _mm_castsi128_ps(v);
102 template <
int x,
int y,
int z,
int w>
105 x >= 0 && x < 2 && y >= 0 && y < 2 && z >= 0 && z < 2 && w >= 0 && w < 2,
106 "invalid blend index");
107 if constexpr (x == 0 && y == 0 && z == 0 && w == 0) {
109 }
else if constexpr (x == 1 && y == 1 && z == 1 && w == 1) {
112 return _mm_blend_ps(a.raw, b.raw, x | (y << 1) | (z << 2) | (w << 3));
118 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
119 int mask = GetMSBitMask(v);
120 if constexpr (N ==
kSize) {
121 return mask == 0x0000FFFF;
123 int constexpr kNumBits = N *
sizeof(
Scalar);
124 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
125 return (mask & kMustBeSet) == kMustBeSet;
131 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
132 int mask = GetMSBitMask(v);
133 if constexpr (N ==
kSize) {
136 int constexpr kNumBits = N *
sizeof(
Scalar);
137 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
138 return (mask & kMayBeSet) != 0;
143 return _mm_broadcast_ss(p);
151 template <
int N = kSize>
153 static_assert(N >= 0 && N <= 4);
154 if constexpr (N == 0) {
156 }
else if constexpr (N == 1) {
157 return Simd{*ptr, 0.0f};
158 }
else if constexpr (N == 2) {
159 __m128i mask = _mm_set_epi32(0, 0, -1, -1);
160 return _mm_maskload_ps(ptr, mask);
161 }
else if constexpr (N == 3) {
162 __m128i mask = _mm_set_epi32(0, -1, -1, -1);
163 return _mm_maskload_ps(ptr, mask);
164 }
else if constexpr (N == 4) {
165 return _mm_loadu_ps(ptr);
171 return _mm_maskload_ps(ptr, x64_simd::kLoadMasksS4[n]);
175 return _mm_i32gather_ps(ptr, indices.raw,
sizeof(
float));
178 template <
int kTupleCount = kSize>
181 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
182 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
183 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
184 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
185 auto a = Simd::Load<kCount0>(ptr).raw;
186 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 4).raw;
187 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 8).raw;
189 auto t0 = _mm_blend_ps(a, b, 0b0100);
190 auto t1 = _mm_blend_ps(t0, c, 0b0010);
191 out0 = _mm_shuffle_ps(t1, t1, _MM_SHUFFLE(1, 2, 3, 0));
193 t0 = _mm_blend_ps(a, b, 0b1001);
194 t1 = _mm_blend_ps(t0, c, 0b0100);
195 out1 = _mm_shuffle_ps(t1, t1, _MM_SHUFFLE(2, 3, 0, 1));
197 t0 = _mm_blend_ps(a, b, 0b0010);
198 t1 = _mm_blend_ps(c, t0, 0b0110);
199 out2 = _mm_shuffle_ps(t1, t1, _MM_SHUFFLE(3, 0, 1, 2));
204 static_assert(i >= 0 && i <= 3,
"Invalid component index");
205 auto zero = _mm_setzero_si128();
206 auto v = _mm_insert_epi32(zero, 0x3f800000, i);
207 return _mm_castsi128_ps(v);
211 return _mm_blendv_ps(b.raw, a.raw, mask.raw);
215 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
218 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
219 if constexpr (x == 0 && y == 1 && z == 2 && w == 3) {
222 return _mm_shuffle_ps(v.raw, v.raw, x | (y << 2) | (z << 4) | (w << 6));
225 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
228 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
229 return _mm_shuffle_ps(a.raw, b.raw, x | (y << 2) | (z << 4) | (w << 6));
232 template <
int N = kSize>
234 static_assert(N >= 0 && N <=
kSize);
235 if constexpr (N == 0) {
236 }
else if constexpr (N <
kSize) {
238 memcpy(ptr, &v,
sizeof(
Scalar) * N);
240 _mm_storeu_ps(ptr, v.raw);
257 auto mask = _mm_movemask_ps(condition.raw);
259 auto const* tableRow =
260 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableS4[mask]);
261 auto pattern = _mm_load_si128(tableRow);
262 auto packed = _mm_permutevar_ps(values.raw, pattern);
263 _mm_storeu_ps(ptr, packed);
264 return _mm_popcnt_u32(mask);
267 template <
int kTupleCount = kSize>
269 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
271 auto d = _mm_shuffle_ps(a.raw, a.raw, _MM_SHUFFLE(1, 2, 3, 0));
272 auto e = _mm_shuffle_ps(b.raw, b.raw, _MM_SHUFFLE(2, 3, 0, 1));
273 auto f = _mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(3, 0, 1, 2));
274 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 4);
275 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 4, 0, 4);
276 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 8, 0, 4);
277 Simd::Store<kCount0>(ptr, _mm_blend_ps(_mm_blend_ps(d, e, 0b0010), f, 0b0100));
278 if constexpr (kCount1 > 0) {
279 Simd::Store<kCount1>(
280 ptr + 4, _mm_blend_ps(_mm_blend_ps(d, e, 0b1001), f, 0b0010));
282 if constexpr (kCount2 > 0) {
283 Simd::Store<kCount2>(
284 ptr + 8, _mm_blend_ps(_mm_blend_ps(d, e, 0b0100), f, 0b1001));
289 return _mm_sqrt_ps(v.raw);
293 return _mm_rcp_ps(v.raw);
297 return _mm_rsqrt_ps(v.raw);
302 return _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
306 return _mm_andnot_ps(SignBitMask().
raw, v.raw);
310 return _mm_min_ps(a.raw, b.raw);
314 return _mm_max_ps(a.raw, b.raw);
318 return _mm_floor_ps(a.raw);
322 return _mm_round_ps(v.raw, _MM_FROUND_TO_NEAREST_INT);
325#if MOCHI_ARCH_X64_SVML
327 return _mm_cos_ps(a.raw);
331 return _mm_sin_ps(a.raw);
335 return _mm_tan_ps(a.raw);
339 return _mm_acos_ps(a.raw);
343 return _mm_asin_ps(a.raw);
347 return _mm_atan_ps(a.raw);
351 return _mm_exp_ps(a.raw);
355 return _mm_log_ps(a.raw);
359 return _mm_tanh_ps(a.raw);
364#if MOCHI_ARCH_X64_FMA
365 return {_mm_fmadd_ps(a.raw, b.raw, c.raw)};
372#if MOCHI_ARCH_X64_FMA
373 return _mm_fmsub_ps(a.raw, b.raw, c.raw);
380#if MOCHI_ARCH_X64_FMA
381 return _mm_fnmadd_ps(a.raw, b.raw, c.raw);
388#if MOCHI_ARCH_X64_FMA
389 return _mm_fnmsub_ps(a.raw, b.raw, c.raw);
397 static_assert(N >= 2 && N <= 4,
"Unsupported N");
398 if constexpr (N == 2) {
400 }
else if constexpr (N == 3) {
404 return Get<0>(
Min(tmp, Simd::Shuffle<2, 3, 0, 1>(tmp)));
410 static_assert(N >= 2 && N <= 4,
"Unsupported N");
411 if constexpr (N == 2) {
413 }
else if constexpr (N == 3) {
423 static_assert(N >= 2 && N <= 4,
"Unsupported N");
425 if constexpr (N == 2) {
427 }
else if constexpr (N == 3) {
439 static_assert(N >= 2 && N <= 4,
"Unsupported N");
442 if constexpr (N == 2) {
443 return buf[0] * buf[1];
444 }
else if constexpr (N == 3) {
445 return buf[0] * buf[1] * buf[2];
446 }
else if constexpr (N == 4) {
447 return buf[0] * buf[1] * buf[2] * buf[3];
453 static_assert(N >= 2 && N <= 4,
"Unsupported N");
454 if constexpr (N == 2) {
455 return _mm_dp_ps(a.raw, b.raw, 0x3F);
456 }
else if constexpr (N == 3) {
457 return _mm_dp_ps(a.raw, b.raw, 0x7F);
458 }
else if constexpr (N == 4) {
459#if MOCHI_COMPILER_CLANG
460 return _mm_dp_ps(a.raw, b.raw, -1);
462 return _mm_dp_ps(a.raw, b.raw, 0xFF);
468 return _mm_cmplt_ps(this->
raw, rhs.raw);
472 return _mm_cmpgt_ps(this->
raw, rhs.raw);
476 return _mm_cmple_ps(this->
raw, rhs.raw);
480 return _mm_cmpge_ps(this->
raw, rhs.raw);
484 return _mm_cmpeq_ps(a.raw, b.raw);
488 return _mm_cmpneq_ps(a.raw, b.raw);
492 return _mm_setzero_ps();
496 auto mask = GetMSBitMask(
Equal(*
this, rhs));
497 return mask == 0x0000FFFF;
501 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
508 __m128 ones = _mm_castsi128_ps(_mm_cmpeq_epi32(dummy, dummy));
509 return _mm_xor_ps(
raw, ones);
513 return _mm_xor_ps(
raw, SignBitMask().
raw);
517 return _mm_add_ps(
raw, rhs.raw);
521 return _mm_sub_ps(
raw, rhs.raw);
525 return _mm_mul_ps(
raw, rhs.raw);
529 return _mm_div_ps(
raw, rhs.raw);
533 return _mm_and_ps(
raw, rhs.raw);
537 return _mm_or_ps(
raw, rhs.raw);
541 return _mm_xor_ps(
raw, rhs.raw);
547 return _mm_movemask_epi8(_mm_castps_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
#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)