From 2039757b854b656a41c9e041776ccd5dad8ec5fe Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sat, 28 Mar 2015 01:14:44 +0200 Subject: [PATCH] vm: Initial support for calling bytecode functions w/o C stack ("stackless"). --- py/bc.c | 3 +++ py/bc.h | 4 ++++ py/mpconfig.h | 5 +++++ py/objfun.c | 32 ++++++++++++++++++++++++++++++++ py/vm.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 85 insertions(+), 2 deletions(-) diff --git a/py/bc.c b/py/bc.c index 3a8dc5e04f..af855e9474 100644 --- a/py/bc.c +++ b/py/bc.c @@ -86,6 +86,9 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t mp_obj_fun_bc_t *self = self_in; mp_uint_t n_state = code_state->n_state; + #if MICROPY_STACKLESS + code_state->prev = NULL; + #endif code_state->code_info = self->bytecode; code_state->sp = &code_state->state[0] - 1; code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; diff --git a/py/bc.h b/py/bc.h index f3885587a9..b4b4d8c550 100644 --- a/py/bc.h +++ b/py/bc.h @@ -46,6 +46,9 @@ typedef struct _mp_code_state { // bit 0 is saved currently_in_except_block value mp_exc_stack_t *exc_sp; mp_obj_dict_t *old_globals; + #if MICROPY_STACKLESS + struct _mp_code_state *prev; + #endif mp_uint_t n_state; // Variable-length mp_obj_t state[0]; @@ -56,6 +59,7 @@ typedef struct _mp_code_state { mp_uint_t mp_decode_uint(const byte **ptr); mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc); +mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args); void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args); void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *code, mp_uint_t len); void mp_bytecode_print2(const byte *code, mp_uint_t len); diff --git a/py/mpconfig.h b/py/mpconfig.h index ae5e776256..94e2737de0 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -125,6 +125,11 @@ #define MICROPY_QSTR_BYTES_IN_LEN (1) #endif +// Avoid using C stack when making Python function calls. +#ifndef MICROPY_STACKLESS +#define MICROPY_STACKLESS (0) +#endif + /*****************************************************************************/ /* Micro Python emitters */ diff --git a/py/objfun.c b/py/objfun.c index d8ea22f394..187bb5a59e 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -142,6 +142,38 @@ STATIC void dump_args(const mp_obj_t *a, mp_uint_t sz) { // Set this to enable a simple stack overflow check. #define VM_DETECT_STACK_OVERFLOW (0) +mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + MP_STACK_CHECK(); + mp_obj_fun_bc_t *self = self_in; + + // skip code-info block + const byte *code_info = self->bytecode; + mp_uint_t code_info_size = mp_decode_uint(&code_info); + const byte *ip = self->bytecode + code_info_size; + + // bytecode prelude: skip arg names + ip += (self->n_pos_args + self->n_kwonly_args) * sizeof(mp_obj_t); + + // bytecode prelude: state size and exception stack size + mp_uint_t n_state = mp_decode_uint(&ip); + mp_uint_t n_exc_stack = mp_decode_uint(&ip); + + // allocate state for locals and stack + mp_uint_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t); + mp_code_state *code_state; + code_state = m_new_obj_var(mp_code_state, byte, state_size); + + code_state->n_state = n_state; + code_state->ip = ip; + mp_setup_code_state(code_state, self_in, n_args, n_kw, args); + + // execute the byte code with the correct globals context + code_state->old_globals = mp_globals_get(); + mp_globals_set(self->globals); + + return code_state; +} + STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { MP_STACK_CHECK(); diff --git a/py/vm.c b/py/vm.c index 29a9e4f6d6..3a7a816977 100644 --- a/py/vm.c +++ b/py/vm.c @@ -129,9 +129,12 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_o // loop and the exception handler, leading to very obscure bugs. #define RAISE(o) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0) +#if MICROPY_STACKLESS +run_code_state: ; +#endif // Pointers which are constant for particular invocation of mp_execute_bytecode() - mp_obj_t *const fastn = &code_state->state[code_state->n_state - 1]; - mp_exc_stack_t *const exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state); + mp_obj_t */*const*/ fastn = &code_state->state[code_state->n_state - 1]; + mp_exc_stack_t */*const*/ exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state); // variables that are visible to the exception handler (declared volatile) volatile bool currently_in_except_block = MP_TAGPTR_TAG0(code_state->exc_sp); // 0 or 1, to detect nested exceptions @@ -865,6 +868,18 @@ unwind_jump:; // unum & 0xff == n_positional // (unum >> 8) & 0xff == n_keyword sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe); + #if MICROPY_STACKLESS + if (mp_obj_get_type(*sp) == &mp_type_fun_bc) { + code_state->ip = ip; + code_state->sp = sp; + code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block); + mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1); + new_state->prev = code_state; + code_state = new_state; + nlr_pop(); + goto run_code_state; + } + #endif SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1)); DISPATCH(); } @@ -925,6 +940,15 @@ unwind_return: nlr_pop(); code_state->sp = sp; assert(exc_sp == exc_stack - 1); + #if MICROPY_STACKLESS + if (code_state->prev != NULL) { + mp_obj_t res = *sp; + mp_globals_set(code_state->old_globals); + code_state = code_state->prev; + *code_state->sp = res; + goto run_code_state; + } + #endif return MP_VM_RETURN_NORMAL; ENTRY(MP_BC_RAISE_VARARGS): { @@ -1122,6 +1146,9 @@ exception_handler: goto outer_dispatch_loop; // continue with dispatch loop } +#if MICROPY_STACKLESS +unwind_loop: +#endif // set file and line number that the exception occurred at // TODO: don't set traceback for exceptions re-raised by END_FINALLY. // But consider how to handle nested exceptions. @@ -1185,6 +1212,18 @@ exception_handler: PUSH(mp_obj_get_type(nlr.ret_val)); code_state->sp = sp; + #if MICROPY_STACKLESS + } else if (code_state->prev != NULL) { + mp_globals_set(code_state->old_globals); + code_state = code_state->prev; + fastn = &code_state->state[code_state->n_state - 1]; + exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state); + // variables that are visible to the exception handler (declared volatile) + currently_in_except_block = MP_TAGPTR_TAG0(code_state->exc_sp); // 0 or 1, to detect nested exceptions + exc_sp = MP_TAGPTR_PTR(code_state->exc_sp); // stack grows up, exc_sp points to top of stack + goto unwind_loop; + + #endif } else { // propagate exception to higher level // TODO what to do about ip and sp? they don't really make sense at this point