py/gc: Reduce code size when MICROPY_GC_SPLIT_HEAP is disabled.

Use C macros to reduce the size of firmware images when the GC split-heap
feature is disabled.

The code size difference of this commit versus HEAD~2 (ie the commit prior
to MICROPY_GC_SPLIT_HEAP being introduced) when split-heap is disabled is:

       bare-arm:    +0 +0.000%
    minimal x86:    +0 +0.000%
       unix x64:   -16 -0.003%
    unix nanbox:   -20 -0.004%
          stm32:    -8 -0.002% PYBV10
         cc3200:    +0 +0.000%
        esp8266:    +8 +0.001% GENERIC
          esp32:    +0 +0.000% GENERIC
            nrf:   -20 -0.011% pca10040
            rp2:    +0 +0.000% PICO
           samd:    -4 -0.003% ADAFRUIT_ITSYBITSY_M4_EXPRESS

The code size difference of this commit versus HEAD~2 split-heap is enabled
with MICROPY_GC_MULTIHEAP=1 (but no extra code to add more heaps):

    unix x64: +1032 +0.197% [incl +544(bss)]
       esp32:  +592 +0.039% GENERIC[incl +16(data) +264(bss)]
pull/8947/head
Rob Knegjens 2022-04-12 21:26:16 -07:00 zatwierdzone przez Damien George
rodzic bcc827d695
commit 4a48531803
2 zmienionych plików z 90 dodań i 44 usunięć

119
py/gc.c
Wyświetl plik

