core: Introduce MICROPY_MAP_SPLIT (unfinished).
Signed-off-by: Jeff Epler <jepler@gmail.com>
This commit is contained in:
parent
8bf6e22a2e
commit
cb62a54966
8 changed files with 185 additions and 40 deletions
|
|
@ -5,6 +5,9 @@
|
|||
// Use the minimal starting configuration (disables all optional features).
|
||||
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_MINIMUM)
|
||||
|
||||
// save a little ROM, maybe ...
|
||||
#define MICROPY_MAP_SPLIT (1)
|
||||
|
||||
// You can disable the built-in MicroPython compiler by setting the following
|
||||
// config option to 0. If you do this then you won't get a REPL prompt, but you
|
||||
// will still be able to execute pre-compiled scripts, compiled with mpy-cross.
|
||||
|
|
|
|||
|
|
@ -243,8 +243,8 @@ static void mp_emit_common_populate_module_context(mp_emit_common_t *emit, qstr
|
|||
mp_module_context_alloc_tables(context, qstr_map_used, emit->const_obj_list.len);
|
||||
for (size_t i = 0; i < emit->qstr_map.alloc; ++i) {
|
||||
if (mp_map_slot_is_filled(&emit->qstr_map, i)) {
|
||||
size_t idx = MP_OBJ_SMALL_INT_VALUE(emit->qstr_map.table[i].value);
|
||||
qstr qst = MP_OBJ_QSTR_VALUE(emit->qstr_map.table[i].key);
|
||||
size_t idx = MP_OBJ_SMALL_INT_VALUE(mp_map_slot_value(&emit->qstr_map, i));
|
||||
qstr qst = MP_OBJ_QSTR_VALUE(mp_map_slot_key(&emit->qstr_map, i));
|
||||
context->constants.qstr_table[idx] = qst;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
76
py/map.c
76
py/map.c
|
|
@ -89,17 +89,31 @@ static size_t get_hash_alloc_greater_or_equal_to(size_t x) {
|
|||
void mp_map_init(mp_map_t *map, size_t n) {
|
||||
if (n == 0) {
|
||||
map->alloc = 0;
|
||||
#if MICROPY_MAP_SPLIT
|
||||
map->keys.o = NULL;
|
||||
map->values = NULL;
|
||||
#else
|
||||
map->table = NULL;
|
||||
#endif
|
||||
} else {
|
||||
map->alloc = n;
|
||||
#if MICROPY_MAP_SPLIT
|
||||
map->keys.q = m_new0(qstr_short_t, map->alloc);
|
||||
map->values = m_new0(mp_obj_t, map->alloc);
|
||||
#else
|
||||
map->table = m_new0(mp_map_elem_t, map->alloc);
|
||||
#endif
|
||||
}
|
||||
map->used = 0;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->is_fixed = 0;
|
||||
#if MICROPY_MAP_SPLIT
|
||||
map->values_mutable = 1;
|
||||
#endif
|
||||
map->is_ordered = 0;
|
||||
}
|
||||
|
||||
#if !MICROPY_MAP_SPLIT
|
||||
void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table) {
|
||||
map->alloc = n;
|
||||
map->used = n;
|
||||
|
|
@ -108,30 +122,71 @@ void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table) {
|
|||
map->is_ordered = 1;
|
||||
map->table = (mp_map_elem_t *)table;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Differentiate from mp_map_clear() - semantics is different
|
||||
void mp_map_deinit(mp_map_t *map) {
|
||||
static void mp_map_release_storage(mp_map_t *map) {
|
||||
if (!map->is_fixed) {
|
||||
#if MICROPY_MAP_SPLIT
|
||||
if (map->all_keys_are_qstrs) {
|
||||
m_del(short_qstr_t, map->keys.q, map->alloc);
|
||||
} else {
|
||||
m_del(mp_obj_t, map->keys.o, map->alloc);
|
||||
}
|
||||
map->keys.q = NULL;
|
||||
m_del(mp_obj_t, map->values, map->alloc);
|
||||
map->values = NULL;
|
||||
#else
|
||||
m_del(mp_map_elem_t, map->table, map->alloc);
|
||||
map->table = NULL;
|
||||
#endif
|
||||
}
|
||||
map->used = map->alloc = 0;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->is_fixed = 0;
|
||||
}
|
||||
// Differentiate from mp_map_clear() - semantics is different
|
||||
void mp_map_deinit(mp_map_t *map) {
|
||||
mp_map_release_storage(map);
|
||||
}
|
||||
|
||||
void mp_map_clear(mp_map_t *map) {
|
||||
if (!map->is_fixed) {
|
||||
m_del(mp_map_elem_t, map->table, map->alloc);
|
||||
}
|
||||
map->alloc = 0;
|
||||
map->used = 0;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->is_fixed = 0;
|
||||
map->table = NULL;
|
||||
mp_map_release_storage(map);
|
||||
}
|
||||
|
||||
static void mp_map_rehash(mp_map_t *map) {
|
||||
size_t old_alloc = map->alloc;
|
||||
size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1);
|
||||
DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc);
|
||||
#if MICROPY_MAP_SPLIT
|
||||
bool all_keys_are_qstrs = map->all_keys_are_qstrs
|
||||
mp_map_key_u old_keys = map->keys;
|
||||
mp_map_key_u new_keys;
|
||||
if (all_keys_are_qstrs) {
|
||||
new_keys.q = m_new0(short_qstr_t, new_alloc)
|
||||
} else {
|
||||
new_keys.o = m_new0(mp_obj_t, new_alloc);
|
||||
}
|
||||
mp_obj_t *old_values = map->values;
|
||||
mp_obj_t *new_values = m_new0(mp_map_elem_t, new_alloc);
|
||||
// If we reach this point, table resizing succeeded, now we can edit the old map.
|
||||
map->alloc = new_alloc;
|
||||
map->used = 0;
|
||||
map->keys = new_keys;
|
||||
map->values = new_values;
|
||||
for (size_t i = 0; i < old_alloc; i++) {
|
||||
if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) {
|
||||
mp_obj_t key;
|
||||
if (all_keys_are_qstrs) {
|
||||
key = MP_OBJ_NEW_QSTR(old_table.q[i]);
|
||||
} else {
|
||||
key = old_table.o[i];
|
||||
}
|
||||
mp_map_lookup(map, key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_values[i];
|
||||
}
|
||||
}
|
||||
m_del(mp_map_elem_t, old_keys, old_alloc);
|
||||
m_del(mp_map_elem_t, old_values, old_alloc);
|
||||
#else
|
||||
mp_map_elem_t *old_table = map->table;
|
||||
mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc);
|
||||
// If we reach this point, table resizing succeeded, now we can edit the old map.
|
||||
|
|
@ -145,6 +200,7 @@ static void mp_map_rehash(mp_map_t *map) {
|
|||
}
|
||||
}
|
||||
m_del(mp_map_elem_t, old_table, old_alloc);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MP_MAP_LOOKUP behaviour:
|
||||
|
|
|
|||
|
|
@ -515,6 +515,12 @@
|
|||
#define MICROPY_COMP_RETURN_IF_EXPR (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
|
||||
#endif
|
||||
|
||||
// Whether to use the "split map" format for maps.
|
||||
// This adds code in the map lookup paths but reduces the size of the maps themselves.
|
||||
#ifndef MICROPY_MAP_SPLIT
|
||||
#define MICROPY_MAP_SPLIT (0)
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Internal debugging stuff */
|
||||
|
||||
|
|
|
|||
85
py/obj.h
85
py/obj.h
|
|
@ -413,6 +413,35 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
|
|||
// These macros are used to define constant map/dict objects
|
||||
// You can put "static" in front of the definition to make it local
|
||||
|
||||
#if MICROPY_MAP_SPLIT
|
||||
#define MP_TABLE_SIZE(table_name) MP_ARRAY_SIZE(table_name##_keys)
|
||||
#define MP_DEFINE_CONST_MAP(map_name, table_name) \
|
||||
const mp_map_t map_name = { \
|
||||
.all_keys_are_qstrs = 1, \
|
||||
.is_fixed = 1, \
|
||||
.is_ordered = 1, \
|
||||
.used = MP_TABLE_SIZE(table_name), \
|
||||
.alloc = MP_TABLE_SIZE(table_name), \
|
||||
.keys.u = (qstr_short_t *)(table_name##_keys), \
|
||||
.values = (mp_obj_t *)(table_name##_values), \
|
||||
}
|
||||
|
||||
#define MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(dict_name, table_name, o, n) \
|
||||
const mp_obj_dict_t dict_name = { \
|
||||
.base = {&mp_type_dict}, \
|
||||
.map = { \
|
||||
.all_keys_are_qstrs = 1, \
|
||||
.is_fixed = 1, \
|
||||
.is_ordered = 1, \
|
||||
.used = n, \
|
||||
.alloc = n, \
|
||||
.table = MP_MAP_TABLE_CAST(table_name), \
|
||||
.keys.u = (qstr_short_t *)(table_name##_keys), \
|
||||
.values = (mp_obj_t *)(table_name##_values), \
|
||||
}, \
|
||||
}
|
||||
#else
|
||||
#define MP_TABLE_SIZE(table_name) MP_ARRAY_SIZE(table_name)
|
||||
#define MP_DEFINE_CONST_MAP(map_name, table_name) \
|
||||
const mp_map_t map_name = { \
|
||||
.all_keys_are_qstrs = 1, \
|
||||
|
|
@ -423,7 +452,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
|
|||
.table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \
|
||||
}
|
||||
|
||||
#define MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, n) \
|
||||
#define MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(dict_name, table_name, o, n) \
|
||||
const mp_obj_dict_t dict_name = { \
|
||||
.base = {&mp_type_dict}, \
|
||||
.map = { \
|
||||
|
|
@ -432,11 +461,16 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t;
|
|||
.is_ordered = 1, \
|
||||
.used = n, \
|
||||
.alloc = n, \
|
||||
.table = (mp_map_elem_t *)(mp_rom_map_elem_t *)table_name, \
|
||||
.table = (mp_map_elem_t *)(mp_rom_map_elem_t *)(table_name + o), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define MP_DEFINE_CONST_DICT(dict_name, table_name) MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, MP_ARRAY_SIZE(table_name))
|
||||
#endif
|
||||
|
||||
#define MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, n) \
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(dict_name, table_name, 0, n)
|
||||
|
||||
#define MP_DEFINE_CONST_DICT(dict_name, table_name) MP_DEFINE_CONST_DICT_WITH_SIZE(dict_name, table_name, MP_TABLE_SIZE(table_name))
|
||||
|
||||
// These macros are used to declare and define constant staticmethod and classmethod objects
|
||||
// You can put "static" in front of the definitions to make them local
|
||||
|
|
@ -479,13 +513,29 @@ typedef struct _mp_rom_map_elem_t {
|
|||
mp_rom_obj_t value;
|
||||
} mp_rom_map_elem_t;
|
||||
|
||||
#if MICROPY_MAP_SPLIT
|
||||
typedef union _mp_map_key_u {
|
||||
qstr_short_t *q;
|
||||
mp_obj_t *o;
|
||||
} mp_map_key_u;
|
||||
#endif
|
||||
|
||||
typedef struct _mp_map_t {
|
||||
size_t all_keys_are_qstrs : 1;
|
||||
size_t is_fixed : 1; // if set, table is fixed/read-only and can't be modified
|
||||
size_t is_ordered : 1; // if set, table is an ordered array, not a hash map
|
||||
size_t used : (8 * sizeof(size_t) - 3);
|
||||
size_t is_fixed : 1; // if set, table is fixed/read-only and can't be modified
|
||||
#if MICROPY_MAP_SPLIT
|
||||
size_t values_mutable : 1; // if set, table's values are mutable even if the map is_fixed
|
||||
#else
|
||||
#endif
|
||||
size_t used : (8 * sizeof(size_t) - 3 - MICROPY_MAP_SPLIT);
|
||||
size_t alloc;
|
||||
#if MICROPY_MAP_SPLIT
|
||||
mp_map_key_u keys;
|
||||
mp_obj_t *values;
|
||||
#else
|
||||
mp_map_elem_t *table;
|
||||
#endif
|
||||
} mp_map_t;
|
||||
|
||||
// mp_set_lookup requires these constants to have the values they do
|
||||
|
|
@ -496,9 +546,30 @@ typedef enum _mp_map_lookup_kind_t {
|
|||
MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND = 3, // only valid for mp_set_lookup
|
||||
} mp_map_lookup_kind_t;
|
||||
|
||||
static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) {
|
||||
static inline mp_obj_t mp_map_slot_key(const mp_map_t *map, size_t pos) {
|
||||
assert(pos < map->alloc);
|
||||
return (map)->table[pos].key != MP_OBJ_NULL && (map)->table[pos].key != MP_OBJ_SENTINEL;
|
||||
#if MICROPY_MAP_SPLIT
|
||||
if ((map)->all_keys_are_qstrs) {
|
||||
return MP_OBJ_NEW_QSTR(map->keys.q[pos]);
|
||||
} else {
|
||||
return map->keys.o[pos];
|
||||
}
|
||||
#else
|
||||
return map->values[pos].key;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline mp_obj_t mp_map_slot_value(const mp_map_t *map, size_t pos) {
|
||||
assert(pos < map->alloc);
|
||||
#if MICROPY_MAP_SPLIT
|
||||
return map->keys.o[pos];
|
||||
#else
|
||||
return map->values[pos].value;
|
||||
#endif
|
||||
}
|
||||
static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) {
|
||||
mp_obj_t key = mp_map_slot_key(map, pos);
|
||||
return key != MP_OBJ_NULL && key != MP_OBJ_SENTINEL;
|
||||
}
|
||||
|
||||
void mp_map_init(mp_map_t *map, size_t n);
|
||||
|
|
|
|||
25
py/objstr.c
25
py/objstr.c
|
|
@ -2149,33 +2149,38 @@ static const mp_rom_map_elem_t array_bytearray_str_bytes_locals_table[] = {
|
|||
#define TABLE_ENTRIES_ARRAY 0
|
||||
#endif
|
||||
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_str_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT,
|
||||
MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT));
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(mp_obj_str_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table,
|
||||
0,
|
||||
TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_HEX + TABLE_ENTRIES_COMPAT);
|
||||
|
||||
#if TABLE_ENTRIES_COMPAT == 0
|
||||
#define mp_obj_bytes_locals_dict mp_obj_str_locals_dict
|
||||
#else
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_bytes_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY,
|
||||
MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_COMPAT));
|
||||
array_bytearray_str_bytes_locals_table,
|
||||
TABLE_ENTRIES_ARRAY,
|
||||
MP_TABLE_SIZE(array_bytearray_str_bytes_locals_table) - (TABLE_ENTRIES_ARRAY + TABLE_ENTRIES_COMPAT));
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_bytearray_locals_dict,
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(mp_obj_bytearray_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table,
|
||||
MP_ARRAY_SIZE(array_bytearray_str_bytes_locals_table) - TABLE_ENTRIES_COMPAT);
|
||||
0,
|
||||
MP_TABLE_SIZE(array_bytearray_str_bytes_locals_table) - TABLE_ENTRIES_COMPAT);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_ARRAY
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_array_locals_dict,
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(mp_obj_array_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table,
|
||||
0,
|
||||
TABLE_ENTRIES_ARRAY);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW && MICROPY_PY_BUILTINS_BYTES_HEX
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE(mp_obj_memoryview_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table + TABLE_ENTRIES_ARRAY,
|
||||
MP_DEFINE_CONST_DICT_WITH_SIZE_AND_OFFSET(mp_obj_memoryview_locals_dict,
|
||||
array_bytearray_str_bytes_locals_table,
|
||||
TABLE_ENTRIES_ARRAY,
|
||||
1); // Just the "hex" entry.
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -38,19 +38,23 @@
|
|||
#define MP_ROM_TABLE_MERGED(storage, name, contents) const storage mp_rom_map_elem_t name[] = { \
|
||||
BOOST_PP_SEQ_FOR_EACH(MP_TABLE_ENTRY, _, contents) \
|
||||
}
|
||||
#define MP_ROM_TABLE MP_ROM_TABLE_MERGED
|
||||
|
||||
// A hypothetical future format for ROM tables
|
||||
#define MP_TABLE_KEY(r, x, element) BOOST_PP_TUPLE_ELEM(0, element),
|
||||
#define MP_TABLE_VALUE(r, x, element) \
|
||||
BOOST_PP_EXPAND(BOOST_PP_TUPLE_ELEM(1, element) (BOOST_PP_TUPLE_ELEM(2, element))),
|
||||
#define MP_ROM_TABLE_SEPARATE(storage, name, contents) \
|
||||
const storage struct { \
|
||||
qstr_short_t keys[BOOST_PP_SEQ_SIZE(contents)]; \
|
||||
mp_rom_obj_t values[BOOST_PP_SEQ_SIZE(contents)]; \
|
||||
} name = { \
|
||||
{ BOOST_PP_SEQ_FOR_EACH(MP_TABLE_KEY, _, contents) }, \
|
||||
#define MP_ROM_TABLE_SPLIT(storage, name, contents) \
|
||||
const storage qstr_short_t name##keys[] = { \
|
||||
BOOST_PP_SEQ_FOR_EACH(MP_TABLE_KEY, _, contents) \
|
||||
}, \
|
||||
const storage mp_rom_obj_t name##values[] = { \
|
||||
{ BOOST_PP_SEQ_FOR_EACH(MP_TABLE_VALUE, _, contents) }, \
|
||||
}
|
||||
|
||||
#if MICROPY_MAP_SPLIT
|
||||
#define MP_ROM_TABLE MP_ROM_TABLE_SPLIT
|
||||
#else
|
||||
#define MP_ROM_TABLE MP_ROM_TABLE_MERGED
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -889,12 +889,12 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
|
|||
for (size_t j = 0; j < map->alloc; j++) {
|
||||
if (mp_map_slot_is_filled(map, j)) {
|
||||
// the key must be a qstr, so intern it if it's a string
|
||||
mp_obj_t key = map->table[j].key;
|
||||
mp_obj_t key = mp_map_slot_key(map, j);
|
||||
if (!mp_obj_is_qstr(key)) {
|
||||
key = mp_obj_str_intern_checked(key);
|
||||
}
|
||||
args2[args2_len++] = key;
|
||||
args2[args2_len++] = map->table[j].value;
|
||||
args2[args2_len++] = mp_map_slot_key(map->table, j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1634,9 +1634,9 @@ void mp_import_all(mp_obj_t module) {
|
|||
// Entry in module global scope may be generated programmatically
|
||||
// (and thus be not a qstr for longer names). Avoid turning it in
|
||||
// qstr if it has '_' and was used exactly to save memory.
|
||||
const char *name = mp_obj_str_get_str(map->table[i].key);
|
||||
const char *name = mp_obj_str_get_str(mp_map_slot_key(map->table, i));
|
||||
if (*name != '_') {
|
||||
qstr qname = mp_obj_str_get_qstr(map->table[i].key);
|
||||
qstr qname = mp_obj_str_get_qstr(mp_map_slot_key(map->table, i));
|
||||
mp_store_name(qname, map->table[i].value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue