diff --git a/bare-arm/main.c b/bare-arm/main.c index 8ee4447e87..fb9ec60379 100644 --- a/bare-arm/main.c +++ b/bare-arm/main.c @@ -5,8 +5,8 @@ #include "py/nlr.h" #include "py/parsehelper.h" #include "py/compile.h" -#include "py/runtime0.h" #include "py/runtime.h" +#include "py/stackctrl.h" #include "py/repl.h" #include "py/pfenv.h" @@ -48,6 +48,7 @@ void do_str(const char *src) { } int main(int argc, char **argv) { + mp_stack_set_limit(10240); mp_init(); do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\n')"); mp_deinit(); diff --git a/esp8266/main.c b/esp8266/main.c index 4bb183df2f..da2f852ebe 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -32,6 +32,7 @@ #include "py/compile.h" #include "py/runtime0.h" #include "py/runtime.h" +#include "py/stackctrl.h" #include "py/gc.h" #include "pyexec.h" #include "gccollect.h" @@ -39,7 +40,7 @@ void user_init(void) { soft_reset: - //mp_stack_set_limit((char*)&_ram_end - (char*)&_heap_end - 1024); + mp_stack_set_limit(10240); mp_hal_init(); gc_init(&_heap_start, &_heap_end); gc_collect_init(); diff --git a/py/builtin.h b/py/builtin.h index 14dc273246..70cc28ae14 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -90,7 +90,6 @@ extern const mp_obj_module_t mp_module_sys; extern const mp_obj_module_t mp_module_gc; extern const mp_obj_dict_t mp_module_builtins_globals; -extern mp_obj_dict_t *mp_module_builtins_override_dict; struct _dummy_t; extern struct _dummy_t mp_sys_stdin_obj; diff --git a/py/emitbc.c b/py/emitbc.c index 3914a9dcbf..a93c03e7e7 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -30,6 +30,7 @@ #include #include +#include "py/mpstate.h" #include "py/emit.h" #include "py/bc0.h" @@ -383,7 +384,7 @@ STATIC void emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) { STATIC void emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) { //printf("source: line %d -> %d offset %d -> %d\n", emit->last_source_line, source_line, emit->last_source_line_offset, emit->bytecode_offset); #if MICROPY_ENABLE_SOURCE_LINE - if (mp_optimise_value >= 3) { + if (MP_STATE_VM(mp_optimise_value) >= 3) { // If we compile with -O3, don't store line numbers. return; } diff --git a/py/gc.c b/py/gc.c index 07f0b80d2e..73a6ca8612 100644 --- a/py/gc.c +++ b/py/gc.c @@ -28,6 +28,7 @@ #include #include +#include "py/mpstate.h" #include "py/gc.h" #include "py/obj.h" #include "py/runtime.h" @@ -48,25 +49,6 @@ #define WORDS_PER_BLOCK (4) #define BYTES_PER_BLOCK (WORDS_PER_BLOCK * BYTES_PER_WORD) -STATIC byte *gc_alloc_table_start; -STATIC mp_uint_t gc_alloc_table_byte_len; -#if MICROPY_ENABLE_FINALISER -STATIC byte *gc_finaliser_table_start; -#endif -// We initialise gc_pool_start to a dummy value so it stays out of the bss -// section. This makes sure we don't trace this pointer in a collect cycle. -// If we did trace it, it would make the first block of the heap always -// reachable, and hence we can never free that block. -STATIC mp_uint_t *gc_pool_start = (void*)4; -STATIC mp_uint_t *gc_pool_end; - -STATIC int gc_stack_overflow; -STATIC mp_uint_t gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]; -STATIC mp_uint_t *gc_sp; -STATIC uint16_t gc_lock_depth; -uint16_t gc_auto_collect_enabled; -STATIC mp_uint_t gc_last_free_atb_index; - // ATB = allocation table byte // 0b00 = FREE -- free block // 0b01 = HEAD -- head of a chain of blocks @@ -90,15 +72,15 @@ STATIC mp_uint_t gc_last_free_atb_index; #define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0) #define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1))) -#define ATB_GET_KIND(block) ((gc_alloc_table_start[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) -#define ATB_ANY_TO_FREE(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0) -#define ATB_FREE_TO_HEAD(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0) -#define ATB_FREE_TO_TAIL(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0) -#define ATB_HEAD_TO_MARK(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0) -#define ATB_MARK_TO_HEAD(block) do { gc_alloc_table_start[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0) +#define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3) +#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) +#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) +#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) +#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) +#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) -#define BLOCK_FROM_PTR(ptr) (((ptr) - (mp_uint_t)gc_pool_start) / BYTES_PER_BLOCK) -#define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (mp_uint_t)gc_pool_start)) +#define BLOCK_FROM_PTR(ptr) (((ptr) - (mp_uint_t)MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK) +#define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (mp_uint_t)MP_STATE_MEM(gc_pool_start))) #define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB) #if MICROPY_ENABLE_FINALISER @@ -107,9 +89,9 @@ STATIC mp_uint_t gc_last_free_atb_index; #define BLOCKS_PER_FTB (8) -#define FTB_GET(block) ((gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) -#define FTB_SET(block) do { gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) -#define FTB_CLEAR(block) do { gc_finaliser_table_start[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) +#define FTB_GET(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1) +#define FTB_SET(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0) +#define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0) #endif // TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool @@ -125,67 +107,67 @@ void gc_init(void *start, void *end) { // => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK) mp_uint_t total_byte_len = (byte*)end - (byte*)start; #if MICROPY_ENABLE_FINALISER - gc_alloc_table_byte_len = total_byte_len * BITS_PER_BYTE / (BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); + MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len * BITS_PER_BYTE / (BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK); #else - gc_alloc_table_byte_len = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); + MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK); #endif - gc_alloc_table_start = (byte*)start; + MP_STATE_MEM(gc_alloc_table_start) = (byte*)start; #if MICROPY_ENABLE_FINALISER - mp_uint_t gc_finaliser_table_byte_len = (gc_alloc_table_byte_len * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB; - gc_finaliser_table_start = gc_alloc_table_start + gc_alloc_table_byte_len; + mp_uint_t gc_finaliser_table_byte_len = (MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB; + MP_STATE_MEM(gc_finaliser_table_start) = MP_STATE_MEM(gc_alloc_table_start) + MP_STATE_MEM(gc_alloc_table_byte_len); #endif - mp_uint_t gc_pool_block_len = gc_alloc_table_byte_len * BLOCKS_PER_ATB; - gc_pool_start = (mp_uint_t*)((byte*)end - gc_pool_block_len * BYTES_PER_BLOCK); - gc_pool_end = (mp_uint_t*)end; + mp_uint_t gc_pool_block_len = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; + MP_STATE_MEM(gc_pool_start) = (mp_uint_t*)((byte*)end - gc_pool_block_len * BYTES_PER_BLOCK); + MP_STATE_MEM(gc_pool_end) = (mp_uint_t*)end; #if MICROPY_ENABLE_FINALISER - assert((byte*)gc_pool_start >= gc_finaliser_table_start + gc_finaliser_table_byte_len); + assert((byte*)MP_STATE_MEM(gc_pool_start) >= MP_STATE_MEM(gc_finaliser_table_start) + gc_finaliser_table_byte_len); #endif // clear ATBs - memset(gc_alloc_table_start, 0, gc_alloc_table_byte_len); + memset(MP_STATE_MEM(gc_alloc_table_start), 0, MP_STATE_MEM(gc_alloc_table_byte_len)); #if MICROPY_ENABLE_FINALISER // clear FTBs - memset(gc_finaliser_table_start, 0, gc_finaliser_table_byte_len); + memset(MP_STATE_MEM(gc_finaliser_table_start), 0, gc_finaliser_table_byte_len); #endif // set last free ATB index to start of heap - gc_last_free_atb_index = 0; + MP_STATE_MEM(gc_last_free_atb_index) = 0; // unlock the GC - gc_lock_depth = 0; + MP_STATE_MEM(gc_lock_depth) = 0; // allow auto collection - gc_auto_collect_enabled = 1; + MP_STATE_MEM(gc_auto_collect_enabled) = 1; DEBUG_printf("GC layout:\n"); - DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_alloc_table_start, gc_alloc_table_byte_len, gc_alloc_table_byte_len * BLOCKS_PER_ATB); + DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_alloc_table_start), MP_STATE_MEM(gc_alloc_table_byte_len), MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB); #if MICROPY_ENABLE_FINALISER - DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_finaliser_table_start, gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB); + 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); #endif - DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_pool_start, gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len); + DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_pool_start), gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len); } void gc_lock(void) { - gc_lock_depth++; + MP_STATE_MEM(gc_lock_depth)++; } void gc_unlock(void) { - gc_lock_depth--; + MP_STATE_MEM(gc_lock_depth)--; } bool gc_is_locked(void) { - return gc_lock_depth != 0; + return MP_STATE_MEM(gc_lock_depth) != 0; } #define VERIFY_PTR(ptr) ( \ (ptr & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \ - && ptr >= (mp_uint_t)gc_pool_start /* must be above start of pool */ \ - && ptr < (mp_uint_t)gc_pool_end /* must be below end of pool */ \ + && ptr >= (mp_uint_t)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \ + && ptr < (mp_uint_t)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \ ) #define VERIFY_MARK_AND_PUSH(ptr) \ @@ -195,19 +177,19 @@ bool gc_is_locked(void) { if (ATB_GET_KIND(_block) == AT_HEAD) { \ /* an unmarked head, mark it, and push it on gc stack */ \ ATB_HEAD_TO_MARK(_block); \ - if (gc_sp < &gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]) { \ - *gc_sp++ = _block; \ + if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \ + *MP_STATE_MEM(gc_sp)++ = _block; \ } else { \ - gc_stack_overflow = 1; \ + MP_STATE_MEM(gc_stack_overflow) = 1; \ } \ } \ } \ } while (0) STATIC void gc_drain_stack(void) { - while (gc_sp > gc_stack) { + while (MP_STATE_MEM(gc_sp) > MP_STATE_MEM(gc_stack)) { // pop the next block off the stack - mp_uint_t block = *--gc_sp; + mp_uint_t block = *--MP_STATE_MEM(gc_sp); // work out number of consecutive blocks in the chain starting with this one mp_uint_t n_blocks = 0; @@ -225,15 +207,15 @@ STATIC void gc_drain_stack(void) { } STATIC void gc_deal_with_stack_overflow(void) { - while (gc_stack_overflow) { - gc_stack_overflow = 0; - gc_sp = gc_stack; + while (MP_STATE_MEM(gc_stack_overflow)) { + MP_STATE_MEM(gc_stack_overflow) = 0; + MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack); // scan entire memory looking for blocks which have been marked but not their children - for (mp_uint_t block = 0; block < gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) { + for (mp_uint_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { // trace (again) if mark bit set if (ATB_GET_KIND(block) == AT_MARK) { - *gc_sp++ = block; + *MP_STATE_MEM(gc_sp)++ = block; gc_drain_stack(); } } @@ -250,7 +232,7 @@ STATIC void gc_sweep(void) { #endif // free unmarked heads and their tails int free_tail = 0; - for (mp_uint_t block = 0; block < gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) { + for (mp_uint_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { switch (ATB_GET_KIND(block)) { case AT_HEAD: #if MICROPY_ENABLE_FINALISER @@ -292,8 +274,13 @@ STATIC void gc_sweep(void) { void gc_collect_start(void) { gc_lock(); - gc_stack_overflow = 0; - gc_sp = gc_stack; + MP_STATE_MEM(gc_stack_overflow) = 0; + MP_STATE_MEM(gc_sp) = MP_STATE_MEM(gc_stack); + // Trace root pointers. This relies on the root pointers being organised + // correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals, + // dict_globals, then the root pointer section of mp_state_vm. + void **ptrs = (void**)(void*)&mp_state_ctx; + gc_collect_root(ptrs, offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t)); } void gc_collect_root(void **ptrs, mp_uint_t len) { @@ -307,18 +294,18 @@ void gc_collect_root(void **ptrs, mp_uint_t len) { void gc_collect_end(void) { gc_deal_with_stack_overflow(); gc_sweep(); - gc_last_free_atb_index = 0; + MP_STATE_MEM(gc_last_free_atb_index) = 0; gc_unlock(); } void gc_info(gc_info_t *info) { - info->total = (gc_pool_end - gc_pool_start) * sizeof(mp_uint_t); + info->total = (MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start)) * sizeof(mp_uint_t); info->used = 0; info->free = 0; info->num_1block = 0; info->num_2block = 0; info->max_block = 0; - for (mp_uint_t block = 0, len = 0; block < gc_alloc_table_byte_len * BLOCKS_PER_ATB; block++) { + for (mp_uint_t block = 0, len = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) { mp_uint_t kind = ATB_GET_KIND(block); if (kind == AT_FREE || kind == AT_HEAD) { if (len == 1) { @@ -361,7 +348,7 @@ void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser) { DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks); // check if GC is locked - if (gc_lock_depth > 0) { + if (MP_STATE_MEM(gc_lock_depth) > 0) { return NULL; } @@ -374,12 +361,12 @@ void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser) { mp_uint_t end_block; mp_uint_t start_block; mp_uint_t n_free = 0; - int collected = !gc_auto_collect_enabled; + int collected = !MP_STATE_MEM(gc_auto_collect_enabled); for (;;) { // look for a run of n_blocks available blocks - for (i = gc_last_free_atb_index; i < gc_alloc_table_byte_len; i++) { - byte a = gc_alloc_table_start[i]; + for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) { + byte a = MP_STATE_MEM(gc_alloc_table_start)[i]; if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; } if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; } if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; } @@ -407,7 +394,7 @@ found: // before this one. Also, whenever we free or shink a block we must check // if this index needs adjusting (see gc_realloc and gc_free). if (n_free == 1) { - gc_last_free_atb_index = (i + 1) / BLOCKS_PER_ATB; + MP_STATE_MEM(gc_last_free_atb_index) = (i + 1) / BLOCKS_PER_ATB; } // mark first block as used head @@ -420,7 +407,7 @@ found: } // get pointer to first block - void *ret_ptr = (void*)(gc_pool_start + start_block * WORDS_PER_BLOCK); + void *ret_ptr = (void*)(MP_STATE_MEM(gc_pool_start) + start_block * WORDS_PER_BLOCK); DEBUG_printf("gc_alloc(%p)\n", ret_ptr); // zero out the additional bytes of the newly allocated blocks @@ -458,7 +445,7 @@ void *gc_alloc_with_finaliser(mp_uint_t n_bytes) { // force the freeing of a piece of memory void gc_free(void *ptr_in) { - if (gc_lock_depth > 0) { + if (MP_STATE_MEM(gc_lock_depth) > 0) { // TODO how to deal with this error? return; } @@ -470,8 +457,8 @@ void gc_free(void *ptr_in) { mp_uint_t block = BLOCK_FROM_PTR(ptr); if (ATB_GET_KIND(block) == AT_HEAD) { // set the last_free pointer to this block if it's earlier in the heap - if (block / BLOCKS_PER_ATB < gc_last_free_atb_index) { - gc_last_free_atb_index = block / BLOCKS_PER_ATB; + if (block / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) { + MP_STATE_MEM(gc_last_free_atb_index) = block / BLOCKS_PER_ATB; } // free head and all of its tail blocks @@ -540,7 +527,7 @@ void *gc_realloc(void *ptr, mp_uint_t n_bytes) { #else // Alternative gc_realloc impl void *gc_realloc(void *ptr_in, mp_uint_t n_bytes) { - if (gc_lock_depth > 0) { + if (MP_STATE_MEM(gc_lock_depth) > 0) { return NULL; } @@ -581,7 +568,7 @@ void *gc_realloc(void *ptr_in, mp_uint_t n_bytes) { // efficiently shrink it (see below for shrinking code). mp_uint_t n_free = 0; mp_uint_t n_blocks = 1; // counting HEAD block - mp_uint_t max_block = gc_alloc_table_byte_len * BLOCKS_PER_ATB; + mp_uint_t max_block = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; for (mp_uint_t bl = block + n_blocks; bl < max_block; bl++) { byte block_type = ATB_GET_KIND(bl); if (block_type == AT_TAIL) { @@ -612,8 +599,8 @@ void *gc_realloc(void *ptr_in, mp_uint_t n_bytes) { } // set the last_free pointer to end of this block if it's earlier in the heap - if ((block + new_blocks) / BLOCKS_PER_ATB < gc_last_free_atb_index) { - gc_last_free_atb_index = (block + new_blocks) / BLOCKS_PER_ATB; + if ((block + new_blocks) / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) { + MP_STATE_MEM(gc_last_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB; } #if EXTENSIVE_HEAP_PROFILING @@ -675,22 +662,22 @@ void gc_dump_alloc_table(void) { #if !EXTENSIVE_HEAP_PROFILING // When comparing heap output we don't want to print the starting // pointer of the heap because it changes from run to run. - printf("GC memory layout; from %p:", gc_pool_start); + printf("GC memory layout; from %p:", MP_STATE_MEM(gc_pool_start)); #endif - for (mp_uint_t bl = 0; bl < gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) { + for (mp_uint_t bl = 0; bl < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; bl++) { if (bl % DUMP_BYTES_PER_LINE == 0) { // a new line of blocks { // check if this line contains only free blocks mp_uint_t bl2 = bl; - while (bl2 < gc_alloc_table_byte_len * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) { + while (bl2 < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) { bl2++; } if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { // there are at least 2 lines containing only free blocks, so abbreviate their printing printf("\n (" UINT_FMT " lines all free)", (bl2 - bl) / DUMP_BYTES_PER_LINE); bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); - if (bl >= gc_alloc_table_byte_len * BLOCKS_PER_ATB) { + if (bl >= MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB) { // got to end of heap break; } @@ -736,7 +723,7 @@ void gc_dump_alloc_table(void) { */ /* this prints the uPy object type of the head block */ case AT_HEAD: { - mp_uint_t *ptr = gc_pool_start + bl * WORDS_PER_BLOCK; + mp_uint_t *ptr = MP_STATE_MEM(gc_pool_start) + bl * WORDS_PER_BLOCK; if (*ptr == (mp_uint_t)&mp_type_tuple) { c = 'T'; } else if (*ptr == (mp_uint_t)&mp_type_list) { c = 'L'; } else if (*ptr == (mp_uint_t)&mp_type_dict) { c = 'D'; } diff --git a/py/gc.h b/py/gc.h index c698c7d21d..bb7e2d4409 100644 --- a/py/gc.h +++ b/py/gc.h @@ -39,11 +39,6 @@ void gc_lock(void); void gc_unlock(void); bool gc_is_locked(void); -// 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 -// you can still allocate/free memory and also explicitly call gc_collect. -extern uint16_t gc_auto_collect_enabled; - // A given port must implement gc_collect by using the other collect functions. void gc_collect(void); void gc_collect_start(void); diff --git a/py/lexer.c b/py/lexer.c index ce6aa783a2..f4aedb4016 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -27,6 +27,7 @@ #include #include +#include "py/mpstate.h" #include "py/lexer.h" #define TAB_SIZE (8) @@ -34,8 +35,6 @@ // TODO seems that CPython allows NULL byte in the input stream // don't know if that's intentional or not, but we don't allow it -mp_uint_t mp_optimise_value; - // TODO replace with a call to a standard function STATIC bool str_strn_equal(const char *str, const char *strn, mp_uint_t len) { mp_uint_t i = 0; @@ -662,7 +661,7 @@ STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, bool first_token) { if (str_strn_equal(tok_kw[i], lex->vstr.buf, lex->vstr.len)) { if (i == MP_ARRAY_SIZE(tok_kw) - 1) { // tok_kw[MP_ARRAY_SIZE(tok_kw) - 1] == "__debug__" - lex->tok_kind = (mp_optimise_value == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE); + lex->tok_kind = (MP_STATE_VM(mp_optimise_value) == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE); } else { lex->tok_kind = MP_TOKEN_KW_FALSE + i; } diff --git a/py/lexer.h b/py/lexer.h index c2d2b678a0..3ec6b6d749 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -192,6 +192,4 @@ typedef enum { mp_import_stat_t mp_import_stat(const char *path); mp_lexer_t *mp_lexer_new_from_file(const char *filename); -extern mp_uint_t mp_optimise_value; - #endif // __MICROPY_INCLUDED_PY_LEXER_H__ diff --git a/py/malloc.c b/py/malloc.c index 10e3566e8e..be2c0db02b 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -30,6 +30,7 @@ #include "py/mpconfig.h" #include "py/misc.h" +#include "py/mpstate.h" #if 0 // print debugging info #define DEBUG_printf DEBUG_printf @@ -38,11 +39,7 @@ #endif #if MICROPY_MEM_STATS -STATIC size_t total_bytes_allocated = 0; -STATIC size_t current_bytes_allocated = 0; -STATIC size_t peak_bytes_allocated = 0; - -#define UPDATE_PEAK() { if (current_bytes_allocated > peak_bytes_allocated) peak_bytes_allocated = current_bytes_allocated; } +#define UPDATE_PEAK() { if (MP_STATE_MEM(current_bytes_allocated) > MP_STATE_MEM(peak_bytes_allocated)) MP_STATE_MEM(peak_bytes_allocated) = MP_STATE_MEM(current_bytes_allocated); } #endif #if MICROPY_ENABLE_GC @@ -68,8 +65,8 @@ void *m_malloc(size_t num_bytes) { return m_malloc_fail(num_bytes); } #if MICROPY_MEM_STATS - total_bytes_allocated += num_bytes; - current_bytes_allocated += num_bytes; + MP_STATE_MEM(total_bytes_allocated) += num_bytes; + MP_STATE_MEM(current_bytes_allocated) += num_bytes; UPDATE_PEAK(); #endif DEBUG_printf("malloc %d : %p\n", num_bytes, ptr); @@ -79,8 +76,8 @@ void *m_malloc(size_t num_bytes) { void *m_malloc_maybe(size_t num_bytes) { void *ptr = malloc(num_bytes); #if MICROPY_MEM_STATS - total_bytes_allocated += num_bytes; - current_bytes_allocated += num_bytes; + MP_STATE_MEM(total_bytes_allocated) += num_bytes; + MP_STATE_MEM(current_bytes_allocated) += num_bytes; UPDATE_PEAK(); #endif DEBUG_printf("malloc %d : %p\n", num_bytes, ptr); @@ -94,8 +91,8 @@ void *m_malloc_with_finaliser(size_t num_bytes) { return m_malloc_fail(num_bytes); } #if MICROPY_MEM_STATS - total_bytes_allocated += num_bytes; - current_bytes_allocated += num_bytes; + MP_STATE_MEM(total_bytes_allocated) += num_bytes; + MP_STATE_MEM(current_bytes_allocated) += num_bytes; UPDATE_PEAK(); #endif DEBUG_printf("malloc %d : %p\n", num_bytes, ptr); @@ -124,8 +121,8 @@ void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes) { // allocated total. If we process only positive increments, // we'll count 3K. size_t diff = new_num_bytes - old_num_bytes; - total_bytes_allocated += diff; - current_bytes_allocated += diff; + MP_STATE_MEM(total_bytes_allocated) += diff; + MP_STATE_MEM(current_bytes_allocated) += diff; UPDATE_PEAK(); #endif DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr); @@ -143,8 +140,8 @@ void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes) { // Also, don't count failed reallocs. if (!(new_ptr == NULL && new_num_bytes != 0)) { size_t diff = new_num_bytes - old_num_bytes; - total_bytes_allocated += diff; - current_bytes_allocated += diff; + MP_STATE_MEM(total_bytes_allocated) += diff; + MP_STATE_MEM(current_bytes_allocated) += diff; UPDATE_PEAK(); } #endif @@ -155,21 +152,21 @@ void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes) { void m_free(void *ptr, size_t num_bytes) { free(ptr); #if MICROPY_MEM_STATS - current_bytes_allocated -= num_bytes; + MP_STATE_MEM(current_bytes_allocated) -= num_bytes; #endif DEBUG_printf("free %p, %d\n", ptr, num_bytes); } #if MICROPY_MEM_STATS size_t m_get_total_bytes_allocated(void) { - return total_bytes_allocated; + return MP_STATE_MEM(total_bytes_allocated); } size_t m_get_current_bytes_allocated(void) { - return current_bytes_allocated; + return MP_STATE_MEM(current_bytes_allocated); } size_t m_get_peak_bytes_allocated(void) { - return peak_bytes_allocated; + return MP_STATE_MEM(peak_bytes_allocated); } #endif diff --git a/py/modgc.c b/py/modgc.c index e3cbe72f9d..38f7c15fee 100644 --- a/py/modgc.c +++ b/py/modgc.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "py/mpstate.h" #include "py/obj.h" #include "py/gc.h" @@ -48,7 +49,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_collect_obj, py_gc_collect); /// \function disable() /// Disable the garbage collector. STATIC mp_obj_t gc_disable(void) { - gc_auto_collect_enabled = 0; + MP_STATE_MEM(gc_auto_collect_enabled) = 0; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable); @@ -56,13 +57,13 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable); /// \function enable() /// Enable the garbage collector. STATIC mp_obj_t gc_enable(void) { - gc_auto_collect_enabled = 1; + MP_STATE_MEM(gc_auto_collect_enabled) = 1; return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(gc_enable_obj, gc_enable); STATIC mp_obj_t gc_isenabled(void) { - return MP_BOOL(gc_auto_collect_enabled); + return MP_BOOL(MP_STATE_MEM(gc_auto_collect_enabled)); } MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled); diff --git a/py/mpconfig.h b/py/mpconfig.h index e3270bd13f..0faa6a7282 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -488,6 +488,11 @@ typedef double mp_float_t; #define MICROPY_PORT_CONSTANTS #endif +// Any root pointers for GC scanning - see mpstate.c +#ifndef MICROPY_PORT_ROOT_POINTERS +#define MICROPY_PORT_ROOT_POINTERS +#endif + /*****************************************************************************/ /* Miscellaneous settings */ diff --git a/py/mpstate.c b/py/mpstate.c new file mode 100644 index 0000000000..2ba3402ef1 --- /dev/null +++ b/py/mpstate.c @@ -0,0 +1,29 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mpstate.h" + +mp_state_ctx_t mp_state_ctx; diff --git a/py/mpstate.h b/py/mpstate.h new file mode 100644 index 0000000000..e966bc80de --- /dev/null +++ b/py/mpstate.h @@ -0,0 +1,155 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __MICROPY_INCLUDED_PY_MPSTATE_H__ +#define __MICROPY_INCLUDED_PY_MPSTATE_H__ + +#include + +#include "py/mpconfig.h" +#include "py/misc.h" +#include "py/nlr.h" +#include "py/obj.h" +#include "py/objexcept.h" + +// This file contains structures defining the state of the Micro Python +// memory system, runtime and virtual machine. The state is a global +// variable, but in the future it is hoped that the state can become local. + +// This structure hold information about the memory allocation system. +typedef struct _mp_state_mem_t { + #if MICROPY_MEM_STATS + size_t total_bytes_allocated; + size_t current_bytes_allocated; + size_t peak_bytes_allocated; + #endif + + byte *gc_alloc_table_start; + mp_uint_t gc_alloc_table_byte_len; + #if MICROPY_ENABLE_FINALISER + byte *gc_finaliser_table_start; + #endif + mp_uint_t *gc_pool_start; + mp_uint_t *gc_pool_end; + + int gc_stack_overflow; + mp_uint_t gc_stack[MICROPY_ALLOC_GC_STACK_SIZE]; + mp_uint_t *gc_sp; + uint16_t gc_lock_depth; + + // 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 + // you can still allocate/free memory and also explicitly call gc_collect. + uint16_t gc_auto_collect_enabled; + + mp_uint_t gc_last_free_atb_index; +} mp_state_mem_t; + +// This structure hold runtime and VM information. It includes a section +// which contains root pointers that must be scanned by the GC. +typedef struct _mp_state_vm_t { + //////////////////////////////////////////////////////////// + // START ROOT POINTER SECTION + // everything that needs GC scanning must go here + // this must start at the start of this structure + // + + // Note: nlr asm code has the offset of this hard-coded + nlr_buf_t *nlr_top; + + qstr_pool_t *last_pool; + + // non-heap memory for creating an exception if we can't allocate RAM + mp_obj_exception_t mp_emergency_exception_obj; + + // memory for exception arguments if we can't allocate RAM + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF + #if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0 + // statically allocated buf + byte mp_emergency_exception_buf[MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE]; + #else + // dynamically allocated buf + byte *mp_emergency_exception_buf; + #endif + #endif + + // map with loaded modules + // TODO: expose as sys.modules + mp_map_t mp_loaded_modules_map; + + // pending exception object (MP_OBJ_NULL if not pending) + mp_obj_t mp_pending_exception; + + // dictionary for the __main__ module + mp_obj_dict_t dict_main; + + // dictionary for overridden builtins + #if MICROPY_CAN_OVERRIDE_BUILTINS + mp_obj_dict_t *mp_module_builtins_override_dict; + #endif + + // include any root pointers defined by a port + MICROPY_PORT_ROOT_POINTERS + + // + // END ROOT POINTER SECTION + //////////////////////////////////////////////////////////// + + // Stack top at the start of program + // Note: this entry is used to locate the end of the root pointer section. + char *stack_top; + + #if MICROPY_STACK_CHECK + mp_uint_t stack_limit; + #endif + + mp_uint_t mp_optimise_value; + + // size of the emergency exception buf, if it's dynamically allocated + #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0 + mp_int_t mp_emergency_exception_buf_size; + #endif +} mp_state_vm_t; + +// This structure combines the above 2 structures, and adds the local +// and global dicts. +// Note: if this structure changes then revisit all nlr asm code since they +// have the offset of nlr_top hard-coded. +typedef struct _mp_state_ctx_t { + // these must come first for root pointer scanning in GC to work + mp_obj_dict_t *dict_locals; + mp_obj_dict_t *dict_globals; + // this must come next for root pointer scanning in GC to work + mp_state_vm_t vm; + mp_state_mem_t mem; +} mp_state_ctx_t; + +extern mp_state_ctx_t mp_state_ctx; + +#define MP_STATE_CTX(x) (mp_state_ctx.x) +#define MP_STATE_VM(x) (mp_state_ctx.vm.x) +#define MP_STATE_MEM(x) (mp_state_ctx.mem.x) + +#endif // __MICROPY_INCLUDED_PY_MPSTATE_H__ diff --git a/py/nlr.h b/py/nlr.h index 824ed6dd69..f414588a5b 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -64,14 +64,14 @@ struct _nlr_buf_t { #endif }; -extern nlr_buf_t *nlr_top; - #if MICROPY_NLR_SETJMP +#include "py/mpstate.h" + NORETURN void nlr_setjmp_jump(void *val); // nlr_push() must be defined as a macro, because "The stack context will be // invalidated if the function which called setjmp() returns." -#define nlr_push(buf) ((buf)->prev = nlr_top, nlr_top = (buf), setjmp((buf)->jmpbuf)) -#define nlr_pop() { nlr_top = nlr_top->prev; } +#define nlr_push(buf) ((buf)->prev = MP_STATE_VM(nlr_top), MP_STATE_VM(nlr_top) = (buf), setjmp((buf)->jmpbuf)) +#define nlr_pop() { MP_STATE_VM(nlr_top) = MP_STATE_VM(nlr_top)->prev; } #define nlr_jump(val) nlr_setjmp_jump(val) #else unsigned int nlr_push(nlr_buf_t *); diff --git a/py/nlrsetjmp.c b/py/nlrsetjmp.c index 76d718bba5..661b650c5c 100644 --- a/py/nlrsetjmp.c +++ b/py/nlrsetjmp.c @@ -26,14 +26,11 @@ #include "py/nlr.h" -// this global variable is used for all nlr implementations -nlr_buf_t *nlr_top; - #if MICROPY_NLR_SETJMP void nlr_setjmp_jump(void *val) { - nlr_buf_t *buf = nlr_top; - nlr_top = buf->prev; + nlr_buf_t *buf = MP_STATE_VM(nlr_top); + MP_STATE_VM(nlr_top) = buf->prev; buf->ret_val = val; longjmp(buf->jmpbuf, 1); } diff --git a/py/nlrthumb.S b/py/nlrthumb.S index 761f835566..c6db844f87 100644 --- a/py/nlrthumb.S +++ b/py/nlrthumb.S @@ -32,6 +32,9 @@ // For reference, arm/thumb callee save regs are: // r4-r11, r13=sp +// the offset of nlr_top within mp_state_ctx_t +#define NLR_TOP_OFFSET (2 * 4) + .syntax unified /*.cpu cortex-m4*/ /*.thumb*/ @@ -68,7 +71,7 @@ nlr_push: bx lr @ return .align 2 nlr_top_addr: - .word nlr_top + .word mp_state_ctx + NLR_TOP_OFFSET .size nlr_push, .-nlr_push /**************************************/ diff --git a/py/nlrx64.S b/py/nlrx64.S index b0c2e74452..43298eba9d 100644 --- a/py/nlrx64.S +++ b/py/nlrx64.S @@ -32,6 +32,11 @@ // For reference, x86-64 callee save regs are: // rbx, rbp, rsp, r12, r13, r14, r15 +// the offset of nlr_top within mp_state_ctx_t +#define NLR_TOP_OFFSET (2 * 8) + +#define NLR_TOP (mp_state_ctx + NLR_TOP_OFFSET) + .file "nlr.s" .text @@ -64,9 +69,9 @@ _nlr_push: movq %r13, 56(%rdi) # store %r13 into nlr_buf movq %r14, 64(%rdi) # store %r14 into nlr_buf movq %r15, 72(%rdi) # store %r15 into nlr_buf - movq nlr_top(%rip), %rax # get last nlr_buf + movq NLR_TOP(%rip), %rax # get last nlr_buf movq %rax, (%rdi) # store it - movq %rdi, nlr_top(%rip) # stor new nlr_buf (to make linked list) + movq %rdi, NLR_TOP(%rip) # stor new nlr_buf (to make linked list) xorq %rax, %rax # return 0, normal return ret # return #if !(defined(__APPLE__) && defined(__MACH__)) @@ -84,9 +89,9 @@ nlr_pop: .globl _nlr_pop _nlr_pop: #endif - movq nlr_top(%rip), %rax # get nlr_top into %rax + movq NLR_TOP(%rip), %rax # get nlr_top into %rax movq (%rax), %rax # load prev nlr_buf - movq %rax, nlr_top(%rip) # store prev nlr_buf (to unlink list) + movq %rax, NLR_TOP(%rip) # store prev nlr_buf (to unlink list) ret # return #if !(defined(__APPLE__) && defined(__MACH__)) .size nlr_pop, .-nlr_pop @@ -104,12 +109,12 @@ nlr_jump: _nlr_jump: #endif movq %rdi, %rax # put return value in %rax - movq nlr_top(%rip), %rdi # get nlr_top into %rdi + movq NLR_TOP(%rip), %rdi # get nlr_top into %rdi test %rdi, %rdi # check for nlr_top being NULL je .fail # fail if nlr_top is NULL movq %rax, 8(%rdi) # store return value movq (%rdi), %rax # load prev nlr_buf - movq %rax, nlr_top(%rip) # store prev nlr_buf (to unlink list) + movq %rax, NLR_TOP(%rip) # store prev nlr_buf (to unlink list) movq 72(%rdi), %r15 # load saved %r15 movq 64(%rdi), %r14 # load saved %r14 movq 56(%rdi), %r13 # load saved %r13 @@ -155,9 +160,9 @@ nlr_push: movq %r15, 72(%rcx) # store %r15 into movq %rdi, 80(%rcx) # store %rdr into movq %rsi, 88(%rcx) # store %rsi into - movq nlr_top(%rip), %rax # get last nlr_buf + movq NLR_TOP(%rip), %rax # get last nlr_buf movq %rax, (%rcx) # store it - movq %rcx, nlr_top(%rip) # stor new nlr_buf (to make linked list) + movq %rcx, NLR_TOP(%rip) # stor new nlr_buf (to make linked list) xorq %rax, %rax # return 0, normal return ret # return @@ -166,9 +171,9 @@ nlr_push: .globl nlr_pop nlr_pop: - movq nlr_top(%rip), %rax # get nlr_top into %rax + movq NLR_TOP(%rip), %rax # get nlr_top into %rax movq (%rax), %rax # load prev nlr_buf - movq %rax, nlr_top(%rip) # store prev nlr_buf (to unlink list) + movq %rax, NLR_TOP(%rip) # store prev nlr_buf (to unlink list) ret # return /**************************************/ @@ -177,12 +182,12 @@ nlr_pop: .globl nlr_jump nlr_jump: movq %rcx, %rax # put return value in %rax - movq nlr_top(%rip), %rcx # get nlr_top into %rcx + movq NLR_TOP(%rip), %rcx # get nlr_top into %rcx test %rcx, %rcx # check for nlr_top being NULL je .fail # fail if nlr_top is NULL movq %rax, 8(%rcx) # store return value movq (%rcx), %rax # load prev nlr_buf - movq %rax, nlr_top(%rip) # store prev nlr_buf (to unlink list) + movq %rax, NLR_TOP(%rip) # store prev nlr_buf (to unlink list) movq 72(%rcx), %r15 # load saved %r15 movq 64(%rcx), %r14 # load saved %r14 movq 56(%rcx), %r13 # load saved %r13 diff --git a/py/nlrx86.S b/py/nlrx86.S index 34d0bfc4ef..275dc82647 100644 --- a/py/nlrx86.S +++ b/py/nlrx86.S @@ -32,10 +32,13 @@ // For reference, x86 callee save regs are: // ebx, esi, edi, ebp, esp, eip +// the offset of nlr_top within mp_state_ctx_t +#define NLR_TOP_OFFSET (2 * 4) + #ifdef _WIN32 -#define NLR_TOP _nlr_top +#define NLR_TOP (_mp_state_ctx + NLR_TOP_OFFSET) #else -#define NLR_TOP nlr_top +#define NLR_TOP (mp_state_ctx + NLR_TOP_OFFSET) #endif .file "nlr.s" diff --git a/py/nlrxtensa.S b/py/nlrxtensa.S index 293fb9f0a5..289996ccee 100644 --- a/py/nlrxtensa.S +++ b/py/nlrxtensa.S @@ -34,11 +34,16 @@ a3-a7 = rest of args */ +// the offset of nlr_top within mp_state_ctx_t +#define NLR_TOP_OFFSET (2 * 4) + +#define NLR_TOP (mp_state_ctx + NLR_TOP_OFFSET) + .file "nlr.s" .text .literal_position - .literal .LC0, nlr_top + .literal .LC0, NLR_TOP .align 4 .global nlr_push .type nlr_push, @function @@ -64,7 +69,7 @@ nlr_push: .size nlr_push, .-nlr_push .literal_position - .literal .LC1, nlr_top + .literal .LC1, NLR_TOP .align 4 .global nlr_pop .type nlr_pop, @function @@ -77,7 +82,7 @@ nlr_pop: .size nlr_pop, .-nlr_pop .literal_position - .literal .LC2, nlr_top + .literal .LC2, NLR_TOP .align 4 .global nlr_jump .type nlr_jump, @function diff --git a/py/objexcept.c b/py/objexcept.c index a60cbb82ed..837e2e85eb 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -29,6 +29,7 @@ #include #include +#include "py/mpstate.h" #include "py/nlr.h" #include "py/objlist.h" #include "py/objstr.h" @@ -36,23 +37,13 @@ #include "py/objtype.h" #include "py/gc.h" -typedef struct _mp_obj_exception_t { - mp_obj_base_t base; - mp_obj_t traceback; // a list object, holding (file,line,block) as numbers (not Python objects); a hack for now - mp_obj_tuple_t *args; -} mp_obj_exception_t; - // Instance of MemoryError exception - needed by mp_malloc_fail const mp_obj_exception_t mp_const_MemoryError_obj = {{&mp_type_MemoryError}, MP_OBJ_NULL, mp_const_empty_tuple}; -// Local non-heap memory for allocating an exception when we run out of RAM -STATIC mp_obj_exception_t mp_emergency_exception_obj; - // Optionally allocated buffer for storing the first argument of an exception // allocated when the heap is locked. #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF # if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0 -STATIC byte mp_emergency_exception_buf[MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE]; #define mp_emergency_exception_buf_size MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE void mp_init_emergency_exception_buf(void) { @@ -62,12 +53,11 @@ void mp_init_emergency_exception_buf(void) { } #else -STATIC mp_int_t mp_emergency_exception_buf_size = 0; -STATIC byte *mp_emergency_exception_buf = NULL; +#define mp_emergency_exception_buf_size MP_STATE_VM(mp_emergency_exception_buf_size) void mp_init_emergency_exception_buf(void) { mp_emergency_exception_buf_size = 0; - mp_emergency_exception_buf = NULL; + MP_STATE_VM(mp_emergency_exception_buf) = NULL; } mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { @@ -78,13 +68,13 @@ mp_obj_t mp_alloc_emergency_exception_buf(mp_obj_t size_in) { } int old_size = mp_emergency_exception_buf_size; - void *old_buf = mp_emergency_exception_buf; + void *old_buf = MP_STATE_VM(mp_emergency_exception_buf); // Update the 2 variables atomically so that an interrupt can't occur // between the assignments. mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); mp_emergency_exception_buf_size = size; - mp_emergency_exception_buf = buf; + MP_STATE_VM(mp_emergency_exception_buf) = buf; MICROPY_END_ATOMIC_SECTION(atomic_state); if (old_buf != NULL) { @@ -134,7 +124,7 @@ mp_obj_t mp_obj_exception_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t mp_obj_exception_t *o = m_new_obj_var_maybe(mp_obj_exception_t, mp_obj_t, 0); if (o == NULL) { // Couldn't allocate heap memory; use local data instead. - o = &mp_emergency_exception_obj; + o = &MP_STATE_VM(mp_emergency_exception_obj); // We can't store any args. n_args = 0; o->args = mp_const_empty_tuple; @@ -308,7 +298,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char if (o == NULL) { // Couldn't allocate heap memory; use local data instead. // Unfortunately, we won't be able to format the string... - o = &mp_emergency_exception_obj; + o = &MP_STATE_VM(mp_emergency_exception_obj); o->base.type = exc_type; o->traceback = MP_OBJ_NULL; o->args = mp_const_empty_tuple; @@ -318,7 +308,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char // of length 1, which has a string object and the string data. if (mp_emergency_exception_buf_size > (sizeof(mp_obj_tuple_t) + sizeof(mp_obj_str_t) + sizeof(mp_obj_t))) { - mp_obj_tuple_t *tuple = (mp_obj_tuple_t *)mp_emergency_exception_buf; + mp_obj_tuple_t *tuple = (mp_obj_tuple_t *)MP_STATE_VM(mp_emergency_exception_buf); mp_obj_str_t *str = (mp_obj_str_t *)&tuple->items[1]; tuple->base.type = &mp_type_tuple; @@ -326,7 +31