summaryrefslogtreecommitdiff
path: root/py/vm.c
diff options
context:
space:
mode:
authorScott Shawcroft <scott@tannewt.org>2018-07-30 12:33:44 -0700
committerGitHub <noreply@github.com>2018-07-30 12:33:44 -0700
commita6d94b68453b8535398ff0b61cd6c4a0c7f77f8b (patch)
tree8defd6392975b8145dc11f0cce9c39a4e440b32a /py/vm.c
parent433a29bb70d51abb84c77a911ced43c6ff14706e (diff)
parentb1006170f1dcfa3a9499ef31c19c8f20736b136a (diff)
Merge pull request #1068 from dhalbert/micropython-25ae98f-merge
Micropython 25ae98f merge
Diffstat (limited to 'py/vm.c')
-rw-r--r--py/vm.c192
1 files changed, 109 insertions, 83 deletions
diff --git a/py/vm.c b/py/vm.c
index 564200037..498ecb491 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -48,14 +48,6 @@
// top element.
// Exception stack also grows up, top element is also pointed at.
-// Exception stack unwind reasons (WHY_* in CPython-speak)
-// TODO perhaps compress this to RETURN=0, JUMP>0, with number of unwinds
-// left to do encoded in the JUMP number
-typedef enum {
- UNWIND_RETURN = 1,
- UNWIND_JUMP,
-} mp_unwind_reason_t;
-
#define DECODE_UINT \
mp_uint_t unum = 0; \
do { \
@@ -144,7 +136,7 @@ mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp
#define ENTRY(op) entry_##op
#define ENTRY_DEFAULT entry_default
#else
- #define DISPATCH() break
+ #define DISPATCH() goto dispatch_loop
#define DISPATCH_WITH_PEND_EXC_CHECK() goto pending_exception_check
#define ENTRY(op) case op
#define ENTRY_DEFAULT default
@@ -344,7 +336,7 @@ dispatch_loop:
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_obj_t top = TOP();
- if (mp_obj_get_type(top)->attr == mp_obj_instance_attr) {
+ if (mp_obj_is_instance_type(mp_obj_get_type(top))) {
mp_obj_instance_t *self = MP_OBJ_TO_PTR(top);
mp_uint_t x = *ip;
mp_obj_t key = MP_OBJ_NEW_QSTR(qst);
@@ -442,7 +434,7 @@ dispatch_loop:
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
mp_obj_t top = TOP();
- if (mp_obj_get_type(top)->attr == mp_obj_instance_attr && sp[-1] != MP_OBJ_NULL) {
+ if (mp_obj_is_instance_type(mp_obj_get_type(top)) && sp[-1] != MP_OBJ_NULL) {
mp_obj_instance_t *self = MP_OBJ_TO_PTR(top);
mp_uint_t x = *ip;
mp_obj_t key = MP_OBJ_NEW_QSTR(qst);
@@ -613,29 +605,18 @@ dispatch_loop:
mp_call_method_n_kw(3, 0, sp);
SET_TOP(mp_const_none);
} else if (MP_OBJ_IS_SMALL_INT(TOP())) {
- mp_int_t cause_val = MP_OBJ_SMALL_INT_VALUE(TOP());
- if (cause_val == UNWIND_RETURN) {
- // stack: (..., __exit__, ctx_mgr, ret_val, UNWIND_RETURN)
- mp_obj_t ret_val = sp[-1];
- sp[-1] = mp_const_none;
- sp[0] = mp_const_none;
- sp[1] = mp_const_none;
- mp_call_method_n_kw(3, 0, sp - 3);
- sp[-3] = ret_val;
- sp[-2] = MP_OBJ_NEW_SMALL_INT(UNWIND_RETURN);
- } else {
- assert(cause_val == UNWIND_JUMP);
- // stack: (..., __exit__, ctx_mgr, dest_ip, num_exc, UNWIND_JUMP)
- mp_obj_t dest_ip = sp[-2];
- mp_obj_t num_exc = sp[-1];
- sp[-2] = mp_const_none;
- sp[-1] = mp_const_none;
- sp[0] = mp_const_none;
- mp_call_method_n_kw(3, 0, sp - 4);
- sp[-4] = dest_ip;
- sp[-3] = num_exc;
- sp[-2] = MP_OBJ_NEW_SMALL_INT(UNWIND_JUMP);
- }
+ // Getting here there are two distinct cases:
+ // - unwind return, stack: (..., __exit__, ctx_mgr, ret_val, SMALL_INT(-1))
+ // - unwind jump, stack: (..., __exit__, ctx_mgr, dest_ip, SMALL_INT(num_exc))
+ // For both cases we do exactly the same thing.
+ mp_obj_t data = sp[-1];
+ mp_obj_t cause = sp[0];
+ sp[-1] = mp_const_none;
+ sp[0] = mp_const_none;
+ sp[1] = mp_const_none;
+ mp_call_method_n_kw(3, 0, sp - 3);
+ sp[-3] = data;
+ sp[-2] = cause;
sp -= 2; // we removed (__exit__, ctx_mgr)
} else {
assert(mp_obj_is_exception_instance(TOP()));
@@ -680,10 +661,11 @@ unwind_jump:;
// of a "with" block contains the context manager info.
// We're going to run "finally" code as a coroutine
// (not calling it recursively). Set up a sentinel
- // on a stack so it can return back to us when it is
+ // on the stack so it can return back to us when it is
// done (when WITH_CLEANUP or END_FINALLY reached).
- PUSH((mp_obj_t)unum); // push number of exception handlers left to unwind
- PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_JUMP)); // push sentinel
+ // The sentinel is the number of exception handlers left to
+ // unwind, which is a non-negative integer.
+ PUSH(MP_OBJ_NEW_SMALL_INT(unum));
ip = exc_sp->handler; // get exception handler byte code address
exc_sp--; // pop exception handler
goto dispatch_loop; // run the exception handler
@@ -720,11 +702,14 @@ unwind_jump:;
} else if (MP_OBJ_IS_SMALL_INT(TOP())) {
// We finished "finally" coroutine and now dispatch back
// to our caller, based on TOS value
- mp_unwind_reason_t reason = MP_OBJ_SMALL_INT_VALUE(POP());
- if (reason == UNWIND_RETURN) {
+ mp_int_t cause = MP_OBJ_SMALL_INT_VALUE(POP());
+ if (cause < 0) {
+ // A negative cause indicates unwind return
goto unwind_return;
} else {
- assert(reason == UNWIND_JUMP);
+ // Otherwise it's an unwind jump and we must push as a raw
+ // number the number of exception handlers to unwind
+ PUSH((mp_obj_t)cause);
goto unwind_jump;
}
} else {
@@ -926,18 +911,22 @@ unwind_jump:;
code_state->sp = sp;
code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block);
mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1);
- if (new_state) {
+ #if !MICROPY_ENABLE_PYSTACK
+ if (new_state == NULL) {
+ // Couldn't allocate codestate on heap: in the strict case raise
+ // an exception, otherwise just fall through to stack allocation.
+ #if MICROPY_STACKLESS_STRICT
+ deep_recursion_error:
+ mp_raise_recursion_depth();
+ #endif
+ } else
+ #endif
+ {
new_state->prev = code_state;
code_state = new_state;
nlr_pop();
goto run_code_state;
}
- #if MICROPY_STACKLESS_STRICT
- else {
- deep_recursion_error:
- mp_exc_recursion_depth();
- }
- #endif
}
#endif
SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1));
@@ -963,18 +952,26 @@ unwind_jump:;
mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun,
out_args.n_args, out_args.n_kw, out_args.args);
- m_del(mp_obj_t, out_args.args, out_args.n_alloc);
- if (new_state) {
+ #if !MICROPY_ENABLE_PYSTACK
+ // Freeing args at this point does not follow a LIFO order so only do it if
+ // pystack is not enabled. For pystack, they are freed when code_state is.
+ mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t));
+ #endif
+ #if !MICROPY_ENABLE_PYSTACK
+ if (new_state == NULL) {
+ // Couldn't allocate codestate on heap: in the strict case raise
+ // an exception, otherwise just fall through to stack allocation.
+ #if MICROPY_STACKLESS_STRICT
+ goto deep_recursion_error;
+ #endif
+ } else
+ #endif
+ {
new_state->prev = code_state;
code_state = new_state;
nlr_pop();
goto run_code_state;
}
- #if MICROPY_STACKLESS_STRICT
- else {
- goto deep_recursion_error;
- }
- #endif
}
#endif
SET_TOP(mp_call_method_n_kw_var(false, unum, sp));
@@ -998,17 +995,21 @@ unwind_jump:;
int adjust = (sp[1] == MP_OBJ_NULL) ? 0 : 1;
mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, n_args + adjust, n_kw, sp + 2 - adjust);
- if (new_state) {
+ #if !MICROPY_ENABLE_PYSTACK
+ if (new_state == NULL) {
+ // Couldn't allocate codestate on heap: in the strict case raise
+ // an exception, otherwise just fall through to stack allocation.
+ #if MICROPY_STACKLESS_STRICT
+ goto deep_recursion_error;
+ #endif
+ } else
+ #endif
+ {
new_state->prev = code_state;
code_state = new_state;
nlr_pop();
goto run_code_state;
}
- #if MICROPY_STACKLESS_STRICT
- else {
- goto deep_recursion_error;
- }
- #endif
}
#endif
SET_TOP(mp_call_method_n_kw(unum & 0xff, (unum >> 8) & 0xff, sp));
@@ -1034,18 +1035,26 @@ unwind_jump:;
mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(out_args.fun,
out_args.n_args, out_args.n_kw, out_args.args);
- m_del(mp_obj_t, out_args.args, out_args.n_alloc);
- if (new_state) {
+ #if !MICROPY_ENABLE_PYSTACK
+ // Freeing args at this point does not follow a LIFO order so only do it if
+ // pystack is not enabled. For pystack, they are freed when code_state is.
+ mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t));
+ #endif
+ #if !MICROPY_ENABLE_PYSTACK
+ if (new_state == NULL) {
+ // Couldn't allocate codestate on heap: in the strict case raise
+ // an exception, otherwise just fall through to stack allocation.
+ #if MICROPY_STACKLESS_STRICT
+ goto deep_recursion_error;
+ #endif
+ } else
+ #endif
+ {
new_state->prev = code_state;
code_state = new_state;
nlr_pop();
goto run_code_state;
}
- #if MICROPY_STACKLESS_STRICT
- else {
- goto deep_recursion_error;
- }
- #endif
}
#endif
SET_TOP(mp_call_method_n_kw_var(true, unum, sp));
@@ -1081,7 +1090,7 @@ unwind_return:
// (not calling it recursively). Set up a sentinel
// on a stack so it can return back to us when it is
// done (when WITH_CLEANUP or END_FINALLY reached).
- PUSH(MP_OBJ_NEW_SMALL_INT(UNWIND_RETURN));
+ PUSH(MP_OBJ_NEW_SMALL_INT(-1));
ip = exc_sp->handler;
exc_sp--;
goto dispatch_loop;
@@ -1096,7 +1105,15 @@ unwind_return:
if (code_state->prev != NULL) {
mp_obj_t res = *sp;
mp_globals_set(code_state->old_globals);
- code_state = code_state->prev;
+ mp_code_state_t *new_code_state = code_state->prev;
+ #if MICROPY_ENABLE_PYSTACK
+ // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var
+ // (The latter is implicitly freed when using pystack due to its LIFO nature.)
+ // The sizeof in the following statement does not include the size of the variable
+ // part of the struct. This arg is anyway not used if pystack is enabled.
+ mp_nonlocal_free(code_state, sizeof(mp_code_state_t));
+ #endif
+ code_state = new_code_state;
*code_state->sp = res;
goto run_code_state;
}
@@ -1105,7 +1122,7 @@ unwind_return:
ENTRY(MP_BC_RAISE_VARARGS): {
MARK_EXC_IP_SELECTIVE();
- mp_uint_t unum = *ip++;
+ mp_uint_t unum = *ip;
mp_obj_t obj;
if (unum == 2) {
mp_warning("exception chaining not supported");
@@ -1126,7 +1143,7 @@ unwind_return:
RAISE(obj);
}
} else {
- obj = POP();
+ obj = TOP();
}
obj = mp_make_raise_obj(obj);
RAISE(obj);
@@ -1149,6 +1166,7 @@ yield:
mp_obj_t send_value = POP();
mp_obj_t t_exc = MP_OBJ_NULL;
mp_obj_t ret_value;
+ code_state->sp = sp; // Save sp because it's needed if mp_resume raises StopIteration
if (inject_exc != MP_OBJ_NULL) {
t_exc = inject_exc;
inject_exc = MP_OBJ_NULL;
@@ -1164,8 +1182,7 @@ yield:
} else if (ret_kind == MP_VM_RETURN_NORMAL) {
// Pop exhausted gen
sp--;
- // TODO: When ret_value can be MP_OBJ_NULL here??
- if (ret_value == MP_OBJ_NULL || ret_value == MP_OBJ_STOP_ITERATION) {
+ if (ret_value == MP_OBJ_STOP_ITERATION) {
// Optimize StopIteration
// TODO: get StopIteration's value
PUSH(mp_const_none);
@@ -1253,10 +1270,10 @@ yield:
} else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) {
fastn[MP_BC_STORE_FAST_MULTI - (mp_int_t)ip[-1]] = POP();
DISPATCH();
- } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 7) {
+ } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) {
SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP()));
DISPATCH();
- } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 36) {
+ } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) {
mp_obj_t rhs = POP();
mp_obj_t lhs = TOP();
SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs));
@@ -1305,11 +1322,12 @@ pending_exception_check:
#if MICROPY_PY_THREAD_GIL
#if MICROPY_PY_THREAD_GIL_VM_DIVISOR
- if (--gil_divisor == 0) {
- gil_divisor = MICROPY_PY_THREAD_GIL_VM_DIVISOR;
- #else
- {
+ if (--gil_divisor == 0)
#endif
+ {
+ #if MICROPY_PY_THREAD_GIL_VM_DIVISOR
+ gil_divisor = MICROPY_PY_THREAD_GIL_VM_DIVISOR;
+ #endif
#if MICROPY_ENABLE_SCHEDULER
// can only switch threads if the scheduler is unlocked
if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE)
@@ -1348,7 +1366,8 @@ exception_handler:
} else if (*code_state->ip == MP_BC_YIELD_FROM) {
// StopIteration inside yield from call means return a value of
// yield from, so inject exception's value as yield from's result
- *++code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val));
+ // (Instead of stack pop then push we just replace exhausted gen with value)
+ *code_state->sp = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val));
code_state->ip++; // yield from is over, move to next instruction
goto outer_dispatch_loop; // continue with dispatch loop
}
@@ -1361,8 +1380,7 @@ unwind_loop:
// 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.
- // TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj and MemoryError_obj)
- if (nlr.ret_val != &mp_const_GeneratorExit_obj && nlr.ret_val != &mp_const_MemoryError_obj) {
+ if (nlr.ret_val != &mp_const_GeneratorExit_obj) {
const byte *ip = code_state->fun_bc->bytecode;
ip = mp_decode_uint_skip(ip); // skip n_state
ip = mp_decode_uint_skip(ip); // skip n_exc_stack
@@ -1438,7 +1456,15 @@ unwind_loop:
#if MICROPY_STACKLESS
} else if (code_state->prev != NULL) {
mp_globals_set(code_state->old_globals);
- code_state = code_state->prev;
+ mp_code_state_t *new_code_state = code_state->prev;
+ #if MICROPY_ENABLE_PYSTACK
+ // Free code_state, and args allocated by mp_call_prepare_args_n_kw_var
+ // (The latter is implicitly freed when using pystack due to its LIFO nature.)
+ // The sizeof in the following statement does not include the size of the variable
+ // part of the struct. This arg is anyway not used if pystack is enabled.
+ mp_nonlocal_free(code_state, sizeof(mp_code_state_t));
+ #endif
+ code_state = new_code_state;
size_t n_state = mp_decode_uint_value(code_state->fun_bc->bytecode);
fastn = &code_state->state[n_state - 1];
exc_stack = (mp_exc_stack_t*)(code_state->state + n_state);