Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/libAtomVM/bitstring.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,20 @@ bool bitstring_extract_f64(
return false;
}
}

intn_from_integer_options_t bitstring_flags_to_intn_opts(enum BitstringFlags bf)
{
intn_from_integer_options_t converted = IntnUnsignedBigEndian;
if (bf & LittleEndianInteger) {
converted |= IntnLittleEndian;
}
if (bf & SignedInteger) {
converted |= IntnSigned;
}
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
if (bf & NativeEndianInteger) {
converted |= IntnLittleEndian;
}
#endif
return converted;
}
3 changes: 3 additions & 0 deletions src/libAtomVM/bitstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#ifndef _BITSTRING_H_
#define _BITSTRING_H_

#include "intn.h"
#include "term.h"
#include "unicode.h"

Expand Down Expand Up @@ -528,6 +529,8 @@ bool bitstring_extract_f32(
bool bitstring_extract_f64(
term src_bin, size_t offset, avm_int_t n, enum BitstringFlags bs_flags, avm_float_t *dst);

intn_from_integer_options_t bitstring_flags_to_intn_opts(enum BitstringFlags bf);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 2 additions & 0 deletions src/libAtomVM/intn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1182,6 +1182,8 @@ int intn_from_integer_bytes(const uint8_t in[], size_t in_size, intn_from_intege
sign = IntNNegativeInteger;
}
*out_sign = sign;
} else if (out_sign) {
*out_sign = IntNPositiveInteger;
}

memset(out, filler, INTN_MAX_RES_LEN * sizeof(intn_digit_t));
Expand Down
63 changes: 54 additions & 9 deletions src/libAtomVM/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1244,19 +1244,64 @@ static term jit_term_alloc_bin_match_state(Context *ctx, term src, int slots)
return term_alloc_bin_match_state(src, slots, &ctx->heap);
}

static term jit_bitstring_extract_integer(Context *ctx, JITState *jit_state, term *bin_ptr, size_t offset, int n, int bs_flags)
static term extract_bigint(Context *ctx, JITState *jit_state, const uint8_t *bytes,
size_t bytes_size, intn_from_integer_options_t opts)
{
TRACE("jit_bitstring_extract_integer: bin_ptr=%p offset=%d n=%d bs_flags=%d\n", (void *) bin_ptr, (int) offset, n, bs_flags);
union maybe_unsigned_int64 value;
bool status = bitstring_extract_integer(((term) bin_ptr) | TERM_PRIMARY_BOXED, offset, n, bs_flags, &value);
if (UNLIKELY(!status)) {
intn_integer_sign_t sign;
intn_digit_t bigint[INTN_MAX_RES_LEN];
int count = intn_from_integer_bytes(bytes, bytes_size, opts, bigint, &sign);
// count will be always >= 0, caller ensures that bits <= INTN_MAX_UNSIGNED_BITS_SIZE

size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);

Heap heap;
if (UNLIKELY(memory_init_heap(&heap, BOXED_BIGINT_HEAP_SIZE(intn_data_size)) != MEMORY_GC_OK)) {
set_error(ctx, jit_state, 0, OUT_OF_MEMORY_ATOM);
return FALSE_ATOM;
}
term t = maybe_alloc_boxed_integer_fragment(ctx, value.s);
if (UNLIKELY(term_is_invalid_term(t))) {
set_error(ctx, jit_state, 0, OUT_OF_MEMORY_ATOM);

term bigint_term
= term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, &heap);
term_initialize_bigint(bigint_term, bigint, count, rounded_res_len);

memory_heap_append_heap(&ctx->heap, &heap);

return bigint_term;
}

