summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
Diffstat (limited to 'py')
-rw-r--r--py/bc.h3
-rw-r--r--py/objfun.c116
-rw-r--r--py/objgenerator.c4
-rw-r--r--py/vm.c120
4 files changed, 120 insertions, 123 deletions
diff --git a/py/bc.h b/py/bc.h
index 915a0f269e..2b9793236f 100644
--- a/py/bc.h
+++ b/py/bc.h
@@ -49,8 +49,7 @@ typedef struct _mp_code_state {
//mp_exc_stack_t exc_state[0];
} mp_code_state;
-mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args, uint n_args, const mp_obj_t *args2, uint n_args2, mp_obj_t *ret);
-mp_vm_return_kind_t mp_execute_bytecode2(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);
void mp_bytecode_print(const void *descr, const byte *code, int len);
void mp_bytecode_print2(const byte *code, int len);
diff --git a/py/objfun.c b/py/objfun.c
index 77d57821b9..57398e6d61 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -237,6 +237,14 @@ arg_error:
fun_pos_args_mismatch(self, self->n_pos_args, n_args);
}
+// With this macro you can tune the maximum number of function state bytes
+// that will be allocated on the stack. Any function that needs more
+// than this will use the heap.
+#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t))
+
+// Set this to enable a simple stack overflow check.
+#define VM_DETECT_STACK_OVERFLOW (0)
+
STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// This function is pretty complicated. It's main aim is to be efficient in speed and RAM
// usage for the common case of positional only args.
@@ -379,7 +387,113 @@ continue2:;
DEBUG_printf("Calling: args=%p, n_args=%d, extra_args=%p, n_extra_args=%d\n", args, n_args, extra_args, n_extra_args);
dump_args(args, n_args);
dump_args(extra_args, n_extra_args);
- mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(self->bytecode, args, n_args, extra_args, n_extra_args, &result);
+
+ // At this point the args have all been processed and we are ready to
+ // execute the bytecode. But we must first build the execution context.
+
+ const byte *ip = self->bytecode;
+
+ // get code info size, and skip line number table
+ machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
+ ip += code_info_size;
+
+ // bytecode prelude: state size and exception stack size; 16 bit uints
+ machine_uint_t n_state = ip[0] | (ip[1] << 8);
+ machine_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
+ ip += 4;
+
+ // allocate state for locals and stack
+#if VM_DETECT_STACK_OVERFLOW
+ n_state += 1;
+#endif
+
+ int state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
+ mp_code_state *code_state;
+ if (state_size > VM_MAX_STATE_ON_STACK) {
+ code_state = m_new_obj_var(mp_code_state, byte, state_size);
+ } else {
+ code_state = alloca(sizeof(mp_code_state) + state_size);
+ }
+
+ 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;
+ code_state->n_state = n_state;
+
+ // init args
+ for (uint i = 0; i < n_args; i++) {
+ code_state->state[n_state - 1 - i] = args[i];
+ }
+ for (uint i = 0; i < n_extra_args; i++) {
+ code_state->state[n_state - 1 - n_args - i] = extra_args[i];
+ }
+
+ // set rest of state to MP_OBJ_NULL
+ for (uint i = 0; i < n_state - n_args - n_extra_args; i++) {
+ code_state->state[i] = MP_OBJ_NULL;
+ }
+
+ // bytecode prelude: initialise closed over variables
+ for (uint n_local = *ip++; n_local > 0; n_local--) {
+ uint local_num = *ip++;
+ code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
+ }
+
+ code_state->ip = ip;
+
+ // execute the byte code
+ mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode(code_state, MP_OBJ_NULL);
+
+#if VM_DETECT_STACK_OVERFLOW
+ if (vm_return_kind == MP_VM_RETURN_NORMAL) {
+ if (code_state->sp < code_state->state) {
+ printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state);
+ assert(0);
+ }
+ }
+ // We can't check the case when an exception is returned in state[n_state - 1]
+ // and there are no arguments, because in this case our detection slot may have
+ // been overwritten by the returned exception (which is allowed).
+ if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_args == 0 && n_extra_args == 0)) {
+ // Just check to see that we have at least 1 null object left in the state.
+ bool overflow = true;
+ for (uint i = 0; i < n_state - n_args - n_extra_args; i++) {
+ if (code_state->state[i] == MP_OBJ_NULL) {
+ overflow = false;
+ break;
+ }
+ }
+ if (overflow) {
+ printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state);
+ assert(0);
+ }
+ }
+#endif
+
+ switch (vm_return_kind) {
+ case MP_VM_RETURN_NORMAL:
+ // return value is in *sp
+ result = *code_state->sp;
+ break;
+
+ case MP_VM_RETURN_EXCEPTION:
+ // return value is in state[n_state - 1]
+ result = code_state->state[n_state - 1];
+ break;
+
+ case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
+ default:
+ assert(0);
+ result = mp_const_none;
+ vm_return_kind = MP_VM_RETURN_NORMAL;
+ break;
+ }
+
+ // free the state if it was allocated on the heap
+ if (state_size > VM_MAX_STATE_ON_STACK) {
+ m_del_var(mp_code_state, byte, state_size, code_state);
+ }
+
mp_globals_set(old_globals);
if (vm_return_kind == MP_VM_RETURN_NORMAL) {
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 7326bced30..b816cc49d0 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -106,7 +106,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
}
mp_obj_dict_t *old_globals = mp_globals_get();
mp_globals_set(self->globals);
- mp_vm_return_kind_t ret_kind = mp_execute_bytecode2(&self->code_state, throw_value);
+ mp_vm_return_kind_t ret_kind = mp_execute_bytecode(&self->code_state, throw_value);
mp_globals_set(old_globals);
switch (ret_kind) {
@@ -263,7 +263,7 @@ mp_obj_t mp_obj_new_gen_instance(mp_obj_dict_t *globals, const byte *bytecode,
o->code_state.exc_sp = (mp_exc_stack_t*)(o->code_state.state + n_state) - 1;
o->code_state.n_state = n_state;
- // copy args to end of state array, in reverse (that's how mp_execute_bytecode2 needs it)
+ // copy args to end of state array, in reverse (that's how mp_execute_bytecode needs it)
for (uint i = 0; i < n_args; i++) {
o->code_state.state[n_state - 1 - i] = args[i];
}
diff --git a/py/vm.c b/py/vm.c
index d57fbf17e3..c084d89114 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -41,12 +41,6 @@
#include "bc.h"
#include "objgenerator.h"
-// With these macros you can tune the maximum number of function state bytes
-// that will be allocated on the stack. Any function that needs more
-// than this will use the heap.
-#define VM_MAX_STATE_ON_STACK (10 * sizeof(machine_uint_t))
-
-#define DETECT_VM_STACK_OVERFLOW (0)
#if 0
#define TRACE(ip) mp_bytecode_print2(ip, 1);
#else
@@ -103,123 +97,13 @@ typedef enum {
currently_in_except_block = MP_TAGPTR_TAG(exc_sp->val_sp); /* restore previous state */ \
exc_sp--; /* pop back to previous exception handler */
-mp_vm_return_kind_t mp_execute_bytecode(const byte *code, const mp_obj_t *args, uint n_args,
- const mp_obj_t *args2, uint n_args2, mp_obj_t *ret) {
- const byte *ip = code;
-
- // get code info size, and skip line number table
- machine_uint_t code_info_size = ip[0] | (ip[1] << 8) | (ip[2] << 16) | (ip[3] << 24);
- ip += code_info_size;
-
- // bytecode prelude: state size and exception stack size; 16 bit uints
- machine_uint_t n_state = ip[0] | (ip[1] << 8);
- machine_uint_t n_exc_stack = ip[2] | (ip[3] << 8);
- ip += 4;
-
- // allocate state for locals and stack
-#if DETECT_VM_STACK_OVERFLOW
- n_state += 1;
-#endif
-
- int state_size = n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t);
- mp_code_state *code_state;
- if (state_size > VM_MAX_STATE_ON_STACK) {
- code_state = m_new_obj_var(mp_code_state, byte, state_size);
- } else {
- code_state = alloca(sizeof(mp_code_state) + state_size);
- }
-
- code_state->code_info = code;
- code_state->sp = &code_state->state[0] - 1;
- code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1;
- code_state->n_state = n_state;
-
- // init args
- for (uint i = 0; i < n_args; i++) {
- code_state->state[n_state - 1 - i] = args[i];
- }
- for (uint i = 0; i < n_args2; i++) {
- code_state->state[n_state - 1 - n_args - i] = args2[i];
- }
-
- // set rest of state to MP_OBJ_NULL
- for (uint i = 0; i < n_state - n_args - n_args2; i++) {
- code_state->state[i] = MP_OBJ_NULL;
- }
-
- // bytecode prelude: initialise closed over variables
- for (uint n_local = *ip++; n_local > 0; n_local--) {
- uint local_num = *ip++;
- code_state->state[n_state - 1 - local_num] = mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
- }
-
- code_state->ip = ip;
-
- // execute the byte code
- mp_vm_return_kind_t vm_return_kind = mp_execute_bytecode2(code_state, MP_OBJ_NULL);
-
-#if DETECT_VM_STACK_OVERFLOW
- if (vm_return_kind == MP_VM_RETURN_NORMAL) {
- if (code_state->sp < code_state->state) {
- printf("VM stack underflow: " INT_FMT "\n", code_state->sp - code_state->state);
- assert(0);
- }
- }
- // We can't check the case when an exception is returned in state[n_state - 1]
- // and there are no arguments, because in this case our detection slot may have
- // been overwritten by the returned exception (which is allowed).
- if (!(vm_return_kind == MP_VM_RETURN_EXCEPTION && n_args == 0 && n_args2 == 0)) {
- // Just check to see that we have at least 1 null object left in the state.
- bool overflow = true;
- for (uint i = 0; i < n_state - n_args - n_args2; i++) {
- if (code_state->state[i] == MP_OBJ_NULL) {
- overflow = false;
- break;
- }
- }
- if (overflow) {
- printf("VM stack overflow state=%p n_state+1=" UINT_FMT "\n", code_state->state, n_state);
- assert(0);
- }
- }
-#endif
-
- mp_vm_return_kind_t ret_kind;
- switch (vm_return_kind) {
- case MP_VM_RETURN_NORMAL:
- // return value is in *sp
- *ret = *code_state->sp;
- ret_kind = MP_VM_RETURN_NORMAL;
- break;
-
- case MP_VM_RETURN_EXCEPTION:
- // return value is in state[n_state - 1]
- *ret = code_state->state[n_state - 1];
- ret_kind = MP_VM_RETURN_EXCEPTION;
- break;
-
- case MP_VM_RETURN_YIELD: // byte-code shouldn't yield
- default:
- assert(0);
- *ret = mp_const_none;
- ret_kind = MP_VM_RETURN_NORMAL;
- break;
- }
-
- // free the state if it was allocated on the heap
- if (state_size > VM_MAX_STATE_ON_STACK) {
- m_del_var(mp_code_state, byte, state_size, code_state);
- }
- return ret_kind;
-}
-
// fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc)
// sp points to bottom of stack which grows up
// returns:
// MP_VM_RETURN_NORMAL, sp valid, return value in *sp
// MP_VM_RETURN_YIELD, ip, sp valid, yielded value in *sp
// MP_VM_RETURN_EXCEPTION, exception in fastn[0]
-mp_vm_return_kind_t mp_execute_bytecode2(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) {
#if MICROPY_OPT_COMPUTED_GOTO
#include "vmentrytable.h"
#define DISPATCH() do { \
@@ -241,7 +125,7 @@ mp_vm_return_kind_t mp_execute_bytecode2(mp_code_state *code_state, volatile mp_
// 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)
- // Pointers which are constant for particular invocation of mp_execute_bytecode2()
+ // 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);