py/emitnative: Add support for archs with windowed registers.

Such that args/return regs for the parent are different to args/return regs
for child calls.  For an architecture to use this feature it should define
the REG_PARENT_xxx macros before including py/emitnative.c.
This commit is contained in:
Damien George 2019-09-10 13:35:19 +10:00
parent 27fe84e661
commit 4107597b84
1 changed files with 45 additions and 22 deletions

View File

@ -94,6 +94,15 @@
#define OFFSETOF_OBJ_FUN_BC_GLOBALS (offsetof(mp_obj_fun_bc_t, globals) / sizeof(uintptr_t))
#define OFFSETOF_OBJ_FUN_BC_CONST_TABLE (offsetof(mp_obj_fun_bc_t, const_table) / sizeof(uintptr_t))
// If not already defined, set parent args to same as child call registers
#ifndef REG_PARENT_RET
#define REG_PARENT_RET REG_RET
#define REG_PARENT_ARG_1 REG_ARG_1
#define REG_PARENT_ARG_2 REG_ARG_2
#define REG_PARENT_ARG_3 REG_ARG_3
#define REG_PARENT_ARG_4 REG_ARG_4
#endif
// Word index of nlr_buf_t.ret_val
#define NLR_BUF_IDX_RET_VAL (1)
@ -413,16 +422,16 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs);
#if N_X86
asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_1);
asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1);
#endif
// Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE);
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE);
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, 0);
// Store function object (passed as first arg) to stack if needed
if (NEED_FUN_OBJ(emit)) {
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1);
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1);
}
// Put n_args in REG_ARG_1, n_kw in REG_ARG_2, args array in REG_LOCAL_3
@ -431,9 +440,9 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_2);
asm_x86_mov_arg_to_r32(emit->as, 3, REG_LOCAL_3);
#else
ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_ARG_2);
ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_ARG_3);
ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_4);
ASM_MOV_REG_REG(emit->as, REG_ARG_1, REG_PARENT_ARG_2);
ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_PARENT_ARG_3);
ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_4);
#endif
// Check number of args matches this function, and call mp_arg_check_num_sig if not
@ -482,14 +491,14 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
#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);
ASM_MOV_REG_REG(emit->as, REG_GENERATOR_STATE, REG_PARENT_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);
asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2);
#endif
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_ARG_2);
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_PARENT_ARG_2);
// Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, LOCAL_IDX_FUN_OBJ(emit));
@ -505,22 +514,22 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
// 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);
asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1);
asm_x86_mov_arg_to_r32(emit->as, 1, REG_PARENT_ARG_2);
asm_x86_mov_arg_to_r32(emit->as, 2, REG_PARENT_ARG_3);
asm_x86_mov_arg_to_r32(emit->as, 3, REG_PARENT_ARG_4);
#endif
// Load REG_FUN_TABLE with a pointer to mp_fun_table, found in the const_table
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE);
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_CONST_TABLE);
ASM_LOAD_REG_REG_OFFSET(emit->as, REG_FUN_TABLE, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args);
// Set code_state.fun_bc
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_ARG_1);
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_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_CODE_STATE_IP, emit->prelude_offset, REG_ARG_1);
emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, emit->prelude_offset, REG_PARENT_ARG_1);
// Set code_state.n_state (only works on little endian targets due to n_state being uint16_t)
emit_native_mov_state_imm_via(emit, emit->code_state_start + offsetof(mp_code_state_t, n_state) / sizeof(uintptr_t), emit->n_state, REG_ARG_1);
@ -528,6 +537,17 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
// Put address of code_state into first arg
ASM_MOV_REG_LOCAL_ADDR(emit->as, REG_ARG_1, emit->code_state_start);
// Copy next 3 args if needed
#if REG_ARG_2 != REG_PARENT_ARG_2
ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_PARENT_ARG_2);
#endif
#if REG_ARG_3 != REG_PARENT_ARG_3
ASM_MOV_REG_REG(emit->as, REG_ARG_3, REG_PARENT_ARG_3);
#endif
#if REG_ARG_4 != REG_PARENT_ARG_4
ASM_MOV_REG_REG(emit->as, REG_ARG_4, REG_PARENT_ARG_4);
#endif
// Call mp_setup_code_state to prepare code_state structure
#if N_THUMB
asm_thumb_bl_ind(emit->as, MP_F_SETUP_CODE_STATE, ASM_THUMB_REG_R4);
@ -1174,7 +1194,7 @@ STATIC void emit_native_global_exc_entry(emit_t *emit) {
ASM_STORE_REG_REG_OFFSET(emit->as, REG_TEMP0, REG_GENERATOR_STATE, OFFSETOF_CODE_STATE_STATE);
// Load return kind
ASM_MOV_REG_IMM(emit->as, REG_RET, MP_VM_RETURN_EXCEPTION);
ASM_MOV_REG_IMM(emit->as, REG_PARENT_RET, MP_VM_RETURN_EXCEPTION);
ASM_EXIT(emit->as);
} else {
@ -1229,7 +1249,7 @@ STATIC void emit_native_global_exc_exit(emit_t *emit) {
}
// Load return value
ASM_MOV_REG_LOCAL(emit->as, REG_RET, LOCAL_IDX_RET_VAL(emit));
ASM_MOV_REG_LOCAL(emit->as, REG_PARENT_RET, LOCAL_IDX_RET_VAL(emit));
}
ASM_EXIT(emit->as);
@ -2617,13 +2637,13 @@ STATIC void emit_native_return_value(emit_t *emit) {
if (peek_vtype(emit, 0) == VTYPE_PTR_NONE) {
emit_pre_pop_discard(emit);
if (return_vtype == VTYPE_PYOBJ) {
emit_native_mov_reg_const(emit, REG_RET, MP_F_CONST_NONE_OBJ);
emit_native_mov_reg_const(emit, REG_PARENT_RET, MP_F_CONST_NONE_OBJ);
} else {
ASM_MOV_REG_IMM(emit->as, REG_ARG_1, 0);
}
} else {
vtype_kind_t vtype;
emit_pre_pop_reg(emit, &vtype, return_vtype == VTYPE_PYOBJ ? REG_RET : REG_ARG_1);
emit_pre_pop_reg(emit, &vtype, return_vtype == VTYPE_PYOBJ ? REG_PARENT_RET : REG_ARG_1);
if (vtype != return_vtype) {
EMIT_NATIVE_VIPER_TYPE_ERROR(emit,
"return expected '%q' but got '%q'",
@ -2632,15 +2652,18 @@ STATIC void emit_native_return_value(emit_t *emit) {
}
if (return_vtype != VTYPE_PYOBJ) {
emit_call_with_imm_arg(emit, MP_F_CONVERT_NATIVE_TO_OBJ, return_vtype, REG_ARG_2);
#if REG_RET != REG_PARENT_ARG_RET
ASM_MOV_REG_REG(emit->as, REG_PARENT_RET, REG_RET);
#endif
}
} else {
vtype_kind_t vtype;
emit_pre_pop_reg(emit, &vtype, REG_RET);
emit_pre_pop_reg(emit, &vtype, REG_PARENT_RET);
assert(vtype == VTYPE_PYOBJ);
}
if (NEED_GLOBAL_EXC_HANDLER(emit)) {
// Save return value for the global exception handler to use
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_RET);
ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_RET_VAL(emit), REG_PARENT_RET);
}
emit_native_unwind_jump(emit, emit->exit_label, emit->exc_stack_size);
emit->last_emit_was_return_value = true;