Skip to content

Commit

Permalink
Create and use 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 Jul 17, 2021
1 parent 928c5f6 commit a9c5441
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 @@ -1142,6 +1142,7 @@ ATidRp |bool |is_utf8_invariant_string_loc|NN const U8* const s \
|STRLEN len \
|NULLOK const U8 ** ep
CTiRp |unsigned|lsbit_pos|PERL_UINTMAX_T word
CTiRp |unsigned|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 @@ -326,6 +326,7 @@
#define mortal_getenv Perl_mortal_getenv
#define mro_get_linear_isa(a) Perl_mro_get_linear_isa(aTHX_ a)
#define mro_method_changed_in(a) Perl_mro_method_changed_in(aTHX_ a)
#define msbit_pos Perl_msbit_pos
#define my_atof(a) Perl_my_atof(aTHX_ a)
#define my_atof3(a,b,c) Perl_my_atof3(aTHX_ a,b,c)
#define my_dirfd Perl_my_dirfd
Expand Down
65 changes: 37 additions & 28 deletions inline.h
Expand Up @@ -664,6 +664,40 @@ Perl_is_utf8_invariant_string_loc(const U8* const s, STRLEN len, const U8 ** ep)
return TRUE;
}

PERL_STATIC_INLINE unsigned
Perl_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_lsbit_pos(PERL_UINTMAX_T word)
{
Expand Down Expand Up @@ -741,34 +775,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 = 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 @@ -2152,6 +2152,12 @@ PERL_CALLCONV void Perl_mro_set_mro(pTHX_ struct mro_meta *const meta, SV *const
PERL_CALLCONV SV* Perl_mro_set_private_data(pTHX_ struct mro_meta *const smeta, const struct mro_alg *const which, SV *const data);
#define PERL_ARGS_ASSERT_MRO_SET_PRIVATE_DATA \
assert(smeta); assert(which); assert(data)
#ifndef PERL_NO_INLINE_FUNCTIONS
PERL_STATIC_INLINE unsigned Perl_msbit_pos(PERL_UINTMAX_T word)
__attribute__warn_unused_result__;
#define PERL_ARGS_ASSERT_MSBIT_POS
#endif

PERL_CALLCONV SV* Perl_multiconcat_stringify(pTHX_ const OP* o);
#define PERL_ARGS_ASSERT_MULTICONCAT_STRINGIFY \
assert(o)
Expand Down

0 comments on commit a9c5441

Please sign in to comment.