21#if MOCHI_USE_SIMD && MOCHI_ARCH_ARM_NEON
34 template <
class U, MOCHI_REQUIRES_NON_BOOL_SCALAR(U, Scalar)>
39 static_assert(i >= 0 && i < 4,
"Index out of range");
40 return vgetq_lane_f32(v.raw, i);
51 result.raw[i] = value;
57 static_assert(i >= 0 && i <
kSize,
"Index out of range");
58 return Set(v, i, value);
62 return vsetq_lane_f32(1.0f, a.raw, 3);
66 return vsetq_lane_f32(0.0f, a.raw, 3);
69 template <
int x,
int y,
int z,
int w>
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) {
77 }
else if constexpr (kCount == 4) {
79 }
else if constexpr (kCount == 1) {
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) {
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) {
88 return vcombine_f32(vget_low_f32(b.raw), vget_high_f32(a.raw));
89 }
else if constexpr (z == 1 && w == 1) {
90 return vcombine_f32(vget_low_f32(a.raw), vget_high_f32(b.raw));
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);
101 static_assert(N >= 1 && N <= 4,
"Invalid number of components");
103 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_f32(v.raw))), 0);
104 if constexpr (N ==
kSize) {
105 return mask == 0xFFFFFFFFFFFFFFFFULL;
107 int constexpr kNumBits = N * 16;
108 auto constexpr kMustBeSet = (uint64_t(1) << kNumBits) - 1;
109 return (mask & kMustBeSet) == kMustBeSet;
115 static_assert(N >= 1 && N <= 4,
"Invalid number of components");
117 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_f32(v.raw))), 0);
118 if constexpr (N ==
kSize) {
121 int constexpr kNumBits = N * 16;
122 auto constexpr kMayBeSet = (uint64_t(1) << kNumBits) - 1;
123 return (mask & kMayBeSet) != 0;
133 return vdupq_laneq_f32(v.raw, i);
136 template <
int N = kSize>
138 static_assert(N >= 0 && N <= 4);
139 if constexpr (N == 0) {
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};
148 return vld1q_f32(ptr);
168 ptr[indices.raw[0]], ptr[indices.raw[1]], ptr[indices.raw[2]], ptr[indices.raw[3]]};
171 template <
int kTupleCount = kSize>
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};
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];
196 return vbslq_f32(vreinterpretq_u32_f32(mask.raw), a.raw, b.raw);
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);
206 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
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]};
213 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
219 return vreinterpretq_f32_s32(vdupq_n_s32((int32_t)0x80000000));
222 template <
int N = kSize>
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);
229 vst1q_f32(ptr, v.raw);
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);
257 template <
int kTupleCount = kSize>
259 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
260 if constexpr (kTupleCount == 1) {
264 }
else if constexpr (kTupleCount == 2) {
265 Simd::Store(ptr,
Simd{a[0], b[0], c[0], a[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]});
273 vst3q_f32(ptr, float32x4x3_t({a.raw, b.raw, c.raw}));
278 return vsqrtq_f32(v.raw);
282 return vrecpeq_f32(v.raw);
286 return vrsqrteq_f32(v.raw);
290 return vabsq_f32(v.raw);
294 return vminq_f32(a.raw, b.raw);
298 return vmaxq_f32(a.raw, b.raw);
302 return vrndmq_f32(a.raw);
306 return vrndaq_f32(v.raw);
310 return vfmaq_f32(c.raw, b.raw, a.raw);
318 return vfmsq_f32(c.raw, b.raw, a.raw);
327 static_assert(N >= 2 && N <= 4,
"Unsupported N");
328 if constexpr (N == 2) {
330 }
else if constexpr (N == 3) {
331 return vminvq_f32(vsetq_lane_f32(std::numeric_limits<Scalar>::infinity(), a.raw, 3));
333 return vminvq_f32(a.raw);
339 static_assert(N >= 2 && N <= 4,
"Unsupported N");
340 if constexpr (N == 2) {
342 }
else if constexpr (N == 3) {
344 return vmaxvq_f32(vsetq_lane_f32(-std::numeric_limits<Scalar>::infinity(), a.raw, 3));
346 return vmaxvq_f32(a.raw);
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);
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];
376 static_assert(N >= 2 && N <= 4,
"Unsupported N");
381 return vreinterpretq_f32_u32(vcltq_f32(this->
raw, rhs.raw));
385 return vreinterpretq_f32_u32(vcgtq_f32(this->
raw, rhs.raw));
389 return vreinterpretq_f32_u32(vcleq_f32(this->
raw, rhs.raw));
393 return vreinterpretq_f32_u32(vcgeq_f32(this->
raw, rhs.raw));
397 return vreinterpretq_f32_u32(vceqq_f32(a.raw, b.raw));
405 return vdupq_n_f32(0);
413 return !(*
this == rhs);
417 return vreinterpretq_f32_u32(vmvnq_u32(vreinterpretq_u32_f32(
raw)));
421 return vnegq_f32(
raw);
425 return vaddq_f32(
raw, rhs.raw);
429 return vsubq_f32(
raw, rhs.raw);
433 return vmulq_f32(
raw, rhs.raw);
437 return vdivq_f32(
raw, rhs.raw);
441 return vreinterpretq_f32_u32(
442 vandq_u32(vreinterpretq_u32_f32(
raw), vreinterpretq_u32_f32(rhs.raw)));
446 return vreinterpretq_f32_u32(
447 vorrq_u32(vreinterpretq_u32_f32(
raw), vreinterpretq_u32_f32(rhs.raw)));
451 return vreinterpretq_f32_u32(
452 veorq_u32(vreinterpretq_u32_f32(
raw), vreinterpretq_u32_f32(rhs.raw)));
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
Simd operator>(Simd rhs) const
Simd operator*(Simd rhs) const
Simd operator^(Simd rhs) 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
Simd operator+(Simd rhs) const
Simd operator/(Simd rhs) const
Simd operator<=(Simd rhs) const
#define MOCHI_ASSERT_VERBOSE(condition_without_side_effects,...)
T Dot(Simd< T, N > a, Simd< T, N > b)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
V LoadIndexed(typename V::Scalar const *ptr, Simd< I, V::kSize > indices)
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
constexpr auto MulAdd(A a, B b, C c)
Simd< T, N > Set(Simd< T, N > a, T value)
constexpr auto NotEqual(T const &a, T const &b)
V Broadcast(typename V::Scalar a)
constexpr auto MulSub(A a, B b, C c)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
constexpr T Select(bool condition, T a, T b)
constexpr auto NegMulAdd(A a, B b, C c)
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)
Simd< T, N > FastRound(Simd< T, N > a)
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
void Store(T *ptr, Simd< T, N > a)
Simd< T, N > RcpSqrtApprox(Simd< T, N > a)
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)
V Load(typename V::Scalar const *ptr)
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)