Skip to content
Merged
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
211 changes: 103 additions & 108 deletions src/libAtomVM/bif.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@
#define INT64_MAX_AS_AVM_FLOAT 9223372036854775295.0 // 0x43DFFFFFFFFFFFFF = 2^62 * 1.1...1b
#endif

static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live,
const intn_digit_t bigres[], size_t bigres_len, intn_integer_sign_t sign);

static void conv_term_to_bigint(term arg1, intn_digit_t *tmp_buf1, const intn_digit_t **b1,
size_t *b1_len, intn_integer_sign_t *b1_sign);

const struct ExportedFunction *bif_registry_get_handler(const char *mfa)
{
const BifNameAndPtr *nameAndPtr = in_word_set(mfa, strlen(mfa));
Expand Down Expand Up @@ -530,6 +524,56 @@ static inline term make_maybe_boxed_int64(Context *ctx, uint32_t fail_label, uin
}
#endif

// this function assumes that bigres_len is always <= bigres buffer capacity
static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live,
const intn_digit_t bigres[], size_t bigres_len, intn_integer_sign_t sign)
{
size_t count = intn_count_digits(bigres, bigres_len);

if (UNLIKELY(count > INTN_MAX_IN_LEN)) {
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
}

if (!intn_fits_int64(bigres, count, sign)) {
size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);

if (UNLIKELY(memory_ensure_free_with_roots(
ctx, BOXED_BIGINT_HEAP_SIZE(intn_data_size), live, ctx->x, MEMORY_CAN_SHRINK)
!= MEMORY_GC_OK)) {
RAISE_ERROR_BIF(fail_label, OUT_OF_MEMORY_ATOM);
}

term bigres_term = term_create_uninitialized_bigint(
intn_data_size, (term_integer_sign_t) sign, &ctx->heap);
term_initialize_bigint(bigres_term, bigres, count, rounded_res_len);

return bigres_term;
} else {
int64_t res64 = intn_to_int64(bigres, count, sign);
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
return make_maybe_boxed_int64(ctx, fail_label, live, res64);
#else
return make_maybe_boxed_int(ctx, fail_label, live, res64);
#endif
}
}

static void conv_term_to_bigint(term t, intn_digit_t *tmp_buf, const intn_digit_t **bigint,
size_t *bigint_len, intn_integer_sign_t *bigint_sign)
{
if (term_is_bigint(t)) {
term_to_bigint(t, bigint, bigint_len, bigint_sign);

} else {
avm_int64_t i64 = term_maybe_unbox_int64(t);
intn_from_int64(i64, tmp_buf, bigint_sign);
*bigint = tmp_buf;
*bigint_len = INTN_INT64_LEN;
}
}

static term add_int64_to_bigint(
Context *ctx, uint32_t fail_label, uint32_t live, int64_t val1, int64_t val2)
{
Expand All @@ -546,23 +590,23 @@ static term add_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t
intn_digit_t tmp_buf1[INTN_INT64_LEN];
intn_digit_t tmp_buf2[INTN_INT64_LEN];

const intn_digit_t *bn1;
size_t bn1_len;
intn_integer_sign_t bn1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign);
const intn_digit_t *bn2;
size_t bn2_len;
intn_integer_sign_t bn2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign);
const intn_digit_t *big1;
size_t big1_len;
intn_integer_sign_t big1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &big1, &big1_len, &big1_sign);
const intn_digit_t *big2;
size_t big2_len;
intn_integer_sign_t big2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &big2, &big2_len, &big2_sign);

size_t bigres_len = INTN_ADD_OUT_LEN(bn1_len, bn2_len);
size_t bigres_len = INTN_ADD_OUT_LEN(big1_len, big2_len);
if (bigres_len > INTN_MAX_RES_LEN) {
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
}

intn_digit_t bigres[INTN_MAX_RES_LEN];
intn_integer_sign_t res_sign;
bigres_len = intn_add(bn1, bn1_len, bn1_sign, bn2, bn2_len, bn2_sign, bigres, &res_sign);
bigres_len = intn_add(big1, big1_len, big1_sign, big2, big2_len, big2_sign, bigres, &res_sign);

