diff --git a/py/asmthumb.c b/py/asmthumb.c index 5bf2d80bb..16337f4d5 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -106,6 +106,10 @@ STATIC byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int num_bytes_to_ } } +uint asm_thumb_get_code_pos(asm_thumb_t *as) { + return as->code_offset; +} + uint asm_thumb_get_code_size(asm_thumb_t *as) { return as->code_size; } diff --git a/py/asmthumb.h b/py/asmthumb.h index 1ce91d38d..e40f95654 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -70,6 +70,7 @@ asm_thumb_t *asm_thumb_new(uint max_num_labels); void asm_thumb_free(asm_thumb_t *as, bool free_code); void asm_thumb_start_pass(asm_thumb_t *as, uint pass); void asm_thumb_end_pass(asm_thumb_t *as); +uint asm_thumb_get_code_pos(asm_thumb_t *as); uint asm_thumb_get_code_size(asm_thumb_t *as); void *asm_thumb_get_code(asm_thumb_t *as); diff --git a/py/asmx64.c b/py/asmx64.c index d501d2cc8..05aa0c2f1 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -176,6 +176,10 @@ STATIC byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int num_bytes_to_writ } } +mp_uint_t asm_x64_get_code_pos(asm_x64_t *as) { + return as->code_offset; +} + mp_uint_t asm_x64_get_code_size(asm_x64_t *as) { return as->code_size; } @@ -222,6 +226,21 @@ STATIC void asm_x64_write_word64(asm_x64_t *as, int64_t w64) { c[7] = IMM64_L7(w64); } +// align must be a multiple of 2 +void asm_x64_align(asm_x64_t* as, mp_uint_t align) { + // TODO fill unused data with NOPs? + as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); +} + +void asm_x64_data(asm_x64_t* as, mp_uint_t bytesize, mp_uint_t val) { + byte *c = asm_x64_get_cur_to_write_bytes(as, bytesize); + // machine is little endian + for (uint i = 0; i < bytesize; i++) { + *c++ = val; + val >>= 8; + } +} + /* unused STATIC void asm_x64_write_word32_to(asm_x64_t *as, int offset, int w32) { byte* c; diff --git a/py/asmx64.h b/py/asmx64.h index cb021b2f3..0d3af9aa1 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -78,9 +78,13 @@ asm_x64_t* asm_x64_new(mp_uint_t max_num_labels); void asm_x64_free(asm_x64_t* as, bool free_code); void asm_x64_start_pass(asm_x64_t *as, uint pass); void asm_x64_end_pass(asm_x64_t *as); +mp_uint_t asm_x64_get_code_pos(asm_x64_t *as); mp_uint_t asm_x64_get_code_size(asm_x64_t* as); void* asm_x64_get_code(asm_x64_t* as); +void asm_x64_align(asm_x64_t *as, mp_uint_t align); +void asm_x64_data(asm_x64_t *as, mp_uint_t bytesize, mp_uint_t val); + void asm_x64_nop(asm_x64_t* as); void asm_x64_push_r64(asm_x64_t* as, int src_r64); void asm_x64_pop_r64(asm_x64_t* as, int dest_r64); diff --git a/py/asmx86.c b/py/asmx86.c index 3de2f12aa..8a4383c82 100644 --- a/py/asmx86.c +++ b/py/asmx86.c @@ -162,6 +162,10 @@ STATIC byte *asm_x86_get_cur_to_write_bytes(asm_x86_t *as, int num_bytes_to_writ } } +mp_uint_t asm_x86_get_code_pos(asm_x86_t *as) { + return as->code_offset; +} + mp_uint_t asm_x86_get_code_size(asm_x86_t *as) { return as->code_size; } @@ -196,6 +200,21 @@ STATIC void asm_x86_write_word32(asm_x86_t *as, int w32) { c[3] = IMM32_L3(w32); } +// align must be a multiple of 2 +void asm_x86_align(asm_x86_t* as, mp_uint_t align) { + // TODO fill unused data with NOPs? + as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); +} + +void asm_x86_data(asm_x86_t* as, mp_uint_t bytesize, mp_uint_t val) { + byte *c = asm_x86_get_cur_to_write_bytes(as, bytesize); + // machine is little endian + for (uint i = 0; i < bytesize; i++) { + *c++ = val; + val >>= 8; + } +} + STATIC void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) { assert(disp_r32 != ASM_X86_REG_ESP); @@ -541,7 +560,13 @@ void asm_x86_push_local_addr(asm_x86_t *as, int local_num, int temp_r32) void asm_x86_call_ind(asm_x86_t *as, void *ptr, mp_uint_t n_args, int temp_r32) { // TODO align stack on 16-byte boundary before the call - assert(n_args <= 3); + assert(n_args <= 5); + if (n_args > 4) { + asm_x86_push_r32(as, ASM_X86_REG_ARG_5); + } + if (n_args > 3) { + asm_x86_push_r32(as, ASM_X86_REG_ARG_4); + } if (n_args > 2) { asm_x86_push_r32(as, ASM_X86_REG_ARG_3); } diff --git a/py/asmx86.h b/py/asmx86.h index 544786cf7..cb245f37d 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -61,6 +61,8 @@ #define ASM_X86_REG_ARG_1 ASM_X86_REG_EAX #define ASM_X86_REG_ARG_2 ASM_X86_REG_ECX #define ASM_X86_REG_ARG_3 ASM_X86_REG_EDX +#define ASM_X86_REG_ARG_4 ASM_X86_REG_EBX +#define ASM_X86_REG_ARG_5 ASM_X86_REG_ESI // condition codes, used for jcc and setcc (despite their j-name!) #define ASM_X86_CC_JB (0x2) // below, unsigned @@ -79,9 +81,13 @@ asm_x86_t* asm_x86_new(mp_uint_t max_num_labels); void asm_x86_free(asm_x86_t* as, bool free_code); void asm_x86_start_pass(asm_x86_t *as, mp_uint_t pass); void asm_x86_end_pass(asm_x86_t *as); +mp_uint_t asm_x86_get_code_pos(asm_x86_t *as); mp_uint_t asm_x86_get_code_size(asm_x86_t* as); void* asm_x86_get_code(asm_x86_t* as); +void asm_x86_align(asm_x86_t *as, mp_uint_t align); +void asm_x86_data(asm_x86_t *as, mp_uint_t bytesize, mp_uint_t val); + void asm_x86_mov_r32_r32(asm_x86_t* as, int dest_r32, int src_r32); void asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32); void asm_x86_mov_i32_to_r32_aligned(asm_x86_t *as, int32_t src_i32, int dest_r32); diff --git a/py/bc.c b/py/bc.c index 865065ab1..613cce795 100644 --- a/py/bc.c +++ b/py/bc.c @@ -78,8 +78,13 @@ STATIC void dump_args(const mp_obj_t *a, mp_uint_t sz) { #define dump_args(...) (void)0 #endif -// code_state should have ->ip filled in (pointing past code info block), -// as well as ->n_state. +// On entry code_state should be allocated somewhere (stack/heap) and +// contain the following valid entries: +// - code_state->code_info should be the offset in bytes from the start of +// the bytecode chunk to the start of the code-info within the bytecode +// - code_state->ip should contain the offset in bytes from the start of +// the bytecode chunk to the start of the prelude within the bytecode +// - code_state->n_state should be set to the state size (locals plus stack) void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { // This function is pretty complicated. It's main aim is to be efficient in speed and RAM // usage for the common case of positional only args. @@ -89,7 +94,7 @@ void mp_setup_code_state(mp_code_state *code_state, mp_obj_t self_in, mp_uint_t #if MICROPY_STACKLESS code_state->prev = NULL; #endif - code_state->code_info = self->bytecode; + code_state->code_info = self->bytecode + (mp_uint_t)code_state->code_info; code_state->sp = &code_state->state[0] - 1; code_state->exc_sp = (mp_exc_stack_t*)(code_state->state + n_state) - 1; @@ -227,7 +232,7 @@ continue2:; } // bytecode prelude: initialise closed over variables - const byte *ip = code_state->ip; + const byte *ip = self->bytecode + (mp_uint_t)code_state->ip; mp_uint_t local_num; while ((local_num = *ip++) != 255) { code_state->state[n_state - 1 - local_num] = diff --git a/py/emitglue.c b/py/emitglue.c index b3e8495d9..610e76d53 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -86,16 +86,17 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, } #if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB -void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_args, mp_uint_t type_sig) { +void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags, mp_uint_t type_sig) { assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); rc->kind = kind; - rc->scope_flags = 0; - rc->n_pos_args = n_args; + rc->scope_flags = scope_flags; + rc->n_pos_args = n_pos_args; + rc->n_kwonly_args = n_kwonly_args; rc->data.u_native.fun_data = fun_data; rc->data.u_native.type_sig = type_sig; #ifdef DEBUG_PRINT - DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_args=" UINT_FMT "\n", kind, fun_data, fun_len, n_args); + DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " n_kwonly_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, n_kwonly_args, (uint)scope_flags); for (mp_uint_t i = 0; i < fun_len; i++) { if (i > 0 && i % 16 == 0) { DEBUG_printf("\n"); @@ -134,7 +135,7 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp break; #if MICROPY_EMIT_NATIVE case MP_CODE_NATIVE_PY: - fun = mp_obj_new_fun_native(rc->n_pos_args, rc->data.u_native.fun_data); + fun = mp_obj_new_fun_native(rc->scope_flags, rc->n_pos_args, rc->n_kwonly_args, def_args, def_kw_args, rc->data.u_native.fun_data); break; case MP_CODE_NATIVE_VIPER: fun = mp_obj_new_fun_viper(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig); diff --git a/py/emitglue.h b/py/emitglue.h index 4e915df2d..97e680b4c 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -44,7 +44,7 @@ typedef struct _mp_raw_code_t mp_raw_code_t; mp_raw_code_t *mp_emit_glue_new_raw_code(void); void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, byte *code, mp_uint_t len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags); -void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_args, mp_uint_t type_sig); +void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_uint_t scope_flags, mp_uint_t type_sig); mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args); mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 220ff12c7..59cdc3aca 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -90,7 +90,7 @@ STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit) { if (emit->pass == MP_PASS_EMIT) { void *f = asm_thumb_get_code(emit->as); - mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0); + mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, asm_thumb_get_code_size(emit->as), emit->scope->num_pos_args, 0, 0, 0); } } diff --git a/py/emitnative.c b/py/emitnative.c index f72112ba0..ff57ef1a3 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -48,6 +48,7 @@ #include "py/nlr.h" #include "py/emit.h" +#include "py/bc.h" #if 0 // print debugging info #define DEBUG_PRINT (1) @@ -70,11 +71,14 @@ #define EXPORT_FUN(name) emit_native_x64_##name +#define ASM_WORD_SIZE (8) + #define REG_RET ASM_X64_REG_RAX #define REG_ARG_1 ASM_X64_REG_RDI #define REG_ARG_2 ASM_X64_REG_RSI #define REG_ARG_3 ASM_X64_REG_RDX #define REG_ARG_4 ASM_X64_REG_RCX +#define REG_ARG_5 ASM_X64_REG_R08 // caller-save #define REG_TEMP0 ASM_X64_REG_RAX @@ -94,12 +98,16 @@ #define ASM_NEW asm_x64_new #define ASM_FREE asm_x64_free #define ASM_GET_CODE asm_x64_get_code +#define ASM_GET_CODE_POS asm_x64_get_code_pos #define ASM_GET_CODE_SIZE asm_x64_get_code_size #define ASM_START_PASS asm_x64_start_pass #define ASM_END_PASS asm_x64_end_pass #define ASM_ENTRY asm_x64_entry #define ASM_EXIT asm_x64_exit +#define ASM_ALIGN asm_x64_align +#define ASM_DATA asm_x64_data + #define ASM_LABEL_ASSIGN asm_x64_label_assign #define ASM_JUMP asm_x64_jmp_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ @@ -202,14 +210,19 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_DELETE_GLOBAL] = 1, [MP_F_NEW_CELL] = 1, [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3, + [MP_F_SETUP_CODE_STATE] = 5, }; #define EXPORT_FUN(name) emit_native_x86_##name +#define ASM_WORD_SIZE (4) + #define REG_RET ASM_X86_REG_EAX #define REG_ARG_1 ASM_X86_REG_ARG_1 #define REG_ARG_2 ASM_X86_REG_ARG_2 #define REG_ARG_3 ASM_X86_REG_ARG_3 +#define REG_ARG_4 ASM_X86_REG_ARG_4 +#define REG_ARG_5 ASM_X86_REG_ARG_5 // caller-save, so can be used as temporaries #define REG_TEMP0 ASM_X86_REG_EAX @@ -229,12 +242,16 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #define ASM_NEW asm_x86_new #define ASM_FREE asm_x86_free #define ASM_GET_CODE asm_x86_get_code +#define ASM_GET_CODE_POS asm_x86_get_code_pos #define ASM_GET_CODE_SIZE asm_x86_get_code_size #define ASM_START_PASS asm_x86_start_pass #define ASM_END_PASS asm_x86_end_pass #define ASM_ENTRY asm_x86_entry #define ASM_EXIT asm_x86_exit +#define ASM_ALIGN asm_x86_align +#define ASM_DATA asm_x86_data + #define ASM_LABEL_ASSIGN asm_x86_label_assign #define ASM_JUMP asm_x86_jmp_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ @@ -292,11 +309,14 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #define EXPORT_FUN(name) emit_native_thumb_##name +#define ASM_WORD_SIZE (4) + #define REG_RET ASM_THUMB_REG_R0 #define REG_ARG_1 ASM_THUMB_REG_R0 #define REG_ARG_2 ASM_THUMB_REG_R1 #define REG_ARG_3 ASM_THUMB_REG_R2 #define REG_ARG_4 ASM_THUMB_REG_R3 +// rest of args go on stack #define REG_TEMP0 ASM_THUMB_REG_R0 #define REG_TEMP1 ASM_THUMB_REG_R1 @@ -314,12 +334,16 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #define ASM_NEW asm_thumb_new #define ASM_FREE asm_thumb_free #define ASM_GET_CODE asm_thumb_get_code +#define ASM_GET_CODE_POS asm_thumb_get_code_pos #define ASM_GET_CODE_SIZE asm_thumb_get_code_size #define ASM_START_PASS asm_thumb_start_pass #define ASM_END_PASS asm_thumb_end_pass #define ASM_ENTRY asm_thumb_entry #define ASM_EXIT asm_thumb_exit +#define ASM_ALIGN asm_thumb_align +#define ASM_DATA asm_thumb_data + #define ASM_LABEL_ASSIGN asm_thumb_label_assign #define ASM_JUMP asm_thumb_b_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ @@ -504,6 +528,10 @@ struct _emit_t { stack_info_t *stack_info; vtype_kind_t saved_stack_vtype; + int code_info_size; + int code_info_offset; + int prelude_offset; + int n_state; int stack_start; int stack_size; @@ -561,6 +589,8 @@ STATIC void emit_post_push_reg(emit_t *emit, vtype_kind_t vtype, int reg); 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); +#define STATE_START (sizeof(mp_code_state) / sizeof(mp_uint_t)) + 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); @@ -584,14 +614,23 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_info = m_new(stack_info_t, emit->stack_info_alloc); } - // set default type for return and arguments + // set default type for return emit->return_vtype = VTYPE_PYOBJ; - for (mp_uint_t i = 0; i < emit->scope->num_pos_args; i++) { + + // set default type for arguments + mp_uint_t num_args = emit->scope->num_pos_args + emit->scope->num_kwonly_args; + if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) { + num_args += 1; + } + if (scope->scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) { + num_args += 1; + } + for (mp_uint_t i = 0; i < num_args; i++) { emit->local_vtype[i] = VTYPE_PYOBJ; } // local variables begin unbound, and have unknown type - for (mp_uint_t i = emit->scope->num_pos_args; i < emit->local_vtype_alloc; i++) { + for (mp_uint_t i = num_args; i < emit->local_vtype_alloc; i++) { emit->local_vtype[i] = VTYPE_UNBOUND; } @@ -603,107 +642,157 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_START_PASS(emit->as, pass == MP_PASS_EMIT ? ASM_PASS_EMIT : ASM_PASS_COMPUTE); - // entry to function - int num_locals = 0; - if (pass > MP_PASS_SCOPE) { - num_locals = scope->num_locals - REG_LOCAL_NUM; - if (num_locals < 0) { - num_locals = 0; - } - emit->stack_start = num_locals; - num_locals += scope->stack_size; - } - ASM_ENTRY(emit->as, num_locals); + // generate code for entry to function - // initialise locals from parameters -#if N_X64 - for (int i = 0; i < scope->num_pos_args; i++) { - if (i == 0) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_1, REG_ARG_1); - } else if (i == 1) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_2, REG_ARG_2); - } else if (i == 2) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3); - } else if (i == 3) { - asm_x64_mov_r64_to_local(emit->as, REG_ARG_4, i - REG_LOCAL_NUM); - } else { - // TODO not implemented - assert(0); + if (emit->do_viper_types) { + + // entry to function + int num_locals = 0; + if (pass > MP_PASS_SCOPE) { + num_locals = scope->num_locals - REG_LOCAL_NUM; + if (num_locals < 0) { + num_locals = 0; + } + emit->stack_start = num_locals; + num_locals += scope->stack_size; } - } -#elif N_X86 - for (int i = 0; i < scope->num_pos_args; i++) { - if (i == 0) { - asm_x86_mov_arg_to_r32(emit->as, i, REG_LOCAL_1); - } else if (i == 1) { - asm_x86_mov_arg_to_r32(emit->as, i, REG_LOCAL_2); - } else if (i == 2) { - asm_x86_mov_arg_to_r32(emit->as, i, REG_LOCAL_3); - } else { - asm_x86_mov_arg_to_r32(emit->as, i, REG_TEMP0); - asm_x86_mov_r32_to_local(emit->as, REG_TEMP0, i - REG_LOCAL_NUM); + ASM_ENTRY(emit->as, num_locals); + + #if N_X86 + for (int i = 0; i < scope->num_pos_args; i++) { + if (i == 0) { + asm_x86_mov_arg_to_r32(emit->as, i, REG_LOCAL_1); + } else if (i == 1) { + asm_x86_mov_arg_to_r32(emit->as, i, REG_LOCAL_2); + } else if (i == 2) { + asm_x86_mov_arg_to_r32(emit->as, i, REG_LOCAL_3); + } else { + asm_x86_mov_arg_to_r32(emit->as, i, REG_TEMP0); + asm_x86_mov_r32_to_local(emit->as, REG_TEMP0, i - REG_LOCAL_NUM); + } } - } -#elif N_THUMB - for (int i = 0; i < scope->num_pos_args; i++) { - if (i == 0) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_1, REG_ARG_1); - } else if (i == 1) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_2, REG_ARG_2); - } else if (i == 2) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3); - } else if (i == 3) { - asm_thumb_mov_local_reg(emit->as, i - REG_LOCAL_NUM, REG_ARG_4); - } else { - // TODO not implemented - assert(0); + #else + for (int i = 0; i < scope->num_pos_args; i++) { + if (i == 0) { + ASM_MOV_REG_REG(emit->as, REG_LOCAL_1, REG_ARG_1); + } else if (i == 1) { + ASM_MOV_REG_REG(emit->as, REG_LOCAL_2, REG_ARG_2); + } else if (i == 2) { + ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3); + } else if (i == 3) { + ASM_MOV_REG_TO_LOCAL(emit->as, REG_ARG_4, i - REG_LOCAL_NUM); + } else { + // TODO not implemented + assert(0); + } + } + #endif + + } else { + // work out size of state (locals plus stack) + emit->n_state = scope->num_locals + scope->stack_size; + + // allocate space on C-stack for code_state structure, which includes state + ASM_ENTRY(emit->as, STATE_START + emit->n_state); + + // prepare incoming arguments for call to mp_setup_code_state + #if N_X86 + asm_x86_mov_arg_to_r32(emit->as, 0, REG_ARG_2); + asm_x86_mov_arg_to_r32(emit->as, 1, REG_ARG_3); + asm_x86_mov_arg_to_r32(emit->as, 2, REG_ARG_4); + asm_x86_mov_arg_to_r32(emit->as, 3, REG_ARG_5); + #else + #if N_THUMB + ASM_MOV_REG_REG(emit->as, ASM_THUMB_REG_R4, REG_ARG_4); + #else + ASM_MOV_REG_REG(emit->as, REG_ARG_5, REG_ARG_4); + #endif + ASM_MOV_REG_REG(emit->as, REG_ARG_4, REG_ARG_3); + ASM_MOV_REG_REG(emit->as, REG_ARG_3, REG_ARG_2); + ASM_MOV_REG_REG(emit->as, REG_ARG_2, REG_ARG_1); + #endif + + // set code_state.code_info (offset from start of this function to code_info data) + // XXX this encoding may change size + ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->code_info_offset, offsetof(mp_code_state, code_info) / sizeof(mp_uint_t), REG_ARG_1); + + // set code_state.ip (offset from start of this function to prelude info) + // XXX this encoding may change size + ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->prelude_offset, offsetof(mp_code_state, ip) / sizeof(mp_uint_t), REG_ARG_1); + + // set code_state.n_state + ASM_MOV_IMM_TO_LOCAL_USING(emit->as, emit->n_state, offsetof(mp_code_state, n_state) / sizeof(mp_uint_t), REG_ARG_1); + + // put address of code_state into first arg + ASM_MOV_LOCAL_ADDR_TO_REG(emit->as, 0, REG_ARG_1); + + // call mp_setup_code_state to prepare code_state structure + #if N_THUMB + asm_thumb_op16(emit->as, 0xb400 | (1 << ASM_THUMB_REG_R4)); // push 5th arg + asm_thumb_bl_ind(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE, ASM_THUMB_REG_R4); + asm_thumb_op16(emit->as, 0xbc00 | (1 << REG_RET)); // pop dummy (was 5th arg) + #else + ASM_CALL_IND(emit->as, mp_fun_table[MP_F_SETUP_CODE_STATE], MP_F_SETUP_CODE_STATE); + #endif + + // cache some locals in registers + if (scope->num_locals > 0) { + ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 0, REG_LOCAL_1); + if (scope->num_locals > 1) { + ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 1, REG_LOCAL_2); + if (scope->num_locals > 2) { + ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - 2, REG_LOCAL_3); + } + } + } + + // set the type of closed over variables + for (mp_uint_t i = 0; i < scope->id_info_len; i++) { + id_info_t *id = &scope->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL) { + emit->local_vtype[id->local_num] = VTYPE_PYOBJ; + } } } + #if N_THUMB // TODO don't load r7 if we don't need it asm_thumb_mov_reg_i32(emit->as, ASM_THUMB_REG_R7, (mp_uint_t)mp_fun_table); -#elif N_ARM - for (int i = 0; i < scope->num_pos_args; i++) { - if (i == 0) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_1, REG_ARG_1); - } else if (i == 1) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_2, REG_ARG_2); - } else if (i == 2) { - ASM_MOV_REG_REG(emit->as, REG_LOCAL_3, REG_ARG_3); - } else if (i == 3) { - asm_arm_mov_local_reg(emit->as, i - REG_LOCAL_NUM, REG_ARG_4); - } else { - // TODO not implemented - assert(0); - } - } + #endif + #if N_ARM // TODO don't load r7 if we don't need it asm_arm_mov_reg_i32(emit->as, ASM_ARM_REG_R7, (mp_uint_t)mp_fun_table); -#else - #error not implemented -#endif - - // initialise closed over variables - for (int i = 0; i < scope->id_info_len; i++) { - id_info_t *id = &scope->id_info[i]; - if (id->kind == ID_INFO_KIND_CELL) { - if (emit->local_vtype[id->local_num] != VTYPE_UNBOUND) { - emit_native_load_fast(emit, id->qst, id->local_num); - vtype_kind_t vtype; - emit_pre_pop_reg(emit, &vtype, REG_ARG_1); - } - ASM_CALL_IND(emit->as, mp_fun_table[MP_F_NEW_CELL], MP_F_NEW_CELL); - emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); - emit_native_store_fast(emit, id->qst, id->local_num); - } - } + #endif } STATIC void emit_native_end_pass(emit_t *emit) { if (!emit->last_emit_was_return_value) { ASM_EXIT(emit->as); } + + if (!emit->do_viper_types) { + // write dummy code info (for mp_setup_code_state to parse) and arg names + emit->code_info_offset = ASM_GET_CODE_POS(emit->as); + ASM_DATA(emit->as, 1, emit->code_info_size); + ASM_ALIGN(emit->as, ASM_WORD_SIZE); + emit->code_info_size = ASM_GET_CODE_POS(emit->as) - emit->code_info_offset; + for (int i = 0; i < emit->scope->num_pos_args + emit->scope->num_kwonly_args; i++) { + ASM_DATA(emit->as, ASM_WORD_SIZE, (mp_uint_t)MP_OBJ_NEW_QSTR(emit->scope->id_info[i].qst)); + } + + // bytecode prelude: initialise closed over variables + emit->prelude_offset = ASM_GET_CODE_POS(emit->as); + for (int i = 0; i < emit->scope->id_info_len; i++) { + id_info_t *id = &emit->scope->id_info[i]; + if (id->kind == ID_INFO_KIND_CELL) { + assert(id->local_num < 255); + ASM_DATA(emit->as, 1, id->local_num); // write the local which should be converted to a cell + } + } + ASM_DATA(emit->as, 1, 255); // end of list sentinel + } + ASM_END_PASS(emit->as); // check stack is back to zero size @@ -722,7 +811,10 @@ STATIC void emit_native_end_pass(emit_t *emit) { type_sig |= (emit->local_vtype[i] & 3) << (i * 2 + 2); } - mp_emit_glue_assign_native(emit->scope->raw_code, emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, f, f_len, emit->scope->num_pos_args, type_sig); + mp_emit_glue_assign_native(emit->scope->raw_code, + emit->do_viper_types ? MP_CODE_NATIVE_VIPER : MP_CODE_NATIVE_PY, + f, f_len, emit->scope->num_pos_args, emit->scope->num_kwonly_args, + emit->scope->scope_flags, type_sig); } } @@ -1199,7 +1291,6 @@ STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) { printf("ViperTypeError: local %s used before type known\n", qstr_str(qst)); } emit_native_pre(emit); -#if N_X64 if (local_num == 0) { emit_post_push_reg(emit, vtype, REG_LOCAL_1); } else if (local_num == 1) { @@ -1208,48 +1299,13 @@ STATIC void emit_native_load_fast(emit_t *emit, qstr qst, mp_uint_t local_num) { emit_post_push_reg(emit, vtype, REG_LOCAL_3); } else { need_reg_single(emit, REG_TEMP0, 0); - asm_x64_mov_local_to_r64(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); + if (emit->do_viper_types) { + ASM_MOV_LOCAL_TO_REG(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); + } else { + ASM_MOV_LOCAL_TO_REG(emit->as, STATE_START + emit->n_state - 1 - local_num, REG_TEMP0); + } emit_post_push_reg(emit, vtype, REG_TEMP0); } -#elif N_X86 - if (local_num == 0) { - emit_post_push_reg(emit, vtype, REG_LOCAL_1); - } else if (local_num == 1) { - emit_post_push_reg(emit, vtype, REG_LOCAL_2); - } else if (local_num == 2) { - emit_post_push_reg(emit, vtype, REG_LOCAL_3); - } else { - need_reg_single(emit, REG_TEMP0, 0); - asm_x86_mov_local_to_r32(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); - emit_post_push_reg(emit, vtype, REG_TEMP0); - } -#elif N_THUMB - if (local_num == 0) { - emit_post_push_reg(emit, vtype, REG_LOCAL_1); - } else if (local_num == 1) { - emit_post_push_reg(emit, vtype, REG_LOCAL_2); - } else if (local_num == 2) { - emit_post_push_reg(emit, vtype, REG_LOCAL_3); - } else { - need_reg_single(emit, REG_TEMP0, 0); - asm_thumb_mov_reg_local(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); - emit_post_push_reg(emit, vtype, REG_TEMP0); - } -#elif N_ARM - if (local_num == 0) { - emit_post_push_reg(emit, vtype, REG_LOCAL_1); - } else if (local_num == 1) { - emit_post_push_reg(emit, vtype, REG_LOCAL_2); - } else if (local_num == 2) { - emit_post_push_reg(emit, vtype, REG_LOCAL_3); - } else { - need_reg_single(emit, REG_TEMP0, 0); - asm_arm_mov_reg_local(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); - emit_post_push_reg(emit, vtype, REG_TEMP0); - } -#else - #error not implemented -#endif } STATIC void emit_native_load_deref(emit_t *emit, qstr qst, mp_uint_t local_num) { @@ -1417,7 +1473,6 @@ STATIC void emit_native_load_subscr(emit_t *emit) { STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num) { vtype_kind_t vtype; -#if N_X64 if (local_num == 0) { emit_pre_pop_reg(emit, &vtype, REG_LOCAL_1); } else if (local_num == 1) { @@ -1426,45 +1481,12 @@ STATIC void emit_native_store_fast(emit_t *emit, qstr qst, mp_uint_t local_num) emit_pre_pop_reg(emit, &vtype, REG_LOCAL_3); } else { emit_pre_pop_reg(emit, &vtype, REG_TEMP0); - asm_x64_mov_r64_to_local(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); + if (emit->do_viper_types) { + ASM_MOV_REG_TO_LOCAL(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); + } else { + ASM_MOV_REG_TO_LOCAL(emit->as, REG_TEMP0, STATE_START + emit->n_state - 1 - local_num); + } } -#elif N_X86 - if (local_num == 0) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_1); - } else if (local_num == 1) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_2); - } else if (local_num == 2) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_3); - } else { - emit_pre_pop_reg(emit, &vtype, REG_TEMP0); - asm_x86_mov_r32_to_local(emit->as, REG_TEMP0, local_num - REG_LOCAL_NUM); - } -#elif N_THUMB - if (local_num == 0) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_1); - } else if (local_num == 1) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_2); - } else if (local_num == 2) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_3); - } else { - emit_pre_pop_reg(emit, &vtype, REG_TEMP0); - asm_thumb_mov_local_reg(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); - } -#elif N_ARM - if (local_num == 0) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_1); - } else if (local_num == 1) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_2); - } else if (local_num == 2) { - emit_pre_pop_reg(emit, &vtype, REG_LOCAL_3); - } else { - emit_pre_pop_reg(emit, &vtype, REG_TEMP0); - asm_arm_mov_local_reg(emit->as, local_num - REG_LOCAL_NUM, REG_TEMP0); - } -#else - #error not implemented -#endif - emit_post(emit); // check types diff --git a/py/nativeglue.c b/py/nativeglue.c index ab4063014..d0896eedd 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -32,6 +32,7 @@ #include "py/runtime0.h" #include "py/runtime.h" #include "py/emitglue.h" +#include "py/bc.h" #if MICROPY_EMIT_NATIVE @@ -135,6 +136,7 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = { mp_delete_global, mp_obj_new_cell, mp_make_closure_from_raw_code, + mp_setup_code_state, }; /* diff --git a/py/obj.h b/py/obj.h index b1f173080..448db762a 100644 --- a/py/obj.h +++ b/py/obj.h @@ -469,7 +469,7 @@ mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, mp_uint_t n_ar mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg); mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!) mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args, mp_obj_t def_kw_args, const byte *code); -mp_obj_t mp_obj_new_fun_native(mp_uint_t n_args, void *fun_data); +mp_obj_t mp_obj_new_fun_native(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data); mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig); mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data); mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); diff --git a/py/objfun.c b/py/objfun.c index 25a835e9b..48c51053d 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -108,8 +108,18 @@ qstr mp_obj_code_get_name(const byte *code_info) { return mp_decode_uint(&code_info); } +#if MICROPY_EMIT_NATIVE +STATIC const mp_obj_type_t mp_type_fun_native; +#endif + qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) { const mp_obj_fun_bc_t *fun = fun_in; + #if MICROPY_EMIT_NATIVE + if (fun->base.type == &mp_type_fun_native) { + // TODO native functions don't have name stored + return MP_QSTR_; + } + #endif const byte *code_info = fun->bytecode; return mp_obj_code_get_name(code_info); } @@ -168,7 +178,8 @@ mp_code_state *mp_obj_fun_bc_prepare_codestate(mp_obj_t self_in, mp_uint_t n_arg } code_state->n_state = n_state; - code_state->ip = ip; + code_state->code_info = 0; // offset to code-info + code_state->ip = ip - self->bytecode; // offset to prelude mp_setup_code_state(code_state, self_in, n_args, n_kw, args); // execute the byte code with the correct globals context @@ -216,7 +227,8 @@ STATIC mp_obj_t fun_bc_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, } code_state->n_state = n_state; - code_state->ip = ip; + code_state->code_info = 0; // offset to code-info + code_state->ip = (byte*)(ip - self->bytecode); // offset to prelude mp_setup_code_state(code_state, self_in, n_args, n_kw, args); // execute the byte code with the correct globals context @@ -339,42 +351,11 @@ mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_ #if MICROPY_EMIT_NATIVE -typedef struct _mp_obj_fun_native_t { - mp_obj_base_t base; - mp_uint_t n_args; - void *fun_data; // GC must be able to trace this pointer - // TODO add mp_map_t *globals -} mp_obj_fun_native_t; - -typedef mp_obj_t (*native_fun_0_t)(void); -typedef mp_obj_t (*native_fun_1_t)(mp_obj_t); -typedef mp_obj_t (*native_fun_2_t)(mp_obj_t, mp_obj_t); -typedef mp_obj_t (*native_fun_3_t)(mp_obj_t, mp_obj_t, mp_obj_t); - STATIC mp_obj_t fun_native_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { - mp_obj_fun_native_t *self = self_in; - - mp_arg_check_num(n_args, n_kw, self->n_args, self->n_args, false); - - void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data); - - switch (n_args) { - case 0: - return ((native_fun_0_t)fun)(); - - case 1: - return ((native_fun_1_t)fun)(args[0]); - - case 2: - return ((native_fun_2_t)fun)(args[0], args[1]); - - case 3: - return ((native_fun_3_t)fun)(args[0], args[1], args[2]); - - default: - assert(0); - return mp_const_none; - } + MP_STACK_CHECK(); + mp_obj_fun_bc_t *self = self_in; + mp_call_fun_t fun = MICROPY_MAKE_POINTER_CALLABLE((void*)self->bytecode); + return fun(self_in, n_args, n_kw, args); } STATIC const mp_obj_type_t mp_type_fun_native = { @@ -383,12 +364,9 @@ STATIC const mp_obj_type_t mp_type_fun_native = { .call = fun_native_call, }; -mp_obj_t mp_obj_new_fun_native(mp_uint_t n_args, void *fun_data) { - assert(0 <= n_args && n_args <= 3); - mp_obj_fun_native_t *o = m_new_obj(mp_obj_fun_native_t); +mp_obj_t mp_obj_new_fun_native(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data) { + mp_obj_fun_bc_t *o = mp_obj_new_fun_bc(scope_flags, n_pos_args, n_kwonly_args, def_args_in, def_kw_args, (const byte*)fun_data); o->base.type = &mp_type_fun_native; - o->n_args = n_args; - o->fun_data = fun_data; return o; } diff --git a/py/objgenerator.c b/py/objgenerator.c index 112becb46..f672b8d99 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -73,7 +73,8 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw o->globals = self_fun->globals; o->code_state.n_state = n_state; - o->code_state.ip = ip; + o->code_state.code_info = 0; // offset to code-info + o->code_state.ip = (byte*)(ip - self_fun->bytecode); // offset to prelude mp_setup_code_state(&o->code_state, self_fun, n_args, n_kw, args); return o; } diff --git a/py/runtime0.h b/py/runtime0.h index 7ee8c9aa4..be9fc8d52 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -151,6 +151,7 @@ typedef enum { MP_F_DELETE_GLOBAL, MP_F_NEW_CELL, MP_F_MAKE_CLOSURE_FROM_RAW_CODE, + MP_F_SETUP_CODE_STATE, MP_F_NUMBER_OF, } mp_fun_kind_t; diff --git a/tests/run-tests b/tests/run-tests index c64d347e8..ebe295c7d 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -154,19 +154,22 @@ def run_tests(pyb, tests, args): # Some tests are known to fail with native emitter # Remove them from the below when they work if args.emit == 'native': - skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class closure_defargs del_deref del_local fun3 fun_calldblstar fun_callstar fun_callstardblstar fun_defargs fun_defargs2 fun_kwargs fun_kwonly fun_kwonlydef fun_kwvarargs fun_varargs gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_throw generator1 generator2 generator_args generator_close generator_closure generator_exc generator_return generator_send globals_del string_format string_join subclass_native2_list subclass_native2_tuple try_finally_loops try_finally_return try_reraise try_reraise2 unboundlocal with1 with_break with_continue with_return'.split()}) + skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_iter gen_yield_from_send gen_yield_from_throw generator1 generator2 generator_args generator_close generator_closure generator_exc generator_return generator_send'.split()}) # require yield + skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join'.split()}) # require yield + skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs + skip_tests.update({'basics/%s.py' % t for t in 'with1 with_break with_continue with_return'.split()}) # require with skip_tests.add('basics/array_construct2.py') # requires generators skip_tests.add('basics/bool1.py') # seems to randomly fail - skip_tests.add('basics/boundmeth1.py') # requires support for many args - skip_tests.add('basics/closure_manyvars.py') # requires closures - skip_tests.add('float/string_format.py') - skip_tests.add('import/gen_context.py') - skip_tests.add('io/file_with.py') - skip_tests.add('io/stringio_with.py') - skip_tests.add('micropython/heapalloc.py') - skip_tests.add('misc/features.py') - skip_tests.add('misc/recursion.py') - skip_tests.add('misc/rge_sm.py') + skip_tests.add('basics/del_deref.py') # requires checking for unbound local + skip_tests.add('basics/del_local.py') # requires checking for unbound local + skip_tests.add('basics/try_finally_loops.py') # requires proper try finally code + skip_tests.add('basics/try_finally_return.py') # requires proper try finally code + skip_tests.add('basics/unboundlocal.py') # requires checking for unbound local + skip_tests.add('import/gen_context.py') # requires yield_value + skip_tests.add('io/file_with.py') # requires with + skip_tests.add('io/stringio_with.py') # requires with + skip_tests.add('misc/features.py') # requires raise_varargs + skip_tests.add('misc/rge_sm.py') # requires yield skip_tests.add('misc/print_exception.py') # because native doesn't have proper traceback info for test_file in tests: