[python] Fixed MicroPython emscripten bug.

Micropython did not work because of two problems:
 - the stack in ascending order on emscripten
 - if optimized, some variables may be stored in local JavaScript
variables, which breaks garbage collection.

Change-Id: Ib454e0c4d995e9f5f85370eea758526119b35773
This commit is contained in:
Léa Saviot
2017-11-24 17:34:48 +01:00
parent a78bd0db88
commit 9a9ccc7a8c
3 changed files with 92 additions and 7 deletions

View File

@@ -67,7 +67,6 @@ py_objs = $(addprefix python/src/py/,\
runtime_utils.o \
scheduler.o \
nativeglue.o \
stackctrl.o \
argcheck.o \
warning.o \
map.o \
@@ -179,8 +178,21 @@ port_objs += $(addprefix python/port/,\
modkandinsky.o \
modkandinsky_impl.o \
mphalport.o \
stackctrl.o \
)
# Reduce optimization for the emscripten platform.
# With optimization, register and stack variables might be held in a JavaScript
# local variable, which breaks garbage collection. Indeed, these JavaScript
# variables cannot be marked as root during garbage collection, which means that
# the heap objects they depend on will likely be destroyed. When the Python
# computing resumes, if necessary heap objects have been destroyed, the Python
# program crashes.
ifeq ($(PLATFORM),emscripten)
$(py_objs): SFLAGS := $(subst -Os,-O0,$(SFLAGS))
endif
# QSTR generation
python/port/genhdr/qstrdefs.generated.h: python/port/genhdr/qstrdefs.in.h

View File

@@ -137,7 +137,9 @@ void MicroPython::registerScriptProvider(ScriptProvider * s) {
}
void gc_collect(void) {
assert(MP_STATE_THREAD(stack_top) != NULL);
void * python_stack_top = MP_STATE_THREAD(stack_top);
assert(python_stack_top != NULL);
gc_collect_start();
/* get the registers.
@@ -146,18 +148,19 @@ void gc_collect(void) {
jmp_buf regs;
setjmp(regs);
void **regs_ptr = (void**)(void*)&regs;
void **regs_ptr = (void**)&regs;
/* On the device, the stack is stored in reverse order, but it might not be
* the case on a computer. We thus have to take the absolute value of the
* addresses difference. */
size_t stackLength;
if ((uintptr_t)MP_STATE_THREAD(stack_top) > (uintptr_t)(&regs)) {
stackLength = (((uintptr_t)(MP_STATE_THREAD(stack_top)) - (uintptr_t)(&regs)) / sizeof(uintptr_t));
if ((uintptr_t)python_stack_top > (uintptr_t)&regs) {
stackLength = ((uintptr_t)python_stack_top - (uintptr_t)&regs) / sizeof(uintptr_t);
gc_collect_root(regs_ptr, stackLength);
} else {
stackLength = (((uintptr_t)(&regs) - (uintptr_t)(MP_STATE_THREAD(stack_top))) / sizeof(uintptr_t));
stackLength = ((uintptr_t)(&regs) - (uintptr_t)python_stack_top) / sizeof(uintptr_t);
gc_collect_root((void **)python_stack_top, stackLength);
}
gc_collect_root(regs_ptr, stackLength);
gc_collect_end();
}

70
python/port/stackctrl.c Normal file
View File

@@ -0,0 +1,70 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Paul Sokolovsky
*
* 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"
#include "py/nlr.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
void mp_stack_ctrl_init(void) {
volatile int stack_dummy;
MP_STATE_THREAD(stack_top) = (char*)&stack_dummy;
}
void mp_stack_set_top(void *top) {
MP_STATE_THREAD(stack_top) = top;
}
mp_uint_t mp_stack_usage(void) {
/* Micropython's stackctrl.c assumes that the stack is in descending order,
* which is not always the case for us. */
volatile int stack_dummy;
if (MP_STATE_THREAD(stack_top) > (char*)&stack_dummy) {
return MP_STATE_THREAD(stack_top) - (char*)&stack_dummy;
} else {
return (char*)&stack_dummy - MP_STATE_THREAD(stack_top);
}
}
#if MICROPY_STACK_CHECK
void mp_stack_set_limit(mp_uint_t limit) {
MP_STATE_THREAD(stack_limit) = limit;
}
void mp_exc_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)));
}
void mp_stack_check(void) {
if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) {
mp_exc_recursion_depth();
}
}
#endif // MICROPY_STACK_CHECK