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>
261 static_assert(kShift >= 0 && kShift < (8 *
sizeof(T)),
"Shift amount out-of-range");
262 if constexpr (kShift == 0) {
269template <
int x,
int y,
class T>
274template <
int x,
int y,
int z,
int w,
class T>
279template <
int x,
int y,
int z,
int w,
class T>
284template <
int x,
int y,
class T,
int N>
289template <
int x,
int y,
int z,
int w,
class T,
int N>
294template <
class T,
int N>
299template <
int i,
class T,
int N>
304template <
class T,
int N>
309template <
int iHalf,
class T,
int N>
314template <
int i,
class T,
int N>
319template <
class T,
int N>
326 using T =
typename V::Scalar;
327 static_assert(V::kIsSupported && std::is_integral_v<T>,
"Must be a supported integral Simd type");
328 if constexpr (V::kIsComposite) {
329 return V::Sequence();
331 alignas(V) T
constexpr kSequence[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
333 std::size(kSequence) >= V::kSize,
334 "Vector size is too large. Size of kSequence must be increased.");
339template <
bool x,
bool y,
bool z,
bool w,
class T,
int N>
342 std::is_floating_point_v<T> && (N == 4),
343 "This implementation is intended for Vec4f or Vec4d only");
347template <
class T,
int N>
349 static_assert(std::is_floating_point_v<T>,
"Requires float or double");
353template <
class T,
int N>
358template <
class T,
int N>
363template <
class T,
int N>
368template <
class T,
int N>
373template <
class T,
int N>
378template <
int COUNT,
class T,
int N>
380 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
384template <
int COUNT,
class T,
int N>
386 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
390template <
int COUNT,
class T,
int N>
392 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
396template <
int COUNT,
class T,
int N>
398 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
402#define MOCHI_SIMD_MEMBERWISE_FALLBACK(T, N, FN, inVec) \
403 alignas(alignof(Simd<T, N>)) T buf[N]; \
405 for (int i = 0; i < N; ++i) { \
406 buf[i] = FN(buf[i]); \
408 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;
461template <
class T,
int N>
468 }
else if constexpr (std::is_same_v<T, float>) {
472 details::SinCosImpl(a, sin, cos, n);
474 auto result =
Select((n & 1) - 1, cos, sin);
476 return result ^ ReinterpretCast<Simd<float, N>>((n ^
ShiftRight<1>(n)) << 31);
482template <
class T,
int N>
487 }
else if constexpr (std::is_same_v<T, float>) {
491 details::SinCosImpl(a, sin, cos, n);
493 auto result =
Select((n & 1) - 1, sin, cos);
495 return result ^ ReinterpretCast<Simd<T, N>>(
ShiftRight<1>(n) << 31);
501template <
class T,
int N>
509 details::SinCosImpl(a, sin, cos, n);
510 auto mask = ReinterpretCast<Simd<float, N>>((n & 1) - 1);
512 auto sresult =
Select(mask, sin, cos) ^ ReinterpretCast<Simd<float, N>>(nr << 31);
513 auto cresult =
Select(mask, cos, sin) ^ ReinterpretCast<Simd<float, N>>((n ^ nr) << 31);
514 return {sresult, cresult};
520template <
class T,
int N>
530template <
class T,
int N>
540template <
class T,
int N>
550template <
class T,
int N>
560template <
class T,
int N>
575 Simd<T, N> const infinity(std::numeric_limits<T>::infinity());
576 Simd<T, N> const log2_e(T(1.44269504088896341));
585 if constexpr (std::is_same_v<T const, float const>) {
587 Simd<T, N> const C2(T(-2.121944400547138e-04));
591 static_assert(std::is_same_v<T const, double const>);
593 Simd<T, N> const C2(T(1.42860682030941723212e-06));
599 if constexpr (std::is_same_v<T const, float const>) {
609 for (
int i = 1; i <= 5; ++i) {
619 x = y * ReinterpretCast<Simd<T, N>>(imm0);
621 Simd<T, N> const exp_lo(T(-88.37626226647949));
622 auto const underFlow = (a < exp_lo);
625 Simd<T, N> const exp_hi(T(88.37626226647949));
626 auto const overFlow = (a > exp_hi);
633 if constexpr (std::is_same_v<T const, double const>) {
635 1.26177193074810590878E-4,
636 3.02994407707441961300E-2,
637 9.99999999999999999910E-1,
640 3.00198505138664455042E-6,
641 2.52448340349684104192E-3,
642 2.27265548208155028766E-1,
643 2.00000000000000000009E0,
647 for (
int i = 1; i <= 2; ++i) {
653 for (
int i = 1; i <= 3; ++i) {
663 x = y * ReinterpretCast<Simd<T, N>>(jmm);
666 auto const underFlow = (a < exp_lo);
670 auto const overFlow = (a > exp_hi);
677template <
class T,
int N>
687template <
class T,
int N>
697#undef MOCHI_SIMD_MEMBERWISE_FALLBACK
701 return V::Equal(a, b);
706 return V::NotEqual(a, b);
709template <
int COUNT,
class T,
int N>
711 if constexpr (COUNT == 1) {
718template <
int COUNT,
class T,
int N>
720#if MOCHI_PLATFORM_MACOS && MOCHI_ARCH_X64 && MOCHI_OPTIMIZED
724 if constexpr (COUNT == 1) {
734 return Abs(a - b) <= epsilon;
739 return Abs(a) <= epsilon;
742template <
int COUNT,
class T,
int N>
744 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
748template <
int COUNT,
class T,
int N>
750 constexpr int COUNT_ = (COUNT == -1) ? N : COUNT;
754template <
class T,
int N>
756 if constexpr (std::is_same_v<T, float>) {
757 constexpr int kMask = 0x7F800000;
761 static_assert(std::is_same_v<T, double>,
"VIsFinite only supports float and double.");
762 constexpr int kMask = 0x7FF00000;
769 return ReinterpretCast<Simd<double, 2>>(temp2i64);
778 return ReinterpretCast<Simd<double, 4>>(temp4i64);
784 for (
int i = 0; i < N; ++i) {
785 uint64_t isFinite = -
static_cast<uint64_t
>(
IsFinite(a.
raw[i]));
786 memcpy(&result.
raw[i], &isFinite,
sizeof(T));
790 static_assert(std::is_void_v<T>,
"Unsupported type or size");
795template <
int i,
class T,
int N>
800 constexpr int kNumInts{(
sizeof(T) * N) /
sizeof(
int)};
801 constexpr int kStride = kNumInts / N;
805template <
class T,
int N>
810template <
class T,
int N>
815template <
class T,
int N>
820template <
class T,
int N>
825template <
int COUNT,
class V>
827 constexpr int COUNT_ = (COUNT == -1) ? V::kSize : COUNT;
831template <
class T,
int N>
833 return Shuffle<1, 2, 0, 3>(
MulSub(a,
Shuffle<1, 2, 0, 3>(b), b *
Shuffle<1, 2, 0, 3>(a)));
836template <
int COUNT,
class T,
int N>
838 static_assert(COUNT == -1 || COUNT >= 2,
"Unsupported COUNT");
839 static_assert(std::is_floating_point_v<T>,
"Requires float or double");
843template <
int COUNT,
class T,
int N>
848template <
int COUNT,
class T,
int N>
853template <
int COUNT,
class T,
int N>
858template <
int COUNT,
class T,
int N>
862 return a / (
VNorm<COUNT>(a) + std::numeric_limits<T>::min());
865template <
class T,
int N>
869 return a / (
Sqrt(normSqr) + std::numeric_limits<T>::min());
872template <
class T,
int N>
879 static_assert(std::is_floating_point_v<T>,
"Requires float or double");
890 if (absMin ==
Get<0>(abs)) {
892 }
else if (absMin ==
Get<1>(abs)) {
906template <
typename T,
int kSize>
907struct NextSupportedSimdSizeHelper {
909 static constexpr int value = std::conditional_t<
911 std::integral_constant<int, kSize>,
912 NextSupportedSimdSizeHelper<T, kSize + 1>>::value;
915template <
typename T,
int kSize>
916inline constexpr int kNextSupportedSimdSize = NextSupportedSimdSizeHelper<T, kSize>::value;
926#if MOCHI_USE_REFLECTION
927template <
typename T,
int N>
928struct SReflectTypeTraits<
superdex::Simd<T, N>> {
929 static constexpr SReflect::CoreType coreType = SReflect::CoreType::CT_array;
930 static SReflect::ArrayTypeInfo
const& GetTypeInfo() {
931 static auto* s_typeInfo =
932 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)
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)