Skip to content

Commit

Permalink
Create and use my_msbit_pos()
Browse files Browse the repository at this point in the history
This inline function finds the bit position of the most significant 1
bit in a word.  It basically separates out code from another inline
function, so that it can be called more generally.

I took the liberty of clarifying the comment and explicitly #ifdef'ing
out a line of code instead of relying on the optimizer to skip it.
  • Loading branch information
khwilliamson committed Jun 6, 2021
1 parent aa55012 commit cd0c6ec
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 28 deletions.
1 change: 1 addition & 0 deletions embed.fnc
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions embed.h
Expand Up @@ -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
Expand Down
65 changes: 37 additions & 28 deletions inline.h
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 */
Expand Down
6 changes: 6 additions & 0 deletions proto.h
Expand Up @@ -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)
Expand Down

0 comments on commit cd0c6ec

Please sign in to comment.