Skip to content

Commit

Permalink
Optimize matching out a bitstring
Browse files Browse the repository at this point in the history
  • Loading branch information
bjorng committed Dec 15, 2023
1 parent c18b204 commit 2123d6f
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 18 deletions.
20 changes: 13 additions & 7 deletions erts/emulator/beam/jit/arm/instr_bs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3730,9 +3730,6 @@ static std::vector<BsmSegment> opt_bsm_segments(
case BsmSegment::action::GET_BITSTRING:
if (seg.size > 64) {
read_action_pos = -1;
} else if (seg.action == BsmSegment::action::GET_BITSTRING &&
seg.size % 8 != 0) {
read_action_pos = -1;
} else {
if ((seg.flags & BSF_LITTLE) != 0 || read_action_pos < 0 ||
seg.size + segs.at(read_action_pos).size > 64) {
Expand Down Expand Up @@ -4122,16 +4119,25 @@ void BeamModuleAssembler::emit_i_bs_match_test_heap(ArgLabel const &Fail,
}
case BsmSegment::action::GET_BITSTRING: {
auto Live = seg.live;
ERTS_ASSERT(seg.size > 64);
comment("get binary %ld", seg.size);
auto ctx = load_source(Ctx, TMP1);

if (position_is_valid) {
a.mov(ARG5, bin_position);
} else {
a.ldur(ARG5, emit_boxed_val(ctx.reg, start_offset));
}
lea(ARG1, arm::Mem(c_p, offsetof(Process, htop)));
a.ldur(ARG2, emit_boxed_val(ctx.reg, orig_offset));
a.and_(ARG3, ARG2, imm(~TAG_PTR_MASK__));
a.and_(ARG2, ARG2, imm(TAG_PTR_MASK__));
if (seg.size <= ERL_ONHEAP_BITS_LIMIT) {
comment("skipped setting registers not used for heap binary");
} else {
a.ldur(ARG2, emit_boxed_val(ctx.reg, orig_offset));
a.and_(ARG3, ARG2, imm(~TAG_PTR_MASK__));
a.and_(ARG2, ARG2, imm(TAG_PTR_MASK__));
}
a.ldur(ARG4, emit_boxed_val(ctx.reg, base_offset));
a.and_(ARG4, ARG4, imm(~ERL_SUB_BITS_FLAG_MASK));
a.ldur(ARG5, emit_boxed_val(ctx.reg, start_offset));
mov_imm(ARG6, seg.size);
a.add(TMP2, ARG5, ARG6);
a.stur(TMP2, emit_boxed_val(ctx.reg, start_offset));
Expand Down
22 changes: 14 additions & 8 deletions erts/emulator/beam/jit/x86/instr_bs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3894,9 +3894,6 @@ static std::vector<BsmSegment> opt_bsm_segments(

if (seg.size > 64) {
read_action_pos = -1;
} else if (seg.action == BsmSegment::action::GET_BITSTRING &&
seg.size % 8 != 0) {
read_action_pos = -1;
} else if ((seg.flags & BSF_LITTLE) != 0 && is_common_size) {
seg.action = BsmSegment::action::READ_INTEGER;
read_action_pos = -1;
Expand Down Expand Up @@ -4367,21 +4364,30 @@ void BeamModuleAssembler::emit_i_bs_match_test_heap(ArgLabel const &Fail,
break;
}
case BsmSegment::action::GET_BITSTRING: {
ERTS_ASSERT(seg.size > 64);
comment("get binary %ld", seg.size);
if (is_ctx_valid) {
a.mov(RET, ctx);
} else {
mov_arg(RET, Ctx);
}
emit_enter_runtime<Update::eHeapOnlyAlloc>();
if (is_position_valid) {
a.mov(ARG5, bin_position);
} else {
a.mov(ARG5, emit_boxed_val(RET, start_offset));
}
a.lea(ARG1, x86::qword_ptr(c_p, offsetof(Process, htop)));
a.mov(ARG2, emit_boxed_val(RET, orig_offset));
a.mov(ARG3, ARG2);
a.and_(ARG2, imm(TAG_PTR_MASK__));
a.and_(ARG3, imm(~TAG_PTR_MASK__));
if (seg.size <= ERL_ONHEAP_BITS_LIMIT) {
comment("skipped setting registers not used for heap binary");
} else {
a.mov(ARG2, emit_boxed_val(RET, orig_offset));
a.mov(ARG3, ARG2);
a.and_(ARG2, imm(TAG_PTR_MASK__));
a.and_(ARG3, imm(~TAG_PTR_MASK__));
}
a.mov(ARG4, emit_boxed_val(RET, base_offset));
a.and_(ARG4, imm(~ERL_SUB_BITS_FLAG_MASK));
a.mov(ARG5, emit_boxed_val(RET, start_offset));
mov_imm(ARG6, seg.size);
a.add(emit_boxed_val(RET, start_offset), ARG6);

Expand Down
52 changes: 49 additions & 3 deletions erts/emulator/test/bs_match_bin_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
init_per_group/2,end_per_group/2,
byte_split_binary/1,bit_split_binary/1,match_huge_bin/1,
bs_match_string_edge_case/1,contexts/1,
empty_binary/1,small_bitstring/1]).
empty_binary/1,small_bitstring/1,
known_position/1]).

-include_lib("common_test/include/ct.hrl").

Expand All @@ -33,7 +34,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[byte_split_binary, bit_split_binary, match_huge_bin,
bs_match_string_edge_case, contexts, empty_binary,
small_bitstring].
small_bitstring,known_position].

groups() ->
[].
Expand Down Expand Up @@ -281,7 +282,9 @@ small_bitstring(_Config) ->
%% heap space for small bitstrings.
rand_seed(),
Bin = rand:bytes(10_000),
ok = small_bitstring_1(id(Bin), id(Bin)).
ok = small_bitstring_1(id(Bin), id(Bin)),
ok = small_bitstring_2(id(Bin), id(7)),
ok = small_bitstring_3(id(Bin), id(64)).

small_bitstring_1(<<A1:1/bits,A2:1/bits,A3:2/bits,
A4:3/bits,A5:1/bits,As0/binary>>,
Expand All @@ -291,6 +294,49 @@ small_bitstring_1(<<A1:1/bits,A2:1/bits,A3:2/bits,
small_bitstring_1(<<>>, <<>>) ->
ok.

small_bitstring_2(<<>>, _) ->
ok;
small_bitstring_2(Bin, N7) ->
%% Ensure that matching fixed sizes gives the same result as
%% matching dynamic sizes.

<<A1:3/bits,A2:7/bits,A3:7/bits,
A4:15/bits,_:32,As/binary>> = Bin,
<<A1:(N7-4)/bits,A2:N7/bits,A3:N7/bits,
A4:(N7+N7+1)/bits,_:32,As/binary>> = Bin,

<<B0:(7+8),B1:3/bits,B2:7/bits,B3:7/bits,
B4:15/bits,B5:(7+10),As/binary>> = Bin,
<<B0:(N7+8),B1:(N7-4)/bits,B2:N7/bits,B3:N7/bits,
B4:(N7+N7+1)/bits,B5:(N7+10),As/binary>> = Bin,

small_bitstring_2(As, N7).

small_bitstring_3(<<>>, _) ->
ok;
small_bitstring_3(Bin, N64) ->
%% Ensure that matching fixed sizes gives the same result as
%% matching dynamic sizes for larger sizes.

<<A1:(64-1)/bits,A2:(64+1)/bits,As/binary>> = Bin,
<<A1:(N64-1)/bits,A2:(N64+1)/bits,As/binary>> = Bin,

<<B1:(64+6)/bits,B2:(64-6)/bits,As/binary>> = Bin,
<<B1:(N64+6)/bits,B2:(N64-6)/bits,As/binary>> = Bin,

<<C0:5,C1:(64+7)/bits,C2:(64-12)/bits,As/binary>> = Bin,
<<C0:5,C1:(N64+7)/bits,C2:(N64-12)/bits,As/binary>> = Bin,

small_bitstring_3(As, N64).

known_position(_Config) ->
%% Cover the case of an extracted bitstring having a known position.
<<Int:8,BitString:9/binary,$j:8>> = id(<<42:8,"abcdefghij">>),
42 = Int,
<<"abcdefghi">> = BitString,

ok.

%%%
%%% Common utilities.
%%%
Expand Down

0 comments on commit 2123d6f

Please sign in to comment.