8#include <rapidfuzz/details/intrinsics.hpp>
19class native_simd<uint64_t> {
21 using value_type = uint64_t;
23 static constexpr int alignment = 32;
24 static const int size = 4;
27 native_simd() noexcept
30 native_simd(__m256i val) noexcept : xmm(val)
33 native_simd(uint64_t a)
noexcept
35 xmm = _mm256_set1_epi64x(
static_cast<int64_t
>(a));
38 native_simd(
const uint64_t* p)
noexcept
43 operator __m256i() const noexcept
48 native_simd load(
const uint64_t* p)
noexcept
50 xmm = _mm256_set_epi64x(
static_cast<int64_t
>(p[3]),
static_cast<int64_t
>(p[2]),
51 static_cast<int64_t
>(p[1]),
static_cast<int64_t
>(p[0]));
55 void store(uint64_t* p)
const noexcept
57 _mm256_store_si256(
reinterpret_cast<__m256i*
>(p), xmm);
60 native_simd operator+(
const native_simd b)
const noexcept
62 return _mm256_add_epi64(xmm, b);
65 native_simd& operator+=(
const native_simd b)
noexcept
67 xmm = _mm256_add_epi64(xmm, b);
71 native_simd operator-(
const native_simd b)
const noexcept
73 return _mm256_sub_epi64(xmm, b);
76 native_simd operator-() const noexcept
78 return _mm256_sub_epi64(_mm256_setzero_si256(), xmm);
81 native_simd& operator-=(
const native_simd b)
noexcept
83 xmm = _mm256_sub_epi64(xmm, b);
89class native_simd<uint32_t> {
91 using value_type = uint32_t;
93 static constexpr int alignment = 32;
94 static const int size = 8;
97 native_simd() noexcept
100 native_simd(__m256i val) noexcept : xmm(val)
103 native_simd(uint32_t a)
noexcept
105 xmm = _mm256_set1_epi32(
static_cast<int>(a));
108 native_simd(
const uint64_t* p)
noexcept
113 operator __m256i()
const
118 native_simd load(
const uint64_t* p)
noexcept
120 xmm = _mm256_set_epi64x(
static_cast<int64_t
>(p[3]),
static_cast<int64_t
>(p[2]),
121 static_cast<int64_t
>(p[1]),
static_cast<int64_t
>(p[0]));
125 void store(uint32_t* p)
const noexcept
127 _mm256_store_si256(
reinterpret_cast<__m256i*
>(p), xmm);
130 native_simd operator+(
const native_simd b)
const noexcept
132 return _mm256_add_epi32(xmm, b);
135 native_simd& operator+=(
const native_simd b)
noexcept
137 xmm = _mm256_add_epi32(xmm, b);
141 native_simd operator-() const noexcept
143 return _mm256_sub_epi32(_mm256_setzero_si256(), xmm);
146 native_simd operator-(
const native_simd b)
const noexcept
148 return _mm256_sub_epi32(xmm, b);
151 native_simd& operator-=(
const native_simd b)
noexcept
153 xmm = _mm256_sub_epi32(xmm, b);
159class native_simd<uint16_t> {
161 using value_type = uint16_t;
163 static constexpr int alignment = 32;
164 static const int size = 16;
167 native_simd() noexcept
170 native_simd(__m256i val) : xmm(val)
173 native_simd(uint16_t a)
noexcept
175 xmm = _mm256_set1_epi16(
static_cast<short>(a));
178 native_simd(
const uint64_t* p)
noexcept
183 operator __m256i() const noexcept
188 native_simd load(
const uint64_t* p)
noexcept
190 xmm = _mm256_set_epi64x(
static_cast<int64_t
>(p[3]),
static_cast<int64_t
>(p[2]),
191 static_cast<int64_t
>(p[1]),
static_cast<int64_t
>(p[0]));
195 void store(uint16_t* p)
const noexcept
197 _mm256_store_si256(
reinterpret_cast<__m256i*
>(p), xmm);
200 native_simd operator+(
const native_simd b)
const noexcept
202 return _mm256_add_epi16(xmm, b);
205 native_simd& operator+=(
const native_simd b)
noexcept
207 xmm = _mm256_add_epi16(xmm, b);
211 native_simd operator-(
const native_simd b)
const noexcept
213 return _mm256_sub_epi16(xmm, b);
216 native_simd operator-() const noexcept
218 return _mm256_sub_epi16(_mm256_setzero_si256(), xmm);
221 native_simd& operator-=(
const native_simd b)
noexcept
223 xmm = _mm256_sub_epi16(xmm, b);
229class native_simd<uint8_t> {
231 using value_type = uint8_t;
233 static constexpr int alignment = 32;
234 static const int size = 32;
237 native_simd() noexcept
240 native_simd(__m256i val) noexcept : xmm(val)
243 native_simd(uint8_t a)
noexcept
245 xmm = _mm256_set1_epi8(
static_cast<char>(a));
248 native_simd(
const uint64_t* p)
noexcept
253 operator __m256i() const noexcept
258 native_simd load(
const uint64_t* p)
noexcept
260 xmm = _mm256_set_epi64x(
static_cast<int64_t
>(p[3]),
static_cast<int64_t
>(p[2]),
261 static_cast<int64_t
>(p[1]),
static_cast<int64_t
>(p[0]));
265 void store(uint8_t* p)
const noexcept
267 _mm256_store_si256(
reinterpret_cast<__m256i*
>(p), xmm);
270 native_simd operator+(
const native_simd b)
const noexcept
272 return _mm256_add_epi8(xmm, b);
275 native_simd& operator+=(
const native_simd b)
noexcept
277 xmm = _mm256_add_epi8(xmm, b);
281 native_simd operator-(
const native_simd b)
const noexcept
283 return _mm256_sub_epi8(xmm, b);
286 native_simd operator-() const noexcept
288 return _mm256_sub_epi8(_mm256_setzero_si256(), xmm);
291 native_simd& operator-=(
const native_simd b)
noexcept
293 xmm = _mm256_sub_epi8(xmm, b);
299std::ostream& operator<<(std::ostream& os,
const native_simd<T>& a)
301 alignas(native_simd<T>::alignment) std::array<T, native_simd<T>::size> res;
304 for (
size_t i = res.size() - 1; i != 0; i--)
305 os << std::bitset<std::numeric_limits<T>::digits>(res[i]) <<
"|";
307 os << std::bitset<std::numeric_limits<T>::digits>(res[0]);
312__m256i hadd_impl(__m256i x)
noexcept;
315inline __m256i hadd_impl<uint8_t>(__m256i x)
noexcept
321inline __m256i hadd_impl<uint16_t>(__m256i x)
noexcept
323 const __m256i mask = _mm256_set1_epi16(0x001f);
324 __m256i y = _mm256_srli_si256(x, 1);
325 x = _mm256_add_epi16(x, y);
326 return _mm256_and_si256(x, mask);
330inline __m256i hadd_impl<uint32_t>(__m256i x)
noexcept
332 const __m256i mask = _mm256_set1_epi32(0x0000003F);
333 x = hadd_impl<uint16_t>(x);
334 __m256i y = _mm256_srli_si256(x, 2);
335 x = _mm256_add_epi32(x, y);
336 return _mm256_and_si256(x, mask);
340inline __m256i hadd_impl<uint64_t>(__m256i x)
noexcept
342 return _mm256_sad_epu8(x, _mm256_setzero_si256());
347native_simd<T> popcount_impl(
const native_simd<T>& v)
noexcept
349 __m256i lookup = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3,
350 1, 2, 2, 3, 2, 3, 3, 4);
351 const __m256i low_mask = _mm256_set1_epi8(0x0F);
352 __m256i lo = _mm256_and_si256(v, low_mask);
353 __m256i hi = _mm256_and_si256(_mm256_srli_epi32(v, 4), low_mask);
354 __m256i popcnt1 = _mm256_shuffle_epi8(lookup, lo);
355 __m256i popcnt2 = _mm256_shuffle_epi8(lookup, hi);
356 __m256i total = _mm256_add_epi8(popcnt1, popcnt2);
357 return hadd_impl<T>(total);
361std::array<T, native_simd<T>::size> popcount(
const native_simd<T>& a)
noexcept
363 alignas(native_simd<T>::alignment) std::array<T, native_simd<T>::size> res;
364 popcount_impl(a).store(&res[0]);
370native_simd<T> andnot(
const native_simd<T>& a,
const native_simd<T>& b)
372 return _mm256_andnot_si256(b, a);
375static inline native_simd<uint8_t> operator==(
const native_simd<uint8_t>& a,
376 const native_simd<uint8_t>& b)
noexcept
378 return _mm256_cmpeq_epi8(a, b);
381static inline native_simd<uint16_t> operator==(
const native_simd<uint16_t>& a,
382 const native_simd<uint16_t>& b)
noexcept
384 return _mm256_cmpeq_epi16(a, b);
387static inline native_simd<uint32_t> operator==(
const native_simd<uint32_t>& a,
388 const native_simd<uint32_t>& b)
noexcept
390 return _mm256_cmpeq_epi32(a, b);
393static inline native_simd<uint64_t> operator==(
const native_simd<uint64_t>& a,
394 const native_simd<uint64_t>& b)
noexcept
396 return _mm256_cmpeq_epi64(a, b);
400static inline native_simd<T> operator!=(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
405static inline native_simd<uint8_t> operator<<(
const native_simd<uint8_t>& a,
int b)
noexcept
407 char mask =
static_cast<char>(0xFF >> b);
408 __m256i am = _mm256_and_si256(a, _mm256_set1_epi8(mask));
409 return _mm256_slli_epi16(am, b);
412static inline native_simd<uint16_t> operator<<(
const native_simd<uint16_t>& a,
int b)
noexcept
414 return _mm256_slli_epi16(a, b);
417static inline native_simd<uint32_t> operator<<(
const native_simd<uint32_t>& a,
int b)
noexcept
419 return _mm256_slli_epi32(a, b);
422static inline native_simd<uint64_t> operator<<(
const native_simd<uint64_t>& a,
int b)
noexcept
424 return _mm256_slli_epi64(a, b);
427static inline native_simd<uint8_t> operator>>(
const native_simd<uint8_t>& a,
int b)
noexcept
429 char mask =
static_cast<char>(0xFF << b);
430 __m256i am = _mm256_and_si256(a, _mm256_set1_epi8(mask));
431 return _mm256_srli_epi16(am, b);
434static inline native_simd<uint16_t> operator>>(
const native_simd<uint16_t>& a,
int b)
noexcept
436 return _mm256_srli_epi16(a, b);
439static inline native_simd<uint32_t> operator>>(
const native_simd<uint32_t>& a,
int b)
noexcept
441 return _mm256_srli_epi32(a, b);
444static inline native_simd<uint64_t> operator>>(
const native_simd<uint64_t>& a,
int b)
noexcept
446 return _mm256_srli_epi64(a, b);
450native_simd<T> operator&(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
452 return _mm256_and_si256(a, b);
456native_simd<T> operator&=(native_simd<T>& a,
const native_simd<T>& b)
noexcept
463native_simd<T> operator|(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
465 return _mm256_or_si256(a, b);
469native_simd<T> operator|=(native_simd<T>& a,
const native_simd<T>& b)
noexcept
476native_simd<T> operator^(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
478 return _mm256_xor_si256(a, b);
482native_simd<T> operator^=(native_simd<T>& a,
const native_simd<T>& b)
noexcept
489native_simd<T> operator~(
const native_simd<T>& a)
noexcept
491 return _mm256_xor_si256(a, _mm256_set1_epi32(-1));
495static inline native_simd<uint8_t> operator>=(
const native_simd<uint8_t>& a,
496 const native_simd<uint8_t>& b)
noexcept
498 return _mm256_cmpeq_epi8(_mm256_max_epu8(a, b), a);
501static inline native_simd<uint16_t> operator>=(
const native_simd<uint16_t>& a,
502 const native_simd<uint16_t>& b)
noexcept
504 return _mm256_cmpeq_epi16(_mm256_max_epu16(a, b), a);
507static inline native_simd<uint32_t> operator>=(
const native_simd<uint32_t>& a,
508 const native_simd<uint32_t>& b)
noexcept
510 return _mm256_cmpeq_epi32(_mm256_max_epu32(a, b), a);
513static inline native_simd<uint64_t> operator>(
const native_simd<uint64_t>& a,
514 const native_simd<uint64_t>& b)
noexcept;
516static inline native_simd<uint64_t> operator>=(
const native_simd<uint64_t>& a,
517 const native_simd<uint64_t>& b)
noexcept
523static inline native_simd<T> operator<=(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
528static inline native_simd<uint8_t> operator>(
const native_simd<uint8_t>& a,
529 const native_simd<uint8_t>& b)
noexcept
534static inline native_simd<uint16_t> operator>(
const native_simd<uint16_t>& a,
535 const native_simd<uint16_t>& b)
noexcept
540static inline native_simd<uint32_t> operator>(
const native_simd<uint32_t>& a,
541 const native_simd<uint32_t>& b)
noexcept
543 __m256i signbit = _mm256_set1_epi32(
static_cast<int32_t
>(0x80000000));
544 __m256i a1 = _mm256_xor_si256(a, signbit);
545 __m256i b1 = _mm256_xor_si256(b, signbit);
546 return _mm256_cmpgt_epi32(a1, b1);
549static inline native_simd<uint64_t> operator>(
const native_simd<uint64_t>& a,
550 const native_simd<uint64_t>& b)
noexcept
552 __m256i sign64 = native_simd<uint64_t>(0x8000000000000000);
553 __m256i aflip = _mm256_xor_si256(a, sign64);
554 __m256i bflip = _mm256_xor_si256(b, sign64);
555 return _mm256_cmpgt_epi64(aflip, bflip);
559static inline native_simd<T> operator<(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
565static inline native_simd<T> max8(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
567 return _mm256_max_epu8(a, b);
571static inline native_simd<T> max16(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
573 return _mm256_max_epu16(a, b);
577static inline native_simd<T> max32(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
579 return _mm256_max_epu32(a, b);
583static inline native_simd<T> min8(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
585 return _mm256_min_epu8(a, b);
589static inline native_simd<T> min16(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
591 return _mm256_min_epu16(a, b);
595static inline native_simd<T> min32(
const native_simd<T>& a,
const native_simd<T>& b)
noexcept
597 return _mm256_min_epu32(a, b);
601static inline native_simd<uint8_t> sllv(
const native_simd<uint8_t>& a,
602 const native_simd<uint8_t>& count_)
noexcept
604 __m256i mask_hi = _mm256_set1_epi32(
static_cast<int32_t
>(0xFF00FF00));
605 __m256i multiplier_lut = _mm256_set_epi8(0, 0, 0, 0, 0, 0, 0, 0,
char(-128), 64, 32, 16, 8, 4, 2, 1, 0, 0,
606 0, 0, 0, 0, 0, 0,
char(-128), 64, 32, 16, 8, 4, 2, 1);
609 _mm256_min_epu8(count_, _mm256_set1_epi8(8));
611 __m256i multiplier = _mm256_shuffle_epi8(
612 multiplier_lut, count_sat);
613 __m256i x_lo = _mm256_mullo_epi16(a, multiplier);
616 __m256i multiplier_hi = _mm256_srli_epi16(multiplier, 8);
617 __m256i a_hi = _mm256_and_si256(a, mask_hi);
618 __m256i x_hi = _mm256_mullo_epi16(a_hi, multiplier_hi);
619 __m256i x = _mm256_blendv_epi8(x_lo, x_hi, mask_hi);
624static inline native_simd<uint16_t> sllv(
const native_simd<uint16_t>& a,
625 const native_simd<uint16_t>& count)
noexcept
627 const __m256i mask = _mm256_set1_epi32(
static_cast<int32_t
>(0xFFFF0000));
628 __m256i low_half = _mm256_sllv_epi32(a, _mm256_andnot_si256(mask, count));
629 __m256i high_half = _mm256_sllv_epi32(_mm256_and_si256(mask, a), _mm256_srli_epi32(count, 16));
630 return _mm256_blend_epi16(low_half, high_half, 0xAA);
633static inline native_simd<uint32_t> sllv(
const native_simd<uint32_t>& a,
634 const native_simd<uint32_t>& count)
noexcept
636 return _mm256_sllv_epi32(a, count);
639static inline native_simd<uint64_t> sllv(
const native_simd<uint64_t>& a,
640 const native_simd<uint64_t>& count)
noexcept
642 return _mm256_sllv_epi64(a, count);