21#if MOCHI_USE_SIMD && MOCHI_ARCH_X64_AVX2
41 :
raw(_mm256_set_ps(h, g, f, e, d, c, b, a)) {}
43 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
44 Simd(U a) :
raw(_mm256_set1_ps(a)) {}
48 :
raw(_mm256_set_m128(high.
raw, low.
raw)) {}
52 static_assert(i >= 0 && i < 8,
"Index out of range");
53 if constexpr (i == 0) {
54 return _mm256_cvtss_f32(v.raw);
55 }
else if constexpr (i == 1) {
56 return _mm256_cvtss_f32(_mm256_shuffle_ps(v.raw, v.raw, 0x01));
57 }
else if constexpr (i == 2) {
58 return _mm256_cvtss_f32(_mm256_shuffle_ps(v.raw, v.raw, 0x02));
59 }
else if constexpr (i == 3) {
60 return _mm256_cvtss_f32(_mm256_shuffle_ps(v.raw, v.raw, 0x03));
61 }
else if constexpr (i == 4) {
62 return _mm256_cvtss_f32(_mm256_permute2f128_ps(v.raw, v.raw, 0x01));
63 }
else if constexpr (i == 5) {
64 auto tmp = _mm256_permute2f128_ps(v.raw, v.raw, 0x01);
65 return _mm256_cvtss_f32(_mm256_shuffle_ps(tmp, tmp, 0x01));
66 }
else if constexpr (i == 6) {
67 auto tmp = _mm256_permute2f128_ps(v.raw, v.raw, 0x01);
68 return _mm256_cvtss_f32(_mm256_shuffle_ps(tmp, tmp, 0x02));
69 }
else if constexpr (i == 7) {
70 auto tmp = _mm256_permute2f128_ps(v.raw, v.raw, 0x01);
71 return _mm256_cvtss_f32(_mm256_shuffle_ps(tmp, tmp, 0x03));
77#if MOCHI_COMPILER_MSVC
78 return v.raw.m256_f32[i];
96 static_assert(iHalf == 0 || iHalf == 1);
97 return _mm256_extractf128_ps(a.raw, iHalf);
102#if MOCHI_COMPILER_MSVC
104 result.raw.m256_f32[i] = value;
107 static constexpr __m256i kMasks[] = {
109 {
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
110 {
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
111 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
112 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
113 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL)},
114 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL)},
115 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL)},
116 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL)}
118 return _mm256_blendv_ps(v.raw, _mm256_set1_ps(value), _mm256_castsi256_ps(kMasks[i]));
125 return Set(v, i, value);
130 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
131 int mask = GetMSBitMask(v);
132 if constexpr (N ==
kSize) {
133 return mask == 0xFFFFFFFF;
135 int constexpr kNumBits = N *
sizeof(
Scalar);
136 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
137 return (mask & kMustBeSet) == kMustBeSet;
143 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
144 int mask = GetMSBitMask(v);
145 if constexpr (N ==
kSize) {
148 int constexpr kNumBits = N *
sizeof(
Scalar);
149 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
150 return (mask & kMayBeSet) != 0;
155 return _mm256_broadcast_ss(p);
163 template <
int N = kSize>
165 static_assert(N >= 0 && N <= 8);
166 if constexpr (N == 0) {
168 }
else if constexpr (N == 1) {
169 return Simd{*ptr, 0.0f};
170 }
else if constexpr (N == 2) {
171 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, 0, -1, -1);
172 return _mm256_maskload_ps(ptr, mask);
173 }
else if constexpr (N == 3) {
174 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, -1, -1, -1);
175 return _mm256_maskload_ps(ptr, mask);
176 }
else if constexpr (N == 4) {
177 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, -1, -1, -1, -1);
178 return _mm256_maskload_ps(ptr, mask);
179 }
else if constexpr (N == 5) {
180 __m256i mask = _mm256_set_epi32(0, 0, 0, -1, -1, -1, -1, -1);
181 return _mm256_maskload_ps(ptr, mask);
182 }
else if constexpr (N == 6) {
183 __m256i mask = _mm256_set_epi32(0, 0, -1, -1, -1, -1, -1, -1);
184 return _mm256_maskload_ps(ptr, mask);
185 }
else if constexpr (N == 7) {
186 __m256i mask = _mm256_set_epi32(0, -1, -1, -1, -1, -1, -1, -1);
187 return _mm256_maskload_ps(ptr, mask);
188 }
else if constexpr (N == 8) {
189 return _mm256_loadu_ps(ptr);
195 return _mm256_maskload_ps(ptr, x64_simd::kLoadMasksS8[n]);
199 return _mm256_i32gather_ps(ptr, indices.raw,
sizeof(
float));
202 template <
int N = kSize>
204 static_assert(N >= 0 && N <=
kSize);
205 if constexpr (N == 0) {
206 }
else if constexpr (N <
kSize) {
208 memcpy(ptr, &v,
sizeof(
Scalar) * N);
210 _mm256_storeu_ps(ptr, v.raw);
214 template <
int kTupleCount = kSize>
217 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
218 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 8);
219 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 8, 0, 8);
220 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 16, 0, 8);
223 auto a = Simd::Load<kCount0>(ptr).raw;
225 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 8).raw;
227 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 16).raw;
229 auto d = _mm256_blend_ps(a, b, 0b10010010);
230 auto e = _mm256_blend_ps(d, c, 0b00100100);
231 auto f = _mm256_permute2f128_ps(e, e, 0x01);
232 auto g = _mm256_blend_ps(e, f, 0b01000100);
233 out0.raw = _mm256_shuffle_ps(g, g, _MM_SHUFFLE(1, 2, 3, 0));
235 d = _mm256_blend_ps(a, b, 0b00100100);
236 e = _mm256_blend_ps(d, c, 0b01001001);
237 f = _mm256_permute2f128_ps(e, e, 0x01);
238 g = _mm256_blend_ps(e, f, 0b10011001);
239 out1.raw = _mm256_shuffle_ps(g, g, _MM_SHUFFLE(2, 3, 0, 1));
241 d = _mm256_blend_ps(a, b, 0b01001001);
242 e = _mm256_blend_ps(d, c, 0b10010010);
243 f = _mm256_permute2f128_ps(e, e, 0x01);
244 g = _mm256_blend_ps(e, f, 0b00100010);
245 out2.raw = _mm256_shuffle_ps(g, g, _MM_SHUFFLE(3, 0, 1, 2));
267 auto mask = _mm256_movemask_ps(condition.raw);
269 auto const* tableRow =
270 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableS8[mask]);
271 auto pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
272 auto packed = _mm256_permutevar8x32_ps(values.raw, pattern);
273 _mm256_storeu_ps(ptr, packed);
274 return _mm_popcnt_u32(mask);
277 template <
int kTupleCount = kSize>
279 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
283 auto d = _mm256_shuffle_ps(a.raw, a.raw, _MM_SHUFFLE(1, 2, 3, 0));
284 auto e = _mm256_shuffle_ps(b.raw, b.raw, _MM_SHUFFLE(2, 3, 0, 1));
285 auto f = _mm256_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(3, 0, 1, 2));
286 auto g = _mm256_blend_ps(d, e, 0b00100010);
287 g = _mm256_blend_ps(g, f, 0b01000100);
288 auto h = _mm256_blend_ps(d, e, 0b10011001);
289 h = _mm256_blend_ps(h, f, 0b00100010);
290 h = _mm256_permute2f128_ps(h, h, 0x01);
291 auto i = _mm256_blend_ps(d, e, 0b01000100);
292 i = _mm256_blend_ps(i, f, 0b10011001);
293 d = _mm256_blend_ps(g, h, 0b11110000);
294 e = _mm256_blend_ps(i, g, 0b11110000);
295 f = _mm256_blend_ps(h, i, 0b11110000);
296 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 8);
297 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 8, 0, 8);
298 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 16, 0, 8);
299 Simd::Store<kCount0>(ptr, d);
300 if constexpr (kCount1 > 0) {
301 Simd::Store<kCount1>(ptr + 8, e);
303 if constexpr (kCount2 > 0) {
304 Simd::Store<kCount2>(ptr + 16, f);
309 return _mm256_blendv_ps(b.raw, a.raw, mask.raw);
313 return _mm256_sqrt_ps(v.raw);
317 return _mm256_rcp_ps(v.raw);
321 return _mm256_rsqrt_ps(v.raw);
326 return _mm256_castsi256_ps(_mm256_set1_epi32(0x80000000));
330 return _mm256_andnot_ps(SignBitMask().
raw, v.raw);
334 return _mm256_min_ps(a.raw, b.raw);
338 return _mm256_max_ps(a.raw, b.raw);
342 return _mm256_floor_ps(a.raw);
346 return _mm256_round_ps(v.raw, _MM_FROUND_TO_NEAREST_INT);
349#if MOCHI_ARCH_X64_SVML
351 return _mm256_cos_ps(a.raw);
355 return _mm256_sin_ps(a.raw);
359 return _mm256_tan_ps(a.raw);
363 return _mm256_acos_ps(a.raw);
367 return _mm256_asin_ps(a.raw);
371 return _mm256_atan_ps(a.raw);
375 return _mm256_exp_ps(a.raw);
379 return _mm256_log_ps(a.raw);
383 return _mm256_tanh_ps(a.raw);
388#if MOCHI_ARCH_X64_FMA
389 return _mm256_fmadd_ps(a.raw, b.raw, c.raw);
396#if MOCHI_ARCH_X64_FMA
397 return _mm256_fmsub_ps(a.raw, b.raw, c.raw);
404#if MOCHI_ARCH_X64_FMA
405 return _mm256_fnmadd_ps(a.raw, b.raw, c.raw);
412#if MOCHI_ARCH_X64_FMA
413 return _mm256_fnmsub_ps(a.raw, b.raw, c.raw);
421 static_assert(N >= 2 && N <= 8,
"Unsupported N");
423 if constexpr (N >= 2 && N <= 4) {
428 if constexpr (N != 8) {
430 auto inf = HalfT{std::numeric_limits<Scalar>::infinity()};
431 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(inf, hi);
433 return HalfT::HMin<4>(HalfT::Min(lo, hi));
439 static_assert(N >= 2 && N <= 8,
"Unsupported N");
441 if constexpr (N >= 2 && N <= 4) {
446 if constexpr (N != 8) {
448 auto inf = HalfT{std::numeric_limits<Scalar>::infinity()};
449 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(-inf, hi);
451 return HalfT::HMax<4>(HalfT::Max(lo, hi));
457 static_assert(N >= 2 && N <= 8,
"Unsupported N");
459 if constexpr (N >= 2 && N <= 4) {
461 }
else if constexpr (N == 5) {
464 }
else if constexpr (N == 6) {
466 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) +
Get<2>(a) +
Get<3>(a);
467 }
else if constexpr (N == 7) {
469 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) + HalfT::Get<2>(tmp) +
Get<3>(a);
470 }
else if constexpr (N == 8) {
480 static_assert(N == 8,
"smaller dot products not yet supported on Vec8f");
481#if MOCHI_COMPILER_CLANG
482 auto ab_half = _mm256_dp_ps(a.raw, b.raw, -1);
484 auto ab_half = _mm256_dp_ps(a.raw, b.raw, 0xFF);
486 auto lo_ab = _mm256_extractf128_ps(ab_half, 0);
487 auto hi_ab = _mm256_extractf128_ps(ab_half, 1);
488 auto my_dot = _mm_add_ps(lo_ab, hi_ab);
489 return _mm256_set_m128(my_dot, my_dot);
493 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_LT_OQ);
497 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_GT_OQ);
501 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_LE_OQ);
505 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_GE_OQ);
509 return _mm256_cmp_ps(a.raw, b.raw, _CMP_EQ_OQ);
513 return _mm256_cmp_ps(a.raw, b.raw, _CMP_NEQ_UQ);
517 return _mm256_setzero_ps();
521 auto mask = GetMSBitMask(
Equal(*
this, rhs));
522 return mask == 0xFFFFFFFF;
526 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
531 auto ones = _mm256_castsi256_ps(_mm256_set1_epi32(-1));
532 return _mm256_xor_ps(
raw, ones);
536 return Zero() - *
this;
540 return _mm256_add_ps(
raw, rhs.raw);
544 return _mm256_sub_ps(
raw, rhs.raw);
548 return _mm256_mul_ps(
raw, rhs.raw);
552 return _mm256_div_ps(
raw, rhs.raw);
556 return _mm256_and_ps(
raw, rhs.raw);
560 return _mm256_or_ps(
raw, rhs.raw);
564 return _mm256_xor_ps(
raw, rhs.raw);
570 return _mm256_movemask_epi8(_mm256_castps_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)
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)
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)