Skip to content

Commit 5bc0a65

Browse files
committed
term: implement big integer term_compare
Refactor term_compare to use metadata such as size and sign before performing any integer comparison (that might be expensive for big integers). Perform digit by digit comparison for big integers only when size and sign are equal. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent c0289be commit 5bc0a65

File tree

3 files changed

+390
-7
lines changed

3 files changed

+390
-7
lines changed

src/libAtomVM/term.c

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -637,12 +637,105 @@ TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalC
637637
}
638638

639639
} else if (term_is_any_integer(t) && term_is_any_integer(other)) {
640-
avm_int64_t t_int = term_maybe_unbox_int64(t);
641-
avm_int64_t other_int = term_maybe_unbox_int64(other);
642-
if (t_int == other_int) {
643-
CMP_POP_AND_CONTINUE();
640+
term_integer_sign_t t_sign;
641+
size_t t_size;
642+
if (term_is_boxed(t)) {
643+
t_sign = term_boxed_integer_sign(t);
644+
t_size = term_boxed_size(t);
645+
} else {
646+
t_sign = term_integer_sign_from_int(term_to_int(t));
647+
t_size = 0;
648+
}
649+
term_integer_sign_t other_sign;
650+
size_t other_size;
651+
if (term_is_boxed(other)) {
652+
other_sign = term_boxed_integer_sign(other);
653+
other_size = term_boxed_size(other);
654+
} else {
655+
other_sign = term_integer_sign_from_int(term_to_int(other));
656+
other_size = 0;
657+
}
658+
659+
_Static_assert(
660+
TermPositiveInteger < TermNegativeInteger, "Unexpected sign definition in term.h");
661+
if (t_sign < other_sign) {
662+
result = TermGreaterThan;
663+
break;
664+
} else if (t_sign > other_sign) {
665+
result = TermLessThan;
666+
break;
667+
}
668+
669+
TermCompareResult more_digits_result;
670+
TermCompareResult less_digits_result;
671+
if (t_sign == TermPositiveInteger) {
672+
more_digits_result = TermGreaterThan;
673+
less_digits_result = TermLessThan;
674+
} else {
675+
more_digits_result = TermLessThan;
676+
less_digits_result = TermGreaterThan;
677+
}
678+
679+
if (t_size == other_size) {
680+
const term *t_ptr = term_to_const_term_ptr(t);
681+
const term *other_ptr = term_to_const_term_ptr(other);
682+
bool equals = true;
683+
if (t_size == 1) {
684+
if (t_ptr[1] != other_ptr[1]) {
685+
result = (t_ptr[1] > other_ptr[1]) ? TermGreaterThan : TermLessThan;
686+
break;
687+
}
688+
#if BOXED_TERMS_REQUIRED_FOR_INT64 == 2
689+
} else if (t_size == 2) {
690+
avm_int64_t t64 = term_unbox_int64(t);
691+
avm_int64_t other64 = term_unbox_int64(other);
692+
if (t64 != other64) {
693+
result = (t64 > other64) ? TermGreaterThan : TermLessThan;
694+
break;
695+
}
696+
#endif
697+
} else {
698+
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
699+
// on 64-bit big endian systems, term size is 64 bit, so a term
700+
// contains 2 intn_digit_t
701+
// however inside a big integer digits are in "little endian" order
702+
// so comparison cannot be directly done in 64-bit chunks
703+
intn_digit_t *t_digits = (intn_digit_t *) t_ptr;
704+
intn_digit_t *other_digits = (intn_digit_t *) other_ptr;
705+
size_t digits_per_term = (sizeof(term) / sizeof(intn_digit_t));
706+
size_t digit_count = (1 + t_size) * digits_per_term;
707+
// t_digits[0] ... t_digits[digits_per_term - 1] is the boxed header
708+
for (size_t i = digit_count - 1; i >= digits_per_term; i--) {
709+
if (t_digits[i] != other_digits[i]) {
710+
result = (t_digits[i] > other_digits[i]) ? more_digits_result
711+
: less_digits_result;
712+
equals = false;
713+
break;
714+
}
715+
}
716+
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
717+
for (size_t i = t_size; i >= 1; i--) {
718+
if (t_ptr[i] != other_ptr[i]) {
719+
result = (t_ptr[i] > other_ptr[i]) ? more_digits_result
720+
: less_digits_result;
721+
equals = false;
722+
break;
723+
}
724+
}
725+
#else
726+
#error "Unsupported endianess"
727+
#endif
728+
}
729+
if (equals) {
730+
CMP_POP_AND_CONTINUE();
731+
} else {
732+
break;
733+
}
734+
} else if (t_size > other_size) {
735+
result = more_digits_result;
736+
break;
644737
} else {
645-
result = (t_int > other_int) ? TermGreaterThan : TermLessThan;
738+
result = less_digits_result;
646739
break;
647740
}
648741

src/libAtomVM/term.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -897,9 +897,15 @@ static inline avm_int64_t term_maybe_unbox_int64(term maybe_boxed_int)
897897
}
898898
}
899899

900+
static inline term_integer_sign_t term_integer_sign_from_int(avm_int_t value)
901+
{
902+
avm_uint_t uvalue = ((avm_uint_t) value);
903+
return (term_integer_sign_t) ((uvalue >> (TERM_BITS - 1)) << TERM_BOXED_INTEGER_SIGN_BIT_POS);
904+
}
905+
900906
static inline term term_make_boxed_int(avm_int_t value, Heap *heap)
901907
{
902-
avm_uint_t sign = (((avm_uint_t) value) >> (TERM_BITS - 1)) << TERM_BOXED_INTEGER_SIGN_BIT_POS;
908+
avm_uint_t sign = (avm_uint_t) term_integer_sign_from_int(value);
903909
term *boxed_int = memory_heap_alloc(heap, 1 + BOXED_TERMS_REQUIRED_FOR_INT);
904910
boxed_int[0] = (BOXED_TERMS_REQUIRED_FOR_INT << 6) | TERM_BOXED_POSITIVE_INTEGER | sign;
905911
boxed_int[1] = value;

0 commit comments

Comments
 (0)