34#if MOCHI_ARCH_X64_SVML
35inline constexpr bool kUseSvml =
true;
37inline constexpr bool kUseSvml =
false;
47#define MOCHI_DEFINE_SIMD_OP_EQ(OP_EQ, OP) \
48 template <class T, int N, class RHS> \
49 MOCHI_ANY MOCHI_FORCE_INLINE Simd<T, N>& operator OP_EQ(Simd<T, N>& lhs, RHS rhs) { \
55#define MOCHI_DEFINE_MIXED_SIMD_SCALAR_OP(OP) \
56 template <class T, int N> \
57 MOCHI_ANY MOCHI_FORCE_INLINE auto operator OP(Simd<T, N> lhs, T rhs) { \
58 return lhs OP Simd<T, N>{rhs}; \
60 template <class T, int N> \
61 MOCHI_ANY MOCHI_FORCE_INLINE auto operator OP(T lhs, Simd<T, N> rhs) { \
62 return Simd<T, N>{lhs} OP rhs; \
72#undef MOCHI_DEFINE_SIMD_OP_EQ
78#undef MOCHI_DEFINE_MIXED_SIMD_SCALAR_OP
82template <
class T,
int N>
84 static_assert(
sizeof(T) ==
sizeof(int) ||
sizeof(T) ==
sizeof(int64_t));
85 using I = std::conditional_t<
sizeof(T) ==
sizeof(
int), int, int64_t>;
87 auto const ia = ReinterpretCast<IVec>(a);
88 auto const zero = IVec{};
89 auto const ones = ~zero;
94template <
class T,
int N>
97 superdex::details::IsValidLogicalMask(lhs) && superdex::details::IsValidLogicalMask(rhs),
98 "operator&& requires all lanes to have all-bits-0 or all-bits-1");
102template <
class T,
int N>
105 superdex::details::IsValidLogicalMask(lhs) && superdex::details::IsValidLogicalMask(rhs),
106 "operator|| requires all lanes to have all-bits-0 or all-bits-1");
114template <
class V, MOCHI_CONCEPT_DEF(IsSimd<V>)>
120template <
class V, MOCHI_CONCEPT_DEF(IsSimd<V>)>
122 return V::Broadcast(p);
125template <
int i,
class T,
int N>
130template <
class T,
int N>
136template <
class V,
class... MoreBools>
138 static_assert(V::kSize ==
sizeof...(bs) + 2,
"Incorrect number of arguments");
139 using I = std::conditional_t<
sizeof(
typename V::Scalar) ==
sizeof(
int), int, int64_t>;
141 constexpr I kSimdBool[2] = {0, -1};
142 return ReinterpretCast<V>(IVec{kSimdBool[b0], kSimdBool[b1], kSimdBool[bs]...});
150template <
int i,
class V>
152 return V::template SetBasisVector<i>();
157 static_assert(V::kSize == 4,
"Unsupported SIMD size");
160 return V::template SetBasisVector<0>();
162 return V::template SetBasisVector<1>();
164 return V::template SetBasisVector<2>();
166 return V::template SetBasisVector<3>();
168 MOCHI_ASSERT(axis >= 0 && axis <= 3,
"Invalid component index");
175 return V::AsPoint(a);
180 return V::AsDirection(a);
183template <
class V, MOCHI_CONCEPT_DEF(IsSimd<V>)>
188template <
int N,
class V, MOCHI_CONCEPT_DEF(IsSimd<V>)>
190 return V::template
Load<N>(ptr);
193template <
class V, MOCHI_CONCEPT_DEF(IsSimd<V>)>
195 return V::Load(ptr, n);
198template <
class V,
class I, MOCHI_CONCEPT_DEF(IsSimd<V>)>
201 static_assert(std::is_integral_v<I>,
"Requires integral type");
202 return V::LoadIndexed(ptr, indices);
205template <
int kTupleCount,
class T,
int N>
208 constexpr int kTupleCount_ = (kTupleCount == -1) ? N : kTupleCount;
212template <
int COUNT,
class T,
int N>
214 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
218template <
class T,
int N>
223template <
class T,
int N,
class MaskT>
227 superdex::details::IsValidLogicalMask(condition),
228 "Not a valid logical mask. Each lane must be all-bits-0 or all-bits-1.");
229 if constexpr (
sizeof(MaskT) == 8 &&
sizeof(T) == 4) {
230 auto conditionI64 = ReinterpretCast<Simd<int64_t, N>>(condition);
234 "Expected StaticCast from int64_t to int32_t to return the lower 32 bits. This is not guaranteed by the C++ standard for static_cast, "
235 "but it is guaranteed by the x64 and ARM implementations. Your new CPU architecture behaves differenty. Therefore, this code will "
236 "need to perform a masking operation, or use Simd<uint64_t, N> and Simd<uint32_t, N> (not supported at the time of writing).");
243template <
int kTupleCount,
class T,
int N>
246 constexpr int kTupleCount_ = (kTupleCount == -1) ? N : kTupleCount;
250template <
class T,
int N,
class MaskT>
254 superdex::details::IsValidLogicalMask(conditionMask),
255 "Not a valid logical mask. Each lane must be all-bits-0 or all-bits-1.");
259template <
int kShift,
class T,
int N>
263 "ShiftRight requires a supported signed integer Simd type");
264 static_assert(kShift >= 0 && kShift < (8 *
sizeof(T)),
"Shift amount out-of-range");
265 if constexpr (kShift == 0) {
272template <
int x,
int y,
class T>
277template <
int x,
int y,
int z,
int w,
class T>
282template <
int x,
int y,
int z,
int w,
class T>
287template <
int x,
int y,
class T,
int N>
292template <
int x,
int y,
int z,
int w,
class T,
int N>
297template <
class T,
int N>
302template <
int i,
class T,
int N>
307template <
int iHalf,
class T,
int N>
312template <
int i,
class T,
int N>
317template <
class T,
int N>
324 using T =
typename V::Scalar;
325 static_assert(V::kIsSupported && std::is_integral_v<T>,
"Must be a supported integral Simd type");
326 if constexpr (V::kIsComposite) {
327 return V::Sequence();
329 alignas(V) T
constexpr kSequence[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
331 std::size(kSequence) >= V::kSize,
332 "Vector size is too large. Size of kSequence must be increased.");
337template <
bool x,
bool y,
bool z,
bool w,
class T,
int N>
340 std::is_floating_point_v<T> && (N == 4),
341 "This implementation is intended for Vec4f or Vec4d only");
345template <
class T,
int N>
347 static_assert(std::is_floating_point_v<T>,
"Requires float or double");
351template <
class T,
int N>
356template <
class T,
int N>
361template <
class T,
int N>
366template <
class T,
int N>
371template <
class T,
int N>
376template <
int COUNT,
class T,
int N>
378 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
382template <
int COUNT,
class T,
int N>
384 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
388template <
int COUNT,
class T,
int N>
390 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
394template <
int COUNT,
class T,
int N>
396 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
400#define MOCHI_SIMD_MEMBERWISE_FALLBACK(T, N, FN, inVec) \
402 alignas(alignof(Simd<T, N>)) T buf[N]; \
404 for (int i = 0; i < N; ++i) { \
405 buf[i] = FN(buf[i]); \
407 return Load<Simd<T, N>>(buf); \
410template <
class T,
int N>
415template <
class T,
int N>
432SinCosImpl(Simd<float, N> xf, Simd<float, N>& outSin, Simd<float, N>& outCos, Simd<int, N>& outN) {
437 auto r =
FastRound(x * 0x1.45f306dc9c883p-1);
439 x = x - r * 0x1.921fb54442d18p0;
445 auto s1 = 0x1.1107605230bc4p-7 - x2 * 0x1.994eb3774cf24p-13;
446 auto c1 = 1.0 - x2 * 0.5;
447 auto c2 = -0x1.6c087e89a359dp-10 + x2 * 0x1.99343027bf8c3p-16;
450 auto s0 = x - x3 * 0x1.555545995a603p-3;
451 auto c0 = c1 + x4 * 0x1.55553e1068f19p-5;
462inline constexpr float kMaxFastSinCosInput = 1e6f;
466 return AllTrue(
Abs(a) <= Simd<float, N>{kMaxFastSinCosInput});
470template <
class T,
int N>
477 }
else if constexpr (std::is_same_v<T, float>) {
478 if (!details::IsFastSinCosInput(a))
485 details::SinCosImpl(a, sin, cos, n);
487 auto result =
Select((n & 1) - 1, cos, sin);
489 return result ^ ReinterpretCast<Simd<float, N>>((n ^
ShiftRight<1>(n)) << 31);
495template <
class T,
int N>
500 }
else if constexpr (std::is_same_v<T, float>) {
501 if (!details::IsFastSinCosInput(a))
508 details::SinCosImpl(a, sin, cos, n);
510 auto result =
Select((n & 1) - 1, sin, cos);
512 return result ^ ReinterpretCast<Simd<T, N>>(
ShiftRight<1>(n) << 31);
518template <
class T,
int N>
524 if (!details::IsFastSinCosInput(a))
530 details::SinCosImpl(a, sin, cos, n);
531 auto mask = ReinterpretCast<Simd<float, N>>((n & 1) - 1);
533 auto sresult =
Select(mask, sin, cos) ^ ReinterpretCast<Simd<float, N>>(nr << 31);
534 auto cresult =
Select(mask, cos, sin) ^ ReinterpretCast<Simd<float, N>>((n ^ nr) << 31);
535 return {sresult, cresult};
541template <
class T,
int N>
551template <
class T,
int N>
561template <
class T,
int N>
571template <
class T,
int N>
581template <
class T,
int N>
596 Simd<T, N> const infinity(std::numeric_limits<T>::infinity());
597 Simd<T, N> const log2_e(T(1.44269504088896341));
606 if constexpr (std::is_same_v<T const, float const>) {
608 Simd<T, N> const C2(T(-2.121944400547138e-04));
612 static_assert(std::is_same_v<T const, double const>);
614 Simd<T, N> const C2(T(1.42860682030941723212e-06));
620 if constexpr (std::is_same_v<T const, float const>) {
630 for (
int i = 1; i <= 5; ++i) {
640 x = y * ReinterpretCast<Simd<T, N>>(imm0);
642 Simd<T, N> const exp_lo(T(-88.37626226647949));
643 auto const underFlow = (a < exp_lo);
646 Simd<T, N> const exp_hi(T(88.37626226647949));
647 auto const overFlow = (a > exp_hi);
654 if constexpr (std::is_same_v<T const, double const>) {
656 1.26177193074810590878E-4,
657 3.02994407707441961300E-2,
658 9.99999999999999999910E-1,
661 3.00198505138664455042E-6,
662 2.52448340349684104192E-3,
663 2.27265548208155028766E-1,
664 2.00000000000000000009E0,
668 for (
int i = 1; i <= 2; ++i) {
674 for (
int i = 1; i <= 3; ++i) {
684 x = y * ReinterpretCast<Simd<T, N>>(jmm);
687 auto const underFlow = (a < exp_lo);
691 auto const overFlow = (a > exp_hi);
698template <
class T,
int N>
708template <
class T,
int N>
718#undef MOCHI_SIMD_MEMBERWISE_FALLBACK
722 return V::Equal(a, b);
727 return V::NotEqual(a, b);
730template <
int COUNT,
class T,
int N>
732 if constexpr (COUNT == 1) {
739template <
int COUNT,
class T,
int N>
741#if MOCHI_PLATFORM_MACOS && MOCHI_ARCH_X64 && MOCHI_OPTIMIZED
745 if constexpr (COUNT == 1) {
755 return Abs(a - b) <= epsilon;
760 return Abs(a) <= epsilon;
763template <
int COUNT,
class T,
int N>
765 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
769template <
int COUNT,
class T,
int N>
771 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
775template <
class T,
int N>
777 if constexpr (std::is_same_v<T, float>) {
778 constexpr int kMask = 0x7F800000;
782 static_assert(std::is_same_v<T, double>,
"VIsFinite only supports float and double.");
783 constexpr int kMask = 0x7FF00000;
790 return ReinterpretCast<Simd<double, 2>>(temp2i64);
799 return ReinterpretCast<Simd<double, 4>>(temp4i64);
802 auto bits = ReinterpretCast<Simd<int64_t, 8>>(a);
803 return ReinterpretCast<Simd<double, 8>>(
VNotEqual(bits & mask, mask));
809 for (
int i = 0; i < N; ++i) {
810 uint64_t isFinite = -
static_cast<uint64_t
>(
IsFinite(a.
raw[i]));
811 memcpy(&result.
raw[i], &isFinite,
sizeof(T));
815 static_assert(std::is_void_v<T>,
"Unsupported type or size");
820template <
int i,
class T,
int N>
825 constexpr int kNumInts{(
sizeof(T) * N) /
sizeof(
int)};
826 constexpr int kStride = kNumInts / N;
830template <
class T,
int N>
835template <
class T,
int N>
840template <
class T,
int N>
845template <
class T,
int N>
850template <
int COUNT,
class V>
852 constexpr int COUNT_ = (COUNT == -1) ? V::kSize : COUNT;
856template <
class T,
int N>
858 return Shuffle<1, 2, 0, 3>(
MulSub(a,
Shuffle<1, 2, 0, 3>(b), b *
Shuffle<1, 2, 0, 3>(a)));
861template <
int COUNT,
class T,
int N>
863 static_assert(COUNT == -1 || COUNT >= 2,
"Unsupported COUNT");
864 static_assert(std::is_floating_point_v<T>,
"Requires float or double");
868template <
int COUNT,
class T,
int N>
873template <
int COUNT,
class T,
int N>
878template <
int COUNT,
class T,
int N>
883template <
int COUNT,
class T,
int N>
887 return a / (
VNorm<COUNT>(a) + std::numeric_limits<T>::min());
890template <
class T,
int N>
894 return a / (
Sqrt(normSqr) + std::numeric_limits<T>::min());
897template <
class T,
int N>
904 static_assert(std::is_floating_point_v<T>,
"Requires float or double");
915 if (absMin ==
Get<0>(abs)) {
917 }
else if (absMin ==
Get<1>(abs)) {
932struct NextSupportedSimdSizeHelper;
934template <
typename T,
int kSize>
935struct NextSupportedSimdSizeHelper<T, kSize, true> {
936 static_assert(kSize > 0,
"SIMD size must be positive.");
938 static constexpr int value =
942template <
typename T,
int kSize>
943struct NextSupportedSimdSizeHelper<T, kSize, false> {
944 static_assert(kSize > 0,
"SIMD size must be positive.");
946 static constexpr bool kIsSingleRegister =
948 static constexpr int value = std::conditional_t<
950 std::integral_constant<int, kSize>,
951 NextSupportedSimdSizeHelper<T, kSize + 1>>::value;
954template <
typename T,
int kSize>
955inline constexpr int kNextSupportedSimdSize = NextSupportedSimdSizeHelper<T, kSize>::value;
965#if MOCHI_USE_REFLECTION
966template <
typename T,
int N>
967struct SReflectTypeTraits<
superdex::Simd<T, N>> {
968 static constexpr SReflect::CoreType coreType = SReflect::CoreType::CT_array;
969 static SReflect::ArrayTypeInfo
const& GetTypeInfo() {
970 static auto* s_typeInfo =
971 SReflect::MakeFixedArrayTypeInfo<superdex::Simd<T, N>, T, N>(
"superdex::Simd",
true);
static constexpr bool kIsComposite
static constexpr bool kIsEmulated
static constexpr bool kIsSupported
#define MOCHI_ASSERT(condition_without_side_effects,...)
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
T Dot(Simd< T, N > a, Simd< T, N > b)
Simd< T, N > VIsFinite(Simd< T, N > a)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
constexpr int kSimdDefaultSize
V LoadIndexed(typename V::Scalar const *ptr, Simd< I, V::kSize > indices)
constexpr T const & Min(T const &a, T const &b)
T NormSqr(Simd< T, N > a)
constexpr auto Equal(T const &a, T const &b)
Simd< T, N > Cross3(Simd< T, N > a, Simd< T, N > b)
constexpr To StaticCast(From const &a)
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 VNearEqual(V a, V b, V epsilon)
Simd< T, N > VNormSqr(Simd< T, N > a)
int IsTrue(Simd< T, N > mask)
V Broadcast(typename V::Scalar a)
constexpr auto MulSub(A a, B b, C c)
Simd< T, N > Normalize(Simd< T, N > a)
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)
std::pair< Simd< T, N >, Simd< T, N > > SinCos(Simd< T, N > a)
constexpr auto NegMulAdd(A a, B b, C c)
Simd< T, N > operator||(Simd< T, N > lhs, Simd< T, N > rhs)
Simd< T, N > operator&&(Simd< T, N > lhs, Simd< T, N > rhs)
V SimdMask(bool b0, bool b1, MoreBools... bs)
Simd< T, N/2 > GetHalf(Simd< T, N > a)
Simd< T, 4 > OrthogonalVector3(Simd< T, 4 > a)
Simd< T, N > VNorm(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)
V VNearZero(V a, V epsilon)
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
V Load(typename V::Scalar const *ptr)
Simd< T, N > Neg(Simd< T, N > a)
bool IsFinite(TransformRT const &a)
Simd< T, N > ShiftRight(Simd< T, N > a)
#define MOCHI_SIMD_MEMBERWISE_FALLBACK(T, N, FN, inVec)
#define MOCHI_DEFINE_MIXED_SIMD_SCALAR_OP(OP)
#define MOCHI_DEFINE_SIMD_OP_EQ(OP_EQ, OP)