SuperDex Physics C++ API
Loading...
Searching...
No Matches
arm_simd_int64_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<int64_t, 2>
27*/
28template <>
29class Simd<int64_t, 2> {
30 public:
31 MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(int64_t, 2, int64x2_t);
32
33 MOCHI_FORCE_INLINE Simd(int64_t a, int64_t 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_s64(a)} {}
36
37 template <int i>
38 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar Get(Simd v) {
39 static_assert(i >= 0 && i < kSize, "Index out of range");
40 return vgetq_lane_s64(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 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_s64(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 x, int y>
74 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Blend(Simd a, Simd b) {
75 static_assert(x >= 0 && x <= 1 && y >= 0 && y <= 1, "invalid blend index");
76 if constexpr (x == 0 && y == 0) {
77 return a;
78 } else if constexpr (x == 1 && y == 1) {
79 return b;
80 } else {
81 auto mask = int64x2_t{x ? (int64_t)0 : (int64_t)-1, y ? (int64_t)0 : (int64_t)-1};
82 return Select(mask, a, b);
83 }
84 }
85
86 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Broadcast(Scalar const* p) {
87 return Simd{*p};
88 }
89
90 template <int i>
91 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Broadcast(Simd v) {
92 static_assert(i >= 0 && i < kSize, "Index out of range");
93 return vdupq_laneq_s64(v.raw, i);
94 }
95
96 template <int N = 2>
97 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HMin(Simd a) {
98 static_assert(N == 2, "Unsupported N");
99 return superdex::Min(a.raw[0], a.raw[1]);
100 }
101
102 template <int N = 2>
103 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HMax(Simd a) {
104 static_assert(N == 2, "Unsupported N");
105 return superdex::Max(a.raw[0], a.raw[1]);
106 }
107
108 template <int N>
109 [[nodiscard]] MOCHI_FORCE_INLINE static Scalar HSum(Simd a) {
110 static_assert(N == 2, "Unsupported N");
111 return vaddvq_s64(a.raw);
112 }
113
114 template <int N = kSize>
115 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Load([[maybe_unused]] Scalar const* ptr) {
116 static_assert(N >= 0 && N <= kSize);
117 if constexpr (N == 0) {
118 return Simd::Zero();
119 } else if constexpr (N == 1) {
120 return Simd{ptr[0], 0};
121 } else {
122 return vld1q_s64(ptr);
123 }
124 }
125
126 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Load(Scalar const* ptr, int n) {
127 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
128 // clang-format off
129 switch (n) {
130 case 1: return Load<1>(ptr);
131 case 2: return Load<2>(ptr);
132 MOCHI_UNLIKELY default: return Zero();
133 } // clang-format on
134 }
135
136 template <int kTupleCount = kSize>
137 MOCHI_FORCE_INLINE static void
138 LoadTransposed(int64_t const* ptr, Simd& out0, Simd& out1, Simd& out2) {
139 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Unsupported kTupleCount");
140 if constexpr (kTupleCount == 1) {
141 out0.raw = int64x2_t{ptr[0], 0};
142 out1.raw = int64x2_t{ptr[1], 0};
143 out2.raw = int64x2_t{ptr[2], 0};
144 } else {
145 int64x2x3_t result = vld3q_s64(ptr);
146 out0.raw = result.val[0];
147 out1.raw = result.val[1];
148 out2.raw = result.val[2];
149 }
150 }
151
152 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Min(Simd a, Simd b) {
153 return vbslq_s64(vcltq_s64(a.raw, b.raw), a.raw, b.raw);
154 }
155
156 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Max(Simd a, Simd b) {
157 return vbslq_s64(vcgtq_s64(a.raw, b.raw), a.raw, b.raw);
158 }
159
160 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Select(Simd mask, Simd a, Simd b) {
161 return vbslq_s64(vreinterpretq_u64_s64(mask.raw), a.raw, b.raw);
162 }
163
164 // return Simd{v[x], v[y]}
165 template <int x = 0, int y = 1>
166 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Shuffle(Simd v) {
167 static_assert(x >= 0 && x < 2, "Invalid index");
168 static_assert(y >= 0 && y < 2, "Invalid index");
169 if constexpr (x == 0 && y == 0) {
170 return Broadcast<0>(v);
171 } else if constexpr (x == 0 && y == 1) {
172 return v; // no change
173 } else if constexpr (x == 1 && y == 0) {
174 return vcombine_s64(vget_high_s64(v.raw), vget_low_s64(v.raw));
175 } else if constexpr (x == 1 && y == 1) {
176 return Broadcast<1>(v);
177 }
178 }
179
180 template <int N = kSize>
181 MOCHI_FORCE_INLINE static void Store([[maybe_unused]] Scalar* ptr, [[maybe_unused]] Simd v) {
182 static_assert(N >= 0 && N <= kSize);
183 if constexpr (N == 0) {
184 } else if constexpr (N < kSize) {
185 memcpy(ptr, &v, sizeof(Scalar) * N);
186 } else {
187 vst1q_s64(ptr, v.raw);
188 }
189 }
190
191 MOCHI_FORCE_INLINE static void Store(Scalar* ptr, Simd v, int n) {
192 MOCHI_ASSERT_VERBOSE(n >= 0 && n <= kSize, "Invalid size parameter");
193 // clang-format off
194 switch (n) {
195 case 1: Store<1>(ptr, v); break;
196 case 2: Store<2>(ptr, v); break;
197 MOCHI_UNLIKELY default: break;
198 } // clang-format on
199 }
200
201 MOCHI_FORCE_INLINE static int StoreSelected(int64_t* ptr, Simd condition, Simd values) {
202 uint64x2_t shifted = vshrq_n_u64(vreinterpretq_u64_s64(condition.raw), 63);
203 uint32_t bit0 = vgetq_lane_u64(shifted, 0);
204 uint32_t bit1 = vgetq_lane_u64(shifted, 1);
205 uint32_t mask = bit0 | (bit1 << 1);
206 uint32_t count = bit0 + bit1;
207 uint8x16_t pattern = vld1q_u8(arm_simd::kStoreSelectedShuffleTableD2[mask]);
208 uint8x16_t packed = vqtbl1q_u8(vreinterpretq_u8_s64(values.raw), pattern);
209 vst1q_s64(ptr, vreinterpretq_s64_u8(packed));
210 return static_cast<int>(count);
211 }
212
213 template <int kTupleCount = kSize>
214 MOCHI_FORCE_INLINE static void StoreTransposed(int64_t* ptr, Simd a, Simd b, Simd c) {
215 static_assert(kTupleCount >= 1 && kTupleCount <= kSize, "Invalid kTupleCount");
216 if constexpr (kTupleCount == 1) {
217 ptr[0] = a.raw[0];
218 ptr[1] = b.raw[0];
219 ptr[2] = c.raw[0];
220 } else {
221 vst3q_s64(ptr, int64x2x3_t({a.raw, b.raw, c.raw}));
222 }
223 }
224
225 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Zero() {
226 return vdupq_n_s64(0);
227 }
228
229 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<(Simd rhs) const {
230 return vreinterpretq_s64_u64(vcltq_s64(this->raw, rhs.raw));
231 }
232
233 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>(Simd rhs) const {
234 return vreinterpretq_s64_u64(vcgtq_s64(this->raw, rhs.raw));
235 }
236
237 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<=(Simd rhs) const {
238 return vreinterpretq_s64_u64(vcleq_s64(this->raw, rhs.raw));
239 }
240
241 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator>=(Simd rhs) const {
242 return vreinterpretq_s64_u64(vcgeq_s64(this->raw, rhs.raw));
243 }
244
245 [[nodiscard]] MOCHI_FORCE_INLINE static Simd Equal(Simd a, Simd b) {
246 return vreinterpretq_s64_u64(vceqq_s64(a.raw, b.raw));
247 }
248
249 [[nodiscard]] MOCHI_FORCE_INLINE static Simd NotEqual(Simd a, Simd b) {
250 return ~Equal(a, b);
251 }
252
253 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-() const {
254 return vnegq_s64(raw);
255 }
256
257 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator+(Simd rhs) const {
258 return vaddq_s64(raw, rhs.raw);
259 }
260
261 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator-(Simd rhs) const {
262 return vsubq_s64(raw, rhs.raw);
263 }
264
265 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator*(Simd rhs) const {
266 return Simd{
267 // NEON does not implement long integer multiplication
268 raw[0] * rhs.raw[0],
269 raw[1] * rhs.raw[1]};
270 }
271
272 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator/(Simd rhs) const {
273 return Simd{
274 // NEON does not implement long integer division
275 raw[0] / rhs.raw[0],
276 raw[1] / rhs.raw[1]};
277 }
278
279 [[nodiscard]] MOCHI_FORCE_INLINE bool operator==(Simd rhs) const {
280 uint32x2_t t = vqmovn_u64(vceqq_s64(raw, rhs.raw));
281 return vget_lane_u64(vreinterpret_u64_u32(t), 0) == uint64_t(-1);
282 }
283
284 [[nodiscard]] MOCHI_FORCE_INLINE bool operator!=(Simd rhs) const {
285 return !(*this == rhs);
286 }
287
288 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator~() const {
289 return vreinterpretq_s64_u32(vmvnq_u32(vreinterpretq_u32_s64(raw)));
290 }
291
292 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator&(Simd rhs) const {
293 return vandq_s64(raw, rhs.raw);
294 }
295
296 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator|(Simd rhs) const {
297 return vorrq_s64(raw, rhs.raw);
298 }
299
300 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator^(Simd rhs) const {
301 return veorq_s64(raw, rhs.raw);
302 }
303
304 [[nodiscard]] MOCHI_FORCE_INLINE Simd operator<<(int shift) const {
305 // NOTE: If shift were a constexpr, then vshlq_n_s64 would be better because the shift amount
306 // could be an immediate value. Fortunately, Clang appears to be smart enough to do the right
307 // thing.
308 auto vShift = Simd(shift);
309 return vshlq_s64(raw, vShift.raw);
310 }
311
312 template <int kShift>
313 [[nodiscard]] MOCHI_FORCE_INLINE static Simd ShiftRight(Simd a) {
314 return vshrq_n_s64(a.raw, kShift);
315 }
316};
317
318} // namespace superdex
319
320#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<<(int shift) 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
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
Definition simd_inl.h:270
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
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
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
Definition simd_inl.h:285
constexpr T Select(bool condition, T a, T b)
T Get(Simd< T, N > v)
Definition simd_inl.h:300
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
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
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
Simd< T, N > ShiftRight(Simd< T, N > a)
Definition simd_inl.h:260
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)