diff --git a/libs/jit/src/jit.erl b/libs/jit/src/jit.erl index c67105b3b..0e3f66cfb 100644 --- a/libs/jit/src/jit.erl +++ b/libs/jit/src/jit.erl @@ -1324,10 +1324,6 @@ first_pass(<>, MMod, MSt0, State0) -> {MSt5, BSOffsetReg0} = MMod:get_array_element(MSt4, MatchStateRegPtr, 2), MSt6 = if - Unit =/= 8 -> - MMod:call_primitive_last(MSt5, ?PRIM_RAISE_ERROR, [ - ctx, jit_state, offset, ?UNSUPPORTED_ATOM - ]); FlagsValue =/= 0 -> MMod:call_primitive_last(MSt5, ?PRIM_RAISE_ERROR, [ ctx, jit_state, offset, ?UNSUPPORTED_ATOM @@ -1349,9 +1345,17 @@ first_pass(<>, MMod, MSt0, State0) -> is_integer(Size) -> % SizeReg is binary size % SizeVal is a constant - MSt11 = MMod:sub(MSt10, SizeReg, Size bsl 4), + MSt11 = + if + (Size * Unit) rem 8 =/= 0 -> + MMod:call_primitive_last(MSt10, ?PRIM_RAISE_ERROR, [ + ctx, jit_state, offset, ?UNSUPPORTED_ATOM + ]); + true -> + MMod:sub(MSt10, SizeReg, (Size * Unit) div 8) + end, MSt12 = cond_jump_to_label({{free, SizeReg}, '<', BSOffsetReg1}, Fail, MMod, MSt11), - {MSt12, Size bsl 4}; + {MSt12, (Size * Unit) div 8}; true -> {MSt11, SizeValReg} = MMod:move_to_native_register(MSt10, Size), MSt12 = MMod:if_else_block( @@ -1363,10 +1367,32 @@ first_pass(<>, MMod, MSt0, State0) -> end, fun(BSt0) -> {BSt1, SizeValReg} = term_to_int(SizeValReg, 0, MMod, BSt0), - BSt2 = MMod:sub(BSt1, SizeReg, SizeValReg), - BSt3 = cond_jump_to_label({SizeReg, '<', BSOffsetReg1}, Fail, MMod, BSt2), - BSt4 = MMod:move_to_native_register(BSt3, SizeValReg, SizeReg), - MMod:free_native_registers(BSt4, [SizeValReg]) + {BSt2, SizeValReg2} = + if + is_integer(SizeValReg) -> + if + (SizeValReg * Unit) rem 8 =/= 0 -> + MMod:call_primitive_last(BSt1, ?PRIM_RAISE_ERROR, [ + ctx, jit_state, offset, ?UNSUPPORTED_ATOM + ]); + true -> + {BSt1, (SizeValReg * Unit) div 8} + end; + true -> + BBSt1 = MMod:mul(BSt1, SizeValReg, Unit), + BBSt2 = MMod:if_block( + BBSt1, {SizeValReg, '&', 16#7, '!=', 0}, fun(BlockSt) -> + MMod:call_primitive_last(BlockSt, ?PRIM_RAISE_ERROR, [ + ctx, jit_state, offset, ?UNSUPPORTED_ATOM + ]) + end + ), + MMod:shift_right(BBSt2, SizeValReg, 3) + end, + BSt3 = MMod:sub(BSt2, SizeReg, SizeValReg2), + BSt4 = cond_jump_to_label({SizeReg, '<', BSOffsetReg1}, Fail, MMod, BSt3), + BSt5 = MMod:move_to_native_register(BSt4, SizeValReg2, SizeReg), + MMod:free_native_registers(BSt5, [SizeValReg]) end ), {MSt12, SizeReg} diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index 9cfc423c6..43545f32f 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -5514,20 +5514,21 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) term bs_bin = term_get_match_state_binary(src); avm_int_t bs_offset = term_get_match_state_offset(src); - if (unit != 8) { - TRACE("bs_get_binary2: Unsupported: unit must be 8.\n"); - RAISE_ERROR(UNSUPPORTED_ATOM); - } avm_int_t size_val = 0; if (term_is_integer(size)) { - size_val = term_to_int(size); + size_val = term_to_int(size) * unit; + if (size_val % 8) { + TRACE("bs_get_binary2: Unsupported: size must be divisible by 8, got: %ld\n", size_val); + RAISE_ERROR(UNSUPPORTED_ATOM); + } + size_val = size_val / 8; } else if (size == ALL_ATOM) { size_val = term_binary_size(bs_bin) - bs_offset / 8; } else { TRACE("bs_get_binary2: size is neither an integer nor the atom `all`\n"); RAISE_ERROR(BADARG_ATOM); } - if (bs_offset % unit != 0) { + if (bs_offset % 8 != 0) { TRACE("bs_get_binary2: Unsupported. Offset on binary read must be aligned on byte boundaries.\n"); RAISE_ERROR(BADARG_ATOM); } @@ -5538,11 +5539,11 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) TRACE("bs_get_binary2/7, fail=%u src=%p live=%u unit=%u\n", (unsigned) fail, (void *) bs_bin, (unsigned) live, (unsigned) unit); - if ((unsigned int) (bs_offset / unit + size_val) > term_binary_size(bs_bin)) { + if ((unsigned int) (bs_offset / 8 + size_val) > term_binary_size(bs_bin)) { TRACE("bs_get_binary2: insufficient capacity -- bs_offset = %d, size_val = %d\n", (int) bs_offset, (int) size_val); JUMP_TO_ADDRESS(mod->labels[fail]); } else { - term_set_match_state_offset(src, bs_offset + size_val * unit); + term_set_match_state_offset(src, bs_offset + size_val * 8); TRIM_LIVE_REGS(live); // 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) JUMP_TO_LABEL(mod, fail); } } - segment_size = signed_size_value; + segment_size = size_in_bytes; + segment_unit = 8; } break; } diff --git a/tests/erlang_tests/test_bs.erl b/tests/erlang_tests/test_bs.erl index 4b3eb011f..6e080962d 100644 --- a/tests/erlang_tests/test_bs.erl +++ b/tests/erlang_tests/test_bs.erl @@ -105,6 +105,7 @@ start() -> ok = test_bs_variable_size_bitstring(), ok = test_float(), + ok = test_bs_match_bitstring_modifier(), 0. @@ -672,6 +673,24 @@ test_create_with_invalid_float_size() -> create_float_binary(Value, Size) -> <>. +test_bs_match_bitstring_modifier() -> + ok = + try + bitstring_match(id(<<123, 234, 245>>), id(15)), + case erlang:system_info(machine) of + "BEAM" -> ok; + "ATOM" -> unexpected + end + catch + error:unsupported -> ok + end, + + {<<123, 234>>, <<245>>} = bitstring_match(id(<<123, 234, 245>>), id(16)), + ok. + +bitstring_match(BS, Size) -> + <> = BS, + {Matched, Rest}. check_x86_64_jt(<<>>) -> ok; check_x86_64_jt(<<16#e9, _Offset:32/little, Tail/binary>>) -> check_x86_64_jt(Tail);