Skip to content

Commit 6c9843d

Browse files
committed
Support matching bitstrings that are in fact binaries (size divisible by 8)
Signed-off-by: Mateusz Front <mateusz.front@swmansion.com>
1 parent 1dafbd5 commit 6c9843d

File tree

3 files changed

+66
-19
lines changed

3 files changed

+66
-19
lines changed

libs/jit/src/jit.erl

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,10 +1324,6 @@ first_pass(<<?OP_BS_GET_BINARY2, Rest0/binary>>, MMod, MSt0, State0) ->
13241324
{MSt5, BSOffsetReg0} = MMod:get_array_element(MSt4, MatchStateRegPtr, 2),
13251325
MSt6 =
13261326
if
1327-
Unit =/= 8 ->
1328-
MMod:call_primitive_last(MSt5, ?PRIM_RAISE_ERROR, [
1329-
ctx, jit_state, offset, ?UNSUPPORTED_ATOM
1330-
]);
13311327
FlagsValue =/= 0 ->
13321328
MMod:call_primitive_last(MSt5, ?PRIM_RAISE_ERROR, [
13331329
ctx, jit_state, offset, ?UNSUPPORTED_ATOM
@@ -1349,9 +1345,17 @@ first_pass(<<?OP_BS_GET_BINARY2, Rest0/binary>>, MMod, MSt0, State0) ->
13491345
is_integer(Size) ->
13501346
% SizeReg is binary size
13511347
% SizeVal is a constant
1352-
MSt11 = MMod:sub(MSt10, SizeReg, Size bsl 4),
1348+
MSt11 =
1349+
if
1350+
(Size * Unit) rem 8 =/= 0 ->
1351+
MMod:call_primitive_last(MSt10, ?PRIM_RAISE_ERROR, [
1352+
ctx, jit_state, offset, ?UNSUPPORTED_ATOM
1353+
]);
1354+
true ->
1355+
MMod:sub(MSt10, SizeReg, (Size * Unit) div 8)
1356+
end,
13531357
MSt12 = cond_jump_to_label({{free, SizeReg}, '<', BSOffsetReg1}, Fail, MMod, MSt11),
1354-
{MSt12, Size bsl 4};
1358+
{MSt12, (Size * Unit) div 8};
13551359
true ->
13561360
{MSt11, SizeValReg} = MMod:move_to_native_register(MSt10, Size),
13571361
MSt12 = MMod:if_else_block(
@@ -1363,10 +1367,32 @@ first_pass(<<?OP_BS_GET_BINARY2, Rest0/binary>>, MMod, MSt0, State0) ->
13631367
end,
13641368
fun(BSt0) ->
13651369
{BSt1, SizeValReg} = term_to_int(SizeValReg, 0, MMod, BSt0),
1366-
BSt2 = MMod:sub(BSt1, SizeReg, SizeValReg),
1367-
BSt3 = cond_jump_to_label({SizeReg, '<', BSOffsetReg1}, Fail, MMod, BSt2),
1368-
BSt4 = MMod:move_to_native_register(BSt3, SizeValReg, SizeReg),
1369-
MMod:free_native_registers(BSt4, [SizeValReg])
1370+
{BSt2, SizeValReg2} =
1371+
if
1372+
is_integer(SizeValReg) ->
1373+
if
1374+
(SizeValReg * Unit) rem 8 =/= 0 ->
1375+
MMod:call_primitive_last(BSt1, ?PRIM_RAISE_ERROR, [
1376+
ctx, jit_state, offset, ?UNSUPPORTED_ATOM
1377+
]);
1378+
true ->
1379+
{BSt1, (SizeValReg * Unit) div 8}
1380+
end;
1381+
true ->
1382+
BBSt1 = MMod:mul(BSt1, SizeValReg, Unit),
1383+
BBSt2 = MMod:if_block(
1384+
BBSt1, {SizeValReg, '&', 16#7, '!=', 0}, fun(BlockSt) ->
1385+
MMod:call_primitive_last(BlockSt, ?PRIM_RAISE_ERROR, [
1386+
ctx, jit_state, offset, ?UNSUPPORTED_ATOM
1387+
])
1388+
end
1389+
),
1390+
MMod:shift_right(BBSt2, SizeValReg, 3)
1391+
end,
1392+
BSt3 = MMod:sub(BSt2, SizeReg, SizeValReg2),
1393+
BSt4 = cond_jump_to_label({SizeReg, '<', BSOffsetReg1}, Fail, MMod, BSt3),
1394+
BSt5 = MMod:move_to_native_register(BSt4, SizeValReg2, SizeReg),
1395+
MMod:free_native_registers(BSt5, [SizeValReg])
13701396
end
13711397
),
13721398
{MSt12, SizeReg}

src/libAtomVM/opcodesswitch.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5514,20 +5514,21 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
55145514
term bs_bin = term_get_match_state_binary(src);
55155515
avm_int_t bs_offset = term_get_match_state_offset(src);
55165516

5517-
if (unit != 8) {
5518-
TRACE("bs_get_binary2: Unsupported: unit must be 8.\n");
5519-
RAISE_ERROR(UNSUPPORTED_ATOM);
5520-
}
55215517
avm_int_t size_val = 0;
55225518
if (term_is_integer(size)) {
5523-
size_val = term_to_int(size);
5519+
size_val = term_to_int(size) * unit;
5520+
if (size_val % 8) {
5521+
TRACE("bs_get_binary2: Unsupported: size must be divisible by 8, got: %ld\n", size_val);
5522+
RAISE_ERROR(UNSUPPORTED_ATOM);
5523+
}
5524+
size_val = size_val / 8;
55245525
} else if (size == ALL_ATOM) {
55255526
size_val = term_binary_size(bs_bin) - bs_offset / 8;
55265527
} else {
55275528
TRACE("bs_get_binary2: size is neither an integer nor the atom `all`\n");
55285529
RAISE_ERROR(BADARG_ATOM);
55295530
}
5530-
if (bs_offset % unit != 0) {
5531+
if (bs_offset % 8 != 0) {
55315532
TRACE("bs_get_binary2: Unsupported. Offset on binary read must be aligned on byte boundaries.\n");
55325533
RAISE_ERROR(BADARG_ATOM);
55335534
}
@@ -5538,11 +5539,11 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
55385539

55395540
TRACE("bs_get_binary2/7, fail=%u src=%p live=%u unit=%u\n", (unsigned) fail, (void *) bs_bin, (unsigned) live, (unsigned) unit);
55405541

5541-
if ((unsigned int) (bs_offset / unit + size_val) > term_binary_size(bs_bin)) {
5542+
if ((unsigned int) (bs_offset / 8 + size_val) > term_binary_size(bs_bin)) {
55425543
TRACE("bs_get_binary2: insufficient capacity -- bs_offset = %d, size_val = %d\n", (int) bs_offset, (int) size_val);
55435544
JUMP_TO_ADDRESS(mod->labels[fail]);
55445545
} else {
5545-
term_set_match_state_offset(src, bs_offset + size_val * unit);
5546+
term_set_match_state_offset(src, bs_offset + size_val * 8);
55465547

55475548
TRIM_LIVE_REGS(live);
55485549
// there is always room for a MAX_REG + 1 register, used as working register
@@ -7054,7 +7055,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
70547055
JUMP_TO_LABEL(mod, fail);
70557056
}
70567057
}
7057-
segment_size = signed_size_value;
7058+
segment_size = size_in_bytes;
7059+
segment_unit = 8;
70587060
}
70597061
break;
70607062
}

tests/erlang_tests/test_bs.erl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ start() ->
105105

106106
ok = test_bs_variable_size_bitstring(),
107107
ok = test_float(),
108+
ok = test_bs_match_bitstring_modifier(),
108109

109110
0.
110111

@@ -672,6 +673,24 @@ test_create_with_invalid_float_size() ->
672673

673674
create_float_binary(Value, Size) ->
674675
<<Value:Size/float>>.
676+
test_bs_match_bitstring_modifier() ->
677+
ok =
678+
try
679+
bitstring_match(id(<<123, 234, 245>>), id(15)),
680+
case erlang:system_info(machine) of
681+
"BEAM" -> ok;
682+
"ATOM" -> unexpected
683+
end
684+
catch
685+
error:unsupported -> ok
686+
end,
687+
688+
{<<123, 234>>, <<245>>} = bitstring_match(id(<<123, 234, 245>>), id(16)),
689+
ok.
690+
691+
bitstring_match(BS, Size) ->
692+
<<Matched:Size/bitstring, Rest/bits>> = BS,
693+
{Matched, Rest}.
675694

676695
check_x86_64_jt(<<>>) -> ok;
677696
check_x86_64_jt(<<16#e9, _Offset:32/little, Tail/binary>>) -> check_x86_64_jt(Tail);

0 commit comments

Comments
 (0)