SuperDex Physics C++ API
Loading...
Searching...
No Matches
simd_arch_emulator_inl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include "../simd.h" // for Intelisense
20
21#if !MOCHI_USE_SIMD
24
25#include <array>
26#include <bit>
27#include <cmath>
28#include <cstring>
29#include <type_traits>
30#include <utility>
31
32namespace superdex {
33
34template <typename F, typename... Args>
35concept IsInvokableWithRaw = requires(F&& f, Args&&... args) { f(args.raw[0]...); };
36
37// NOTE: This specialization could support any size N, but it is currently restricted to multiples
38// of the default size to better match the behavior of native SIMD implementations.
39//
40// NOTE: Half (16-bit float) is not currently supported for emulation. We could add this
41// functionality in the future for compilers with native support (e.g. using __half for CUDA).
42//
43// PERFORMANCE: The artificial size restriction sometimes causes users to round up to the next
44// multiple of the default size, resulting in wasted loads, stores, and ALU operations. If
45// performance of SIMD emulation (e.g. GPU kernel) is important, then consider removing the
46// restriction.
47//
48template <typename T, int N>
49 requires(IsSimdSupportedType<T> && !IsHalf<T> && (N % kSimdDefaultSize<T> == 0))
51 using UIntT = std::conditional_t<sizeof(T) == 4, uint32_t, uint64_t>;
52 static constexpr T kOnesMask = std::bit_cast<T>(~UIntT{0});
53
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)...}; \
58 }; \
59 return eval(*this, rhs, std::make_index_sequence<N>{}); \
60 }
61
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...>) { \
65 return Simd( \
66 std::bit_cast<Scalar>(std::bit_cast<UIntT>(lhs.raw[I]) \
67 Op std::bit_cast<UIntT>(rhs.raw[I]))...); \
68 }; \
69 return eval(*this, rhs, std::make_index_sequence<N>{}); \
70 }
71
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...); \
78 }; \
79 return eval(a, std::make_index_sequence<M>{}); \
80 }
81
82 template <int M = N, auto F>
83 MOCHI_ANY MOCHI_FORCE_INLINE static T FoldApply(Simd a) {
84 auto eval = []<size_t... I>(Simd const& x, std::index_sequence<I...>) {
85 return F(x.raw[I]...);
86 };
87 return eval(a, std::make_index_sequence<M>{});
88 }
89
90 using ST = std::conditional_t<std::floating_point<T>, T, float>;
91 template <ST (*F)(ST)>
92 MOCHI_ANY MOCHI_FORCE_INLINE static Simd Xapply(
93 std::conditional_t<std::floating_point<T>, Simd, struct DoesNotExist>& x) {
94 if constexpr (std::floating_point<T>) {
95 return Simd(F, x);
96 } else {
97 return Simd{};
98 }
99 }
100
101 template <typename S, S (*F)(S, S)>
102 MOCHI_ANY MOCHI_FORCE_INLINE static Simd XYapply(Simd const& x, Simd const& y) {
103 return Simd(F, x, y);
104 }
105
106 template <typename S, S (*F)(S, S, S)>
107 MOCHI_ANY MOCHI_FORCE_INLINE static Simd XYZapply(Simd const& x, Simd const& y, Simd const& z) {
108 return Simd(F, x, y, z);
109 }
110
111 public:
112 static constexpr int kSize = N;
113 static constexpr bool kIsSupported = true;
114 static constexpr bool kIsComposite = false;
115 static constexpr bool kIsEmulated = true;
116 using Scalar = T;
117 // Warning: Do not use std::array as it is not supported in CUDA device code.
120
121 MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd() = default;
122 MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd(Simd const& rhs) = default;
123 MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd(NativeType const& rhs) : raw(rhs) {}
124 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
126 for (int i = 0; i < N; ++i) {
127 raw[i] = a;
128 }
129 }
131 requires(N > 2 && N % 2 == 0)
132 {
133 for (int i = 0; i < N / 2; ++i) {
134 raw[i] = a.raw[i];
135 }
136 for (int i = 0; i < N / 2; ++i) {
137 raw[(N / 2) + i] = b[i];
138 }
139 }
140
141 template <typename... Args>
142 requires(
143 sizeof...(Args) > 1 && sizeof...(Args) <= kSize && (std::convertible_to<Args, T> && ...))
144 MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd(Args... args)
145 : raw{static_cast<T>(std::forward<Args>(args))...} {}
146
147 template <typename F, typename... Args>
148 MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd(F const& f, Args const&... args)
149 requires IsInvokableWithRaw<F, Args...>
150 {
151 for (size_t i = 0; i < N; ++i) {
152 raw[i] = f(args.raw[i]...);
153 }
154 }
155
156 MOCHI_ANY MOCHI_FORCE_INLINE static constexpr size_t size() {
157 return static_cast<size_t>(kSize);
158 }
159
160 MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd& operator=(Simd const& rhs) = default;
161
162 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
164 for (int i = 0; i < N; ++i) {
165 raw[i] = a;
166 }
167 return *this;
168 }
169
170 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Zero() {
171 return NativeType{};
172 }
173
174 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE Scalar constexpr operator[](int i) const {
175 return raw[i];
176 }
177
178 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE Scalar constexpr Get(int i) const {
179 return raw[i];
180 }
181
182 template <int i>
183 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Scalar Get(Simd v) {
184 static_assert(i >= 0 && i < kSize, "Index out of range");
185 return v.raw[i];
186 }
187
188 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Scalar Get(Simd v, int i) {
189 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range.");
190 return v.raw[i];
191 }
192
193 template <int i>
194 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd<T, N / 2> GetHalf(Simd v)
195 requires(N >= 2 && N % 2 == 0)
196 {
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]...};
200 };
201 return eval(std::make_index_sequence<N / 2>{});
202 }
203
204 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd
205 Set(Simd v, int i, Scalar value) {
206 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range.");
207 auto result = v;
208 result.raw[i] = value;
209 return result;
210 }
211
212 template <int i>
213 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static Simd Set(Simd v, Scalar value) {
214 static_assert(i >= 0 && i < kSize, "Index out of range");
215 return Set(v, i, value);
216 }
217
218 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd AsPoint(Simd a)
219 requires(N == 4)
220 {
221 return {a.raw[0], a.raw[1], a.raw[2], Scalar{1}};
222 }
223
224 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd AsDirection(Simd a)
225 requires(N == 4)
226 {
227 return {a.raw[0], a.raw[1], a.raw[2], Scalar{0}};
228 }
229
230 template <int i>
231 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd SetBasisVector()
232 requires(N == 4)
233 {
234 static_assert(i >= 0 && i <= 3, "Invalid component index");
235 return {
236 i == 0 ? Scalar{1} : Scalar{0},
237 i == 1 ? Scalar{1} : Scalar{0},
238 i == 2 ? Scalar{1} : Scalar{0},
239 i == 3 ? Scalar{1} : Scalar{0}};
240 }
241
242 template <int... x>
243 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Blend(Simd a, Simd b)
244 requires(sizeof...(x) == N)
245 {
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])...};
249 };
250 return eval(a, b, std::make_index_sequence<N>{});
251 }
252
253 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Broadcast(Scalar const* p) {
254 return Simd{*p};
255 }
256
257 template <int i>
258 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Broadcast(Simd v) {
259 static_assert(i >= 0 && i < kSize, "Index out of range");
260 return Simd{v.raw[i]};
261 }
262
263 template <int x = 0, int y = 1>
264 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Shuffle(Simd a)
265 requires(N == 2)
266 {
267 static_assert(x >= 0 && x < 2 && y >= 0 && y < 2, "Invalid index");
268 return Simd{a.raw[x], a.raw[y]};
269 }
270
271 template <int x = 0, int y = 1, int z = 2, int w = 3>
272 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Shuffle(Simd a, Simd b)
273 requires(N == 4)
274 {
275 static_assert(
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]};
278 }
279
280 template <int x = 0, int y = 1, int z = 2, int w = 3>
281 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Shuffle(Simd v)
282 requires(N == 4)
283 {
284 return Shuffle<x, y, z, w>(v, v);
285 }
286
287 template <int M = kSize>
288 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static Simd Load([[maybe_unused]] Scalar const* ptr) {
289 static_assert(M >= 0 && M <= kSize);
290 if constexpr (M == 0) {
291 return Zero();
292 } else {
293 Simd result;
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));
297 }
298 return result;
299 }
300 }
301
302 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static Simd Load(Scalar const* ptr, int n) {
303 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid load size.");
304 Simd result;
305 for (int i = 0; i < n; ++i) {
306 result.raw[i] = ptr[i];
307 }
308 for (int i = n; i < kSize; ++i) {
309 result.raw[i] = Scalar{0};
310 }
311 return result;
312 }
313
314 template <typename IntT>
316 Scalar const* ptr,
317 Simd<IntT, N> const& indices)
318 requires(std::integral<IntT>)
319 {
320 auto eval = []<size_t... I>(
321 Scalar const* ptr, Simd<IntT, N> const& indices, std::index_sequence<I...>) {
322 return Simd{ptr[indices.raw[I]]...};
323 };
324 return eval(ptr, indices, std::make_index_sequence<N>{});
325 }
326
327 template <int kTupleCount = kSize>
328 MOCHI_ANY MOCHI_FORCE_INLINE static void
329 LoadTransposed(Scalar const* ptr, Simd& out0, Simd& out1, Simd& out2) {
330 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
331 constexpr Scalar zero{};
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)...};
337 };
338 eval(std::make_index_sequence<kSize>{});
339 }
340
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));
348 }
349 }
350
351 MOCHI_ANY MOCHI_FORCE_INLINE static void Store(Scalar* ptr, Simd v, int n) {
352 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid store size.");
353 for (int i = 0; i < n; ++i) {
354 ptr[i] = v.raw[i];
355 }
356 }
357
358 MOCHI_ANY MOCHI_FORCE_INLINE static int StoreSelected(Scalar* ptr, Simd condition, Simd values) {
359 int count = 0;
360 auto eval = [&count]<size_t... I>(
361 Scalar* ptr,
362 Simd<Scalar, N> const& condition,
363 Simd<Scalar, N> const& values,
364 std::index_sequence<I...>) {
365 ((ptr[count] = values.raw[I], count += static_cast<int>(!!condition.raw[I])), ...);
366 };
367 eval(ptr, condition, values, std::make_index_sequence<N>{});
368 return count;
369 }
370
371 template <int kTupleCount = kSize>
372 MOCHI_ANY MOCHI_FORCE_INLINE static void
373 StoreTransposed(Scalar* ptr, Simd out0, Simd out1, Simd out2) {
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])
379 : (void)0),
380 ...);
381 };
382 eval(std::make_index_sequence<kSize>{});
383 }
384
385 template <int M = kSize>
386 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Scalar HMin(Simd a) {
387 static_assert(M >= 1 && M <= kSize, "Unsupported M");
388 return FoldApply<M, [](auto... v) { return superdex::Min(v...); }>(a);
389 }
390
391 template <int M = kSize>
392 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Scalar HMax(Simd a) {
393 static_assert(M >= 1 && M <= kSize, "Unsupported M");
394 return FoldApply<M, [](auto... v) { return superdex::Max(v...); }>(a);
395 }
396
397 template <int M = kSize>
398 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Scalar HSum(Simd a) {
399 static_assert(M >= 1 && M <= kSize, "Unsupported M");
400 return FoldApply<M, [](auto... v) { return (v + ...); }>(a);
401 }
402
403 template <int M = kSize>
404 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Scalar HProd(Simd a) {
405 static_assert(M >= 1 && M <= kSize, "Unsupported M");
406 return FoldApply<M, [](auto... v) { return (v * ...); }>(a);
407 }
408
409 template <int M = kSize>
410 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd Dot(Simd a, Simd b) {
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...>) {
413 return Simd{((a.raw[I] * b.raw[I]) + ...)};
414 };
415 return eval(a, b, std::make_index_sequence<M>{});
416 }
417
420
421 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd
422 Select(Simd mask, Simd a, Simd b) {
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])...};
426 };
427 return eval(mask, a, b, std::make_index_sequence<N>{});
428 }
429
430 // Unary functions.
431 // TODO: SinCosImpl in simd_inl.h might be faster than this Cos() and Sin().
432 static constexpr auto Sqrt = Xapply<std::sqrt>;
433 static constexpr auto Abs = Xapply<std::abs>;
434 static constexpr auto Floor = Xapply<std::floor>;
435 static constexpr auto FastRound = Xapply<std::round>;
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>;
447
448 // Binary functions.
449 static constexpr auto Min = XYapply<T const&, superdex::Min<T>>;
450 static constexpr auto Max = XYapply<T const&, superdex::Max<T>>;
451
452 MOCHI_ANY MOCHI_FORCE_INLINE static Simd Equal(Simd const& x, Simd const& y) {
453 return Simd([](T a, T b) { return a == b ? Scalar{kOnesMask} : Scalar{0}; }, x, y);
454 }
455
456 MOCHI_ANY MOCHI_FORCE_INLINE static Simd NotEqual(Simd const& x, Simd const& y) {
457 return Simd([](T a, T b) { return a != b ? Scalar{kOnesMask} : Scalar{0}; }, x, y);
458 }
459
460 // Ternary functions.
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>>;
465
466 // Unary operators.
467 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd operator-() const {
468 return Simd([](T a) { return -a; }, *this);
469 }
470
471 // This one uses a function that may not work on CUDA (std::bit_cast)
472 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE constexpr Simd operator~() const {
473 return Simd([](T a) { return std::bit_cast<Scalar>(~std::bit_cast<UIntT>(a)); }, *this);
474 }
475
476 // Binary operators.
477 MOCHI_SIMD_EMULATOR_OP_1(<, lhs.raw[I] < rhs.raw[I] ? kOnesMask : Scalar{0});
478 MOCHI_SIMD_EMULATOR_OP_1(>, lhs.raw[I] > rhs.raw[I] ? kOnesMask : Scalar{0});
479 MOCHI_SIMD_EMULATOR_OP_1(<=, lhs.raw[I] <= rhs.raw[I] ? kOnesMask : Scalar{0});
480 MOCHI_SIMD_EMULATOR_OP_1(>=, lhs.raw[I] >= rhs.raw[I] ? kOnesMask : Scalar{0});
481 MOCHI_SIMD_EMULATOR_OP_1(+, lhs.raw[I] + rhs.raw[I]);
482 MOCHI_SIMD_EMULATOR_OP_1(-, lhs.raw[I] - rhs.raw[I]);
483 MOCHI_SIMD_EMULATOR_OP_1(*, lhs.raw[I] * rhs.raw[I]);
484 MOCHI_SIMD_EMULATOR_OP_1(/, lhs.raw[I] / rhs.raw[I]);
490
491 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE constexpr bool operator==(Simd rhs) const {
492 auto eval = []<size_t... I>(Simd const& lhs, Simd const& rhs, std::index_sequence<I...>) {
493 return ((lhs.raw[I] == rhs.raw[I]) && ...);
494 };
495 return eval(*this, rhs, std::make_index_sequence<N>{});
496 }
497
498 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE constexpr bool operator!=(Simd rhs) const {
499 return !(*this == rhs);
500 }
501
502 template <int kShift>
503 [[nodiscard]] MOCHI_ANY MOCHI_FORCE_INLINE static constexpr Simd ShiftRight(Simd a) {
504 return a >> kShift;
505 }
506
507#undef MOCHI_SIMD_EMULATOR_OP_1
508#undef MOCHI_SIMD_EMULATOR_OP_2
509#undef MOCHI_SIMD_EMULATOR_HREDUCE_BOOL
510};
511
512template <class To, class FromT, int FromN>
513MOCHI_ANY MOCHI_FORCE_INLINE To ReinterpretCast(Simd<FromT, FromN> const& in)
514 requires(IsSimd<To> && Simd<FromT, FromN>::kIsSupported)
515{
516 if constexpr (std::is_same_v<std::decay_t<To>, Simd<FromT, FromN>>) {
517 return in;
518 } else {
519 static_assert(sizeof(To) == sizeof(FromT) * FromN, "Size mismatch");
520 To out;
521 std::memcpy(&out.raw, &in.raw, sizeof(in.raw));
522 return out;
523 }
524}
525
526template <class To, class FromT, int FromN>
529{
530 if constexpr (std::is_same_v<std::decay_t<To>, Simd<FromT, FromN>>) {
531 return in;
532 } else {
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])...};
536 };
537 return eval(std::make_index_sequence<FromN>{});
538 }
539}
540
541} // namespace superdex
542#endif
Scalar constexpr operator[](int i) const
static constexpr Simd Broadcast(Simd v)
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
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 Floor
static Simd NotEqual(Simd const &x, Simd const &y)
constexpr Simd operator~() const
constexpr Simd(Simd const &rhs)=default
static Simd Load(Scalar const *ptr, int n)
static constexpr Scalar Get(Simd v)
static constexpr bool kIsComposite
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 Simd SetBasisVector()
static constexpr Scalar HProd(Simd a)
constexpr Simd & operator=(Simd const &rhs)=default
static constexpr Simd AsDirection(Simd a)
static void Store(Scalar *ptr, Simd v, int n)
static constexpr bool kIsEmulated
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)
constexpr Simd operator-() const
static constexpr auto RcpSqrtApprox
static constexpr Simd< T, N/2 > GetHalf(Simd v)
constexpr Simd()=default
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
constexpr Simd(F const &f, Args const &... args)
static constexpr auto NegMulAdd
constexpr Simd(NativeType const &rhs)
static Simd Set(Simd v, Scalar value)
NativeType raw
Definition simd.h:174
static constexpr bool kIsSupported
Definition simd.h:97
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_FORCE_INLINE
#define MOCHI_ANY
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
Definition simd_inl.h:270
constexpr int kSimdDefaultSize
Definition simd.h:33
constexpr T const & Min(T const &a, T const &b)
constexpr To StaticCast(From const &a)
Definition basic_utils.h:79
Simd< T, N > Set(Simd< T, N > a, T value)
Definition simd_inl.h:315
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)