36 #if MICROPY_DEBUG_VERBOSE // print debugging info 37 #define DEBUG_PRINT (1) 38 #define DEBUG_printf DEBUG_printf 39 #else // don't print debugging info 40 #define DEBUG_PRINT (0) 41 #define DEBUG_printf(...) (void)0 45 #define EXTENSIVE_HEAP_PROFILING (0) 47 #define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD) 48 #define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK) 61 #define BLOCKS_PER_ATB (4) 62 #define ATB_MASK_0 (0x03) 63 #define ATB_MASK_1 (0x0c) 64 #define ATB_MASK_2 (0x30) 65 #define ATB_MASK_3 (0xc0) 67 #define ATB_0_IS_FREE(a) (((a) & ATB_MASK_0) == 0) 68 #define ATB_1_IS_FREE(a) (((a) & ATB_MASK_1) == 0) 69 #define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0) 70 #define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0) 72 #define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1))) 73 #define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) 74 #define ATB_ANY_TO_FREE(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0) 75 #define ATB_FREE_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0) 76 #define ATB_FREE_TO_TAIL(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0) 77 #define ATB_HEAD_TO_MARK(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0) 78 #define ATB_MARK_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0) 80 #define BLOCK_FROM_PTR(ptr) (((byte*)(ptr) - MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK) 81 #define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (uintptr_t)MP_STATE_MEM(gc_pool_start))) 82 #define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB) 84 #if MICROPY_ENABLE_FINALISER 88 #define BLOCKS_PER_FTB (8) 90 #define FTB_GET(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) 91 #define FTB_SET(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) 92 #define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) 95 #if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL 96 #define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1) 97 #define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex)) 106 end = (
void*)((
uintptr_t)end & (~(BYTES_PER_BLOCK - 1)));
115 #if MICROPY_ENABLE_FINALISER 123 #if MICROPY_ENABLE_FINALISER 124 size_t gc_finaliser_table_byte_len = (
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB;
128 size_t gc_pool_block_len =
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB;
129 MP_STATE_MEM(gc_pool_start) = (
byte*)end - gc_pool_block_len * BYTES_PER_BLOCK;
132 #if MICROPY_ENABLE_FINALISER 139 #if MICROPY_ENABLE_FINALISER 153 #if MICROPY_GC_ALLOC_THRESHOLD 159 #if MICROPY_PY_THREAD 165 #if MICROPY_ENABLE_FINALISER 166 DEBUG_printf(
" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n",
MP_STATE_MEM(gc_finaliser_table_start), gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB);
188 #define VERIFY_PTR(ptr) ( \ 189 ((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 \ 190 && ptr >= (void*)MP_STATE_MEM(gc_pool_start) \ 191 && ptr < (void*)MP_STATE_MEM(gc_pool_end) \ 195 #define VERIFY_MARK_AND_PUSH(ptr) \ 197 if (VERIFY_PTR(ptr)) { \ 198 size_t _block = BLOCK_FROM_PTR(ptr); \ 199 if (ATB_GET_KIND(_block) == AT_HEAD) { \ 201 DEBUG_printf("gc_mark(%p)\n", ptr); \ 202 ATB_HEAD_TO_MARK(_block); \ 203 if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \ 204 *MP_STATE_MEM(gc_sp)++ = _block; \ 206 MP_STATE_MEM(gc_stack_overflow) = 1; \ 212 STATIC void gc_drain_stack(
void) {
221 }
while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
224 void **ptrs = (
void**)PTR_FROM_BLOCK(block);
225 for (
size_t i = n_blocks * BYTES_PER_BLOCK /
sizeof(
void*); i > 0; i--, ptrs++) {
227 VERIFY_MARK_AND_PUSH(ptr);
232 STATIC void gc_deal_with_stack_overflow(
void) {
238 for (
size_t block = 0; block <
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
240 if (ATB_GET_KIND(block) == AT_MARK) {
248 STATIC void gc_sweep(
void) {
249 #if MICROPY_PY_GC_COLLECT_RETVAL 254 for (
size_t block = 0; block <
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
255 switch (ATB_GET_KIND(block)) {
257 #if MICROPY_ENABLE_FINALISER 258 if (FTB_GET(block)) {
260 if (obj->type !=
NULL) {
266 #if MICROPY_ENABLE_SCHEDULER 270 #if MICROPY_ENABLE_SCHEDULER 281 #if MICROPY_PY_GC_COLLECT_RETVAL 288 ATB_ANY_TO_FREE(block);
293 ATB_MARK_TO_HEAD(block);
303 #if MICROPY_GC_ALLOC_THRESHOLD 316 for (
size_t i = 0; i < len; i++) {
318 VERIFY_MARK_AND_PUSH(ptr);
324 gc_deal_with_stack_overflow();
341 for (
size_t block = 0, len = 0, len_free = 0; !finish;) {
342 size_t kind = ATB_GET_KIND(block);
366 finish = (block ==
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB);
369 kind = ATB_GET_KIND(block);
372 if (finish || kind == AT_FREE || kind == AT_HEAD) {
375 }
else if (len == 2) {
381 if (finish || kind == AT_HEAD) {
390 info->
used *= BYTES_PER_BLOCK;
391 info->
free *= BYTES_PER_BLOCK;
395 void *
gc_alloc(
size_t n_bytes,
bool has_finaliser) {
396 size_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK;
418 #if MICROPY_GC_ALLOC_THRESHOLD 431 if (ATB_0_IS_FREE(a)) {
if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0;
goto found; } }
else { n_free = 0; }
432 if (ATB_1_IS_FREE(a)) {
if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1;
goto found; } }
else { n_free = 0; }
433 if (ATB_2_IS_FREE(a)) {
if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2;
goto found; } }
else { n_free = 0; }
434 if (ATB_3_IS_FREE(a)) {
if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3;
goto found; } }
else { n_free = 0; }
452 start_block = i - n_free + 1;
460 MP_STATE_MEM(gc_last_free_atb_index) = (i + 1) / BLOCKS_PER_ATB;
464 ATB_FREE_TO_HEAD(start_block);
468 for (
size_t bl = start_block + 1; bl <= end_block; bl++) {
469 ATB_FREE_TO_TAIL(bl);
474 void *ret_ptr = (
void*)(
MP_STATE_MEM(gc_pool_start) + start_block * BYTES_PER_BLOCK);
477 #if MICROPY_GC_ALLOC_THRESHOLD 483 #if MICROPY_GC_CONSERVATIVE_CLEAR 485 memset((
byte*)ret_ptr, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK);
492 memset((
byte*)ret_ptr + n_bytes, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK - n_bytes);
495 #if MICROPY_ENABLE_FINALISER 501 FTB_SET(start_block);
508 #if EXTENSIVE_HEAP_PROFILING 542 size_t block = BLOCK_FROM_PTR(ptr);
543 assert(ATB_GET_KIND(block) == AT_HEAD);
545 #if MICROPY_ENABLE_FINALISER 550 if (block / BLOCKS_PER_ATB <
MP_STATE_MEM(gc_last_free_atb_index)) {
551 MP_STATE_MEM(gc_last_free_atb_index) = block / BLOCKS_PER_ATB;
556 ATB_ANY_TO_FREE(block);
558 }
while (ATB_GET_KIND(block) == AT_TAIL);
562 #if EXTENSIVE_HEAP_PROFILING 570 if (VERIFY_PTR(ptr)) {
571 size_t block = BLOCK_FROM_PTR(ptr);
572 if (ATB_GET_KIND(block) == AT_HEAD) {
577 }
while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
579 return n_blocks * BYTES_PER_BLOCK;
592 if (n_bytes <= n_existing) {
597 has_finaliser =
false;
599 #if MICROPY_ENABLE_FINALISER 600 has_finaliser = FTB_GET(BLOCK_FROM_PTR((
mp_uint_t)ptr));
602 has_finaliser =
false;
605 void *ptr2 =
gc_alloc(n_bytes, has_finaliser);
609 memcpy(ptr2, ptr, n_existing);
615 #else // Alternative gc_realloc impl 617 void *
gc_realloc(
void *ptr_in,
size_t n_bytes,
bool allow_move) {
619 if (ptr_in ==
NULL) {
632 if (!VERIFY_PTR(ptr)) {
637 size_t block = BLOCK_FROM_PTR(ptr);
642 if (ATB_GET_KIND(block) != AT_HEAD) {
653 size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK;
663 size_t max_block =
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB;
664 for (
size_t bl = block + n_blocks; bl < max_block; bl++) {
665 byte block_type = ATB_GET_KIND(bl);
666 if (block_type == AT_TAIL) {
670 if (block_type == AT_FREE) {
672 if (n_blocks + n_free >= new_blocks) {
682 if (new_blocks == n_blocks) {
688 if (new_blocks < n_blocks) {
690 for (
size_t bl = block + new_blocks, count = n_blocks - new_blocks; count > 0; bl++, count--) {
695 if ((block + new_blocks) / BLOCKS_PER_ATB <
MP_STATE_MEM(gc_last_free_atb_index)) {
696 MP_STATE_MEM(gc_last_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB;
701 #if EXTENSIVE_HEAP_PROFILING 709 if (new_blocks <= n_blocks + n_free) {
711 for (
size_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
712 assert(ATB_GET_KIND(bl) == AT_FREE);
713 ATB_FREE_TO_TAIL(bl);
718 #if MICROPY_GC_CONSERVATIVE_CLEAR 720 memset((
byte*)ptr_in + n_blocks * BYTES_PER_BLOCK, 0, (new_blocks - n_blocks) * BYTES_PER_BLOCK);
723 memset((
byte*)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes);
726 #if EXTENSIVE_HEAP_PROFILING 733 #if MICROPY_ENABLE_FINALISER 734 bool ftb_state = FTB_GET(block);
736 bool ftb_state =
false;
747 void *ptr_out =
gc_alloc(n_bytes, ftb_state);
750 if (ptr_out ==
NULL) {
754 DEBUG_printf(
"gc_realloc(%p -> %p)\n", ptr_in, ptr_out);
755 memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK);
759 #endif // Alternative gc_realloc impl 772 static const size_t DUMP_BYTES_PER_LINE = 64;
773 #if !EXTENSIVE_HEAP_PROFILING 778 for (
size_t bl = 0; bl <
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; bl++) {
779 if (bl % DUMP_BYTES_PER_LINE == 0) {
784 while (bl2 <
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) {
787 if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) {
790 bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1));
791 if (bl >=
MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB) {
803 switch (ATB_GET_KIND(bl)) {
804 case AT_FREE:
c =
'.';
break;
833 void **ptr = (
void**)(
MP_STATE_MEM(gc_pool_start) + bl * BYTES_PER_BLOCK);
838 #if MICROPY_PY_BUILTINS_BYTEARRAY 844 #if MICROPY_PY_BUILTINS_FLOAT 860 for (
const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
861 if ((
const byte*)ptr == *q) {
871 case AT_TAIL:
c =
'=';
break;
872 case AT_MARK:
c =
'm';
break;
897 for (
int i = 0; i < 25; i+=2) {
905 printf(
"Before GC:\n");
907 printf(
"Starting GC...\n");
911 printf(
"After GC:\n");
916 #endif // MICROPY_ENABLE_GC const mp_print_t mp_plat_print
void gc_dump_alloc_table(void)
const mp_obj_type_t mp_type_fun_bc
void * memset(void *b, int c, size_t len)
void mp_load_method_maybe(mp_obj_t obj, qstr attr, mp_obj_t *dest)
const mp_obj_type_t mp_type_float
int DEBUG_printf(const char *fmt,...)
const mp_obj_type_t mp_type_bytearray
void gc_init(void *start, void *end)
int mp_print_str(const mp_print_t *print, const char *str)
#define MP_OBJ_FROM_PTR(p)
#define offsetof(type, field)
const mp_obj_type_t mp_type_bytes
void gc_collect_start(void)
mp_state_ctx_t mp_state_ctx
const mp_obj_type_t mp_type_array
void gc_collect_root(void **ptrs, size_t len)
void gc_info(gc_info_t *info)
const mp_obj_type_t mp_type_str
void gc_collect_end(void)
void * gc_realloc(void *ptr, size_t n_bytes, bool allow_move)
void * malloc(size_t size)
const mp_obj_type_t mp_type_tuple
const mp_obj_type_t mp_type_dict
const mp_obj_type_t mp_type_module
int mp_printf(const mp_print_t *print, const char *fmt,...)
void * gc_alloc(size_t n_bytes, bool has_finaliser)
void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg)
const mp_obj_type_t mp_type_list
size_t gc_nbytes(const void *ptr)
void * memcpy(void *dst, const void *src, size_t n)