summaryrefslogtreecommitdiffstatshomepage
path: root/py
diff options
context:
space:
mode:
Diffstat (limited to 'py')
-rw-r--r--py/compile.c4
-rw-r--r--py/emitglue.c4
-rw-r--r--py/emitnative.c345
-rw-r--r--py/emitnx86.c1
-rw-r--r--py/nativeglue.c39
-rw-r--r--py/obj.h1
-rw-r--r--py/objfun.c2
-rw-r--r--py/objgenerator.c64
-rw-r--r--py/runtime0.h1
9 files changed, 377 insertions, 84 deletions
diff --git a/py/compile.c b/py/compile.c
index a7039be115..e90b366e0e 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -1703,6 +1703,7 @@ STATIC void compile_yield_from(compiler_t *comp) {
EMIT_ARG(get_iter, false);
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
EMIT_ARG(yield, MP_EMIT_YIELD_FROM);
+ reserve_labels_for_native(comp, 3);
}
#if MICROPY_PY_ASYNC_AWAIT
@@ -2634,6 +2635,7 @@ STATIC void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
if (MP_PARSE_NODE_IS_NULL(pns->nodes[0])) {
EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
EMIT_ARG(yield, MP_EMIT_YIELD_VALUE);
+ reserve_labels_for_native(comp, 1);
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) {
pns = (mp_parse_node_struct_t*)pns->nodes[0];
compile_node(comp, pns->nodes[0]);
@@ -2641,6 +2643,7 @@ STATIC void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
} else {
compile_node(comp, pns->nodes[0]);
EMIT_ARG(yield, MP_EMIT_YIELD_VALUE);
+ reserve_labels_for_native(comp, 1);
}
}
@@ -2873,6 +2876,7 @@ STATIC void compile_scope_comp_iter(compiler_t *comp, mp_parse_node_struct_t *pn
compile_node(comp, pn_inner_expr);
if (comp->scope_cur->kind == SCOPE_GEN_EXPR) {
EMIT_ARG(yield, MP_EMIT_YIELD_VALUE);
+ reserve_labels_for_native(comp, 1);
EMIT(pop_top);
} else {
EMIT_ARG(store_comp, comp->scope_cur->kind, 4 * for_depth + 5);
diff --git a/py/emitglue.c b/py/emitglue.c
index f99631450b..064a838007 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -136,6 +136,10 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar
case MP_CODE_NATIVE_PY:
case MP_CODE_NATIVE_VIPER:
fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data, rc->data.u_native.const_table);
+ // Check for a generator function, and if so change the type of the object
+ if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
+ ((mp_obj_base_t*)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap;
+ }
break;
#endif
#if MICROPY_EMIT_INLINE_ASM
diff --git a/py/emitnative.c b/py/emitnative.c
index 20ad7a1d8d..828541fbb8 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -65,6 +65,14 @@
// emit->stack_start: Python object stack | emit->n_state
// locals (reversed, L0 at end) |
//
+// C stack layout for native generator functions:
+// 0=emit->stack_start: nlr_buf_t
+//
+// Then REG_GENERATOR_STATE points to:
+// 0=emit->code_state_start: mp_code_state_t
+// emit->stack_start: Python object stack | emit->n_state
+// locals (reversed, L0 at end) |
+//
// C stack layout for viper functions:
// 0: nlr_buf_t [optional]
// emit->code_state_start: fun_obj, old_globals [optional]
@@ -81,12 +89,12 @@
// 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)->scope->scope_flags & MP_SCOPE_FLAG_REFGLOBALS))
+ || ((emit)->scope->scope_flags & (MP_SCOPE_FLAG_GENERATOR | 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
// their state at the start of the function and updates to locals will be lost)
-#define CAN_USE_REGS_FOR_LOCALS(emit) ((emit)->scope->exc_stack_size == 0)
+#define CAN_USE_REGS_FOR_LOCALS(emit) ((emit)->scope->exc_stack_size == 0 && !(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR))
// Indices within the local C stack for various variables
#define LOCAL_IDX_EXC_VAL(emit) (NLR_BUF_IDX_RET_VAL)
@@ -95,18 +103,14 @@
#define LOCAL_IDX_RET_VAL(emit) (NLR_BUF_IDX_LOCAL_3)
#define LOCAL_IDX_FUN_OBJ(emit) ((emit)->code_state_start + offsetof(mp_code_state_t, fun_bc) / sizeof(uintptr_t))
#define LOCAL_IDX_OLD_GLOBALS(emit) ((emit)->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t))
+#define LOCAL_IDX_GEN_PC(emit) ((emit)->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t))
#define LOCAL_IDX_LOCAL_VAR(emit, local_num) ((emit)->stack_start + (emit)->n_state - 1 - (local_num))
+#define REG_GENERATOR_STATE (REG_LOCAL_3)
+
// number of arguments to viper functions are limited to this value
#define REG_ARG_NUM (4)
-// define additional generic helper macros
-#define ASM_MOV_LOCAL_IMM_VIA(as, local_num, imm, reg_temp) \
- do { \
- ASM_MOV_REG_IMM((as), (reg_temp), (imm)); \
- ASM_MOV_LOCAL_REG((as), (local_num), (reg_temp)); \
- } while (false)
-
#define EMIT_NATIVE_VIPER_TYPE_ERROR(emit, ...) do { \
*emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \
} while (0)
@@ -202,6 +206,7 @@ struct _emit_t {
exc_stack_entry_t *exc_stack;
int prelude_offset;
+ int start_offset;
int n_state;
uint16_t code_state_start;
uint16_t stack_start;
@@ -252,6 +257,37 @@ STATIC void emit_call_with_imm_arg(emit_t *emit, mp_fun_kind_t fun_kind, mp_int_
STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num);
STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num);
+STATIC void emit_native_mov_state_reg(emit_t *emit, int local_num, int reg_src) {
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ ASM_STORE_REG_REG_OFFSET(emit->as, reg_src, REG_GENERATOR_STATE, local_num);
+ } else {
+ ASM_MOV_LOCAL_REG(emit->as, local_num, reg_src);
+ }
+}
+
+STATIC void emit_native_mov_reg_state(emit_t *emit, int reg_dest, int local_num) {
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ ASM_LOAD_REG_REG_OFFSET(emit->as, reg_dest, REG_GENERATOR_STATE, local_num);
+ } else {
+ ASM_MOV_REG_LOCAL(emit->as, reg_dest, local_num);
+ }
+}
+
+STATIC void emit_native_mov_reg_state_addr(emit_t *emit, int reg_dest, int local_num) {
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ ASM_MOV_REG_IMM(emit->as, reg_dest, local_num * ASM_WORD_SIZE);
+ ASM_ADD_REG_REG(emit->as, reg_dest, REG_GENERATOR_STATE);
+ } else {
+ ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, local_num);
+ }
+}
+
+#define emit_native_mov_state_imm_via(emit, local_num, imm, reg_temp) \
+ do { \
+ ASM_MOV_REG_IMM((emit)->as, (reg_temp), (imm)); \
+ emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \
+ } while (false)
+
STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
DEBUG_printf("start_pass(pass=%u, scope=%p)\n", pass, scope);
@@ -392,7 +428,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
if (i < REG_LOCAL_NUM && CAN_USE_REGS_FOR_LOCALS(emit) && (i != 2 || emit->scope->num_pos_args == 3)) {
ASM_MOV_REG_REG(emit->as, reg_local_table[i], r);
} else {
- ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_LOCAL_VAR(emit, i), r);
+ emit_native_mov_state_reg(emit, LOCAL_IDX_LOCAL_VAR(emit, i), r);
}
}
// Get 3rd local from the stack back into REG_LOCAL_3 if this reg couldn't be written to above
@@ -406,11 +442,32 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
// work out size of state (locals plus stack)
emit->n_state = scope->num_locals + scope->stack_size;
- // the locals and stack start after the code_state structure
- emit->stack_start = emit->code_state_start + sizeof(mp_code_state_t) / sizeof(mp_uint_t);
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ emit->code_state_start = 0;
+ emit->stack_start = sizeof(mp_code_state_t) / sizeof(mp_uint_t);
+ mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->prelude_offset);
+ mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->start_offset);
+ ASM_ENTRY(emit->as, sizeof(nlr_buf_t) / sizeof(uintptr_t));
+
+ // Put address of code_state into REG_GENERATOR_STATE
+ #if N_X86
+ asm_x86_mov_arg_to_r32(emit->as, 0, REG_GENERATOR_STATE);
+ #else
+ ASM_MOV_REG_REG(emit->as, REG_GENERATOR_STATE, REG_ARG_1);
+ #endif
+
+ // Put throw value into LOCAL_IDX_EXC_VAL slot, for yield/yield-from
+ #if N_X86
+ asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_2);
+ #endif
+ ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_ARG_2);
+ } else {
+ // The locals and stack start after the code_state structure
+ emit->stack_start = emit->code_state_start + sizeof(mp_code_state_t) / sizeof(mp_uint_t);
- // allocate space on C-stack for code_state structure, which includes state
- ASM_ENTRY(emit->as, emit->stack_start + emit->n_state);
+ // Allocate space on C-stack for code_state structure, which includes state
+ ASM_ENTRY(emit->as, emit->stack_start + emit->n_state);
+ }
// TODO don't load r7 if we don't need it
#if N_THUMB
@@ -421,33 +478,35 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
ASM_MOV_REG_IMM(emit->as, ASM_XTENSA_REG_A15, (uint32_t)mp_fun_table);
#endif
- // prepare incoming arguments for call to mp_setup_code_state
+ if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) {
+ // Prepare incoming arguments for call to mp_setup_code_state
- #if N_X86
- asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_1);
- asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_2);
- asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_3);
- asm_x86_mov_arg_to_r32(emit->as, 3, REG_ARG_4);
- #endif
+ #if N_X86
+ asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_1);
+ asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_2);
+ asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_3);
+ asm_x86_mov_arg_to_r32(emit->as, 3, REG_ARG_4);
+ #endif
- // set code_state.fun_bc
- ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1);
+ // Set code_state.fun_bc
+ ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1);
- // set code_state.ip (offset from start of this function to prelude info)
- // XXX this encoding may change size
- ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), emit->prelude_offset, REG_ARG_1);
+ // Set code_state.ip (offset from start of this function to prelude info)
+ // TODO this encoding may change size in the final pass, need to make it fixed
+ emit_native_mov_state_imm_via(emit, emit->code_state_start + offsetof(mp_code_state_t, ip) / sizeof(uintptr_t), emit->prelude_offset, REG_ARG_1);
- // put address of code_state into first arg
- ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, emit->code_state_start);
+ // Put address of code_state into first arg
+ ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, emit->code_state_start);
- // call mp_setup_code_state to prepare code_state structure
- #if N_THUMB
- asm_thumb_bl_ind(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE, ASM_THUMB_REG_R4);
- #elif N_ARM
- asm_arm_bl_ind(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE, ASM_ARM_REG_R4);
- #else
- ASM_CALL_IND(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE);
- #endif
+ // Call mp_setup_code_state to prepare code_state structure
+ #if N_THUMB
+ asm_thumb_bl_ind(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE, ASM_THUMB_REG_R4);
+ #elif N_ARM
+ asm_arm_bl_ind(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE, ASM_ARM_REG_R4);
+ #else
+ ASM_CALL_IND(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE);
+ #endif
+ }
emit_native_global_exc_entry(emit);
@@ -631,7 +690,7 @@ STATIC void need_reg_single(emit_t *emit, int reg_needed, int skip_stack_pos) {
stack_info_t *si = &emit->stack_info[i];
if (si->kind == STACK_REG && si->data.u_reg == reg_needed) {
si->kind = STACK_VALUE;
- ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg);
+ emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg);
}
}
}
@@ -642,7 +701,7 @@ STATIC void need_reg_all(emit_t *emit) {
stack_info_t *si = &emit->stack_info[i];
if (si->kind == STACK_REG) {
si->kind = STACK_VALUE;
- ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg);
+ emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg);
}
}
}
@@ -654,7 +713,7 @@ STATIC void need_stack_settled(emit_t *emit) {
if (si->kind == STACK_REG) {
DEBUG_printf(" reg(%u) to local(%u)\n", si->data.u_reg, emit->stack_start + i);
si->kind = STACK_VALUE;
- ASM_MOV_LOCAL_REG(emit->as, emit->stack_start + i, si->data.u_reg);
+ emit_native_mov_state_reg(emit, emit->stack_start + i, si->data.u_reg);
}
}
for (int i = 0; i < emit->stack_size; i++) {
@@ -662,7 +721,7 @@ STATIC void need_stack_settled(emit_t *emit) {
if (si->kind == STACK_IMM) {
DEBUG_printf(" imm(" INT_FMT ") to local(%u)\n", si->data.u_imm, emit->stack_start + i);
si->kind = STACK_VALUE;
- ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + i, si->data.u_imm, REG_TEMP0);
+ emit_native_mov_state_imm_via(emit, emit->stack_start + i, si->data.u_imm, REG_TEMP0);
}
}
}
@@ -674,7 +733,7 @@ STATIC void emit_access_stack(emit_t *emit, int pos, vtype_kind_t *vtype, int re
*vtype = si->vtype;
switch (si->kind) {
case STACK_VALUE:
- ASM_MOV_REG_LOCAL(emit->as, reg_dest, emit->stack_start + emit->stack_size - pos);
+ emit_native_mov_reg_state(emit, reg_dest, emit->stack_start + emit->stack_size - pos);
break;
case STACK_REG:
@@ -696,7 +755,7 @@ STATIC void emit_fold_stack_top(emit_t *emit, int reg_dest) {
si[0] = si[1];
if (si->kind == STACK_VALUE) {
// if folded element was on the stack we need to put it in a register
- ASM_MOV_REG_LOCAL(emit->as, reg_dest, emit->stack_start + emit->stack_size - 1);
+ emit_native_mov_reg_state(emit, reg_dest, emit->stack_start + emit->stack_size - 1);
si->kind = STACK_REG;
si->data.u_reg = reg_dest;
}
@@ -819,19 +878,19 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de
si->kind = STACK_VALUE;
switch (si->vtype) {
case VTYPE_PYOBJ:
- ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, si->data.u_imm, reg_dest);
+ emit_native_mov_state_imm_via(emit, emit->stack_start + emit->stack_size - 1 - i, si->data.u_imm, reg_dest);
break;
case VTYPE_BOOL:
if (si->data.u_imm == 0) {
- ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_false, reg_dest);
+ emit_native_mov_state_imm_via(emit, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_false, reg_dest);
} else {
- ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_true, reg_dest);
+ emit_native_mov_state_imm_via(emit, emit->stack_start + emit->stack_size - 1 - i, (mp_uint_t)mp_const_true, reg_dest);
}
si->vtype = VTYPE_PYOBJ;
break;
case VTYPE_INT:
case VTYPE_UINT:
- ASM_MOV_LOCAL_IMM_VIA(emit->as, emit->stack_start + emit->stack_size - 1 - i, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm), reg_dest);
+ emit_native_mov_state_imm_via(emit, emit->stack_start + emit->stack_size - 1 - i, (uintptr_t)MP_OBJ_NEW_SMALL_INT(si->data.u_imm), reg_dest);
si->vtype = VTYPE_PYOBJ;
break;
default:
@@ -849,9 +908,9 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de
stack_info_t *si = &emit->stack_info[emit->stack_size - 1 - i];
if (si->vtype != VTYPE_PYOBJ) {
mp_uint_t local_num = emit->stack_start + emit->stack_size - 1 - i;
- ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, local_num);
+ emit_native_mov_reg_state(emit, REG_ARG_1, local_num);
emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, si->vtype, REG_ARG_2); // arg2 = type
- ASM_MOV_LOCAL_REG(emit->as, local_num, REG_RET);
+ emit_native_mov_state_reg(emit, local_num, REG_RET);
si->vtype = VTYPE_PYOBJ;
DEBUG_printf(" convert_native_to_obj(local_num=" UINT_FMT ")\n", local_num);
}
@@ -859,7 +918,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_pop(emit_t *emit, mp_uint_t reg_de
// Adujust the stack for a pop of n_pop items, and load the stack pointer into reg_dest.
adjust_stack(emit, -n_pop);
- ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, emit->stack_start + emit->stack_size);
+ emit_native_mov_reg_state_addr(emit, reg_dest, emit->stack_start + emit->stack_size);
}
// vtype of all n_push objects is VTYPE_PYOBJ
@@ -870,7 +929,7 @@ STATIC void emit_get_stack_pointer_to_reg_for_push(emit_t *emit, mp_uint_t reg_d
emit->stack_info[emit->stack_size + i].kind = STACK_VALUE;
emit->stack_info[emit->stack_size + i].vtype = VTYPE_PYOBJ;
}
- ASM_MOV_REG_LOCAL_ADDR(emit->as, reg_dest, emit->stack_start + emit->stack_size);
+ emit_native_mov_reg_state_addr(emit, reg_dest, emit->stack_start + emit->stack_size);
adjust_stack(emit, n_push);
}
@@ -932,7 +991,7 @@ STATIC void emit_load_reg_with_ptr(emit_t *emit, int reg, mp_uint_t ptr, size_t
if (emit->pass == MP_PASS_EMIT) {
emit->const_table[table_off] = ptr;
}
- ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit));
+ emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_FUN_OBJ(emit));
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_TEMP0, offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t));
ASM_LOAD_REG_REG_OFFSET(emit->as, reg, REG_TEMP0, table_off);
}
@@ -985,17 +1044,21 @@ 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;
- // 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);
+ if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) {
+ // Set new globals
+ emit_native_mov_reg_state(emit, 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)
+ emit_native_mov_state_reg(emit, 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
- ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, start_label, false);
+ if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) {
+ // Optimisation: if globals didn't change don't push the nlr context
+ ASM_JUMP_IF_REG_ZERO(emit->as, REG_RET, start_label, false);
+ }
// Wrap everything in an nlr context
ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, 0);
@@ -1028,16 +1091,41 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) {
ASM_JUMP_IF_REG_NONZERO(emit->as, REG_LOCAL_1, nlr_label, false);
}
- // 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);
+ if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) {
+ // Restore old globals
+ emit_native_mov_reg_state(emit, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
+ emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
+ }
+
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ // Store return value in state[0]
+ ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, LOCAL_IDX_EXC_VAL(emit));
+ ASM_STORE_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, offsetof(mp_code_state_t, state) / sizeof(uintptr_t));
- // Re-raise exception out to caller
- ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit));
- emit_call(emit, MP_F_NATIVE_RAISE);
+ // Load return kind
+ ASM_MOV_REG_IMM(emit->as, REG_RET, MP_VM_RETURN_EXCEPTION);
+
+ ASM_EXIT(emit->as);
+ } else {
+ // Re-raise exception out to caller
+ ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit));
+ emit_call(emit, MP_F_NATIVE_RAISE);
+ }
// Label for start of function
emit_native_label_assign(emit, start_label);
+
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_GEN_PC(emit));
+ ASM_JUMP_REG(emit->as, REG_TEMP0);
+ emit->start_offset = mp_asm_base_get_code_pos(&emit->as->base);
+
+ // This is the first entry of the generator
+
+ // Check LOCAL_IDX_EXC_VAL for any injected value
+ ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit));
+ emit_call(emit, MP_F_NATIVE_RAISE);
+ }
}
}
@@ -1047,22 +1135,26 @@ STATIC void emit_native_global_exc_exit(emit_t *emit) {
if (NEED_GLOBAL_EXC_HANDLER(emit)) {
// Get old globals
- ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_OLD_GLOBALS(emit));
+ if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) {
+ emit_native_mov_reg_state(emit, 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);
- }
+ 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);
+ // Restore old globals
+ emit_call(emit, MP_F_NATIVE_SWAP_GLOBALS);
+ }
// Pop the nlr context
emit_call(emit, MP_F_NLR_POP);
- if (emit->scope->exc_stack_size == 0) {
- // Destination label for above optimisation
- emit_native_label_assign(emit, emit->exit_label + 1);
+ if (!(emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR)) {
+ if (emit->scope->exc_stack_size == 0) {
+ // Destination label for above optimisation
+ emit_native_label_assign(emit, emit->exit_label + 1);
+ }
}
// Load return value
@@ -1212,7 +1304,7 @@ STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) {
emit_post_push_reg(emit, vtype, reg_local_table[local_num]);
} else {
need_reg_single(emit, REG_TEMP0, 0);
- ASM_MOV_REG_LOCAL(emit->as, REG_TEMP0, LOCAL_IDX_LOCAL_VAR(emit, local_num));
+ emit_native_mov_reg_state(emit, REG_TEMP0, LOCAL_IDX_LOCAL_VAR(emit, local_num));
emit_post_push_reg(emit, vtype, REG_TEMP0);
}
}
@@ -1431,7 +1523,7 @@ STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num)
emit_pre_pop_reg(emit, &vtype, reg_local_table[local_num]);
} else {
emit_pre_pop_reg(emit, &vtype, REG_TEMP0);
- ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_LOCAL_VAR(emit, local_num), REG_TEMP0);
+ emit_native_mov_state_reg(emit, LOCAL_IDX_LOCAL_VAR(emit, local_num), REG_TEMP0);
}
emit_post(emit);
@@ -2464,6 +2556,22 @@ STATIC void emit_native_call_method(emit_t *emit, mp_uint_t n_positional, mp_uin
STATIC void emit_native_return_value(emit_t *emit) {
DEBUG_printf("return_value\n");
+
+ if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) {
+ // Save pointer to current stack position for caller to access return value
+ emit_get_stack_pointer_to_reg_for_pop(emit, REG_TEMP0, 1);
+ emit_native_mov_state_reg(emit, offsetof(mp_code_state_t, sp) / sizeof(uintptr_t), REG_TEMP0);
+
+ // Put return type in return value slot
+ ASM_MOV_REG_IMM(emit->as, REG_TEMP0, MP_VM_RETURN_NORMAL);
+ ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_TEMP0);
+
+ // Do the unwinding jump to get to the return handler
+ emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size);
+ emit->last_emit_was_return_value = true;
+ return;
+ }
+
if (emit->do_viper_types) {
vtype_kind_t return_vtype = emit->scope->scope_flags >> MP_SCOPE_FLAG_VIPERRET_POS;
if (peek_vtype(emit, 0) == VTYPE_PTR_NONE) {
@@ -2510,10 +2618,85 @@ STATIC void emit_native_raise_varargs(emit_t *emit, mp_uint_t n_args) {
}
STATIC void emit_native_yield(emit_t *emit, int kind) {
- // not supported (for now)
- (void)emit;
- (void)kind;
- mp_raise_NotImplementedError("native yield");
+ // Note: 1 (yield) or 3 (yield from) labels are reserved for this function, starting at *emit->label_slot
+
+ if (emit->do_viper_types) {
+ mp_raise_NotImplementedError("native yield");
+ }
+ emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
+
+ need_stack_settled(emit);
+
+ if (kind == MP_EMIT_YIELD_FROM) {
+
+ // Top of yield-from loop, conceptually implementing:
+ // for item in generator:
+ // yield item
+
+ // Jump to start of loop
+ emit_native_jump(emit, *emit->label_slot + 2);
+
+ // Label for top of loop
+ emit_native_label_assign(emit, *emit->label_slot + 1);
+ }
+
+ // Save pointer to current stack position for caller to access yielded value
+ emit_get_stack_pointer_to_reg_for_pop(emit, REG_TEMP0, 1);
+ emit_native_mov_state_reg(emit, offsetof(mp_code_state_t, sp) / sizeof(uintptr_t), REG_TEMP0);
+
+ // Put return type in return value slot
+ ASM_MOV_REG_IMM(emit->as, REG_TEMP0, MP_VM_RETURN_YIELD);
+ ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_TEMP0);
+
+ // Save re-entry PC
+ ASM_MOV_REG_PCREL(emit->as, REG_TEMP0, *emit->label_slot);
+ emit_native_mov_state_reg(emit, LOCAL_IDX_GEN_PC(emit), REG_TEMP0);
+
+ // Jump to exit handler
+ ASM_JUMP(emit->as, emit->exit_label);
+
+ // Label re-entry point
+ mp_asm_base_label_assign(&emit->as->base, *emit->label_slot);
+
+ // Re-open any active exception handler
+ if (emit->exc_stack_size > 0) {
+ // Find innermost active exception handler, to restore as current handler
+ exc_stack_entry_t *e = &emit->exc_stack[emit->exc_stack_size - 1];
+ for (; e >= emit->exc_stack; --e) {
+ if (e->is_active) {
+ // Found active handler, get its PC
+ ASM_MOV_REG_PCREL(emit->as, REG_RET, e->label);
+ ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_PC(emit), REG_RET);
+ }
+ }
+ }
+
+ emit_native_adjust_stack_size(emit, 1); // send_value
+
+ if (kind == MP_EMIT_YIELD_VALUE) {
+ // Check LOCAL_IDX_EXC_VAL for any injected value
+ ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_EXC_VAL(emit));
+ emit_call(emit, MP_F_NATIVE_RAISE);
+ } else {
+ // Label loop entry
+ emit_native_label_assign(emit, *emit->label_slot + 2);
+
+ // Get the next item from the delegate generator
+ vtype_kind_t vtype;
+ emit_pre_pop_reg(emit, &vtype, REG_ARG_2); // send_value
+ emit_access_stack(emit, 1, &vtype, REG_ARG_1); // generator
+ ASM_MOV_REG_LOCAL(emit->as, REG_ARG_3, LOCAL_IDX_EXC_VAL(emit)); // throw_value
+ emit_post_push_reg(emit, VTYPE_PYOBJ, REG_ARG_3);
+ emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, 1); // ret_value
+ emit_call(emit, MP_F_NATIVE_YIELD_FROM);
+
+ // If returned non-zero then generator continues
+ ASM_JUMP_IF_REG_NONZERO(emit->as, REG_RET, *emit->label_slot + 1, true);
+
+ // Pop exhausted gen, replace with ret_value
+ emit_native_adjust_stack_size(emit, 1); // ret_value
+ emit_fold_stack_top(emit, REG_ARG_1);
+ }
}
STATIC void emit_native_start_except_handler(emit_t *emit) {
diff --git a/py/emitnx86.c b/py/emitnx86.c
index 597a0fd4a8..7c96c3b82b 100644
--- a/py/emitnx86.c
+++ b/py/emitnx86.c
@@ -66,6 +66,7 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
[MP_F_SETUP_CODE_STATE] = 4,
[MP_F_SMALL_INT_FLOOR_DIVIDE] = 2,
[MP_F_SMALL_INT_MODULO] = 2,
+ [MP_F_NATIVE_YIELD_FROM] = 3,
};
#define N_X86 (1)
diff --git a/py/nativeglue.c b/py/nativeglue.c
index a15a2eae31..b3a50ef198 100644
--- a/py/nativeglue.c
+++ b/py/nativeglue.c
@@ -106,7 +106,7 @@ mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const m
// wrapper that makes raise obj and raises it
// END_FINALLY opcode requires that we don't raise if o==None
void mp_native_raise(mp_obj_t o) {
- if (o != mp_const_none) {
+ if (o != MP_OBJ_NULL && o != mp_const_none) {
nlr_raise(mp_make_raise_obj(o));
}
}
@@ -137,6 +137,42 @@ STATIC mp_obj_t mp_native_iternext(mp_obj_iter_buf_t *iter) {
return mp_iternext(obj);
}
+STATIC bool mp_native_yield_from(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value) {
+ mp_vm_return_kind_t ret_kind;
+ nlr_buf_t nlr_buf;
+ mp_obj_t throw_value = *ret_value;
+ if (nlr_push(&nlr_buf) == 0) {
+ if (throw_value != MP_OBJ_NULL) {
+ send_value = MP_OBJ_NULL;
+ }
+ ret_kind = mp_resume(gen, send_value, throw_value, ret_value);
+ nlr_pop();
+ } else {
+ ret_kind = MP_VM_RETURN_EXCEPTION;
+ *ret_value = nlr_buf.ret_val;
+ }
+
+ if (ret_kind == MP_VM_RETURN_YIELD) {
+ return true;
+ } else if (ret_kind == MP_VM_RETURN_NORMAL) {
+ if (*ret_value == MP_OBJ_STOP_ITERATION) {
+ *ret_value = mp_const_none;
+ }
+ } else {
+ assert(ret_kind == MP_VM_RETURN_EXCEPTION);
+ if (!mp_obj_exception_match(*ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
+ nlr_raise(*ret_value);
+ }
+ *ret_value = mp_obj_exception_get_value(*ret_value);
+ }
+
+ if (throw_value != MP_OBJ_NULL && mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) {
+ nlr_raise(mp_make_raise_obj(throw_value));
+ }
+
+ return false;
+}
+
// these must correspond to the respective enum in runtime0.h
void *const mp_fun_table[MP_F_NUMBER_OF] = {
mp_convert_obj_to_native,
@@ -189,6 +225,7 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = {
mp_setup_code_state,
mp_small_int_floor_divide,
mp_small_int_modulo,
+ mp_native_yield_from,
};
/*
diff --git a/py/obj.h b/py/obj.h
index 9503848161..0781ba5c55 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -558,6 +558,7 @@ extern const mp_obj_type_t mp_type_zip;
extern const mp_obj_type_t mp_type_array;
extern const mp_obj_type_t mp_type_super;
extern const mp_obj_type_t mp_type_gen_wrap;
+extern const mp_obj_type_t mp_type_native_gen_wrap;
extern const mp_obj_type_t mp_type_gen_instance;
extern const mp_obj_type_t mp_type_fun_builtin_0;
extern const mp_obj_type_t mp_type_fun_builtin_1;
diff --git a/py/objfun.c b/py/objfun.c
index ce6fd22a5b..112eadb418 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -154,7 +154,7 @@ STATIC const mp_obj_type_t mp_type_fun_native;
qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) {
const mp_obj_fun_bc_t *fun = MP_OBJ_TO_PTR(fun_in);
#if MICROPY_EMIT_NATIVE
- if (fun->base.type == &mp_type_fun_native) {
+ if (fun->base.type == &mp_type_fun_native || fun->base.type == &mp_type_native_gen_wrap) {
// TODO native functions don't have name stored
return MP_QSTR_;
}
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 58a33d40b4..348d2d79df 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -74,6 +74,53 @@ const mp_obj_type_t mp_type_gen_wrap = {
};
/******************************************************************************/
+// native generator wrapper
+
+#if MICROPY_EMIT_NATIVE
+
+STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ // The state for a native generating function is held in the same struct as a bytecode function
+ mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in);
+
+ // Determine start of prelude, and extract n_state from it
+ uintptr_t prelude_offset = ((uintptr_t*)self_fun->bytecode)[0];
+ size_t n_state = mp_decode_uint_value(self_fun->bytecode + prelude_offset);
+ size_t n_exc_stack = 0;
+
+ // Allocate the generator object, with room for local stack and exception stack
+ mp_obj_gen_instance_t *o = m_new_obj_var(mp_obj_gen_instance_t, byte,
+ n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t));
+ o->base.type = &mp_type_gen_instance;
+
+ // Parse the input arguments and set up the code state
+ o->globals = self_fun->globals;
+ o->code_state.fun_bc = self_fun;
+ o->code_state.ip = (const byte*)prelude_offset;
+ mp_setup_code_state(&o->code_state, n_args, n_kw, args);
+
+ // Indicate we are a native function, which doesn't use this variable
+ o->code_state.exc_sp = NULL;
+
+ // Prepare the generator instance for execution
+ uintptr_t start_offset = ((uintptr_t*)self_fun->bytecode)[1];
+ o->code_state.ip = MICROPY_MAKE_POINTER_CALLABLE((void*)(self_fun->bytecode + start_offset));
+
+ return MP_OBJ_FROM_PTR(o);
+}
+
+const mp_obj_type_t mp_type_native_gen_wrap = {
+ { &mp_type_type },
+ .name = MP_QSTR_generator,
+ .call = native_gen_wrap_call,
+ .unary_op = mp_generic_unary_op,
+ #if MICROPY_PY_FUNCTION_ATTRS
+ .attr = mp_obj_fun_bc_attr,
+ #endif
+};
+
+#endif // MICROPY_EMIT_NATIVE
+
+/******************************************************************************/
/* generator instance */
STATIC void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
@@ -118,7 +165,22 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
self->code_state.old_globals = mp_globals_get();
mp_globals_set(self->globals);
self->globals = NULL;
- mp_vm_return_kind_t ret_kind = mp_execute_bytecode(&self->code_state, throw_value);
+
+ mp_vm_return_kind_t ret_kind;
+
+ #if MICROPY_EMIT_NATIVE
+ if (self->code_state.exc_sp == NULL) {
+ // A native generator, with entry point 2 words into the "bytecode" pointer
+ typedef uintptr_t (*mp_fun_native_gen_t)(void*, mp_obj_t);
+ mp_fun_native_gen_t fun = MICROPY_MAKE_POINTER_CALLABLE((const void*)(self->code_state.fun_bc->bytecode + 2 * sizeof(uintptr_t)));
+ ret_kind = fun((void*)&self->code_state, throw_value);
+ } else
+ #endif
+ {
+ // A bytecode generator
+ ret_kind = mp_execute_bytecode(&self->code_state, throw_value);
+ }
+
self->globals = mp_globals_get();
mp_globals_set(self->code_state.old_globals);
diff --git a/py/runtime0.h b/py/runtime0.h
index 2c6b5fae9d..78d744d29f 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -197,6 +197,7 @@ typedef enum {
MP_F_SETUP_CODE_STATE,
MP_F_SMALL_INT_FLOOR_DIVIDE,
MP_F_SMALL_INT_MODULO,
+ MP_F_NATIVE_YIELD_FROM,
MP_F_NUMBER_OF,
} mp_fun_kind_t;