SuperDex Physics C++ API
Loading...
Searching...
No Matches
arm_simd_float_4_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 "arm_simd_inl.h" // for IntelliSense
20
21#if MOCHI_USE_SIMD && MOCHI_ARCH_ARM_NEON
22
23namespace superdex {
24
25/***********************************************************************************************
26 Simd<float, 4>
27*/
28template <>
29class Simd<float, 4> {
30 public:
31 MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(float, 4, float32x4_t);
32
33 MOCHI_FORCE_INLINE Simd(float a, float b, float c = 0.0f, float d = 0.0f) : raw{a, b, c, d} {}
34 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
35 MOCHI_FORCE_INLINE Simd(U a) : raw{vdupq_n_f32(a)} {}
36
37 template <int i>
38 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar Get(Simd v) {
39 static_assert(i >= 0 && i < 4, "Index out of range");
40 return vgetq_lane_f32(v.raw, i);
41 }
42
43 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar Get(Simd v, int i) {
44 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
45 return v.raw[i];
46 }
47
48 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Set(Simd v, int i, Scalar value) {
49 MOCHI_ASSERT_VERBOSE(i >= 0 && i < kSize, "Index out of range");
50 auto result = v;
51 result.raw[i] = value;
52 return result;
53 }
54
55 template <int i>
56 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Set(Simd v, Scalar value) {
57 static_assert(i >= 0 && i < kSize, "Index out of range");
58 return Set(v, i, value);
59 }
60
61 [[nodiscard]] MOCHI_FORCE_INLINE static Simd AsPoint(Simd a) {
62 return vsetq_lane_f32(1.0f, a.raw, 3);
63 }
64
65 [[nodiscard]] MOCHI_FORCE_INLINE static Simd AsDirection(Simd a) {
66 return vsetq_lane_f32(0.0f, a.raw, 3);
67 }
68
69 template <int x, int y, int z, int w>
70 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Blend(Simd a, Simd b) {
71 static_assert(
72 x >= 0 && x <= 1 && y >= 0 && y <= 1 && z >= 0 && z <= 1 && w >= 0 && w <= 1,
73 "invalid blend index");
74 int constexpr kCount = x + y + z + w;
75 if constexpr (kCount == 0) {
76 return a;
77 } else if constexpr (kCount == 4) {
78 return b;
79 } else if constexpr (kCount == 1) {
80 // Replace one lane of a with the corresponding lane of b.
81 int constexpr kLane = x ? 0 : (y ? 1 : (z ? 2 : 3));
82 return vcopyq_laneq_f32(a.raw, kLane, b.raw, kLane);
83 } else if constexpr (kCount == 3) {
84 // Replace one lane of b with the corresponding lane of a.
85 int constexpr kLane = !x ? 0 : (!y ? 1 : (!z ? 2 : 3));
86 return vcopyq_laneq_f32(b.raw, kLane, a.raw, kLane);
87 } else if constexpr (x == 1 && y == 1) { // <1,1,0,0>: low half from b, high from a
88 return vcombine_f32(vget_low_f32(b.raw), vget_high_f32(a.raw));
89 } else if constexpr (z == 1 && w == 1) { // <0,0,1,1>: low half from a, high from b
90 return vcombine_f32(vget_low_f32(a.raw), vget_high_f32(b.raw));
91 } else {
92 // Remaining patterns: <1,0,1,0>, <0,1,0,1>, <1,0,0,1>, <0,1,1,0>.
93 // mask lane = -1 -> select from a; mask lane = 0 -> select from b.
94 auto mask = int32x4_t{x ? 0 : -1, y ? 0 : -1, z ? 0 : -1, w ? 0 : -1};
95 return Select(vreinterpretq_f32_s32(mask), a, b);
96 }
97 }
98
99 template <int N>
100 [[nodiscard]] MOCHI_FORCE_INLINE static bool AllTrue(Simd v) {
101 static_assert(N >= 1 && N <= 4, "Invalid number of components");
102 uint64_t mask =
103 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_f32(v.raw))), 0);
104 if constexpr (N == kSize) {
105 return mask == 0xFFFFFFFFFFFFFFFFULL;
106 } else {
107 int constexpr kNumBits = N * 16; // 64-bit mask has 16 bits per lane
108 auto constexpr kMustBeSet = (uint64_t(1) << kNumBits) - 1;
109 return (mask & kMustBeSet) == kMustBeSet;
110 }
111 }
112
113 template <int N>
114 [[nodiscard]] MOCHI_FORCE_INLINE static bool AnyTrue(Simd v) {
115 static_assert(N >= 1 && N <= 4, "Invalid number of components");
116 uint64_t mask =
117 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_f32(v.raw))), 0);
118 if constexpr (N == kSize) {
119 return mask != 0;
120 } else {
121 int constexpr kNumBits = N * 16; // 64-bit mask has 16 bits per lane
122 auto constexpr kMayBeSet = (uint64_t(1) << kNumBits) - 1;
123 return (mask & kMayBeSet) != 0;
124 }
125 }
126
127 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Broadcast(float const* p) {
128 return Simd{*p};
129 }
130
131 template <int i>
132 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Broadcast(Simd v) {
133 return vdupq_laneq_f32(v.raw, i);
134 }
135
136 template <int N = kSize>
137 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Load([[maybe_unused]] float const* ptr) {
138 static_assert(N >= 0 && N <= 4);
139 if constexpr (N == 0) {
140 return Simd::Zero();
141 } else if constexpr (N == 1) {
142 return Simd{ptr[0], 0, 0, 0};
143 } else if constexpr (N == 2) {
144 return Simd{ptr[0], ptr[1], 0, 0};
145 } else if constexpr (N == 3) {
146 return Simd{ptr[0], ptr[1], ptr[2], 0};
147 } else {
148 return vld1q_f32(ptr);
149 }
150 }
151
152 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Load(Scalar const* ptr, int n) {
153 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
154 // clang-format off
155 switch (n) {
156 case 1: return Load<1>(ptr);
157 case 2: return Load<2>(ptr);
158 case 3: return Load<3>(ptr);
159 case 4: return Load<4>(ptr);
160 MOCHI_UNLIKELY default: return Zero();
161 } // clang-format on
162 }
163
164 [[nodiscard]] MOCHI_FORCE_INLINE static Simd LoadIndexed(
165 float const* ptr,
166 Simd<int, 4> const& indices) {
167 return Simd<float, 4>{
168 ptr[indices.raw[0]], ptr[indices.raw[1]], ptr[indices.raw[2]], ptr[indices.raw[3]]};
169 }
170
171 template <int kTupleCount = kSize>
172 MOCHI_FORCE_INLINE static void
173 LoadTransposed(float const* ptr, Simd& out0, Simd& out1, Simd& out2) {
174 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Unsupported kTupleCount");
175 if constexpr (kTupleCount == 1) {
176 out0.raw = float32x4_t{ptr[0], 0.0f, 0.0f, 0.0f};
177 out1.raw = float32x4_t{ptr[1], 0.0f, 0.0f, 0.0f};
178 out2.raw = float32x4_t{ptr[2], 0.0f, 0.0f, 0.0f};
179 } else if constexpr (kTupleCount == 2) {
180 out0.raw = float32x4_t{ptr[0], ptr[3], 0.0f, 0.0f};
181 out1.raw = float32x4_t{ptr[1], ptr[4], 0.0f, 0.0f};
182 out2.raw = float32x4_t{ptr[2], ptr[5], 0.0f, 0.0f};
183 } else if constexpr (kTupleCount == 3) {
184 out0.raw = float32x4_t{ptr[0], ptr[3], ptr[6], 0.0f};
185 out1.raw = float32x4_t{ptr[1], ptr[4], ptr[7], 0.0f};
186 out2.raw = float32x4_t{ptr[2], ptr[5], ptr[8], 0.0f};
187 } else { // kTupleCount == 4 (kSize)
188 float32x4x3_t result = vld3q_f32(ptr);
189 out0.raw = result.val[0];
190 out1.raw = result.val[1];
191 out2.raw = result.val[2];
192 }
193 }
194
195 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Select(Simd mask, Simd a, Simd b) {
196 return vbslq_f32(vreinterpretq_u32_f32(mask.raw), a.raw, b.raw);
197 }
198
199 template <int i>
200 [[nodiscard]] MOCHI_FORCE_INLINE static Simd SetBasisVector() {
201 static_assert(i >= 0 && i <= 3, "Invalid component index");
202 auto zeros = vdupq_n_f32(0);
203 return vsetq_lane_f32(1.0f, zeros, i);
204 }
205
206 template <int x = 0, int y = 1, int z = 2, int w = 3>
207 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Shuffle(Simd a, Simd b) {
208 static_assert(
209 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4, "Invalid index");
210 return Simd{a.raw[x], a.raw[y], b.raw[z], b.raw[w]};
211 }
212
213 template <int x = 0, int y = 1, int z = 2, int w = 3>
214 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Shuffle(Simd v) {
215 return Shuffle<x, y, z, w>(v, v);
216 }
217
218 [[nodiscard]] MOCHI_FORCE_INLINE static Simd SignBitMask() {
219 return vreinterpretq_f32_s32(vdupq_n_s32((int32_t)0x80000000));
220 }
221
222 template <int N = kSize>
223 MOCHI_FORCE_INLINE static void Store([[maybe_unused]] float* ptr, [[maybe_unused]] Simd v) {
224 static_assert(N >= 0 && N <= kSize);
225 if constexpr (N == 0) {
226 } else if constexpr (N < kSize) {
227 memcpy(ptr, &v, sizeof(float) * N);
228 } else {
229 vst1q_f32(ptr, v.raw);
230 }
231 }
232
233 MOCHI_FORCE_INLINE static void Store(Scalar* ptr, Simd v, int n) {
234 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
235 // clang-format off
236 switch (n) {
237 case 1: Store<1>(ptr, v); break;
238 case 2: Store<2>(ptr, v); break;
239 case 3: Store<3>(ptr, v); break;
240 case 4: Store<4>(ptr, v); break;
241 MOCHI_UNLIKELY default: break;
242 } // clang-format on
243 }
244
245 MOCHI_FORCE_INLINE static int StoreSelected(float* ptr, Simd condition, Simd values) {
246 uint32x4_t shifted = vshrq_n_u32(vreinterpretq_u32_f32(condition.raw), 31);
247 uint32x4_t const multipliers = {1, 2, 4, 8};
248 uint32x4_t weighted = vmulq_u32(shifted, multipliers);
249 uint32_t count = vaddvq_u32(shifted);
250 uint32_t mask = vaddvq_u32(weighted);
251 uint8x16_t pattern = vld1q_u8(arm_simd::kStoreSelectedShuffleTableS4[mask]);
252 uint8x16_t packed = vqtbl1q_u8(vreinterpretq_u8_f32(values.raw), pattern);
253 vst1q_f32(ptr, vreinterpretq_f32_u8(packed));
254 return static_cast<int>(count);
255 }
256
257 template <int kTupleCount = kSize>
258 MOCHI_FORCE_INLINE static void StoreTransposed(float* ptr, Simd a, Simd b, Simd c) {
259 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
260 if constexpr (kTupleCount == 1) {
261 ptr[0] = a.raw[0];
262 ptr[1] = b.raw[0];
263 ptr[2] = c.raw[0];
264 } else if constexpr (kTupleCount == 2) {
265 Simd::Store(ptr, Simd{a[0], b[0], c[0], a[1]});
266 ptr[4] = b.raw[1];
267 ptr[5] = c.raw[1];
268 } else if constexpr (kTupleCount == 3) {
269 Simd::Store(ptr + 0, Simd{a[0], b[0], c[0], a[1]});
270 Simd::Store(ptr + 4, Simd{b[1], c[1], a[2], b[2]});
271 ptr[8] = c.raw[2];
272 } else {
273 vst3q_f32(ptr, float32x4x3_t({a.raw, b.raw, c.raw}));
274 }
275 }
276
277 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Sqrt(Simd v) {
278 return vsqrtq_f32(v.raw);
279 }
280
281 [[nodiscard]] MOCHI_FORCE_INLINE static Simd RcpApprox(Simd v) {
282 return vrecpeq_f32(v.raw);
283 }
284
285 [[nodiscard]] MOCHI_FORCE_INLINE static Simd RcpSqrtApprox(Simd v) {
286 return vrsqrteq_f32(v.raw);
287 }
288
289 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Abs(Simd v) {
290 return vabsq_f32(v.raw);
291 }
292
293 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Min(Simd a, Simd b) {
294 return vminq_f32(a.raw, b.raw);
295 }
296
297 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Max(Simd a, Simd b) {
298 return vmaxq_f32(a.raw, b.raw);
299 }
300
301 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Floor(Simd a) {
302 return vrndmq_f32(a.raw);
303 }
304
305 [[nodiscard]] MOCHI_FORCE_INLINE static Simd FastRound(Simd v) {
306 return vrndaq_f32(v.raw);
307 }
308
309 [[nodiscard]] MOCHI_FORCE_INLINE static Simd MulAdd(Simd a, Simd b, Simd c) {
310 return vfmaq_f32(c.raw, b.raw, a.raw);
311 }
312
313 [[nodiscard]] MOCHI_FORCE_INLINE static Simd MulSub(Simd a, Simd b, Simd c) {
314 return MulAdd(a, b, -c);
315 }
316
317 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NegMulAdd(Simd a, Simd b, Simd c) {
318 return vfmsq_f32(c.raw, b.raw, a.raw);
319 }
320
321 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NegMulSub(Simd a, Simd b, Simd c) {
322 return NegMulAdd(a, b, -c);
323 }
324
325 template <int N = 4>
326 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HMin(Simd a) {
327 static_assert(N >= 2 && N <= 4, "Unsupported N");
328 if constexpr (N == 2) {
329 return superdex::Min(a.raw[0], a.raw[1]);
330 } else if constexpr (N == 3) {
331 return vminvq_f32(vsetq_lane_f32(std::numeric_limits<Scalar>::infinity(), a.raw, 3));
332 } else {
333 return vminvq_f32(a.raw);
334 }
335 }
336
337 template <int N = 4>
338 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HMax(Simd a) {
339 static_assert(N >= 2 && N <= 4, "Unsupported N");
340 if constexpr (N == 2) {
341 return superdex::Max(a.raw[0], a.raw[1]);
342 } else if constexpr (N == 3) {
343 // HMax({x, y, z, -infinity})
344 return vmaxvq_f32(vsetq_lane_f32(-std::numeric_limits<Scalar>::infinity(), a.raw, 3));
345 } else {
346 return vmaxvq_f32(a.raw);
347 }
348 }
349
350 template <int N>
351 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HSum(Simd a) {
352 static_assert(N >= 2 && N <= 4, "Unsupported N");
353 if constexpr (N == 2) {
354 return a.raw[0] + a.raw[1];
355 } else if constexpr (N == 3) {
356 return a.raw[0] + a.raw[1] + a.raw[2];
357 } else if constexpr (N == 4) {
358 return vaddvq_f32(a.raw);
359 }
360 }
361
362 template <int N>
363 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HProd(Simd a) {
364 static_assert(N >= 2 && N <= 4, "Unsupported N");
365 if constexpr (N == 2) {
366 return a.raw[0] * a.raw[1];
367 } else if constexpr (N == 3) {
368 return a.raw[0] * a.raw[1] * a.raw[2];
369 } else if constexpr (N == 4) {
370 return a.raw[0] * a.raw[1] * a.raw[2] * a.raw[3];
371 }
372 }
373
374 template <int N>
375 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Dot(Simd a, Simd b) {
376 static_assert(N >= 2 && N <= 4, "Unsupported N");
377 return Simd{HSum<N>(a * b)};
378 }
379
380 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<(Simd rhs) const {
381 return vreinterpretq_f32_u32(vcltq_f32(this->raw, rhs.raw));
382 }
383
384 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>(Simd rhs) const {
385 return vreinterpretq_f32_u32(vcgtq_f32(this->raw, rhs.raw));
386 }
387
388 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<=(Simd rhs) const {
389 return vreinterpretq_f32_u32(vcleq_f32(this->raw, rhs.raw));
390 }
391
392 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>=(Simd rhs) const {
393 return vreinterpretq_f32_u32(vcgeq_f32(this->raw, rhs.raw));
394 }
395
396 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Equal(Simd a, Simd b) {
397 return vreinterpretq_f32_u32(vceqq_f32(a.raw, b.raw));
398 }
399
400 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NotEqual(Simd a, Simd b) {
401 return ~Equal(a, b);
402 }
403
404 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Zero() {
405 return vdupq_n_f32(0);
406 }
407
408 [[nodiscard]] MOCHI_FORCE_INLINE bool operator==(Simd rhs) const {
409 return AllTrue<kSize>(Equal(*this, rhs));
410 }
411
412 [[nodiscard]] MOCHI_FORCE_INLINE bool operator!=(Simd rhs) const {
413 return !(*this == rhs);
414 }
415
416 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator~() const {
417 return vreinterpretq_f32_u32(vmvnq_u32(vreinterpretq_u32_f32(raw)));
418 }
419
420 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-() const {
421 return vnegq_f32(raw);
422 }
423
424 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator+(Simd rhs) const {
425 return vaddq_f32(raw, rhs.raw);
426 }
427
428 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-(Simd rhs) const {
429 return vsubq_f32(raw, rhs.raw);
430 }
431
432 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator*(Simd rhs) const {
433 return vmulq_f32(raw, rhs.raw);
434 }
435
436 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator/(Simd rhs) const {
437 return vdivq_f32(raw, rhs.raw);
438 }
439
440 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator&(Simd rhs) const {
441 return vreinterpretq_f32_u32(
442 vandq_u32(vreinterpretq_u32_f32(raw), vreinterpretq_u32_f32(rhs.raw)));
443 }
444
445 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator|(Simd rhs) const {
446 return vreinterpretq_f32_u32(
447 vorrq_u32(vreinterpretq_u32_f32(raw), vreinterpretq_u32_f32(rhs.raw)));
448 }
449
450 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator^(Simd rhs) const {
451 return vreinterpretq_f32_u32(
452 veorq_u32(vreinterpretq_u32_f32(raw), vreinterpretq_u32_f32(rhs.raw)));
453 }
454};
455
456} // namespace superdex
457
458#endif // MOCHI_USE_SIMD && MOCHI_ARCH_ARM_NEON
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
NativeType raw
Definition simd.h:174
Simd operator>(Simd rhs) const
Simd operator*(Simd rhs) const
Simd operator^(Simd rhs) const
Simd operator-() const
Simd operator>=(Simd rhs) const
Simd operator<(Simd rhs) const
bool operator!=(Simd rhs) const
Simd operator|(Simd rhs) const
static constexpr int kSize
Definition simd.h:96
Simd operator+(Simd rhs) const
Simd operator~() const
Simd operator/(Simd rhs) const
Simd operator<=(Simd rhs) const
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
Definition debug.h:102
#define MOCHI_UNLIKELY
#define MOCHI_FORCE_INLINE
T Dot(Simd< T, N > a, Simd< T, N > b)
Definition simd.h:666
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
Definition simd_inl.h:270
V LoadIndexed(typename V::Scalar const *ptr, Simd< I, V::kSize > indices)
Definition simd_inl.h:200
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
T HSum(Simd< T, N > a)
Definition simd_inl.h:379
T HMin(Simd< T, N > a)
Definition simd_inl.h:391
bool AllTrue(T const &a)
Definition basic_utils.h:60
constexpr auto MulAdd(A a, B b, C c)
Simd< T, N > Set(Simd< T, N > a, T value)
Definition simd_inl.h:315
constexpr auto NotEqual(T const &a, T const &b)
T HMax(Simd< T, N > a)
Definition simd_inl.h:397
V Broadcast(typename V::Scalar a)
Definition simd_inl.h:115
constexpr T Abs(T a)
Definition basic_utils.h:50
constexpr auto MulSub(A a, B b, C c)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
Definition simd_inl.h:285
bool AnyTrue(T const &a)
Definition basic_utils.h:66
constexpr T Select(bool condition, T a, T b)
constexpr T Sqrt(T a)
constexpr auto NegMulAdd(A a, B b, C c)
T Get(Simd< T, N > v)
Definition simd_inl.h:300
constexpr T Floor(T a)
T HProd(Simd< T, N > a)
Definition simd_inl.h:385
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)
Definition simd_inl.h:207
Simd< T, N > FastRound(Simd< T, N > a)
Definition simd_inl.h:416
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
Definition simd_inl.h:245
void Store(T *ptr, Simd< T, N > a)
Definition simd_inl.h:213
Simd< T, N > RcpSqrtApprox(Simd< T, N > a)
Definition simd_inl.h:359
constexpr T RcpApprox(T a)
constexpr auto NegMulSub(A a, B b, C c)
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
Definition simd_inl.h:225
V Load(typename V::Scalar const *ptr)
Definition simd_inl.h:184
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)