diff options
author | Sverker Eriksson <sverker@erlang.org> | 2023-01-24 18:04:04 +0100 |
---|---|---|
committer | Sverker Eriksson <sverker@erlang.org> | 2023-01-24 18:04:04 +0100 |
commit | 619438e6b6edef3bef5c0145d781bc458b7e5bc3 (patch) | |
tree | e94f02b80889f8c479cca4442a0a0e4a1adbc7f6 | |
parent | b889662a5e0dba0c82590dbb1ef4bedf344c71d2 (diff) | |
parent | c013d3ad41bd4d44ef5467f8ad2f09a3901f5126 (diff) | |
download | erlang-619438e6b6edef3bef5c0145d781bc458b7e5bc3.tar.gz |
Merge branch 'jv-atom-pointer-order' OTP-18414
-rw-r--r-- | erts/emulator/beam/beam_common.c | 2 | ||||
-rw-r--r-- | erts/emulator/beam/beam_transform_helpers.c | 2 | ||||
-rw-r--r-- | erts/emulator/beam/dist.c | 9 | ||||
-rw-r--r-- | erts/emulator/beam/erl_map.c | 27 | ||||
-rw-r--r-- | erts/emulator/beam/erl_map.h | 2 | ||||
-rw-r--r-- | erts/emulator/beam/erl_utils.h | 12 | ||||
-rw-r--r-- | erts/emulator/beam/external.c | 18 | ||||
-rw-r--r-- | erts/emulator/beam/utils.c | 213 | ||||
-rw-r--r-- | erts/emulator/test/map_SUITE.erl | 4 | ||||
-rw-r--r-- | lib/kernel/test/logger_formatter_SUITE.erl | 2 | ||||
-rw-r--r-- | lib/stdlib/test/io_SUITE.erl | 11 |
11 files changed, 233 insertions, 69 deletions
diff --git a/erts/emulator/beam/beam_common.c b/erts/emulator/beam/beam_common.c index a6e8905794..d2893e45c6 100644 --- a/erts/emulator/beam/beam_common.c +++ b/erts/emulator/beam/beam_common.c @@ -2186,7 +2186,7 @@ erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live, Sint c; key = *old_keys; - if ((c = (key == new_key) ? 0 : CMP_TERM(key, new_key)) < 0) { + if ((c = (key == new_key) ? 0 : erts_cmp_flatmap_keys(key, new_key)) < 0) { /* Copy old key and value */ *kp++ = key; *hp++ = *old_vals; diff --git a/erts/emulator/beam/beam_transform_helpers.c b/erts/emulator/beam/beam_transform_helpers.c index 3a4ed5cd0b..dcc3a2fdc0 100644 --- a/erts/emulator/beam/beam_transform_helpers.c +++ b/erts/emulator/beam/beam_transform_helpers.c @@ -150,7 +150,7 @@ oparg_compare(BeamOpArg* a, BeamOpArg* b) static int oparg_term_compare(SortBeamOpArg* a, SortBeamOpArg* b) { - Sint res = CMP_TERM(a->term, b->term); + Sint res = erts_cmp_flatmap_keys(a->term, b->term); if (res < 0) { return -1; diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 6ac22dfab9..5688ad763f 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -6227,7 +6227,7 @@ nodes(Process *c_p, Eterm node_types, Eterm options) } } else { - Eterm ks[2], *hp, keys_tuple = THE_NON_VALUE; + Eterm ks[2], *hp; Uint map_size = 0, el_xtra, xtra; ErtsHeapFactory hfact; @@ -6262,8 +6262,8 @@ nodes(Process *c_p, Eterm node_types, Eterm options) vs[map_size++] = eni->type; } - info_map = erts_map_from_sorted_ks_and_vs(&hfact, ks, vs, - map_size, &keys_tuple); + info_map = erts_map_from_ks_and_vs(&hfact, ks, vs, map_size); + ASSERT(is_value(info_map)); hp = erts_produce_heap(&hfact, 3+2, xtra); @@ -6849,8 +6849,7 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, map_size++; } - info = erts_map_from_sorted_ks_and_vs(&hfact, ks, vs, - map_size, NULL); + info = erts_map_from_ks_and_vs(&hfact, ks, vs, map_size); ASSERT(is_value(info)); } else { /* Info list */ diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 8cdc3353d0..09c47e57df 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -438,7 +438,9 @@ static Eterm flatmap_from_validated_list(Process *p, Eterm list, Eterm fill_valu idx = size; - while(idx > 0 && (c = CMP_TERM(key,ks[idx-1])) < 0) { idx--; } + while(idx > 0 && (c = erts_cmp_flatmap_keys(key,ks[idx-1])) < 0) { + idx--; + } if (c == 0) { /* last compare was equal, @@ -748,21 +750,6 @@ Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Ui return res; } -Eterm erts_map_from_sorted_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, - Uint n, Eterm *key_tuple) -{ -#ifdef DEBUG - Uint i; - /* verify that key array contains unique and sorted keys... */ - for (i = 1; i < n; i++) { - ASSERT(CMP_TERM(ks[i-1], ks[i]) < 0); - } -#endif - - return from_ks_and_vs(factory, ks, vs, n, key_tuple, NULL); -} - - Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n, Eterm key, Eterm value) { @@ -1383,7 +1370,7 @@ static Eterm flatmap_merge(Process *p, Eterm nodeA, Eterm nodeB) { vs2 = flatmap_get_values(mp2); while(i1 < n1 && i2 < n2) { - c = (ks1[i1] == ks2[i2]) ? 0 : CMP_TERM(ks1[i1],ks2[i2]); + c = (ks1[i1] == ks2[i2]) ? 0 : erts_cmp_flatmap_keys(ks1[i1],ks2[i2]); if (c == 0) { /* use righthand side arguments map value, * but advance both maps */ @@ -2161,7 +2148,7 @@ Eterm erts_maps_put(Process *p, Eterm key, Eterm value, Eterm map) { ASSERT(n >= 0); /* copy map in order */ - while (n && ((c = CMP_TERM(*ks, key)) < 0)) { + while (n && ((c = erts_cmp_flatmap_keys(*ks, key)) < 0)) { *shp++ = *ks++; *hp++ = *vs++; n--; @@ -3059,7 +3046,7 @@ int erts_validate_and_sort_flatmap(flatmap_t* mp) for (ix = 1; ix < sz; ix++) { jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + while( jx > 0 && (c = erts_cmp_flatmap_keys(ks[jx],ks[jx-1])) <= 0 ) { /* identical key -> error */ if (c == 0) return 0; @@ -3090,7 +3077,7 @@ void erts_usort_flatmap(flatmap_t* mp) for (ix = 1; ix < sz; ix++) { jx = ix; - while( jx > 0 && (c = CMP_TERM(ks[jx],ks[jx-1])) <= 0 ) { + while( jx > 0 && (c = erts_cmp_flatmap_keys(ks[jx],ks[jx-1])) <= 0 ) { /* identical key -> remove it */ if (c == 0) { sys_memmove(ks+jx-1,ks+jx,(sz-ix)*sizeof(Eterm)); diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h index 3430ec6d7a..d3a023bc07 100644 --- a/erts/emulator/beam/erl_map.h +++ b/erts/emulator/beam/erl_map.h @@ -106,8 +106,6 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int rejec erts_hashmap_from_ks_and_vs_extra((F), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE); Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n); -Eterm erts_map_from_sorted_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, - Uint n, Eterm *key_tuple); Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n, Eterm k, Eterm v); diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 2142e8d757..dce3665a8d 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -112,6 +112,7 @@ int eq(Eterm, Eterm); ERTS_GLB_INLINE Sint erts_cmp(Eterm, Eterm, int, int); ERTS_GLB_INLINE int erts_cmp_atoms(Eterm a, Eterm b); +ERTS_GLB_INLINE Sint erts_cmp_flatmap_keys(Eterm, Eterm); Sint erts_cmp_compound(Eterm, Eterm, int, int); @@ -231,6 +232,17 @@ ERTS_GLB_INLINE Sint erts_cmp(Eterm a, Eterm b, int exact, int eq_only) { return erts_cmp_compound(a,b,exact,eq_only); } +/* + * Only to be used for the *internal* sort order of flatmap keys. + */ +ERTS_GLB_INLINE Sint erts_cmp_flatmap_keys(Eterm key_a, Eterm key_b) { + if (is_atom(key_a) && is_atom(key_b)) { + return key_a - key_b; + } + return erts_cmp(key_a, key_b, 1, 0); +} + + #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ #endif diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index fe39809e34..d175664a59 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -3197,7 +3197,6 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, long num_reductions = r; n = next_map_element - map_array; - ASSERT(n > MAP_SMALL_MAP_LIMIT); if (ctx == NULL) { /* No context means that the external representation of term * being encoded will fit in a heap binary (64 bytes). This @@ -3468,9 +3467,20 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, Eterm *kptr = flatmap_get_keys(mp); Eterm *vptr = flatmap_get_values(mp); - WSTACK_PUSH4(s, (UWord)kptr, (UWord)vptr, - ENC_MAP_PAIR, size); - } + if (dflags & DFLAG_DETERMINISTIC) { + ASSERT(map_array == NULL); + next_map_element = map_array = alloc_map_array(size); + while (size--) { + *next_map_element++ = *kptr++; + *next_map_element++ = *vptr++; + } + WSTACK_PUSH2(s, ENC_START_SORTING_MAP, THE_NON_VALUE); + } + else { + WSTACK_PUSH4(s, (UWord)kptr, (UWord)vptr, + ENC_MAP_PAIR, size); + } + } } else { Eterm hdr; Uint node_sz; diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index 2d1cca9c41..1b4e1ca385 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -108,9 +108,11 @@ void *ycf_debug_get_stack_start(void) { #endif #if defined(DEBUG) +# define IF_DEBUG(X) X # define DBG_RANDOM_REDS(REDS, SEED) \ ((REDS) * 0.01 * erts_sched_local_random_float(SEED)) #else +# define IF_DEBUG(X) # define DBG_RANDOM_REDS(REDS, SEED) (REDS) #endif @@ -1500,6 +1502,88 @@ not_equal: return 0; } +static +Sint compare_flatmap_atom_keys(const Eterm* a_keys, + const Eterm* b_keys, + int nkeys) +{ + Eterm min_key = THE_NON_VALUE; + Eterm a, b; + int ai, bi; + Sint res; + + ASSERT(nkeys > 0); + ai = nkeys; + while (*a_keys == *b_keys) { + ASSERT(is_atom(*a_keys)); + a_keys++; + b_keys++; + if (--ai == 0) + return 0; + } + + /* Found atom key diff. Find the smallest atom. */ + bi = ai; + a = *a_keys; + b = *b_keys; + IF_DEBUG(res = 0); + do { + ASSERT((ai && is_atom(a)) || (!ai && a == ERTS_UINT_MAX)); + ASSERT((bi && is_atom(b)) || (!bi && b == ERTS_UINT_MAX)); + + if (a < b) { + ASSERT(ai && is_atom(a)); + if (is_non_value(min_key) || erts_cmp_atoms(a, min_key) < 0) { + min_key = a; + res = -1; + } + a = --ai ? *(++a_keys) : ERTS_UINT_MAX; + } + else if (a > b) { + ASSERT(bi && is_atom(b)); + if (is_non_value(min_key) || erts_cmp_atoms(b, min_key) < 0) { + min_key = b; + res = 1; + } + b = --bi ? *(++b_keys) : ERTS_UINT_MAX; + } + else { + ASSERT(ai && bi && is_atom(a) && is_atom(b)); + a = --ai ? *(++a_keys) : ERTS_UINT_MAX; + b = --bi ? *(++b_keys) : ERTS_UINT_MAX; + } + } while (ai|bi); + ASSERT(is_atom(min_key)); + ASSERT(res != 0); + return res; +} + +static +Sint compare_flatmap_atom_key_values(const Eterm* keys, + const Eterm* a_vals, + const Eterm* b_vals, + int nkeys, + int exact) +{ + Eterm min_key = THE_NON_VALUE; + Sint res = 0; + + ASSERT(nkeys > 0); + do { + ASSERT(is_atom(*keys)); + if (is_non_value(min_key) || erts_cmp_atoms(*keys, min_key) < 0) { + Sint valcmp = erts_cmp(*a_vals, *b_vals, exact, 0); + if (valcmp) { + min_key = *keys; + res = valcmp; + } + } + keys++; + a_vals++; + b_vals++; + } while (--nkeys); + return res; +} /* @@ -1556,10 +1640,13 @@ Sint erts_cmp_compound(Eterm a, Eterm b, int exact, int eq_only) #define HASHMAP_PHASE2_ARE_KEYS_EQUAL 5 #define HASHMAP_PHASE2_IS_MIN_KEY_A 6 #define HASHMAP_PHASE2_IS_MIN_KEY_B 7 - +#define FLATMAP_ATOM_KEYS_OP 8 +#define FLATMAP_ATOM_VALUES_OP 9 #define OP_WORD(OP) (((OP) << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_HEADER) -#define TERM_ARRAY_OP_WORD(SZ) OP_WORD(((SZ) << OP_BITS) | TERM_ARRAY_OP) +#define TERM_ARRAY_OP_WORD(SZ) OP_WORD(((SZ) << OP_BITS) | TERM_ARRAY_OP) +#define FLATMAP_ATOM_KEYS_OP_WORD(SZ) OP_WORD(((SZ) << OP_BITS) | FLATMAP_ATOM_KEYS_OP) +#define FLATMAP_ATOM_VALUES_OP_WORD(SZ) OP_WORD(((SZ) << OP_BITS) | FLATMAP_ATOM_VALUES_OP) #define GET_OP(WORD) (ASSERT(is_header(WORD)), ((WORD) >> _TAG_PRIMARY_SIZE) & OP_MASK) #define GET_OP_ARG(WORD) (ASSERT(is_header(WORD)), ((WORD) >> (OP_BITS + _TAG_PRIMARY_SIZE))) @@ -1735,50 +1822,99 @@ tailrecur_ne: { struct erts_cmp_hashmap_state* sp; if (is_flatmap_header(ahdr)) { + flatmap_t* afm = (flatmap_t*)flatmap_val(a); + flatmap_t* bfm; if (!is_flatmap(b)) { if (is_hashmap(b)) { - aa = (Eterm *)flatmap_val(a); - i = flatmap_get_size((flatmap_t*)aa) - hashmap_size(b); - ASSERT(i != 0); - RETURN_NEQ(i); + ASSERT(flatmap_get_size(afm) < hashmap_size(b)); + RETURN_NEQ(-1); } a_tag = MAP_DEF; goto mixed_types; } - aa = (Eterm *)flatmap_val(a); - bb = (Eterm *)flatmap_val(b); - - i = flatmap_get_size((flatmap_t*)aa); - if (i != flatmap_get_size((flatmap_t*)bb)) { - RETURN_NEQ((int)(i - flatmap_get_size((flatmap_t*)bb))); + bfm = (flatmap_t*)flatmap_val(b); + i = flatmap_get_size(afm); + if (i != flatmap_get_size(bfm)) { + RETURN_NEQ((int)(i - flatmap_get_size(bfm))); } if (i == 0) { goto pop_next; } - aa += 2; - bb += 2; if (exact) { + aa = &afm->keys; + bb = &bfm->keys; i += 1; /* increment for tuple-keys */ goto term_array; } else { - /* Value array */ - WSTACK_PUSH3(stack,(UWord)(bb+1),(UWord)(aa+1),TERM_ARRAY_OP_WORD(i)); - /* Switch back from 'exact' key compare */ - WSTACK_PUSH(stack,OP_WORD(SWITCH_EXACT_OFF_OP)); - /* Now do 'exact' compare of key tuples */ - a = *aa; - b = *bb; - exact = 1; - goto bodyrecur; + Eterm* a_keys = flatmap_get_keys(afm); + Eterm* b_keys = flatmap_get_keys(bfm); + Eterm* a_vals = flatmap_get_values(afm); + Eterm* b_vals = flatmap_get_values(bfm); + int n_numbers; /* sorted before atoms */ + int n_atoms; + int n_rest; /* sorted after atoms */ + int n = 0; + + while (n < i && !(is_atom(a_keys[n]) && + is_atom(b_keys[n]))) { + ++n; + } + n_numbers = n; + while (n < i && (is_atom(a_keys[n]) && + is_atom(b_keys[n]))) { + ++n; + } + n_atoms = n - n_numbers; + n_rest = i - n; + + ASSERT(n_numbers + n_atoms + n_rest == i); + ASSERT(n_atoms || !n_rest); + + if (n_rest) { + WSTACK_PUSH3(stack, + (UWord)&b_vals[n_numbers+n_atoms], + (UWord)&a_vals[n_numbers+n_atoms], + TERM_ARRAY_OP_WORD(n_rest)); + } + if (n_atoms) { + WSTACK_PUSH4(stack, + (UWord)&b_vals[n_numbers], + (UWord)&a_vals[n_numbers], + (UWord)&a_keys[n_numbers], + FLATMAP_ATOM_VALUES_OP_WORD(n_atoms)); + } + if (n_numbers) { + WSTACK_PUSH3(stack, (UWord)b_vals, (UWord)a_vals, + TERM_ARRAY_OP_WORD(n_numbers)); + } + if (!exact) { + WSTACK_PUSH(stack, OP_WORD(SWITCH_EXACT_OFF_OP)); + exact = 1; + } + if (n_rest) { + WSTACK_PUSH3(stack, + (UWord)&b_keys[n_numbers+n_atoms], + (UWord)&a_keys[n_numbers+n_atoms], + TERM_ARRAY_OP_WORD(n_rest)); + } + if (n_atoms) { + WSTACK_PUSH3(stack, + (UWord)&b_keys[n_numbers], + (UWord)&a_keys[n_numbers], + FLATMAP_ATOM_KEYS_OP_WORD(n_atoms)); + } + if (n_numbers) { + WSTACK_PUSH3(stack, (UWord)b_keys, (UWord)a_keys, + TERM_ARRAY_OP_WORD(n_numbers)); + } } + goto pop_next; } if (!is_hashmap(b)) { if (is_flatmap(b)) { - bb = (Eterm *)flatmap_val(b); - i = hashmap_size(a) - flatmap_get_size((flatmap_t*)bb); - ASSERT(i != 0); - RETURN_NEQ(i); + ASSERT(hashmap_size(a) > flatmap_get_size(flatmap_val(b))); + RETURN_NEQ(1); } a_tag = MAP_DEF; goto mixed_types; @@ -2343,6 +2479,29 @@ pop_next: sp->bp = hashmap_iterator_next(&b_stack); goto case_HASHMAP_PHASE2_LOOP; + case FLATMAP_ATOM_KEYS_OP: + i = GET_OP_ARG(something); + aa = (Eterm*) WSTACK_POP(stack); + bb = (Eterm*) WSTACK_POP(stack); + j = compare_flatmap_atom_keys(aa, bb, i); + if (j) + goto not_equal; + else + goto pop_next; + + case FLATMAP_ATOM_VALUES_OP: { + const Eterm* keys; + i = GET_OP_ARG(something); + keys = (const Eterm*) WSTACK_POP(stack); + aa = (Eterm*) WSTACK_POP(stack); + bb = (Eterm*) WSTACK_POP(stack); + j = compare_flatmap_atom_key_values(keys, aa, bb, i, exact); + if (j) + goto not_equal; + else + goto pop_next; + } + default: ASSERT(!"Invalid cmp op"); } /* switch */ diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl index 3dc6e8a46a..99957dde7b 100644 --- a/erts/emulator/test/map_SUITE.erl +++ b/erts/emulator/test/map_SUITE.erl @@ -2968,6 +2968,10 @@ t_erts_internal_order(_Config) when is_list(_Config) -> 0 = erts_internal:cmp_term(2147483648,2147483648), 1 = erts_internal:cmp_term(2147483648,0), + %% Make sure it's not the internal flatmap order + %% where low indexed 'true' < 'a'. + -1 = erts_internal:cmp_term(a,true), + M = #{0 => 0,2147483648 => 0}, true = M =:= binary_to_term(term_to_binary(M)), true = M =:= binary_to_term(term_to_binary(M, [deterministic])), diff --git a/lib/kernel/test/logger_formatter_SUITE.erl b/lib/kernel/test/logger_formatter_SUITE.erl index d160dce32f..bd91d92bbf 100644 --- a/lib/kernel/test/logger_formatter_SUITE.erl +++ b/lib/kernel/test/logger_formatter_SUITE.erl @@ -183,7 +183,7 @@ single_line(_Config) -> ct:log(String3), match = re:run(String3,"\\[1,2,3,4,5,6,7,8,9,10\\]",[{capture,none}]), match = re:run(String3, - "#{a => map,few => accociations,with => a}", + "#{((a => map|with => a|few => accociations)[,}]){3}", [{capture,none}]), %% This part is added to make sure that the previous test made diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 17fd6d41fd..4882f220a3 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -2271,20 +2271,15 @@ format_string(_Config) -> ok. maps(_Config) -> - %% Note that order in which a map is printed is arbitrary. In - %% practice, small maps (non-HAMT) are printed in key order, but - %% the breakpoint for creating big maps (HAMT) is lower in the - %% debug-compiled run-time system than in the optimized run-time - %% system. - %% + %% Note that order in which a map is printed is arbitrary. %% Therefore, play it completely safe by not assuming any order %% in a map with more than one element. "#{}" = fmt("~w", [#{}]), "#{a => b}" = fmt("~w", [#{a=>b}]), - re_fmt(<<"#\\{(a => b),[.][.][.]\\}">>, + re_fmt(<<"#\\{(a => b|c => d),[.][.][.]\\}">>, "~W", [#{a => b,c => d},2]), - re_fmt(<<"#\\{(a => b),[.][.][.]\\}">>, + re_fmt(<<"#\\{(a => b|c => d|e => f),[.][.][.]\\}">>, "~W", [#{a => b,c => d,e => f},2]), "#{}" = fmt("~p", [#{}]), |