From 13bf7edad251df9943bd75665a3510535849b0ac Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Wed, 14 Oct 2026 17:36:22 +0300 Subject: Deduplicate scroll and SIMD paths while preserving performance --- src/text_run.c | 102 ++++++++++++++++++++++----------------------------------- 1 file changed, 40 insertions(+), 62 deletions(-) (limited to 'src/text_run.c') diff --git a/src/text_run.c b/src/text_run.c index bf0e8a9..028b160 100644 --- a/src/text_run.c +++ b/src/text_run.c @@ -53,17 +53,11 @@ static TERM_MAYBE_UNUSED int width_run_find(uint32_t cp) static TERM_MAYBE_UNUSED uint16_t width_cache_covers(const struct width_cache *wc, const uint32_t *cp) { - uint32_t lo0 = wc->lo[0], span0 = wc->span[0]; - uint32_t lo1 = wc->lo[1], span1 = wc->span[1]; - uint32_t lo2 = wc->lo[2], span2 = wc->span[2]; - uint32_t lo3 = wc->lo[3], span3 = wc->span[3]; uint16_t covered = 0; for (int l = 0; l < 16; l++) { - uint32_t c = cp[l]; - bool hit = ((c - lo0 <= span0) | - (c - lo1 <= span1) | - (c - lo2 <= span2) | - (c - lo3 <= span3)); + bool hit = false; + for (int k = 0; k < TERM_WIDTH_SLOTS; k++) + hit |= cp[l] - wc->lo[k] <= wc->span[k]; covered |= (uint16_t)(hit << l); } return covered; @@ -146,24 +140,20 @@ TERM_TARGET_AVX512 static inline struct width_ranges width_ranges_load(const struct width_cache *wc) { struct width_ranges r; - r.lo[0] = _mm512_set1_epi32((int)wc->lo[0]); - r.span[0] = _mm512_set1_epi32((int)wc->span[0]); - r.lo[1] = _mm512_set1_epi32((int)wc->lo[1]); - r.span[1] = _mm512_set1_epi32((int)wc->span[1]); - r.lo[2] = _mm512_set1_epi32((int)wc->lo[2]); - r.span[2] = _mm512_set1_epi32((int)wc->span[2]); - r.lo[3] = _mm512_set1_epi32((int)wc->lo[3]); - r.span[3] = _mm512_set1_epi32((int)wc->span[3]); + for (int k = 0; k < TERM_WIDTH_SLOTS; k++) { + r.lo[k] = _mm512_set1_epi32((int)wc->lo[k]); + r.span[k] = _mm512_set1_epi32((int)wc->span[k]); + } return r; } TERM_TARGET_AVX512 static inline __mmask16 width_ranges_cover(const struct width_ranges *r, __m512i cps) { - return _mm512_cmple_epu32_mask(_mm512_sub_epi32(cps, r->lo[0]), r->span[0]) - | _mm512_cmple_epu32_mask(_mm512_sub_epi32(cps, r->lo[1]), r->span[1]) - | _mm512_cmple_epu32_mask(_mm512_sub_epi32(cps, r->lo[2]), r->span[2]) - | _mm512_cmple_epu32_mask(_mm512_sub_epi32(cps, r->lo[3]), r->span[3]); + __mmask16 covered = 0; + for (int k = 0; k < TERM_WIDTH_SLOTS; k++) + covered |= _mm512_cmple_epu32_mask(_mm512_sub_epi32(cps, r->lo[k]), r->span[k]); + return covered; } TERM_TARGET_AVX512 @@ -189,6 +179,31 @@ static inline void store_narrow_cells(uint64_t * restrict dst, __m512i base, __m #undef TERM_NARROW_QUAD } +/* Both cell and history writers validate and classify the same UTF-8 block. */ +TERM_TARGET_AVX512 +static inline bool classify_3byte_block(__m512i in, struct width_ranges *ranges, + struct width_cache *wc, __m512i *cps) +{ + if (TERM_EXPECT(_mm512_test_epi8_mask( + _mm512_xor_si512(in, _mm512_loadu_si512(utf8_3byte_want)), + _mm512_loadu_si512(utf8_3byte_bits)) & UTF8_3BYTE_SPAN, 0)) + return false; + *cps = utf8_3byte_decode(_mm512_permutexvar_epi8(_mm512_loadu_si512(utf8_3byte_shuffle), in)); + if (_mm512_cmplt_epu32_mask(*cps, _mm512_set1_epi32((int)UTF8_3BYTE_MIN)) || + _mm512_cmple_epu32_mask( + _mm512_sub_epi32(*cps, _mm512_set1_epi32((int)UTF8_SURROGATE_LO)), + _mm512_set1_epi32((int)(UTF8_SURROGATE_HI - UTF8_SURROGATE_LO)))) + return false; + __mmask16 covered = width_ranges_cover(ranges, *cps); + if (TERM_EXPECT(covered != 0xFFFF || wc->klass == 0, 0)) { + uint32_t cp[16]; + _mm512_storeu_si512(cp, *cps); + if (!width_cache_extend(wc, cp, covered)) return false; + *ranges = width_ranges_load(wc); + } + return true; +} + TERM_TARGET_AVX512 static TERM_NOINLINE int decode_3byte_blocks(uint64_t * restrict dst, const char * restrict buf, int len, int cols_left, @@ -197,33 +212,14 @@ decode_3byte_blocks(uint64_t * restrict dst, const char * restrict buf, int len, int * restrict out_cols, uint32_t * restrict last_cp) { struct width_ranges ranges = width_ranges_load(wc); - __m512i shuffle = _mm512_loadu_si512(utf8_3byte_shuffle); - __m512i want = _mm512_loadu_si512(utf8_3byte_want); - __m512i bits = _mm512_loadu_si512(utf8_3byte_bits); uint32_t last = *last_cp; int i = 0, cx = 0; while (i + 48 <= len && cx + 32 <= cols_left && ((unsigned char)buf[i + 45] & UTF8_LEAD3_MASK) == UTF8_LEAD3_TAG) { __m512i in = _mm512_maskz_loadu_epi8(UTF8_3BYTE_SPAN, buf + i); - if (TERM_EXPECT(_mm512_test_epi8_mask(_mm512_xor_si512(in, want), bits) & - UTF8_3BYTE_SPAN, 0)) - break; - - __m512i cps = utf8_3byte_decode(_mm512_permutexvar_epi8(shuffle, in)); - if (_mm512_cmplt_epu32_mask(cps, _mm512_set1_epi32((int)UTF8_3BYTE_MIN)) || - _mm512_cmple_epu32_mask( - _mm512_sub_epi32(cps, _mm512_set1_epi32((int)UTF8_SURROGATE_LO)), - _mm512_set1_epi32((int)(UTF8_SURROGATE_HI - UTF8_SURROGATE_LO)))) - break; - __mmask16 covered = width_ranges_cover(&ranges, cps); - if (TERM_EXPECT(covered != 0xFFFF || wc->klass == 0, 0)) { - uint32_t cp[16]; - _mm512_storeu_si512(cp, cps); - if (!width_cache_extend(wc, cp, covered)) - break; - ranges = width_ranges_load(wc); - } + __m512i cps; + if (!classify_3byte_block(in, &ranges, wc, &cps)) break; if (wc->klass == 2) { store_wide_cells(dst, base_wide_pair, cps); @@ -258,9 +254,6 @@ text_3byte_blocks(uint8_t * restrict dst, const char * restrict buf, int len, in 0, 36, 37, 38, 0, 39, 40, 41, 0, 42, 43, 44, 0, 45, 46, 47, }; struct width_ranges ranges = width_ranges_load(wc); - __m512i shuffle = _mm512_loadu_si512(utf8_3byte_shuffle); - __m512i want = _mm512_loadu_si512(utf8_3byte_want); - __m512i bits = _mm512_loadu_si512(utf8_3byte_bits); __m512i idx = _mm512_loadu_si512(spread); __m512i prefix = _mm512_set1_epi8((char)0xF5); uint32_t last = *last_cp; @@ -269,23 +262,8 @@ text_3byte_blocks(uint8_t * restrict dst, const char * restrict buf, int len, in while (i + 48 <= len && cx + 32 <= cols_left && ((unsigned char)buf[i + 45] & UTF8_LEAD3_MASK) == UTF8_LEAD3_TAG) { __m512i in = _mm512_maskz_loadu_epi8(UTF8_3BYTE_SPAN, buf + i); - if (TERM_EXPECT(_mm512_test_epi8_mask(_mm512_xor_si512(in, want), bits) & - UTF8_3BYTE_SPAN, 0)) - break; - __m512i cps = utf8_3byte_decode(_mm512_permutexvar_epi8(shuffle, in)); - if (_mm512_cmplt_epu32_mask(cps, _mm512_set1_epi32((int)UTF8_3BYTE_MIN)) || - _mm512_cmple_epu32_mask( - _mm512_sub_epi32(cps, _mm512_set1_epi32((int)UTF8_SURROGATE_LO)), - _mm512_set1_epi32((int)(UTF8_SURROGATE_HI - UTF8_SURROGATE_LO)))) - break; - __mmask16 covered = width_ranges_cover(&ranges, cps); - if (TERM_EXPECT(covered != 0xFFFF || wc->klass == 0, 0)) { - uint32_t cp[16]; - _mm512_storeu_si512(cp, cps); - if (!width_cache_extend(wc, cp, covered)) - break; - ranges = width_ranges_load(wc); - } + __m512i cps; + if (!classify_3byte_block(in, &ranges, wc, &cps)) break; if (wc->klass == 2) { __m512i out = _mm512_mask_mov_epi8(_mm512_permutexvar_epi8(idx, in), 0x1111111111111111ull, prefix); -- cgit v1.2.3