py/emitnative: Make viper funcs run with their correct globals context.

Viper functions will now capture the globals at the point they were defined
and use these globals when executing.
This commit is contained in:
Damien George 2018-09-15 22:37:07 +10:00
parent f12e039c2b
commit 93d71c5436
4 changed files with 62 additions and 28 deletions

View File

@ -3287,7 +3287,12 @@ STATIC void scope_compute_things(scope_t *scope) {
#if MICROPY_EMIT_NATIVE
if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
// This function makes a reference to a global variable
scope->scope_flags |= MP_SCOPE_FLAG_REFGLOBALS;
if (scope->emit_options == MP_EMIT_OPT_VIPER
&& mp_native_type_from_qstr(id->qst) >= MP_NATIVE_TYPE_INT) {
// A casting operator in viper mode, not a real global reference
} else {
scope->scope_flags |= MP_SCOPE_FLAG_REFGLOBALS;
}
}
#endif
// params always count for 1 local, even if they are a cell

View File

@ -66,7 +66,8 @@
// locals (reversed, L0 at end) |
//
// C stack layout for viper functions:
// 0 = emit->stack_start: nlr_buf_t [optional] |
// 0 fun_obj, old_globals [optional]
// emit->stack_start: nlr_buf_t [optional] |
// Python object stack | emit->n_state
// locals (reversed, L0 at end) |
// (L0-L2 may be in regs instead)
@ -76,7 +77,7 @@
// Whether the native/viper function needs to be wrapped in an exception handler
#define NEED_GLOBAL_EXC_HANDLER(emit) ((emit)->scope->exc_stack_size > 0 \
|| (!(emit)->do_viper_types && ((emit)->scope->scope_flags & MP_SCOPE_FLAG_REFGLOBALS)))
|| ((emit)->scope->scope_flags & MP_SCOPE_FLAG_REFGLOBALS))
// Whether registers can be used to store locals (only true if there are no
// exception handlers, because otherwise an nlr_jump will restore registers to
@ -312,8 +313,13 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
}
}
// The locals and stack start at the beginning of the C stack
emit->stack_start = 0;
// Work out where the locals and Python stack start within the C stack
if (NEED_GLOBAL_EXC_HANDLER(emit)) {
// Reserve 2 words for function object and old globals
emit->stack_start = 2;
} else {
emit->stack_start = 0;
}
// Entry to function
ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs);
@ -325,6 +331,14 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
asm_arm_mov_reg_i32(emit->as, ASM_ARM_REG_R7, (mp_uint_t)mp_fun_table);
#endif
// Store function object (passed as first arg) to stack if needed
if (NEED_GLOBAL_EXC_HANDLER(emit)) {
#if N_X86
asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_1);
#endif
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1);
}
// Put n_args in REG_ARG_1, n_kw in REG_ARG_2, args array in REG_LOCAL_3
#if N_X86
asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_1);
@ -931,15 +945,13 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) {
mp_uint_t start_label = *emit->label_slot + 2;
mp_uint_t global_except_label = *emit->label_slot + 3;
if (!emit->do_viper_types) {
// Set new globals
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_FUN_OBJ(emit));
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t));
emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
// Set new globals
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_FUN_OBJ(emit));
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_ARG_1, REG_ARG_1, offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t));
emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
// Save old globals (or NULL if globals didn't change)
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_OLD_GLOBALS(emit), REG_RET);
}
// Save old globals (or NULL if globals didn't change)
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_OLD_GLOBALS(emit), REG_RET);
if (emit->scope->exc_stack_size == 0) {
// Optimisation: if globals didn't change don't push the nlr context
@ -976,11 +988,9 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) {
ASM_JUMP_IF_REG_NONZERO(emit->as, REG_LOCAL_1, nlr_label, false);
}
if (!emit->do_viper_types) {
// Restore old globals
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
}
// Restore old globals
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
// Re-raise exception out to caller
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit));
@ -996,19 +1006,17 @@ STATIC void emit_native_global_exc_exit(emit_t *emit) {
emit_native_label_assign(emit, emit->exit_label);
if (NEED_GLOBAL_EXC_HANDLER(emit)) {
if (!emit->do_viper_types) {
// Get old globals
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
// Get old globals
ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
if (emit->scope->exc_stack_size == 0) {
// Optimisation: if globals didn't change then don't restore them and don't do nlr_pop
ASM_JUMP_IF_REG_ZERO(emit->as, REG_ARG_1, emit->exit_label + 1, false);
}
// Restore old globals
emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
if (emit->scope->exc_stack_size == 0) {
// Optimisation: if globals didn't change then don't restore them and don't do nlr_pop
ASM_JUMP_IF_REG_ZERO(emit->as, REG_ARG_1, emit->exit_label + 1, false);
}
// Restore old globals
emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
// Pop the nlr context
emit_call(emit, MP_F_NLR_POP);
adjust_stack(emit, -(mp_int_t)(sizeof(nlr_buf_t) / sizeof(uintptr_t)));

View File

@ -0,0 +1,19 @@
# test that viper functions capture their globals context
gl = {}
exec("""
@micropython.viper
def f():
return x
""", gl)
# x is not yet in the globals, f should not see it
try:
print(gl['f']())
except NameError:
print('NameError')
# x is in globals, f should now see it
gl['x'] = 123
print(gl['f']())

View File

@ -0,0 +1,2 @@
NameError
123