summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/gc.c7
-rw-r--r--py/modmicropython.c10
-rw-r--r--py/modthread.c6
-rw-r--r--py/mpconfig.h11
-rw-r--r--py/mpstate.h6
-rw-r--r--py/nlr.h19
-rw-r--r--py/nlrsetjmp.c1
-rw-r--r--py/nlrthumb.c2
-rw-r--r--py/nlrx64.c2
-rw-r--r--py/nlrx86.c2
-rw-r--r--py/nlrxtensa.c2
-rw-r--r--py/py.mk1
-rw-r--r--py/pystack.c56
-rw-r--r--py/pystack.h123
-rw-r--r--py/runtime.c2
-rw-r--r--py/runtime.h1
16 files changed, 249 insertions, 2 deletions
diff --git a/py/gc.c b/py/gc.c
index 734f5c3648..5196954e2e 100644
--- a/py/gc.c
+++ b/py/gc.c
@@ -320,11 +320,18 @@ void gc_collect_start(void) {
#endif
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.qstr_last_chunk) / sizeof(void*));
+
+ #if MICROPY_ENABLE_PYSTACK
+ // Trace root pointers from the Python stack.
+ ptrs = (void**)(void*)MP_STATE_THREAD(pystack_start);
+ gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void*));
+ #endif
}
void gc_collect_root(void **ptrs, size_t len) {
diff --git a/py/modmicropython.c b/py/modmicropython.c
index 2aac53adc7..c14a0177de 100644
--- a/py/modmicropython.c
+++ b/py/modmicropython.c
@@ -112,6 +112,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_st
#endif // MICROPY_PY_MICROPYTHON_MEM_INFO
+#if MICROPY_ENABLE_PYSTACK
+STATIC mp_obj_t mp_micropython_pystack_use(void) {
+ return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_pystack_use_obj, mp_micropython_pystack_use);
+#endif
+
#if MICROPY_ENABLE_GC
STATIC mp_obj_t mp_micropython_heap_lock(void) {
gc_lock();
@@ -167,6 +174,9 @@ STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
{ MP_ROM_QSTR(MP_QSTR_alloc_emergency_exception_buf), MP_ROM_PTR(&mp_alloc_emergency_exception_buf_obj) },
#endif
+ #if MICROPY_ENABLE_PYSTACK
+ { MP_ROM_QSTR(MP_QSTR_pystack_use), MP_ROM_PTR(&mp_micropython_pystack_use_obj) },
+ #endif
#if MICROPY_ENABLE_GC
{ MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) },
{ MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },
diff --git a/py/modthread.c b/py/modthread.c
index cb071d0f86..61ada50351 100644
--- a/py/modthread.c
+++ b/py/modthread.c
@@ -165,6 +165,12 @@ STATIC void *thread_entry(void *args_in) {
mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
mp_stack_set_limit(args->stack_size);
+ #if MICROPY_ENABLE_PYSTACK
+ // TODO threading and pystack is not fully supported, for now just make a small stack
+ mp_obj_t mini_pystack[128];
+ mp_pystack_init(mini_pystack, &mini_pystack[128]);
+ #endif
+
// set locals and globals from the calling context
mp_locals_set(args->dict_locals);
mp_globals_set(args->dict_globals);
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 7784367085..96cd8c651a 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -441,6 +441,17 @@
#define MICROPY_ENABLE_FINALISER (0)
#endif
+// Whether to enable a separate allocator for the Python stack.
+// If enabled then the code must call mp_pystack_init before mp_init.
+#ifndef MICROPY_ENABLE_PYSTACK
+#define MICROPY_ENABLE_PYSTACK (0)
+#endif
+
+// Number of bytes that memory returned by mp_pystack_alloc will be aligned by.
+#ifndef MICROPY_PYSTACK_ALIGN
+#define MICROPY_PYSTACK_ALIGN (8)
+#endif
+
// Whether to check C stack usage. C stack used for calling Python functions,
// etc. Not checking means segfault on overflow.
#ifndef MICROPY_STACK_CHECK
diff --git a/py/mpstate.h b/py/mpstate.h
index 6a39ebdea9..d3b53f9151 100644
--- a/py/mpstate.h
+++ b/py/mpstate.h
@@ -224,6 +224,12 @@ typedef struct _mp_state_thread_t {
#if MICROPY_STACK_CHECK
size_t stack_limit;
#endif
+
+ #if MICROPY_ENABLE_PYSTACK
+ uint8_t *pystack_start;
+ uint8_t *pystack_end;
+ uint8_t *pystack_cur;
+ #endif
} mp_state_thread_t;
// This structure combines the above 3 structures.
diff --git a/py/nlr.h b/py/nlr.h
index 63fe392d9e..1235f14609 100644
--- a/py/nlr.h
+++ b/py/nlr.h
@@ -62,15 +62,32 @@ struct _nlr_buf_t {
#if MICROPY_NLR_SETJMP
jmp_buf jmpbuf;
#endif
+
+ #if MICROPY_ENABLE_PYSTACK
+ void *pystack;
+ #endif
};
+// Helper macros to save/restore the pystack state
+#if MICROPY_ENABLE_PYSTACK
+#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur)
+#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack
+#else
+#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf
+#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf
+#endif
+
#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 = MP_STATE_THREAD(nlr_top), MP_STATE_THREAD(nlr_top) = (buf), setjmp((buf)->jmpbuf))
+#define nlr_push(buf) ( \
+ (buf)->prev = MP_STATE_THREAD(nlr_top), \
+ MP_NLR_SAVE_PYSTACK(buf), \
+ MP_STATE_THREAD(nlr_top) = (buf), \
+ setjmp((buf)->jmpbuf))
#define nlr_pop() { MP_STATE_THREAD(nlr_top) = MP_STATE_THREAD(nlr_top)->prev; }
#define nlr_jump(val) nlr_setjmp_jump(val)
#else
diff --git a/py/nlrsetjmp.c b/py/nlrsetjmp.c
index 1fb4594403..63376a5537 100644
--- a/py/nlrsetjmp.c
+++ b/py/nlrsetjmp.c
@@ -35,6 +35,7 @@ void nlr_setjmp_jump(void *val) {
nlr_jump_fail(val);
}
top->ret_val = val;
+ MP_NLR_RESTORE_PYSTACK(top);
*top_ptr = top->prev;
longjmp(top->jmpbuf, 1);
}
diff --git a/py/nlrthumb.c b/py/nlrthumb.c
index 6e7d717667..eab5759f21 100644
--- a/py/nlrthumb.c
+++ b/py/nlrthumb.c
@@ -82,6 +82,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
nlr->prev = *top;
+ MP_NLR_SAVE_PYSTACK(nlr);
*top = nlr;
return 0; // normal return
}
@@ -99,6 +100,7 @@ NORETURN __attribute__((naked)) void nlr_jump(void *val) {
}
top->ret_val = val;
+ MP_NLR_RESTORE_PYSTACK(top);
*top_ptr = top->prev;
__asm volatile (
diff --git a/py/nlrx64.c b/py/nlrx64.c
index 847d10398e..ddcd761665 100644
--- a/py/nlrx64.c
+++ b/py/nlrx64.c
@@ -91,6 +91,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
nlr->prev = *top;
+ MP_NLR_SAVE_PYSTACK(nlr);
*top = nlr;
return 0; // normal return
}
@@ -108,6 +109,7 @@ NORETURN void nlr_jump(void *val) {
}
top->ret_val = val;
+ MP_NLR_RESTORE_PYSTACK(top);
*top_ptr = top->prev;
__asm volatile (
diff --git a/py/nlrx86.c b/py/nlrx86.c
index 094dea3cc8..3a27460eb6 100644
--- a/py/nlrx86.c
+++ b/py/nlrx86.c
@@ -73,6 +73,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
nlr->prev = *top;
+ MP_NLR_SAVE_PYSTACK(nlr);
*top = nlr;
return 0; // normal return
}
@@ -90,6 +91,7 @@ NORETURN void nlr_jump(void *val) {
}
top->ret_val = val;
+ MP_NLR_RESTORE_PYSTACK(top);
*top_ptr = top->prev;
__asm volatile (
diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c
index 4520e7e7ac..5a969fc87c 100644
--- a/py/nlrxtensa.c
+++ b/py/nlrxtensa.c
@@ -58,6 +58,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) {
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr) {
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
nlr->prev = *top;
+ MP_NLR_SAVE_PYSTACK(nlr);
*top = nlr;
return 0; // normal return
}
@@ -75,6 +76,7 @@ NORETURN void nlr_jump(void *val) {
}
top->ret_val = val;
+ MP_NLR_RESTORE_PYSTACK(top);
*top_ptr = top->prev;
__asm volatile (
diff --git a/py/py.mk b/py/py.mk
index f5faad1821..0b5d5f8c40 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -110,6 +110,7 @@ PY_O_BASENAME = \
nlrsetjmp.o \
malloc.o \
gc.o \
+ pystack.o \
qstr.o \
vstr.o \
mpprint.o \
diff --git a/py/pystack.c b/py/pystack.c
new file mode 100644
index 0000000000..767c307e9a
--- /dev/null
+++ b/py/pystack.c
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 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 <stdio.h>
+
+#include "py/runtime.h"
+
+#if MICROPY_ENABLE_PYSTACK
+
+void mp_pystack_init(void *start, void *end) {
+ MP_STATE_THREAD(pystack_start) = start;
+ MP_STATE_THREAD(pystack_end) = end;
+ MP_STATE_THREAD(pystack_cur) = start;
+}
+
+void *mp_pystack_alloc(size_t n_bytes) {
+ n_bytes = (n_bytes + (MICROPY_PYSTACK_ALIGN - 1)) & ~(MICROPY_PYSTACK_ALIGN - 1);
+ #if MP_PYSTACK_DEBUG
+ n_bytes += MICROPY_PYSTACK_ALIGN;
+ #endif
+ if (MP_STATE_THREAD(pystack_cur) + n_bytes > MP_STATE_THREAD(pystack_end)) {
+ // out of memory in the pystack
+ mp_raise_recursion_depth();
+ }
+ void *ptr = MP_STATE_THREAD(pystack_cur);
+ MP_STATE_THREAD(pystack_cur) += n_bytes;
+ #if MP_PYSTACK_DEBUG
+ *(size_t*)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN) = n_bytes;
+ #endif
+ return ptr;
+}
+
+#endif
diff --git a/py/pystack.h b/py/pystack.h
new file mode 100644
index 0000000000..82ac3743d1
--- /dev/null
+++ b/py/pystack.h
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 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_PYSTACK_H
+#define MICROPY_INCLUDED_PY_PYSTACK_H
+
+#include "py/mpstate.h"
+
+// Enable this debugging option to check that the amount of memory freed is
+// consistent with amounts that were previously allocated.
+#define MP_PYSTACK_DEBUG (0)
+
+#if MICROPY_ENABLE_PYSTACK
+
+void mp_pystack_init(void *start, void *end);
+void *mp_pystack_alloc(size_t n_bytes);
+
+// This function can free multiple continuous blocks at once: just pass the
+// pointer to the block that was allocated first and it and all subsequently
+// allocated blocks will be freed.
+static inline void mp_pystack_free(void *ptr) {
+ assert((uint8_t*)ptr >= MP_STATE_THREAD(pystack_start));
+ assert((uint8_t*)ptr <= MP_STATE_THREAD(pystack_cur));
+ #if MP_PYSTACK_DEBUG
+ size_t n_bytes_to_free = MP_STATE_THREAD(pystack_cur) - (uint8_t*)ptr;
+ size_t n_bytes = *(size_t*)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN);
+ while (n_bytes < n_bytes_to_free) {
+ n_bytes += *(size_t*)(MP_STATE_THREAD(pystack_cur) - n_bytes - MICROPY_PYSTACK_ALIGN);
+ }
+ if (n_bytes != n_bytes_to_free) {
+ mp_printf(&mp_plat_print, "mp_pystack_free() failed: %u != %u\n", (uint)n_bytes_to_free,
+ (uint)*(size_t*)(MP_STATE_THREAD(pystack_cur) - MICROPY_PYSTACK_ALIGN));
+ assert(0);
+ }
+ #endif
+ MP_STATE_THREAD(pystack_cur) = (uint8_t*)ptr;
+}
+
+static inline void mp_pystack_realloc(void *ptr, size_t n_bytes) {
+ mp_pystack_free(ptr);
+ mp_pystack_alloc(n_bytes);
+}
+
+static inline size_t mp_pystack_usage(void) {
+ return MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start);
+}
+
+static inline size_t mp_pystack_limit(void) {
+ return MP_STATE_THREAD(pystack_end) - MP_STATE_THREAD(pystack_start);
+}
+
+#endif
+
+#if !MICROPY_ENABLE_PYSTACK
+
+#define mp_local_alloc(n_bytes) alloca(n_bytes)
+
+static inline void mp_local_free(void *ptr) {
+ (void)ptr;
+}
+
+static inline void *mp_nonlocal_alloc(size_t n_bytes) {
+ return m_new(uint8_t, n_bytes);
+}
+
+static inline void *mp_nonlocal_realloc(void *ptr, size_t old_n_bytes, size_t new_n_bytes) {
+ return m_renew(uint8_t, ptr, old_n_bytes, new_n_bytes);
+}
+
+static inline void mp_nonlocal_free(void *ptr, size_t n_bytes) {
+ m_del(uint8_t, ptr, n_bytes);
+}
+
+#else
+
+static inline void *mp_local_alloc(size_t n_bytes) {
+ return mp_pystack_alloc(n_bytes);
+}
+
+static inline void mp_local_free(void *ptr) {
+ mp_pystack_free(ptr);
+}
+
+static inline void *mp_nonlocal_alloc(size_t n_bytes) {
+ return mp_pystack_alloc(n_bytes);
+}
+
+static inline void *mp_nonlocal_realloc(void *ptr, size_t old_n_bytes, size_t new_n_bytes) {
+ (void)old_n_bytes;
+ mp_pystack_realloc(ptr, new_n_bytes);
+ return ptr;
+}
+
+static inline void mp_nonlocal_free(void *ptr, size_t n_bytes) {
+ (void)n_bytes;
+ mp_pystack_free(ptr);
+}
+
+#endif
+
+#endif // MICROPY_INCLUDED_PY_PYSTACK_H
diff --git a/py/runtime.c b/py/runtime.c
index 5fd053e1a2..3a4a8a93b0 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1457,7 +1457,7 @@ NORETURN void mp_raise_NotImplementedError(const char *msg) {
mp_raise_msg(&mp_type_NotImplementedError, msg);
}
-#if MICROPY_STACK_CHECK
+#if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK
NORETURN void mp_raise_recursion_depth(void) {
nlr_raise(mp_obj_new_exception_arg1(&mp_type_RuntimeError,
MP_OBJ_NEW_QSTR(MP_QSTR_maximum_space_recursion_space_depth_space_exceeded)));
diff --git a/py/runtime.h b/py/runtime.h
index a19f64c067..6288e88367 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -27,6 +27,7 @@
#define MICROPY_INCLUDED_PY_RUNTIME_H
#include "py/mpstate.h"
+#include "py/pystack.h"
typedef enum {
MP_VM_RETURN_NORMAL,