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_s32(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);
63 return vreinterpretq_s32_s64(int64x2_t{a, b});
68 static_assert(N >= 1 && N <= 4,
"Invalid number of components");
70 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_s32(v.raw))), 0);
71 if constexpr (N ==
kSize) {
72 return mask == 0xFFFFFFFFFFFFFFFFULL;
74 int constexpr kNumBits = N * 16;
75 auto constexpr kMustBeSet = (uint64_t(1) << kNumBits) - 1;
76 return (mask & kMustBeSet) == kMustBeSet;
82 static_assert(N >= 1 && N <= 4,
"Invalid number of components");
84 vget_lane_u64(vreinterpret_u64_u16(vqmovn_u32(vreinterpretq_u32_s32(v.raw))), 0);
85 if constexpr (N ==
kSize) {
88 int constexpr kNumBits = N * 16;
89 auto constexpr kMayBeSet = (uint64_t(1) << kNumBits) - 1;
90 return (mask & kMayBeSet) != 0;
100 static_assert(i >= 0 && i <
kSize,
"Index out of range");
101 return vdupq_laneq_s32(v.raw, i);
106 static_assert(N >= 2 && N <= 4,
"Unsupported N");
107 if constexpr (N == 2) {
109 }
else if constexpr (N == 3) {
111 return vminvq_s32(vsetq_lane_s32(std::numeric_limits<Scalar>::max(), a.raw, 3));
113 return vminvq_s32(a.raw);
119 static_assert(N >= 2 && N <= 4,
"Unsupported N");
120 if constexpr (N == 2) {
122 }
else if constexpr (N == 3) {
124 return vmaxvq_s32(vsetq_lane_s32(std::numeric_limits<Scalar>::lowest(), a.raw, 3));
126 return vmaxvq_s32(a.raw);
132 static_assert(N >= 2 && N <= 4,
"Unsupported N");
133 if constexpr (N == 2) {
134 return a.raw[0] + a.raw[1];
135 }
else if constexpr (N == 3) {
136 return a.raw[0] + a.raw[1] + a.raw[2];
137 }
else if constexpr (N == 4) {
138 return vaddvq_s32(a.raw);
142 template <
int N = kSize>
144 static_assert(N >= 0 && N <= 4);
145 if constexpr (N == 0) {
147 }
else if constexpr (N == 1) {
148 return Simd{ptr[0], 0, 0, 0};
149 }
else if constexpr (N == 2) {
150 return Simd{ptr[0], ptr[1], 0, 0};
151 }
else if constexpr (N == 3) {
152 return Simd{ptr[0], ptr[1], ptr[2], 0};
154 return vld1q_s32(ptr);
170 template <
int kTupleCount = kSize>
173 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
174 if constexpr (kTupleCount == 1) {
175 out0.raw = int32x4_t{ptr[0], 0, 0, 0};
176 out1.raw = int32x4_t{ptr[1], 0, 0, 0};
177 out2.raw = int32x4_t{ptr[2], 0, 0, 0};
178 }
else if constexpr (kTupleCount == 2) {
179 out0.raw = int32x4_t{ptr[0], ptr[3], 0, 0};
180 out1.raw = int32x4_t{ptr[1], ptr[4], 0, 0};
181 out2.raw = int32x4_t{ptr[2], ptr[5], 0, 0};
182 }
else if constexpr (kTupleCount == 3) {
183 out0.raw = int32x4_t{ptr[0], ptr[3], ptr[6], 0};
184 out1.raw = int32x4_t{ptr[1], ptr[4], ptr[7], 0};
185 out2.raw = int32x4_t{ptr[2], ptr[5], ptr[8], 0};
187 int32x4x3_t result = vld3q_s32(ptr);
188 out0.raw = result.val[0];
189 out1.raw = result.val[1];
190 out2.raw = result.val[2];
195 return vminq_s32(a.raw, b.raw);
199 return vmaxq_s32(a.raw, b.raw);
203 return vbslq_s32(vreinterpretq_u32_s32(mask.raw), a.raw, b.raw);
206 template <
int x,
int y,
int z,
int w>
209 x >= 0 && x <= 1 && y >= 0 && y <= 1 && z >= 0 && z <= 1 && w >= 0 && w <= 1,
210 "invalid blend index");
211 int constexpr kCount = x + y + z + w;
212 if constexpr (kCount == 0) {
214 }
else if constexpr (kCount == 4) {
216 }
else if constexpr (kCount == 1) {
218 int constexpr kLane = x ? 0 : (y ? 1 : (z ? 2 : 3));
219 return vcopyq_laneq_s32(a.raw, kLane, b.raw, kLane);
220 }
else if constexpr (kCount == 3) {
222 int constexpr kLane = !x ? 0 : (!y ? 1 : (!z ? 2 : 3));
223 return vcopyq_laneq_s32(b.raw, kLane, a.raw, kLane);
224 }
else if constexpr (x == 1 && y == 1) {
225 return vcombine_s32(vget_low_s32(b.raw), vget_high_s32(a.raw));
226 }
else if constexpr (z == 1 && w == 1) {
227 return vcombine_s32(vget_low_s32(a.raw), vget_high_s32(b.raw));
231 Simd const mask{int32x4_t{x ? 0 : -1, y ? 0 : -1, z ? 0 : -1, w ? 0 : -1}};
232 return Select(mask, a, b);
237 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
240 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
241 return Simd{v.raw[x], v.raw[y], v.raw[z], v.raw[w]};
244 template <
int x = 0,
int y = 1,
int z = 2,
int w = 3>
247 x >= 0 && x < 4 && y >= 0 && y < 4 && z >= 0 && z < 4 && w >= 0 && w < 4,
"Invalid index");
248 return Simd{a.raw[x], a.raw[y], b.raw[z], b.raw[w]};
251 template <
int N = kSize>
253 static_assert(N >= 0 && N <=
kSize);
254 if constexpr (N == 0) {
255 }
else if constexpr (N <
kSize) {
256 memcpy(ptr, &v,
sizeof(
int) * N);
258 vst1q_s32(ptr, v.raw);
275 uint32x4_t shifted = vshrq_n_u32(vreinterpretq_u32_s32(condition.raw), 31);
276 uint32x4_t multipliers = {1, 2, 4, 8};
277 uint32x4_t weighted = vmulq_u32(shifted, multipliers);
278 uint32_t count = vaddvq_u32(shifted);
279 uint32_t mask = vaddvq_u32(weighted);
280 uint8x16_t pattern = vld1q_u8(arm_simd::kStoreSelectedShuffleTableS4[mask]);
281 uint8x16_t packed = vqtbl1q_u8(vreinterpretq_u8_s32(values.raw), pattern);
282 vst1q_s32(ptr, vreinterpretq_s32_u8(packed));
283 return static_cast<int>(count);
286 template <
int kTupleCount = kSize>
288 static_assert(kTupleCount >= 1 && kTupleCount <=
kSize,
"Invalid kTupleCount");
289 if constexpr (kTupleCount == 1) {
293 }
else if constexpr (kTupleCount == 2) {
294 Simd::Store(ptr,
Simd{a[0], b[0], c[0], a[1]});
297 }
else if constexpr (kTupleCount == 3) {
298 Simd::Store(ptr + 0,
Simd{a[0], b[0], c[0], a[1]});
299 Simd::Store(ptr + 4,
Simd{b[1], c[1], a[2], b[2]});
302 vst3q_s32(ptr, int32x4x3_t({a.raw, b.raw, c.raw}));
307 return vdupq_n_s32(0);
311 return vreinterpretq_s32_u32(vcltq_s32(this->
raw, rhs.raw));
315 return vreinterpretq_s32_u32(vcgtq_s32(this->
raw, rhs.raw));
319 return vreinterpretq_s32_u32(vcleq_s32(this->
raw, rhs.raw));
323 return vreinterpretq_s32_u32(vcgeq_s32(this->
raw, rhs.raw));
327 return vreinterpretq_s32_u32(vceqq_s32(a.raw, b.raw));
335 uint16x4_t t = vqmovn_u32(vceqq_s32(
raw, rhs.raw));
336 return vget_lane_u64(vreinterpret_u64_u16(t), 0) == uint64_t(-1);
340 return !(*
this == rhs);
344 return vmvnq_s32(
raw);
348 return vnegq_s32(
raw);
352 return vaddq_s32(
raw, rhs.raw);
356 return vsubq_s32(
raw, rhs.raw);
360 return vmulq_s32(
raw, rhs.raw);
369 raw[3] / rhs.raw[3]};
373 return vandq_s32(
raw, rhs.raw);
377 return vorrq_s32(
raw, rhs.raw);
381 return veorq_s32(
raw, rhs.raw);
388 auto vShift =
Simd(shift);
389 return vshlq_s32(
raw, vShift.raw);
392 template <
int kShift>
394 return vshrq_n_s32(a.raw, kShift);
Simd operator&(Simd rhs) const
bool operator==(Simd rhs) const
Simd operator>(Simd rhs) const
Simd operator<<(int shift) 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,...)
Simd< T, 2 > Shuffle(Simd< T, 2 > a)
constexpr T const & Min(T const &a, T const &b)
constexpr auto Equal(T const &a, T const &b)
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)
Simd< T, N > Blend(Simd< T, N > a, Simd< T, N > b)
constexpr T Select(bool condition, T a, T b)
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)
void StoreTransposed(T *ptr, Simd< T, N > a, Simd< T, N > b, Simd< T, N > c)
void Store(T *ptr, Simd< T, N > a)
int StoreSelected(T *ptr, Simd< MaskT, N > condition, Simd< T, N > values)
V Load(typename V::Scalar const *ptr)
Simd< T, N > ShiftRight(Simd< T, N > a)
#define MOCHI_NATIVE_SIMD_IMPL_BOILERPLATE(T, N, NativeT)