@ -230,6 +230,7 @@ bool gc_is_locked(void) {
return MP_STATE_THREAD(gc_lock_depth) != 0; return MP_STATE_THREAD(gc_lock_depth) != 0;
} }
#if MICROPY_GC_SPLIT_HEAP
// Returns the area to which this pointer belongs, or NULL if it isn't // Returns the area to which this pointer belongs, or NULL if it isn't
// allocated on the GC-managed heap. // allocated on the GC-managed heap.
STATIC inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) { STATIC inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) {
@ -244,6 +245,14 @@ STATIC inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) {
} }
return NULL; return NULL;
} }
#endif
// ptr should be of type void*
#define VERIFY_PTR(ptr) ( \
((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \
&& ptr >= (void *)MP_STATE_MEM(area).gc_pool_start /* must be above start of pool */ \
&& ptr < (void *)MP_STATE_MEM(area).gc_pool_end /* must be below end of pool */ \
)
#ifndef TRACE_MARK #ifndef TRACE_MARK
#if DEBUG_PRINT #if DEBUG_PRINT
@ -257,18 +266,20 @@ STATIC inline mp_state_mem_area_t *gc_get_ptr_area(const void *ptr) {
// children: mark the unmarked child blocks and put those newly marked // children: mark the unmarked child blocks and put those newly marked
// blocks on the stack. When all children have been checked, pop off the // blocks on the stack. When all children have been checked, pop off the
// topmost block on the stack and repeat with that one. // topmost block on the stack and repeat with that one.
STATIC void gc_mark_subtree(mp_gc_stack_item_t item) { #if MICROPY_GC_SPLIT_HEAP
// Start with the item passed in the argument. STATIC void gc_mark_subtree(mp_state_mem_area_t *area, size_t block)
#else
STATIC void gc_mark_subtree(size_t block)
#endif
{
// Start with the block passed in the argument.
size_t sp = 0; size_t sp = 0;
for (;;) { for (;;) {
MICROPY_GC_HOOK_LOOP MICROPY_GC_HOOK_LOOP
#if MICROPY_GC_SPLIT_HEAP #if !MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = item.area; mp_state_mem_area_t * area = &MP_STATE_MEM(area);
#else
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
#endif #endif
size_t block = item.block;
// work out number of consecutive blocks in the chain starting with this one // work out number of consecutive blocks in the chain starting with this one
size_t n_blocks = 0; size_t n_blocks = 0;
@ -283,11 +294,18 @@ STATIC void gc_mark_subtree(mp_gc_stack_item_t item) {
void *ptr = *ptrs; void *ptr = *ptrs;
// If this is a heap pointer that hasn't been marked, mark it and push // If this is a heap pointer that hasn't been marked, mark it and push
// it's children to the stack. // it's children to the stack.
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *ptr_area = gc_get_ptr_area(ptr); mp_state_mem_area_t *ptr_area = gc_get_ptr_area(ptr);
if (!ptr_area) { if (!ptr_area) {
// Not a heap-allocated pointer (might even be random data). // Not a heap-allocated pointer (might even be random data).
continue; continue;
} }
#else
if (!VERIFY_PTR(ptr)) {
continue;
}
mp_state_mem_area_t *ptr_area = area;
#endif
size_t ptr_block = BLOCK_FROM_PTR(ptr_area, ptr); size_t ptr_block = BLOCK_FROM_PTR(ptr_area, ptr);
if (ATB_GET_KIND(ptr_area, ptr_block) != AT_HEAD) { if (ATB_GET_KIND(ptr_area, ptr_block) != AT_HEAD) {
// This block is already marked. // This block is already marked.
@ -297,24 +315,27 @@ STATIC void gc_mark_subtree(mp_gc_stack_item_t item) {
TRACE_MARK(ptr_block, ptr); TRACE_MARK(ptr_block, ptr);
ATB_HEAD_TO_MARK(ptr_area, ptr_block); ATB_HEAD_TO_MARK(ptr_area, ptr_block);
if (sp < MICROPY_ALLOC_GC_STACK_SIZE) { if (sp < MICROPY_ALLOC_GC_STACK_SIZE) {
MP_STATE_MEM(gc_block_stack)[sp] = ptr_block;
#if MICROPY_GC_SPLIT_HEAP #if MICROPY_GC_SPLIT_HEAP
mp_gc_stack_item_t ptr_item = {ptr_area, ptr_block}; MP_STATE_MEM(gc_area_stack)[sp] = ptr_area;
#else
mp_gc_stack_item_t ptr_item = {ptr_block};
#endif #endif
MP_STATE_MEM(gc_stack)[sp++] = ptr_item; sp += 1;
} else { } else {
MP_STATE_MEM(gc_stack_overflow) = 1; MP_STATE_MEM(gc_stack_overflow) = 1;
} }
} }
// Are there any items on the stack? // Are there any blocks on the stack?
if (sp == 0) { if (sp == 0) {
break; // No, stack is empty, we're done. break; // No, stack is empty, we're done.
} }
// pop the next item off the stack // pop the next block off the stack
item = MP_STATE_MEM(gc_stack)[--sp]; sp -= 1;
block = MP_STATE_MEM(gc_block_stack)[sp];
#if MICROPY_GC_SPLIT_HEAP
area = MP_STATE_MEM(gc_area_stack)[sp];
#endif
} }
} }
@ -329,13 +350,10 @@ STATIC void gc_deal_with_stack_overflow(void) {
// trace (again) if mark bit set // trace (again) if mark bit set
if (ATB_GET_KIND(area, block) == AT_MARK) { if (ATB_GET_KIND(area, block) == AT_MARK) {
#if MICROPY_GC_SPLIT_HEAP #if MICROPY_GC_SPLIT_HEAP
mp_gc_stack_item_t item = {area, block}; gc_mark_subtree(area, block);
#else #else
mp_gc_stack_item_t item = {block}; gc_mark_subtree(block);
#endif #endif
// *MP_STATE_MEM(gc_sp)++ = item;
// gc_drain_stack();
gc_mark_subtree(item);
} }
} }
} }
@ -435,22 +453,31 @@ static void *gc_get_ptr(void **ptrs, int i) {
} }
void gc_collect_root(void **ptrs, size_t len) { void gc_collect_root(void **ptrs, size_t len) {
#if !MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
#endif
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
MICROPY_GC_HOOK_LOOP MICROPY_GC_HOOK_LOOP
void *ptr = gc_get_ptr(ptrs, i); void *ptr = gc_get_ptr(ptrs, i);
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area = gc_get_ptr_area(ptr); mp_state_mem_area_t *area = gc_get_ptr_area(ptr);
if (area) { if (!area) {
size_t block = BLOCK_FROM_PTR(area, ptr); continue;
if (ATB_GET_KIND(area, block) == AT_HEAD) { }
// An unmarked head: mark it, and mark all its children #else
ATB_HEAD_TO_MARK(area, block); if (!VERIFY_PTR(ptr)) {
#if MICROPY_GC_SPLIT_HEAP continue;
mp_gc_stack_item_t item = {area, block}; }
#else #endif
mp_gc_stack_item_t item = {block}; size_t block = BLOCK_FROM_PTR(area, ptr);
#endif if (ATB_GET_KIND(area, block) == AT_HEAD) {
gc_mark_subtree(item); // An unmarked head: mark it, and mark all its children
} ATB_HEAD_TO_MARK(area, block);
#if MICROPY_GC_SPLIT_HEAP
gc_mark_subtree(area, block);
#else
gc_mark_subtree(block);
#endif
} }
} }
} }
@ -716,8 +743,15 @@ void gc_free(void *ptr) {
} }
// get the GC block number corresponding to this pointer // get the GC block number corresponding to this pointer
mp_state_mem_area_t *area = gc_get_ptr_area(ptr); mp_state_mem_area_t *area;
#if MICROPY_GC_SPLIT_HEAP
area = gc_get_ptr_area(ptr);
assert(area); assert(area);
#else
assert(VERIFY_PTR(ptr));
area = &MP_STATE_MEM(area);
#endif
size_t block = BLOCK_FROM_PTR(area, ptr); size_t block = BLOCK_FROM_PTR(area, ptr);
assert(ATB_GET_KIND(area, block) == AT_HEAD); assert(ATB_GET_KIND(area, block) == AT_HEAD);
@ -760,7 +794,18 @@ void gc_free(void *ptr) {
size_t gc_nbytes(const void *ptr) { size_t gc_nbytes(const void *ptr) {
GC_ENTER(); GC_ENTER();
mp_state_mem_area_t *area = gc_get_ptr_area(ptr);
mp_state_mem_area_t *area;
#if MICROPY_GC_SPLIT_HEAP
area = gc_get_ptr_area(ptr);
#else
if (VERIFY_PTR(ptr)) {
area = &MP_STATE_MEM(area);
} else {
area = NULL;
}
#endif
if (area) { if (area) {
size_t block = BLOCK_FROM_PTR(area, ptr); size_t block = BLOCK_FROM_PTR(area, ptr);
if (ATB_GET_KIND(area, block) == AT_HEAD) { if (ATB_GET_KIND(area, block) == AT_HEAD) {
@ -829,8 +874,14 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
GC_ENTER(); GC_ENTER();
// get the GC block number corresponding to this pointer // get the GC block number corresponding to this pointer
mp_state_mem_area_t *area = gc_get_ptr_area(ptr); mp_state_mem_area_t *area;
#if MICROPY_GC_SPLIT_HEAP
area = gc_get_ptr_area(ptr);
assert(area); assert(area);
#else
assert(VERIFY_PTR(ptr));
area = &MP_STATE_MEM(area);
#endif
size_t block = BLOCK_FROM_PTR(area, ptr); size_t block = BLOCK_FROM_PTR(area, ptr);
assert(ATB_GET_KIND(area, block) == AT_HEAD); assert(ATB_GET_KIND(area, block) == AT_HEAD);

Wyświetl plik

@ -89,15 +89,6 @@ typedef struct _mp_state_mem_area_t {
size_t gc_last_free_atb_index; size_t gc_last_free_atb_index;
} mp_state_mem_area_t; } mp_state_mem_area_t;
// This structure holds a single stacked block and the area it is on. Used
// during garbage collection.
typedef struct {
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area;
#endif
size_t block;
} mp_gc_stack_item_t;
// This structure hold information about the memory allocation system. // This structure hold information about the memory allocation system.
typedef struct _mp_state_mem_t { typedef struct _mp_state_mem_t {
#if MICROPY_MEM_STATS #if MICROPY_MEM_STATS
@ -109,7 +100,11 @@ typedef struct _mp_state_mem_t {
mp_state_mem_area_t area; mp_state_mem_area_t area;
int gc_stack_overflow; int gc_stack_overflow;
mp_gc_stack_item_t gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]; MICROPY_GC_STACK_ENTRY_TYPE gc_block_stack[MICROPY_ALLOC_GC_STACK_SIZE];
#if MICROPY_GC_SPLIT_HEAP
// Array that tracks the area for each block on gc_block_stack.
mp_state_mem_area_t *gc_area_stack[MICROPY_ALLOC_GC_STACK_SIZE];
#endif
// This variable controls auto garbage collection. If set to 0 then the // This variable controls auto garbage collection. If set to 0 then the
// GC won't automatically run when gc_alloc can't find enough blocks. But // GC won't automatically run when gc_alloc can't find enough blocks. But