diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index f63054bdd6526..fe0c91a7ac672 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -81,11 +81,12 @@ SRC_C += $(SRC_BITMAP) CFLAGS += \ -DCIRCUITPY_AESIO=1 \ -DCIRCUITPY_AUDIOCORE=1 \ - -DCIRCUITPY_AUDIOMIXER=1 \ -DCIRCUITPY_AUDIOCORE_DEBUG=1 \ + -DCIRCUITPY_AUDIOMIXER=1 \ -DCIRCUITPY_BITMAPTOOLS=1 \ -DCIRCUITPY_DISPLAYIO_UNIX=1 \ -DCIRCUITPY_GIFIO=1 \ + -DCIRCUITPY_LONG_LIVED_GC=1 \ -DCIRCUITPY_OS_GETENV=1 \ -DCIRCUITPY_RAINBOWIO=1 \ -DCIRCUITPY_STRUCT=1 \ diff --git a/py/builtinimport.c b/py/builtinimport.c index 92f3150306d10..8e48583898942 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -159,7 +159,7 @@ STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) { // parse, compile and execute the module in its context mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj); mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals); - mp_obj_module_set_globals(module_obj, make_dict_long_lived(mod_globals, 10)); + mp_obj_module_set_globals(module_obj, MP_OBJ_TO_PTR(make_obj_long_lived(MP_OBJ_FROM_PTR(mod_globals)))); } #endif @@ -188,8 +188,7 @@ STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, co // finish nlr block, restore context nlr_pop(); - mp_obj_module_set_globals(module_obj, - make_dict_long_lived(mp_obj_module_get_globals(module_obj), 10)); + mp_obj_module_set_globals(module_obj, MP_OBJ_TO_PTR(make_obj_long_lived(MP_OBJ_FROM_PTR(mod_globals)))); mp_globals_set(old_globals); mp_locals_set(old_locals); } else { diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 639fae9bbc5d8..33239e86e04bc 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -36,6 +36,18 @@ CFLAGS += -DCIRCUITPY=$(CIRCUITPY) CIRCUITPY_FULL_BUILD ?= 1 CFLAGS += -DCIRCUITPY_FULL_BUILD=$(CIRCUITPY_FULL_BUILD) +# CircuitPython implements a "long lived" region in the garbage collector. This +# improves memory fragmentation on low-RAM boards. However, the semantics of +# object identity in imported modules are subtly altered compared to standard +# Python. (https://github.com/adafruit/circuitpython/issues/2687) +# +# This _partially_ disables the long-lived machinery: objects are never copied +# to the upper memory area. However, certain allocations that are initially +# made in the upper memory area still are, and code in the GC to deal with the +# two halves of the heap differently remain, increasing code size. +CIRCUITPY_LONG_LIVED_GC ?= 1 +CFLAGS += -DCIRCUITPY_LONG_LIVED_GC=$(CIRCUITPY_LONG_LIVED_GC) + # Reduce the size of in-flash properties. Requires support in the .ld linker # file, so not enabled by default. CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 0 diff --git a/py/gc.c b/py/gc.c index d723272f04ac5..6dc98d9acbe0b 100644 --- a/py/gc.c +++ b/py/gc.c @@ -505,6 +505,10 @@ bool gc_alloc_possible(void) { // We place long lived objects at the end of the heap rather than the start. This reduces // fragmentation by localizing the heap churn to one portion of memory (the start of the heap.) void *gc_alloc(size_t n_bytes, unsigned int alloc_flags, bool long_lived) { + #if !CIRCUITPY_LONG_LIVED_GC + long_lived = false; + #endif + bool has_finaliser = alloc_flags & GC_ALLOC_FLAG_HAS_FINALISER; size_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK; DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks); @@ -795,6 +799,7 @@ bool gc_has_finaliser(const void *ptr) { } void *gc_make_long_lived(void *old_ptr) { + #if CIRCUITPY_LONG_LIVED_GC // If its already in the long lived section then don't bother moving it. if (old_ptr >= MP_STATE_MEM(gc_lowest_long_lived_ptr)) { return old_ptr; @@ -819,6 +824,9 @@ void *gc_make_long_lived(void *old_ptr) { // confuse things when its mutable.) memcpy(new_ptr, old_ptr, n_bytes); return new_ptr; + #else + return old_ptr; + #endif } #if 0 diff --git a/py/gc_long_lived.c b/py/gc_long_lived.c index 647c4f75b7754..59f003b36909c 100644 --- a/py/gc_long_lived.c +++ b/py/gc_long_lived.c @@ -29,15 +29,19 @@ #include "py/gc.h" #include "py/mpstate.h" -mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_depth) { +#if CIRCUITPY_LONG_LIVED_GC +STATIC mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_depth); +STATIC mp_obj_property_t *make_property_long_lived(mp_obj_property_t *prop, uint8_t max_depth); +STATIC mp_obj_dict_t *make_dict_long_lived(mp_obj_dict_t *dict, uint8_t max_depth); +STATIC mp_obj_str_t *make_str_long_lived(mp_obj_str_t *str); +mp_obj_t make_obj_long_lived_depth(mp_obj_t obj, uint8_t max_depth); + +STATIC mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_depth) { #ifndef MICROPY_ENABLE_GC return fun_bc; #endif - if (fun_bc == NULL || MP_OBJ_FROM_PTR(fun_bc) == mp_const_none || max_depth == 0) { - return fun_bc; - } fun_bc->bytecode = gc_make_long_lived((byte *)fun_bc->bytecode); - fun_bc->globals = make_dict_long_lived(fun_bc->globals, max_depth - 1); + fun_bc->globals = make_dict_long_lived(fun_bc->globals, max_depth); for (uint32_t i = 0; i < gc_nbytes(fun_bc->const_table) / sizeof(mp_obj_t); i++) { // Skip things that aren't allocated on the heap (and hence have zero bytes.) if (gc_nbytes(MP_OBJ_TO_PTR(fun_bc->const_table[i])) == 0) { @@ -49,8 +53,8 @@ mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_dep raw_code->fun_data = gc_make_long_lived((byte *)raw_code->fun_data); raw_code->const_table = gc_make_long_lived((byte *)raw_code->const_table); } - ((mp_uint_t *)fun_bc->const_table)[i] = (mp_uint_t)make_obj_long_lived( - (mp_obj_t)fun_bc->const_table[i], max_depth - 1); + ((mp_uint_t *)fun_bc->const_table)[i] = (mp_uint_t)make_obj_long_lived_depth( + (mp_obj_t)fun_bc->const_table[i], max_depth); } fun_bc->const_table = gc_make_long_lived((mp_uint_t *)fun_bc->const_table); @@ -63,33 +67,30 @@ mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_dep continue; } if (mp_obj_is_type(fun_bc->extra_args[i], &mp_type_dict)) { - fun_bc->extra_args[i] = MP_OBJ_FROM_PTR(make_dict_long_lived(MP_OBJ_TO_PTR(fun_bc->extra_args[i]), max_depth - 1)); + fun_bc->extra_args[i] = MP_OBJ_FROM_PTR(make_dict_long_lived(MP_OBJ_TO_PTR(fun_bc->extra_args[i]), max_depth)); } else { - fun_bc->extra_args[i] = make_obj_long_lived(fun_bc->extra_args[i], max_depth - 1); + fun_bc->extra_args[i] = make_obj_long_lived_depth(fun_bc->extra_args[i], max_depth); } } return gc_make_long_lived(fun_bc); } -mp_obj_property_t *make_property_long_lived(mp_obj_property_t *prop, uint8_t max_depth) { +STATIC mp_obj_property_t *make_property_long_lived(mp_obj_property_t *prop, uint8_t max_depth) { #ifndef MICROPY_ENABLE_GC return prop; #endif - if (max_depth == 0) { - return prop; - } - prop->proxy[0] = make_obj_long_lived(prop->proxy[0], max_depth - 1); - prop->proxy[1] = make_obj_long_lived(prop->proxy[1], max_depth - 1); - prop->proxy[2] = make_obj_long_lived(prop->proxy[2], max_depth - 1); + prop->proxy[0] = make_obj_long_lived_depth(prop->proxy[0], max_depth); + prop->proxy[1] = make_obj_long_lived_depth(prop->proxy[1], max_depth); + prop->proxy[2] = make_obj_long_lived_depth(prop->proxy[2], max_depth); return gc_make_long_lived(prop); } -mp_obj_dict_t *make_dict_long_lived(mp_obj_dict_t *dict, uint8_t max_depth) { +STATIC mp_obj_dict_t *make_dict_long_lived(mp_obj_dict_t *dict, uint8_t max_depth) { #ifndef MICROPY_ENABLE_GC return dict; #endif - if (dict == NULL || max_depth == 0 || dict == &MP_STATE_VM(dict_main) || dict->map.is_fixed) { + if (dict == &MP_STATE_VM(dict_main) || dict->map.is_fixed) { return dict; } // Don't recurse unnecessarily. Return immediately if we've already seen this dict. @@ -105,7 +106,7 @@ mp_obj_dict_t *make_dict_long_lived(mp_obj_dict_t *dict, uint8_t max_depth) { for (size_t i = 0; i < dict->map.alloc; i++) { if (mp_map_slot_is_filled(&dict->map, i)) { mp_obj_t value = dict->map.table[i].value; - dict->map.table[i].value = make_obj_long_lived(value, max_depth - 1); + dict->map.table[i].value = make_obj_long_lived_depth(value, max_depth); } } dict = gc_make_long_lived(dict); @@ -119,31 +120,44 @@ mp_obj_str_t *make_str_long_lived(mp_obj_str_t *str) { return gc_make_long_lived(str); } -mp_obj_t make_obj_long_lived(mp_obj_t obj, uint8_t max_depth) { +mp_obj_t make_obj_long_lived_depth(mp_obj_t obj, uint8_t max_depth) { #ifndef MICROPY_ENABLE_GC return obj; #endif - if (MP_OBJ_TO_PTR(obj) == NULL) { + if (max_depth <= 0) { return obj; } + max_depth -= 1; + + // This also catches the case that obj is a NULL pointer or is a + // "non-object object" such as a number or a qstr. + // If not in the GC pool, do nothing. This can happen (at least) when - // there are frozen mp_type_bytes objects in ROM. - if (!VERIFY_PTR((void *)obj)) { + // there are frozen mp_type_bytes objects in ROM. This also catches the case that the object is MP_OBJ_NULL + // and the case that it's not !mp_obj_is_obj + void *ptr = MP_OBJ_TO_PTR(obj); + if (!VERIFY_PTR((void *)ptr)) { return obj; } - if (mp_obj_is_type(obj, &mp_type_fun_bc)) { - mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(obj); - return MP_OBJ_FROM_PTR(make_fun_bc_long_lived(fun_bc, max_depth)); - } else if (mp_obj_is_type(obj, &mp_type_property)) { - mp_obj_property_t *prop = MP_OBJ_TO_PTR(obj); - return MP_OBJ_FROM_PTR(make_property_long_lived(prop, max_depth)); - } else if (mp_obj_is_type(obj, &mp_type_str) || mp_obj_is_type(obj, &mp_type_bytes)) { - mp_obj_str_t *str = MP_OBJ_TO_PTR(obj); - return MP_OBJ_FROM_PTR(make_str_long_lived(str)); - } else if (mp_obj_is_type(obj, &mp_type_type)) { + + const mp_obj_type_t *type = ((mp_obj_base_t *)ptr)->type; + + if (type == &mp_type_fun_bc) { + ptr = make_fun_bc_long_lived(ptr, max_depth); + } else if (type == &mp_type_dict) { + ptr = make_dict_long_lived(ptr, max_depth); + } else if (type == &mp_type_property) { + ptr = make_property_long_lived(ptr, max_depth); + } else if (type == &mp_type_str || type == &mp_type_bytes) { + ptr = make_str_long_lived(ptr); + } else if (type != &mp_type_type) { // Types are already long lived during creation. - return obj; - } else { - return MP_OBJ_FROM_PTR(gc_make_long_lived(MP_OBJ_TO_PTR(obj))); + ptr = gc_make_long_lived(ptr); } + return MP_OBJ_FROM_PTR(ptr); +} + +mp_obj_t make_obj_long_lived(mp_obj_t obj) { + return make_obj_long_lived_depth(obj, 10); } +#endif diff --git a/py/gc_long_lived.h b/py/gc_long_lived.h index 229bc73911c8f..d1bcd59fe7689 100644 --- a/py/gc_long_lived.h +++ b/py/gc_long_lived.h @@ -34,10 +34,12 @@ #include "py/objproperty.h" #include "py/objstr.h" -mp_obj_fun_bc_t *make_fun_bc_long_lived(mp_obj_fun_bc_t *fun_bc, uint8_t max_depth); -mp_obj_property_t *make_property_long_lived(mp_obj_property_t *prop, uint8_t max_depth); -mp_obj_dict_t *make_dict_long_lived(mp_obj_dict_t *dict, uint8_t max_depth); -mp_obj_str_t *make_str_long_lived(mp_obj_str_t *str); -mp_obj_t make_obj_long_lived(mp_obj_t obj, uint8_t max_depth); +#if CIRCUITPY_LONG_LIVED_GC +mp_obj_t make_obj_long_lived(mp_obj_t obj); +mp_obj_t make_obj_long_lived_depth(mp_obj_t obj, uint8_t depth); +#else +#define make_obj_long_lived(obj) (obj) +#define make_obj_long_lived_depth(obj, depth) (obj) +#endif #endif // MICROPY_INCLUDED_PY_GC_LONG_LIVED_H diff --git a/py/objtype.c b/py/objtype.c index 5b04c361e18b0..af9e001dff681 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1235,7 +1235,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) } } - o->locals_dict = make_dict_long_lived(MP_OBJ_TO_PTR(locals_dict), 10); + o->locals_dict = MP_OBJ_TO_PTR(make_obj_long_lived(locals_dict)); #if ENABLE_SPECIAL_ACCESSORS // Check if the class has any special accessor methods diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index aeb92f59d3b6b..8157039a03ac8 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -126,7 +126,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input // If the code was loaded from a file it's likely to be running for a while so we'll long // live it and collect any garbage before running. if (input_kind == MP_PARSE_FILE_INPUT) { - module_fun = make_obj_long_lived(module_fun, 6); + module_fun = make_obj_long_lived_depth(module_fun, 6); gc_collect(); } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy