Skip to content

Commit 2039757

Browse files
committed
vm: Initial support for calling bytecode functions w/o C stack ("stackless").
1 parent f88eec0 commit 2039757

File tree

5 files changed

+85
-2
lines changed

5 files changed

+85
-2
lines changed

py/bc.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t
8686
mp_obj_fun_bc_t *self = self_in;
8787
mp_uint_t n_state = code_state->n_state;
8888

89+
#if MICROPY_STACKLESS
90+
code_state->prev = NULL;
91+
#endif
8992
code_state->code_info = self->bytecode;
9093
code_state->sp = &code_state->state[0] - 1;
9194
code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;

py/bc.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ typedef struct _mp_code_state {
4646
// bit 0 is saved currently_in_except_block value
4747
mp_exc_stack_t *exc_sp;
4848
mp_obj_dict_t *old_globals;
49+
#if MICROPY_STACKLESS
50+
struct _mp_code_state *prev;
51+
#endif
4952
mp_uint_t n_state;
5053
// Variable-length
5154
mp_obj_t state[0];
@@ -56,6 +59,7 @@ typedef struct _mp_code_state {
5659
mp_uint_t mp_decode_uint(const byte **ptr);
5760

5861
mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_obj_t inject_exc);
62+
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);
5963
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);
6064
void mp_bytecode_print(const void *descr, mp_uint_t n_total_args, const byte *code, mp_uint_t len);
6165
void mp_bytecode_print2(const byte *code, mp_uint_t len);

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@
125125
#define MICROPY_QSTR_BYTES_IN_LEN (1)
126126
#endif
127127

128+
// Avoid using C stack when making Python function calls.
129+
#ifndef MICROPY_STACKLESS
130+
#define MICROPY_STACKLESS (0)
131+
#endif
132+
128133
/*****************************************************************************/
129134
/* Micro Python emitters */
130135

py/objfun.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,38 @@ STATIC void dump_args(const mp_obj_t *a, mp_uint_t sz) {
142142
// Set this to enable a simple stack overflow check.
143143
#define VM_DETECT_STACK_OVERFLOW (0)
144144

145+
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) {
146+
MP_STACK_CHECK();
147+
mp_obj_fun_bc_t *self = self_in;
148+
149+
// skip code-info block
150+
const byte *code_info = self->bytecode;
151+
mp_uint_t code_info_size = mp_decode_uint(&code_info);
152+
const byte *ip = self->bytecode + code_info_size;
153+
154+
// bytecode prelude: skip arg names
155+
ip += (self->n_pos_args + self->n_kwonly_args) * sizeof(mp_obj_t);
156+
157+
// bytecode prelude: state size and exception stack size
158+
mp_uint_t n_state = mp_decode_uint(&ip);
159+
mp_uint_t n_exc_stack = mp_decode_uint(&ip);
160+
161+
// allocate state for locals and stack
162+
mp_uint_t state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
163+
mp_code_state *code_state;
164+
code_state = m_new_obj_var(mp_code_state, byte, state_size);
165+
166+
code_state->n_state = n_state;
167+
code_state->ip = ip;
168+
mp_setup_code_state(code_state, self_in, n_args, n_kw, args);
169+
170+
// execute the byte code with the correct globals context
171+
code_state->old_globals = mp_globals_get();
172+
mp_globals_set(self->globals);
173+
174+
return code_state;
175+
}
176+
145177
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) {
146178
MP_STACK_CHECK();
147179

py/vm.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,12 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state *code_state, volatile mp_o
129129
// loop and the exception handler, leading to very obscure bugs.
130130
#define RAISE(o) do { nlr_pop(); nlr.ret_val = o; goto exception_handler; } while(0)
131131

132+
#if MICROPY_STACKLESS
133+
run_code_state: ;
134+
#endif
132135
// Pointers which are constant for particular invocation of mp_execute_bytecode()
133-
mp_obj_t *const fastn = &code_state->state[code_state->n_state - 1];
134-
mp_exc_stack_t *const exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state);
136+
mp_obj_t */*const*/ fastn = &code_state->state[code_state->n_state - 1];
137+
mp_exc_stack_t */*const*/ exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state);
135138

136139
// variables that are visible to the exception handler (declared volatile)
137140
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:;
865868
// unum & 0xff == n_positional
866869
// (unum >> 8) & 0xff == n_keyword
867870
sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe);
871+
#if MICROPY_STACKLESS
872+
if (mp_obj_get_type(*sp) == &mp_type_fun_bc) {
873+
code_state->ip = ip;
874+
code_state->sp = sp;
875+
code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
876+
mp_code_state *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1);
877+
new_state->prev = code_state;
878+
code_state = new_state;
879+
nlr_pop();
880+
goto run_code_state;
881+
}
882+
#endif
868883
SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1));
869884
DISPATCH();
870885
}
@@ -925,6 +940,15 @@ unwind_jump:;
925940
nlr_pop();
926941
code_state->sp = sp;
927942
assert(exc_sp == exc_stack - 1);
943+
#if MICROPY_STACKLESS
944+
if (code_state->prev != NULL) {
945+
mp_obj_t res = *sp;
946+
mp_globals_set(code_state->old_globals);
947+
code_state = code_state->prev;
948+
*code_state->sp = res;
949+
goto run_code_state;
950+
}
951+
#endif
928952
return MP_VM_RETURN_NORMAL;
929953

930954
ENTRY(MP_BC_RAISE_VARARGS): {
@@ -1122,6 +1146,9 @@ unwind_jump:;
11221146
goto outer_dispatch_loop; // continue with dispatch loop
11231147
}
11241148

1149+
#if MICROPY_STACKLESS
1150+
unwind_loop:
1151+
#endif
11251152
// set file and line number that the exception occurred at
11261153
// TODO: don't set traceback for exceptions re-raised by END_FINALLY.
11271154
// But consider how to handle nested exceptions.
@@ -1185,6 +1212,18 @@ unwind_jump:;
11851212
PUSH(mp_obj_get_type(nlr.ret_val));
11861213
code_state->sp = sp;
11871214

1215+
#if MICROPY_STACKLESS
1216+
} else if (code_state->prev != NULL) {
1217+
mp_globals_set(code_state->old_globals);
1218+
code_state = code_state->prev;
1219+
fastn = &code_state->state[code_state->n_state - 1];
1220+
exc_stack = (mp_exc_stack_t*)(code_state->state + code_state->n_state);
1221+
// variables that are visible to the exception handler (declared volatile)
1222+
currently_in_except_block = MP_TAGPTR_TAG0(code_state->exc_sp); // 0 or 1, to detect nested exceptions
1223+
exc_sp = MP_TAGPTR_PTR(code_state->exc_sp); // stack grows up, exc_sp points to top of stack
1224+
goto unwind_loop;
1225+
1226+
#endif
11881227
} else {
11891228
// propagate exception to higher level
11901229
// TODO what to do about ip and sp? they don't really make sense at this point

0 commit comments

Comments
 (0)