From 04e4e4bd13d6216e53a156c41eb9d205ef0f07c3 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Fri, 14 Feb 2020 21:24:22 -0300 Subject: [PATCH 01/10] the algorithm has already been fully implemented (sse2 and avx2), adding test accessing the library through rffi (currently failing) --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/__init__.py | 3 +++ rpython/rlib/fastutf8/capi.py | 2 +- rpython/rlib/test/test_fastutf8.py | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 rpython/rlib/fastutf8/__init__.py create mode 100644 rpython/rlib/test/test_fastutf8.py diff --git a/rpython/rlib/fastutf8/__init__.py b/rpython/rlib/fastutf8/__init__.py new file mode 100644 index 0000000000..77c62d5e45 --- /dev/null +++ b/rpython/rlib/fastutf8/__init__.py @@ -0,0 +1,3 @@ +from rpython.rlib.fastutf8 import capi + +fu8 = capi.setup() diff --git a/rpython/rlib/fastutf8/capi.py b/rpython/rlib/fastutf8/capi.py index 211db2d07e..cec4229844 100644 --- a/rpython/rlib/fastutf8/capi.py +++ b/rpython/rlib/fastutf8/capi.py @@ -6,7 +6,7 @@ from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.rtyper.tool import rffi_platform as platform from rpython.translator.platform import platform as trans_plaform -ROOT = py.path.local(rpythonroot).join('rpython', 'rlib', 'rutf8') +ROOT = py.path.local(rpythonroot).join('rpython', 'rlib', 'fastutf8') SRC = ROOT.join('src') if sys.platform.startswith('linux'): diff --git a/rpython/rlib/test/test_fastutf8.py b/rpython/rlib/test/test_fastutf8.py new file mode 100644 index 0000000000..f204161e27 --- /dev/null +++ b/rpython/rlib/test/test_fastutf8.py @@ -0,0 +1,17 @@ +#encoding: utf-8 +import pytest +import sys +from hypothesis import given, strategies as st, settings, example + +from rpython.rlib.fastutf8 import fu8 +from rpython.rtyper.lltypesystem import lltype, rffi + + +@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')))) +@example("\xf0\x90\x80\x80\xe0\xa0\x80\xe0\xa0\x80\xe0\xa0\x80\xf0\x90\x80\x80\xe0\xa0\x80\xf0\x90\x80\x80\xf0\x90\x80\x80\xf0\x90\x80\x80".decode('utf8')) +def test_unichr_as_utf8(chars): + print(chars) + bytes = chars.encode('utf-8') + ptr = rffi.str2charp(bytes) + assert fu8.count_utf8_codepoints(ptr, len(bytes)) == len(chars) + rffi.free_charp(ptr) -- GitLab From b36669de930c7a4099a62b5ac6deeaadb2a2d5ea Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Sat, 15 Feb 2020 14:34:53 -0300 Subject: [PATCH 02/10] deactivated avx/avx2 version, has a flaw in left shifting. hinged count_utf8_codepoints in rutf8 library --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/src/utf8-avx.c | 7 ++++--- rpython/rlib/fastutf8/src/utf8-sse4.c | 2 +- rpython/rlib/fastutf8/src/utf8.c | 5 +++-- rpython/rlib/rutf8.py | 11 +++-------- rpython/rlib/test/test_fastutf8.py | 16 ++++++++++------ 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/rpython/rlib/fastutf8/src/utf8-avx.c b/rpython/rlib/fastutf8/src/utf8-avx.c index 1366084457..62761612e4 100644 --- a/rpython/rlib/fastutf8/src/utf8-avx.c +++ b/rpython/rlib/fastutf8/src/utf8-avx.c @@ -7,9 +7,9 @@ #define BIT(B,P) ((B >> (P-1)) & 0x1) -void _print_mmy(const char * msg, __m256i chunk) +void _print_mm256x(const char * msg, __m256i chunk) { - printf("%s:", msg); + printf("%s:\n", msg); // unpack the first 8 bytes, padding with zeros uint64_t a = _mm256_extract_epi64(chunk, 0); uint64_t b = _mm256_extract_epi64(chunk, 1); @@ -110,6 +110,7 @@ ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len) } __m256i state2 = _mm256_andnot_si256(threebytemarker, twobytemarker); + // ERROR, this shift is flawed, it will cut of at the register boundary __m256i contbytes = _mm256_slli_si256(_mm256_blendv_epi8(state2, _mm256_set1_epi8(0x1), twobytemarker), 1); if (_mm256_movemask_epi8(threebytemarker) != 0) { @@ -245,7 +246,7 @@ ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len) return num_codepoints; } - ssize_t result = fu8_count_utf8_codepoints_seq(encoded, len); + ssize_t result = fu8_count_utf8_codepoints_seq((const char*)encoded, len); if (result == -1) { return -1; } diff --git a/rpython/rlib/fastutf8/src/utf8-sse4.c b/rpython/rlib/fastutf8/src/utf8-sse4.c index 2b286cf81e..26271b28a1 100644 --- a/rpython/rlib/fastutf8/src/utf8-sse4.c +++ b/rpython/rlib/fastutf8/src/utf8-sse4.c @@ -223,7 +223,7 @@ ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len) return num_codepoints; } - ssize_t result = fu8_count_utf8_codepoints_seq(encoded, len); + ssize_t result = fu8_count_utf8_codepoints_seq((const char*)encoded, len); if (result == -1) { return -1; } diff --git a/rpython/rlib/fastutf8/src/utf8.c b/rpython/rlib/fastutf8/src/utf8.c index 52c4ee56a7..5f7419240a 100644 --- a/rpython/rlib/fastutf8/src/utf8.c +++ b/rpython/rlib/fastutf8/src/utf8.c @@ -43,10 +43,11 @@ ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len) detect_instructionset(); } + /* AVX IS NOT WORKING YET. HAVE A LOOK AT THE LEFT SHIFT ISSUE if (len >= 32 && (instruction_set & ISET_AVX2) != 0) { // to the MOON! return fu8_count_utf8_codepoints_avx(utf8, len); - } + }*/ if (len >= 16 && (instruction_set == ISET_SSE4) != 0) { // speed!! return fu8_count_utf8_codepoints_sse4(utf8, len); @@ -159,7 +160,7 @@ ssize_t _fu8_idx2bytepos(size_t index, assert(index != 0 && "index must not be 0"); // note that itab STILL can be NULL - + return 0; } size_t _fu8_idxtab_lookup_bytepos_i(struct fu8_idxtab * tab, size_t cpidx) diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py index 2a06ff5f62..15ed1a0bbd 100644 --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -25,6 +25,7 @@ from rpython.rlib.types import char, none from rpython.rlib.rarithmetic import r_uint from rpython.rlib.unicodedata import unicodedb from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib.fastutf8 import fu8 # We always use MAXUNICODE = 0x10ffff when unicode objects use utf8 MAXUNICODE = 0x10ffff @@ -474,16 +475,10 @@ def codepoints_in_utf8(value, start=0, end=sys.maxint): if end > len(value): end = len(value) assert 0 <= start <= end - length = 0 - for i in range(start, end): - # we want to count the number of chars not between 0x80 and 0xBF; - # we do that by casting the char to a signed integer - signedchar = rffi.cast(rffi.SIGNEDCHAR, ord(value[i])) - if rffi.cast(lltype.Signed, signedchar) >= -0x40: - length += 1 + ptr = rffi.get_raw_address_of_string(value) + length = fu8.count_utf8_codepoints(ptr+start, end-start) return length - @jit.elidable def surrogate_in_utf8(utf8): """Check if the UTF-8 byte string 'value' contains a surrogate. diff --git a/rpython/rlib/test/test_fastutf8.py b/rpython/rlib/test/test_fastutf8.py index f204161e27..e28a281987 100644 --- a/rpython/rlib/test/test_fastutf8.py +++ b/rpython/rlib/test/test_fastutf8.py @@ -6,12 +6,16 @@ from hypothesis import given, strategies as st, settings, example from rpython.rlib.fastutf8 import fu8 from rpython.rtyper.lltypesystem import lltype, rffi +def _check_size(bytes, size): + ptr = rffi.str2charp(bytes) + assert fu8.count_utf8_codepoints(ptr, len(bytes)) == size + rffi.free_charp(ptr) -@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')))) -@example("\xf0\x90\x80\x80\xe0\xa0\x80\xe0\xa0\x80\xe0\xa0\x80\xf0\x90\x80\x80\xe0\xa0\x80\xf0\x90\x80\x80\xf0\x90\x80\x80\xf0\x90\x80\x80".decode('utf8')) +@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')), min_size=16)) def test_unichr_as_utf8(chars): print(chars) - bytes = chars.encode('utf-8') - ptr = rffi.str2charp(bytes) - assert fu8.count_utf8_codepoints(ptr, len(bytes)) == len(chars) - rffi.free_charp(ptr) + _check_size(chars.encode('utf-8'), len(chars)) + +def test_avx_crash(): + text = u'AAAAAAAAAAAAA\xa6\xa6AAAAAAAAAAAAAAAAA' + _check_size(text.encode('utf-8'), len(text)) -- GitLab From 305bf34b3164775c570bd0c0845e0212e7aa4700 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Sat, 15 Feb 2020 14:51:08 -0300 Subject: [PATCH 03/10] detect_instructionset on other archs (which does nothing right now) simplified the scalar version --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/src/utf8-scalar.c | 48 +++---------------------- rpython/rlib/fastutf8/src/utf8.c | 7 ++++ 2 files changed, 11 insertions(+), 44 deletions(-) diff --git a/rpython/rlib/fastutf8/src/utf8-scalar.c b/rpython/rlib/fastutf8/src/utf8-scalar.c index 77556157ad..c41000a6c7 100644 --- a/rpython/rlib/fastutf8/src/utf8-scalar.c +++ b/rpython/rlib/fastutf8/src/utf8-scalar.c @@ -19,52 +19,12 @@ int _check_continuation(const uint8_t ** encoded, const uint8_t * endptr, int co ssize_t fu8_count_utf8_codepoints_seq(const char * utf8, size_t len) { size_t num_codepoints = 0; - uint8_t byte = 0; - const uint8_t * encoded = (const uint8_t*)utf8; - const uint8_t * endptr = encoded + len; + const char * encoded = utf8; + const char * endptr = encoded + len; while (encoded < endptr) { - byte = *encoded++; - if (byte < 0x80) { - num_codepoints += 1; - continue; - } else { - //asm("int $3"); - if ((byte & 0xe0) == 0xc0) { - // one continuation byte - if (byte < 0xc2) { - return -1; - } - if (_check_continuation(&encoded, endptr, 1) != 0) { - return -1; - } - } else if ((byte & 0xf0) == 0xe0) { - // two continuation byte - if (_check_continuation(&encoded, endptr, 2) != 0) { - return -1; - } - uint8_t byte1 = encoded[-2]; - //surrogates shouldn't be valid UTF-8! - if ((byte == 0xe0 && byte1 < 0xa0) || - (byte == 0xed && byte1 > 0x9f && !ALLOW_SURROGATES)) { - return -1; - } - } else if ((byte & 0xf8) == 0xf0) { - // three continuation byte - if (_check_continuation(&encoded, endptr, 3) != 0) { - return -1; - } - uint8_t byte1 = encoded[-3]; - if ((byte == 0xf0 && byte1 < 0x90) || - (byte == 0xf4 && byte1 > 0x8f) || - (byte >= 0xf5)) { - return -1; - } - } else { - // TODO - return -1; - } - num_codepoints += 1; + if (*encoded++ >= -0x40) { + num_codepoints++; } } return num_codepoints; diff --git a/rpython/rlib/fastutf8/src/utf8.c b/rpython/rlib/fastutf8/src/utf8.c index 5f7419240a..42f61a9a00 100644 --- a/rpython/rlib/fastutf8/src/utf8.c +++ b/rpython/rlib/fastutf8/src/utf8.c @@ -11,6 +11,7 @@ int instruction_set = -1; #define ISET_AVX 0x2 #define ISET_AVX2 0x4 +#if __x86_64__ void detect_instructionset(void) { long eax; @@ -36,6 +37,12 @@ void detect_instructionset(void) instruction_set |= ISET_AVX2; } } +#else +void detect_instructionset(void) +{ + // do nothing on other architectures +} +#endif ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len) { -- GitLab From 78d2cca39e71d54513a99404ff4defa369395cf2 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Wed, 19 Feb 2020 17:04:33 -0300 Subject: [PATCH 04/10] simplified the code, it only count the number of code points that indicate a unicode char. This is based on the assumption that internally the strings a saved correctly and stored correctly as utf8 --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/src/utf8-avx.c | 168 ++------------------------ rpython/rlib/fastutf8/src/utf8-sse4.c | 167 ++----------------------- rpython/rlib/fastutf8/src/utf8.c | 8 +- rpython/rlib/fastutf8/src/utf8.h | 6 +- rpython/rlib/test/test_fastutf8.py | 4 +- 5 files changed, 28 insertions(+), 325 deletions(-) diff --git a/rpython/rlib/fastutf8/src/utf8-avx.c b/rpython/rlib/fastutf8/src/utf8-avx.c index 62761612e4..76042f0ec1 100644 --- a/rpython/rlib/fastutf8/src/utf8-avx.c +++ b/rpython/rlib/fastutf8/src/utf8-avx.c @@ -63,7 +63,7 @@ void _print_mm256x(const char * msg, __m256i chunk) ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len) { - const uint8_t * encoded = (const uint8_t*)utf8; + const char * encoded = utf8; __builtin_prefetch(encoded, 0, 0); size_t num_codepoints = 0; __m256i chunk; @@ -71,7 +71,6 @@ ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len) if (len == 0) { return 0; } - __m256i zero = _mm256_set1_epi8(0x00); while (len >= 32) { chunk = _mm256_loadu_si256((__m256i*)encoded); if (_mm256_movemask_epi8(chunk) == 0) { @@ -83,174 +82,23 @@ ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len) } __builtin_prefetch(encoded+32, 0, 0); - __m256i count = _mm256_set1_epi8(0x1); - //_print_mm256x("chunk", chunk); // fight against the fact that there is no comparison on unsigned values - __m256i chunk_signed = _mm256_add_epi8(chunk, _mm256_set1_epi8(0x80)); - //_print_mm256x("shunk", chunk_signed); - - // ERROR checking - // checking procedure works the following way: - // - // 1) mark all continuation bytes with either 0x1, 0x3, 0x7 (one, two or three bytes continuation) - // 2) then check that there is no byte that has an invalid continuation - __m256i twobytemarker = _mm256_cmpgt_epi8( chunk_signed, _mm256_set1_epi8(0xc0-1-0x80)); - __m256i threebytemarker = _mm256_cmpgt_epi8(chunk_signed, _mm256_set1_epi8(0xe0-1-0x80)); - __m256i fourbytemarker = _mm256_cmpgt_epi8( chunk_signed, _mm256_set1_epi8(0xf0-1-0x80)); - - // the general idea of the following code collects 0xff for each byte position - // in the variable contbytes. - // at the end check if each position in contbytes set to 0xff is a valid continuation byte - - // check that 0xc0 > 0xc2 - __m256i validtwobm = _mm256_cmpgt_epi8(chunk_signed, _mm256_set1_epi8(0xc2-1-0x80)); - if (_mm256_movemask_epi8(_mm256_xor_si256(validtwobm, twobytemarker)) != 0) { - // two byte marker should not be in range [0xc0-0xc2) - return -1; - } - - __m256i state2 = _mm256_andnot_si256(threebytemarker, twobytemarker); - // ERROR, this shift is flawed, it will cut of at the register boundary - __m256i contbytes = _mm256_slli_si256(_mm256_blendv_epi8(state2, _mm256_set1_epi8(0x1), twobytemarker), 1); - - if (_mm256_movemask_epi8(threebytemarker) != 0) { - // contains at least one 3 byte marker - __m256i istate3 = _mm256_andnot_si256(fourbytemarker, threebytemarker); - __m256i state3 = _mm256_slli_si256(_mm256_blendv_epi8(zero, _mm256_set1_epi8(0x3), istate3), 1); - state3 = _mm256_or_si256(state3, _mm256_slli_si256(state3, 1)); - - contbytes = _mm256_or_si256(contbytes, state3); - - // range check - __m256i equal_e0 = _mm256_cmpeq_epi8(_mm256_blendv_epi8(zero, chunk_signed, istate3), - _mm256_set1_epi8(0xe0-0x80)); - if (_mm256_movemask_epi8(equal_e0) != 0) { - __m256i mask = _mm256_blendv_epi8(_mm256_set1_epi8(0x7f), chunk_signed, _mm256_slli_si256(equal_e0, 1)); - __m256i check_surrogate = _mm256_cmpgt_epi8(_mm256_set1_epi8(0xa0-0x80), mask); // lt - if (_mm256_movemask_epi8(check_surrogate) != 0) { - // invalid surrograte character!!! - return -1; - } - } - - // verify that there are now surrogates - if (!ALLOW_SURROGATES) { - __m256i equal_ed = _mm256_cmpeq_epi8(_mm256_blendv_epi8(zero, chunk_signed, istate3), - _mm256_set1_epi8(0xed-0x80)); - if (_mm256_movemask_epi8(equal_ed) != 0) { - __m256i mask = _mm256_blendv_epi8(_mm256_set1_epi8(0x80), chunk_signed, _mm256_slli_si256(equal_ed, 1)); - __m256i check_surrogate = _mm256_cmpgt_epi8(mask, _mm256_set1_epi8(0xa0-1-0x80)); - if (_mm256_movemask_epi8(check_surrogate) != 0) { - // invalid surrograte character!!! - return -1; - } - } - } - } - - if (_mm256_movemask_epi8(fourbytemarker) != 0) { - // contain a 4 byte marker - __m256i istate4 = _mm256_slli_si256(_mm256_blendv_epi8(zero, _mm256_set1_epi8(0x7), fourbytemarker), 1); - __m256i state4 =_mm256_or_si256(istate4, _mm256_slli_si256(istate4, 1)); - state4 =_mm256_or_si256(state4, _mm256_slli_si256(istate4, 2)); - - contbytes = _mm256_or_si256(contbytes, state4); - - // range check, filter out f0 and - __m256i equal_f0 = _mm256_cmpeq_epi8(_mm256_blendv_epi8(zero, chunk_signed, fourbytemarker), - _mm256_set1_epi8(0xf0-0x80)); - if (_mm256_movemask_epi8(equal_f0) != 0) { - __m256i mask = _mm256_blendv_epi8(_mm256_set1_epi8(0x7f), chunk_signed, _mm256_slli_si256(equal_f0, 1)); - __m256i check_surrogate = _mm256_cmpgt_epi8(_mm256_set1_epi8(0x90-0x80), mask); - if (_mm256_movemask_epi8(check_surrogate) != 0) { - return -1; - } - } - - __m256i equal_f4 = _mm256_cmpeq_epi8(_mm256_blendv_epi8(zero, chunk_signed, fourbytemarker), - _mm256_set1_epi8(0xf4-0x80)); - if (_mm256_movemask_epi8(equal_f4) != 0) { - __m256i mask = _mm256_blendv_epi8(_mm256_set1_epi8(0x80), chunk_signed, _mm256_slli_si256(equal_f4, 1)); - __m256i check_surrogate = _mm256_cmpgt_epi8(mask, _mm256_set1_epi8(0x90-1-0x80)); - if (_mm256_movemask_epi8(check_surrogate) != 0) { - return -1; - } - } - - __m256i equal_f5_gt = _mm256_cmpgt_epi8(_mm256_blendv_epi8(zero, chunk_signed, fourbytemarker), - _mm256_set1_epi8(0xf4-0x80)); - if (_mm256_movemask_epi8(equal_f5_gt) != 0) { - return -1; - } - } - - // now check that contbytes and the actual byte values have a valid - // continuation at each position the marker indicates to have one - __m256i check_cont = _mm256_cmpgt_epi8(contbytes, zero); - __m256i contpos = _mm256_and_si256(_mm256_set1_epi8(0xc0), chunk); - contpos = _mm256_cmpeq_epi8(_mm256_set1_epi8(0x80), contpos); - __m256i validcont = _mm256_xor_si256(check_cont, contpos); - if (_mm256_movemask_epi8(validcont) != 0) { - // uff, nope, that is really not utf8 - return -1; - } - - // CORRECT, calculate the length - // copy 0x00 over to each place which is a continuation byte - count = _mm256_blendv_epi8(count, zero, contpos); - - // count the code points using 2x 32 bit hadd and one last 16 hadd - // the result will end up at the lowest position - count = _mm256_hadd_epi32(count, zero); - count = _mm256_hadd_epi32(count, zero); - count = _mm256_hadd_epi16(count, zero); - uint16_t c = _mm256_extract_epi16(count, 0); - uint16_t c2 = _mm256_extract_epi16(count, 8); - uint16_t points = (c & 0xff) + ((c >> 8) & 0xff) + (c2 & 0xff) + ((c2 >> 8) & 0xff); - - // these cases need to be handled: - // 16 byte boundary -> | <- 16 byte boundary - // -----------------------------------------+-------------------- - // 1) 2 byte code point. e.g. ... c2 | 80 ... - // 2) 3 byte code point. e.g. ... e6 | 80 80 ... - // 3) 3 byte code point. e.g. ... e6 80 | 80 ... - // 4) 4 byte code point. e.g. ... f2 | 80 80 80 ... - // 5) 4 byte code point. e.g. ... f2 80 | 80 80 ... - // 6) 4 byte code point. e.g. ... f2 80 80 | 80 ... - // - int mask_chunk = _mm256_movemask_epi8(chunk); - int mask_conti = _mm256_movemask_epi8(contpos); - - // little endian - int lenoff = 32; - int minus_codepoints = 0; - if (BIT(mask_chunk, 32) != 0 && BIT(mask_conti, 32) == 0) { // 1), 2), 4) - minus_codepoints = 1; - lenoff -= 1; - } else if (BIT(mask_chunk, 31) != 0 && BIT(mask_conti, 31) == 0 && - BIT(mask_conti, 32) == 1) { // 3), 5) - minus_codepoints = 1; - lenoff -= 2; - } else if (BIT(mask_chunk, 30) != 0 && BIT(mask_conti, 30) == 0 && - BIT(mask_conti, 31) == 1 && BIT(mask_conti, 32) == 1) { // 6) - minus_codepoints = 1; - lenoff -= 3; - } - - num_codepoints += points - minus_codepoints; - len -= lenoff; - encoded += lenoff; + __m256i count = _mm256_cmpgt_epi8(chunk, _mm256_set1_epi8(-0x41)); + unsigned int mask = (unsigned int)_mm256_movemask_epi8(count); + int bitcount = __builtin_popcount(mask); + len -= 32; + encoded += 32; + num_codepoints += bitcount; } if (len == 0) { return num_codepoints; } - ssize_t result = fu8_count_utf8_codepoints_seq((const char*)encoded, len); + ssize_t result = fu8_count_utf8_codepoints_seq(encoded, len); if (result == -1) { return -1; } return num_codepoints + result; - return -1; } diff --git a/rpython/rlib/fastutf8/src/utf8-sse4.c b/rpython/rlib/fastutf8/src/utf8-sse4.c index 26271b28a1..fffda5db9a 100644 --- a/rpython/rlib/fastutf8/src/utf8-sse4.c +++ b/rpython/rlib/fastutf8/src/utf8-sse4.c @@ -42,7 +42,7 @@ void _print_mmx(const char * msg, __m128i chunk) ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len) { - const uint8_t * encoded = (const uint8_t*)utf8; + const char * encoded = utf8; __builtin_prefetch(encoded, 0, 0); size_t num_codepoints = 0; __m128i chunk; @@ -50,8 +50,6 @@ ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len) if (len == 0) { return 0; } - __m128i zero = _mm_set1_epi8(0x00); - while (len >= 16) { chunk = _mm_loadu_si128((__m128i*)encoded); if (_mm_movemask_epi8(chunk) == 0) { @@ -63,167 +61,20 @@ ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len) } __builtin_prefetch(encoded+16, 0, 0); - __m128i count = _mm_set1_epi8(0x1); - //_print_mmx("chunk", chunk); - // fight against the fact that there is no comparison on unsigned values - __m128i chunk_signed = _mm_add_epi8(chunk, _mm_set1_epi8(0x80)); - //_print_mmx("shunk", chunk_signed); - - // ERROR checking - // checking procedure works the following way: - // - // 1) mark all continuation bytes with either 0x1, 0x3, 0x7 (one, two or three bytes continuation) - // 2) then check that there is no byte that has an invalid continuation - __m128i twobytemarker = _mm_cmplt_epi8(_mm_set1_epi8(0xc0-1-0x80), chunk_signed); - __m128i threebytemarker = _mm_cmplt_epi8(_mm_set1_epi8(0xe0-1-0x80), chunk_signed); - __m128i fourbytemarker = _mm_cmplt_epi8(_mm_set1_epi8(0xf0-1-0x80), chunk_signed); - - // the general idea of the following code collects 0xff for each byte position - // in the variable contbytes. - // at the end check if each position in contbytes set to 0xff is a valid continuation byte - - // check that 0xc0 > 0xc2 - __m128i validtwobm = _mm_cmplt_epi8(_mm_set1_epi8(0xc2-1-0x80), chunk_signed); - if (_mm_movemask_epi8(_mm_xor_si128(validtwobm, twobytemarker)) != 0) { - // two byte marker should not be in range [0xc0-0xc2) - return -1; - } - - __m128i state2 = _mm_andnot_si128(threebytemarker, twobytemarker); - __m128i contbytes = _mm_slli_si128(_mm_blendv_epi8(state2, _mm_set1_epi8(0x1), twobytemarker), 1); - - if (_mm_movemask_epi8(threebytemarker) != 0) { - // contains at least one 3 byte marker - __m128i istate3 = _mm_andnot_si128(fourbytemarker, threebytemarker); - __m128i state3 = _mm_slli_si128(_mm_blendv_epi8(zero, _mm_set1_epi8(0x3), istate3), 1); - state3 = _mm_or_si128(state3, _mm_slli_si128(state3, 1)); - - contbytes = _mm_or_si128(contbytes, state3); - - // range check - __m128i equal_e0 = _mm_cmpeq_epi8(_mm_blendv_epi8(zero, chunk_signed, istate3), - _mm_set1_epi8(0xe0-0x80)); - if (_mm_movemask_epi8(equal_e0) != 0) { - __m128i mask = _mm_blendv_epi8(_mm_set1_epi8(0x7f), chunk_signed, _mm_slli_si128(equal_e0, 1)); - __m128i check_surrogate = _mm_cmplt_epi8(mask, _mm_set1_epi8(0xa0-0x80)); - if (_mm_movemask_epi8(check_surrogate) != 0) { - // invalid surrograte character!!! - return -1; - } - } - - // verify that there are now surrogates - if (!ALLOW_SURROGATES) { - __m128i equal_ed = _mm_cmpeq_epi8(_mm_blendv_epi8(zero, chunk_signed, istate3), - _mm_set1_epi8(0xed-0x80)); - if (_mm_movemask_epi8(equal_ed) != 0) { - __m128i mask = _mm_blendv_epi8(_mm_set1_epi8(0x80), chunk_signed, _mm_slli_si128(equal_ed, 1)); - __m128i check_surrogate = _mm_cmpgt_epi8(mask, _mm_set1_epi8(0xa0-1-0x80)); - if (_mm_movemask_epi8(check_surrogate) != 0) { - // invalid surrograte character!!! - return -1; - } - } - } - } - - if (_mm_movemask_epi8(fourbytemarker) != 0) { - // contain a 4 byte marker - __m128i istate4 = _mm_slli_si128(_mm_blendv_epi8(zero, _mm_set1_epi8(0x7), fourbytemarker), 1); - __m128i state4 =_mm_or_si128(istate4, _mm_slli_si128(istate4, 1)); - state4 =_mm_or_si128(state4, _mm_slli_si128(istate4, 2)); - - contbytes = _mm_or_si128(contbytes, state4); - - // range check, filter out f0 and - __m128i equal_f0 = _mm_cmpeq_epi8(_mm_blendv_epi8(zero, chunk_signed, fourbytemarker), - _mm_set1_epi8(0xf0-0x80)); - if (_mm_movemask_epi8(equal_f0) != 0) { - __m128i mask = _mm_blendv_epi8(_mm_set1_epi8(0x7f), chunk_signed, _mm_slli_si128(equal_f0, 1)); - __m128i check_surrogate = _mm_cmplt_epi8(mask, _mm_set1_epi8(0x90-0x80)); - if (_mm_movemask_epi8(check_surrogate) != 0) { - return -1; - } - } - - __m128i equal_f4 = _mm_cmpeq_epi8(_mm_blendv_epi8(zero, chunk_signed, fourbytemarker), - _mm_set1_epi8(0xf4-0x80)); - if (_mm_movemask_epi8(equal_f4) != 0) { - __m128i mask = _mm_blendv_epi8(_mm_set1_epi8(0x80), chunk_signed, _mm_slli_si128(equal_f4, 1)); - __m128i check_surrogate = _mm_cmpgt_epi8(mask, _mm_set1_epi8(0x90-1-0x80)); - if (_mm_movemask_epi8(check_surrogate) != 0) { - return -1; - } - } - - __m128i equal_f5_gt = _mm_cmpgt_epi8(_mm_blendv_epi8(zero, chunk_signed, fourbytemarker), - _mm_set1_epi8(0xf4-0x80)); - if (_mm_movemask_epi8(equal_f5_gt) != 0) { - return -1; - } - } - - // now check that contbytes and the actual byte values have a valid - // continuation at each position the marker indicates to have one - __m128i check_cont = _mm_cmpgt_epi8(contbytes, zero); - __m128i contpos = _mm_and_si128(_mm_set1_epi8(0xc0), chunk); - contpos = _mm_cmpeq_epi8(_mm_set1_epi8(0x80), contpos); - __m128i validcont = _mm_xor_si128(check_cont, contpos); - if (_mm_movemask_epi8(validcont) != 0) { - // uff, nope, that is really not utf8 - return -1; - } - - // CORRECT, calculate the length - // copy 0x00 over to each place which is a continuation byte - count = _mm_blendv_epi8(count, zero, contpos); - - // count the code points using 2x 32 bit hadd and one last 16 hadd - // the result will end up at the lowest position - count = _mm_hadd_epi32(count, count); - count = _mm_hadd_epi32(count, count); - count = _mm_hadd_epi16(count, count); - uint16_t c = _mm_extract_epi16(count, 0); - - // these cases need to be handled: - // 16 byte boundary -> | <- 16 byte boundary - // -----------------------------------------+-------------------- - // 1) 2 byte code point. e.g. ... c2 | 80 ... - // 2) 3 byte code point. e.g. ... e6 | 80 80 ... - // 3) 3 byte code point. e.g. ... e6 80 | 80 ... - // 4) 4 byte code point. e.g. ... f2 | 80 80 80 ... - // 5) 4 byte code point. e.g. ... f2 80 | 80 80 ... - // 6) 4 byte code point. e.g. ... f2 80 80 | 80 ... - // - int mask_chunk = _mm_movemask_epi8(chunk); - int mask_conti = _mm_movemask_epi8(contpos); - - // little endian - int lenoff = 16; - int minus_codepoints = 0; - if (BIT(mask_chunk, 16) != 0 && BIT(mask_conti, 16) == 0) { // 1), 2), 4) - minus_codepoints = 1; - lenoff -= 1; - } else if (BIT(mask_chunk, 15) != 0 && BIT(mask_conti, 15) == 0 && - BIT(mask_conti, 16) == 1) { // 3), 5) - minus_codepoints = 1; - lenoff -= 2; - } else if (BIT(mask_chunk, 14) != 0 && BIT(mask_conti, 14) == 0 && - BIT(mask_conti, 15) == 1 && BIT(mask_conti, 16) == 1) { // 6) - minus_codepoints = 1; - lenoff -= 3; - } - - num_codepoints += (c & 0xff) + ((c >> 8) & 0xff) - minus_codepoints; - len -= lenoff; - encoded += lenoff; + // there is no comparison on unsigned values + __m128i count = _mm_cmplt_epi8(_mm_set1_epi8(-0x41), chunk); + int mask = _mm_movemask_epi8(count); + int bitcount = __builtin_popcount(mask); + len -= 16; + encoded += 16; + num_codepoints += bitcount; } if (len == 0) { return num_codepoints; } - ssize_t result = fu8_count_utf8_codepoints_seq((const char*)encoded, len); + ssize_t result = fu8_count_utf8_codepoints_seq(encoded, len); if (result == -1) { return -1; } diff --git a/rpython/rlib/fastutf8/src/utf8.c b/rpython/rlib/fastutf8/src/utf8.c index 42f61a9a00..962050533a 100644 --- a/rpython/rlib/fastutf8/src/utf8.c +++ b/rpython/rlib/fastutf8/src/utf8.c @@ -26,8 +26,10 @@ void detect_instructionset(void) "=d" (edx) : "a" (op)); + __builtin_cpu_init(); instruction_set = 0; if (ecx & (1<<19)) { // sse4.1 + printf("supports\n"); instruction_set |= ISET_SSE4; } if(__builtin_cpu_supports("avx")) { @@ -46,16 +48,16 @@ void detect_instructionset(void) ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len) { + // assumption: utf8 is always a correctly encoded, if not return any result. if (instruction_set == -1) { detect_instructionset(); } - /* AVX IS NOT WORKING YET. HAVE A LOOK AT THE LEFT SHIFT ISSUE if (len >= 32 && (instruction_set & ISET_AVX2) != 0) { // to the MOON! return fu8_count_utf8_codepoints_avx(utf8, len); - }*/ - if (len >= 16 && (instruction_set == ISET_SSE4) != 0) { + } + if (len >= 16 && (instruction_set & ISET_SSE4) != 0) { // speed!! return fu8_count_utf8_codepoints_sse4(utf8, len); } diff --git a/rpython/rlib/fastutf8/src/utf8.h b/rpython/rlib/fastutf8/src/utf8.h index 9ef8a02f4a..044842163b 100644 --- a/rpython/rlib/fastutf8/src/utf8.h +++ b/rpython/rlib/fastutf8/src/utf8.h @@ -19,8 +19,8 @@ #endif /** - * Returns -1 if the given string is not a valid utf8 encoded string. - * Otherwise returns the amount code point in the given string. + * Given valid utf8 encoded bytes, it returns the amount of code points. + * Returns any result if the bytes are not encoded correctly. * len: length in bytes (8-bit) * * The above documentation also applies for several vectorized implementations @@ -29,7 +29,6 @@ * fu8_count_utf8_codepoints dispatches amongst several * implementations (e.g. seq, SSE4, AVX) */ -// TODO rename (fu8 prefix) RPY_EXTERN ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len); RPY_EXTERN ssize_t fu8_count_utf8_codepoints_seq(const char * utf8, size_t len); RPY_EXTERN ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len); @@ -39,6 +38,7 @@ RPY_EXTERN ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len); struct fu8_idxtab; /** + * DO NOT USE! NOT READY * Looks up the byte position of the utf8 code point at the index. * Assumptions: * diff --git a/rpython/rlib/test/test_fastutf8.py b/rpython/rlib/test/test_fastutf8.py index e28a281987..ffb58b928e 100644 --- a/rpython/rlib/test/test_fastutf8.py +++ b/rpython/rlib/test/test_fastutf8.py @@ -11,10 +11,12 @@ def _check_size(bytes, size): assert fu8.count_utf8_codepoints(ptr, len(bytes)) == size rffi.free_charp(ptr) -@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')), min_size=16)) +@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')), min_size=15)) def test_unichr_as_utf8(chars): print(chars) _check_size(chars.encode('utf-8'), len(chars)) + chars_double = chars+chars + _check_size(chars_double.encode('utf-8'), len(chars_double)) def test_avx_crash(): text = u'AAAAAAAAAAAAA\xa6\xa6AAAAAAAAAAAAAAAAA' -- GitLab From a973b1e65150587ff31b010f9fd886da23d4d151 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Wed, 19 Feb 2020 17:16:46 -0300 Subject: [PATCH 05/10] extend the categories that hypothesis draws the unicodes from --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/src/utf8.c | 1 - rpython/rlib/test/test_fastutf8.py | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/rpython/rlib/fastutf8/src/utf8.c b/rpython/rlib/fastutf8/src/utf8.c index 962050533a..a2622ea9a3 100644 --- a/rpython/rlib/fastutf8/src/utf8.c +++ b/rpython/rlib/fastutf8/src/utf8.c @@ -29,7 +29,6 @@ void detect_instructionset(void) __builtin_cpu_init(); instruction_set = 0; if (ecx & (1<<19)) { // sse4.1 - printf("supports\n"); instruction_set |= ISET_SSE4; } if(__builtin_cpu_supports("avx")) { diff --git a/rpython/rlib/test/test_fastutf8.py b/rpython/rlib/test/test_fastutf8.py index ffb58b928e..0d13443e2a 100644 --- a/rpython/rlib/test/test_fastutf8.py +++ b/rpython/rlib/test/test_fastutf8.py @@ -11,12 +11,14 @@ def _check_size(bytes, size): assert fu8.count_utf8_codepoints(ptr, len(bytes)) == size rffi.free_charp(ptr) -@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')), min_size=15)) -def test_unichr_as_utf8(chars): - print(chars) +@given(st.text(alphabet=st.characters(whitelist_categories=('Lu','Lo','So')), min_size=15, max_size=31)) +def test_unichr_as_utf8_sse4(chars): + _check_size(chars.encode('utf-8'), len(chars)) + +SOME = ('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Mn', 'Mc', 'Me', 'Nd', 'Nl', 'No', 'So',) +@given(st.text(alphabet=st.characters(whitelist_categories=SOME), min_size=31)) +def test_unichr_as_utf8_avx(chars): _check_size(chars.encode('utf-8'), len(chars)) - chars_double = chars+chars - _check_size(chars_double.encode('utf-8'), len(chars_double)) def test_avx_crash(): text = u'AAAAAAAAAAAAA\xa6\xa6AAAAAAAAAAAAAAAAA' -- GitLab From bae977a0888ef4f40e608b8ad38be854b13e8f52 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Wed, 19 Feb 2020 17:35:48 -0300 Subject: [PATCH 06/10] use rffi.ptradd instead of + --HG-- branch : rutf8-simd --- rpython/rlib/rutf8.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py index 15ed1a0bbd..3b3a8e83a4 100644 --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -476,7 +476,7 @@ def codepoints_in_utf8(value, start=0, end=sys.maxint): end = len(value) assert 0 <= start <= end ptr = rffi.get_raw_address_of_string(value) - length = fu8.count_utf8_codepoints(ptr+start, end-start) + length = fu8.count_utf8_codepoints(rffi.ptradd(ptr, start), end-start) return length @jit.elidable -- GitLab From 10319bf8a7f14f6a2b44238ceb5e800e48e586d7 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Thu, 20 Feb 2020 15:12:18 -0300 Subject: [PATCH 07/10] add assert that could not be determined by the translation step, the amount of codepoints always need to be 0 or positive --HG-- branch : rutf8-simd --- rpython/rlib/rutf8.py | 1 + 1 file changed, 1 insertion(+) diff --git a/rpython/rlib/rutf8.py b/rpython/rlib/rutf8.py index 3b3a8e83a4..c695a6b0ae 100644 --- a/rpython/rlib/rutf8.py +++ b/rpython/rlib/rutf8.py @@ -477,6 +477,7 @@ def codepoints_in_utf8(value, start=0, end=sys.maxint): assert 0 <= start <= end ptr = rffi.get_raw_address_of_string(value) length = fu8.count_utf8_codepoints(rffi.ptradd(ptr, start), end-start) + assert length >= 0 # needed for translation. return length @jit.elidable -- GitLab From 17a9144fb4cc994a1277597f5a0a0342f3ee357f Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Thu, 27 Feb 2020 15:59:26 -0300 Subject: [PATCH 08/10] removed RPYTHON_LL2CTYPES from the command line of the compiler, linking of pypy-c.so fails --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/capi.py | 2 +- rpython/rlib/fastutf8/src/utf8.c | 3 ++- rpython/rlib/fastutf8/src/utf8.h | 8 +++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/rpython/rlib/fastutf8/capi.py b/rpython/rlib/fastutf8/capi.py index cec4229844..f5849fd1ac 100644 --- a/rpython/rlib/fastutf8/capi.py +++ b/rpython/rlib/fastutf8/capi.py @@ -23,7 +23,7 @@ IDXTAB.become(rffi.CStruct("fu8_idxtab", IDXTABP = lltype.Ptr(IDXTAB) def setup(): - compile_extra = ['-DRPYTHON_LL2CTYPES', '-DALLOW_SURROGATES=0', '-fPIC'] + compile_extra = ['-fPIC'] eci_kwds = dict( include_dirs = [SRC], includes = ['utf8.h'], diff --git a/rpython/rlib/fastutf8/src/utf8.c b/rpython/rlib/fastutf8/src/utf8.c index a2622ea9a3..c85e899bf5 100644 --- a/rpython/rlib/fastutf8/src/utf8.c +++ b/rpython/rlib/fastutf8/src/utf8.c @@ -1,5 +1,6 @@ -#include "utf8.h" +#define _GNU_SOURCE 1 +#include "utf8.h" #include #include diff --git a/rpython/rlib/fastutf8/src/utf8.h b/rpython/rlib/fastutf8/src/utf8.h index 044842163b..2e9760b593 100644 --- a/rpython/rlib/fastutf8/src/utf8.h +++ b/rpython/rlib/fastutf8/src/utf8.h @@ -4,20 +4,18 @@ #include #include -#ifdef RPYTHON_LL2CTYPES - /* only for testing: ll2ctypes sets RPY_EXTERN from the command-line */ #ifndef RPY_EXTERN # define RPY_EXTERN RPY_EXPORTED #endif #ifdef _WIN32 -# define RPY_EXPORTED __declspec(dllexport) +#ifndef RPY_EXPORTED +#define RPY_EXPORTED __declspec(dllexport) +#endif #else # define RPY_EXPORTED extern __attribute__((visibility("default"))) #endif -#endif - /** * Given valid utf8 encoded bytes, it returns the amount of code points. * Returns any result if the bytes are not encoded correctly. -- GitLab From 9c285b17ff6c48bcf32d6c0fca5aeae701cab239 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Fri, 28 Feb 2020 07:39:50 -0300 Subject: [PATCH 09/10] removed C code that was intended to index unicode bytes. Has already been implemented in rpython. --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/capi.py | 5 - rpython/rlib/fastutf8/src/utf8-scalar.c | 17 --- rpython/rlib/fastutf8/src/utf8-sse4.c | 7 - rpython/rlib/fastutf8/src/utf8.c | 182 ------------------------ rpython/rlib/fastutf8/src/utf8.h | 30 ---- 5 files changed, 241 deletions(-) diff --git a/rpython/rlib/fastutf8/capi.py b/rpython/rlib/fastutf8/capi.py index f5849fd1ac..fe1a9c5daf 100644 --- a/rpython/rlib/fastutf8/capi.py +++ b/rpython/rlib/fastutf8/capi.py @@ -47,11 +47,6 @@ def setup(): [rffi.CCHARP, rffi.SSIZE_T], rffi.SSIZE_T, compilation_info=eci, _nowrapper=True) - index2byteposition = rffi.llexternal("fu8_idx2bytepos", - [rffi.SIZE_T, rffi.CCHARP, rffi.SIZE_T, IDXTABP], - rffi.SSIZE_T, compilation_info=eci, - _nowrapper=True) - return CInterface(locals()) diff --git a/rpython/rlib/fastutf8/src/utf8-scalar.c b/rpython/rlib/fastutf8/src/utf8-scalar.c index c41000a6c7..7ef3382ea3 100644 --- a/rpython/rlib/fastutf8/src/utf8-scalar.c +++ b/rpython/rlib/fastutf8/src/utf8-scalar.c @@ -1,22 +1,5 @@ #include "utf8.h" -int _check_continuation(const uint8_t ** encoded, const uint8_t * endptr, int count) { - ssize_t size = endptr - *encoded; - - if (size < count) { - // not enough bytes to be a valid 2 byte utf8 code point - return -1; - } - for (int i = 0; i < count; i++) { - uint8_t byte = *(*encoded)++; - if ((byte & 0xc0) != 0x80) { - // continuation byte does NOT match 0x10xxxxxx - return -1; - } - } - return 0; -} - ssize_t fu8_count_utf8_codepoints_seq(const char * utf8, size_t len) { size_t num_codepoints = 0; const char * encoded = utf8; diff --git a/rpython/rlib/fastutf8/src/utf8-sse4.c b/rpython/rlib/fastutf8/src/utf8-sse4.c index fffda5db9a..02c4fac874 100644 --- a/rpython/rlib/fastutf8/src/utf8-sse4.c +++ b/rpython/rlib/fastutf8/src/utf8-sse4.c @@ -81,10 +81,3 @@ ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len) return num_codepoints + result; } - -ssize_t fu8_idx2bytepos_sse4(size_t index, - const uint8_t * utf8, size_t len, - struct fu8_idxtab * t) -{ - return 0; -} diff --git a/rpython/rlib/fastutf8/src/utf8.c b/rpython/rlib/fastutf8/src/utf8.c index c85e899bf5..ff37ef2158 100644 --- a/rpython/rlib/fastutf8/src/utf8.c +++ b/rpython/rlib/fastutf8/src/utf8.c @@ -66,185 +66,3 @@ ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len) return fu8_count_utf8_codepoints_seq(utf8, len); } -typedef struct fu8_idxtab { - int character_step; - size_t * byte_positions; - size_t bytepos_table_length; -} fu8_idxtab_t; - -#include - -fu8_idxtab_t * _fu8_alloc_idxtab(int cp_count, int character_step) -{ - if (cp_count <= character_step) { - return NULL; - } - long s = (cp_count/character_step) * sizeof(size_t); - char * c = calloc(1, sizeof(fu8_idxtab_t)+s); - fu8_idxtab_t * i = (fu8_idxtab_t*)c; - i->character_step = character_step; - i->byte_positions = (size_t*)(c + sizeof(fu8_idxtab_t)); - i->bytepos_table_length = cp_count/character_step; - return i; -} - -void fu8_free_idxtab(struct fu8_idxtab * t) -{ - // why manage this in C? - // it might at some point have a different data structure, - // then we can handle this easily here without modifying the API - free(t); t = NULL; -} - -void _fu8_itab_set_bucket(struct fu8_idxtab * tab, int bucket, size_t off, size_t cpidx) -{ - size_t oldval = tab->byte_positions[bucket]; - if (oldval != 0) { - assert(oldval != off && "table mismatch"); - } - assert(bucket >= 0 && bucket < tab->bytepos_table_length && "index out of bounds"); - tab->byte_positions[bucket] = off; -} - -ssize_t _fu8_build_idxtab(size_t cpidx, size_t cpidx_off, size_t cplen, - const uint8_t * utf8, size_t bytelen, size_t byteoff, - struct fu8_idxtab ** tab) { - size_t code_point_index = cpidx_off; - const uint8_t * utf8_start_position = utf8 + byteoff; - const uint8_t * utf8_end_position = utf8 + bytelen - byteoff; - - struct fu8_idxtab * itab = tab[0]; - if (itab == NULL) { - tab[0] = itab = _fu8_alloc_idxtab(cplen, 1000); - } - - int bucket_step = -1; - int bucket = -1; - if (itab) { - bucket_step = itab->character_step; - bucket = cpidx_off / bucket_step; - //printf("bucket %d step %d iindex_off %ld\n", bucket, bucket_step, cpidx_off); - } - - while (utf8 < utf8_end_position) { - //printf("%d %llx ok\n", code_point_index, utf8); - if (code_point_index == cpidx) { - //printf("return %llx %llx %llx\n", utf8_start_position, utf8, utf8_end_position); - return utf8 - utf8_start_position; - } - - if (bucket_step != -1 && code_point_index != 0 && (code_point_index % bucket_step) == 0) { - _fu8_itab_set_bucket(itab, bucket++, byteoff + utf8 - utf8_start_position, code_point_index); - } - - uint8_t c = *utf8++; - //printf("%x\n", c); - code_point_index += 1; - if ((c & 0xc0) == 0) { - continue; - } - if ((c & 0xe0) == 0xc0) { - utf8 += 1; - continue; - } - if ((c & 0xf0) == 0xe0) { - utf8 += 2; - continue; - } - if ((c & 0xf8) == 0xf0) { - utf8 += 3; - continue; - } - } - - return -1; // out of bounds!! -} - -size_t _fu8_idxtab_lookup_bytepos_i(struct fu8_idxtab * tab, size_t cpidx); - -ssize_t _fu8_idx2bytepos(size_t index, - const uint8_t * utf8, size_t bytelen, size_t cplen, - struct fu8_idxtab ** tab) -{ - - assert(index != 0 && "index must not be 0"); - // note that itab STILL can be NULL - return 0; -} - -size_t _fu8_idxtab_lookup_bytepos_i(struct fu8_idxtab * tab, size_t cpidx) -{ - if (cpidx == 0 || tab == NULL) { - return 0; - } - int step = tab->character_step; - int tidx = cpidx / step; - size_t val = tab->byte_positions[tidx]; - while (tidx > 0) { - if (val != 0) { - //printf("%llx at %d %d/%d\n", val, tidx, cpidx, step); - return val; - } - tidx--; - val = tab->byte_positions[tidx]; - } - // no clue, start at the beginning! - return 0; - - //int lp, rp; // left position, right position - //int mp; // middle position - //int count; - //lp = 0; - //rp = 16; - - //if (cpidx == 0) { - // return -1; - //} - - //size_t valid_left = -1; - - //do { - // count = (rp - lp); - // mp = lp + count / 2; - - // size_t lval = tab->codepoint_positions[lp]; - // size_t mval = tab->codepoint_positions[mp]; - // size_t rval = tab->codepoint_positions[rp]; - // printf("l %d m %d r %d\nlv %d mv %d rv %d\n", lp, mp, rp, lval, mval, rval); - // if (lval != 0 && lval <= cpidx) { - // valid_left = lp; - // } else if (lval == 0) { - // // nothing is known about the left most value - // break; - // } - - // if (mval == cpidx) { - // return mp; - // } - - // if (mval == 0 || mval < cpidx) { - // // nothing is known about the middle value, - // // or mval is smaller the searched code point index - // rp = mp; - // continue; - // } else { - // lp = mp; - // continue; - // } - - //} while (count > 1); - - //return valid_left; -} - -ssize_t fu8_idx2bytepos(size_t index, - const uint8_t * utf8, size_t bytelen, - size_t cplen, - struct fu8_idxtab ** tab) -{ - if (index == 0) { return 0; } - if (index >= cplen) { return -1; } - size_t off = _fu8_idxtab_lookup_bytepos_i(tab[0], index); - //printf("found %llx\n", off); - return _fu8_build_idxtab(index, 0, cplen, utf8, bytelen, 0, tab); -} diff --git a/rpython/rlib/fastutf8/src/utf8.h b/rpython/rlib/fastutf8/src/utf8.h index 2e9760b593..b2a7ed7dc8 100644 --- a/rpython/rlib/fastutf8/src/utf8.h +++ b/rpython/rlib/fastutf8/src/utf8.h @@ -31,33 +31,3 @@ RPY_EXTERN ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len); RPY_EXTERN ssize_t fu8_count_utf8_codepoints_seq(const char * utf8, size_t len); RPY_EXTERN ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len); RPY_EXTERN ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len); - - -struct fu8_idxtab; - -/** - * DO NOT USE! NOT READY - * Looks up the byte position of the utf8 code point at the index. - * Assumptions: - * - * * utf8 parameter is utf8 encoded, otherwise the result is undefined. - * * passing one struct fu8_idxtab instance to several different utf8 strings - * yields undefined behaviour - * - * Return values: - * - * -1, if the index is out of bounds of utf8 - * X, where X >= 0. X is the byte postion for the code point at index - * - * If table is not NULL, this routine builds up a lookup - * table to speed up indexing. - * - */ -RPY_EXTERN ssize_t fu8_idx2bytepos(size_t index, - const uint8_t * utf8, size_t bytelen, - size_t cplen, - struct fu8_idxtab ** tab); -RPY_EXTERN void fu8_free_idxtab(struct fu8_idxtab * t); -RPY_EXTERN ssize_t fu8_idx2bytepso_sse4(size_t index, - const uint8_t * utf8, size_t len, - struct fu8_idxtab ** t); -- GitLab From 3404ee575e1ba0a524901d01f0c65c1f0fec8053 Mon Sep 17 00:00:00 2001 From: Richard Plangger Date: Fri, 28 Feb 2020 08:15:27 -0300 Subject: [PATCH 10/10] some header definitions do not need to be exported as RPY_EXTERN, do only enable the compilation fox x86_64 platforms --HG-- branch : rutf8-simd --- rpython/rlib/fastutf8/capi.py | 26 +++++++++++++++----------- rpython/rlib/fastutf8/src/utf8.h | 6 +++--- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/rpython/rlib/fastutf8/capi.py b/rpython/rlib/fastutf8/capi.py index fe1a9c5daf..97c66e690d 100644 --- a/rpython/rlib/fastutf8/capi.py +++ b/rpython/rlib/fastutf8/capi.py @@ -4,7 +4,8 @@ from rpython.tool.version import rpythonroot from rpython.rtyper.lltypesystem import lltype, rffi from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.rtyper.tool import rffi_platform as platform -from rpython.translator.platform import platform as trans_plaform +from rpython.translator.platform import platform as trans_platform +from rpython.translator.platform import is_host_build ROOT = py.path.local(rpythonroot).join('rpython', 'rlib', 'fastutf8') SRC = ROOT.join('src') @@ -23,24 +24,27 @@ IDXTAB.become(rffi.CStruct("fu8_idxtab", IDXTABP = lltype.Ptr(IDXTAB) def setup(): + import platform as pyplatform compile_extra = ['-fPIC'] eci_kwds = dict( include_dirs = [SRC], includes = ['utf8.h'], libraries = _libs, compile_extra = compile_extra) - # compile the SSE4.1 and AVX version - compile_extra.append('-msse4.1') - ofile_eci = ExternalCompilationInfo(**eci_kwds) - sse4_o, = trans_plaform._compile_o_files([SRC.join('utf8-sse4.c')], ofile_eci) - compile_extra.pop() - compile_extra.append('-mavx2') - ofile_eci = ExternalCompilationInfo(**eci_kwds) - avx_o, = trans_plaform._compile_o_files([SRC.join('utf8-avx.c')], ofile_eci) - del ofile_eci + if is_host_build() and pyplatform.processor() == 'x86_64': + # compile the SSE4.1 and AVX version + compile_extra.append('-msse4.1') + ofile_eci = ExternalCompilationInfo(**eci_kwds) + sse4_o, = trans_platform._compile_o_files([SRC.join('utf8-sse4.c')], ofile_eci) + compile_extra.pop() + compile_extra.append('-mavx2') + ofile_eci = ExternalCompilationInfo(**eci_kwds) + avx_o, = trans_platform._compile_o_files([SRC.join('utf8-avx.c')], ofile_eci) + compile_extra.pop() + del ofile_eci + eci_kwds['link_files'] = [sse4_o.strpath, avx_o.strpath] eci_kwds['separate_module_files'] = [SRC.join('utf8.c')] - eci_kwds['link_files'] = [sse4_o.strpath, avx_o.strpath] eci = ExternalCompilationInfo(**eci_kwds) platform.verify_eci(eci) count_utf8_codepoints = rffi.llexternal("fu8_count_utf8_codepoints", diff --git a/rpython/rlib/fastutf8/src/utf8.h b/rpython/rlib/fastutf8/src/utf8.h index b2a7ed7dc8..78177ac84b 100644 --- a/rpython/rlib/fastutf8/src/utf8.h +++ b/rpython/rlib/fastutf8/src/utf8.h @@ -28,6 +28,6 @@ * implementations (e.g. seq, SSE4, AVX) */ RPY_EXTERN ssize_t fu8_count_utf8_codepoints(const char * utf8, size_t len); -RPY_EXTERN ssize_t fu8_count_utf8_codepoints_seq(const char * utf8, size_t len); -RPY_EXTERN ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len); -RPY_EXTERN ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len); +ssize_t fu8_count_utf8_codepoints_seq(const char * utf8, size_t len); +ssize_t fu8_count_utf8_codepoints_sse4(const char * utf8, size_t len); +ssize_t fu8_count_utf8_codepoints_avx(const char * utf8, size_t len); -- GitLab