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)]
This commit is contained in:
parent
bcc827d695
commit
4a48531803
119
py/gc.c
119
py/gc.c
|
@ -230,6 +230,7 @@ bool gc_is_locked(void) {
|
|||
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
|
||||
// allocated on the GC-managed heap.
|
||||
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;
|
||||
}
|
||||
#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
|
||||
#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
|
||||
// blocks on the stack. When all children have been checked, pop off the
|
||||
// topmost block on the stack and repeat with that one.
|
||||
STATIC void gc_mark_subtree(mp_gc_stack_item_t item) {
|
||||
// Start with the item passed in the argument.
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
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;
|
||||
for (;;) {
|
||||
MICROPY_GC_HOOK_LOOP
|
||||
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
mp_state_mem_area_t *area = item.area;
|
||||
#else
|
||||
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
|
||||
#if !MICROPY_GC_SPLIT_HEAP
|
||||
mp_state_mem_area_t * area = &MP_STATE_MEM(area);
|
||||
#endif
|
||||
size_t block = item.block;
|
||||
|
||||
// work out number of consecutive blocks in the chain starting with this one
|
||||
size_t n_blocks = 0;
|
||||
|
@ -283,11 +294,18 @@ STATIC void gc_mark_subtree(mp_gc_stack_item_t item) {
|
|||
void *ptr = *ptrs;
|
||||
// If this is a heap pointer that hasn't been marked, mark it and push
|
||||
// it's children to the stack.
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
mp_state_mem_area_t *ptr_area = gc_get_ptr_area(ptr);
|
||||
if (!ptr_area) {
|
||||
// Not a heap-allocated pointer (might even be random data).
|
||||
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);
|
||||
if (ATB_GET_KIND(ptr_area, ptr_block) != AT_HEAD) {
|
||||
// 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);
|
||||
ATB_HEAD_TO_MARK(ptr_area, ptr_block);
|
||||
if (sp < MICROPY_ALLOC_GC_STACK_SIZE) {
|
||||
MP_STATE_MEM(gc_block_stack)[sp] = ptr_block;
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
mp_gc_stack_item_t ptr_item = {ptr_area, ptr_block};
|
||||
#else
|
||||
mp_gc_stack_item_t ptr_item = {ptr_block};
|
||||
MP_STATE_MEM(gc_area_stack)[sp] = ptr_area;
|
||||
#endif
|
||||
MP_STATE_MEM(gc_stack)[sp++] = ptr_item;
|
||||
sp += 1;
|
||||
} else {
|
||||
MP_STATE_MEM(gc_stack_overflow) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Are there any items on the stack?
|
||||
// Are there any blocks on the stack?
|
||||
if (sp == 0) {
|
||||
break; // No, stack is empty, we're done.
|
||||
}
|
||||
|
||||
// pop the next item off the stack
|
||||
item = MP_STATE_MEM(gc_stack)[--sp];
|
||||
// pop the next block off the stack
|
||||
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
|
||||
if (ATB_GET_KIND(area, block) == AT_MARK) {
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
mp_gc_stack_item_t item = {area, block};
|
||||
gc_mark_subtree(area, block);
|
||||
#else
|
||||
mp_gc_stack_item_t item = {block};
|
||||
gc_mark_subtree(block);
|
||||
#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) {
|
||||
#if !MICROPY_GC_SPLIT_HEAP
|
||||
mp_state_mem_area_t *area = &MP_STATE_MEM(area);
|
||||
#endif
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
MICROPY_GC_HOOK_LOOP
|
||||
void *ptr = gc_get_ptr(ptrs, i);
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
mp_state_mem_area_t *area = gc_get_ptr_area(ptr);
|
||||
if (area) {
|
||||
size_t block = BLOCK_FROM_PTR(area, ptr);
|
||||
if (ATB_GET_KIND(area, block) == AT_HEAD) {
|
||||
// An unmarked head: mark it, and mark all its children
|
||||
ATB_HEAD_TO_MARK(area, block);
|
||||
#if MICROPY_GC_SPLIT_HEAP
|
||||
mp_gc_stack_item_t item = {area, block};
|
||||
#else
|
||||
mp_gc_stack_item_t item = {block};
|
||||
#endif
|
||||
gc_mark_subtree(item);
|
||||
}
|
||||
if (!area) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (!VERIFY_PTR(ptr)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
size_t block = BLOCK_FROM_PTR(area, ptr);
|
||||
if (ATB_GET_KIND(area, block) == AT_HEAD) {
|
||||
// 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
|
||||
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);
|
||||
#else
|
||||
assert(VERIFY_PTR(ptr));
|
||||
area = &MP_STATE_MEM(area);
|
||||
#endif
|
||||
|
||||
size_t block = BLOCK_FROM_PTR(area, ptr);
|
||||
assert(ATB_GET_KIND(area, block) == AT_HEAD);
|
||||
|
||||
|
@ -760,7 +794,18 @@ void gc_free(void *ptr) {
|
|||
|
||||
size_t gc_nbytes(const void *ptr) {
|
||||
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) {
|
||||
size_t block = BLOCK_FROM_PTR(area, ptr);
|
||||
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();
|
||||
|
||||
// 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);
|
||||
#else
|
||||
assert(VERIFY_PTR(ptr));
|
||||
area = &MP_STATE_MEM(area);
|
||||
#endif
|
||||
size_t block = BLOCK_FROM_PTR(area, ptr);
|
||||
assert(ATB_GET_KIND(area, block) == AT_HEAD);
|
||||
|
||||
|
|
15
py/mpstate.h
15
py/mpstate.h
|
@ -89,15 +89,6 @@ typedef struct _mp_state_mem_area_t {
|
|||
size_t gc_last_free_atb_index;
|
||||
} 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.
|
||||
typedef struct _mp_state_mem_t {
|
||||
#if MICROPY_MEM_STATS
|
||||
|
@ -109,7 +100,11 @@ typedef struct _mp_state_mem_t {
|
|||
mp_state_mem_area_t area;
|
||||
|
||||
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
|
||||
// GC won't automatically run when gc_alloc can't find enough blocks. But
|
||||
|
|
Loading…
Reference in New Issue