return make_bigint(ctx, fail_label, live, bigres, bigres_len, res_sign);
}
Expand Down Expand Up @@ -684,23 +728,23 @@ static term sub_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t
intn_digit_t tmp_buf1[INTN_INT64_LEN];
intn_digit_t tmp_buf2[INTN_INT64_LEN];

const intn_digit_t *bn1;
size_t bn1_len;
intn_integer_sign_t bn1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign);
const intn_digit_t *bn2;
size_t bn2_len;
intn_integer_sign_t bn2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign);
const intn_digit_t *big1;
size_t big1_len;
intn_integer_sign_t big1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &big1, &big1_len, &big1_sign);
const intn_digit_t *big2;
size_t big2_len;
intn_integer_sign_t big2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &big2, &big2_len, &big2_sign);

size_t bigres_len = INTN_SUB_OUT_LEN(bn1_len, bn2_len);
size_t bigres_len = INTN_SUB_OUT_LEN(big1_len, big2_len);
if (bigres_len > INTN_MAX_RES_LEN) {
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
}

intn_digit_t bigres[INTN_MAX_RES_LEN];
intn_integer_sign_t res_sign;
bigres_len = intn_sub(bn1, bn1_len, bn1_sign, bn2, bn2_len, bn2_sign, bigres, &res_sign);
bigres_len = intn_sub(big1, big1_len, big1_sign, big2, big2_len, big2_sign, bigres, &res_sign);

return make_bigint(ctx, fail_label, live, bigres, bigres_len, res_sign);
}
Expand Down Expand Up @@ -796,56 +840,6 @@ term bif_erlang_sub_2(Context *ctx, uint32_t fail_label, int live, term arg1, te
}
}

// this function assumes that bigres_len is always <= bigres buffer capacity
static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live,
const intn_digit_t bigres[], size_t bigres_len, intn_integer_sign_t sign)
{
size_t count = intn_count_digits(bigres, bigres_len);

if (UNLIKELY(count > INTN_MAX_IN_LEN)) {
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
}

if (!intn_fits_int64(bigres, count, sign)) {
size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);

if (UNLIKELY(memory_ensure_free_with_roots(
ctx, BOXED_BIGINT_HEAP_SIZE(intn_data_size), live, ctx->x, MEMORY_CAN_SHRINK)
!= MEMORY_GC_OK)) {
RAISE_ERROR_BIF(fail_label, OUT_OF_MEMORY_ATOM);
}

term bigres_term = term_create_uninitialized_bigint(
intn_data_size, (term_integer_sign_t) sign, &ctx->heap);
term_initialize_bigint(bigres_term, bigres, count, rounded_res_len);

return bigres_term;
} else {
int64_t res64 = intn_to_int64(bigres, count, sign);
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
return make_maybe_boxed_int64(ctx, fail_label, live, res64);
#else
return make_maybe_boxed_int(ctx, fail_label, live, res64);
#endif
}
}

static void conv_term_to_bigint(term arg1, intn_digit_t *tmp_buf1, const intn_digit_t **b1,
size_t *b1_len, intn_integer_sign_t *b1_sign)
{
if (term_is_bigint(arg1)) {
term_to_bigint(arg1, b1, b1_len, b1_sign);

} else {
avm_int64_t i64 = term_maybe_unbox_int64(arg1);
intn_from_int64(i64, tmp_buf1, b1_sign);
*b1 = tmp_buf1;
*b1_len = INTN_INT64_LEN;
}
}

static term mul_int64_to_bigint(
Context *ctx, uint32_t fail_label, uint32_t live, int64_t val1, int64_t val2)
{
Expand All @@ -862,23 +856,23 @@ static term mul_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t
intn_digit_t tmp_buf1[INTN_INT64_LEN];
intn_digit_t tmp_buf2[INTN_INT64_LEN];

const intn_digit_t *bn1;
size_t bn1_len;
intn_integer_sign_t bn1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign);
const intn_digit_t *bn2;
size_t bn2_len;
intn_integer_sign_t bn2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign);
const intn_digit_t *big1;
size_t big1_len;
intn_integer_sign_t big1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &big1, &big1_len, &big1_sign);
const intn_digit_t *big2;
size_t big2_len;
intn_integer_sign_t big2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &big2, &big2_len, &big2_sign);

