diff --git a/py/gc.c b/py/gc.c index c8256fb03b..9c8d203d28 100644 --- a/py/gc.c +++ b/py/gc.c @@ -240,7 +240,7 @@ void gc_info(gc_info_t *info) { void *gc_alloc(machine_uint_t n_bytes) { machine_uint_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK; - //printf("gc_alloc(%u bytes -> %u blocks)\n", n_bytes, n_blocks); + DEBUG_printf("gc_alloc(%u bytes -> %u blocks)\n", n_bytes, n_blocks); // check for 0 allocation if (n_blocks == 0) { @@ -267,6 +267,7 @@ void *gc_alloc(machine_uint_t n_bytes) { if (collected) { return NULL; } + DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes); gc_collect(); collected = 1; } @@ -341,6 +342,14 @@ void *gc_realloc(void *ptr, machine_uint_t n_bytes) { } } +void gc_dump_info() { + gc_info_t info; + gc_info(&info); + printf("GC: total: " UINT_FMT ", used: " UINT_FMT ", free: " UINT_FMT "\n", info.total, info.used, info.free); + printf(" No. of 1-blocks: " UINT_FMT ", 2-blocks: " UINT_FMT ", max blk sz: " UINT_FMT "\n", + info.num_1block, info.num_2block, info.max_block); +} + #if DEBUG_PRINT static void gc_dump_at(void) { for (machine_uint_t bl = 0; bl < gc_alloc_table_byte_len * BLOCKS_PER_ATB; bl++) { diff --git a/py/malloc.c b/py/malloc.c index c87d91c0a0..7f55fa7c8e 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -19,6 +19,22 @@ static int peak_bytes_allocated = 0; #define UPDATE_PEAK() { if (current_bytes_allocated > peak_bytes_allocated) peak_bytes_allocated = current_bytes_allocated; } #endif +#if MICROPY_ENABLE_GC +#include "gc.h" + +// We redirect standard alloc functions to GC heap - just for the rest of +// this module. In the rest of micropython source, system malloc can be +// freely accessed - for interfacing with system and 3rd-party libs for +// example. On the other hand, some (e.g. bare-metal) ports may use GC +// heap as system heap, so, to avoid warnings, we do undef's first. +#undef malloc +#undef free +#undef realloc +#define malloc gc_alloc +#define free gc_free +#define realloc gc_realloc +#endif // MICROPY_ENABLE_GC + void *m_malloc(int num_bytes) { if (num_bytes == 0) { return NULL; diff --git a/stm/malloc0.c b/stm/malloc0.c index eaa436f4fb..85a643f72d 100644 --- a/stm/malloc0.c +++ b/stm/malloc0.c @@ -29,18 +29,6 @@ void *realloc(void *ptr, size_t n) { #endif -void *malloc(size_t n) { - return gc_alloc(n); -} - -void free(void *ptr) { - gc_free(ptr); -} - -void *realloc(void *ptr, size_t n) { - return gc_realloc(ptr, n); -} - void __assert_func(void) { printf("\nASSERT FAIL!"); for (;;) { diff --git a/stm/mpconfigport.h b/stm/mpconfigport.h index fd7af29d27..1d79243f9b 100644 --- a/stm/mpconfigport.h +++ b/stm/mpconfigport.h @@ -21,6 +21,14 @@ typedef float machine_float_t; machine_float_t machine_sqrt(machine_float_t x); +// There is no classical C heap in bare-metal ports, only Python +// garbage-collected heap. For completeness, emulate C heap via +// GC heap. Note that MicroPython core never uses malloc() and friends, +// so these defines are mostly to help extension module writers. +#define malloc gc_alloc +#define free gc_free +#define realloc gc_realloc + // board specific definitions // choose 1 of these boards diff --git a/stm/std.h b/stm/std.h index a2bf706176..843ddd8270 100644 --- a/stm/std.h +++ b/stm/std.h @@ -2,10 +2,6 @@ typedef unsigned int size_t; void __assert_func(void); -void *malloc(size_t n); -void free(void *ptr); -void *realloc(void *ptr, size_t n); - void *memcpy(void *dest, const void *src, size_t n); void *memmove(void *dest, const void *src, size_t n); void *memset(void *s, int c, size_t n); diff --git a/unix/Makefile b/unix/Makefile index 317bcb5ebf..1ce00eb28f 100644 --- a/unix/Makefile +++ b/unix/Makefile @@ -35,6 +35,7 @@ endif # source files SRC_C = \ main.c \ + gccollect.c \ file.c \ socket.c \ $(SRC_MOD) diff --git a/unix/gccollect.c b/unix/gccollect.c new file mode 100644 index 0000000000..60bc99323e --- /dev/null +++ b/unix/gccollect.c @@ -0,0 +1,68 @@ +#include +#include + +#include "misc.h" +#include "mpconfig.h" +#include "gc.h" + +#if MICROPY_ENABLE_GC + +extern void *stack_top; + +// We capture here callee-save registers, i.e. ones which may contain +// interesting values held there by our callers. It doesn't make sense +// to capture caller-saved registers, because they, well, put on the +// stack already by the caller. +#ifdef __x86_64__ +typedef machine_uint_t regs_t[6]; + +void gc_helper_get_regs(regs_t arr) { + register long rbx asm ("rbx"); + register long rbp asm ("rbp"); + register long r12 asm ("r12"); + register long r13 asm ("r13"); + register long r14 asm ("r14"); + register long r15 asm ("r15"); + arr[0] = rbx; + arr[1] = rbp; + arr[2] = r12; + arr[3] = r13; + arr[4] = r14; + arr[5] = r15; +} +#endif + +#ifdef __i386__ +typedef machine_uint_t regs_t[4]; + +void gc_helper_get_regs(regs_t arr) { + register long ebx asm ("ebx"); + register long esi asm ("esi"); + register long edi asm ("edi"); + register long ebp asm ("ebp"); + arr[0] = ebx; + arr[1] = esi; + arr[2] = edi; + arr[3] = ebp; +} +#endif + +void gc_collect(void) { + //gc_dump_info(); + + gc_collect_start(); + // this traces .data and .bss sections + extern char __bss_start, _end; + //printf(".bss: %p-%p\n", &__bss_start, &_end); + gc_collect_root((void**)&__bss_start, ((uint32_t)&_end - (uint32_t)&__bss_start) / sizeof(uint32_t)); + regs_t regs; + gc_helper_get_regs(regs); + // GC stack (and regs because we captured them) + gc_collect_root((void**)®s, ((uint32_t)stack_top - (uint32_t)®s) / sizeof(uint32_t)); + gc_collect_end(); + + //printf("-----\n"); + //gc_dump_info(); +} + +#endif //MICROPY_ENABLE_GC diff --git a/unix/main.c b/unix/main.c index 9bdcc53a7c..192a0e6e8b 100644 --- a/unix/main.c +++ b/unix/main.c @@ -15,12 +15,20 @@ #include "runtime0.h" #include "runtime.h" #include "repl.h" +#include "gc.h" #if MICROPY_USE_READLINE #include #include #endif +// Heap size of GC heap (if enabled) +// TODO: allow to specify on command line +#define HEAP_SIZE 128*1024 + +// Stack top at the start of program +void *stack_top; + extern const mp_obj_fun_native_t mp_builtin_open_obj; void file_init(); void microsocket_init(); @@ -217,7 +225,24 @@ mp_obj_t qstr_info(void) { return mp_const_none; } +#if MICROPY_ENABLE_GC +// TODO: this doesn't belong here +static mp_obj_t pyb_gc(void) { + gc_collect(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(pyb_gc_obj, pyb_gc); +#endif + int main(int argc, char **argv) { + volatile int stack_dummy; + stack_top = (void*)&stack_dummy; + +#if MICROPY_ENABLE_GC + char *heap = malloc(HEAP_SIZE); + gc_init(heap, heap + HEAP_SIZE); +#endif + qstr_init(); rt_init(); @@ -263,6 +288,9 @@ int main(int argc, char **argv) { rt_store_name(qstr_from_str("test"), test_obj_new(42)); rt_store_name(qstr_from_str("mem_info"), rt_make_function_n(0, mem_info)); rt_store_name(qstr_from_str("qstr_info"), rt_make_function_n(0, qstr_info)); +#if MICROPY_ENABLE_GC + rt_store_name(qstr_from_str("gc"), (mp_obj_t)&pyb_gc_obj); +#endif file_init(); microsocket_init();