Skip to content

Commit 3fa7ca2

Browse files
committed
WIP
Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent ff800f7 commit 3fa7ca2

File tree

2 files changed

+102
-6
lines changed

2 files changed

+102
-6
lines changed

src/libAtomVM/bif.c

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <math.h>
2525

2626
#include "atom.h"
27+
#include "intn.h"
2728
#include "bitstring.h"
2829
#include "defaultatoms.h"
2930
#include "dictionary.h"
@@ -618,6 +619,102 @@ term bif_erlang_sub_2(Context *ctx, uint32_t fail_label, int live, term arg1, te
618619
}
619620
}
620621

622+
static inline void intn_to_term_size(size_t n, size_t *intn_data_size, size_t *rounded_num_len)
623+
{
624+
size_t bytes = n * sizeof(intn_digit_t);
625+
size_t rounded = ((bytes + 7) >> 3) << 3;
626+
*intn_data_size = rounded / sizeof(term);
627+
*rounded_num_len = rounded / sizeof(intn_digit_t);
628+
}
629+
630+
static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live,
631+
const intn_digit_t bigres[], size_t bigres_len)
632+
{
633+
size_t count = intn_count_digits(bigres, bigres_len);
634+
635+
if (UNLIKELY(count > INTN_MAX_IN_LEN)) {
636+
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
637+
}
638+
639+
if (count > INTN_INT64_LEN) {
640+
size_t intn_data_size;
641+
size_t rounded_res_len;
642+
intn_to_term_size(count, &intn_data_size, &rounded_res_len);
643+
644+
if (UNLIKELY(
645+
memory_ensure_free_with_roots(ctx, BOXED_INTN_SIZE(intn_data_size), live, ctx->x, MEMORY_CAN_SHRINK)
646+
!= MEMORY_GC_OK)) {
647+
RAISE_ERROR_BIF(fail_label, OUT_OF_MEMORY_ATOM);
648+
}
649+
650+
term bigres_term = term_create_uninitialized_intn(intn_data_size, &ctx->heap);
651+
intn_digit_t *dest_buf = (void *) term_intn_data(bigres_term);
652+
intn_sign_extend(bigres, count, rounded_res_len, dest_buf);
653+
654+
return bigres_term;
655+
} else {
656+
int64_t res64 = intn_2_digits_to_int64(bigres, count);
657+
#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1
658+
return make_maybe_boxed_int64(ctx, fail_label, live, res64);
659+
#else
660+
return make_maybe_boxed_int(ctx, fail_label, live, res64);
661+
#endif
662+
}
663+
}
664+
665+
static void term_to_big_int(term arg1, intn_digit_t *tmp_buf1, intn_digit_t **b1, size_t *b1_len)
666+
{
667+
if (term_is_boxed_integer(arg1)
668+
&& (term_boxed_size(arg1) > (INTN_INT64_LEN * sizeof(intn_digit_t)) / sizeof(term))) {
669+
*b1 = term_intn_data(arg1);
670+
*b1_len = term_intn_size(arg1) * (sizeof(term) / sizeof(intn_digit_t));
671+
} else {
672+
avm_int64_t i64 = term_maybe_unbox_int64(arg1);
673+
int64_to_intn_2(i64, tmp_buf1);
674+
*b1 = tmp_buf1;
675+
*b1_len = INTN_INT64_LEN;
676+
}
677+
}
678+
679+
static void pair_to_big_int(term arg1, term arg2, intn_digit_t *tmp_buf1, intn_digit_t *tmp_buf2,
680+
intn_digit_t **b1, size_t *b1_len, intn_digit_t **b2, size_t *b2_len)
681+
{
682+
term_to_big_int(arg1, tmp_buf1, b1, b1_len);
683+
term_to_big_int(arg2, tmp_buf2, b2, b2_len);
684+
}
685+
686+
static term mul_int64_to_bigint(
687+
Context *ctx, uint32_t fail_label, uint32_t live, int64_t val1, int64_t val2)
688+
{
689+
size_t mul_out_len = INTN_MUL_OUT_LEN(INTN_INT64_LEN, INTN_INT64_LEN);
690+
intn_digit_t mul_out[mul_out_len];
691+
intn_mul_int64(val1, val2, mul_out);
692+
return make_bigint(ctx, fail_label, live, mul_out, mul_out_len);
693+
}
694+
695+
static term mul_maybe_bigint(
696+
Context *ctx, uint32_t fail_label, uint32_t live, term arg1, term arg2)
697+
{
698+
intn_digit_t tmp_buf1[INTN_INT64_LEN];
699+
intn_digit_t tmp_buf2[INTN_INT64_LEN];
700+
701+
intn_digit_t *bn1;
702+
size_t bn1_len;
703+
intn_digit_t *bn2;
704+
size_t bn2_len;
705+
pair_to_big_int(arg1, arg2, tmp_buf1, tmp_buf2, &bn1, &bn1_len, &bn2, &bn2_len);
706+
707+
size_t bigres_len = INTN_MUL_OUT_LEN(bn1_len, bn2_len);
708+
if (bigres_len > INTN_MAX_RES_LEN) {
709+
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
710+
}
711+
712+
intn_digit_t bigres[INTN_MAX_RES_LEN];
713+
intn_mulmns(bn1, bn1_len, bn2, bn2_len, bigres);
714+
715+
return make_bigint(ctx, fail_label, live, bigres, bigres_len);
716+
}
717+
621718
static term mul_overflow_helper(Context *ctx, uint32_t fail_label, uint32_t live, term arg1, term arg2)
622719
{
623720
avm_int_t val1 = term_to_int(arg1);
@@ -637,7 +734,7 @@ static term mul_overflow_helper(Context *ctx, uint32_t fail_label, uint32_t live
637734
#endif
638735

639736
} else {
640-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
737+
return mul_int64_to_bigint(ctx, fail_label, live, val1, val2);
641738
}
642739
}
643740

