SuperDex Physics C++ API
Loading...
Searching...
No Matches
arm_simd_double_2_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<double, 2> (NEON Implementation)
27*/
28template <>
29class Simd<double, 2> {
30 public:
31 MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(double, 2, float64x2_t);
32
33 MOCHI_FORCE_INLINE Simd(double a, double b) : raw{a, b} {}
34 template <class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
35 MOCHI_FORCE_INLINE Simd(U a) : raw{vdupq_n_f64(a)} {}
36
37 template <int i>
38 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar Get(Simd v) {
39 static_assert(i >= 0 && i < 2, "Index out of range");
40 return vgetq_lane_f64(v.raw, i);
41 }
42
43 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar Get(Simd v, int i) {
44 MOCHI_ASSERT_VERBOSE(i >= 0 && i < 2, "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 template <int N>
62 [[nodiscard]] MOCHI_FORCE_INLINE static bool AllTrue(Simd v) {
63 static_assert(N == 1 || N == 2, "Invalid N");
64 uint64_t mask =
65 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_f64(v.raw))), 0);
66 if constexpr (N == 1) {
67 return (mask & 0x00000000FFFFFFFFULL) == 0x00000000FFFFFFFFULL;
68 } else {
69 return mask == 0xFFFFFFFFFFFFFFFFULL;
70 }
71 }
72
73 template <int N>
74 [[nodiscard]] MOCHI_FORCE_INLINE static bool AnyTrue(Simd v) {
75 static_assert(N == 1 || N == 2, "Invalid N");
76 uint64_t mask =
77 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_f64(v.raw))), 0);
78 if constexpr (N == 1) {
79 return (mask & 0x00000000FFFFFFFFULL) == 0x00000000FFFFFFFFULL;
80 } else {
81 return mask != 0;
82 }
83 }
84
85 template <int x, int y>
86 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Blend(Simd a, Simd b) {
87 static_assert(x >= 0 && x <= 1 && y >= 0 && y <= 1, "invalid blend index");
88 if constexpr (x == 0 && y == 0) {
89 return a;
90 } else if constexpr (x == 1 && y == 1) {
91 return b;
92 } else {
93 auto mask = vreinterpretq_f64_s64(
94 int64x2_t{x ? (int64_t)0 : (int64_t)-1, y ? (int64_t)0 : (int64_t)-1});
95 return Select(mask, a, b);
96 }
97 }
98
99 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Broadcast(double const* p) {
100 return Simd{*p};
101 }
102
103 template <int i>
104 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Broadcast(Simd v) {
105 static_assert(i >= 0 && i < 2, "Index out of range");
106 return vdupq_laneq_f64(v.raw, i);
107 }
108
109 template <int N = kSize>
110 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Load([[maybe_unused]] double const* ptr) {
111 static_assert(N >= 0 && N <= kSize);
112 if constexpr (N == 0) {
113 return Simd::Zero();
114 } else if constexpr (N == 1) {
115 return Simd{*ptr, 0.0};
116 } else {
117 return vld1q_f64(ptr);
118 }
119 }
120
121 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Load(Scalar const* ptr, int n) {
122 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
123 // clang-format off
124 switch (n) {
125 case 1: return Load<1>(ptr);
126 case 2: return Load<2>(ptr);
127 MOCHI_UNLIKELY default: return Zero();
128 } // clang-format on
129 }
130
131 [[nodiscard]] MOCHI_FORCE_INLINE static Simd LoadIndexed(
132 double const* ptr,
133 Simd<int64_t, 2> const& indices) {
134 return Simd<double, 2>{ptr[indices.raw[0]], ptr[indices.raw[1]]};
135 }
136
137 template <int kTupleCount = kSize>
138 MOCHI_FORCE_INLINE static void
139 LoadTransposed(double const* ptr, Simd& out0, Simd& out1, Simd& out2) {
140 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Unsupported kTupleCount");
141 if constexpr (kTupleCount == 1) {
142 out0.raw = float64x2_t{ptr[0], 0.0};
143 out1.raw = float64x2_t{ptr[1], 0.0};
144 out2.raw = float64x2_t{ptr[2], 0.0};
145 } else {
146 float64x2x3_t result = vld3q_f64(ptr);
147 out0.raw = result.val[0];
148 out1.raw = result.val[1];
149 out2.raw = result.val[2];
150 }
151 }
152
153 template <int N = kSize>
154 static void Store([[maybe_unused]] double* ptr, [[maybe_unused]] Simd v) {
155 static_assert(N >= 0 && N <= kSize);
156 if constexpr (N == 0) {
157 } else if constexpr (N == 1) {
158 *ptr = Get<0>(v);
159 } else {
160 vst1q_f64(ptr, v.raw);
161 }
162 }
163
164 MOCHI_FORCE_INLINE static void Store(Scalar* ptr, Simd v, int n) {
165 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
166 // clang-format off
167 switch (n) {
168 case 1: Store<1>(ptr, v); break;
169 case 2: Store<2>(ptr, v); break;
170 MOCHI_UNLIKELY default: break;
171 } // clang-format on
172 }
173
174 MOCHI_FORCE_INLINE static int StoreSelected(double* ptr, Simd condition, Simd values) {
175 uint64x2_t shifted = vshrq_n_u64(vreinterpretq_u64_f64(condition.raw), 63);
176 uint32_t bit0 = vgetq_lane_u64(shifted, 0);
177 uint32_t bit1 = vgetq_lane_u64(shifted, 1);
178 uint32_t mask = bit0 | (bit1 << 1);
179 uint32_t count = bit0 + bit1;
180 uint8x16_t pattern = vld1q_u8(arm_simd::kStoreSelectedShuffleTableD2[mask]);
181 uint8x16_t packed = vqtbl1q_u8(vreinterpretq_u8_f64(values.raw), pattern);
182 vst1q_f64(ptr, vreinterpretq_f64_u8(packed));
183 return static_cast<int>(count);
184 }
185
186 template <int kTupleCount = kSize>
187 MOCHI_FORCE_INLINE static void StoreTransposed(double* ptr, Simd a, Simd b, Simd c) {
188 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
189 if constexpr (kTupleCount == 1) {
190 ptr[0] = a.raw[0];
191 ptr[1] = b.raw[0];
192 ptr[2] = c.raw[0];
193 } else {
194 vst3q_f64(ptr, float64x2x3_t({a.raw, b.raw, c.raw}));
195 }
196 }
197
198 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Select(Simd mask, Simd a, Simd b) {
199 return vbslq_f64(vreinterpretq_u64_f64(mask.raw), a.raw, b.raw);
200 }
201
202 // return Simd{v[x], v[y]}
203 template <int x = 0, int y = 1>
204 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Shuffle(Simd v) {
205 static_assert(x >= 0 && x < 2, "Invalid index");
206 static_assert(y >= 0 && y < 2, "Invalid index");
207 if constexpr (x == 0 && y == 0) {
208 return Broadcast<0>(v);
209 } else if constexpr (x == 0 && y == 1) {
210 return v; // no change
211 } else if constexpr (x == 1 && y == 0) {
212 return vcombine_f64(vget_high_f64(v.raw), vget_low_f64(v.raw));
213 } else if constexpr (x == 1 && y == 1) {
214 return Broadcast<1>(v);
215 }
216 }
217
218 [[nodiscard]] MOCHI_FORCE_INLINE static Simd SignBitMask() {
219 return vreinterpretq_f64_s64(vdupq_n_s64((int64_t)0x8000000000000000LL));
220 }
221
222 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Sqrt(Simd v) {
223 return vsqrtq_f64(v.raw);
224 }
225
226 [[nodiscard]] MOCHI_FORCE_INLINE static Simd RcpApprox(Simd v) {
227 return vrecpeq_f64(v.raw);
228 }
229
230 [[nodiscard]] MOCHI_FORCE_INLINE static Simd RcpSqrtApprox(Simd v) {
231 return vrsqrteq_f64(v.raw);
232 }
233
234 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Abs(Simd v) {
235 return vabsq_f64(v.raw);
236 }
237
238 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Min(Simd a, Simd b) {
239 return vminq_f64(a.raw, b.raw);
240 }
241
242 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Max(Simd a, Simd b) {
243 return vmaxq_f64(a.raw, b.raw);
244 }
245
246 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Floor(Simd a) {
247 return vrndmq_f64(a.raw);
248 }
249
250 [[nodiscard]] MOCHI_FORCE_INLINE static Simd FastRound(Simd v) {
251 return vrndaq_f64(v.raw);
252 }
253
254 [[nodiscard]] MOCHI_FORCE_INLINE static Simd MulAdd(Simd a, Simd b, Simd c) {
255 return vfmaq_f64(c.raw, b.raw, a.raw);
256 }
257
258 [[nodiscard]] MOCHI_FORCE_INLINE static Simd MulSub(Simd a, Simd b, Simd c) {
259 return MulAdd(a, b, -c);
260 }
261
262 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NegMulAdd(Simd a, Simd b, Simd c) {
263 return vfmsq_f64(c.raw, b.raw, a.raw);
264 }
265
266 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NegMulSub(Simd a, Simd b, Simd c) {
267 return NegMulAdd(a, b, -c);
268 }
269
270 template <int N = 2>
271 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HMin(Simd a) {
272 static_assert(N == 2, "Unsupported N");
273 return vminvq_f64(a.raw);
274 }
275
276 template <int N = 2>
277 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HMax(Simd a) {
278 static_assert(N == 2, "Unsupported N");
279 return vmaxvq_f64(a.raw);
280 }
281
282 template <int N>
283 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HSum(Simd a) {
284 static_assert(N == 2, "Unsupported N");
285 return vaddvq_f64(a.raw);
286 }
287
288 template <int N>
289 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HProd(Simd a) {
290 static_assert(N == 2, "Unsupported N");
291 return Get<0>(a) * Get<1>(a);
292 }
293
294 template <int N>
295 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Dot(Simd a, Simd b) {
296 static_assert(N == 2, "Unsupported N");
297 return Simd{HSum<N>(a * b)};
298 }
299
300 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<(Simd rhs) const {
301 return vreinterpretq_f64_u64(vcltq_f64(this->raw, rhs.raw));
302 }
303
304 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>(Simd rhs) const {
305 return vreinterpretq_f64_u64(vcgtq_f64(this->raw, rhs.raw));
306 }
307
308 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<=(Simd rhs) const {
309 return vreinterpretq_f64_u64(vcleq_f64(this->raw, rhs.raw));
310 }
311
312 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>=(Simd rhs) const {
313 return vreinterpretq_f64_u64(vcgeq_f64(this->raw, rhs.raw));
314 }
315
316 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Equal(Simd a, Simd b) {
317 return vreinterpretq_f64_u64(vceqq_f64(a.raw, b.raw));
318 }
319
320 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NotEqual(Simd a, Simd b) {
321 return ~Equal(a, b);
322 }
323
324 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Zero() {
325 return vdupq_n_f64(0);
326 }
327
328 [[nodiscard]] MOCHI_FORCE_INLINE bool operator==(Simd rhs) const {
329 return AllTrue<kSize>(Equal(*this, rhs));
330 }
331
332 [[nodiscard]] MOCHI_FORCE_INLINE bool operator!=(Simd rhs) const {
333 return !(*this == rhs);
334 }
335
336 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator~() const {
337 return vreinterpretq_f64_u32(vmvnq_u32(vreinterpretq_u32_f64(raw)));
338 }
339
340 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-() const {
341 return vnegq_f64(raw);
342 }
343
344 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator+(Simd rhs) const {
345 return vaddq_f64(raw, rhs.raw);
346 }
347
348 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-(Simd rhs) const {
349 return vsubq_f64(raw, rhs.raw);
350 }
351
352 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator*(Simd rhs) const {
353 return vmulq_f64(raw, rhs.raw);
354 }
355
356 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator/(Simd rhs) const {
357 return vdivq_f64(raw, rhs.raw);
358 }
359
360 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator&(Simd rhs) const {
361 return vreinterpretq_f64_s64(
362 vandq_s64(vreinterpretq_s64_f64(raw), vreinterpretq_s64_f64(rhs.raw)));
363 }
364
365 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator|(Simd rhs) const {
366 return vreinterpretq_f64_s64(
367 vorrq_s64(vreinterpretq_s64_f64(raw), vreinterpretq_s64_f64(rhs.raw)));
368 }
369
370 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator^(Simd rhs) const {
371 return vreinterpretq_f64_s64(
372 veorq_s64(vreinterpretq_s64_f64(raw), vreinterpretq_s64_f64(rhs.raw)));
373 }
374};
375
376} // namespace superdex
377
378#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)