diff --git a/py/compile.c b/py/compile.c index ec6b463c05..5748256f24 100644 --- a/py/compile.c +++ b/py/compile.c @@ -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 diff --git a/py/emitnative.c b/py/emitnative.c index f791978dfb..4445aeaabd 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -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))); diff --git a/tests/micropython/viper_globals.py b/tests/micropython/viper_globals.py new file mode 100644 index 0000000000..9c68dc3da8 --- /dev/null +++ b/tests/micropython/viper_globals.py @@ -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']()) diff --git a/tests/micropython/viper_globals.py.exp b/tests/micropython/viper_globals.py.exp new file mode 100644 index 0000000000..5731b89c1b --- /dev/null +++ b/tests/micropython/viper_globals.py.exp @@ -0,0 +1,2 @@ +NameError +123