Skip to content

Commit fa9752a

Browse files
committed
Add actual support for =:= and =/=
This change makes possible to distinguish between 5.0 and 5 using =:= erlang operator. It has been implemented by adding an opts parameter to term_compare function, which now has a `TermCompareExact` flag for exact term comparison. TermCompareResult enum has not been changed and it still has the same semantic. There are still some TODOs about sorting operations (and similar) used in maps that need to be addressed, and in general map comparison semantic is not yet 100% compliant with OTP. Signed-off-by: Davide Bettio <[email protected]>
1 parent b620e2c commit fa9752a

File tree

5 files changed

+62
-61
lines changed

5 files changed

+62
-61
lines changed

src/libAtomVM/bif.c

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,7 @@ term bif_erlang_xor_2(Context *ctx, term arg1, term arg2)
13311331

13321332
term bif_erlang_equal_to_2(Context *ctx, term arg1, term arg2)
13331333
{
1334-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1334+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
13351335
if (result == TermEquals) {
13361336
return TRUE_ATOM;
13371337
} else if (result & (TermLessThan | TermGreaterThan)) {
@@ -1343,9 +1343,7 @@ term bif_erlang_equal_to_2(Context *ctx, term arg1, term arg2)
13431343

13441344
term bif_erlang_not_equal_to_2(Context *ctx, term arg1, term arg2)
13451345
{
1346-
//TODO: fix this implementation
1347-
//it should compare any kind of type, and 5.0 != 5 is false
1348-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1346+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
13491347
if (result & (TermLessThan | TermGreaterThan)) {
13501348
return TRUE_ATOM;
13511349
} else if (result == TermEquals) {
@@ -1358,7 +1356,7 @@ term bif_erlang_not_equal_to_2(Context *ctx, term arg1, term arg2)
13581356
term bif_erlang_exactly_equal_to_2(Context *ctx, term arg1, term arg2)
13591357
{
13601358
//TODO: 5.0 != 5
1361-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1359+
TermCompareResult result = term_compare(arg1, arg2, TermCompareExact, ctx->global);
13621360
if (result == TermEquals) {
13631361
return TRUE_ATOM;
13641362
} else if (result & (TermLessThan | TermGreaterThan)) {
@@ -1370,8 +1368,7 @@ term bif_erlang_exactly_equal_to_2(Context *ctx, term arg1, term arg2)
13701368

13711369
term bif_erlang_exactly_not_equal_to_2(Context *ctx, term arg1, term arg2)
13721370
{
1373-
//TODO: 5.0 != 5
1374-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1371+
TermCompareResult result = term_compare(arg1, arg2, TermCompareExact, ctx->global);
13751372
if (result & (TermLessThan | TermGreaterThan)) {
13761373
return TRUE_ATOM;
13771374
} else if (result == TermEquals) {
@@ -1383,7 +1380,7 @@ term bif_erlang_exactly_not_equal_to_2(Context *ctx, term arg1, term arg2)
13831380

13841381
term bif_erlang_greater_than_2(Context *ctx, term arg1, term arg2)
13851382
{
1386-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1383+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
13871384
if (result == TermGreaterThan) {
13881385
return TRUE_ATOM;
13891386
} else if (result & (TermEquals | TermLessThan)) {
@@ -1395,7 +1392,7 @@ term bif_erlang_greater_than_2(Context *ctx, term arg1, term arg2)
13951392

13961393
term bif_erlang_less_than_2(Context *ctx, term arg1, term arg2)
13971394
{
1398-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1395+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
13991396
if (result == TermLessThan) {
14001397
return TRUE_ATOM;
14011398
} else if (result & (TermEquals | TermGreaterThan)) {
@@ -1407,7 +1404,7 @@ term bif_erlang_less_than_2(Context *ctx, term arg1, term arg2)
14071404

14081405
term bif_erlang_less_than_or_equal_2(Context *ctx, term arg1, term arg2)
14091406
{
1410-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1407+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
14111408
if (result & (TermLessThan | TermEquals)) {
14121409
return TRUE_ATOM;
14131410
} else if (result == TermGreaterThan) {
@@ -1419,7 +1416,7 @@ term bif_erlang_less_than_or_equal_2(Context *ctx, term arg1, term arg2)
14191416

14201417
term bif_erlang_greater_than_or_equal_2(Context *ctx, term arg1, term arg2)
14211418
{
1422-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
1419+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
14231420
if (result & (TermGreaterThan | TermEquals)) {
14241421
return TRUE_ATOM;
14251422
} else if (result & TermLessThan) {

src/libAtomVM/dictionary.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static DictionaryFunctionResult dictionary_find(
3232
struct ListHead *item;
3333
LIST_FOR_EACH (item, dictionary) {
3434
struct DictEntry *entry = GET_LIST_ENTRY(item, struct DictEntry, head);
35-
TermCompareResult result = term_compare(entry->key, key, global);
35+
TermCompareResult result = term_compare(entry->key, key, TermCompareExact, global);
3636
if (result == TermEquals) {
3737
*found = entry;
3838
return DictionaryOk;

src/libAtomVM/opcodesswitch.h

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,8 @@ static bool sort_kv_pairs(struct kv_pair *kv, int size, GlobalContext *global)
860860
for (int i = 1; i < k; i++) {
861861
term t_max = kv[max_pos].key;
862862
term t = kv[i].key;
863-
TermCompareResult result = term_compare(t, t_max, global);
863+
// TODO: not sure if exact is the right choice here
864+
TermCompareResult result = term_compare(t, t_max, TermCompareExact, global);
864865
if (result == TermGreaterThan) {
865866
max_pos = i;
866867
} else if (UNLIKELY(result == TermCompareMemoryAllocFail)) {
@@ -2182,7 +2183,7 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
21822183
#ifdef IMPL_EXECUTE_LOOP
21832184
TRACE("is_lt/2, label=%i, arg1=%lx, arg2=%lx\n", label, arg1, arg2);
21842185

2185-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
2186+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
21862187
if (result == TermLessThan) {
21872188
NEXT_INSTRUCTION(next_off);
21882189
} else if (result & (TermGreaterThan | TermEquals)) {
@@ -2214,7 +2215,7 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
22142215
#ifdef IMPL_EXECUTE_LOOP
22152216
TRACE("is_ge/2, label=%i, arg1=%lx, arg2=%lx\n", label, arg1, arg2);
22162217

2217-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
2218+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
22182219
if (result & (TermGreaterThan | TermEquals)) {
22192220
NEXT_INSTRUCTION(next_off);
22202221
} else if (result == TermLessThan) {
@@ -2246,8 +2247,7 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
22462247
#ifdef IMPL_EXECUTE_LOOP
22472248
TRACE("is_equal/2, label=%i, arg1=%lx, arg2=%lx\n", label, arg1, arg2);
22482249

2249-
//TODO: implement this
2250-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
2250+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
22512251
if (result == TermEquals) {
22522252
NEXT_INSTRUCTION(next_off);
22532253
} else if (result & (TermLessThan | TermGreaterThan)) {
@@ -2279,7 +2279,7 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
22792279
#ifdef IMPL_EXECUTE_LOOP
22802280
TRACE("is_not_equal/2, label=%i, arg1=%lx, arg2=%lx\n", label, arg1, arg2);
22812281

2282-
TermCompareResult result = term_compare(arg1, arg2, ctx->global);
2282+
TermCompareResult result = term_compare(arg1, arg2, TermCompareNoOpts, ctx->global);
22832283
if (result & (TermLessThan | TermGreaterThan)) {
22842284
NEXT_INSTRUCTION(next_off);
22852285
} else if (result == TermEquals) {
@@ -2311,12 +2311,13 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
23112311
#ifdef IMPL_EXECUTE_LOOP
23122312
TRACE("is_eq_exact/2, label=%i, arg1=%lx, arg2=%lx\n", label, arg1, arg2);
23132313

2314-
// handle error
2315-
//TODO: implement this
2316-
if (term_exactly_equals(arg1, arg2, ctx->global)) {
2314+
TermCompareResult result = term_compare(arg1, arg2, TermCompareExact, ctx->global);
2315+
if (result == TermEquals) {
23172316
NEXT_INSTRUCTION(next_off);
2318-
} else {
2317+
} else if (result & (TermLessThan | TermGreaterThan)) {
23192318
i = POINTER_TO_II(mod->labels[label]);
2319+
} else {
2320+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
23202321
}
23212322
#endif
23222323

@@ -2342,12 +2343,13 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
23422343
#ifdef IMPL_EXECUTE_LOOP
23432344
TRACE("is_not_eq_exact/2, label=%i, arg1=%lx, arg2=%lx\n", label, arg1, arg2);
23442345

2345-
// handle error
2346-
//TODO: implement this
2347-
if (!term_exactly_equals(arg1, arg2, ctx->global)) {
2346+
TermCompareResult result = term_compare(arg1, arg2, TermCompareExact, ctx->global);
2347+
if (result & (TermLessThan | TermGreaterThan)) {
23482348
NEXT_INSTRUCTION(next_off);
2349-
} else {
2349+
} else if (result == TermEquals) {
23502350
i = POINTER_TO_II(mod->labels[label]);
2351+
} else {
2352+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
23512353
}
23522354
#endif
23532355

@@ -5414,7 +5416,8 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
54145416
} else {
54155417
term src_key = term_get_map_key(src, src_pos);
54165418
term new_key = kv[kv_pos].key;
5417-
switch (term_compare(src_key, new_key, ctx->global)) {
5419+
// TODO: not sure if exact is the right choice here
5420+
switch (term_compare(src_key, new_key, TermCompareExact, ctx->global)) {
54185421
case TermLessThan: {
54195422
term src_value = term_get_map_value(src, src_pos);
54205423
term_set_map_assoc(map, j, src_key, src_value);
@@ -6573,9 +6576,12 @@ static bool maybe_call_native(Context *ctx, AtomString module_name, AtomString f
65736576
DECODE_COMPACT_TERM(update_value, code, i, next_off);
65746577
#ifdef IMPL_EXECUTE_LOOP
65756578
if (reuse) {
6576-
// handle error
6577-
if (term_exactly_equals(update_value, term_get_tuple_element(dst, update_ix - 1), ctx->global)) {
6579+
term old_value = term_get_tuple_element(dst, update_ix - 1);
6580+
TermCompareResult result = term_compare(update_value, old_value, TermCompareExact, ctx->global);
6581+
if (result == TermEquals) {
65786582
continue;
6583+
} else if (UNLIKELY(result == TermCompareMemoryAllocFail)) {
6584+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
65796585
}
65806586
reuse = false;
65816587
}

src/libAtomVM/term.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,21 @@ static int term_type_to_index(term t)
369369
if (term_is_invalid_term(t)) {
370370
return 0;
371371

372-
} else if (term_is_number(t)) {
372+
} else if (term_is_any_integer(t)) {
373373
return 1;
374374

375-
} else if (term_is_atom(t)) {
375+
} else if (term_is_float(t)) {
376376
return 2;
377377

378-
} else if (term_is_reference(t)) {
378+
} else if (term_is_atom(t)) {
379379
return 3;
380380

381-
} else if (term_is_function(t)) {
381+
} else if (term_is_reference(t)) {
382382
return 4;
383383

384+
} else if (term_is_function(t)) {
385+
return 5;
386+
384387
} else if (term_is_pid(t)) {
385388
return 6;
386389

@@ -404,7 +407,7 @@ static int term_type_to_index(term t)
404407
}
405408
}
406409

407-
TermCompareResult term_compare(term t, term other, GlobalContext *global)
410+
TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalContext *global)
408411
{
409412
struct TempStack temp_stack;
410413
if (UNLIKELY(temp_stack_init(&temp_stack) != TempStackOk)) {
@@ -560,7 +563,18 @@ TermCompareResult term_compare(term t, term other, GlobalContext *global)
560563
break;
561564
}
562565

563-
} else if (term_is_number(t) && term_is_number(other)) {
566+
} else if (term_is_float(t) && term_is_float(other)) {
567+
avm_float_t t_float = term_to_float(t);
568+
avm_float_t other_float = term_to_float(other);
569+
if (t_float == other_float) {
570+
other = temp_stack_pop(&temp_stack);
571+
t = temp_stack_pop(&temp_stack);
572+
} else {
573+
result = (t_float > other_float) ? TermGreaterThan : TermLessThan;
574+
break;
575+
}
576+
577+
} else if (term_is_number(t) && term_is_number(other) && ((opts & TermCompareExact) != TermCompareExact)) {
564578
avm_float_t t_float = term_conv_to_float(t);
565579
avm_float_t other_float = term_conv_to_float(other);
566580
if (t_float == other_float) {
@@ -570,6 +584,7 @@ TermCompareResult term_compare(term t, term other, GlobalContext *global)
570584
result = (t_float > other_float) ? TermGreaterThan : TermLessThan;
571585
break;
572586
}
587+
573588
} else if (term_is_atom(t) && term_is_atom(other)) {
574589
int t_atom_index = term_to_atom_index(t);
575590
AtomString t_atom_string = (AtomString) valueshashtable_get_value(global->atoms_ids_table,

src/libAtomVM/term.h

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ enum RefcBinaryFlags
106106
RefcBinaryIsConst
107107
};
108108

109+
typedef enum
110+
{
111+
TermCompareNoOpts = 0,
112+
TermCompareExact = 1
113+
} TermCompareOpts;
114+
109115
typedef enum
110116
{
111117
TermCompareMemoryAllocFail = 0,
@@ -131,7 +137,7 @@ extern const term empty_tuple;
131137
* @param global the global context
132138
* @return any of TermEquals, TermLessThan, TermGreaterThan or TermCompareMemoryAllocFail error.
133139
*/
134-
TermCompareResult term_compare(term t, term other, GlobalContext *global);
140+
TermCompareResult term_compare(term t, term other, TermCompareOpts opts, GlobalContext *global);
135141

136142
/**
137143
* @brief Create a reference-counted binary on the heap
@@ -1338,30 +1344,6 @@ static inline int term_list_length(term t, int *proper)
13381344
return len;
13391345
}
13401346

1341-
/**
1342-
* @brief Returns 1 if given terms are exactly equal.
1343-
*
1344-
* @details Compares 2 given terms and returns 1 if they are the same.
1345-
* @param a first term
1346-
* @param b second term
1347-
* @return 1 if they are the same, 0 otherwise.
1348-
*/
1349-
static inline int term_exactly_equals(term a, term b, GlobalContext *global)
1350-
{
1351-
if (a == b) {
1352-
return 1;
1353-
} else {
1354-
TermCompareResult result = term_compare(a, b, global);
1355-
if (result == TermEquals) {
1356-
return 1;
1357-
} else if (UNLIKELY(result == TermCompareMemoryAllocFail)) {
1358-
AVM_ABORT();
1359-
} else {
1360-
return 0;
1361-
}
1362-
}
1363-
}
1364-
13651347
static inline int term_is_float(term t)
13661348
{
13671349
if (term_is_boxed(t)) {
@@ -1656,7 +1638,8 @@ static inline int term_find_map_pos(term map, term key, GlobalContext *global)
16561638
int arity = term_get_tuple_arity(keys);
16571639
for (int i = 0; i < arity; ++i) {
16581640
term k = term_get_tuple_element(keys, i);
1659-
TermCompareResult result = term_compare(key, k, global);
1641+
// TODO: not sure if exact is the right choice here
1642+
TermCompareResult result = term_compare(key, k, TermCompareExact, global);
16601643
if (result == TermEquals) {
16611644
return i;
16621645
} else if (UNLIKELY(result == TermCompareMemoryAllocFail)) {

0 commit comments

Comments
 (0)