diff --git a/embed.fnc b/embed.fnc index 660bfb7eb7f6..3f13229001e8 100644 --- a/embed.fnc +++ b/embed.fnc @@ -1140,6 +1140,7 @@ ATidRp |bool |is_utf8_invariant_string_loc|NN const U8* const s \ |STRLEN len \ |NULLOK const U8 ** ep CTiRp |unsigned|my_ffs|PERL_UINTMAX_T word +CTiRp |unsigned|my_msbit_pos|PERL_UINTMAX_T word CTiRp |unsigned|single_1bit_pos|PERL_UINTMAX_T word #ifndef EBCDIC CTiRp |unsigned int|variant_byte_number|PERL_UINTMAX_T word diff --git a/embed.h b/embed.h index 1488b4f08382..b6fb09f3cd31 100644 --- a/embed.h +++ b/embed.h @@ -331,6 +331,7 @@ #define my_fflush_all() Perl_my_fflush_all(aTHX) #define my_ffs Perl_my_ffs #define my_fork Perl_my_fork +#define my_msbit_pos Perl_my_msbit_pos #define my_popen_list(a,b,c) Perl_my_popen_list(aTHX_ a,b,c) #define my_setenv(a,b) Perl_my_setenv(aTHX_ a,b) #define my_socketpair Perl_my_socketpair diff --git a/inline.h b/inline.h index 17ff6954874c..2a4671d986fe 100644 --- a/inline.h +++ b/inline.h @@ -598,6 +598,40 @@ Perl_single_1bit_pos(PERL_UINTMAX_T word) >> PERL_deBruijnShift_]; } +PERL_STATIC_INLINE unsigned +Perl_my_msbit_pos(PERL_UINTMAX_T word) +{ + /* Find the position (0..63) of the most significant set bit in the input + * word */ + + ASSUME(word != 0); + + /* Isolate the msb; http://codeforces.com/blog/entry/10330 + * + * Only the most significant set bit matters. Or'ing word with its right + * shift of 1 makes that bit and the next one to its right both 1. + * Repeating that with the right shift of 2 makes for 4 1-bits in a row. + * ... We end with the msb and all to the right being 1. */ + word |= (word >> 1); + word |= (word >> 2); + word |= (word >> 4); + word |= (word >> 8); + word |= (word >> 16); + +# if PERL_UINTMAX_SIZE > 4 + + word |= (word >> 32); + +# endif + + /* Then subtracting the right shift by 1 clears all but the left-most of + * the 1 bits, which is our desired result */ + word -= (word >> 1); + + /* Now we have a single bit set */ + return single_1bit_pos(word); +} + PERL_STATIC_INLINE unsigned Perl_my_ffs(PERL_UINTMAX_T word) { @@ -658,34 +692,9 @@ Perl_variant_byte_number(PERL_UINTMAX_T word) /* Bytes are stored like * Byte1 Byte2 ... Byte8 * 63..56 55..47 ... 7...0 - * - * Isolate the msb; http://codeforces.com/blog/entry/10330 - * - * Only the most significant set bit matters. Or'ing word with its right - * shift of 1 makes that bit and the next one to its right both 1. Then - * right shifting by 2 makes for 4 1-bits in a row. ... We end with the - * msb and all to the right being 1. */ - word |= word >> 1; - word |= word >> 2; - word |= word >> 4; - word |= word >> 8; - word |= word >> 16; - word |= word >> 32; /* This should get optimized out on 32-bit systems. */ - - /* Then subtracting the right shift by 1 clears all but the left-most of - * the 1 bits, which is our desired result */ - word -= (word >> 1); - - /* Here 'word' has a single bit set: the msb of the first byte in which it - * is set. Calculate that position in the word. We can use this - * specialized solution: https://stackoverflow.com/a/32339674/1626653, - * assumes an 8-bit byte. (On a 32-bit machine, the larger numbers should - * just get shifted off at compile time) */ - word = (word >> 7) * ((UINTMAX_C( 7) << 56) | (UINTMAX_C(15) << 48) - | (UINTMAX_C(23) << 40) | (UINTMAX_C(31) << 32) - | (39 << 24) | (47 << 16) - | (55 << 8) | (63 << 0)); - word >>= PERL_WORDSIZE * 7; /* >> by either 56 or 24 */ + * so getting the msb of the whole modified word is getting the msb of the + * first byte that has its msb set */ + word = my_msbit_pos(word); /* Here, word contains the position 63,55,...,23,15,7 of that bit. Convert * to 0..7 */ diff --git a/proto.h b/proto.h index 72824bf89086..c43d148d8586 100644 --- a/proto.h +++ b/proto.h @@ -2188,6 +2188,12 @@ PERL_CALLCONV int Perl_my_mkstemp_cloexec(char *templte) #define PERL_ARGS_ASSERT_MY_MKSTEMP_CLOEXEC \ assert(templte) +#ifndef PERL_NO_INLINE_FUNCTIONS +PERL_STATIC_INLINE unsigned Perl_my_msbit_pos(PERL_UINTMAX_T word) + __attribute__warn_unused_result__; +#define PERL_ARGS_ASSERT_MY_MSBIT_POS +#endif + PERL_CALLCONV PerlIO* Perl_my_popen_list(pTHX_ const char* mode, int n, SV ** args); #define PERL_ARGS_ASSERT_MY_POPEN_LIST \ assert(mode); assert(args)