28template <
class T,
int N,
int Cand
idate>
29[[nodiscard]]
constexpr int FindCompositeFirstSize() {
30 if constexpr (Candidate <= 0) {
32 }
else if constexpr (Candidate >= N) {
33 return FindCompositeFirstSize<T, N, Candidate / 2>();
36 Simd<T, N - Candidate>::kIsSupported) {
39 return FindCompositeFirstSize<T, N, Candidate / 2>();
43template <
class T,
int N>
44inline constexpr int kCompositeFirstSize = FindCompositeFirstSize<T, N, kSimdDefaultSize<T>>();
46template <
class V,
size_t... Is>
48 return V{
static_cast<typename V::Scalar
>(Is)...};
57template <
class T,
int N>
58class Simd<T, N, std::enable_if_t<(details::kCompositeFirstSize<T, N> > 0),
SimdConcept>> {
77 "Inconsistent SIMD emulation flags");
92 (
IsSimd<Half> && std::is_same_v<Scalar, typename Half::Scalar> &&
93 (Half::kSize * 2 ==
kSize)))>
96 using SecondTail =
typename Second::Second;
97 if constexpr (SecondTail::kIsComposite) {
99 std::is_same_v<First, typename Half::First> &&
100 std::is_same_v<typename Second::First, typename Half::Second> &&
101 std::is_same_v<typename SecondTail::First, typename Half::First> &&
102 std::is_same_v<typename SecondTail::Second, typename Half::Second>) {
105 second.second.first = b.first;
106 second.second.second = b.second;
112 Half::Store(values, a);
113 Half::Store(values + Half::kSize, b);
114 first = First::Load(values);
119 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
168 class... MoreScalars,
170 (
sizeof...(MoreScalars) + 8 <=
kSize) &&
171 (kSizeFirst != 16 ||
sizeof...(MoreScalars) + 8 <= 16) &&
172 (std::is_convertible_v<MoreScalars, Scalar> && ...))>
182 MoreScalars... args) {
191 static constexpr auto kNumArgs =
sizeof...(args);
192 if constexpr (kNumArgs == 0) {
194 }
else if constexpr (kNumArgs == 1) {
199 }
else if constexpr (
sizeof...(args) + 8 <=
kSizeFirst) {
200 first =
First{a, b, c, d, e, f, g, h, args...};
204 first = First::Load(lanes);
211 class... MoreScalars,
213 (kSizeFirst == 16) && (
sizeof...(MoreScalars) + 16 <=
kSize) &&
214 (std::is_convertible_v<MoreScalars, Scalar> && ...))>
232 MoreScalars... args) {
233 first =
First{a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p};
234 static constexpr auto kNumArgs =
sizeof...(args);
235 if constexpr (kNumArgs == 0) {
237 }
else if constexpr (kNumArgs == 1) {
250 static_assert(i >= 0 && i <
kSize,
"Index out of range");
257 static_assert(i == 0 || i == 1,
"Half index out of range");
258 static_assert(N % 2 == 0,
"Vector size must be even");
259 static_assert(Half::kIsSupported);
261 if constexpr (i == 0) {
268 using SecondTail =
typename Second::Second;
269 if constexpr (SecondTail::kIsComposite) {
271 std::is_same_v<First, typename Half::First> &&
272 std::is_same_v<typename Second::First, typename Half::Second> &&
273 std::is_same_v<typename SecondTail::First, typename Half::First> &&
274 std::is_same_v<typename SecondTail::Second, typename Half::Second>) {
275 if constexpr (i == 0) {
276 return Half{a.first, a.second.first};
278 return Half{a.second.second.first, a.second.second.second};
285 return Half::Load(values + i * Half::kSize);
291 return {First::Set(a.first, i, value), a.second};
293 return {a.first, Second::Set(a.second, i -
kSizeFirst, value)};
300 return {First::template
Set<i>(a.first, value), a.second};
302 return {a.first, Second::template
Set<i -
kSizeFirst>(a.second, value)};
308 static_assert(
kSize == 4,
"Unsupported size");
314 return details::MakeSimdSequence<Simd>(std::make_index_sequence<kSize>{});
316 alignas(
First)
Scalar constexpr kSequence[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
317 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
318 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
319 static_assert(
First::kSize <= 32,
"Vector size is too large");
320 auto first = First::Load(kSequence);
322 if constexpr (std::is_same_v<First, Second>) {
325 second = Second::Sequence();
327 static_assert(
Second::kSize <= 32,
"Vector size is too large");
328 second = Second::Load(kSequence);
334 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
336 static_assert(
kSize == 4,
"Unsupported size");
340 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
342 static_assert(
kSize == 4,
"Unsupported size");
346 template <
int SZ = kSize>
348 static_assert(SZ <=
kSize);
357 template <
int SZ = kSize>
368 static_assert(
kSize == 4,
"Unsupported size");
369 return {a.first, Second::template
Set<1>(a.second,
Scalar(0))};
373 static_assert(
kSize == 4,
"Unsupported size");
374 return {a.first, Second::template
Set<1>(a.second,
Scalar(1))};
377 template <
int x,
int y,
int z,
int w>
379 static_assert(
kSize == 4,
"Unsupported size");
386 return {First::Broadcast(p), Second::Broadcast(p)};
409 template <
int SZ = kSize>
423 return {First::Load(ptr, n),
Second{}};
429 template <
typename IntT, MOCHI_CONCEPT(std::is_
integral_v<IntT>)>
432 using IMatch = std::conditional_t<
sizeof(
Scalar) == 4,
int, int64_t>;
434 static_assert(IVec::kIsComposite &&
sizeof(IVec) ==
sizeof(
Simd));
437 First::LoadIndexed(ptr, matchingIndices.first),
438 Second::LoadIndexed(ptr, matchingIndices.second)};
441 template <
int kTupleCount =
kSize,
class... OutputVectors>
444 OutputVectors&... out) {
445 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
448 ((out.second = {}), ...);
452 ptr +
sizeof...(OutputVectors) *
kSizeFirst, out.second...);
458 First::Select(mask.first, a.first, b.first),
459 Second::Select(mask.second, a.second, b.second)};
462 template <
int kShift>
464 static_assert(kShift >= 0 && kShift < (8 *
sizeof(T)),
"Shift amount out-of-range");
465 if constexpr (kShift == 0) {
474 template <
int SZ = kSize>
487 memcpy(ptr, &v,
sizeof(
Scalar) * n);
491 int count = First::StoreSelected(ptr, condition.first, values.first);
492 count += Second::StoreSelected(ptr + count, condition.second, values.second);
496 template <
int kTupleCount =
kSize,
class... InputVectors>
501 ptr +
sizeof...(InputVectors) *
kSizeFirst, v.second...);
506 return {First::SignBitMask(), Second::SignBitMask()};
509 template <
int SZ = kSize>
512 return First::template
HMin<SZ>(a.first);
523 First::Min(a.first, First::Min(a.second.first, a.second.second)));
528 First::Min(a.second.first, First::Min(a.second.second.first, a.second.second.second))));
537 template <
int SZ = kSize>
540 return First::template
HMax<SZ>(a.first);
551 First::Max(a.first, First::Max(a.second.first, a.second.second)));
556 First::Max(a.second.first, First::Max(a.second.second.first, a.second.second.second))));
565 template <
int SZ = kSize>
568 return First::template
HSum<SZ>(a.first);
577 return First::template
HSum<kSizeFirst>(a.first + a.second.first + a.second.second);
581 a.first + a.second.first + a.second.second.first + a.second.second.second);
589 template <
int SZ = kSize>
592 return First::template
HProd<SZ>(a.first);
605 template <
int SZ = kSize>
611 return {First::Zero(), Second::Zero()};
616 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
627 First::Equal(this->first, rhs.first) & Second::Equal(this->second, rhs.second));
629 return (this->first == rhs.first) && (this->second == rhs.second);
634 return !(*
this == rhs);
643 return {this->first << i, this->second << i};
653 return {this->first >> i, this->second >> i};
662#define MOCHI_SIMD_COMPOSITE_FN_1(FnName) \
663 [[nodiscard]] static MOCHI_ANY MOCHI_FORCE_INLINE Simd FnName(Simd a) { \
664 return {First::FnName(a.first), Second::FnName(a.second)}; \
667#define MOCHI_SIMD_COMPOSITE_FN_2(FnName) \
668 [[nodiscard]] static MOCHI_ANY MOCHI_FORCE_INLINE Simd FnName(Simd a, Simd b) { \
669 return {First::FnName(a.first, b.first), Second::FnName(a.second, b.second)}; \
672#define MOCHI_SIMD_COMPOSITE_FN_3(FnName) \
673 [[nodiscard]] static MOCHI_ANY MOCHI_FORCE_INLINE Simd FnName(Simd a, Simd b, Simd c) { \
675 First::FnName(a.first, b.first, c.first), Second::FnName(a.second, b.second, c.second)}; \
678#define MOCHI_SIMD_COMPOSITE_OP_1(OP) \
679 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE Simd operator OP() const { \
680 return {OP this->first, OP this->second}; \
683#define MOCHI_SIMD_COMPOSITE_OP_2(OP) \
684 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE Simd operator OP(Simd rhs) const { \
685 return {this->first OP rhs.first, this->second OP rhs.second}; \
688#define MOCHI_SIMD_COMPOSITE_OP_EQ(OP_EQ, OP) \
689 MOCHI_ANY MOCHI_FORCE_INLINE Simd& operator OP_EQ(Simd a) { \
690 this->first = this->first OP a.first; \
691 this->second = this->second OP a.second; \
695#define MOCHI_SIMD_COMPOSITE_OP_EQ_WITH_SCALAR(OP_EQ, OP) \
696 MOCHI_SIMD_COMPOSITE_OP_EQ(OP_EQ, OP); \
697 MOCHI_ANY MOCHI_FORCE_INLINE Simd& operator OP_EQ(Scalar a) { \
698 this->first = this->first OP First{a}; \
699 this->second = this->second OP Second{a}; \
765#undef MOCHI_SIMD_COMPOSITE_FN_1
766#undef MOCHI_SIMD_COMPOSITE_FN_2
767#undef MOCHI_SIMD_COMPOSITE_FN_3
768#undef MOCHI_SIMD_COMPOSITE_OP_1
769#undef MOCHI_SIMD_COMPOSITE_OP_2
770#undef MOCHI_SIMD_COMPOSITE_OP_EQ
771#undef MOCHI_SIMD_COMPOSITE_OP_EQ_WITH_SCALAR
782 static_assert(
sizeof(
typename To::Scalar) * To::kSize ==
sizeof(FromT) * FromN,
"Size mismatch");
784 sizeof(
typename To::Scalar) * To::kSizeFirst ==
sizeof(FromT) * From::kSizeFirst,
785 "Component size mismatch");
787 ReinterpretCast<typename To::First>(a.first), ReinterpretCast<typename To::Second>(a.second)};
792[[nodiscard]]
constexpr bool IsCompositeWithEqualHalves() {
793 if constexpr (T::kIsComposite) {
794 return T::kSizeFirst == T::kSizeSecond;
800template <
class To,
class From, std::size_t... Is>
802StaticCastElementwise(From
const& a, std::index_sequence<Is...>) {
803 return To{
static_cast<typename To::Scalar
>(a[Is])...};
809template <
class To,
class From, MOCHI_CONCEPT_DEF(IsSimd<To>&& IsSimd<From>)>
811 static_assert(To::kSize == From::kSize,
"Size mismatch");
812 static_assert(To::kIsSupported,
"Unsupported type");
813 using ToHalf =
Simd<
typename To::Scalar, To::kSize / 2>;
814 using FromHalf =
Simd<
typename From::Scalar, From::kSize / 2>;
815 if constexpr (std::is_same_v<typename To::Scalar, typename From::Scalar>) {
817 }
else if constexpr (
818 To::kIsComposite && From::kIsComposite &&
819 (
sizeof(
typename To::Scalar) ==
sizeof(
typename From::Scalar))) {
823 }
else if constexpr (
824 From::kIsComposite && !To::kIsComposite && ToHalf::kIsSupported &&
825 (
sizeof(
typename To::Scalar) * 2 ==
sizeof(
typename From::Scalar))) {
829 }
else if constexpr (
830 To::kIsComposite && !From::kIsComposite && FromHalf::kIsSupported &&
831 (
sizeof(
typename To::Scalar) == 2 *
sizeof(
typename From::Scalar))) {
837 }
else if constexpr (
838 ::superdex::details::IsCompositeWithEqualHalves<From>() &&
839 ::superdex::details::IsCompositeWithEqualHalves<To>() &&
840 sizeof(
typename To::Scalar) !=
sizeof(
typename From::Scalar)) {
848 return superdex::details::StaticCastElementwise<To>(a, std::make_index_sequence<From::kSize>{});
static Simd Equal(Simd a, Simd b)
static Simd RcpApprox(Simd a)
static Simd RcpSqrtApprox(Simd a)
static Simd NotEqual(Simd a, Simd b)
static Simd NegMulSub(Simd a, Simd b, Simd c)
static void Store(Scalar *ptr, Simd a)
static Scalar HMax(Simd a)
static Simd Floor(Simd a)
static Scalar HMin(Simd a)
static Scalar HProd(Simd a)
static Simd Shuffle(Simd a, Simd b)
Simd< T, N - First::kSize > Second
static Simd Set(Simd a, int i, Scalar value)
static void Store(Scalar *ptr, Simd v, int n)
Simd(Scalar a, Scalar b, Scalar c, Scalar d, Scalar e, Scalar f, Scalar g, Scalar h, MoreScalars... args)
static constexpr int kSizeSecond
static void StoreTransposed(Scalar *ptr, InputVectors... v)
Simd(Scalar a, Scalar b, Scalar c, Scalar d=Scalar(0))
Scalar operator[](int i) const
static constexpr bool kIsEmulated
static Simd Load(Scalar const *ptr, int n)
static constexpr int kSize
static Simd Shuffle(Simd a)
static Simd LoadIndexed(Scalar const *ptr, Simd< IntT, kSize > const &indices)
static Simd ShiftRight(Simd a)
bool operator!=(Simd rhs) const
static Simd Broadcast(Scalar const *p)
Simd(Scalar a, Scalar b, Scalar c, Scalar d, Scalar e, Scalar f, Scalar g, Scalar h, Scalar i, Scalar j, Scalar k, Scalar l, Scalar m, Scalar n, Scalar o, Scalar p, MoreScalars... args)
Simd(Half const &a, Half const &b)
static Simd Broadcast(Simd a)
Simd(Simd const &a)=default
static int StoreSelected(Scalar *ptr, Simd condition, Simd values)
static Simd NegMulAdd(Simd a, Simd b, Simd c)
Simd operator<<(int i) const
static Simd MulSub(Simd a, Simd b, Simd c)
static Simd MulAdd(Simd a, Simd b, Simd c)
static Simd AsPoint(Simd a)
Simd(Scalar a, Scalar b, Scalar c, Scalar d, Scalar e, Scalar f=Scalar(0))
static Simd SignBitMask()
static Simd Max(Simd a, Simd b)
Simd & operator>>=(int i)
static bool AllTrue(Simd a)
static Scalar HSum(Simd a)
static constexpr size_t size()
Simd< T, details::kCompositeFirstSize< T, N > > NativeType
static Simd Min(Simd a, Simd b)
bool operator==(Simd rhs) const
Simd & operator=(Simd const &rhs)=default
static Scalar Get(Simd a)
Simd operator>>(int i) const
static Simd SetBasisVector()
static auto GetHalf(Simd a)
Simd & operator<<=(int i)
static Simd Set(Simd a, Scalar value)
static bool AnyTrue(Simd a)
static void LoadTransposed(Scalar const *ptr, OutputVectors &... out)
Simd(First const &p0, Second const &p1)
static Simd Broadcast(Scalar a)
static Simd Dot(Simd a, Simd b)
static Simd AsDirection(Simd a)
Simd(Scalar a, Scalar b, Scalar c, Scalar d, Scalar e, Scalar f, Scalar g)
static Simd Select(Simd mask, Simd a, Simd b)
static constexpr bool kIsComposite
static constexpr bool kIsSupported
static constexpr int kSizeFirst
static Simd Blend(Simd a, Simd b)
static Simd Load(T const *ptr)
static Simd FastRound(Simd a)
static constexpr bool kIsComposite
static constexpr bool kIsEmulated
static constexpr int kSize
static constexpr bool kIsSupported
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
constexpr T const & Min(T const &a, T const &b)
constexpr To StaticCast(From const &a)
Simd< T, N > Set(Simd< T, N > a, T value)
V Broadcast(typename V::Scalar a)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
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)
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
void Store(T *ptr, Simd< T, N > a)
V Load(typename V::Scalar const *ptr)
Simd< T, N > ShiftRight(Simd< T, N > a)
#define MOCHI_SIMD_COMPOSITE_FN_3(FnName)
#define MOCHI_SIMD_COMPOSITE_OP_1(OP)
#define MOCHI_SIMD_COMPOSITE_OP_EQ(OP_EQ, OP)
#define MOCHI_SIMD_COMPOSITE_FN_2(FnName)
#define MOCHI_SIMD_COMPOSITE_OP_2(OP)
#define MOCHI_SIMD_COMPOSITE_OP_EQ_WITH_SCALAR(OP_EQ, OP)
#define MOCHI_SIMD_COMPOSITE_FN_1(FnName)