static term jit_bitstring_extract_integer(
Context *ctx, JITState *jit_state, term *bin_ptr, size_t offset, int n, int bs_flags)
{
TRACE("jit_bitstring_extract_integer: bin_ptr=%p offset=%d n=%d bs_flags=%d\n",
(void *) bin_ptr, (int) offset, n, bs_flags);
if (n <= 64) {
union maybe_unsigned_int64 value;
bool status = bitstring_extract_integer(
((term) bin_ptr) | TERM_PRIMARY_BOXED, offset, n, bs_flags, &value);
if (UNLIKELY(!status)) {
return FALSE_ATOM;
}
term t = maybe_alloc_boxed_integer_fragment(ctx, value.s);
if (UNLIKELY(term_is_invalid_term(t))) {
set_error(ctx, jit_state, 0, OUT_OF_MEMORY_ATOM);
}
return t;
} else if ((offset % 8 == 0) && (n % 8 == 0) && (n <= INTN_MAX_UNSIGNED_BITS_SIZE)) {
term bs_bin = ((term) bin_ptr) | TERM_PRIMARY_BOXED;
unsigned long capacity = term_binary_size(bs_bin);
if (8 * capacity - offset < (unsigned long) n) {
return FALSE_ATOM;
}
size_t byte_offset = offset / 8;
const uint8_t *int_bytes = (const uint8_t *) term_binary_data(bs_bin);

return extract_bigint(
ctx, jit_state, int_bytes + byte_offset, n / 8, bitstring_flags_to_intn_opts(bs_flags));
} else {
return FALSE_ATOM;
}
return t;
}

static term jit_bitstring_extract_float(Context *ctx, term *bin_ptr, size_t offset, int n, int bs_flags)
Expand Down
145 changes: 95 additions & 50 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,40 @@ static bool maybe_call_native(Context *ctx, atom_index_t module_name, atom_index
#endif

#ifndef AVM_NO_EMU
static term extract_nbits_integer(Context *ctx, const uint8_t *bytes, size_t bytes_size, intn_from_integer_options_t opts)
{
intn_integer_sign_t sign;
intn_digit_t bigint[INTN_MAX_RES_LEN];
int count = intn_from_integer_bytes(bytes, bytes_size, opts, bigint, &sign);
if (UNLIKELY(count < 0)) {
// this is likely unreachable, compiler seem to generate an external term
// and to encode this as SMALL_BIG_EXT, so I don't think this code is executed
ctx->x[0] = ERROR_ATOM;
ctx->x[1] = OVERFLOW_ATOM;
return term_invalid_term();
}

size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);

Heap heap;
if (UNLIKELY(
memory_init_heap(&heap, BOXED_BIGINT_HEAP_SIZE(intn_data_size)) != MEMORY_GC_OK)) {
ctx->x[0] = ERROR_ATOM;
ctx->x[1] = OUT_OF_MEMORY_ATOM;
return term_invalid_term();
}

term bigint_term
= term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, &heap);
term_initialize_bigint(bigint_term, bigint, count, rounded_res_len);

memory_heap_append_heap(&ctx->heap, &heap);

return bigint_term;
}

static size_t decode_nbits_integer(Context *ctx, const uint8_t *encoded, term *out_term)
{
const uint8_t *new_encoded = encoded;
Expand All @@ -1826,41 +1860,9 @@ static bool maybe_call_native(Context *ctx, atom_index_t module_name, atom_index
len += 9;

if (out_term) {
intn_integer_sign_t sign;
intn_digit_t bigint[INTN_MAX_RES_LEN];
int count = intn_from_integer_bytes(new_encoded, len, IntnSigned, bigint, &sign);
if (UNLIKELY(count < 0)) {
// this is likely unreachable, compiler seem to generate an external term
// and to encode this as SMALL_BIG_EXT, so I don't think this code is executed
ctx->x[0] = ERROR_ATOM;
ctx->x[1] = OVERFLOW_ATOM;
*out_term = term_invalid_term();
goto return_size;
}

size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);

Heap heap;
if (UNLIKELY(
memory_init_heap(&heap, BOXED_BIGINT_HEAP_SIZE(intn_data_size)) != MEMORY_GC_OK)) {
ctx->x[0] = ERROR_ATOM;
ctx->x[1] = OUT_OF_MEMORY_ATOM;
*out_term = term_invalid_term();
goto return_size;
}

term bigint_term
= term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, &heap);
term_initialize_bigint(bigint_term, bigint, count, rounded_res_len);

memory_heap_append_heap(&ctx->heap, &heap);

*out_term = bigint_term;
*out_term = extract_nbits_integer(ctx, new_encoded, len, IntnSigned);
}

