From d822f18a1cf6c48ec72c469d631bf3b0adc392be Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 18 May 2025 16:48:33 +0200 Subject: [PATCH 01/27] WIP: refactor bitwise ops Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index f0995657d..482352696 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1428,23 +1428,28 @@ term bif_erlang_float_1(Context *ctx, uint32_t fail_label, int live, term arg1) typedef int64_t (*bitwise_op)(int64_t a, int64_t b); -static inline term bitwise_helper(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2, bitwise_op op) +static inline term bitwise_helper( + Context *ctx, uint32_t fail_label, int live, term arg1, term arg2, bitwise_op op) { - UNUSED(live); + if (LIKELY(term_is_any_integer(arg1) && term_is_any_integer(arg2))) { + size_t arg1_size = term_is_integer(arg1) ? 0 : term_boxed_size(arg1); + size_t arg2_size = term_is_integer(arg2) ? 0 : term_boxed_size(arg2); + if (MAX(arg1_size, arg2_size) <= BOXED_TERMS_REQUIRED_FOR_INT64) { + int64_t a = term_maybe_unbox_int64(arg1); + int64_t b = term_maybe_unbox_int64(arg2); + int64_t result = op(a, b); - if (UNLIKELY(!term_is_any_integer(arg1) || !term_is_any_integer(arg2))) { +#if BOXED_TERMS_REQUIRED_FOR_INT64 > 1 + return make_maybe_boxed_int64(ctx, fail_label, live, result); +#else + return make_maybe_boxed_int(ctx, fail_label, live, result); +#endif + } else { + abort(); + } + } else { RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); } - - int64_t a = term_maybe_unbox_int64(arg1); - int64_t b = term_maybe_unbox_int64(arg2); - int64_t result = op(a, b); - - #if BOXED_TERMS_REQUIRED_FOR_INT64 > 1 - return make_maybe_boxed_int64(ctx, fail_label, live, result); - #else - return make_maybe_boxed_int(ctx, fail_label, live, result); - #endif } static inline int64_t bor(int64_t a, int64_t b) From 9796c01dbde526d1c2532f645e0c7ee712064c71 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 25 May 2025 23:26:07 +0200 Subject: [PATCH 02/27] WIP bormn Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 101 ++++++++++++++++++++++++++++++++++ tests/erlang_tests/bigint.erl | 67 +++++++++++++++++++++- 2 files changed, 167 insertions(+), 1 deletion(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 240dc4194..db97bfb80 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -380,6 +380,107 @@ size_t intn_addmnu( return i; } +static void cond_neg( + intn_integer_sign_t sign, const intn_digit_t in[], size_t in_len, intn_digit_t out[]) +{ + if (sign == IntNPositiveInteger) { + memcpy(out, in, sizeof(intn_digit_t) * in_len); + } else { + uint32_t carry = 1; + for (size_t i = 0; i < in_len; i++) { + uint64_t temp = (uint64_t) (~in[i]) + (uint64_t) carry; + out[i] = (uint32_t) temp; + carry = temp >> 32; + } + } +} + +static size_t prepare_out(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, const intn_digit_t *b[], + size_t *b_len, intn_integer_sign_t *b_sign, intn_digit_t out[]) +{ + const intn_digit_t *longest; + size_t longest_len; + intn_integer_sign_t longest_sign; + + if (m_len > n_len) { + longest = m; + longest_len = m_len; + longest_sign = m_sign; + *b = n; + *b_len = n_len; + *b_sign = n_sign; + } else { + longest = n; + longest_len = n_len; + longest_sign = n_sign; + *b = m; + *b_len = m_len; + *b_sign = m_sign; + } + + cond_neg(longest_sign, longest, longest_len, out); + if (longest_sign == IntNPositiveInteger) { + out[longest_len] = 0; + } else { + out[longest_len] = UINT32_MAX; + } + return longest_len + 1; +} + +static inline uint32_t bit_op(uint32_t a, uint32_t b) { return a | b; } + +static inline size_t neg_and_bitwise(const intn_digit_t b[], size_t b_len, + intn_integer_sign_t b_sign, intn_digit_t out[], size_t out_len) +{ + if (b_sign == IntNPositiveInteger) { + for (size_t i = 0; i < b_len; i++) { + out[i] = bit_op(out[i], b[i]); + } + for (size_t i = b_len; i < out_len; i++) { + out[i] = bit_op(out[i], 0); + } + } else { + uint32_t carry = 1; + for (size_t i = 0; i < b_len; i++) { + uint64_t temp = (uint64_t) (~b[i]) + (uint64_t) carry; + out[i] = bit_op(out[i], (uint32_t) temp); + carry = temp >> 32; + } + if (b_len < out_len) { + out[b_len] = bit_op(out[b_len], (UINT32_MAX) + carry); + } + for (size_t i = b_len + 1; i < out_len; i++) { + out[i] = bit_op(out[i], UINT32_MAX); + } + } + return out_len; +} + +size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign) +{ + intn_digit_t working_buf[INTN_MAX_IN_LEN + 1]; + + const intn_digit_t *b; + size_t b_len; + intn_integer_sign_t b_sign; + + size_t count + = prepare_out(m, m_len, m_sign, n, n_len, n_sign, &b, &b_len, &b_sign, working_buf); + + neg_and_bitwise(b, b_len, b_sign, working_buf, count); + + *out_sign = (intn_integer_sign_t) ((unsigned int) m_sign | (unsigned int) n_sign) + & IntNNegativeInteger; + + size_t ret_count = count; // MAX(count, count2); + cond_neg(*out_sign, working_buf, ret_count, out); + + return ret_count; +} + size_t intn_count_digits(const intn_digit_t *num, size_t num_len) { int i; diff --git a/tests/erlang_tests/bigint.erl b/tests/erlang_tests/bigint.erl index b7434e870..2583cf002 100644 --- a/tests/erlang_tests/bigint.erl +++ b/tests/erlang_tests/bigint.erl @@ -53,7 +53,8 @@ start() -> conv_to_from_float() + external_term_decode() + big_literals() + - to_external_term(). + to_external_term() + + test_bor(). test_mul() -> Expected_INT64_MIN = ?MODULE:pow(-2, 63), @@ -790,6 +791,70 @@ to_external_term() -> 0. +test_bor() -> + Pattern1 = erlang:binary_to_integer( + ?MODULE:id( + <<"10101010101010101010101010101010101010101010101010101010101010100000000000000000">> + ), + 2 + ), + Pattern2 = erlang:binary_to_integer( + ?MODULE:id( + <<"1010101010101010101010101010101010101010101010101010101010101010000000000000000">> + ), + 2 + ), + Res1 = erlang:binary_to_integer( + ?MODULE:id( + <<"11111111111111111111111111111111111111111111111111111111111111110000000000000000">> + ), + 2 + ), + Res1 = Pattern1 bor Pattern2, + + Pattern3 = erlang:binary_to_integer(?MODULE:id(<<"-1">>), 2), + Res2 = ?MODULE:id(-1), + Res2 = Pattern1 bor Pattern3, + + Pattern4 = erlang:binary_to_integer(?MODULE:id(<<"-5555555511111111123456789ABCDEF0">>), 16), + Pattern5 = erlang:binary_to_integer(?MODULE:id(<<"+30303030333333333111111111111111">>), 16), + Res3 = erlang:binary_to_integer(?MODULE:id(<<"-4545454500000000022446688AACCEEF">>), 16), + Res3 = Pattern4 bor Pattern5, + + Pattern6 = erlang:binary_to_integer(?MODULE:id(<<"-30303030333333333111111111111111">>), 16), + Res4 = erlang:binary_to_integer(?MODULE:id(<<"-10101010111111111010101010101001">>), 16), + Res4 = Pattern4 bor Pattern6, + + Pattern7 = erlang:binary_to_integer( + ?MODULE:id(<<"-8000000000000000000000000000000000000000000000000000000000000000">>), 16 + ), + Res5 = ?MODULE:id(-1), + Res5 = ?MODULE:id(Pattern7) bor ?MODULE:id(-1), + + Res6 = Pattern4, + Res6 = ?MODULE:id(Pattern4) bor ?MODULE:id(Pattern7), + + Res7 = erlang:binary_to_integer( + ?MODULE:id(<<"-7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>), 16 + ), + Res7 = ?MODULE:id(Pattern7) bor ?MODULE:id(1), + + Pattern8 = erlang:binary_to_integer( + ?MODULE:id(<<"5555555555555555555555555555555555555555555555555555555555555555">>), 16 + ), + Pattern9 = erlang:binary_to_integer( + ?MODULE:id(<<"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA">>), 16 + ), + Res8 = erlang:binary_to_integer( + ?MODULE:id(<<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>), 16 + ), + Res8 = Pattern8 bor Pattern9, + + Res9 = ?MODULE:id(-1), + Res9 = ?MODULE:id(-1) bor Res8, + + 0. + id(X) -> X. From 8914d6c43ff6a3c74857d2728037744d51f6e8e8 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 25 May 2025 23:26:59 +0200 Subject: [PATCH 03/27] WIP bigint bitwise bif Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index 482352696..72a4efb02 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1427,9 +1427,13 @@ term bif_erlang_float_1(Context *ctx, uint32_t fail_label, int live, term arg1) } typedef int64_t (*bitwise_op)(int64_t a, int64_t b); +typedef size_t (*bitwise_big_op)( + const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, + intn_digit_t out[], intn_integer_sign_t *out_sign); static inline term bitwise_helper( - Context *ctx, uint32_t fail_label, int live, term arg1, term arg2, bitwise_op op) + Context *ctx, uint32_t fail_label, int live, term arg1, term arg2, bitwise_op op, bitwise_big_op big_op) { if (LIKELY(term_is_any_integer(arg1) && term_is_any_integer(arg2))) { size_t arg1_size = term_is_integer(arg1) ? 0 : term_boxed_size(arg1); @@ -1445,7 +1449,21 @@ static inline term bitwise_helper( return make_maybe_boxed_int(ctx, fail_label, live, result); #endif } else { - abort(); + intn_digit_t tmp_buf1[INTN_INT64_LEN]; + intn_digit_t tmp_buf2[INTN_INT64_LEN]; + intn_digit_t *m; + size_t m_len; + intn_integer_sign_t m_sign; + intn_digit_t *n; + size_t n_len; + intn_integer_sign_t n_sign; + args_to_bigint(arg1, arg2, tmp_buf1, tmp_buf2, &m, &m_len, &m_sign, &n, &n_len, &n_sign); + + intn_digit_t bigres[INTN_MAX_RES_LEN]; + intn_integer_sign_t bigres_sign; + size_t bigres_len = big_op(m, m_len, m_sign, n, n_len, n_sign, bigres, &bigres_sign); + + return make_bigint(ctx, fail_label, live, bigres, bigres_len, bigres_sign); } } else { RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); @@ -1462,7 +1480,7 @@ term bif_erlang_bor_2(Context *ctx, uint32_t fail_label, int live, term arg1, te if (LIKELY(term_is_integer(arg1) && term_is_integer(arg2))) { return arg1 | arg2; } else { - return bitwise_helper(ctx, fail_label, live, arg1, arg2, bor); + return bitwise_helper(ctx, fail_label, live, arg1, arg2, bor, intn_bormn); } } @@ -1476,7 +1494,7 @@ term bif_erlang_band_2(Context *ctx, uint32_t fail_label, int live, term arg1, t if (LIKELY(term_is_integer(arg1) && term_is_integer(arg2))) { return arg1 & arg2; } else { - return bitwise_helper(ctx, fail_label, live, arg1, arg2, band); + return bitwise_helper(ctx, fail_label, live, arg1, arg2, band, NULL); } } @@ -1490,7 +1508,7 @@ term bif_erlang_bxor_2(Context *ctx, uint32_t fail_label, int live, term arg1, t if (LIKELY(term_is_integer(arg1) && term_is_integer(arg2))) { return (arg1 ^ arg2) | TERM_INTEGER_TAG; } else { - return bitwise_helper(ctx, fail_label, live, arg1, arg2, bxor); + return bitwise_helper(ctx, fail_label, live, arg1, arg2, bxor, NULL); } } From 5e275273052adccfcb1712c313e6d8173e79f284 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 25 May 2025 23:27:13 +0200 Subject: [PATCH 04/27] WIP add missing header Signed-off-by: Davide Bettio --- src/libAtomVM/intn.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index ee41a19f2..8928a10e8 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -91,6 +91,10 @@ int intn_to_integer_bytes(const intn_digit_t in[], size_t in_len, intn_integer_s size_t intn_required_unsigned_integer_bytes(const intn_digit_t in[], size_t in_len); +size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign); + static inline void intn_copy( const intn_digit_t *num, size_t num_len, intn_digit_t *out, size_t extend_to) { From 536416a83400cfd4d66961d9890c77cf6c0fbf36 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 26 May 2025 18:00:16 +0200 Subject: [PATCH 05/27] WIP intn bor Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 44 ++++++++++++++++++++++++-------------------- src/libAtomVM/intn.h | 8 ++++---- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index db97bfb80..0eb9338c4 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -395,7 +395,7 @@ static void cond_neg( } } -static size_t prepare_out(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, +static size_t prepare_working_buf(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, const intn_digit_t *b[], size_t *b_len, intn_integer_sign_t *b_sign, intn_digit_t out[]) { @@ -420,18 +420,13 @@ static size_t prepare_out(const intn_digit_t m[], size_t m_len, intn_integer_sig } cond_neg(longest_sign, longest, longest_len, out); - if (longest_sign == IntNPositiveInteger) { - out[longest_len] = 0; - } else { - out[longest_len] = UINT32_MAX; - } - return longest_len + 1; + return longest_len; } -static inline uint32_t bit_op(uint32_t a, uint32_t b) { return a | b; } +typedef intn_digit_t (*bit_op_t)(intn_digit_t a, intn_digit_t b); -static inline size_t neg_and_bitwise(const intn_digit_t b[], size_t b_len, - intn_integer_sign_t b_sign, intn_digit_t out[], size_t out_len) +static inline void signed_bitwise(const intn_digit_t b[], size_t b_len, intn_integer_sign_t b_sign, + intn_digit_t out[], size_t out_len, bit_op_t bit_op) { if (b_sign == IntNPositiveInteger) { for (size_t i = 0; i < b_len; i++) { @@ -454,31 +449,40 @@ static inline size_t neg_and_bitwise(const intn_digit_t b[], size_t b_len, out[i] = bit_op(out[i], UINT32_MAX); } } - return out_len; +} + +static inline intn_integer_sign_t sign_bitwise( + intn_integer_sign_t m_sign, intn_integer_sign_t n_sign, bit_op_t bit_op) +{ + return (intn_integer_sign_t) bit_op((unsigned int) m_sign, (unsigned int) n_sign) + & IntNNegativeInteger; +} + +static inline intn_digit_t digit_bor(intn_digit_t a, intn_digit_t b) +{ + return a | b; } size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], intn_integer_sign_t *out_sign) { - intn_digit_t working_buf[INTN_MAX_IN_LEN + 1]; + intn_digit_t working_buf[INTN_MAX_IN_LEN]; const intn_digit_t *b; size_t b_len; intn_integer_sign_t b_sign; size_t count - = prepare_out(m, m_len, m_sign, n, n_len, n_sign, &b, &b_len, &b_sign, working_buf); + = prepare_working_buf(m, m_len, m_sign, n, n_len, n_sign, &b, &b_len, &b_sign, working_buf); - neg_and_bitwise(b, b_len, b_sign, working_buf, count); + signed_bitwise(b, b_len, b_sign, working_buf, count, digit_bor); + intn_integer_sign_t res_sign = sign_bitwise(m_sign, n_sign, digit_bor); - *out_sign = (intn_integer_sign_t) ((unsigned int) m_sign | (unsigned int) n_sign) - & IntNNegativeInteger; - - size_t ret_count = count; // MAX(count, count2); - cond_neg(*out_sign, working_buf, ret_count, out); + cond_neg(res_sign, working_buf, count, out); + *out_sign = res_sign; - return ret_count; + return count; } size_t intn_count_digits(const intn_digit_t *num, size_t num_len) diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index 8928a10e8..5a286fb67 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -73,6 +73,10 @@ void intn_mul_int64(int64_t num1, int64_t num2, intn_digit_t *out, intn_integer_ void print_num(const uint32_t num[], int len); +size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign); + size_t intn_count_digits(const intn_digit_t *num, size_t num_len); char *intn_to_string(const intn_digit_t *num, size_t len, intn_integer_sign_t num_sign, int base, @@ -91,10 +95,6 @@ int intn_to_integer_bytes(const intn_digit_t in[], size_t in_len, intn_integer_s size_t intn_required_unsigned_integer_bytes(const intn_digit_t in[], size_t in_len); -size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, - const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], - intn_integer_sign_t *out_sign); - static inline void intn_copy( const intn_digit_t *num, size_t num_len, intn_digit_t *out, size_t extend_to) { From d569fb9cbc8bded9f79c6edf80bc6676d27ad540 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 26 May 2025 18:09:21 +0200 Subject: [PATCH 06/27] all the other bitwise Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 67 ++++++++++++++++++++++++++++++++++++++++++++ src/libAtomVM/intn.h | 8 ++++++ 2 files changed, 75 insertions(+) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 0eb9338c4..6961c5e40 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -485,6 +485,73 @@ size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_si return count; } +static inline intn_digit_t digit_band(intn_digit_t a, intn_digit_t b) +{ + return a & b; +} + +size_t intn_bandmn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign) +{ + intn_digit_t working_buf[INTN_MAX_IN_LEN]; + + const intn_digit_t *b; + size_t b_len; + intn_integer_sign_t b_sign; + + size_t count + = prepare_working_buf(m, m_len, m_sign, n, n_len, n_sign, &b, &b_len, &b_sign, working_buf); + + signed_bitwise(b, b_len, b_sign, working_buf, count, digit_bor); + intn_integer_sign_t res_sign = sign_bitwise(m_sign, n_sign, digit_band); + + cond_neg(res_sign, working_buf, count, out); + *out_sign = res_sign; + + return count; +} + +static inline intn_digit_t digit_bxor(intn_digit_t a, intn_digit_t b) +{ + return a ^ b; +} + +size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign) +{ + intn_digit_t working_buf[INTN_MAX_IN_LEN]; + + const intn_digit_t *b; + size_t b_len; + intn_integer_sign_t b_sign; + + size_t count + = prepare_working_buf(m, m_len, m_sign, n, n_len, n_sign, &b, &b_len, &b_sign, working_buf); + + signed_bitwise(b, b_len, b_sign, working_buf, count, digit_bor); + intn_integer_sign_t res_sign = sign_bitwise(m_sign, n_sign, digit_bxor); + + cond_neg(res_sign, working_buf, count, out); + *out_sign = res_sign; + + if (res_sign == IntNNegativeInteger) { + bool all_zeros = true; + for (size_t i = 0; i < count; i++) { + if (out[i] != 0) { + all_zeros = false; + break; + } + } + if (all_zeros) { + *out_sign = IntNPositiveInteger; + } + } + + return count; +} + size_t intn_count_digits(const intn_digit_t *num, size_t num_len) { int i; diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index 5a286fb67..65098465c 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -77,6 +77,14 @@ size_t intn_bormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_si const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], intn_integer_sign_t *out_sign); +size_t intn_bandmn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign); + +size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_sign, + const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], + intn_integer_sign_t *out_sign); + size_t intn_count_digits(const intn_digit_t *num, size_t num_len); char *intn_to_string(const intn_digit_t *num, size_t len, intn_integer_sign_t num_sign, int base, From eb6cf94072988818c455f7cb2bb3086cb1bb9fd6 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 26 May 2025 18:09:35 +0200 Subject: [PATCH 07/27] make them available Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index 72a4efb02..c365d48f5 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1494,7 +1494,7 @@ term bif_erlang_band_2(Context *ctx, uint32_t fail_label, int live, term arg1, t if (LIKELY(term_is_integer(arg1) && term_is_integer(arg2))) { return arg1 & arg2; } else { - return bitwise_helper(ctx, fail_label, live, arg1, arg2, band, NULL); + return bitwise_helper(ctx, fail_label, live, arg1, arg2, band, intn_bandmn); } } @@ -1508,7 +1508,7 @@ term bif_erlang_bxor_2(Context *ctx, uint32_t fail_label, int live, term arg1, t if (LIKELY(term_is_integer(arg1) && term_is_integer(arg2))) { return (arg1 ^ arg2) | TERM_INTEGER_TAG; } else { - return bitwise_helper(ctx, fail_label, live, arg1, arg2, bxor, NULL); + return bitwise_helper(ctx, fail_label, live, arg1, arg2, bxor, intn_bxormn); } } From 5154147fb7675666fae25f612f5247cd64ca2ca9 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 10 Jun 2025 00:24:24 +0200 Subject: [PATCH 08/27] intn: band Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 6961c5e40..9a959785c 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -458,6 +458,17 @@ static inline intn_integer_sign_t sign_bitwise( & IntNNegativeInteger; } +// normalizes -0 to 0 +static inline size_t count_and_normalize_sign(const intn_digit_t num[], size_t len, intn_integer_sign_t sign, intn_integer_sign_t *out_sign) { + size_t count = intn_count_digits(num, len); + if ((count == 0) && (sign == IntNNegativeInteger)) { + *out_sign = IntNPositiveInteger; + } else { + *out_sign = sign; + } + return count; +} + static inline intn_digit_t digit_bor(intn_digit_t a, intn_digit_t b) { return a | b; @@ -507,9 +518,9 @@ size_t intn_bandmn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s intn_integer_sign_t res_sign = sign_bitwise(m_sign, n_sign, digit_band); cond_neg(res_sign, working_buf, count, out); - *out_sign = res_sign; + size_t res_count = count_and_normalize_sign(out, count, res_sign, out_sign); - return count; + return res_count; } static inline intn_digit_t digit_bxor(intn_digit_t a, intn_digit_t b) @@ -534,22 +545,9 @@ size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s intn_integer_sign_t res_sign = sign_bitwise(m_sign, n_sign, digit_bxor); cond_neg(res_sign, working_buf, count, out); - *out_sign = res_sign; - - if (res_sign == IntNNegativeInteger) { - bool all_zeros = true; - for (size_t i = 0; i < count; i++) { - if (out[i] != 0) { - all_zeros = false; - break; - } - } - if (all_zeros) { - *out_sign = IntNPositiveInteger; - } - } + size_t res_count = count_and_normalize_sign(out, count, res_sign, out_sign); - return count; + return res_count; } size_t intn_count_digits(const intn_digit_t *num, size_t num_len) From 0105ace5ade0fda5199bf5ff8113dafc525333b7 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 10 Jun 2025 00:26:31 +0200 Subject: [PATCH 09/27] intn_bsl Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 9a959785c..cf1377629 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -550,6 +550,74 @@ size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s return res_count; } +#define INTN_BSL_MAX_OUT_LEN 8 + +static inline size_t size_round_to(size_t n, size_t round_to) +{ + return (n + (round_to - 1)) & ~(round_to - 1); +} + +size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out) +{ + size_t digit_bit_size = sizeof(uint32_t) * 8; + + size_t digit_left_bit_shift = n % 32; + size_t right_shift_n = (32 - digit_left_bit_shift); + + size_t counted_digits = intn_count_digits(num, len); + size_t ms_digit_bits = 32 - uint32_nlz(num[counted_digits - 1]); + size_t effective_bits_len = (counted_digits - 1) * digit_bit_size + ms_digit_bits; + size_t new_bits_len = size_round_to(effective_bits_len + n, digit_bit_size); + + size_t new_digits_count = new_bits_len / digit_bit_size; + + if (new_digits_count > INTN_BSL_MAX_OUT_LEN) { + return new_digits_count; + } + + size_t initial_zeros = MIN(n / digit_bit_size, INTN_BSL_MAX_OUT_LEN); + memset(out, 0, initial_zeros * sizeof(uint32_t)); + + if (right_shift_n == 32) { + memcpy(out + initial_zeros, num, len * sizeof(uint32_t)); + return initial_zeros + len; + } + + uint32_t last_digit = 0; + size_t i; + for (i = 0; i < counted_digits; i++) { + uint32_t digit = num[i]; + out[initial_zeros + i] = (digit << digit_left_bit_shift) | (last_digit >> right_shift_n); + last_digit = digit; + } + uint32_t maybe_last_out = (last_digit >> right_shift_n); + + if (initial_zeros + i > new_digits_count) { + abort(); + } + + if (maybe_last_out) { + out[initial_zeros + i] = maybe_last_out; + return initial_zeros + i + 1; + } + + return initial_zeros + i; +} + +/* + uint32_t last_digit = 0; + for (size_t i = initial_zeros; i < out_len - 1; i++) { + uint32_t digit = num[i - initial_zeros]; + if (i - initial_zeros >= counted_digits) { + abort(); + } + out[i] = (digit << digit_left_bit_shift) | (last_digit >> right_shift_n); + last_digit = digit; + fprintf(stderr, "in: %i, (%i), last_digit: %i\n", (int) i - initial_zeros, (int) i, (int) last_digit); + } + out[out_len - 1] = (last_digit >> right_shift_n); +*/ + size_t intn_count_digits(const intn_digit_t *num, size_t num_len) { int i; From 5033d9bf406525c1f4309e60329b4f122ce89dbd Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 10 Jun 2025 00:30:34 +0200 Subject: [PATCH 10/27] bsl tests --- tests/erlang_tests/bigint.erl | 38 ++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/erlang_tests/bigint.erl b/tests/erlang_tests/bigint.erl index 2583cf002..fe3d83d80 100644 --- a/tests/erlang_tests/bigint.erl +++ b/tests/erlang_tests/bigint.erl @@ -54,7 +54,8 @@ start() -> external_term_decode() + big_literals() + to_external_term() + - test_bor(). + test_bor() + + test_bsl(). test_mul() -> Expected_INT64_MIN = ?MODULE:pow(-2, 63), @@ -855,6 +856,41 @@ test_bor() -> 0. +test_bsl() -> + %% versione negativa + Pattern1 = erlang:binary_to_integer(?MODULE:id(<<"CAFE1234AABBCCDD98765432">>), 16), + <<"CAFE1234AABBCCDD98765432000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(24), 16), + <<"195FC2469557799BB30ECA8640000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(29), 16), + <<"CAFE1234AABBCCDD9876543200000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(32), 16), + <<"657F091A555DE66ECC3B2A19000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(35), 16), + <<"CAFE1234AABBCCDD98765432000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(48), 16), + <<"657F091A555DE66ECC3B2A190000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(51), 16), + <<"CAFE1234AABBCCDD987654320000000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(64), 16), + <<"CAFE1234AABBCCDD987654320000000000000000000000000000000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(160), 16), + <<"657F00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(127), 16), + <<"CAFE00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(128), 16), + <<"195FC00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(129), 16), + <<"CAFE000000000000000000000000000000000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(240), 16), + + Pattern2 = erlang:binary_to_integer(?MODULE:id(<<"-CAFE1234AABBCCDD98765432">>), 16), + <<"-CAFE1234AABBCCDD98765432000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(24), 16), + <<"-195FC2469557799BB30ECA8640000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(29), 16), + <<"-CAFE1234AABBCCDD9876543200000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(32), 16), + <<"-657F091A555DE66ECC3B2A19000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(35), 16), + <<"-CAFE1234AABBCCDD98765432000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(48), 16), + <<"-657F091A555DE66ECC3B2A190000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(51), 16), + <<"-CAFE1234AABBCCDD987654320000000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(64), 16), + <<"-CAFE1234AABBCCDD987654320000000000000000000000000000000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(160), 16), + <<"-657F00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(127), 16), + <<"-CAFE00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(128), 16), + <<"-195FC00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(129), 16), + <<"-CAFE000000000000000000000000000000000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(240), 16), + + + + 0. + + id(X) -> X. From 45c16d6f420089148ef254524f96bdcd891fbd34 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 10 Jun 2025 18:08:57 +0200 Subject: [PATCH 11/27] intn_h Signed-off-by: Davide Bettio --- src/libAtomVM/intn.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index 65098465c..dc0978e55 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -85,6 +85,8 @@ size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s const intn_digit_t n[], size_t n_len, intn_integer_sign_t n_sign, intn_digit_t out[], intn_integer_sign_t *out_sign); +size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out); + size_t intn_count_digits(const intn_digit_t *num, size_t num_len); char *intn_to_string(const intn_digit_t *num, size_t len, intn_integer_sign_t num_sign, int base, From 5572a3e0f64186c4d7e7f13315679312aa3c7dd9 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 10 Jun 2025 18:10:37 +0200 Subject: [PATCH 12/27] bifs bsl Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 129 +++++++++++++++++++++++------ tests/erlang_tests/test_bs_int.erl | 14 ++-- 2 files changed, 113 insertions(+), 30 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index c365d48f5..5a99683a9 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1512,47 +1512,128 @@ term bif_erlang_bxor_2(Context *ctx, uint32_t fail_label, int live, term arg1, t } } -typedef int64_t (*bitshift_op)(int64_t a, avm_int_t b); - -static inline term bitshift_helper(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2, bitshift_op op) +static inline int32_t int32_bsr(int32_t n, unsigned int rshift) { - UNUSED(live); + return (int32_t) ((n < 0) ? ~(~((uint32_t ) n) >> rshift) : (((uint32_t) n) >> rshift)); +} - if (UNLIKELY(!term_is_any_integer(arg1) || !term_is_integer(arg2))) { - RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); +static inline bool int32_bsl_overflow(int32_t n, unsigned int lshift, int32_t *out) +{ + // + if ((n != 0) && (lshift > 64)) { + return true; } + // - int64_t a = term_maybe_unbox_int64(arg1); - avm_int_t b = term_to_int(arg2); - int64_t result = op(a, b); - - #if BOXED_TERMS_REQUIRED_FOR_INT64 > 1 - return make_maybe_boxed_int64(ctx, fail_label, live, result); - #else - return make_maybe_boxed_int(ctx, fail_label, live, result); - #endif + int32_t res = (int32_t) (((uint32_t) n) << lshift); + *out = res; + int32_t check = int32_bsr(res, lshift); + return check != n; } -static inline int64_t bsl(int64_t a, avm_int_t b) +static inline int64_t int64_bsr(int64_t n, unsigned int rshift) { - // TODO check for overflow - return a << b; + return (int64_t) ((n < 0) ? ~(~((uint64_t ) n) >> rshift) : (((uint64_t) n) >> rshift)); } -term bif_erlang_bsl_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2) +static inline bool int64_bsl_overflow(int64_t n, unsigned int lshift, int64_t *out) { - return bitshift_helper(ctx, fail_label, live, arg1, arg2, bsl); + // + if ((n != 0) && (lshift > 64)) { + return true; + } + // + + int64_t res = (int64_t) (((uint64_t) n) << lshift); + *out = res; + int64_t check = int64_bsr(res, lshift); + return check != n; } -static inline int64_t bsr(int64_t a, avm_int_t b) +term bif_erlang_bsl_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2) { - // TODO check for underflow - return a >> b; + if (LIKELY(term_is_any_integer(arg1) && term_is_any_integer(arg2))) { + size_t arg1_size = term_is_integer(arg1) ? 0 : term_boxed_size(arg1); + avm_int_t b = term_to_int(arg2); + if (arg1_size <= BOXED_TERMS_REQUIRED_FOR_INT64) { + int64_t a = term_maybe_unbox_int64(arg1); + //if (uint64_leading_zeros(a) >= b) { + int64_t result; + if (!int64_bsl_overflow(a, b, &result)) { + #if BOXED_TERMS_REQUIRED_FOR_INT64 > 1 + return make_maybe_boxed_int64(ctx, fail_label, live, result); + #else + return make_maybe_boxed_int(ctx, fail_label, live, result); + #endif + } else { + fprintf(stderr, "was with b: %i, with: ", (int) b); + term_display(stderr, arg1, ctx); + fprintf(stderr, "\n"); + } + } + + intn_digit_t tmp_buf1[INTN_INT64_LEN]; + intn_digit_t tmp_buf2[INTN_INT64_LEN]; + intn_digit_t *m; + size_t m_len; + intn_integer_sign_t m_sign; + intn_digit_t *n; + size_t n_len; + intn_integer_sign_t n_sign; + args_to_bigint(arg1, arg2, tmp_buf1, tmp_buf2, &m, &m_len, &m_sign, &n, &n_len, &n_sign); + + intn_digit_t bigres[INTN_MAX_RES_LEN]; + size_t bigres_len; + bigres_len = intn_bsl(m, m_len, b, bigres); + fprintf(stderr, "bigres: %i\n", (int) bigres_len); + + return make_bigint(ctx, fail_label, live, bigres, bigres_len, m_sign); + } else { + RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); + } } term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2) { - return bitshift_helper(ctx, fail_label, live, arg1, arg2, bsr); + if (LIKELY(term_is_any_integer(arg1) && term_is_any_integer(arg2))) { + size_t arg1_size = term_is_integer(arg1) ? 0 : term_boxed_size(arg1); + + avm_int_t b = term_to_int(arg2); + + fprintf(stderr, "going to do b: %i, with: ", (int) b); + term_display(stderr, arg1, ctx); + fprintf(stderr, "\n"); + + if (arg1_size <= BOXED_TERMS_REQUIRED_FOR_INT64) { + uint64_t a = (uint64_t) term_maybe_unbox_int64(arg1); + int64_t result = (int64_t) (a >> b); //FIX THIS + + #if BOXED_TERMS_REQUIRED_FOR_INT64 > 1 + return make_maybe_boxed_int64(ctx, fail_label, live, result); + #else + return make_maybe_boxed_int(ctx, fail_label, live, result); + #endif + } + + intn_digit_t tmp_buf1[INTN_INT64_LEN]; + intn_digit_t tmp_buf2[INTN_INT64_LEN]; + intn_digit_t *m; + size_t m_len; + intn_integer_sign_t m_sign; + intn_digit_t *n; + size_t n_len; + intn_integer_sign_t n_sign; + args_to_bigint(arg1, arg2, tmp_buf1, tmp_buf2, &m, &m_len, &m_sign, &n, &n_len, &n_sign); + + intn_digit_t bigres[INTN_MAX_RES_LEN]; + size_t bigres_len; + //intn_bsl(m, m_len, b, bigres, &bigres_len); //FIXME TODO CHANGE ME + abort(); + + return make_bigint(ctx, fail_label, live, bigres, bigres_len, m_sign); + } else { + RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); + } } term bif_erlang_bnot_1(Context *ctx, uint32_t fail_label, int live, term arg1) diff --git a/tests/erlang_tests/test_bs_int.erl b/tests/erlang_tests/test_bs_int.erl index 91ac02f90..2ed30235a 100644 --- a/tests/erlang_tests/test_bs_int.erl +++ b/tests/erlang_tests/test_bs_int.erl @@ -32,12 +32,14 @@ start() -> Signedness <- [unsigned, signed] ], - [ - test_bs_ints(Binaries, Size, Endianness, Signedness) - || Size <- [64], - Endianness <- [big, little, native], - Signedness <- [unsigned] - ], + % 64 cannot be used (64 makes use of temporary big integers) + % 48 would be great, but `bitstring_insert_integer` doesn't support 48 bits + % [ + % test_bs_ints(Binaries, Size, Endianness, Signedness) + % || Size <- [48], + % Endianness <- [big, little, native], + % Signedness <- [unsigned] + % ], 0. From 820c5e481479d3e773a87fc47e7afabdeff830ac Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 11:51:54 +0200 Subject: [PATCH 13/27] TEMP TEMP UNREACHABLE IS ABORT Signed-off-by: Davide Bettio --- src/libAtomVM/utils.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libAtomVM/utils.h b/src/libAtomVM/utils.h index 14e12e195..f5caace65 100644 --- a/src/libAtomVM/utils.h +++ b/src/libAtomVM/utils.h @@ -324,9 +324,10 @@ static inline __attribute__((always_inline)) func_ptr_t cast_void_to_func_ptr(vo #ifdef __GNUC__ #define UNREACHABLE() \ - __builtin_unreachable() + abort() #else - #define UNREACHABLE(...) + #define UNREACHABLE(...) \ + abort() #endif #if defined(__GNUC__) && !defined(__clang__) From b8b88c780d7201e6b6e985b16f3e10343d970a01 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 12:25:21 +0200 Subject: [PATCH 14/27] WIP bsr Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 4 +-- src/libAtomVM/intn.c | 49 +++++++++++++++++++++++++++++++++++ src/libAtomVM/intn.h | 1 + tests/erlang_tests/bigint.erl | 20 +++++++++++++- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index 5a99683a9..aac7baaa5 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1626,9 +1626,7 @@ term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, te args_to_bigint(arg1, arg2, tmp_buf1, tmp_buf2, &m, &m_len, &m_sign, &n, &n_len, &n_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; - size_t bigres_len; - //intn_bsl(m, m_len, b, bigres, &bigres_len); //FIXME TODO CHANGE ME - abort(); + size_t bigres_len = intn_bsr(m, m_len, b, bigres); return make_bigint(ctx, fail_label, live, bigres, bigres_len, m_sign); } else { diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index cf1377629..db54c99b2 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -604,6 +604,55 @@ size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out) return initial_zeros + i; } +size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out) +{ + size_t digit_bit_size = sizeof(uint32_t) * 8; + + size_t digit_right_bit_shift = n % 32; + size_t left_shift_n = (32 - digit_right_bit_shift); + + size_t counted_digits = intn_count_digits(num, len); + size_t ms_digit_bits = 32 - uint32_nlz(num[counted_digits - 1]); + size_t effective_bits_len = (counted_digits - 1) * digit_bit_size + ms_digit_bits; + size_t new_bits_len = size_round_to(effective_bits_len - n, digit_bit_size); + + size_t new_digits_count = new_bits_len / digit_bit_size; + + if (new_digits_count > INTN_BSL_MAX_OUT_LEN) { + return new_digits_count; + } + + size_t discarded = MIN(n / digit_bit_size, len); + + if (left_shift_n == 32) { + memcpy(out, num + discarded, (len - discarded) * sizeof(uint32_t)); + return len - discarded; + } + + size_t i; + for (i = discarded; i < counted_digits - 1; i++) { + uint32_t next_digit = num[i + 1]; + uint32_t digit = num[i]; + out[i - discarded] = (digit >> digit_right_bit_shift) | (next_digit << left_shift_n); + } + uint32_t maybe_last_out = (num[i] >> digit_right_bit_shift); + +/* + if (initial_zeros + i > new_digits_count) { + abort(); + } +*/ + + if (maybe_last_out) { + out[i - discarded] = maybe_last_out; + return i - discarded + 1; + } + + return i - discarded; +} + + + /* uint32_t last_digit = 0; for (size_t i = initial_zeros; i < out_len - 1; i++) { diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index dc0978e55..ddfacc7ac 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -86,6 +86,7 @@ size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s intn_integer_sign_t *out_sign); size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out); +size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out); size_t intn_count_digits(const intn_digit_t *num, size_t num_len); diff --git a/tests/erlang_tests/bigint.erl b/tests/erlang_tests/bigint.erl index fe3d83d80..9ecbabe33 100644 --- a/tests/erlang_tests/bigint.erl +++ b/tests/erlang_tests/bigint.erl @@ -55,7 +55,8 @@ start() -> big_literals() + to_external_term() + test_bor() + - test_bsl(). + test_bsl() + + test_bsr(). test_mul() -> Expected_INT64_MIN = ?MODULE:pow(-2, 63), @@ -890,6 +891,23 @@ test_bsl() -> 0. +test_bsr() -> + erlang:display('bsr'), + %% versione negativa + Pattern1 = erlang:binary_to_integer(?MODULE:id(<<"CAFE1234AABBCCDD98765432987654321">>), 16), + <<"CAFE1234AABBCCDD98765432987">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(24), 16), + <<"657F091A555DE66ECC3B2A194C">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(29), 16), + <<"CAFE1234AABBCCDD987654329">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(32), 16), + <<"195FC2469557799BB30ECA865">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(35), 16), + <<"CAFE1234AABBCCDD98765">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(48), 16), + <<"195FC2469557799BB30EC">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(51), 16), + <<"CAFE1234AABBCCDD9">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(64), 16), + <<"C">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(128), 16), + + 0. + + + id(X) -> X. From 7651d3bdc0391a1ff86c41b142a2e0d47af2519c Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 13:31:52 +0200 Subject: [PATCH 15/27] Disable unused functions Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index aac7baaa5..1352c1c22 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1512,6 +1512,7 @@ term bif_erlang_bxor_2(Context *ctx, uint32_t fail_label, int live, term arg1, t } } +#if 0 static inline int32_t int32_bsr(int32_t n, unsigned int rshift) { return (int32_t) ((n < 0) ? ~(~((uint32_t ) n) >> rshift) : (((uint32_t) n) >> rshift)); @@ -1530,6 +1531,7 @@ static inline bool int32_bsl_overflow(int32_t n, unsigned int lshift, int32_t *o int32_t check = int32_bsr(res, lshift); return check != n; } +#endif static inline int64_t int64_bsr(int64_t n, unsigned int rshift) { From 536808b142cda60dd2e3e380d574dd2a4043d8b7 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 13:51:12 +0200 Subject: [PATCH 16/27] Safe shift Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index 1352c1c22..2b0cc65d1 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1521,8 +1521,9 @@ static inline int32_t int32_bsr(int32_t n, unsigned int rshift) static inline bool int32_bsl_overflow(int32_t n, unsigned int lshift, int32_t *out) { // - if ((n != 0) && (lshift > 64)) { - return true; + if ((n != 0) && (lshift > 32)) { + *out = 0; + return (n != 0); } // @@ -1531,6 +1532,14 @@ static inline bool int32_bsl_overflow(int32_t n, unsigned int lshift, int32_t *o int32_t check = int32_bsr(res, lshift); return check != n; } + +static inline int32_t int32_bsr_safe(int32_t n, unsigned int rshift) +{ + if (rshift > 32) { + return n < 0 ? -1 : 0; + } + return int32_bsr(n, rshift); +} #endif static inline int64_t int64_bsr(int64_t n, unsigned int rshift) @@ -1541,8 +1550,9 @@ static inline int64_t int64_bsr(int64_t n, unsigned int rshift) static inline bool int64_bsl_overflow(int64_t n, unsigned int lshift, int64_t *out) { // - if ((n != 0) && (lshift > 64)) { - return true; + if (lshift > 64) { + *out = 0; + return (n != 0); } // @@ -1552,6 +1562,14 @@ static inline bool int64_bsl_overflow(int64_t n, unsigned int lshift, int64_t *o return check != n; } +static inline int64_t int64_bsr_safe(int64_t n, unsigned int rshift) +{ + if (rshift > 64) { + return n < 0 ? -1 : 0; + } + return int64_bsr(n, rshift); +} + term bif_erlang_bsl_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2) { if (LIKELY(term_is_any_integer(arg1) && term_is_any_integer(arg2))) { @@ -1608,7 +1626,7 @@ term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, te if (arg1_size <= BOXED_TERMS_REQUIRED_FOR_INT64) { uint64_t a = (uint64_t) term_maybe_unbox_int64(arg1); - int64_t result = (int64_t) (a >> b); //FIX THIS + int64_t result = int64_bsr_safe(a, b); #if BOXED_TERMS_REQUIRED_FOR_INT64 > 1 return make_maybe_boxed_int64(ctx, fail_label, live, result); From 5b5d5eb686490891c69f5a9f007797f0de8d1e16 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 15:08:45 +0200 Subject: [PATCH 17/27] bigint: cleanup shift tests Signed-off-by: Davide Bettio --- tests/erlang_tests/bigint.erl | 117 +++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/tests/erlang_tests/bigint.erl b/tests/erlang_tests/bigint.erl index 9ecbabe33..53fbd1fff 100644 --- a/tests/erlang_tests/bigint.erl +++ b/tests/erlang_tests/bigint.erl @@ -860,40 +860,86 @@ test_bor() -> test_bsl() -> %% versione negativa Pattern1 = erlang:binary_to_integer(?MODULE:id(<<"CAFE1234AABBCCDD98765432">>), 16), - <<"CAFE1234AABBCCDD98765432000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(24), 16), - <<"195FC2469557799BB30ECA8640000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(29), 16), - <<"CAFE1234AABBCCDD9876543200000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(32), 16), - <<"657F091A555DE66ECC3B2A19000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(35), 16), - <<"CAFE1234AABBCCDD98765432000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(48), 16), - <<"657F091A555DE66ECC3B2A190000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(51), 16), - <<"CAFE1234AABBCCDD987654320000000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(64), 16), - <<"CAFE1234AABBCCDD987654320000000000000000000000000000000000000000">> = erlang:integer_to_binary(Pattern1 bsl ?MODULE:id(160), 16), - <<"657F00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(127), 16), - <<"CAFE00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(128), 16), - <<"195FC00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(129), 16), - <<"CAFE000000000000000000000000000000000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(16#CAFE) bsl ?MODULE:id(240), 16), + <<"CAFE1234AABBCCDD98765432000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(24), 16 + ), + <<"195FC2469557799BB30ECA8640000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(29), 16 + ), + <<"CAFE1234AABBCCDD9876543200000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(32), 16 + ), + <<"657F091A555DE66ECC3B2A19000000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(35), 16 + ), + <<"CAFE1234AABBCCDD98765432000000000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(48), 16 + ), + <<"657F091A555DE66ECC3B2A190000000000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(51), 16 + ), + <<"CAFE1234AABBCCDD987654320000000000000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(64), 16 + ), + <<"CAFE1234AABBCCDD987654320000000000000000000000000000000000000000">> = erlang:integer_to_binary( + Pattern1 bsl ?MODULE:id(160), 16 + ), + <<"657F00000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(16#CAFE) bsl ?MODULE:id(127), 16 + ), + <<"CAFE00000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(16#CAFE) bsl ?MODULE:id(128), 16 + ), + <<"195FC00000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(16#CAFE) bsl ?MODULE:id(129), 16 + ), + <<"CAFE000000000000000000000000000000000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(16#CAFE) bsl ?MODULE:id(240), 16 + ), Pattern2 = erlang:binary_to_integer(?MODULE:id(<<"-CAFE1234AABBCCDD98765432">>), 16), - <<"-CAFE1234AABBCCDD98765432000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(24), 16), - <<"-195FC2469557799BB30ECA8640000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(29), 16), - <<"-CAFE1234AABBCCDD9876543200000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(32), 16), - <<"-657F091A555DE66ECC3B2A19000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(35), 16), - <<"-CAFE1234AABBCCDD98765432000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(48), 16), - <<"-657F091A555DE66ECC3B2A190000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(51), 16), - <<"-CAFE1234AABBCCDD987654320000000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(64), 16), - <<"-CAFE1234AABBCCDD987654320000000000000000000000000000000000000000">> = erlang:integer_to_binary(Pattern2 bsl ?MODULE:id(160), 16), - <<"-657F00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(127), 16), - <<"-CAFE00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(128), 16), - <<"-195FC00000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(129), 16), - <<"-CAFE000000000000000000000000000000000000000000000000000000000000">> = erlang:integer_to_binary(?MODULE:id(-16#CAFE) bsl ?MODULE:id(240), 16), - - + <<"-CAFE1234AABBCCDD98765432000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(24), 16 + ), + <<"-195FC2469557799BB30ECA8640000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(29), 16 + ), + <<"-CAFE1234AABBCCDD9876543200000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(32), 16 + ), + <<"-657F091A555DE66ECC3B2A19000000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(35), 16 + ), + <<"-CAFE1234AABBCCDD98765432000000000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(48), 16 + ), + <<"-657F091A555DE66ECC3B2A190000000000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(51), 16 + ), + <<"-CAFE1234AABBCCDD987654320000000000000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(64), 16 + ), + <<"-CAFE1234AABBCCDD987654320000000000000000000000000000000000000000">> = erlang:integer_to_binary( + Pattern2 bsl ?MODULE:id(160), 16 + ), + <<"-657F00000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(-16#CAFE) bsl ?MODULE:id(127), 16 + ), + <<"-CAFE00000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(-16#CAFE) bsl ?MODULE:id(128), 16 + ), + <<"-195FC00000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(-16#CAFE) bsl ?MODULE:id(129), 16 + ), + <<"-CAFE000000000000000000000000000000000000000000000000000000000000">> = erlang:integer_to_binary( + ?MODULE:id(-16#CAFE) bsl ?MODULE:id(240), 16 + ), 0. test_bsr() -> erlang:display('bsr'), - %% versione negativa + Pattern1 = erlang:binary_to_integer(?MODULE:id(<<"CAFE1234AABBCCDD98765432987654321">>), 16), <<"CAFE1234AABBCCDD98765432987">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(24), 16), <<"657F091A555DE66ECC3B2A194C">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(29), 16), @@ -903,12 +949,23 @@ test_bsr() -> <<"195FC2469557799BB30EC">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(51), 16), <<"CAFE1234AABBCCDD9">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(64), 16), <<"C">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(128), 16), + <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(250), 16), + <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(256), 16), + + %Pattern2 = erlang:binary_to_integer(?MODULE:id(<<"-CAFE1234AABBCCDD98765432987654321">>), 16), + %<<"-CAFE1234AABBCCDD98765432988">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(24), 16), + %<<"-657F091A555DE66ECC3B2A194D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(29), 16), + %<<"-CAFE1234AABBCCDD98765432A">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(32), 16), + %<<"-195FC2469557799BB30ECA866">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(35), 16), + %<<"-CAFE1234AABBCCDD98766">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(48), 16), + %<<"-195FC2469557799BB30ED">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(51), 16), + %<<"-CAFE1234AABBCCDDA">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(64), 16), + %<<"-D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(128), 16), + %<<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(250), 16), + %<<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(256), 16), 0. - - - id(X) -> X. From 06d0e612502694980d329e3d12b47d57aea0385c Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 15:09:08 +0200 Subject: [PATCH 18/27] bsr fixes Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index db54c99b2..ec68bc53e 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -614,14 +614,16 @@ size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out) size_t counted_digits = intn_count_digits(num, len); size_t ms_digit_bits = 32 - uint32_nlz(num[counted_digits - 1]); size_t effective_bits_len = (counted_digits - 1) * digit_bit_size + ms_digit_bits; - size_t new_bits_len = size_round_to(effective_bits_len - n, digit_bit_size); - size_t new_digits_count = new_bits_len / digit_bit_size; - - if (new_digits_count > INTN_BSL_MAX_OUT_LEN) { - return new_digits_count; + if (n > effective_bits_len) { + out[0] = 0; + return 1; } + ///size_t new_bits_len = size_round_to(effective_bits_len - n, digit_bit_size); + + ////size_t new_digits_count = new_bits_len / digit_bit_size; + size_t discarded = MIN(n / digit_bit_size, len); if (left_shift_n == 32) { From a449a93356683a965e526219538dc72ecfcb75a3 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 5 Jul 2025 19:35:33 +0200 Subject: [PATCH 19/27] Make bsr work with signed numbers Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 2 +- src/libAtomVM/intn.c | 103 +++++++++++++++++++++++----------- src/libAtomVM/intn.h | 2 +- tests/erlang_tests/bigint.erl | 22 ++++---- 4 files changed, 83 insertions(+), 46 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index 2b0cc65d1..d9a259c12 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1646,7 +1646,7 @@ term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, te args_to_bigint(arg1, arg2, tmp_buf1, tmp_buf2, &m, &m_len, &m_sign, &n, &n_len, &n_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; - size_t bigres_len = intn_bsr(m, m_len, b, bigres); + size_t bigres_len = intn_bsr(m, m_len, m_sign, b, bigres); return make_bigint(ctx, fail_label, live, bigres, bigres_len, m_sign); } else { diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index ec68bc53e..47686351a 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -35,6 +35,9 @@ #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) +static size_t cond_neg_in_place(intn_integer_sign_t sign, intn_digit_t out[]); +static size_t neg_in_place(intn_digit_t out[], size_t len); + /* * Multiplication */ @@ -380,18 +383,23 @@ size_t intn_addmnu( return i; } +static void neg(const intn_digit_t in[], size_t in_len, intn_digit_t out[]) +{ + uint32_t carry = 1; + for (size_t i = 0; i < in_len; i++) { + uint64_t temp = (uint64_t) (~in[i]) + (uint64_t) carry; + out[i] = (uint32_t) temp; + carry = temp >> 32; + } +} + static void cond_neg( intn_integer_sign_t sign, const intn_digit_t in[], size_t in_len, intn_digit_t out[]) { if (sign == IntNPositiveInteger) { memcpy(out, in, sizeof(intn_digit_t) * in_len); } else { - uint32_t carry = 1; - for (size_t i = 0; i < in_len; i++) { - uint64_t temp = (uint64_t) (~in[i]) + (uint64_t) carry; - out[i] = (uint32_t) temp; - carry = temp >> 32; - } + neg(in, in_len, out); } } @@ -604,21 +612,15 @@ size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out) return initial_zeros + i; } -size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out) +size_t bsru(const uint32_t num[], size_t effective_bits_len, size_t n, uint32_t last_digit, uint32_t *out) { size_t digit_bit_size = sizeof(uint32_t) * 8; size_t digit_right_bit_shift = n % 32; size_t left_shift_n = (32 - digit_right_bit_shift); - size_t counted_digits = intn_count_digits(num, len); - size_t ms_digit_bits = 32 - uint32_nlz(num[counted_digits - 1]); - size_t effective_bits_len = (counted_digits - 1) * digit_bit_size + ms_digit_bits; - - if (n > effective_bits_len) { - out[0] = 0; - return 1; - } + size_t len = size_round_to(effective_bits_len, digit_bit_size) / digit_bit_size; + size_t counted_digits = len; ///size_t new_bits_len = size_round_to(effective_bits_len - n, digit_bit_size); @@ -637,7 +639,7 @@ size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out) uint32_t digit = num[i]; out[i - discarded] = (digit >> digit_right_bit_shift) | (next_digit << left_shift_n); } - uint32_t maybe_last_out = (num[i] >> digit_right_bit_shift); + uint32_t maybe_last_out = (num[i] >> digit_right_bit_shift) | (last_digit << left_shift_n); /* if (initial_zeros + i > new_digits_count) { @@ -653,6 +655,35 @@ size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out) return i - discarded; } +size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out) +{ + size_t digit_bit_size = sizeof(uint32_t) * 8; + size_t counted_digits = intn_count_digits(num, len); + size_t ms_digit_bits = 32 - uint32_nlz(num[counted_digits - 1]); + size_t effective_bits_len = (counted_digits - 1) * digit_bit_size + ms_digit_bits; + + if (n > effective_bits_len) { + out[0] = (num_sign == IntNPositiveInteger) ? 0 : 1; + return 1; + } + + if (num_sign == IntNPositiveInteger) { + return bsru(num, effective_bits_len, n, 0, out); + } else { + uint32_t tmp_buf[INTN_MAX_RES_LEN]; + memset(out, 0, INTN_MAX_RES_LEN * sizeof(const uint32_t)); + neg(num, counted_digits, tmp_buf); + size_t shifted_len = bsru(tmp_buf, effective_bits_len, n, (uint32_t) -1, out); + + size_t len = size_round_to(effective_bits_len - n, digit_bit_size) / digit_bit_size; + neg_in_place(out, len); + + fprintf(stderr, "shifted: %i, len: %i\n", (int) shifted_len, (int) len); + print_num(out, len); + return len; + } +} + /* @@ -912,26 +943,32 @@ int intn_parse( return out_len; } +static size_t neg_in_place(intn_digit_t out[], size_t len) +{ + uint32_t carry = 1; + size_t i; + int last_non_zero = -1; + for (i = 0; i < len; i++) { + uint64_t temp = (uint64_t) (~out[i]) + (uint64_t) carry; + if ((uint32_t) temp != 0) { + last_non_zero = i; + } + out[i] = (uint32_t) temp; + carry = temp >> 32; + } + if (carry) { + abort(); + out[i] = carry; + return i; + } else { + return last_non_zero + 1; + } +} + static size_t cond_neg_in_place(intn_integer_sign_t sign, intn_digit_t out[]) { if (sign == IntNNegativeInteger) { - uint32_t carry = 1; - size_t i; - int last_non_zero = -1; - for (i = 0; i < INTN_MAX_RES_LEN - 1; i++) { - uint64_t temp = (uint64_t) (~out[i]) + (uint64_t) carry; - if ((uint32_t) temp != 0) { - last_non_zero = i; - } - out[i] = (uint32_t) temp; - carry = temp >> 32; - } - if (carry) { - out[i] = carry; - return i; - } else { - return last_non_zero + 1; - } + return neg_in_place(out, INTN_MAX_RES_LEN - 1); } else { return intn_count_digits(out, INTN_MAX_IN_LEN); } diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index ddfacc7ac..f7c73fb21 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -86,7 +86,7 @@ size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s intn_integer_sign_t *out_sign); size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out); -size_t intn_bsr(const uint32_t num[], size_t len, size_t n, uint32_t *out); +size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out); size_t intn_count_digits(const intn_digit_t *num, size_t num_len); diff --git a/tests/erlang_tests/bigint.erl b/tests/erlang_tests/bigint.erl index 53fbd1fff..1e00701b9 100644 --- a/tests/erlang_tests/bigint.erl +++ b/tests/erlang_tests/bigint.erl @@ -952,17 +952,17 @@ test_bsr() -> <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(250), 16), <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(256), 16), - %Pattern2 = erlang:binary_to_integer(?MODULE:id(<<"-CAFE1234AABBCCDD98765432987654321">>), 16), - %<<"-CAFE1234AABBCCDD98765432988">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(24), 16), - %<<"-657F091A555DE66ECC3B2A194D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(29), 16), - %<<"-CAFE1234AABBCCDD98765432A">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(32), 16), - %<<"-195FC2469557799BB30ECA866">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(35), 16), - %<<"-CAFE1234AABBCCDD98766">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(48), 16), - %<<"-195FC2469557799BB30ED">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(51), 16), - %<<"-CAFE1234AABBCCDDA">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(64), 16), - %<<"-D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(128), 16), - %<<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(250), 16), - %<<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(256), 16), + Pattern2 = erlang:binary_to_integer(?MODULE:id(<<"-CAFE1234AABBCCDD98765432987654321">>), 16), + <<"-CAFE1234AABBCCDD98765432988">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(24), 16), + <<"-657F091A555DE66ECC3B2A194D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(29), 16), + <<"-CAFE1234AABBCCDD98765432A">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(32), 16), + <<"-195FC2469557799BB30ECA866">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(35), 16), + <<"-CAFE1234AABBCCDD98766">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(48), 16), + <<"-195FC2469557799BB30ED">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(51), 16), + <<"-CAFE1234AABBCCDDA">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(64), 16), + <<"-D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(128), 16), + <<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(250), 16), + <<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(256), 16), 0. From 5f710e8e0dc1512b016739c38b4d6e26a607b96e Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 00:56:27 +0200 Subject: [PATCH 20/27] bsr cleanup #1 Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 55 +++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 47686351a..3049e3f04 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -612,47 +612,34 @@ size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out) return initial_zeros + i; } -size_t bsru(const uint32_t num[], size_t effective_bits_len, size_t n, uint32_t last_digit, uint32_t *out) +void bsru(const uint32_t num[], size_t effective_bits_len, size_t n, uint32_t last_digit, uint32_t *out) { - size_t digit_bit_size = sizeof(uint32_t) * 8; - - size_t digit_right_bit_shift = n % 32; - size_t left_shift_n = (32 - digit_right_bit_shift); - - size_t len = size_round_to(effective_bits_len, digit_bit_size) / digit_bit_size; - size_t counted_digits = len; + size_t digit_bit_size = sizeof(uint32_t) * 8; // 32 - ///size_t new_bits_len = size_round_to(effective_bits_len - n, digit_bit_size); + size_t digit_right_bit_shift = n % digit_bit_size; + size_t left_shift_n = (digit_bit_size - digit_right_bit_shift); - ////size_t new_digits_count = new_bits_len / digit_bit_size; + size_t len_in_digits = size_round_to(effective_bits_len, digit_bit_size) / digit_bit_size; - size_t discarded = MIN(n / digit_bit_size, len); + // caller makes sure that discarded < len_in_digits + size_t discarded = n / digit_bit_size; if (left_shift_n == 32) { - memcpy(out, num + discarded, (len - discarded) * sizeof(uint32_t)); - return len - discarded; + memcpy(out, num + discarded, (len_in_digits - discarded) * sizeof(uint32_t)); + return; } size_t i; - for (i = discarded; i < counted_digits - 1; i++) { + for (i = discarded; i < len_in_digits - 1; i++) { uint32_t next_digit = num[i + 1]; uint32_t digit = num[i]; out[i - discarded] = (digit >> digit_right_bit_shift) | (next_digit << left_shift_n); } uint32_t maybe_last_out = (num[i] >> digit_right_bit_shift) | (last_digit << left_shift_n); -/* - if (initial_zeros + i > new_digits_count) { - abort(); - } -*/ - if (maybe_last_out) { out[i - discarded] = maybe_last_out; - return i - discarded + 1; } - - return i - discarded; } size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out) @@ -662,29 +649,25 @@ size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t ms_digit_bits = 32 - uint32_nlz(num[counted_digits - 1]); size_t effective_bits_len = (counted_digits - 1) * digit_bit_size + ms_digit_bits; - if (n > effective_bits_len) { + if (n >= effective_bits_len) { out[0] = (num_sign == IntNPositiveInteger) ? 0 : 1; return 1; } + size_t shifted_len = size_round_to(effective_bits_len - n, digit_bit_size) / digit_bit_size; + if (num_sign == IntNPositiveInteger) { - return bsru(num, effective_bits_len, n, 0, out); + bsru(num, effective_bits_len, n, 0, out); + } else { uint32_t tmp_buf[INTN_MAX_RES_LEN]; - memset(out, 0, INTN_MAX_RES_LEN * sizeof(const uint32_t)); neg(num, counted_digits, tmp_buf); - size_t shifted_len = bsru(tmp_buf, effective_bits_len, n, (uint32_t) -1, out); - - size_t len = size_round_to(effective_bits_len - n, digit_bit_size) / digit_bit_size; - neg_in_place(out, len); - - fprintf(stderr, "shifted: %i, len: %i\n", (int) shifted_len, (int) len); - print_num(out, len); - return len; + bsru(tmp_buf, effective_bits_len, n, (uint32_t) -1, out); + neg_in_place(out, shifted_len); } -} - + return shifted_len; +} /* uint32_t last_digit = 0; From d872572779e9787b8921122b899c9ff83dea0b00 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:17:38 +0200 Subject: [PATCH 21/27] More bitshift cleanups Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index d9a259c12..92460b699 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -1585,10 +1585,6 @@ term bif_erlang_bsl_2(Context *ctx, uint32_t fail_label, int live, term arg1, te #else return make_maybe_boxed_int(ctx, fail_label, live, result); #endif - } else { - fprintf(stderr, "was with b: %i, with: ", (int) b); - term_display(stderr, arg1, ctx); - fprintf(stderr, "\n"); } } @@ -1603,11 +1599,10 @@ term bif_erlang_bsl_2(Context *ctx, uint32_t fail_label, int live, term arg1, te args_to_bigint(arg1, arg2, tmp_buf1, tmp_buf2, &m, &m_len, &m_sign, &n, &n_len, &n_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; - size_t bigres_len; - bigres_len = intn_bsl(m, m_len, b, bigres); - fprintf(stderr, "bigres: %i\n", (int) bigres_len); + size_t bigres_len = intn_bsl(m, m_len, b, bigres); return make_bigint(ctx, fail_label, live, bigres, bigres_len, m_sign); + } else { RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); } @@ -1620,10 +1615,6 @@ term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, te avm_int_t b = term_to_int(arg2); - fprintf(stderr, "going to do b: %i, with: ", (int) b); - term_display(stderr, arg1, ctx); - fprintf(stderr, "\n"); - if (arg1_size <= BOXED_TERMS_REQUIRED_FOR_INT64) { uint64_t a = (uint64_t) term_maybe_unbox_int64(arg1); int64_t result = int64_bsr_safe(a, b); @@ -1649,6 +1640,7 @@ term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, te size_t bigres_len = intn_bsr(m, m_len, m_sign, b, bigres); return make_bigint(ctx, fail_label, live, bigres, bigres_len, m_sign); + } else { RAISE_ERROR_BIF(fail_label, BADARITH_ATOM); } From e059016f2090461b5d4c7f2099d31f6f1e541b28 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:18:27 +0200 Subject: [PATCH 22/27] More bitshift cleanups Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 3049e3f04..cfb16afde 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -669,20 +669,6 @@ size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, return shifted_len; } -/* - uint32_t last_digit = 0; - for (size_t i = initial_zeros; i < out_len - 1; i++) { - uint32_t digit = num[i - initial_zeros]; - if (i - initial_zeros >= counted_digits) { - abort(); - } - out[i] = (digit << digit_left_bit_shift) | (last_digit >> right_shift_n); - last_digit = digit; - fprintf(stderr, "in: %i, (%i), last_digit: %i\n", (int) i - initial_zeros, (int) i, (int) last_digit); - } - out[out_len - 1] = (last_digit >> right_shift_n); -*/ - size_t intn_count_digits(const intn_digit_t *num, size_t num_len) { int i; From 058fde86cfdc8630696b889c2dca6c3dc1fa41a2 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:20:12 +0200 Subject: [PATCH 23/27] Very big shift Signed-off-by: Davide Bettio --- tests/erlang_tests/bigint.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/erlang_tests/bigint.erl b/tests/erlang_tests/bigint.erl index 1e00701b9..b238a3c64 100644 --- a/tests/erlang_tests/bigint.erl +++ b/tests/erlang_tests/bigint.erl @@ -951,6 +951,8 @@ test_bsr() -> <<"C">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(128), 16), <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(250), 16), <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(256), 16), + <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(257), 16), + <<"0">> = erlang:integer_to_binary(Pattern1 bsr ?MODULE:id(600), 16), Pattern2 = erlang:binary_to_integer(?MODULE:id(<<"-CAFE1234AABBCCDD98765432987654321">>), 16), <<"-CAFE1234AABBCCDD98765432988">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(24), 16), @@ -963,6 +965,8 @@ test_bsr() -> <<"-D">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(128), 16), <<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(250), 16), <<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(256), 16), + <<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(257), 16), + <<"-1">> = erlang:integer_to_binary(Pattern2 bsr ?MODULE:id(600), 16), 0. From 9528a13bedcc4c40ee85cd3759b8971cfa46b9a3 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:20:48 +0200 Subject: [PATCH 24/27] bitshift format Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index cfb16afde..1d689b228 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -608,7 +608,7 @@ size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out) out[initial_zeros + i] = maybe_last_out; return initial_zeros + i + 1; } - + return initial_zeros + i; } @@ -654,7 +654,7 @@ size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, return 1; } - size_t shifted_len = size_round_to(effective_bits_len - n, digit_bit_size) / digit_bit_size; + size_t shifted_len = size_round_to(effective_bits_len - n, digit_bit_size) / digit_bit_size; if (num_sign == IntNPositiveInteger) { bsru(num, effective_bits_len, n, 0, out); From b2be17ef192a20d87eeae4e5334f917ad98ced7c Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:21:17 +0200 Subject: [PATCH 25/27] count_and_normalize_sign new line { Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 1d689b228..26eef09a6 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -467,7 +467,8 @@ static inline intn_integer_sign_t sign_bitwise( } // normalizes -0 to 0 -static inline size_t count_and_normalize_sign(const intn_digit_t num[], size_t len, intn_integer_sign_t sign, intn_integer_sign_t *out_sign) { +static inline size_t count_and_normalize_sign(const intn_digit_t num[], size_t len, intn_integer_sign_t sign, intn_integer_sign_t *out_sign) +{ size_t count = intn_count_digits(num, len); if ((count == 0) && (sign == IntNNegativeInteger)) { *out_sign = IntNPositiveInteger; From c7b674f6b6383fcc5c08591ef7d7f3a0394a3965 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:22:02 +0200 Subject: [PATCH 26/27] bitshift 100 columns Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 6 ++++-- src/libAtomVM/intn.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index 26eef09a6..bbc4878b7 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -613,7 +613,8 @@ size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out) return initial_zeros + i; } -void bsru(const uint32_t num[], size_t effective_bits_len, size_t n, uint32_t last_digit, uint32_t *out) +void bsru( + const uint32_t num[], size_t effective_bits_len, size_t n, uint32_t last_digit, uint32_t *out) { size_t digit_bit_size = sizeof(uint32_t) * 8; // 32 @@ -643,7 +644,8 @@ void bsru(const uint32_t num[], size_t effective_bits_len, size_t n, uint32_t la } } -size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out) +size_t intn_bsr( + const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out) { size_t digit_bit_size = sizeof(uint32_t) * 8; size_t counted_digits = intn_count_digits(num, len); diff --git a/src/libAtomVM/intn.h b/src/libAtomVM/intn.h index f7c73fb21..efed1ff6a 100644 --- a/src/libAtomVM/intn.h +++ b/src/libAtomVM/intn.h @@ -86,7 +86,8 @@ size_t intn_bxormn(const intn_digit_t m[], size_t m_len, intn_integer_sign_t m_s intn_integer_sign_t *out_sign); size_t intn_bsl(const uint32_t num[], size_t len, size_t n, uint32_t *out); -size_t intn_bsr(const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out); +size_t intn_bsr( + const uint32_t num[], size_t len, intn_integer_sign_t num_sign, size_t n, uint32_t *out); size_t intn_count_digits(const intn_digit_t *num, size_t num_len); From 80a3a7e45db23738aab21e952026fc445de35f0c Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 6 Jul 2025 01:22:25 +0200 Subject: [PATCH 27/27] count_and_normalize_sign 100 columns Signed-off-by: Davide Bettio --- src/libAtomVM/intn.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libAtomVM/intn.c b/src/libAtomVM/intn.c index bbc4878b7..50965c515 100644 --- a/src/libAtomVM/intn.c +++ b/src/libAtomVM/intn.c @@ -467,7 +467,8 @@ static inline intn_integer_sign_t sign_bitwise( } // normalizes -0 to 0 -static inline size_t count_and_normalize_sign(const intn_digit_t num[], size_t len, intn_integer_sign_t sign, intn_integer_sign_t *out_sign) +static inline size_t count_and_normalize_sign( + const intn_digit_t num[], size_t len, intn_integer_sign_t sign, intn_integer_sign_t *out_sign) { size_t count = intn_count_digits(num, len); if ((count == 0) && (sign == IntNNegativeInteger)) {