Skip to content

Commit 5354905

Browse files
committed
Handle 8-divisible bitstring variable lengths in binary construction
Signed-off-by: Paul Guyot <pguyot@kallisys.net>
1 parent a6d56b9 commit 5354905

File tree

4 files changed

+83
-15
lines changed

4 files changed

+83
-15
lines changed

libs/jit/src/jit.erl

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,12 +2124,11 @@ first_pass(<<?OP_BS_GET_POSITION, Rest0/binary>>, MMod, MSt0, State0) ->
21242124
{MSt3, Reg} = MMod:move_to_native_register(MSt2, Src),
21252125
{MSt4, Reg} = MMod:and_(MSt3, {free, Reg}, ?TERM_PRIMARY_CLEAR_MASK),
21262126
MSt5 = MMod:move_array_element(MSt4, Reg, 2, Reg),
2127-
MSt6 = MMod:shift_left(MSt5, Reg, 4),
2128-
MSt7 = MMod:or_(MSt6, Reg, ?TERM_INTEGER_TAG),
2129-
MSt8 = MMod:move_to_vm_register(MSt7, Reg, Dest),
2130-
MSt9 = MMod:free_native_registers(MSt8, [Reg, Dest]),
2131-
?ASSERT_ALL_NATIVE_FREE(MSt9),
2132-
first_pass(Rest3, MMod, MSt9, State0);
2127+
{MSt6, Reg} = term_from_int(Reg, MMod, MSt5),
2128+
MSt7 = MMod:move_to_vm_register(MSt6, Reg, Dest),
2129+
MSt8 = MMod:free_native_registers(MSt7, [Reg, Dest]),
2130+
?ASSERT_ALL_NATIVE_FREE(MSt8),
2131+
first_pass(Rest3, MMod, MSt8, State0);
21332132
% 168
21342133
first_pass(<<?OP_BS_SET_POSITION, Rest0/binary>>, MMod, MSt0, State0) ->
21352134
?ASSERT_ALL_NATIVE_FREE(MSt0),
@@ -2769,20 +2768,38 @@ first_pass_bs_create_bin_insert_value(
27692768
),
27702769
{MSt5, NewOffset, CreatedBin};
27712770
first_pass_bs_create_bin_insert_value(
2772-
AtomType, _Flags, Src, Size, _SegmentUnit, _Fail, CreatedBin, Offset, MMod, MSt0
2771+
AtomType, _Flags, Src, Size, SegmentUnit, Fail, CreatedBin, Offset, MMod, MSt0
27732772
) when AtomType =:= binary orelse AtomType =:= append orelse AtomType =:= private_append ->
2774-
{MSt1, SizeValue} = MMod:call_primitive(MSt0, ?PRIM_BITSTRING_COPY_BINARY, [
2775-
ctx, jit_state, CreatedBin, Offset, Src, Size
2773+
{MSt4, SizeInBits} =
2774+
if
2775+
is_integer(Size) andalso Size band 16#F =:= ?TERM_INTEGER_TAG ->
2776+
{MSt0, term_from_int(SegmentUnit * (Size bsr 4))};
2777+
Size =:= ?ALL_ATOM ->
2778+
{MSt0, Size};
2779+
SegmentUnit =:= 1 ->
2780+
{MSt1, SizeReg} = MMod:move_to_native_register(MSt0, Size),
2781+
MSt2 = cond_raise_badarg_or_jump_to_fail_label(
2782+
{SizeReg, '&', ?TERM_IMMED_TAG_MASK, '!=', ?TERM_INTEGER_TAG}, Fail, MMod, MSt1
2783+
),
2784+
{MSt2, {free, SizeReg}};
2785+
true ->
2786+
{MSt1, SizeReg} = term_to_int(Size, Fail, MMod, MSt0),
2787+
MSt2 = MMod:mul(MSt1, SizeReg, SegmentUnit),
2788+
{MSt3, SizeReg} = term_from_int(SizeReg, MMod, MSt2),
2789+
{MSt3, {free, SizeReg}}
2790+
end,
2791+
{MSt5, SizeValue} = MMod:call_primitive(MSt4, ?PRIM_BITSTRING_COPY_BINARY, [
2792+
ctx, jit_state, CreatedBin, Offset, Src, SizeInBits
27762793
]),
2777-
MSt2 = MMod:if_block(MSt1, {SizeValue, '<', 0}, fun(BlockSt) ->
2794+
MSt6 = MMod:if_block(MSt5, {SizeValue, '<', 0}, fun(BlockSt) ->
27782795
MMod:call_primitive_last(BlockSt, ?PRIM_HANDLE_ERROR, [
27792796
ctx, jit_state, offset
27802797
])
27812798
end),
2782-
{MSt3, NewOffset} = first_pass_bs_create_bin_insert_value_increment_offset(
2783-
MMod, MSt2, Offset, SizeValue, 1
2799+
{MSt7, NewOffset} = first_pass_bs_create_bin_insert_value_increment_offset(
2800+
MMod, MSt6, Offset, SizeValue, 1
27842801
),
2785-
{MSt3, NewOffset, CreatedBin};
2802+
{MSt7, NewOffset, CreatedBin};
27862803
first_pass_bs_create_bin_insert_value(
27872804
_OtherType, _Flag, _Src, _Size, _SegmentUnit, _Fail, CreatedBin, Offset, _MMod, MSt0
27882805
) ->
@@ -3786,6 +3803,13 @@ decode_allocator_list0(MMod, AccNeed, Remaining, Rest0) ->
37863803
term_from_int(Int) when is_integer(Int) ->
37873804
(Int bsl 4) bor ?TERM_INTEGER_TAG.
37883805

3806+
term_from_int(Int, _MMod, MSt0) when is_integer(Int) ->
3807+
{MSt0, term_from_int(Int)};
3808+
term_from_int(Reg, MMod, MSt0) when is_atom(Reg) ->
3809+
MSt1 = MMod:shift_left(MSt0, Reg, 4),
3810+
MSt2 = MMod:or_(MSt1, Reg, ?TERM_INTEGER_TAG),
3811+
{MSt2, Reg}.
3812+
37893813
term_get_tuple_arity(Tuple, MMod, MSt0) ->
37903814
{MSt1, Reg} =
37913815
case Tuple of

src/libAtomVM/jit.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,11 @@ static int jit_bitstring_copy_binary(Context *ctx, JITState *jit_state, term t,
14591459
size_t binary_size = term_binary_size(src);
14601460
if (size != ALL_ATOM) {
14611461
binary_size = (size_t) term_to_int(size);
1462+
if (binary_size % 8) {
1463+
set_error(ctx, jit_state, 0, UNSUPPORTED_ATOM);
1464+
return -1;
1465+
}
1466+
binary_size = binary_size / 8;
14621467
}
14631468
memcpy(dst, bin, binary_size);
14641469
return binary_size * 8;

src/libAtomVM/opcodesswitch.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6948,8 +6948,14 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
69486948
JUMP_TO_LABEL(mod, fail);
69496949
}
69506950
}
6951+
avm_int_t size_in_bits = signed_size_value * segment_unit;
6952+
if (size_in_bits % 8) {
6953+
TRACE("bs_create_bin/6: size in bits (%d) is not evenly divisible by 8\n", (int) size_in_bits);
6954+
RAISE_ERROR(UNSUPPORTED_ATOM);
6955+
}
6956+
avm_int_t size_in_bytes = size_in_bits / 8;
69516957
size_t binary_size = term_binary_size(src);
6952-
if ((size_t) signed_size_value > binary_size) {
6958+
if ((size_t) size_in_bytes > binary_size) {
69536959
if (fail == 0) {
69546960
RAISE_ERROR(BADARG_ATOM);
69556961
} else {
@@ -7109,7 +7115,8 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
71097115
TRACE("bs_create_bin/6: size value less than 0: %i\n", (int) signed_size_value);
71107116
RAISE_ERROR(BADARG_ATOM);
71117117
}
7112-
size_value = (size_t) signed_size_value;
7118+
// We checked earlier it's a multiple of 8
7119+
size_value = ((size_t) signed_size_value) * segment_unit / 8;
71137120
if (size_value > src_size) {
71147121
if (fail == 0) {
71157122
RAISE_ERROR(BADARG_ATOM);

tests/erlang_tests/test_bs.erl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ start() ->
9999

100100
ok = test_bs_skip_bits2_little(),
101101

102+
ok = test_bs_variable_size_bitstring(),
103+
102104
0.
103105

104106
test_pack_small_ints({A, B, C}, Expect) ->
@@ -532,6 +534,36 @@ test_bs_match_string_select() ->
532534
test_bs_skip_bits2_little() ->
533535
ok = check_x86_64_jt(id(<<16#e9, 0:32>>)).
534536

537+
test_bs_variable_size_bitstring() ->
538+
B1 = id(<<1, 2>>),
539+
B2 = id(<<3, 4>>),
540+
S1 = id(16),
541+
S2 = id(8),
542+
<<1, 2, 3, 4>> = <<B1:S1/bitstring, B2/binary>>,
543+
<<1, 2, 3>> = <<B1:S1/bitstring, B2:S2/bitstring>>,
544+
S3 = id(all),
545+
% AtomVM emu flavor actually accepts a dynamic all because a literal term
546+
% is evaluated like a variable one. BEAM and jit flavor don't.
547+
SupportsVariableAll =
548+
case erlang:system_info(machine) of
549+
"BEAM" ->
550+
no;
551+
"ATOM" ->
552+
case erlang:system_info(emu_flavor) of
553+
jit -> no;
554+
emu -> ok
555+
end
556+
end,
557+
ok =
558+
try
559+
<<1, 2, 3, 4>> = <<B1:S1/bitstring, B2:S3/bitstring>>,
560+
SupportsVariableAll
561+
catch
562+
error:badarg ->
563+
ok
564+
end,
565+
ok.
566+
535567
check_x86_64_jt(<<>>) -> ok;
536568
check_x86_64_jt(<<16#e9, _Offset:32/little, Tail/binary>>) -> check_x86_64_jt(Tail);
537569
check_x86_64_jt(Bin) -> {unexpected, Bin}.

0 commit comments

Comments
 (0)