return_size:
return (new_encoded - encoded) + len;
}
#endif
Expand Down Expand Up @@ -5298,25 +5300,44 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
union maybe_unsigned_int64 value;
term bs_bin = term_get_match_state_binary(src);
avm_int_t bs_offset = term_get_match_state_offset(src);
bool status = bitstring_extract_integer(bs_bin, bs_offset, increment, flags_value, &value);
if (UNLIKELY(!status)) {
TRACE("bs_get_integer2: error extracting integer.\n");
JUMP_TO_ADDRESS(mod->labels[fail]);
} else {
term_set_match_state_offset(src, bs_offset + increment);
term t;
if (increment <= 64) {
bool status = bitstring_extract_integer(bs_bin, bs_offset, increment, flags_value, &value);
if (UNLIKELY(!status)) {
TRACE("bs_get_integer2: error extracting integer.\n");
JUMP_TO_ADDRESS(mod->labels[fail]);
} else {
term_set_match_state_offset(src, bs_offset + increment);

term t = maybe_alloc_boxed_integer_fragment(ctx, value.s);
if (UNLIKELY(term_is_invalid_term(t))) {
t = maybe_alloc_boxed_integer_fragment(ctx, value.s);
if (UNLIKELY(term_is_invalid_term(t))) {
HANDLE_ERROR();
}
}
} else if ((bs_offset % 8 == 0) && (increment % 8 == 0) && (increment <= INTN_MAX_UNSIGNED_BITS_SIZE)) {
unsigned long capacity = term_binary_size(bs_bin);
if (8 * capacity - bs_offset < (unsigned long) increment) {
JUMP_TO_ADDRESS(mod->labels[fail]);
}
size_t byte_offset = bs_offset / 8;
const uint8_t *int_bytes = (const uint8_t *) term_binary_data(bs_bin);

t = extract_nbits_integer(ctx, int_bytes + byte_offset, increment / 8,
bitstring_flags_to_intn_opts(flags_value));
term_set_match_state_offset(src, bs_offset + increment);
if (term_is_invalid_term(t)) {
HANDLE_ERROR();
}
} else {
JUMP_TO_ADDRESS(mod->labels[fail]);
}
#endif

DEST_REGISTER(dreg);
DECODE_DEST_REGISTER(dreg, pc);

#ifdef IMPL_EXECUTE_LOOP
WRITE_REGISTER(dreg, t);
}
WRITE_REGISTER(dreg, t);
#endif
break;
}
Expand Down Expand Up @@ -7273,15 +7294,35 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
avm_int_t size_val = term_to_int(size);
avm_int_t increment = size_val * unit;
union maybe_unsigned_int64 value;
bool status = bitstring_extract_integer(bs_bin, bs_offset, increment, flags_value, &value);
if (UNLIKELY(!status)) {
TRACE("bs_match/3: error extracting integer.\n");
term t;
if (increment <= 64) {
bool status = bitstring_extract_integer(bs_bin, bs_offset, increment, flags_value, &value);
if (UNLIKELY(!status)) {
TRACE("bs_match/3: error extracting integer.\n");
goto bs_match_jump_to_fail;
}
//FIXME: handling of 64-bit unsigned integers is not reliable
t = maybe_alloc_boxed_integer_fragment(ctx, value.s);
if (UNLIKELY(term_is_invalid_term(t))) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
} else if ((bs_offset % 8 == 0) && (increment % 8 == 0) && (increment <= INTN_MAX_UNSIGNED_BITS_SIZE)) {
unsigned long capacity = term_binary_size(bs_bin);
if (8 * capacity - bs_offset < (unsigned long) increment) {
goto bs_match_jump_to_fail;
}
size_t byte_offset = bs_offset / 8;
const uint8_t *int_bytes
= (const uint8_t *) term_binary_data(bs_bin);

t = extract_nbits_integer(ctx, int_bytes + byte_offset,
increment / 8, bitstring_flags_to_intn_opts(flags_value));
if (term_is_invalid_term(t)) {
HANDLE_ERROR();
}
} else {
goto bs_match_jump_to_fail;
}
term t = maybe_alloc_boxed_integer_fragment(ctx, value.s);
if (UNLIKELY(term_is_invalid_term(t))) {
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
}
#endif
DEST_REGISTER(dreg);
DECODE_DEST_REGISTER(dreg, pc);
Expand Down Expand Up @@ -7388,6 +7429,10 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
DECODE_LITERAL(pattern_value, pc);
j++;
#ifdef IMPL_EXECUTE_LOOP
if (size > 64) {
// TODO: implement support for big integers also here
RAISE_ERROR(BADARG_ATOM);
}
union maybe_unsigned_int64 matched_value;
bool status = bitstring_extract_integer(bs_bin, bs_offset, size, 0, &matched_value);
if (UNLIKELY(!status)) {
Expand Down
39 changes: 39 additions & 0 deletions tests/erlang_tests/bigint.erl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ start() ->
test_is_number() +
test_gt_lt_guards() +
to_external_term() +
test_pattern_match() +
test_band() +
test_bxor() +
test_bor() +
Expand Down Expand Up @@ -2138,6 +2139,44 @@ to_external_term() ->

0.

test_pattern_match() ->
<<Int72:72/integer-little-signed>> = ?MODULE:id(<<23, 4, 222, 66, 172, 197, 113, 183, 80>>),
<<"50B771C5AC42DE0417">> = erlang:integer_to_binary(?MODULE:id(Int72), 16),
<<Int80:80/integer-little-signed>> = ?MODULE:id(
<<165, 63, 196, 58, 33, 96, 209, 59, 244, 213>>
),
<<"-2A0BC42E9FDEC53BC05B">> = erlang:integer_to_binary(?MODULE:id(Int80), 16),
<<Int120:120/unsigned-big-integer>> = ?MODULE:id(
<<0, 242, 138, 221, 68, 111, 58, 120, 145, 135, 164, 56, 164, 12, 205>>
),
<<"F28ADD446F3A789187A438A40CCD">> = erlang:integer_to_binary(?MODULE:id(Int120), 16),
<<Int256:256/unsigned-big-integer>> = ?MODULE:id(
<<202, 196, 64, 150, 63, 238, 50, 47, 214, 81, 247, 55, 151, 242, 169, 106, 162, 211, 73,
155, 211, 85, 164, 237, 153, 138, 191, 77, 87, 183, 204, 111>>
),
<<"CAC440963FEE322FD651F73797F2A96AA2D3499BD355A4ED998ABF4D57B7CC6F">> = erlang:integer_to_binary(
?MODULE:id(Int256), 16
),

<<"foo", Int128:128/unsigned-little-integer, Bar/binary>> = ?MODULE:id(
<<102, 111, 111, 183, 226, 155, 102, 249, 246, 168, 101, 53, 36, 21, 10, 133, 223, 231, 10,
98, 97, 114>>
),
<<"AE7DF850A15243565A8F6F9669BE2B7">> = erlang:integer_to_binary(?MODULE:id(Int128), 16),
<<"bar">> = ?MODULE:id(Bar),

ok =
case
?MODULE:id(
<<102, 111, 111, 183, 226, 155, 102, 249, 246, 168, 101, 53, 36, 21, 10, 133, 223,
231>>
)
of
<<"foo", _I128:128/unsigned-little-integer, Bar/binary>> -> error;
_ -> ok
end,
0.

test_band() ->
MaxPatternBin = <<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>,
MaxPattern = erlang:binary_to_integer(?MODULE:id(MaxPatternBin), 16),
Expand Down
Loading