94constexpr int8_t nibble_delta(uint8_t x) {
95 return static_cast<int8_t
>(2 * std::popcount(x) - 4);
98constexpr int8_t byte_delta(uint8_t x) {
99 return static_cast<int8_t
>(2 * std::popcount(x) - 8);
102constexpr int8_t min_prefix(uint8_t x,
int bits) {
105 for (
int bit = 0; bit < bits; ++bit) {
106 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
107 if (bit == 0 || cur < best) {
111 return static_cast<int8_t
>(best);
114constexpr int8_t min_prefix_offset(uint8_t x,
int bits) {
118 for (
int bit = 0; bit < bits; ++bit) {
119 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
120 if (bit == 0 || cur < best) {
122 best_offset = bit + 1;
125 return static_cast<int8_t
>(best_offset);
128template <
size_t N,
typename Fn>
129constexpr std::array<int8_t, N> make_lut(Fn fn) {
130 std::array<int8_t, N> out{};
131 for (
size_t i = 0; i < N; ++i) {
132 out[i] = fn(
static_cast<uint8_t
>(i));
137static inline constexpr std::array<int8_t, 16> kNibbleDelta =
138 make_lut<16>([](uint8_t x) {
return nibble_delta(x); });
139static inline constexpr std::array<int8_t, 16> kNibbleMin =
140 make_lut<16>([](uint8_t x) {
return min_prefix(x, 4); });
141static inline constexpr std::array<int8_t, 16> kNibbleMinOffset =
142 make_lut<16>([](uint8_t x) {
return min_prefix_offset(x, 4); });
143static inline constexpr std::array<std::array<int8_t, 16>, 4>
144 kPartialNibbleMinOffset = [] {
145 std::array<std::array<int8_t, 16>, 4> out{};
146 for (
size_t width = 1; width < out.size(); ++width) {
147 out[width] = make_lut<16>(
148 [width](uint8_t x) {
return min_prefix_offset(x, width); });
152static inline constexpr std::array<int8_t, 256> kByteDelta =
153 make_lut<256>([](uint8_t x) {
return byte_delta(x); });
154static inline constexpr std::array<int8_t, 256> kByteMin =
155 make_lut<256>([](uint8_t x) {
return min_prefix(x, 8); });
156static inline constexpr std::array<int8_t, 256> kByteMinOffset =
157 make_lut<256>([](uint8_t x) {
return min_prefix_offset(x, 8); });
159static inline void scan_bit(
const uint64_t* s,
163 size_t& best_offset)
noexcept {
164 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
165 const size_t offset = bit + 1;
166 if (current < best) {
168 best_offset = offset;
186static inline ExcessResult excess_min_128_scalar_bits(
const uint64_t* s,
188 size_t right)
noexcept {
192 left = std::min<size_t>(left, 128);
193 right = std::min<size_t>(right, 128);
195 int best = prefix_excess_128(s, left);
196 size_t best_offset = left;
198 return {best, best_offset};
202 for (
size_t bit = left; bit < right; ++bit) {
203 detail::scan_bit(s, bit, current, best, best_offset);
205 return {best, best_offset};
223static inline ExcessResult excess_min_128_nibble_lut(
const uint64_t* s,
225 size_t right)
noexcept {
229 left = std::min<size_t>(left, 128);
230 right = std::min<size_t>(right, 128);
232 int best = prefix_excess_128(s, left);
233 size_t best_offset = left;
235 return {best, best_offset};
240 for (; bit < right && (bit & 3u) != 0; ++bit) {
241 detail::scan_bit(s, bit, current, best, best_offset);
244 for (; bit + 4 <= right; bit += 4) {
245 const uint8_t nibble =
246 static_cast<uint8_t
>((s[bit >> 6] >> (bit & 63)) & 0xFu);
247 const int candidate = current + detail::kNibbleMin[nibble];
248 if (candidate < best) {
250 best_offset = bit +
static_cast<size_t>(detail::kNibbleMinOffset[nibble]);
252 current += detail::kNibbleDelta[nibble];
255 for (; bit < right; ++bit) {
256 detail::scan_bit(s, bit, current, best, best_offset);
258 return {best, best_offset};
276static inline ExcessResult excess_min_128_byte_lut(
const uint64_t* s,
278 size_t right)
noexcept {
282 left = std::min<size_t>(left, 128);
283 right = std::min<size_t>(right, 128);
285 int best = prefix_excess_128(s, left);
286 size_t best_offset = left;
288 return {best, best_offset};
293 for (; bit < right && (bit & 7u) != 0; ++bit) {
294 detail::scan_bit(s, bit, current, best, best_offset);
297 for (; bit + 8 <= right; bit += 8) {
299 static_cast<uint8_t
>((s[bit >> 6] >> (bit & 63)) & 0xFFu);
300 const int candidate = current + detail::kByteMin[byte];
301 if (candidate < best) {
303 best_offset = bit +
static_cast<size_t>(detail::kByteMinOffset[byte]);
305 current += detail::kByteDelta[byte];
308 for (; bit < right; ++bit) {
309 detail::scan_bit(s, bit, current, best, best_offset);
311 return {best, best_offset};
328static inline ExcessResult excess_min_128_hybrid_lut(
const uint64_t* s,
330 size_t right)
noexcept {
334 const size_t clamped_left = std::min<size_t>(left, 128);
335 const size_t clamped_right = std::min<size_t>(right, 128);
336 const size_t width = clamped_right - clamped_left;
339 return excess_min_128_scalar_bits(s, left, right);
341 if (width <= 64 && (clamped_left & 7u) == 0 && (clamped_right & 7u) == 0) {
342 return excess_min_128_byte_lut(s, left, right);
345 return excess_min_128_nibble_lut(s, left, right);
347 return excess_min_128(s, left, right);
350#ifdef PIXIE_AVX2_SUPPORT
352static inline const __m128i excess_lut_delta_128 = _mm_setr_epi8(
357static inline const __m128i excess_lut_min_128 = _mm_setr_epi8(
362static inline const __m128i excess_lut_nibble_index_128 = _mm_setr_epi8(
367static inline const __m128i excess_lut_low_nibble_index_128 = _mm_setr_epi8(
372static inline const __m128i excess_lut_high_nibble_index_128 = _mm_setr_epi8(
377static inline const __m128i excess_lut_nibble_mask_128 = _mm_set1_epi8(0x0F);
382static inline __m128i excess_nibbles_64_sse(uint64_t word)
noexcept {
383 const __m128i word_vec = _mm_cvtsi64_si128(
static_cast<int64_t
>(word));
384 const __m128i lo_nibbles =
385 _mm_and_si128(word_vec, excess_lut_nibble_mask_128);
386 const __m128i hi_nibbles =
387 _mm_and_si128(_mm_srli_epi16(word_vec, 4), excess_lut_nibble_mask_128);
388 return _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
391static inline __m128i excess_prefix_sum_16x_i8(__m128i v)
noexcept {
393 __m128i t = _mm_slli_si128(x, 1);
394 x = _mm_add_epi8(x, t);
395 t = _mm_slli_si128(x, 2);
396 x = _mm_add_epi8(x, t);
397 t = _mm_slli_si128(x, 4);
398 x = _mm_add_epi8(x, t);
399 t = _mm_slli_si128(x, 8);
400 return _mm_add_epi8(x, t);
403static inline int horizontal_min_i8(__m128i v)
noexcept {
404 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 8));
405 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 4));
406 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 2));
407 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 1));
408 return static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(v, 0)));
411static inline void scan_full_nibbles_64_sse(uint64_t word,
412 int lane_base_excess,
413 size_t lane_base_offset,
417 size_t& best_offset)
noexcept {
418 if (first_nibble >= last_nibble) {
422 const __m128i nibbles = excess_nibbles_64_sse(word);
424 excess_prefix_sum_16x_i8(_mm_shuffle_epi8(excess_lut_delta_128, nibbles));
425 const __m128i excl_ps = _mm_alignr_epi8(ps, _mm_setzero_si128(), 15);
426 const __m128i candidates = _mm_add_epi8(
427 _mm_add_epi8(_mm_set1_epi8(
static_cast<int8_t
>(lane_base_excess)),
429 _mm_shuffle_epi8(excess_lut_min_128, nibbles));
431 const __m128i idx = excess_lut_nibble_index_128;
432 const __m128i first_minus_one =
433 _mm_set1_epi8(
static_cast<int8_t
>(
static_cast<int>(first_nibble) - 1));
434 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(last_nibble));
435 const __m128i active = _mm_and_si128(_mm_cmpgt_epi8(idx, first_minus_one),
436 _mm_cmpgt_epi8(last, idx));
437 const __m128i masked_candidates =
438 _mm_blendv_epi8(_mm_set1_epi8(127), candidates, active);
440 __m128i min128 = masked_candidates;
441 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
442 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
443 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
444 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
446 const int candidate_min =
447 static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
448 if (candidate_min < best) {
449 const __m128i equal_min = _mm_cmpeq_epi8(
450 masked_candidates, _mm_set1_epi8(
static_cast<int8_t
>(candidate_min)));
451 const uint32_t equal_mask =
452 static_cast<uint32_t
>(_mm_movemask_epi8(equal_min));
453 const uint32_t nibble_index = std::countr_zero(equal_mask);
454 const uint8_t nibble =
455 static_cast<uint8_t
>((word >> (nibble_index * 4u)) & 0xFu);
456 best = candidate_min;
457 best_offset = lane_base_offset +
static_cast<size_t>(nibble_index) * 4u +
458 static_cast<size_t>(kNibbleMinOffset[nibble]);
462static inline size_t partial_nibble_min_offset(uint8_t nibble,
463 size_t width)
noexcept {
464 return static_cast<size_t>(kPartialNibbleMinOffset[width][nibble]);
467static inline ExcessResult excess_min_128_split64_sse_impl(
470 size_t right)
noexcept {
474 left = std::min<size_t>(left, 128);
475 right = std::min<size_t>(right, 128);
477 int best = prefix_excess_128(s, left);
478 size_t best_offset = left;
480 return {best, best_offset};
485 for (; bit < right && (bit & 3u) != 0; ++bit) {
486 scan_bit(s, bit, current, best, best_offset);
489 size_t first_full_nibble = bit >> 2;
490 const size_t last_full_nibble = right >> 2;
491 while (first_full_nibble < last_full_nibble) {
492 const size_t word_index = first_full_nibble >> 4;
493 const size_t lane_first = first_full_nibble & 15u;
494 const size_t lane_last =
495 std::min<size_t>(last_full_nibble - word_index * 16u, 16);
496 const size_t lane_base_offset = word_index * 64u;
497 scan_full_nibbles_64_sse(
498 s[word_index], prefix_excess_128(s, lane_base_offset), lane_base_offset,
499 lane_first, lane_last, best, best_offset);
500 first_full_nibble = word_index * 16u + lane_last;
503 bit = std::max(bit, first_full_nibble * 4u);
504 current = prefix_excess_128(s, bit);
505 for (; bit < right; ++bit) {
506 scan_bit(s, bit, current, best, best_offset);
509 return {best, best_offset};
526static inline ExcessResult excess_min_128_lane64_sse(
const uint64_t* s,
528 size_t right)
noexcept {
532 const size_t clamped_left = std::min<size_t>(left, 128);
533 const size_t clamped_right = std::min<size_t>(right, 128);
534 const size_t first_full_nibble = ((clamped_left + 3u) & ~size_t{3}) >> 2;
535 const size_t last_full_nibble = clamped_right >> 2;
536 if (first_full_nibble < last_full_nibble &&
537 (first_full_nibble >> 4) != ((last_full_nibble - 1u) >> 4)) {
538 return excess_min_128(s, left, right);
540 return detail::excess_min_128_split64_sse_impl(s, left, right);
555static inline ExcessResult excess_min_128_split64_sse(
const uint64_t* s,
557 size_t right)
noexcept {
558 return detail::excess_min_128_split64_sse_impl(s, left, right);
575static inline ExcessResult excess_min_128_deinterleaved_sse(
578 size_t right)
noexcept {
582 left = std::min<size_t>(left, 128);
583 right = std::min<size_t>(right, 128);
585 int best = prefix_excess_128(s, left);
586 size_t best_offset = left;
588 return {best, best_offset};
593 for (; bit < right && (bit & 3u) != 0; ++bit) {
594 detail::scan_bit(s, bit, current, best, best_offset);
597 const size_t first_nibble = bit >> 2;
598 const size_t last_full_nibble = right >> 2;
599 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
600 const size_t end_nibble =
601 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
603 if (first_nibble < end_nibble) {
604 const __m128i bytes = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
605 const __m128i lo_nibbles = _mm_and_si128(bytes, excess_lut_nibble_mask_128);
606 const __m128i hi_nibbles =
607 _mm_and_si128(_mm_srli_epi16(bytes, 4), excess_lut_nibble_mask_128);
608 const __m128i lo_delta = _mm_shuffle_epi8(excess_lut_delta_128, lo_nibbles);
609 const __m128i hi_delta = _mm_shuffle_epi8(excess_lut_delta_128, hi_nibbles);
610 const __m128i byte_delta = _mm_add_epi8(lo_delta, hi_delta);
611 const __m128i byte_prefix = detail::excess_prefix_sum_16x_i8(byte_delta);
612 const __m128i byte_prefix_before = _mm_slli_si128(byte_prefix, 1);
614 __m128i lo_local_min = _mm_shuffle_epi8(excess_lut_min_128, lo_nibbles);
615 __m128i hi_local_min = _mm_shuffle_epi8(excess_lut_min_128, hi_nibbles);
617 const __m128i byte_index = excess_lut_nibble_index_128;
618 if (right_partial_width != 0) {
619 const bool partial_is_high = (last_full_nibble & 1u) != 0;
620 const size_t partial_byte = last_full_nibble >> 1;
621 const __m128i partial_source = partial_is_high ? hi_nibbles : lo_nibbles;
622 __m128i partial_min =
623 _mm_shuffle_epi8(excess_lut_pos0_sse, partial_source);
624 if (right_partial_width >= 2) {
625 partial_min = _mm_min_epi8(
626 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, partial_source));
628 if (right_partial_width >= 3) {
629 partial_min = _mm_min_epi8(
630 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, partial_source));
632 const __m128i partial_lane = _mm_cmpeq_epi8(
633 byte_index, _mm_set1_epi8(
static_cast<int8_t
>(partial_byte)));
634 if (partial_is_high) {
635 hi_local_min = _mm_blendv_epi8(hi_local_min, partial_min, partial_lane);
637 lo_local_min = _mm_blendv_epi8(lo_local_min, partial_min, partial_lane);
641 const __m128i lo_candidates =
642 _mm_add_epi8(byte_prefix_before, lo_local_min);
643 const __m128i hi_candidates =
644 _mm_add_epi8(_mm_add_epi8(byte_prefix_before, lo_delta), hi_local_min);
646 __m128i masked_lo = lo_candidates;
647 __m128i masked_hi = hi_candidates;
648 if (first_nibble != 0 || end_nibble != 32) {
649 const __m128i first_minus_one = _mm_set1_epi8(
650 static_cast<int8_t
>(
static_cast<int>(first_nibble) - 1));
651 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
652 const __m128i lo_active = _mm_and_si128(
653 _mm_cmpgt_epi8(excess_lut_low_nibble_index_128, first_minus_one),
654 _mm_cmpgt_epi8(last, excess_lut_low_nibble_index_128));
655 const __m128i hi_active = _mm_and_si128(
656 _mm_cmpgt_epi8(excess_lut_high_nibble_index_128, first_minus_one),
657 _mm_cmpgt_epi8(last, excess_lut_high_nibble_index_128));
658 masked_lo = _mm_blendv_epi8(_mm_set1_epi8(127), lo_candidates, lo_active);
659 masked_hi = _mm_blendv_epi8(_mm_set1_epi8(127), hi_candidates, hi_active);
661 const int candidate_min =
662 detail::horizontal_min_i8(_mm_min_epi8(masked_lo, masked_hi));
664 if (candidate_min < best) {
665 const __m128i min_vec = _mm_set1_epi8(
static_cast<int8_t
>(candidate_min));
666 const uint32_t lo_equal_mask =
static_cast<uint32_t
>(
667 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_lo, min_vec)));
668 const uint32_t hi_equal_mask =
static_cast<uint32_t
>(
669 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_hi, min_vec)));
670 const uint32_t lo_nibble_index =
673 :
static_cast<uint32_t
>(std::countr_zero(lo_equal_mask)) * 2u;
674 const uint32_t hi_nibble_index =
677 :
static_cast<uint32_t
>(std::countr_zero(hi_equal_mask)) * 2u +
679 const uint32_t nibble_index = std::min(lo_nibble_index, hi_nibble_index);
680 const uint32_t byte_offset = nibble_index >> 1u;
681 const uint64_t byte_word = s[byte_offset >> 3u];
682 const uint8_t
byte =
static_cast<uint8_t
>(
683 (byte_word >> ((byte_offset & 7u) * 8u)) & 0xFFu);
684 const uint8_t nibble = (nibble_index & 1u) == 0
685 ?
static_cast<uint8_t
>(
byte & 0xFu)
686 :
static_cast<uint8_t
>((
byte >> 4u) & 0xFu);
687 const size_t local_offset =
688 right_partial_width != 0 && nibble_index == last_full_nibble
689 ? detail::partial_nibble_min_offset(nibble, right_partial_width)
690 :
static_cast<size_t>(detail::kNibbleMinOffset[nibble]);
691 best = candidate_min;
692 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
696 return {best, best_offset};
712static inline ExcessResult excess_min_128_deinterleaved_full_sse(
715 size_t right)
noexcept {
719 left = std::min<size_t>(left, 128);
720 right = std::min<size_t>(right, 128);
721 if (left != 0 || right != 128) {
722 return excess_min_128_deinterleaved_sse(s, left, right);
725 const __m128i bytes = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
726 const __m128i lo_nibbles = _mm_and_si128(bytes, excess_lut_nibble_mask_128);
727 const __m128i hi_nibbles =
728 _mm_and_si128(_mm_srli_epi16(bytes, 4), excess_lut_nibble_mask_128);
729 const __m128i lo_delta = _mm_shuffle_epi8(excess_lut_delta_128, lo_nibbles);
730 const __m128i hi_delta = _mm_shuffle_epi8(excess_lut_delta_128, hi_nibbles);
731 const __m128i byte_delta = _mm_add_epi8(lo_delta, hi_delta);
732 const __m128i byte_prefix = detail::excess_prefix_sum_16x_i8(byte_delta);
733 const __m128i byte_prefix_before = _mm_slli_si128(byte_prefix, 1);
735 const __m128i lo_candidates = _mm_add_epi8(
736 byte_prefix_before, _mm_shuffle_epi8(excess_lut_min_128, lo_nibbles));
737 const __m128i hi_candidates =
738 _mm_add_epi8(_mm_add_epi8(byte_prefix_before, lo_delta),
739 _mm_shuffle_epi8(excess_lut_min_128, hi_nibbles));
740 const int candidate_min =
741 detail::horizontal_min_i8(_mm_min_epi8(lo_candidates, hi_candidates));
744 size_t best_offset = 0;
745 if (candidate_min < best) {
746 const __m128i min_vec = _mm_set1_epi8(
static_cast<int8_t
>(candidate_min));
747 const uint32_t lo_equal_mask =
static_cast<uint32_t
>(
748 _mm_movemask_epi8(_mm_cmpeq_epi8(lo_candidates, min_vec)));
749 const uint32_t hi_equal_mask =
static_cast<uint32_t
>(
750 _mm_movemask_epi8(_mm_cmpeq_epi8(hi_candidates, min_vec)));
751 const uint32_t lo_nibble_index =
754 :
static_cast<uint32_t
>(std::countr_zero(lo_equal_mask)) * 2u;
755 const uint32_t hi_nibble_index =
758 :
static_cast<uint32_t
>(std::countr_zero(hi_equal_mask)) * 2u + 1u;
759 const uint32_t nibble_index = std::min(lo_nibble_index, hi_nibble_index);
760 const uint32_t byte_offset = nibble_index >> 1u;
761 const uint64_t byte_word = s[byte_offset >> 3u];
763 static_cast<uint8_t
>((byte_word >> ((byte_offset & 7u) * 8u)) & 0xFFu);
764 const uint8_t nibble = (nibble_index & 1u) == 0
765 ?
static_cast<uint8_t
>(
byte & 0xFu)
766 :
static_cast<uint8_t
>((
byte >> 4u) & 0xFu);
767 best = candidate_min;
768 best_offset =
static_cast<size_t>(nibble_index) * 4u +
769 static_cast<size_t>(detail::kNibbleMinOffset[nibble]);
771 return {best, best_offset};
786static inline ExcessResult excess_min_128_deinterleaved_byte16_sse(
789 size_t right)
noexcept {
793 const size_t clamped_left = std::min<size_t>(left, 128);
794 const size_t clamped_right = std::min<size_t>(right, 128);
795 const size_t width = clamped_right - clamped_left;
796 if (width <= 16 && (clamped_left & 7u) == 0 && (clamped_right & 7u) == 0) {
797 return excess_min_128_byte_lut(s, left, right);
799 return excess_min_128_deinterleaved_sse(s, left, right);
816static inline ExcessResult excess_min_128_short_skip(
const uint64_t* s,
818 size_t right)
noexcept {
822 const size_t clamped_left = std::min<size_t>(left, 128);
823 const size_t clamped_right = std::min<size_t>(right, 128);
824 const size_t width = clamped_right - clamped_left;
826 return excess_min_128_scalar_bits(s, left, right);
829 const size_t first_full_nibble = ((clamped_left + 3u) & ~size_t{3}) >> 2;
830 const size_t last_full_nibble = clamped_right >> 2;
831 if (first_full_nibble < last_full_nibble &&
832 (first_full_nibble >> 4) == ((last_full_nibble - 1u) >> 4)) {
833 return excess_min_128_lane64_sse(s, left, right);
836 return excess_min_128_split64_sse(s, left, right);
838 return excess_min_128(s, left, right);
842static inline const __m256i excess_branch_lut_em4 = _mm256_setr_epi8(
843 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
844 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
845 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
846 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
848static inline const __m256i excess_branch_lut_em3 = _mm256_setr_epi8(
849 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
850 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
851 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
852 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
854static inline const __m256i excess_branch_lut_em2 = _mm256_setr_epi8(
855 0x02, 0x08, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00,
856 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
857 0x02, 0x08, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00,
858 0x0A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00);
860static inline const __m256i excess_branch_lut_em1 = _mm256_setr_epi8(
861 0x01, 0x04, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00,
862 0x01, 0x04, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00,
863 0x01, 0x04, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00,
864 0x01, 0x04, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00);
866static inline const __m256i excess_branch_lut_e0 = _mm256_setr_epi8(
867 0x00, 0x02, 0x02, 0x08, 0x00, 0x0A, 0x0A, 0x00,
868 0x00, 0x0A, 0x0A, 0x00, 0x08, 0x02, 0x02, 0x00,
869 0x00, 0x02, 0x02, 0x08, 0x00, 0x0A, 0x0A, 0x00,
870 0x00, 0x0A, 0x0A, 0x00, 0x08, 0x02, 0x02, 0x00);
872static inline const __m256i excess_branch_lut_e1 = _mm256_setr_epi8(
873 0x00, 0x01, 0x00, 0x05, 0x00, 0x05, 0x04, 0x01,
874 0x00, 0x01, 0x00, 0x05, 0x00, 0x05, 0x04, 0x01,
875 0x00, 0x01, 0x00, 0x05, 0x00, 0x05, 0x04, 0x01,
876 0x00, 0x01, 0x00, 0x05, 0x00, 0x05, 0x04, 0x01);
878static inline const __m256i excess_branch_lut_e2 = _mm256_setr_epi8(
879 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0A,
880 0x00, 0x00, 0x00, 0x0A, 0x00, 0x08, 0x08, 0x02,
881 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0A,
882 0x00, 0x00, 0x00, 0x0A, 0x00, 0x08, 0x08, 0x02);
884static inline const __m256i excess_branch_lut_e3 = _mm256_setr_epi8(
885 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
886 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
887 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
888 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04);
890static inline const __m256i excess_branch_lut_e4 = _mm256_setr_epi8(
891 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
892 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
893 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
894 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08);
897static inline __m256i excess_bit_masks_16x()
noexcept {
898 return _mm256_setr_epi16(0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020,
899 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800,
900 0x1000, 0x2000, 0x4000, (int16_t)0x8000);
903static inline __m256i excess_prefix_sum_16x_i16(__m256i v)
noexcept {
905 __m256i t = _mm256_slli_si256(x, 2);
906 x = _mm256_add_epi16(x, t);
907 t = _mm256_slli_si256(x, 4);
908 x = _mm256_add_epi16(x, t);
909 t = _mm256_slli_si256(x, 8);
910 x = _mm256_add_epi16(x, t);
912 __m128i lo = _mm256_extracti128_si256(x, 0);
913 __m128i hi = _mm256_extracti128_si256(x, 1);
914 const int16_t carry = (int16_t)_mm_extract_epi16(lo, 7);
915 hi = _mm_add_epi16(hi, _mm_set1_epi16(carry));
917 __m256i out = _mm256_castsi128_si256(lo);
918 out = _mm256_inserti128_si256(out, hi, 1);
922static inline int16_t excess_last_prefix_16x_i16(__m256i pref)
noexcept {
923 __m128i hi = _mm256_extracti128_si256(pref, 1);
924 return (int16_t)_mm_extract_epi16(hi, 7);
927static inline __m256i excess_bit_masks_32x8()
noexcept {
928 return _mm256_setr_epi8(0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (
char)0x80,
929 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (
char)0x80,
930 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (
char)0x80,
931 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (
char)0x80);
934static inline __m256i excess_byte_selectors_32x8()
noexcept {
935 return _mm256_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
936 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
939static inline __m256i excess_prefix_sum_32x_i8(__m256i v)
noexcept {
941 __m256i t = _mm256_slli_si256(x, 1);
942 x = _mm256_add_epi8(x, t);
943 t = _mm256_slli_si256(x, 2);
944 x = _mm256_add_epi8(x, t);
945 t = _mm256_slli_si256(x, 4);
946 x = _mm256_add_epi8(x, t);
947 t = _mm256_slli_si256(x, 8);
948 x = _mm256_add_epi8(x, t);
950 __m128i lo = _mm256_extracti128_si256(x, 0);
951 __m128i hi = _mm256_extracti128_si256(x, 1);
952 const int8_t carry = (int8_t)_mm_extract_epi8(lo, 15);
953 hi = _mm_add_epi8(hi, _mm_set1_epi8(carry));
955 __m256i out = _mm256_castsi128_si256(lo);
956 out = _mm256_inserti128_si256(out, hi, 1);
960static inline int8_t excess_last_prefix_32x_i8(__m256i pref)
noexcept {
961 __m128i hi = _mm256_extracti128_si256(pref, 1);
962 return (int8_t)_mm_extract_epi8(hi, 15);
977static inline ExcessResult excess_min_128_expand16_avx2(
const uint64_t* s,
979 size_t right)
noexcept {
983 left = std::min<size_t>(left, 128);
984 right = std::min<size_t>(right, 128);
986 int best = prefix_excess_128(s, left);
987 size_t best_offset = left;
989 return {best, best_offset};
992 const __m256i masks = excess_bit_masks_16x();
993 const __m256i zero = _mm256_setzero_si256();
994 const __m256i pos = _mm256_set1_epi16(1);
995 const __m256i neg = _mm256_set1_epi16(-1);
998 alignas(32) int16_t prefix_values[16];
999 for (
size_t chunk = 0; chunk < 8; ++chunk) {
1000 const size_t chunk_bit = chunk * 16;
1001 const uint16_t bits =
1003 ?
static_cast<uint16_t
>((s[0] >> (chunk * 16)) & 0xFFFFu)
1004 :
static_cast<uint16_t
>((s[1] >> ((chunk - 4) * 16)) & 0xFFFFu);
1005 const int delta = 2 *
static_cast<int>(std::popcount(bits)) - 16;
1007 if (chunk_bit + 1 <= right && chunk_bit + 16 >= left) {
1008 const __m256i selected = _mm256_and_si256(
1009 _mm256_set1_epi16(
static_cast<int16_t
>(bits)), masks);
1010 const __m256i is_zero = _mm256_cmpeq_epi16(selected, zero);
1011 const __m256i steps = _mm256_blendv_epi8(pos, neg, is_zero);
1012 const __m256i pref =
1013 _mm256_add_epi16(excess_prefix_sum_16x_i16(steps),
1014 _mm256_set1_epi16(
static_cast<int16_t
>(carry)));
1015 _mm256_store_si256(
reinterpret_cast<__m256i*
>(prefix_values), pref);
1017 for (
size_t lane = 0; lane < 16; ++lane) {
1018 const size_t offset = chunk_bit + lane + 1;
1019 if (offset < left || offset > right) {
1022 const int value = prefix_values[lane];
1025 best_offset = offset;
1032 return {best, best_offset};
1047static inline void excess_positions_512_branching_lut(
const uint64_t* s,
1049 uint64_t* out)
noexcept {
1050 out[0] = out[1] = out[2] = out[3] = 0;
1051 out[4] = out[5] = out[6] = out[7] = 0;
1053 if (target_x < -512 || target_x > 512) {
1058 const __m256i vdelta =
1059 _mm256_setr_epi8(-4, -2, -2, 0, -2, 0, 0, 2, -2, 0, 0, 2, 0, 2, 2, 4, -4,
1060 -2, -2, 0, -2, 0, 0, 2, -2, 0, 0, 2, 0, 2, 2, 4);
1061 const __m256i vmult = _mm256_set1_epi16(0x1001);
1062 const __m128i vnibble_mask = _mm_set1_epi8(0x0F);
1064 for (
int k = 0; k < 4; ++k) {
1065 __m128i word_vec = _mm_loadu_si128((
const __m128i*)&s[2 * k]);
1066 __m128i lo_nibbles = _mm_and_si128(word_vec, vnibble_mask);
1067 __m128i hi_nibbles =
1068 _mm_and_si128(_mm_srli_epi16(word_vec, 4), vnibble_mask);
1070 __m128i unpack_lo = _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
1071 __m128i unpack_hi = _mm_unpackhi_epi8(lo_nibbles, hi_nibbles);
1072 __m256i nibbles = _mm256_inserti128_si256(_mm256_castsi128_si256(unpack_lo),
1075 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
1076 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1077 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1078 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1079 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1081 __m128i ps_lo = _mm256_castsi256_si128(ps);
1082 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1083 __m128i carry = _mm_set1_epi8((int8_t)_mm_extract_epi8(ps_lo, 15));
1084 ps_hi = _mm_add_epi8(ps_hi, carry);
1085 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1087 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1088 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1090 int target_rel = target_x - cur;
1092 2 * (std::popcount(s[2 * k]) + std::popcount(s[2 * k + 1])) - 128;
1094 const int d = 2 * target_rel - block_delta;
1095 if (d < -128 || d > 128) {
1100 if (target_rel == 128 || target_rel == -128) {
1101 out[2 * k + 1] |= (uint64_t{1} << 63);
1106 __m256i t = _mm256_sub_epi8(_mm256_set1_epi8((int8_t)target_rel), excl_ps);
1107 __m256i total_match = _mm256_setzero_si256();
1108 __m256i t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(-4));
1109 total_match = _mm256_or_si256(
1111 _mm256_and_si256(t_eq,
1112 _mm256_shuffle_epi8(excess_branch_lut_em4, nibbles)));
1113 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(-3));
1114 total_match = _mm256_or_si256(
1116 _mm256_and_si256(t_eq,
1117 _mm256_shuffle_epi8(excess_branch_lut_em3, nibbles)));
1118 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(-2));
1119 total_match = _mm256_or_si256(
1121 _mm256_and_si256(t_eq,
1122 _mm256_shuffle_epi8(excess_branch_lut_em2, nibbles)));
1123 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(-1));
1124 total_match = _mm256_or_si256(
1126 _mm256_and_si256(t_eq,
1127 _mm256_shuffle_epi8(excess_branch_lut_em1, nibbles)));
1128 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(0));
1129 total_match = _mm256_or_si256(
1131 _mm256_and_si256(t_eq,
1132 _mm256_shuffle_epi8(excess_branch_lut_e0, nibbles)));
1133 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(1));
1134 total_match = _mm256_or_si256(
1136 _mm256_and_si256(t_eq,
1137 _mm256_shuffle_epi8(excess_branch_lut_e1, nibbles)));
1138 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(2));
1139 total_match = _mm256_or_si256(
1141 _mm256_and_si256(t_eq,
1142 _mm256_shuffle_epi8(excess_branch_lut_e2, nibbles)));
1143 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(3));
1144 total_match = _mm256_or_si256(
1146 _mm256_and_si256(t_eq,
1147 _mm256_shuffle_epi8(excess_branch_lut_e3, nibbles)));
1148 t_eq = _mm256_cmpeq_epi8(t, _mm256_set1_epi8(4));
1149 total_match = _mm256_or_si256(
1151 _mm256_and_si256(t_eq,
1152 _mm256_shuffle_epi8(excess_branch_lut_e4, nibbles)));
1154 __m256i res = _mm256_maddubs_epi16(total_match, vmult);
1155 __m128i packed = _mm_packus_epi16(_mm256_castsi256_si128(res),
1156 _mm256_extracti128_si256(res, 1));
1157 _mm_storeu_si128((__m128i*)&out[2 * k], packed);
1170static inline void excess_positions_512_branching_lut(
const uint64_t* s,
1172 uint64_t* out)
noexcept {
1173 excess_positions_512(s, target_x, out);
1177#ifdef PIXIE_AVX512_SUPPORT
1178static inline __m512i excess_lut_delta_64x()
noexcept {
1179 return _mm512_broadcast_i32x4(
1180 _mm_setr_epi8(-4, -2, -2, 0, -2, 0, 0, 2, -2, 0, 0, 2, 0, 2, 2, 4));
1183static inline __m512i excess_lut_pos0_64x()
noexcept {
1184 return _mm512_broadcast_i32x4(
1185 _mm_setr_epi8(-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1));
1188static inline __m512i excess_lut_pos1_64x()
noexcept {
1189 return _mm512_broadcast_i32x4(
1190 _mm_setr_epi8(-2, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 2, -2, 0, 0, 2));
1193static inline __m512i excess_lut_pos2_64x()
noexcept {
1194 return _mm512_broadcast_i32x4(
1195 _mm_setr_epi8(-3, -1, -1, 1, -1, 1, 1, 3, -3, -1, -1, 1, -1, 1, 1, 3));
1198static inline __m512i excess_bit_masks_64x8()
noexcept {
1199 alignas(64)
static constexpr int8_t masks[64] = {
1200 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1201 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1202 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1203 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1204 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1205 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1206 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80,
1207 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (int8_t)0x80};
1208 return _mm512_load_si512((
const void*)masks);
1211static inline __m512i excess_byte_selectors_64x8()
noexcept {
1212 alignas(64)
static constexpr int8_t selectors[64] = {
1213 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
1214 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
1215 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7};
1216 return _mm512_load_si512((
const void*)selectors);
1219static inline __m512i excess_prefix_sum_64x_i8(__m512i v)
noexcept {
1221 __m512i t = _mm512_bslli_epi128(x, 1);
1222 x = _mm512_add_epi8(x, t);
1223 t = _mm512_bslli_epi128(x, 2);
1224 x = _mm512_add_epi8(x, t);
1225 t = _mm512_bslli_epi128(x, 4);
1226 x = _mm512_add_epi8(x, t);
1227 t = _mm512_bslli_epi128(x, 8);
1228 x = _mm512_add_epi8(x, t);
1230 const __m512i last_byte = _mm512_set1_epi8(15);
1231 const __m512i lane_carry = _mm512_shuffle_epi8(x, last_byte);
1232 const __m512i shift1_idx =
1233 _mm512_setr_epi32(0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
1234 const __m512i shift2_idx =
1235 _mm512_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7);
1236 const __m512i shift3_idx =
1237 _mm512_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3);
1240 _mm512_maskz_permutexvar_epi32(0xFFF0, shift1_idx, lane_carry);
1241 lane_base = _mm512_add_epi8(lane_base, _mm512_maskz_permutexvar_epi32(
1242 0xFF00, shift2_idx, lane_carry));
1243 lane_base = _mm512_add_epi8(lane_base, _mm512_maskz_permutexvar_epi32(
1244 0xF000, shift3_idx, lane_carry));
1245 return _mm512_add_epi8(x, lane_base);
1248static inline __m512i excess_prefix_sum_2x32_i8(__m512i v)
noexcept {
1250 __m512i t = _mm512_bslli_epi128(x, 1);
1251 x = _mm512_add_epi8(x, t);
1252 t = _mm512_bslli_epi128(x, 2);
1253 x = _mm512_add_epi8(x, t);
1254 t = _mm512_bslli_epi128(x, 4);
1255 x = _mm512_add_epi8(x, t);
1256 t = _mm512_bslli_epi128(x, 8);
1257 x = _mm512_add_epi8(x, t);
1259 const __m512i last_byte = _mm512_set1_epi8(15);
1260 const __m512i lane_carry = _mm512_shuffle_epi8(x, last_byte);
1261 const __m512i prev_lane_idx =
1262 _mm512_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8);
1263 const __mmask16 carry_to_second_lane_of_each_half = 0xF0F0;
1264 const __m512i lane_base = _mm512_maskz_permutexvar_epi32(
1265 carry_to_second_lane_of_each_half, prev_lane_idx, lane_carry);
1266 return _mm512_add_epi8(x, lane_base);
1269static inline __m512i excess_nibbles_64x_from_256(__m256i words)
noexcept {
1270 const __m512i bytes16 = _mm512_cvtepu8_epi16(words);
1271 const __m512i low = _mm512_and_si512(bytes16, _mm512_set1_epi16(0x000F));
1272 const __m512i high = _mm512_and_si512(_mm512_srli_epi16(bytes16, 4),
1273 _mm512_set1_epi16(0x000F));
1274 return _mm512_or_si512(low, _mm512_slli_epi16(high, 8));
1277static inline __m512i excess_exclusive_prefix_2x32_i8(__m512i pref)
noexcept {
1278 const __m512i zero = _mm512_setzero_si512();
1279 __m512i out = _mm512_alignr_epi8(pref, zero, 15);
1281 const __m512i last_byte = _mm512_set1_epi8(15);
1282 const __m512i lane_carry = _mm512_shuffle_epi8(pref, last_byte);
1283 const __m512i prev_lane_idx =
1284 _mm512_setr_epi32(0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8);
1285 const __m512i carry_dwords =
1286 _mm512_permutexvar_epi32(prev_lane_idx, lane_carry);
1287 const __mmask64 first_byte_of_second_lane_in_each_half =
1288 (uint64_t{1} << 16) | (uint64_t{1} << 48);
1289 return _mm512_or_si512(
1290 out, _mm512_maskz_mov_epi8(first_byte_of_second_lane_in_each_half,
1294static inline uint64_t excess_repeat_byte(
int value)
noexcept {
1295 return uint64_t{0x0101010101010101} *
1296 static_cast<uint8_t
>(
static_cast<int8_t
>(value));
1312static inline void excess_positions_512_lut_avx512(
const uint64_t* s,
1314 uint64_t* out)
noexcept {
1315 out[0] = out[1] = out[2] = out[3] = 0;
1316 out[4] = out[5] = out[6] = out[7] = 0;
1318 if (target_x < -512 || target_x > 512) {
1322 static const __m512i vdelta = excess_lut_delta_64x();
1323 static const __m512i vpos0 = excess_lut_pos0_64x();
1324 static const __m512i vpos1 = excess_lut_pos1_64x();
1325 static const __m512i vpos2 = excess_lut_pos2_64x();
1326 static const __m512i vbit0 = _mm512_set1_epi8(1);
1327 static const __m512i vbit1 = _mm512_set1_epi8(2);
1328 static const __m512i vbit2 = _mm512_set1_epi8(4);
1329 static const __m512i vbit3 = _mm512_set1_epi8(8);
1330 static const __m512i vmult = _mm512_set1_epi16(0x1001);
1332 for (
int k = 0; k < 2; ++k) {
1333 const int base_word = 4 * k;
1335 2 * (std::popcount(s[base_word]) + std::popcount(s[base_word + 1])) -
1337 const int delta1 = 2 * (std::popcount(s[base_word + 2]) +
1338 std::popcount(s[base_word + 3])) -
1340 const int target0 = target_x;
1341 const int target1 = target_x - delta0;
1342 const bool reachable0 = [&] {
1343 const int d = 2 * target0 - delta0;
1344 return -128 <= d && d <= 128;
1346 const bool reachable1 = [&] {
1347 const int d = 2 * target1 - delta1;
1348 return -128 <= d && d <= 128;
1351 if (!reachable0 && !reachable1) {
1352 target_x -= delta0 + delta1;
1356 const __m256i words =
1357 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(&s[base_word]));
1358 const __m512i nibbles = excess_nibbles_64x_from_256(words);
1360 excess_prefix_sum_2x32_i8(_mm512_shuffle_epi8(vdelta, nibbles));
1361 const __m512i excl_ps = excess_exclusive_prefix_2x32_i8(ps);
1362 const uint64_t repeated0 = excess_repeat_byte(target0);
1363 const uint64_t repeated1 = excess_repeat_byte(target1);
1364 const __m512i vtgt =
1365 _mm512_setr_epi64(repeated0, repeated0, repeated0, repeated0, repeated1,
1366 repeated1, repeated1, repeated1);
1367 const __m512i t = _mm512_sub_epi8(vtgt, excl_ps);
1369 const __mmask64 cmp0 =
1370 _mm512_cmpeq_epi8_mask(_mm512_shuffle_epi8(vpos0, nibbles), t);
1371 const __mmask64 cmp1 =
1372 _mm512_cmpeq_epi8_mask(_mm512_shuffle_epi8(vpos1, nibbles), t);
1373 const __mmask64 cmp2 =
1374 _mm512_cmpeq_epi8_mask(_mm512_shuffle_epi8(vpos2, nibbles), t);
1375 const __mmask64 cmp3 = _mm512_cmpeq_epi8_mask(ps, vtgt);
1376 __m512i total_match = _mm512_maskz_mov_epi8(cmp0, vbit0);
1378 _mm512_or_si512(total_match, _mm512_maskz_mov_epi8(cmp1, vbit1));
1380 _mm512_or_si512(total_match, _mm512_maskz_mov_epi8(cmp2, vbit2));
1382 _mm512_or_si512(total_match, _mm512_maskz_mov_epi8(cmp3, vbit3));
1384 const __mmask64 active =
1385 (reachable0 ? __mmask64{0x00000000FFFFFFFFull} : __mmask64{0}) |
1386 (reachable1 ? __mmask64{0xFFFFFFFF00000000ull} : __mmask64{0});
1387 total_match = _mm512_maskz_mov_epi8(active, total_match);
1389 const __m512i res = _mm512_maddubs_epi16(total_match, vmult);
1390 const __m256i packed = _mm512_cvtepi16_epi8(res);
1391 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(&out[base_word]), packed);
1393 target_x -= delta0 + delta1;
1404static inline void excess_positions_512_lut_avx512(
const uint64_t* s,
1406 uint64_t* out)
noexcept {
1407 excess_positions_512(s, target_x, out);
1423static inline void excess_positions_512_expand(
const uint64_t* s,
1425 uint64_t* out)
noexcept {
1426 out[0] = out[1] = out[2] = out[3] = 0;
1427 out[4] = out[5] = out[6] = out[7] = 0;
1429 if (target_x < -512 || target_x > 512) {
1433#ifdef PIXIE_AVX2_SUPPORT
1434 static const __m256i masks = excess_bit_masks_16x();
1435 static const __m256i vzero = _mm256_setzero_si256();
1436 static const __m256i vallones = _mm256_cmpeq_epi16(vzero, vzero);
1437 static const __m256i vminus1 = _mm256_set1_epi16(-1);
1438 static const __m256i vtwo = _mm256_set1_epi16(2);
1439 const __m256i vtarget = _mm256_set1_epi16((int16_t)target_x);
1442 for (
int block = 0; block < 4; ++block) {
1443 const int target_rel = target_x - cur;
1444 if (target_rel <= -64 || target_rel >= 64) {
1445 const int block_delta =
1446 2 * (std::popcount(s[2 * block]) + std::popcount(s[2 * block + 1])) -
1448 const int reachability = 2 * target_rel - block_delta;
1449 if (reachability < -128 || reachability > 128) {
1455 for (
int j = 0; j < 8; ++j) {
1456 const int k = 8 * block + j;
1457 const size_t word_idx = size_t(k) >> 2;
1458 const size_t shift = size_t(k & 3) * 16;
1459 const uint16_t bits16 =
1460 static_cast<uint16_t
>((s[word_idx] >> shift) & 0xFFFFull);
1462 const __m256i vb = _mm256_set1_epi16((int16_t)bits16);
1463 const __m256i m = _mm256_and_si256(vb, masks);
1464 const __m256i is_zero = _mm256_cmpeq_epi16(m, vzero);
1465 const __m256i is_set = _mm256_andnot_si256(is_zero, vallones);
1466 const __m256i steps =
1467 _mm256_add_epi16(vminus1, _mm256_and_si256(is_set, vtwo));
1469 const __m256i pref_rel = excess_prefix_sum_16x_i16(steps);
1470 const __m256i base = _mm256_set1_epi16((int16_t)cur);
1471 const __m256i pref_abs = _mm256_add_epi16(pref_rel, base);
1472 const __m256i cmp = _mm256_cmpeq_epi16(pref_abs, vtarget);
1474 const uint32_t m32 = (uint32_t)_mm256_movemask_epi8(cmp);
1475 const uint16_t m16 = (uint16_t)_pext_u32(m32, 0xAAAAAAAAu);
1477 out[word_idx] |= uint64_t(m16) << shift;
1478 cur += (int)excess_last_prefix_16x_i16(pref_rel);
1483 for (
size_t i = 0; i < 512; ++i) {
1484 const uint64_t w = s[i >> 6];
1485 const int bit = int((w >> (i & 63)) & 1ull);
1486 cur += bit ? +1 : -1;
1487 if (cur == target_x) {
1488 out[i >> 6] |= (uint64_t{1} << (i & 63));
1506static inline void excess_positions_512_expand8(
const uint64_t* s,
1508 uint64_t* out)
noexcept {
1509 out[0] = out[1] = out[2] = out[3] = 0;
1510 out[4] = out[5] = out[6] = out[7] = 0;
1512 if (target_x < -512 || target_x > 512) {
1516#ifdef PIXIE_AVX2_SUPPORT
1517 static const __m256i byte_selectors = excess_byte_selectors_32x8();
1518 static const __m256i masks = excess_bit_masks_32x8();
1519 static const __m256i vzero = _mm256_setzero_si256();
1520 static const __m256i vallones = _mm256_cmpeq_epi8(vzero, vzero);
1521 static const __m256i vminus1 = _mm256_set1_epi8(-1);
1522 static const __m256i vtwo = _mm256_set1_epi8(2);
1525 for (
int k = 0; k < 16; ++k) {
1526 const size_t word_idx = size_t(k) >> 1;
1527 const size_t shift = size_t(k & 1) * 32;
1528 const uint32_t bits32 =
1529 static_cast<uint32_t
>((s[word_idx] >> shift) & 0xFFFFFFFFull);
1531 const int target_rel = target_x - cur;
1532 if (target_rel < -32 || target_rel > 32) {
1533 cur += 2 *
static_cast<int>(std::popcount(bits32)) - 32;
1537 const __m256i src = _mm256_set1_epi32((
int)bits32);
1538 const __m256i bytes = _mm256_shuffle_epi8(src, byte_selectors);
1539 const __m256i m = _mm256_and_si256(bytes, masks);
1540 const __m256i is_zero = _mm256_cmpeq_epi8(m, vzero);
1541 const __m256i is_set = _mm256_andnot_si256(is_zero, vallones);
1542 const __m256i steps =
1543 _mm256_add_epi8(vminus1, _mm256_and_si256(is_set, vtwo));
1545 const __m256i pref_rel = excess_prefix_sum_32x_i8(steps);
1546 const __m256i vtarget = _mm256_set1_epi8((int8_t)target_rel);
1547 const __m256i cmp = _mm256_cmpeq_epi8(pref_rel, vtarget);
1548 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(cmp));
1550 out[word_idx] |= uint64_t(mask) << shift;
1551 cur +=
static_cast<int>(excess_last_prefix_32x_i8(pref_rel));
1555 for (
size_t i = 0; i < 512; ++i) {
1556 const uint64_t w = s[i >> 6];
1557 const int bit = int((w >> (i & 63)) & 1ull);
1558 cur += bit ? +1 : -1;
1559 if (cur == target_x) {
1560 out[i >> 6] |= (uint64_t{1} << (i & 63));
1578static inline void excess_positions_512_expand_avx512(
const uint64_t* s,
1580 uint64_t* out)
noexcept {
1581 out[0] = out[1] = out[2] = out[3] = 0;
1582 out[4] = out[5] = out[6] = out[7] = 0;
1584 if (target_x < -512 || target_x > 512) {
1588#ifdef PIXIE_AVX512_SUPPORT
1589 static const __m512i byte_selectors = excess_byte_selectors_64x8();
1590 static const __m512i masks = excess_bit_masks_64x8();
1591 static const __m512i vzero = _mm512_setzero_si512();
1592 static const __m512i vallones = _mm512_set1_epi8(-1);
1593 static const __m512i vminus1 = _mm512_set1_epi8(-1);
1594 static const __m512i vtwo = _mm512_set1_epi8(2);
1597 for (
int k = 0; k < 8; ++k) {
1598 const uint64_t bits64 = s[k];
1599 const int target_rel = target_x - cur;
1600 if (target_rel < -64 || target_rel > 64) {
1601 cur += 2 *
static_cast<int>(std::popcount(bits64)) - 64;
1605 const __m512i src = _mm512_set1_epi64(
static_cast<int64_t
>(bits64));
1606 const __m512i bytes = _mm512_shuffle_epi8(src, byte_selectors);
1607 const __m512i m = _mm512_and_si512(bytes, masks);
1608 const __mmask64 is_zero = _mm512_cmpeq_epi8_mask(m, vzero);
1609 const __m512i is_set = _mm512_maskz_mov_epi8(~is_zero, vallones);
1610 const __m512i steps =
1611 _mm512_add_epi8(vminus1, _mm512_and_si512(is_set, vtwo));
1613 const __m512i pref_rel = excess_prefix_sum_64x_i8(steps);
1614 const __mmask64 match =
1615 _mm512_cmpeq_epi8_mask(pref_rel, _mm512_set1_epi8((int8_t)target_rel));
1616 out[k] =
static_cast<uint64_t
>(match);
1617 cur += 2 *
static_cast<int>(std::popcount(bits64)) - 64;
1620 excess_positions_512_expand8(s, target_x, out);
1624struct ExcessByteLut {
1625 uint8_t masks[256][17];
1628 constexpr ExcessByteLut() : masks{}, deltas{} {
1629 for (
int b = 0; b < 256; ++b) {
1631 for (
int i = 0; i < 8; ++i) {
1636 deltas[b] =
static_cast<int8_t
>(2 * pop - 8);
1638 for (
int t = -8; t <= 8; ++t) {
1641 for (
int i = 0; i < 8; ++i) {
1645 int excess = 2 * cur_pop - (i + 1);
1650 masks[b][t + 8] = mask;
1671static inline void excess_positions_512_byte_lut(
const uint64_t* s,
1673 uint64_t* out)
noexcept {
1674 out[0] = out[1] = out[2] = out[3] = 0;
1675 out[4] = out[5] = out[6] = out[7] = 0;
1677 if (target_x < -512 || target_x > 512) {
1681 const uint8_t* bytes =
reinterpret_cast<const uint8_t*
>(s);
1682 uint8_t* out_bytes =
reinterpret_cast<uint8_t*
>(out);
1685 for (
int i = 0; i < 64; ++i) {
1686 const uint8_t b = bytes[i];
1687 const int target_rel = target_x - cur;
1688 if (target_rel >= -8 && target_rel <= 8) {
1689 out_bytes[i] = kExcessByteLut.masks[b][target_rel + 8];
1691 cur += kExcessByteLut.deltas[b];