Skip to content

Commit e81e035

Browse files
committed
opcodesswitch: add support to big integer constants
Allow literals bigger than 64 bit, such as: 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF As a side note, bigger literals than (2^256 - 1) are encoded as external terms. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent caf8a8d commit e81e035

File tree

2 files changed

+124
-14
lines changed

2 files changed

+124
-14
lines changed

src/libAtomVM/opcodesswitch.h

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "debug.h"
3131
#include "defaultatoms.h"
3232
#include "exportedfunction.h"
33+
#include "intn.h"
3334
#include "nifs.h"
3435
#include "opcodes.h"
3536
#include "scheduler.h"
@@ -233,13 +234,11 @@ typedef dreg_t dreg_gc_safe_t;
233234
break; \
234235
case COMPACT_NBITS_VALUE:{ \
235236
int sz = (first_byte >> 5) + 2; \
236-
if (UNLIKELY(sz > 8)) { \
237-
/* TODO: when first_byte >> 5 is 7, a different encoding is used */ \
238-
fprintf(stderr, "Unexpected nbits vaue @ %" PRIuPTR "\n", (uintptr_t) ((decode_pc) - 1)); \
239-
AVM_ABORT(); \
240-
break; \
237+
if (LIKELY(sz <= 8)) { \
238+
(decode_pc) += sz; \
239+
} else { \
240+
(decode_pc) += decode_nbits_integer(NULL, (decode_pc), NULL); \
241241
} \
242-
(decode_pc) += sz; \
243242
break; \
244243
} \
245244
default: \
@@ -712,11 +711,10 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
712711
\
713712
case COMPACT_NBITS_VALUE: { \
714713
size_t num_bytes = (first_byte >> 5) + 2; \
715-
dest_term = large_integer_to_term(ctx, num_bytes, decode_pc); \
714+
dest_term = large_integer_to_term(ctx, num_bytes, &decode_pc); \
716715
if (UNLIKELY(term_is_invalid_term(dest_term))) { \
717716
HANDLE_ERROR(); \
718717
} \
719-
(decode_pc) += num_bytes; \
720718
break; \
721719
} \
722720
default: \
@@ -1511,28 +1509,39 @@ static inline term maybe_alloc_boxed_integer_fragment_helper(Context *ctx, avm_i
15111509
}
15121510
}
15131511

