diff options
author | Karl Williamson <khw@cpan.org> | 2021-07-20 10:07:42 -0600 |
---|---|---|
committer | Karl Williamson <khw@cpan.org> | 2021-07-30 05:41:28 -0600 |
commit | fc1bb663cac6846450ceb387751b491f2ecef13e (patch) | |
tree | 978076ea241438fae617926ed8a9d29526f1655f /inline.h | |
parent | 330cd0ce7cbde4175b21369c749c6a3186d2ac77 (diff) | |
download | perl-fc1bb663cac6846450ceb387751b491f2ecef13e.tar.gz |
Use clz, ctz for msb_pos, lsb_pos, if available
On many modern platforms these functions can be replaced by a single
machine instruction or two. This commit looks for this possibility and
uses it if possible.
Diffstat (limited to 'inline.h')
-rw-r--r-- | inline.h | 111 |
1 files changed, 109 insertions, 2 deletions
@@ -664,6 +664,59 @@ Perl_is_utf8_invariant_string_loc(const U8* const s, STRLEN len, const U8 ** ep) return TRUE; } +/* See if the platform has builtins for finding the most/least significant bit, + * and which one is right for using on 32 and 64 bit operands */ +#if (__has_builtin(__builtin_clz) || PERL_GCC_VERSION_GE(3,4,0)) +# if U32SIZE == INTSIZE +# define PERL_CLZ_32 __builtin_clz +# endif +# if defined(U64TYPE) && U64SIZE == INTSIZE +# define PERL_CLZ_64 __builtin_clz +# endif +#endif +#if (__has_builtin(__builtin_ctz) || PERL_GCC_VERSION_GE(3,4,0)) +# if U32SIZE == INTSIZE +# define PERL_CTZ_32 __builtin_ctz +# endif +# if defined(U64TYPE) && U64SIZE == INTSIZE +# define PERL_CTZ_64 __builtin_ctz +# endif +#endif + +#if (__has_builtin(__builtin_clzl) || PERL_GCC_VERSION_GE(3,4,0)) +# if U32SIZE == LONGSIZE && ! defined(PERL_CLZ_32) +# define PERL_CLZ_32 __builtin_clzl +# endif +# if defined(U64TYPE) && U64SIZE == LONGSIZE && ! defined(PERL_CLZ_64) +# define PERL_CLZ_64 __builtin_clzl +# endif +#endif +#if (__has_builtin(__builtin_ctzl) || PERL_GCC_VERSION_GE(3,4,0)) +# if U32SIZE == LONGSIZE && ! defined(PERL_CTZ_32) +# define PERL_CTZ_32 __builtin_ctzl +# endif +# if defined(U64TYPE) && U64SIZE == LONGSIZE && ! defined(PERL_CTZ_64) +# define PERL_CTZ_64 __builtin_ctzl +# endif +#endif + +#if (__has_builtin(__builtin_clzll) || PERL_GCC_VERSION_GE(3,4,0)) +# if U32SIZE == LONGLONGSIZE && ! defined(PERL_CLZ_32) +# define PERL_CLZ_32 __builtin_clzll +# endif +# if defined(U64TYPE) && U64SIZE == LONGLONGSIZE && ! defined(PERL_CLZ_64) +# define PERL_CLZ_64 __builtin_clzll +# endif +#endif +#if (__has_builtin(__builtin_ctzll) || PERL_GCC_VERSION_GE(3,4,0)) +# if U32SIZE == LONGLONGSIZE && ! defined(PERL_CTZ_32) +# define PERL_CTZ_32 __builtin_ctzll +# endif +# if defined(U64TYPE) && U64SIZE == LONGLONGSIZE && ! defined(PERL_CTZ_64) +# define PERL_CTZ_64 __builtin_ctzll +# endif +#endif + /* Below are functions to find the first, last, or only set bit in a word. On * platforms with 64-bit capability, there is a pair for each operation; the * first taking a 64 bit operand, and the second a 32 bit one. The logic is @@ -679,7 +732,20 @@ Perl_lsbit_pos64(U64 word) ASSUME(word != 0); - /* Isolate the lsb; + /* If we can determine that the platform has a usable fast method to get + * this info, use that */ + +# if defined(PERL_CTZ_64) + + return (unsigned) PERL_CTZ_64(word); + +# else + + /* Here, we didn't find a fast method for finding the lsb. Fall back to + * making the lsb the only set bit in the word, and use our function that + * works on words with a single bit set. + * + * Isolate the lsb; * https://stackoverflow.com/questions/757059/position-of-least-significant-bit-that-is-set * * The word will look like this, with a rightmost set bit in position 's': @@ -695,6 +761,9 @@ Perl_lsbit_pos64(U64 word) * complain about negating an unsigned.) */ return single_1bit_pos64(word & (~word + 1)); + +# endif + } # define lsbit_pos_uintmax_(word) lsbit_pos64(word) @@ -710,9 +779,22 @@ Perl_lsbit_pos32(U32 word) ASSUME(word != 0); +#if defined(PERL_CTZ_32) + + return (unsigned) PERL_CTZ_32(word); + +#else + return single_1bit_pos32(word & (~word + 1)); + +#endif + } +/* Convert the leading zeros count to the bit position of the first set bit. + * This just subtracts from the highest position, 31 or 63 */ +#define LZC_TO_MSBIT_POS_(size, lzc) ((size##SIZE * CHARBITS - 1) - (lzc)) + #ifdef U64TYPE /* HAS_QUAD not usable outside the core */ PERL_STATIC_INLINE unsigned @@ -723,7 +805,20 @@ Perl_msbit_pos64(U64 word) ASSUME(word != 0); - /* Isolate the msb; http://codeforces.com/blog/entry/10330 + /* If we can determine that the platform has a usable fast method to get + * this, use that */ + +# if defined(PERL_CLZ_64) + + return (unsigned) LZC_TO_MSBIT_POS_(U64, PERL_CLZ_64(word)); + +# else + + /* Here, we didn't find a fast method for finding the msb. Fall back to + * making the msb the only set bit in the word, and use our function that + * works on words with a single bit set. + * + * 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. @@ -742,6 +837,9 @@ Perl_msbit_pos64(U64 word) /* Now we have a single bit set */ return single_1bit_pos64(word); + +# endif + } # define msbit_pos_uintmax_(word) msbit_pos64(word) @@ -757,6 +855,12 @@ Perl_msbit_pos32(U32 word) ASSUME(word != 0); +#if defined(PERL_CLZ_32) + + return (unsigned) LZC_TO_MSBIT_POS_(U32, PERL_CLZ_32(word)); + +#else + word |= (word >> 1); word |= (word >> 2); word |= (word >> 4); @@ -764,6 +868,9 @@ Perl_msbit_pos32(U32 word) word |= (word >> 16); word -= (word >> 1); return single_1bit_pos32(word); + +#endif + } #ifdef U64TYPE /* HAS_QUAD not usable outside the core */ |