aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George2018-09-15 22:37:07 +1000
committerDamien George2018-09-15 22:39:27 +1000
commit93d71c5436488d52d47d165dd020217415e79a64 (patch)
treef00d9f11c16d8ea52d3574603a834bf912cddfab
parentf12e039c2bb805364f55b1fb81b92c1b03c9104a (diff)
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.
-rw-r--r--py/compile.c7
-rw-r--r--py/emitnative.c62
-rw-r--r--tests/micropython/viper_globals.py19
-rw-r--r--tests/micropython/viper_globals.py.exp2
4 files changed, 62 insertions, 28 deletions
diff --git a/py/compile.c b/py/compile.c
index ec6b463c0..5748256f2 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 f791978df..4445aeaab 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));
-
- 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);
- }
+ // Get old globals
+ ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
- // 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 000000000..9c68dc3da
--- /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 000000000..5731b89c1
--- /dev/null
+++ b/tests/micropython/viper_globals.py.exp
@@ -0,0 +1,2 @@
+NameError
+123