[m-rev.] for post-commit review: bitmap tests for secondary tags
Julien Fischer
jfischer at opturion.com
Sat Mar 16 22:53:23 AEDT 2024
On Sat, 16 Mar 2024, Zoltan Somogyi wrote:
> Use bitmaps for faster tests of sectags.
>
> compiler/tag_switch.m:
> The try_chain and try_me_else chain methods of implementing tag switches
> are based on tests for membership of one tag group after another.
>
> A sectag group has one or more members. When testing whether the actual
> sectag matches one of the sectags in a group with three or more members,
> try to
>
> - encode the group's values as one or more bitmap words,
> - convert each bitmap word into a test for whether the actual rval matches
> one of the sectag values encoded in that bitmap word, and
> - OR together the resulting tests.
>
> Use bitmap tests only for bitmap words that encode three or more sectag
> values in the group; for only one or two, continue to use simple
> equality tests.
>
> For the last few days, we have already used a much simpler version
> of this scheme for try_chain and try_me_else_chain switches on ptags.
> Move the code that does the work on ptags to be next to the new code
> that works on sectags, since they are based on the same principles.
> diff --git a/compiler/tag_switch.m b/compiler/tag_switch.m
> index be86d9a38..e82deeed5 100644
> --- a/compiler/tag_switch.m
> +++ b/compiler/tag_switch.m
...
> +%---------------------------------------------------------------------------%
> +%
> +% Infrastructure needed for both try-me-else and try chain switches
> +% that test whether a tag (ptag or sectag) is in a given set.
> +%
> +% For ptags we know that all the sets in which we want to test for membership
> +% will be subsets of {0 .. 7}, which means that
> +%
> +% - the bitmap form of the set will always fit into one word, and
> +% - using the value of the ptag to index into this word will always be ok.
> +%
> +% Neither is true for secondary tags, because
> +%
> +% - testing a secondary tag for membership in the set {2, 5, 77, 80}
> +% will require looking in two words even on 64 bit machines, and
> +%
> +% - testing a secondary tag for membership in the set {12, 15, 33, 37}
> +% will require looking in either one or two words on 32 bit machines,
> +% but arranging things so that we look in only one word requires
> +% making the least significant bit of that word correspond to
> +% a sectag value that is *not* zero, requiring a subtraction
> +% of an initial offset from the actual sectag value.
> +%
> +% If we want to take advantage of the invariants applying to ptags (and we do),
> +% these differences require separate code for testing ptags vs testing sectags.
> +% We nevertheless group all that code together here, because this should make
> +% understanding and modifying this code simpler.
> +%
> +% A note about the mixed use of both signed and unsigned integers here.
> +%
> +% - The macros we use to get the ptag or sectag bits of a word were designed
> +% before Mercury supported unsigned integers, and therefore they return
> +% signed integers.
> +%
> +% - On the other hand, we want to be able to use bitmaps that all the bits
> +% of a word, including the most-significant bit. If we used signed integer
> +% operations on a bitmap that had the most-significant bit set, we would
> +% be invoking undefined behavior, which would be bad, since I (zs) don't
> +% want the C compiler to either optimize away the test rval entirely,
> +% to to make demons fly out of my nose :-)
The demons have have evidently been at work in that last sentence:
s/to to/or to/
> +% The result is that we use unsigned integers to hold the bits being shifted,
> +% masked and tested, but signed integers (that are always non-negative)
> +% as the shift amounts.
> +%
> +
> +:- pred test_ptag_is_in_set(rval::in, ptag::in, list(ptag)::in,
> + rval::out) is det.
> +
> +test_ptag_is_in_set(PtagRval, MainPtag, OtherPtags, TestRval) :-
> + (
> + OtherPtags = [],
> + MainPtag = ptag(MainPtagUint8),
> + TestRval = binop(eq(int_type_int), PtagRval,
> + const(llconst_int(uint8.cast_to_int(MainPtagUint8))))
> + ;
> + OtherPtags = [_ | _],
> + encode_ptags_as_bitmap_loop(MainPtag, OtherPtags, 0u, Bitmap),
> + LeftShiftOp = unchecked_left_shift(int_type_uint, shift_by_int),
> + SelectedBitMaskRval = binop(LeftShiftOp,
> + const(llconst_uint(1u)), PtagRval),
> + SelectedBitRval = binop(bitwise_and(int_type_uint),
> + SelectedBitMaskRval, const(llconst_uint(Bitmap))),
> + TestRval = binop(ne(int_type_uint),
> + SelectedBitRval, const(llconst_uint(0u)))
> + ).
> +
...
> +:- pred test_sectag_is_in_bitmaps(uint::in, rval::in,
> + bitmap_word::in, list(bitmap_word)::in, rval::out) is det.
> +
> +test_sectag_is_in_bitmaps(WordSize, SectagRval, HeadBitmap, TailBitmaps,
> + TestRval) :-
> + HeadBitmap = bitmap_word(StartOffset, Bitmap0, ValuesCord),
> + Values = cord.list(ValuesCord),
> + % When we have just one sectag value, we just test for equality directly.
> + %
> + % For three or more sectag values, it is almost certainly cheaper
> + % to do a substraction, a shift, and bitwise and a test against zero
s/substraction/subtraction/
The rest looks fine.
Julien.
More information about the reviews
mailing list