@@ -665,8 +762,7 @@ static term mul_boxed_helper(Context *ctx, uint32_t fail_label, uint32_t live, t
665762
return make_boxed_int64(ctx, fail_label, live, res64);
666763

667764
#elif BOXED_TERMS_REQUIRED_FOR_INT64 == 1
668-
TRACE("overflow: arg1: " AVM_INT64_FMT ", arg2: " AVM_INT64_FMT "\n", arg1, arg2);
669-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
765+
return mul_int64_to_bigint(ctx, fail_label, live, val1, val2);
670766
#else
671767
#error "Unsupported configuration."
672768
#endif
@@ -682,16 +778,15 @@ static term mul_boxed_helper(Context *ctx, uint32_t fail_label, uint32_t live, t
682778
avm_int64_t res;
683779

684780
if (BUILTIN_MUL_OVERFLOW_INT64(val1, val2, &res)) {
685-
TRACE("overflow: arg1: 0x%lx, arg2: 0x%lx\n", arg1, arg2);
686-
RAISE_ERROR_BIF(fail_label, OVERFLOW_ATOM);
781+
return mul_int64_to_bigint(ctx, fail_label, live, val1, val2);
687782
}
688783

689784
return make_maybe_boxed_int64(ctx, fail_label, live, res);
690785
}
691786
#endif
692787

693788
default:
694-
UNREACHABLE();
789+
return mul_maybe_bigint(ctx, fail_label, live, arg1, arg2);
695790
}
696791
} else {
697792
avm_float_t farg1 = term_conv_to_float(arg1);

src/libAtomVM/term.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ extern "C" {
8484
#define FUNCTION_REFERENCE_SIZE 4
8585
#define BOXED_INT_SIZE (BOXED_TERMS_REQUIRED_FOR_INT + 1)
8686
#define BOXED_INT64_SIZE (BOXED_TERMS_REQUIRED_FOR_INT64 + 1)
87+
#define BOXED_INTN_SIZE(term_size) ((term_size) + 1)
8788
#define BOXED_FUN_SIZE 3
8889
#define FLOAT_SIZE (sizeof(float_term_t) / sizeof(term) + 1)
8990
#define REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1))

0 commit comments

Comments
 (0)