Skip to content

Commit 5aa8f8c

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 5354905 commit 5aa8f8c

File tree

3 files changed

+67
-19
lines changed

3 files changed

+67
-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
@@ -5447,20 +5447,21 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
54475447
term bs_bin = term_get_match_state_binary(src);
54485448
avm_int_t bs_offset = term_get_match_state_offset(src);
54495449

5450-
if (unit != 8) {
5451-
TRACE("bs_get_binary2: Unsupported: unit must be 8.\n");
5452-
RAISE_ERROR(UNSUPPORTED_ATOM);
5453-
}
54545450
avm_int_t size_val = 0;
54555451
if (term_is_integer(size)) {
5456-
size_val = term_to_int(size);
5452+
size_val = term_to_int(size) * unit;
5453+
if (size_val % 8) {
5454+
TRACE("bs_get_binary2: Unsupported: size must be divisible by 8, got: %ld\n", size_val);
5455+
RAISE_ERROR(UNSUPPORTED_ATOM);
5456+
}
5457+
size_val = size_val / 8;
54575458
} else if (size == ALL_ATOM) {
54585459
size_val = term_binary_size(bs_bin) - bs_offset / 8;
54595460
} else {
54605461
TRACE("bs_get_binary2: size is neither an integer nor the atom `all`\n");
54615462
RAISE_ERROR(BADARG_ATOM);
54625463
}
5463-
if (bs_offset % unit != 0) {
5464+
if (bs_offset % 8 != 0) {
54645465
TRACE("bs_get_binary2: Unsupported. Offset on binary read must be aligned on byte boundaries.\n");
54655466
RAISE_ERROR(BADARG_ATOM);
54665467
}
@@ -5471,11 +5472,11 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
54715472

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

5474-
if ((unsigned int) (bs_offset / unit + size_val) > term_binary_size(bs_bin)) {
5475+
if ((unsigned int) (bs_offset / 8 + size_val) > term_binary_size(bs_bin)) {
54755476
TRACE("bs_get_binary2: insufficient capacity -- bs_offset = %d, size_val = %d\n", (int) bs_offset, (int) size_val);
54765477
JUMP_TO_ADDRESS(mod->labels[fail]);
54775478
} else {
5478-
term_set_match_state_offset(src, bs_offset + size_val * unit);
5479+
term_set_match_state_offset(src, bs_offset + size_val * 8);
54795480

54805481
TRIM_LIVE_REGS(live);
54815482
// there is always room for a MAX_REG + 1 register, used as working register
@@ -6962,7 +6963,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
69626963
JUMP_TO_LABEL(mod, fail);
69636964
}
69646965
}
6965-
segment_size = signed_size_value;
6966+
segment_size = size_in_bytes;
6967+
segment_unit = 8;
69666968
}
69676969
break;
69686970
}

tests/erlang_tests/test_bs.erl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ start() ->
100100
ok = test_bs_skip_bits2_little(),
101101

102102
ok = test_bs_variable_size_bitstring(),
103+
ok = test_bs_match_bitstring_modifier(),
103104

104105
0.
105106

@@ -564,6 +565,25 @@ test_bs_variable_size_bitstring() ->
564565
end,
565566
ok.
566567

568+
test_bs_match_bitstring_modifier() ->
569+
ok =
570+
try
571+
bitstring_match(id(<<123, 234, 245>>), id(15)),
572+
case erlang:system_info(machine) of
573+
"BEAM" -> ok;
574+
"ATOM" -> unexpected
575+
end
576+
catch
577+
error:unsupported -> ok
578+
end,
579+
580+
{<<123, 234>>, <<245>>} = bitstring_match(id(<<123, 234, 245>>), id(16)),
581+
ok.
582+
583+
bitstring_match(BS, Size) ->
584+
<<Matched:Size/bitstring, Rest/bits>> = BS,
585+
{Matched, Rest}.
586+
567587
check_x86_64_jt(<<>>) -> ok;
568588
check_x86_64_jt(<<16#e9, _Offset:32/little, Tail/binary>>) -> check_x86_64_jt(Tail);
569589
check_x86_64_jt(Bin) -> {unexpected, Bin}.

0 commit comments

Comments
 (0)