diff --git a/examples/embedding/mpconfigport_minimal.h b/examples/embedding/mpconfigport_minimal.h index fa52be4ad7..1050d947d9 100644 --- a/examples/embedding/mpconfigport_minimal.h +++ b/examples/embedding/mpconfigport_minimal.h @@ -97,14 +97,9 @@ extern const struct _mp_obj_module_t mp_module_os; // Do not change anything beyond this line ////////////////////////////////////////// -// Define to 1 to use undertested inefficient GC helper implementation -// (if more efficient arch-specific one is not available). -#ifndef MICROPY_GCREGS_SETJMP - #ifdef __mips__ - #define MICROPY_GCREGS_SETJMP (1) - #else - #define MICROPY_GCREGS_SETJMP (0) - #endif +#if !defined(__x86_64__) || !defined(__i386__) || !defined(__thumb2__) || !defined(__thumb__) || !defined(__arm__) +// Fall back to setjmp() implementation for discovery of GC pointers in registers. +#define MICROPY_GCREGS_SETJMP (1) #endif // type definitions for the specific machine diff --git a/lib/utils/gchelper.h b/lib/utils/gchelper.h index 7149b6a72e..4b6ead6ba6 100644 --- a/lib/utils/gchelper.h +++ b/lib/utils/gchelper.h @@ -28,7 +28,21 @@ #include -uintptr_t gc_helper_get_sp(void); -uintptr_t gc_helper_get_regs_and_sp(uintptr_t *regs); +#if MICROPY_GCREGS_SETJMP +#include +typedef jmp_buf gc_helper_regs_t; +#else + +#if defined(__x86_64__) +typedef uintptr_t gc_helper_regs_t[6]; +#elif defined(__i386__) +typedef uintptr_t gc_helper_regs_t[4]; +#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) +typedef uintptr_t gc_helper_regs_t[10]; +#endif + +#endif + +void gc_helper_collect_regs_and_stack(void); #endif // MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H diff --git a/lib/utils/gchelper_generic.c b/lib/utils/gchelper_generic.c new file mode 100644 index 0000000000..9750e8b0c8 --- /dev/null +++ b/lib/utils/gchelper_generic.c @@ -0,0 +1,156 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 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 + +#include "py/mpstate.h" +#include "py/gc.h" +#include "lib/utils/gchelper.h" + +#if MICROPY_ENABLE_GC + +// Even if we have specific support for an architecture, it is +// possible to force use of setjmp-based implementation. +#if !MICROPY_GCREGS_SETJMP + +// 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. +#if defined(__x86_64__) + +STATIC void gc_helper_get_regs(gc_helper_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"); + #ifdef __clang__ + // TODO: + // This is dirty workaround for Clang. It tries to get around + // uncompliant (wrt to GCC) behavior of handling register variables. + // Application of this patch here is random, and done only to unbreak + // MacOS build. Better, cross-arch ways to deal with Clang issues should + // be found. + asm ("" : "=r" (rbx)); + asm ("" : "=r" (rbp)); + asm ("" : "=r" (r12)); + asm ("" : "=r" (r13)); + asm ("" : "=r" (r14)); + asm ("" : "=r" (r15)); + #endif + arr[0] = rbx; + arr[1] = rbp; + arr[2] = r12; + arr[3] = r13; + arr[4] = r14; + arr[5] = r15; +} + +#elif defined(__i386__) + +STATIC void gc_helper_get_regs(gc_helper_regs_t arr) { + register long ebx asm ("ebx"); + register long esi asm ("esi"); + register long edi asm ("edi"); + register long ebp asm ("ebp"); + #ifdef __clang__ + // TODO: + // This is dirty workaround for Clang. It tries to get around + // uncompliant (wrt to GCC) behavior of handling register variables. + // Application of this patch here is random, and done only to unbreak + // MacOS build. Better, cross-arch ways to deal with Clang issues should + // be found. + asm ("" : "=r" (ebx)); + asm ("" : "=r" (esi)); + asm ("" : "=r" (edi)); + asm ("" : "=r" (ebp)); + #endif + arr[0] = ebx; + arr[1] = esi; + arr[2] = edi; + arr[3] = ebp; +} + +#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) + +// Fallback implementation, prefer gchelper_m0.s or gchelper_m3.s + +STATIC void gc_helper_get_regs(gc_helper_regs_t arr) { + register long r4 asm ("r4"); + register long r5 asm ("r5"); + register long r6 asm ("r6"); + register long r7 asm ("r7"); + register long r8 asm ("r8"); + register long r9 asm ("r9"); + register long r10 asm ("r10"); + register long r11 asm ("r11"); + register long r12 asm ("r12"); + register long r13 asm ("r13"); + arr[0] = r4; + arr[1] = r5; + arr[2] = r6; + arr[3] = r7; + arr[4] = r8; + arr[5] = r9; + arr[6] = r10; + arr[7] = r11; + arr[8] = r12; + arr[9] = r13; +} + +#else + +#error "Architecture not supported for gc_helper_get_regs. Set MICROPY_GCREGS_SETJMP to use the fallback implementation." + +#endif + +#else // !MICROPY_GCREGS_SETJMP + +// Even if we have specific support for an architecture, it is +// possible to force use of setjmp-based implementation. + +STATIC void gc_helper_get_regs(gc_helper_regs_t arr) { + setjmp(arr); +} + +#endif // MICROPY_GCREGS_SETJMP + +// Explicitly mark this as noinline to make sure the regs variable +// is effectively at the top of the stack: otherwise, in builds where +// LTO is enabled and a lot of inlining takes place we risk a stack +// layout where regs is lower on the stack than pointers which have +// just been allocated but not yet marked, and get incorrectly sweeped. +MP_NOINLINE void gc_helper_collect_regs_and_stack(void) { + gc_helper_regs_t regs; + gc_helper_get_regs(regs); + // GC stack (and regs because we captured them) + void **regs_ptr = (void **)(void *)®s; + gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)®s) / sizeof(uintptr_t)); +} + +#endif // MICROPY_ENABLE_GC diff --git a/lib/utils/gchelper_m3.s b/lib/utils/gchelper_m3.s index 3aac0dedfd..5220fa0883 100644 --- a/lib/utils/gchelper_m3.s +++ b/lib/utils/gchelper_m3.s @@ -31,18 +31,6 @@ .section .text .align 2 - .global gc_helper_get_sp - .type gc_helper_get_sp, %function - -@ uint gc_helper_get_sp(void) -gc_helper_get_sp: - @ return the sp - mov r0, sp - bx lr - - .size gc_helper_get_sp, .-gc_helper_get_sp - - .global gc_helper_get_regs_and_sp .type gc_helper_get_regs_and_sp, %function diff --git a/lib/utils/gchelper_native.c b/lib/utils/gchelper_native.c new file mode 100644 index 0000000000..6bf386b519 --- /dev/null +++ b/lib/utils/gchelper_native.c @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 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 + +#include "py/mpstate.h" +#include "py/gc.h" +#include "lib/utils/gchelper.h" + +#if MICROPY_ENABLE_GC + +// provided by gchelper_*.s +uintptr_t gc_helper_get_regs_and_sp(uintptr_t *regs); + +MP_NOINLINE void gc_helper_collect_regs_and_stack(void) { + // get the registers and the sp + gc_helper_regs_t regs; + uintptr_t sp = gc_helper_get_regs_and_sp(regs); + + // trace the stack, including the registers (since they live on the stack in this function) + gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); +} + +#endif diff --git a/mpy-cross/Makefile b/mpy-cross/Makefile index 2116cc6708..f80ee761b7 100644 --- a/mpy-cross/Makefile +++ b/mpy-cross/Makefile @@ -48,6 +48,7 @@ LDFLAGS = $(LDFLAGS_MOD) $(LDFLAGS_ARCH) -lm $(LDFLAGS_EXTRA) SRC_C = \ main.c \ gccollect.c \ + lib/utils/gchelper_generic.c \ # Add fmode when compiling with mingw gcc COMPILER_TARGET := $(shell $(CC) -dumpmachine) diff --git a/mpy-cross/gccollect.c b/mpy-cross/gccollect.c index f655a07da7..120f1a2252 100644 --- a/mpy-cross/gccollect.c +++ b/mpy-cross/gccollect.c @@ -29,120 +29,13 @@ #include "py/mpstate.h" #include "py/gc.h" +#include "lib/utils/gchelper.h" + #if MICROPY_ENABLE_GC -// Even if we have specific support for an architecture, it is -// possible to force use of setjmp-based implementation. -#if !MICROPY_GCREGS_SETJMP - -// 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. -#if defined(__x86_64__) -typedef mp_uint_t regs_t[6]; - -STATIC 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"); - #ifdef __clang__ - // TODO: - // This is dirty workaround for Clang. It tries to get around - // uncompliant (wrt to GCC) behavior of handling register variables. - // Application of this patch here is random, and done only to unbreak - // MacOS build. Better, cross-arch ways to deal with Clang issues should - // be found. - asm ("" : "=r" (rbx)); - asm ("" : "=r" (rbp)); - asm ("" : "=r" (r12)); - asm ("" : "=r" (r13)); - asm ("" : "=r" (r14)); - asm ("" : "=r" (r15)); - #endif - arr[0] = rbx; - arr[1] = rbp; - arr[2] = r12; - arr[3] = r13; - arr[4] = r14; - arr[5] = r15; -} - -#elif defined(__i386__) - -typedef mp_uint_t regs_t[4]; - -STATIC 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; -} - -#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) - -typedef mp_uint_t regs_t[10]; - -STATIC void gc_helper_get_regs(regs_t arr) { - register long r4 asm ("r4"); - register long r5 asm ("r5"); - register long r6 asm ("r6"); - register long r7 asm ("r7"); - register long r8 asm ("r8"); - register long r9 asm ("r9"); - register long r10 asm ("r10"); - register long r11 asm ("r11"); - register long r12 asm ("r12"); - register long r13 asm ("r13"); - arr[0] = r4; - arr[1] = r5; - arr[2] = r6; - arr[3] = r7; - arr[4] = r8; - arr[5] = r9; - arr[6] = r10; - arr[7] = r11; - arr[8] = r12; - arr[9] = r13; -} - -#else - -// If we don't have architecture-specific optimized support, -// just fall back to setjmp-based implementation. -#undef MICROPY_GCREGS_SETJMP -#define MICROPY_GCREGS_SETJMP (1) - -#endif // Arch-specific selection -#endif // !MICROPY_GCREGS_SETJMP - -// If MICROPY_GCREGS_SETJMP was requested explicitly, or if -// we enabled it as a fallback above. -#if MICROPY_GCREGS_SETJMP -#include - -typedef jmp_buf regs_t; - -STATIC void gc_helper_get_regs(regs_t arr) { - setjmp(arr); -} - -#endif // MICROPY_GCREGS_SETJMP - void gc_collect(void) { gc_collect_start(); - regs_t regs; - gc_helper_get_regs(regs); - // GC stack (and regs because we captured them) - void **regs_ptr = (void **)(void *)®s; - gc_collect_root(regs_ptr, ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)®s) / sizeof(mp_uint_t)); + gc_helper_collect_regs_and_stack(); gc_collect_end(); } diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index f2b4ae99bf..2282e5e086 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -68,14 +68,9 @@ #define MICROPY_PY_BUILTINS_STR_UNICODE (1) -// Define to 1 to use undertested inefficient GC helper implementation -// (if more efficient arch-specific one is not available). -#ifndef MICROPY_GCREGS_SETJMP - #ifdef __mips__ - #define MICROPY_GCREGS_SETJMP (1) - #else - #define MICROPY_GCREGS_SETJMP (0) - #endif +#if !defined(__x86_64__) || !defined(__i386__) || !defined(__thumb2__) || !defined(__thumb__) || !defined(__arm__) +// Fall back to setjmp() implementation for discovery of GC pointers in registers. +#define MICROPY_GCREGS_SETJMP (1) #endif #define MICROPY_PY___FILE__ (0) diff --git a/mpy-cross/mpy-cross.vcxproj b/mpy-cross/mpy-cross.vcxproj index 805580c783..74a366712a 100644 --- a/mpy-cross/mpy-cross.vcxproj +++ b/mpy-cross/mpy-cross.vcxproj @@ -89,6 +89,9 @@ + + MICROPY_GCREGS_SETJMP + diff --git a/ports/cc3200/application.mk b/ports/cc3200/application.mk index 86ea3c7309..b216ca16dd 100644 --- a/ports/cc3200/application.mk +++ b/ports/cc3200/application.mk @@ -126,6 +126,7 @@ APP_UTIL_SRC_C = $(addprefix util/,\ ) APP_UTIL_SRC_S = $(addprefix util/,\ + cortex_m3_get_sp.s \ sleeprestore.s \ ) @@ -143,6 +144,7 @@ APP_LIB_SRC_C = $(addprefix lib/,\ mp-readline/readline.c \ netutils/netutils.c \ timeutils/timeutils.c \ + utils/gchelper_native.c \ utils/pyexec.c \ utils/interrupt_char.c \ utils/sys_stdio_mphal.c \ diff --git a/ports/cc3200/mptask.c b/ports/cc3200/mptask.c index 79c7fc349d..f06a502776 100644 --- a/ports/cc3200/mptask.c +++ b/ports/cc3200/mptask.c @@ -111,9 +111,11 @@ static const char fresh_boot_py[] = "# boot.py -- run on boot-up\r\n" DECLARE PUBLIC FUNCTIONS ******************************************************************************/ +uintptr_t cortex_m3_get_sp(void); + void TASK_MicroPython (void *pvParameters) { // get the top of the stack to initialize the garbage collector - uint32_t sp = gc_helper_get_sp(); + uint32_t sp = cortex_m3_get_sp(); bool safeboot = false; mptask_pre_init(); diff --git a/ports/cc3200/util/cortex_m3_get_sp.s b/ports/cc3200/util/cortex_m3_get_sp.s new file mode 100644 index 0000000000..b7e78f1d3b --- /dev/null +++ b/ports/cc3200/util/cortex_m3_get_sp.s @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-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. + */ + + .syntax unified + .cpu cortex-m3 + .thumb + + .section .text + .align 2 + + .global cortex_m3_get_sp + .type cortex_m3_get_sp, %function + +@ uint cortex_m3_get_sp(void) +cortex_m3_get_sp: + @ return the sp + mov r0, sp + bx lr + + .size cortex_m3_get_sp, .-cortex_m3_get_sp diff --git a/ports/cc3200/util/gccollect.c b/ports/cc3200/util/gccollect.c index 65b2f1605a..eac0c86b02 100644 --- a/ports/cc3200/util/gccollect.c +++ b/ports/cc3200/util/gccollect.c @@ -41,12 +41,8 @@ void gc_collect(void) { // start the GC gc_collect_start(); - // get the registers and the sp - mp_uint_t regs[10]; - mp_uint_t sp = gc_helper_get_regs_and_sp(regs); - - // trace the stack, including the registers (since they live on the stack in this function) - gc_collect_root((void**)sp, ((mp_uint_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + // trace the stack and the registers + gc_helper_collect_regs_and_stack(); // trace root pointers from any threads #if MICROPY_PY_THREAD diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 3bf9916e7f..cbd2b85957 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -96,6 +96,7 @@ SRC_C = \ mphalport.c \ lib/mp-readline/readline.c \ lib/libc/string0.c \ + lib/utils/gchelper_native.c \ lib/utils/printf.c \ lib/utils/pyexec.c \ lib/utils/stdout_helpers.c \ diff --git a/ports/mimxrt/main.c b/ports/mimxrt/main.c index a718cb46c7..d94bf24412 100644 --- a/ports/mimxrt/main.c +++ b/ports/mimxrt/main.c @@ -75,9 +75,7 @@ int main(void) { void gc_collect(void) { gc_collect_start(); - uintptr_t regs[10]; - uintptr_t sp = gc_helper_get_regs_and_sp(regs); - gc_collect_root((void **)sp, ((uintptr_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + gc_helper_collect_regs_and_stack(); gc_collect_end(); } diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 47a45af61d..430740ccf7 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -94,6 +94,7 @@ LIB_SRC_C += $(addprefix lib/,\ libm/atanf.c \ libm/atan2f.c \ libm/roundf.c \ + utils/gchelper_native.c \ utils/sys_stdio_mphal.c \ ) diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index 5fc5d4ece1..a59a8de0ad 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -31,14 +31,7 @@ int main() { void gc_collect(void) { gc_collect_start(); - - // get the registers and the sp - uintptr_t regs[10]; - uintptr_t sp = gc_helper_get_regs_and_sp(regs); - - // trace the stack, including the registers (since they live on the stack in this function) - gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - (uint32_t)sp) / sizeof(uint32_t)); - + gc_helper_collect_regs_and_stack(); gc_collect_end(); } diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 2a5c2905cb..ffd0f06a3f 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -65,6 +65,7 @@ SRC_C = \ lib/tinyusb/src/device/usbd_control.c \ lib/tinyusb/src/portable/microchip/samd/dcd_samd.c \ lib/tinyusb/src/tusb.c \ + lib/utils/gchelper_native.c \ lib/utils/printf.c \ lib/utils/pyexec.c \ lib/utils/stdout_helpers.c \ diff --git a/ports/samd/main.c b/ports/samd/main.c index 6c6b6f3541..038373ceb4 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -65,9 +65,7 @@ void samd_main(void) { void gc_collect(void) { gc_collect_start(); - uintptr_t regs[10]; - uintptr_t sp = gc_helper_get_regs_and_sp(regs); - gc_collect_root((void **)sp, ((uintptr_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + gc_helper_collect_regs_and_stack(); gc_collect_end(); } diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 4e35a4454d..fdea95e92f 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -148,6 +148,7 @@ LIB_SRC_C = $(addprefix lib/,\ netutils/trace.c \ netutils/dhcpserver.c \ timeutils/timeutils.c \ + utils/gchelper_native.c \ utils/pyexec.c \ utils/interrupt_char.c \ utils/sys_stdio_mphal.c \ diff --git a/ports/stm32/gccollect.c b/ports/stm32/gccollect.c index cd7b22c5f2..ad5645738e 100644 --- a/ports/stm32/gccollect.c +++ b/ports/stm32/gccollect.c @@ -43,12 +43,8 @@ void gc_collect(void) { // start the GC gc_collect_start(); - // get the registers and the sp - uintptr_t regs[10]; - uintptr_t sp = gc_helper_get_regs_and_sp(regs); - - // trace the stack, including the registers (since they live on the stack in this function) - gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + // trace the stack and registers + gc_helper_collect_regs_and_stack(); // trace root pointers from any threads #if MICROPY_PY_THREAD diff --git a/ports/teensy/Makefile b/ports/teensy/Makefile index adfc8d74f8..f2623eabd3 100644 --- a/ports/teensy/Makefile +++ b/ports/teensy/Makefile @@ -104,6 +104,7 @@ STM_SRC_C = $(addprefix ports/stm32/,\ LIB_SRC_C = $(addprefix lib/,\ libc/string0.c \ mp-readline/readline.c \ + utils/gchelper_native.c \ utils/pyexec.c \ utils/sys_stdio_mphal.c \ ) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index d9e77c26cd..ec14166149 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -205,6 +205,7 @@ SRC_C = \ LIB_SRC_C += $(addprefix lib/,\ $(LIB_SRC_C_EXTRA) \ timeutils/timeutils.c \ + utils/gchelper_generic.c \ ) OBJ = $(PY_O) diff --git a/ports/unix/gccollect.c b/ports/unix/gccollect.c index 231f016c2e..f0441e4ea3 100644 --- a/ports/unix/gccollect.c +++ b/ports/unix/gccollect.c @@ -29,146 +29,15 @@ #include "py/mpstate.h" #include "py/gc.h" +#include "lib/utils/gchelper.h" + #if MICROPY_ENABLE_GC -// Even if we have specific support for an architecture, it is -// possible to force use of setjmp-based implementation. -#if !MICROPY_GCREGS_SETJMP - -// 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. -#if defined(__x86_64__) -typedef mp_uint_t regs_t[6]; - -STATIC 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"); - #ifdef __clang__ - // TODO: - // This is dirty workaround for Clang. It tries to get around - // uncompliant (wrt to GCC) behavior of handling register variables. - // Application of this patch here is random, and done only to unbreak - // MacOS build. Better, cross-arch ways to deal with Clang issues should - // be found. - asm ("" : "=r" (rbx)); - asm ("" : "=r" (rbp)); - asm ("" : "=r" (r12)); - asm ("" : "=r" (r13)); - asm ("" : "=r" (r14)); - asm ("" : "=r" (r15)); - #endif - arr[0] = rbx; - arr[1] = rbp; - arr[2] = r12; - arr[3] = r13; - arr[4] = r14; - arr[5] = r15; -} - -#elif defined(__i386__) - -typedef mp_uint_t regs_t[4]; - -STATIC 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"); - #ifdef __clang__ - // TODO: - // This is dirty workaround for Clang. It tries to get around - // uncompliant (wrt to GCC) behavior of handling register variables. - // Application of this patch here is random, and done only to unbreak - // MacOS build. Better, cross-arch ways to deal with Clang issues should - // be found. - asm ("" : "=r" (ebx)); - asm ("" : "=r" (esi)); - asm ("" : "=r" (edi)); - asm ("" : "=r" (ebp)); - #endif - arr[0] = ebx; - arr[1] = esi; - arr[2] = edi; - arr[3] = ebp; -} - -#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__) - -typedef mp_uint_t regs_t[10]; - -STATIC void gc_helper_get_regs(regs_t arr) { - register long r4 asm ("r4"); - register long r5 asm ("r5"); - register long r6 asm ("r6"); - register long r7 asm ("r7"); - register long r8 asm ("r8"); - register long r9 asm ("r9"); - register long r10 asm ("r10"); - register long r11 asm ("r11"); - register long r12 asm ("r12"); - register long r13 asm ("r13"); - arr[0] = r4; - arr[1] = r5; - arr[2] = r6; - arr[3] = r7; - arr[4] = r8; - arr[5] = r9; - arr[6] = r10; - arr[7] = r11; - arr[8] = r12; - arr[9] = r13; -} - -#else - -// If we don't have architecture-specific optimized support, -// just fall back to setjmp-based implementation. -#undef MICROPY_GCREGS_SETJMP -#define MICROPY_GCREGS_SETJMP (1) - -#endif // Arch-specific selection -#endif // !MICROPY_GCREGS_SETJMP - -// If MICROPY_GCREGS_SETJMP was requested explicitly, or if -// we enabled it as a fallback above. -#if MICROPY_GCREGS_SETJMP -#include - -typedef jmp_buf regs_t; - -STATIC void gc_helper_get_regs(regs_t arr) { - setjmp(arr); -} - -#endif // MICROPY_GCREGS_SETJMP - -// this function is used by mpthreadport.c -MP_NOINLINE void gc_collect_regs_and_stack(void); - -// Explicitly mark this as noinline to make sure the regs variable -// is effectively at the top of the stack: otherwise, in builds where -// LTO is enabled and a lot of inlining takes place we risk a stack -// layout where regs is lower on the stack than pointers which have -// just been allocated but not yet marked, and get incorrectly sweeped. -MP_NOINLINE void gc_collect_regs_and_stack(void) { - regs_t regs; - gc_helper_get_regs(regs); - // GC stack (and regs because we captured them) - void **regs_ptr = (void **)(void *)®s; - gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)®s) / sizeof(uintptr_t)); -} - void gc_collect(void) { // gc_dump_info(); gc_collect_start(); - gc_collect_regs_and_stack(); + gc_helper_collect_regs_and_stack(); #if MICROPY_PY_THREAD mp_thread_gc_others(); #endif diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 5708ceab68..0565c0d763 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -178,14 +178,9 @@ extern const struct _mp_print_t mp_stderr_print; -// Define to 1 to use undertested inefficient GC helper implementation -// (if more efficient arch-specific one is not available). -#ifndef MICROPY_GCREGS_SETJMP - #ifdef __mips__ - #define MICROPY_GCREGS_SETJMP (1) - #else - #define MICROPY_GCREGS_SETJMP (0) - #endif +#if !defined(__x86_64__) || !defined(__i386__) || !defined(__thumb2__) || !defined(__thumb__) || !defined(__arm__) +// Fall back to setjmp() implementation for discovery of GC pointers in registers. +#define MICROPY_GCREGS_SETJMP (1) #endif #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 66c4289400..711cf2a6bb 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -39,6 +39,8 @@ #include #include +#include "lib/utils/gchelper.h" + // Some platforms don't have SIGRTMIN but if we do have it, use it to avoid // potential conflict with other uses of the more commonly used SIGUSR1. #ifdef SIGRTMIN @@ -88,8 +90,7 @@ STATIC void mp_thread_gc(int signo, siginfo_t *info, void *context) { (void)info; // unused (void)context; // unused if (signo == MP_THREAD_GC_SIGNAL) { - void gc_collect_regs_and_stack(void); - gc_collect_regs_and_stack(); + gc_helper_collect_regs_and_stack(); // We have access to the context (regs, stack) of the thread but it seems // that we don't need the extra information, enough is captured by the // gc_collect_regs_and_stack function above diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index e961ff4b24..25e1d20d5d 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -110,14 +110,9 @@ extern const struct _mp_obj_module_t mp_module_os; // Do not change anything beyond this line ////////////////////////////////////////// -// Define to 1 to use undertested inefficient GC helper implementation -// (if more efficient arch-specific one is not available). -#ifndef MICROPY_GCREGS_SETJMP - #ifdef __mips__ - #define MICROPY_GCREGS_SETJMP (1) - #else - #define MICROPY_GCREGS_SETJMP (0) - #endif +#if !defined(__x86_64__) || !defined(__i386__) || !defined(__thumb2__) || !defined(__thumb__) || !defined(__arm__) +// Fall back to setjmp() implementation for discovery of GC pointers in registers. +#define MICROPY_GCREGS_SETJMP (1) #endif // type definitions for the specific machine diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 6c89635131..b2d11872ed 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -28,6 +28,7 @@ endif # source files SRC_C = \ + lib/utils/gchelper_generic.c \ lib/utils/printf.c \ ports/unix/main.c \ ports/unix/input.c \ diff --git a/ports/windows/micropython.vcxproj b/ports/windows/micropython.vcxproj index dea06de358..f70fe96158 100644 --- a/ports/windows/micropython.vcxproj +++ b/ports/windows/micropython.vcxproj @@ -86,6 +86,7 @@ +