34template <
typename F,
typename... Args>
48template <
typename T,
int N>
51 using UIntT = std::conditional_t<
sizeof(T) == 4, uint32_t, uint64_t>;
52 static constexpr T kOnesMask = std::bit_cast<T>(~UIntT{0});
54#define MOCHI_SIMD_EMULATOR_OP_1(Op, Expr) \
55 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd operator Op(Simd rhs) const { \
56 auto eval = []<size_t... I>(Simd const& lhs, Simd const& rhs, std::index_sequence<I...>) { \
57 return Simd{(Expr)...}; \
59 return eval(*this, rhs, std::make_index_sequence<N>{}); \
62#define MOCHI_SIMD_EMULATOR_OP_2(Op) \
63 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd operator Op(Simd rhs) const { \
64 auto eval = []<size_t... I>(Simd const& lhs, Simd const& rhs, std::index_sequence<I...>) { \
66 std::bit_cast<Scalar>(std::bit_cast<UIntT>(lhs.raw[I]) \
67 Op std::bit_cast<UIntT>(rhs.raw[I]))...); \
69 return eval(*this, rhs, std::make_index_sequence<N>{}); \
72#define MOCHI_SIMD_EMULATOR_HREDUCE_BOOL(Name, Op) \
73 template <int M = kSize> \
74 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr bool Name(Simd a) { \
75 static_assert(M >= 1 && M <= kSize, "Unsupported M"); \
76 auto eval = []<size_t... I>(Simd const& a, std::index_sequence<I...>) { \
77 return ((std::bit_cast<UIntT>(a.raw[I]) != UIntT{0}) Op...); \
79 return eval(a, std::make_index_sequence<M>{}); \
82 template <
int M = N, auto F>
84 auto eval = []<
size_t... I>(Simd
const& x, std::index_sequence<I...>) {
85 return F(x.raw[I]...);
87 return eval(a, std::make_index_sequence<M>{});
90 using ST = std::conditional_t<std::floating_point<T>, T,
float>;
91 template <ST (*F)(ST)>
93 std::conditional_t<std::floating_point<T>,
Simd,
struct DoesNotExist>& x) {
94 if constexpr (std::floating_point<T>) {
101 template <
typename S, S (*F)(S, S)>
103 return Simd(F, x, y);
106 template <
typename S, S (*F)(S, S, S)>
108 return Simd(F, x, y, z);
124 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
126 for (
int i = 0; i < N; ++i) {
131 requires(N > 2 && N % 2 == 0)
133 for (
int i = 0; i < N / 2; ++i) {
136 for (
int i = 0; i < N / 2; ++i) {
137 raw[(N / 2) + i] = b[i];
141 template <
typename... Args>
143 sizeof...(Args) > 1 &&
sizeof...(Args) <= kSize && (std::convertible_to<Args, T> && ...))
145 :
raw{
static_cast<T
>(std::forward<Args>(args))...} {}
147 template <
typename F,
typename... Args>
151 for (
size_t i = 0; i < N; ++i) {
152 raw[i] = f(args.raw[i]...);
157 return static_cast<size_t>(
kSize);
162 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
164 for (
int i = 0; i < N; ++i) {
184 static_assert(i >= 0 && i <
kSize,
"Index out of range");
195 requires(N >= 2 && N % 2 == 0)
197 static_assert(i == 0 || i == 1);
198 auto eval = [&v]<
size_t... I>(std::index_sequence<I...>) {
199 return Simd<T, N / 2>{v.raw[i * (N / 2) + I]...};
201 return eval(std::make_index_sequence<N / 2>{});
208 result.
raw[i] = value;
214 static_assert(i >= 0 && i <
kSize,
"Index out of range");
215 return Set(v, i, value);
221 return {a.raw[0], a.raw[1], a.raw[2],
Scalar{1}};
227 return {a.raw[0], a.raw[1], a.raw[2],
Scalar{0}};
234 static_assert(i >= 0 && i <= 3,
"Invalid component index");
244 requires(
sizeof...(x) == N)
246 static_assert(((x == 0 || x == 1) && ...),
"Invalid index");
247 auto eval = []<
size_t... I>(
Simd const& a,
Simd const& b, std::index_sequence<I...>) {
248 return Simd{(x ? b.raw[I] : a.raw[I])...};
250 return eval(a, b, std::make_index_sequence<N>{});
259 static_assert(i >= 0 && i <
kSize,
"Index out of range");
263 template <
int x = 0,
int y = 1>
267 static_assert(x >= 0 && x < 2 && y >= 0 && y < 2,
"Invalid index");
268 return Simd{a.raw[x], a.raw[y]};
271 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
276 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
277 return Simd{a.raw[x], a.raw[y], b.raw[z], b.raw[w]};
280 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
287 template <
int M = kSize>
289 static_assert(M >= 0 && M <=
kSize);
290 if constexpr (M == 0) {
294 std::memcpy(result.
raw.data(), ptr, M *
sizeof(
Scalar));
295 if constexpr (M <
kSize) {
296 std::memset(result.
raw.data() + M, 0, (
kSize - M) *
sizeof(
Scalar));
305 for (
int i = 0; i < n; ++i) {
306 result.
raw[i] = ptr[i];
308 for (
int i = n; i <
kSize; ++i) {
314 template <
typename IntT>
318 requires(std::integral<IntT>)
320 auto eval = []<
size_t... I>(
322 return Simd{ptr[indices.raw[I]]...};
324 return eval(ptr, indices, std::make_index_sequence<N>{});
327 template <
int kTupleCount = kSize>
330 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
332 constexpr int kTupleSize = 3;
333 auto eval = [ptr, zero, &out0, &out1, &out2]<
size_t... I>(std::index_sequence<I...>) {
334 out0 =
Simd{(I < kTupleCount ? ptr[I * kTupleSize + 0] : zero)...};
335 out1 =
Simd{(I < kTupleCount ? ptr[I * kTupleSize + 1] : zero)...};
336 out2 =
Simd{(I < kTupleCount ? ptr[I * kTupleSize + 2] : zero)...};
338 eval(std::make_index_sequence<kSize>{});
341 template <
int M = kSize>
343 [[maybe_unused]]
Scalar* ptr,
344 [[maybe_unused]]
Simd v) {
345 static_assert(M >= 0 && M <=
kSize,
"Unsupported M");
346 if constexpr (M != 0) {
347 std::memcpy(ptr, v.raw.data(), M *
sizeof(
Scalar));
353 for (
int i = 0; i < n; ++i) {
360 auto eval = [&count]<
size_t... I>(
364 std::index_sequence<I...>) {
365 ((ptr[count] = values.
raw[I], count +=
static_cast<int>(!!condition.
raw[I])), ...);
367 eval(ptr, condition, values, std::make_index_sequence<N>{});
371 template <
int kTupleCount = kSize>
374 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
375 auto eval = [ptr, &out0, &out1, &out2]<
size_t... I>(std::index_sequence<I...>) {
376 ((I < kTupleCount ? (void)(ptr[I * 3 + 0] = out0.
raw[I],
377 ptr[I * 3 + 1] = out1.
raw[I],
378 ptr[I * 3 + 2] = out2.
raw[I])
382 eval(std::make_index_sequence<kSize>{});
385 template <
int M = kSize>
387 static_assert(M >= 1 && M <=
kSize,
"Unsupported M");
388 return FoldApply<M, [](
auto... v) {
return superdex::Min(v...); }>(a);
391 template <
int M = kSize>
393 static_assert(M >= 1 && M <=
kSize,
"Unsupported M");
394 return FoldApply<M, [](
auto... v) {
return superdex::Max(v...); }>(a);
397 template <
int M = kSize>
399 static_assert(M >= 1 && M <=
kSize,
"Unsupported M");
400 return FoldApply<M, [](
auto... v) {
return (v + ...); }>(a);
403 template <
int M = kSize>
405 static_assert(M >= 1 && M <=
kSize,
"Unsupported M");
406 return FoldApply<M, [](
auto... v) {
return (v * ...); }>(a);
409 template <
int M = kSize>
411 static_assert(M > 0 && M <=
kSize,
"Unsupported M");
412 auto eval = []<
size_t... I>(
Simd const& a,
Simd const& b, std::index_sequence<I...>) {
415 return eval(a, b, std::make_index_sequence<M>{});
423 auto eval = []<
size_t... I>(
424 auto const& mask,
Simd const& a,
Simd const& b, std::index_sequence<I...>) {
425 return Simd{(std::bit_cast<UIntT>(mask.raw[I]) != UIntT{0} ? a.raw[I] : b.raw[I])...};
427 return eval(mask, a, b, std::make_index_sequence<N>{});
432 static constexpr auto Sqrt = Xapply<std::sqrt>;
433 static constexpr auto Abs = Xapply<std::abs>;
434 static constexpr auto Floor = Xapply<std::floor>;
436 static constexpr auto RcpApprox = Xapply<[](ST a) {
return ST{1} / a; }>;
437 static constexpr auto RcpSqrtApprox = Xapply<[](ST a) {
return ST{1} / std::sqrt(a); }>;
438 static constexpr auto Cos = Xapply<std::cos>;
439 static constexpr auto Sin = Xapply<std::sin>;
440 static constexpr auto Tan = Xapply<std::tan>;
441 static constexpr auto ACos = Xapply<std::acos>;
442 static constexpr auto ASin = Xapply<std::asin>;
443 static constexpr auto ATan = Xapply<std::atan>;
444 static constexpr auto Exp = Xapply<std::exp>;
445 static constexpr auto Ln = Xapply<std::log>;
446 static constexpr auto Tanh = Xapply<std::tanh>;
449 static constexpr auto Min = XYapply<T const&, superdex::Min<T>>;
450 static constexpr auto Max = XYapply<T const&, superdex::Max<T>>;
453 return Simd([](T a, T b) {
return a == b ?
Scalar{kOnesMask} :
Scalar{0}; }, x, y);
457 return Simd([](T a, T b) {
return a != b ?
Scalar{kOnesMask} :
Scalar{0}; }, x, y);
461 static constexpr auto MulAdd = XYZapply<T, superdex::MulAdd<T, T, T>>;
462 static constexpr auto MulSub = XYZapply<T, superdex::MulSub<T, T, T>>;
463 static constexpr auto NegMulAdd = XYZapply<T, superdex::NegMulAdd<T, T, T>>;
464 static constexpr auto NegMulSub = XYZapply<T, superdex::NegMulSub<T, T, T>>;
468 return Simd([](T a) {
return -a; }, *
this);
473 return Simd([](T a) {
return std::bit_cast<Scalar>(~std::bit_cast<UIntT>(a)); }, *
this);
492 auto eval = []<
size_t... I>(
Simd const& lhs,
Simd const& rhs, std::index_sequence<I...>) {
493 return ((lhs.raw[I] == rhs.
raw[I]) && ...);
495 return eval(*
this, rhs, std::make_index_sequence<N>{});
499 return !(*
this == rhs);
502 template <
int kShift>
507#undef MOCHI_SIMD_EMULATOR_OP_1
508#undef MOCHI_SIMD_EMULATOR_OP_2
509#undef MOCHI_SIMD_EMULATOR_HREDUCE_BOOL
512template <
class To,
class FromT,
int FromN>
516 if constexpr (std::is_same_v<std::decay_t<To>, Simd<FromT, FromN>>) {
519 static_assert(
sizeof(To) ==
sizeof(FromT) * FromN,
"Size mismatch");
521 std::memcpy(&out.raw, &in.raw,
sizeof(in.raw));
526template <
class To,
class FromT,
int FromN>
533 static_assert(To::kSize == FromN,
"Size mismatch");
534 auto eval = [&in]<
size_t... I>(std::index_sequence<I...>) {
535 return To{
static_cast<typename To::Scalar
>(in.raw[I])...};
537 return eval(std::make_index_sequence<FromN>{});
Scalar constexpr operator[](int i) const
static constexpr Simd Broadcast(Simd v)
static constexpr auto ASin
static constexpr Scalar HMax(Simd a)
static constexpr Simd Blend(Simd a, Simd b)
static constexpr auto NegMulSub
static constexpr bool AnyTrue(Simd a)
static int StoreSelected(Scalar *ptr, Simd condition, Simd values)
static constexpr Simd Broadcast(Scalar const *p)
Scalar constexpr Get(int i) const
static constexpr Simd Set(Simd v, int i, Scalar value)
constexpr Simd & operator=(U a)
static void Store(Scalar *ptr, Simd v)
constexpr bool operator!=(Simd rhs) const
Array< Scalar, N > NativeType
static void LoadTransposed(Scalar const *ptr, Simd &out0, Simd &out1, Simd &out2)
static constexpr Simd Dot(Simd a, Simd b)
static constexpr Scalar HMin(Simd a)
static constexpr Scalar Get(Simd v, int i)
static constexpr bool AllTrue(Simd a)
static constexpr Simd Zero()
static constexpr auto Min
static constexpr auto Tan
static constexpr auto Sqrt
static constexpr auto Floor
static Simd NotEqual(Simd const &x, Simd const &y)
constexpr Simd operator~() const
constexpr Simd(Simd const &rhs)=default
static constexpr auto Sin
static Simd Load(Scalar const *ptr, int n)
static constexpr Scalar Get(Simd v)
static constexpr bool kIsComposite
static constexpr auto Max
static constexpr Simd Select(Simd mask, Simd a, Simd b)
constexpr bool operator==(Simd rhs) const
static constexpr Simd AsPoint(Simd a)
static constexpr auto RcpApprox
static constexpr Simd Shuffle(Simd a, Simd b)
constexpr Simd(Simd< T, N/2 > a, Simd< T, N/2 > b)
static constexpr auto Cos
static constexpr auto Tanh
static constexpr Simd SetBasisVector()
static constexpr Scalar HProd(Simd a)
constexpr Simd & operator=(Simd const &rhs)=default
static constexpr Simd AsDirection(Simd a)
static constexpr auto Abs
static void Store(Scalar *ptr, Simd v, int n)
static constexpr bool kIsEmulated
static constexpr auto ATan
static void StoreTransposed(Scalar *ptr, Simd out0, Simd out1, Simd out2)
static constexpr size_t size()
static constexpr Simd ShiftRight(Simd a)
static constexpr auto MulSub
static constexpr bool kIsSupported
static Simd Load(Scalar const *ptr)
static constexpr auto Exp
constexpr Simd operator-() const
static constexpr auto RcpSqrtApprox
static constexpr Simd< T, N/2 > GetHalf(Simd v)
static Simd Equal(Simd const &x, Simd const &y)
static constexpr Scalar HSum(Simd a)
static constexpr auto FastRound
static Simd LoadIndexed(Scalar const *ptr, Simd< IntT, N > const &indices)
static constexpr Simd Shuffle(Simd v)
static constexpr Simd Shuffle(Simd a)
static constexpr auto MulAdd
static constexpr auto ACos
constexpr Simd(F const &f, Args const &... args)
static constexpr auto NegMulAdd
static constexpr int kSize
constexpr Simd(NativeType const &rhs)
static Simd Set(Simd v, Scalar value)
static constexpr bool kIsSupported
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
constexpr int kSimdDefaultSize
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)
constexpr T const & Max(T const &a, T const &b)
#define MOCHI_SIMD_EMULATOR_OP_2(Op)
#define MOCHI_SIMD_EMULATOR_HREDUCE_BOOL(Name, Op)
#define MOCHI_SIMD_EMULATOR_OP_1(Op, Expr)