size_t bigres_len = INTN_MUL_OUT_LEN(bn1_len, bn2_len);
size_t bigres_len = INTN_MUL_OUT_LEN(big1_len, big2_len);
if (bigres_len > INTN_MAX_RES_LEN) {
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
}

intn_digit_t bigres[INTN_MAX_RES_LEN];
intn_integer_sign_t res_sign;
intn_mul(bn1, bn1_len, bn1_sign, bn2, bn2_len, bn2_sign, bigres, &res_sign);
intn_mul(big1, big1_len, big1_sign, big2, big2_len, big2_sign, bigres, &res_sign);

return make_bigint(ctx, fail_label, live, bigres, bigres_len, res_sign);
}
Expand Down Expand Up @@ -1026,27 +1020,28 @@ static term div_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t
intn_digit_t tmp_buf1[INTN_INT64_LEN];
intn_digit_t tmp_buf2[INTN_INT64_LEN];

const intn_digit_t *bn1;
size_t bn1_len;
intn_integer_sign_t bn1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign);
const intn_digit_t *bn2;
size_t bn2_len;
intn_integer_sign_t bn2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign);
const intn_digit_t *big1;
size_t big1_len;
intn_integer_sign_t big1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &big1, &big1_len, &big1_sign);
const intn_digit_t *big2;
size_t big2_len;
intn_integer_sign_t big2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &big2, &big2_len, &big2_sign);

int cmp_result = intn_cmp(bn1, bn1_len, bn2, bn2_len);
int cmp_result = intn_cmp(big1, big1_len, big2, big2_len);
if (cmp_result < 0) {
// a / b when a < b -> always 0
return term_from_int(0);
} else if (cmp_result == 0) {
// a / b when a == b -> always +-1
return (bn1_sign == bn2_sign) ? term_from_int(1) : term_from_int(-1);
return (big1_sign == big2_sign) ? term_from_int(1) : term_from_int(-1);
}

intn_digit_t bigres[INTN_MAX_RES_LEN];
intn_integer_sign_t res_sign;
size_t bigres_len = intn_div(bn1, bn1_len, bn1_sign, bn2, bn2_len, bn2_sign, bigres, &res_sign, NULL, NULL);
size_t bigres_len = intn_div(
big1, big1_len, big1_sign, big2, big2_len, big2_sign, bigres, &res_sign, NULL, NULL);

return make_bigint(ctx, fail_label, live, bigres, bigres_len, res_sign);
}
Expand Down Expand Up @@ -1348,16 +1343,16 @@ static term rem_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t
intn_digit_t tmp_buf1[INTN_INT64_LEN];
intn_digit_t tmp_buf2[INTN_INT64_LEN];

const intn_digit_t *bn1;
size_t bn1_len;
intn_integer_sign_t bn1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign);
const intn_digit_t *bn2;
size_t bn2_len;
intn_integer_sign_t bn2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign);
const intn_digit_t *big1;
size_t big1_len;
intn_integer_sign_t big1_sign;
conv_term_to_bigint(arg1, tmp_buf1, &big1, &big1_len, &big1_sign);
const intn_digit_t *big2;
size_t big2_len;
intn_integer_sign_t big2_sign;
conv_term_to_bigint(arg2, tmp_buf2, &big2, &big2_len, &big2_sign);

int cmp_result = intn_cmp(bn1, bn1_len, bn2, bn2_len);
int cmp_result = intn_cmp(big1, big1_len, big2, big2_len);
if (cmp_result < 0) {
// a rem b when |a| < |b| -> always a
return arg1;
Expand All @@ -1369,9 +1364,9 @@ static term rem_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t
intn_digit_t q[INTN_MAX_RES_LEN];
intn_digit_t bigres[INTN_MAX_RES_LEN];
size_t bigres_len;
intn_divu(bn1, bn1_len, bn2, bn2_len, q, bigres, &bigres_len);
intn_divu(big1, big1_len, big2, big2_len, q, bigres, &bigres_len);

return make_bigint(ctx, fail_label, live, bigres, bigres_len, bn1_sign);
return make_bigint(ctx, fail_label, live, bigres, bigres_len, big1_sign);
}

static term rem_boxed_helper(Context *ctx, uint32_t fail_label, uint32_t live, term arg1, term arg2)
Expand Down
Loading