vm: Initial support for calling bytecode functions w/o C stack ("stackless").

This commit is contained in:
Paul Sokolovsky 2015-03-28 01:14:44 +02:00
parent f88eec0de2
commit 2039757b85
5 changed files with 85 additions and 2 deletions

View File

@ -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_obj_fun_bc_t *self = self_in;
mp_uint_t n_state = code_state->n_state; 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->code_info = self->bytecode;
code_state->sp = &code_state->state[0] - 1; code_state->sp = &code_state->state[0] - 1;
code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;

View File

@ -46,6 +46,9 @@ typedef struct _mp_code_state {
// bit 0 is saved currently_in_except_block value // bit 0 is saved currently_in_except_block value
mp_exc_stack_t *exc_sp; mp_exc_stack_t *exc_sp;
mp_obj_dict_t *old_globals; mp_obj_dict_t *old_globals;
#if MICROPY_STACKLESS
struct _mp_code_state *prev;
#endif
mp_uint_t n_state; mp_uint_t n_state;
// Variable-length // Variable-length
mp_obj_t state[0]; mp_obj_t state[0];
@ -56,6 +59,7 @@ typedef struct _mp_code_state {
mp_uint_t mp_decode_uint(const byte **ptr); 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_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_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_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); void mp_bytecode_print2(const byte *code, mp_uint_t len);

View File

@ -125,6 +125,11 @@
#define MICROPY_QSTR_BYTES_IN_LEN (1) #define MICROPY_QSTR_BYTES_IN_LEN (1)
#endif #endif
// Avoid using C stack when making Python function calls.
#ifndef MICROPY_STACKLESS
#define MICROPY_STACKLESS (0)
#endif
/*****************************************************************************/ /*****************************************************************************/
/* Micro Python emitters */ /* Micro Python emitters */

View File

@ -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. // Set this to enable a simple stack overflow check.
#define VM_DETECT_STACK_OVERFLOW (0) #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) { 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(); MP_STACK_CHECK();

43
py/vm.c
View File

@ -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. // 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) #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() // 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_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_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) // 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 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 & 0xff == n_positional
// (unum >> 8) & 0xff == n_keyword // (unum >> 8) & 0xff == n_keyword
sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe); 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)); SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1));
DISPATCH(); DISPATCH();
} }
@ -925,6 +940,15 @@ unwind_return:
nlr_pop(); nlr_pop();
code_state->sp = sp; code_state->sp = sp;
assert(exc_sp == exc_stack - 1); 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; return MP_VM_RETURN_NORMAL;
ENTRY(MP_BC_RAISE_VARARGS): { ENTRY(MP_BC_RAISE_VARARGS): {
@ -1122,6 +1146,9 @@ exception_handler:
goto outer_dispatch_loop; // continue with dispatch loop goto outer_dispatch_loop; // continue with dispatch loop
} }
#if MICROPY_STACKLESS
unwind_loop:
#endif
// set file and line number that the exception occurred at // set file and line number that the exception occurred at
// TODO: don't set traceback for exceptions re-raised by END_FINALLY. // TODO: don't set traceback for exceptions re-raised by END_FINALLY.
// But consider how to handle nested exceptions. // But consider how to handle nested exceptions.
@ -1185,6 +1212,18 @@ exception_handler:
PUSH(mp_obj_get_type(nlr.ret_val)); PUSH(mp_obj_get_type(nlr.ret_val));
code_state->sp = sp; 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 { } else {
// propagate exception to higher level // propagate exception to higher level
// TODO what to do about ip and sp? they don't really make sense at this point // TODO what to do about ip and sp? they don't really make sense at this point