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");
58#if MOCHI_COMPILER_MSVC
59 return raw.m256_f32[i];
67 static_assert(iHalf == 0 || iHalf == 1);
68 return _mm256_extractf128_ps(a.raw, iHalf);
73#if MOCHI_COMPILER_MSVC
75 result.raw.m256_f32[i] = value;
78 static constexpr __m256i kMasks[] = {
80 {
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
81 {
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
82 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
83 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL)},
84 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL),
static_cast<long long>(0x0000000000000000LL)},
85 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL),
static_cast<long long>(0x0000000000000000LL)},
86 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x00000000FFFFFFFFLL)},
87 {
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0x0000000000000000LL),
static_cast<long long>(0xFFFFFFFF00000000LL)}
89 return _mm256_blendv_ps(v.raw, _mm256_set1_ps(value), _mm256_castsi256_ps(kMasks[i]));
96 return Set(v, i, value);
101 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
102 int mask = GetMSBitMask(v);
103 if constexpr (N ==
kSize) {
104 return mask == 0xFFFFFFFF;
106 int constexpr kNumBits = N *
sizeof(
Scalar);
107 auto constexpr kMustBeSet = (1UL << kNumBits) - 1;
108 return (mask & kMustBeSet) == kMustBeSet;
114 static_assert(N >= 1 && N <=
kSize,
"Unsupported N");
115 int mask = GetMSBitMask(v);
116 if constexpr (N ==
kSize) {
119 int constexpr kNumBits = N *
sizeof(
Scalar);
120 auto constexpr kMayBeSet = (1UL << kNumBits) - 1;
121 return (mask & kMayBeSet) != 0;
126 return _mm256_broadcast_ss(p);
134 template <
int N = kSize>
136 static_assert(N >= 0 && N <= 8);
137 if constexpr (N == 0) {
139 }
else if constexpr (N == 1) {
140 return Simd{*ptr, 0.0f};
141 }
else if constexpr (N == 2) {
142 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, 0, -1, -1);
143 return _mm256_maskload_ps(ptr, mask);
144 }
else if constexpr (N == 3) {
145 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, 0, -1, -1, -1);
146 return _mm256_maskload_ps(ptr, mask);
147 }
else if constexpr (N == 4) {
148 __m256i mask = _mm256_set_epi32(0, 0, 0, 0, -1, -1, -1, -1);
149 return _mm256_maskload_ps(ptr, mask);
150 }
else if constexpr (N == 5) {
151 __m256i mask = _mm256_set_epi32(0, 0, 0, -1, -1, -1, -1, -1);
152 return _mm256_maskload_ps(ptr, mask);
153 }
else if constexpr (N == 6) {
154 __m256i mask = _mm256_set_epi32(0, 0, -1, -1, -1, -1, -1, -1);
155 return _mm256_maskload_ps(ptr, mask);
156 }
else if constexpr (N == 7) {
157 __m256i mask = _mm256_set_epi32(0, -1, -1, -1, -1, -1, -1, -1);
158 return _mm256_maskload_ps(ptr, mask);
159 }
else if constexpr (N == 8) {
160 return _mm256_loadu_ps(ptr);
166#if MOCHI_ARCH_X64_AVX512
167 return _mm256_maskz_loadu_ps(x64_simd::kLaneMasksS8[n], ptr);
169 return _mm256_maskload_ps(ptr, x64_simd::kLoadMasksS8[n]);
174 return _mm256_i32gather_ps(ptr, indices.raw,
sizeof(
float));
177 template <
int N = kSize>
179 static_assert(N >= 0 && N <=
kSize);
180 if constexpr (N == 0) {
181 }
else if constexpr (N <
kSize) {
183 memcpy(ptr, &v,
sizeof(
Scalar) * N);
185 _mm256_storeu_ps(ptr, v.raw);
189 template <
int kTupleCount = kSize>
192 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
193 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 8);
194 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 8, 0, 8);
195 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 16, 0, 8);
198 auto a = Simd::Load<kCount0>(ptr).raw;
200 auto b = Simd::Load<kCount1>(kCount1 == 0 ? ptr : ptr + 8).raw;
202 auto c = Simd::Load<kCount2>(kCount2 == 0 ? ptr : ptr + 16).raw;
204 auto d = _mm256_blend_ps(a, b, 0b10010010);
205 auto e = _mm256_blend_ps(d, c, 0b00100100);
206 auto f = _mm256_permute2f128_ps(e, e, 0x01);
207 auto g = _mm256_blend_ps(e, f, 0b01000100);
208 out0.raw = _mm256_shuffle_ps(g, g, _MM_SHUFFLE(1, 2, 3, 0));
210 d = _mm256_blend_ps(a, b, 0b00100100);
211 e = _mm256_blend_ps(d, c, 0b01001001);
212 f = _mm256_permute2f128_ps(e, e, 0x01);
213 g = _mm256_blend_ps(e, f, 0b10011001);
214 out1.raw = _mm256_shuffle_ps(g, g, _MM_SHUFFLE(2, 3, 0, 1));
216 d = _mm256_blend_ps(a, b, 0b01001001);
217 e = _mm256_blend_ps(d, c, 0b10010010);
218 f = _mm256_permute2f128_ps(e, e, 0x01);
219 g = _mm256_blend_ps(e, f, 0b00100010);
220 out2.raw = _mm256_shuffle_ps(g, g, _MM_SHUFFLE(3, 0, 1, 2));
225#if MOCHI_ARCH_X64_AVX512
226 _mm256_mask_storeu_ps(ptr, x64_simd::kLaneMasksS8[n], v.raw);
245 auto mask = _mm256_movemask_ps(condition.raw);
247 auto const* tableRow =
248 reinterpret_cast<__m128i const*
>(x64_simd::kStoreSelectedShuffleTableS8[mask]);
249 auto pattern = _mm256_cvtepu8_epi32(_mm_loadl_epi64(tableRow));
250 auto packed = _mm256_permutevar8x32_ps(values.raw, pattern);
251 _mm256_storeu_ps(ptr, packed);
252 return _mm_popcnt_u32(mask);
255 template <
int kTupleCount = kSize>
257 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
261 auto d = _mm256_shuffle_ps(a.raw, a.raw, _MM_SHUFFLE(1, 2, 3, 0));
262 auto e = _mm256_shuffle_ps(b.raw, b.raw, _MM_SHUFFLE(2, 3, 0, 1));
263 auto f = _mm256_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(3, 0, 1, 2));
264 auto g = _mm256_blend_ps(d, e, 0b00100010);
265 g = _mm256_blend_ps(g, f, 0b01000100);
266 auto h = _mm256_blend_ps(d, e, 0b10011001);
267 h = _mm256_blend_ps(h, f, 0b00100010);
268 h = _mm256_permute2f128_ps(h, h, 0x01);
269 auto i = _mm256_blend_ps(d, e, 0b01000100);
270 i = _mm256_blend_ps(i, f, 0b10011001);
271 d = _mm256_blend_ps(g, h, 0b11110000);
272 e = _mm256_blend_ps(i, g, 0b11110000);
273 f = _mm256_blend_ps(h, i, 0b11110000);
274 constexpr int kCount0 =
Clamp(kTupleCount * 3 - 0, 0, 8);
275 constexpr int kCount1 =
Clamp(kTupleCount * 3 - 8, 0, 8);
276 constexpr int kCount2 =
Clamp(kTupleCount * 3 - 16, 0, 8);
277 Simd::Store<kCount0>(ptr, d);
278 if constexpr (kCount1 > 0) {
279 Simd::Store<kCount1>(ptr + 8, e);
281 if constexpr (kCount2 > 0) {
282 Simd::Store<kCount2>(ptr + 16, f);
287 return _mm256_blendv_ps(b.raw, a.raw, mask.raw);
291 return _mm256_sqrt_ps(v.raw);
295 return _mm256_rcp_ps(v.raw);
299 return _mm256_rsqrt_ps(v.raw);
304 return _mm256_castsi256_ps(_mm256_set1_epi32(0x80000000));
308 return _mm256_andnot_ps(SignBitMask().
raw, v.raw);
312 return _mm256_min_ps(a.raw, b.raw);
316 return _mm256_max_ps(a.raw, b.raw);
320 return _mm256_floor_ps(a.raw);
324 return _mm256_round_ps(v.raw, _MM_FROUND_TO_NEAREST_INT);
327#if MOCHI_ARCH_X64_SVML
329 return _mm256_cos_ps(a.raw);
333 return _mm256_sin_ps(a.raw);
337 return _mm256_tan_ps(a.raw);
341 return _mm256_acos_ps(a.raw);
345 return _mm256_asin_ps(a.raw);
349 return _mm256_atan_ps(a.raw);
353 return _mm256_exp_ps(a.raw);
357 return _mm256_log_ps(a.raw);
361 return _mm256_tanh_ps(a.raw);
366#if MOCHI_ARCH_X64_FMA
367 return _mm256_fmadd_ps(a.raw, b.raw, c.raw);
374#if MOCHI_ARCH_X64_FMA
375 return _mm256_fmsub_ps(a.raw, b.raw, c.raw);
382#if MOCHI_ARCH_X64_FMA
383 return _mm256_fnmadd_ps(a.raw, b.raw, c.raw);
390#if MOCHI_ARCH_X64_FMA
391 return _mm256_fnmsub_ps(a.raw, b.raw, c.raw);
399 static_assert(N >= 2 && N <= 8,
"Unsupported N");
401 if constexpr (N >= 2 && N <= 4) {
406 if constexpr (N != 8) {
408 auto inf = HalfT{std::numeric_limits<Scalar>::infinity()};
409 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(inf, hi);
411 return HalfT::HMin<4>(HalfT::Min(lo, hi));
417 static_assert(N >= 2 && N <= 8,
"Unsupported N");
419 if constexpr (N >= 2 && N <= 4) {
424 if constexpr (N != 8) {
426 auto inf = HalfT{std::numeric_limits<Scalar>::infinity()};
427 hi = HalfT::Blend<1, N >= 6, N >= 7, 0>(-inf, hi);
429 return HalfT::HMax<4>(HalfT::Max(lo, hi));
435 static_assert(N >= 2 && N <= 8,
"Unsupported N");
437 if constexpr (N >= 2 && N <= 4) {
439 }
else if constexpr (N == 5) {
442 }
else if constexpr (N == 6) {
444 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) +
Get<2>(a) +
Get<3>(a);
445 }
else if constexpr (N == 7) {
447 return HalfT::Get<0>(tmp) + HalfT::Get<1>(tmp) + HalfT::Get<2>(tmp) +
Get<3>(a);
448 }
else if constexpr (N == 8) {
458 static_assert(N == 8,
"smaller dot products not yet supported on Vec8f");
459#if MOCHI_COMPILER_CLANG
460 auto ab_half = _mm256_dp_ps(a.raw, b.raw, -1);
462 auto ab_half = _mm256_dp_ps(a.raw, b.raw, 0xFF);
464 auto lo_ab = _mm256_extractf128_ps(ab_half, 0);
465 auto hi_ab = _mm256_extractf128_ps(ab_half, 1);
466 auto my_dot = _mm_add_ps(lo_ab, hi_ab);
467 return _mm256_set_m128(my_dot, my_dot);
471 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_LT_OQ);
475 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_GT_OQ);
479 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_LE_OQ);
483 return _mm256_cmp_ps(this->
raw, rhs.raw, _CMP_GE_OQ);
487 return _mm256_cmp_ps(a.raw, b.raw, _CMP_EQ_OQ);
491 return _mm256_cmp_ps(a.raw, b.raw, _CMP_NEQ_UQ);
495 return _mm256_setzero_ps();
499 auto mask = GetMSBitMask(
Equal(*
this, rhs));
500 return mask == 0xFFFFFFFF;
504 auto mask = GetMSBitMask(
NotEqual(*
this, rhs));
509 auto ones = _mm256_castsi256_ps(_mm256_set1_epi32(-1));
510 return _mm256_xor_ps(
raw, ones);
514 return Zero() - *
this;
518 return _mm256_add_ps(
raw, rhs.raw);
522 return _mm256_sub_ps(
raw, rhs.raw);
526 return _mm256_mul_ps(
raw, rhs.raw);
530 return _mm256_div_ps(
raw, rhs.raw);
534 return _mm256_and_ps(
raw, rhs.raw);
538 return _mm256_or_ps(
raw, rhs.raw);
542 return _mm256_xor_ps(
raw, rhs.raw);
548 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
Scalar operator[](int i) 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)