1514-
static term large_integer_to_term(Context *ctx, int num_bytes, const uint8_t *compact_term)
1512+
static size_t decode_nbits_integer(Context *ctx, const uint8_t *encoded, term *out_term);
1513+
1514+
static term large_integer_to_term(Context *ctx, int num_bytes, const uint8_t **encoded)
15151515
{
1516+
const uint8_t *compact_term = *encoded;
15161517
switch (num_bytes) {
1518+
case 0:
1519+
case 1:
1520+
UNREACHABLE();
1521+
15171522
case 2: {
1523+
*encoded += 2;
15181524
int16_t ret_val16 = ((int16_t) compact_term[0]) << 8 | compact_term[1];
15191525
return maybe_alloc_boxed_integer_fragment_helper(ctx, ret_val16, 2);
15201526
}
15211527

15221528
case 3: {
1529+
*encoded += 3;
15231530
struct Int24 ret_val24;
15241531
ret_val24.val24 = ((int32_t) compact_term[0]) << 16 | ((int32_t) compact_term[1] << 8) | compact_term[2];
15251532
return maybe_alloc_boxed_integer_fragment_helper(ctx, ret_val24.val24, 3);
15261533
}
15271534

15281535
case 4: {
1536+
*encoded += 4;
15291537
int32_t ret_val32;
15301538
ret_val32 = ((int32_t) compact_term[0]) << 24 | ((int32_t) compact_term[1] << 16)
15311539
| ((int32_t) compact_term[2] << 8) | compact_term[3];
15321540
return maybe_alloc_boxed_integer_fragment_helper(ctx, ret_val32, 4);
15331541
}
15341542

15351543
case 5: {
1544+
*encoded += 5;
15361545
struct Int40 ret_val40;
15371546
ret_val40.val40 = ((int64_t) compact_term[0]) << 32 | ((int64_t) compact_term[1] << 24)
15381547
| ((int64_t) compact_term[2] << 16) | ((int64_t) compact_term[3] << 8)
@@ -1542,6 +1551,7 @@ static term large_integer_to_term(Context *ctx, int num_bytes, const uint8_t *co
15421551
}
15431552

15441553
case 6: {
1554+
*encoded += 6;
15451555
struct Int48 ret_val48;
15461556
ret_val48.val48 = ((int64_t) compact_term[0]) << 40 | ((int64_t) compact_term[1] << 32)
15471557
| ((int64_t) compact_term[2] << 24) | ((int64_t) compact_term[3] << 16)
@@ -1551,6 +1561,7 @@ static term large_integer_to_term(Context *ctx, int num_bytes, const uint8_t *co
15511561
}
15521562

15531563
case 7: {
1564+
*encoded += 7;
15541565
struct Int56 ret_val56;
15551566
ret_val56.val56 = ((int64_t) compact_term[0]) << 48 | ((int64_t) compact_term[1] << 40)
15561567
| ((int64_t) compact_term[2] << 32) | ((int64_t) compact_term[3] << 24)
@@ -1561,6 +1572,7 @@ static term large_integer_to_term(Context *ctx, int num_bytes, const uint8_t *co
15611572
}
15621573

15631574
case 8: {
1575+
*encoded += 8;
15641576
int64_t ret_val64;
15651577
ret_val64 = ((int64_t) compact_term[0]) << 56 | ((int64_t) compact_term[1] << 48)
15661578
| ((int64_t) compact_term[2] << 40) | ((int64_t) compact_term[3] << 32)
@@ -1570,10 +1582,15 @@ static term large_integer_to_term(Context *ctx, int num_bytes, const uint8_t *co
15701582
return maybe_alloc_boxed_integer_fragment_helper(ctx, ret_val64, 8);
15711583
}
15721584

1573-
default:
1574-
ctx->x[0] = ERROR_ATOM;
1575-
ctx->x[1] = OVERFLOW_ATOM;
1576-
return term_invalid_term();
1585+
case 9: {
1586+
term int_term;
1587+
*encoded += decode_nbits_integer(ctx, compact_term, &int_term);
1588+
return int_term;
1589+
}
1590+
1591+
default: {
1592+
UNREACHABLE();
1593+
}
15771594
}
15781595
}
15791596

@@ -1747,6 +1764,54 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
17471764

17481765
#endif
17491766

1767+
static size_t decode_nbits_integer(Context *ctx, const uint8_t *encoded, term *out_term)
1768+
{
1769+
const uint8_t *new_encoded = encoded;
1770+
unsigned int len;
1771+
DECODE_LITERAL(len, new_encoded);
1772+
1773+
len += 9;
1774+
1775+
if (out_term) {
1776+
intn_integer_sign_t sign;
1777+
intn_digit_t bigint[INTN_MAX_RES_LEN];
1778+
int count = intn_from_integer_bytes(new_encoded, len, IntnSigned, bigint, &sign);
1779+
if (UNLIKELY(count < 0)) {
1780+
// this is likely unreachable, compiler seem to generate an external term
1781+
// and to encode this as SMALL_BIG_EXT, so I don't think this code is executed
1782+
ctx->x[0] = ERROR_ATOM;
1783+
ctx->x[1] = OVERFLOW_ATOM;
1784+
*out_term = term_invalid_term();
1785+
goto return_size;
1786+
}
1787+
1788+
size_t intn_data_size;
1789+
size_t rounded_res_len;
1790+
term_intn_to_term_size(count, &intn_data_size, &rounded_res_len);
1791+
1792+
Heap heap;
1793+
if (UNLIKELY(
1794+
memory_init_heap(&heap, BOXED_INTN_SIZE(intn_data_size)) != MEMORY_GC_OK)) {
1795+
ctx->x[0] = ERROR_ATOM;
1796+
ctx->x[1] = OUT_OF_MEMORY_ATOM;
1797+
*out_term = term_invalid_term();
1798+
goto return_size;
1799+
}
1800+
1801+
term bigint_term
1802+
= term_create_uninitialized_intn(intn_data_size, (term_integer_sign_t) sign, &heap);
1803+
intn_digit_t *dest_buf = (void *) term_intn_data(bigint_term);
1804+
intn_copy(bigint, count, dest_buf, rounded_res_len);
1805+
1806+
memory_heap_append_heap(&ctx->heap, &heap);
1807+
1808+
*out_term = bigint_term;
1809+
}
1810+
1811+
return_size:
1812+
return (new_encoded - encoded) + len;
1813+
}
1814+
17501815
#ifndef __clang__
17511816
#pragma GCC diagnostic push
17521817
#ifdef __GNUC__

tests/erlang_tests/bigint.erl

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
sort/1,
2828
twice/1,
2929
fact/1,
30+
lit_ovf1/0,
31+
lit_ovf2/0,
3032
divtrunc/2,
3133
the_out_of_order_list/0,
3234
the_ordered_list/0,
@@ -49,7 +51,8 @@ start() ->
4951
parse_bigint() +
5052
test_cmp() +
5153
conv_to_from_float() +
52-
external_term_decode().
54+
external_term_decode() +
55+
big_literals().
5356

5457
test_mul() ->
5558
Expected_INT64_MIN = ?MODULE:pow(-2, 63),
@@ -612,6 +615,48 @@ external_term_decode() ->
612615
),
613616
0.
614617

618+
big_literals() ->
619+
<<"-CAFE1234ABCD9876EFAB0189FEDCBA98">> = ?MODULE:id(
620+
erlang:integer_to_binary(?MODULE:id(-16#CAFE1234ABCD9876EFAB0189FEDCBA98), 16)
621+
),
622+
<<"-CAFE1234ABCD9876EFAB0189FEDCBA984">> = ?MODULE:id(
623+
erlang:integer_to_binary(?MODULE:id(-16#CAFE1234ABCD9876EFAB0189FEDCBA984), 16)
624+
),
625+
<<"-CAFE1234ABCD9876EFAB0189FEDCBA9842">> = ?MODULE:id(
626+
erlang:integer_to_binary(?MODULE:id(-16#CAFE1234ABCD9876EFAB0189FEDCBA9842), 16)
627+
),
628+
<<"CAFE1234ABCD9876EFAB0189FEDCBA9842">> = ?MODULE:id(
629+
erlang:integer_to_binary(?MODULE:id(16#CAFE1234ABCD9876EFAB0189FEDCBA9842), 16)
630+
),
631+
632+
<<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">> = ?MODULE:id(
633+
erlang:integer_to_binary(
634+
?MODULE:id(16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF), 16
635+
)
636+
),
637+
638+
<<"-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">> = ?MODULE:id(
639+
erlang:integer_to_binary(
640+
?MODULE:id(-16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF), 16
641+
)
642+
),
643+
644+
% this cannot be tested
645+
% bigger literals, such as the one here, are encoded using an external term
646+
% (having SMALL_BIG_EXT type).
647+
% The reader function is not able to distinguish between different kind of invalid
648+
% errors, such as overflow, so this cannot be tested.
649+
% ok = expect_overflow(fun ?MODULE:lit_ovf1/0),
650+
% ok = expect_overflow(fun ?MODULE:lit_ovf2/0),
651+
652+
0.
653+
654+
lit_ovf1() ->
655+
?MODULE:id(16#10000000000000000000000000000000000000000000000000000000000000000).
656+
657+
lit_ovf2() ->
658+
?MODULE:id(-16#10000000000000000000000000000000000000000000000000000000000000000).
659+
615660
divtrunc(X, Y) ->
616661
erlang:trunc(X / Y).
617662

0 commit comments

Comments
 (0)