Commit 9988618e authored by Damien George's avatar Damien George
Browse files

py: Implement full func arg passing for native emitter.

This patch gets full function argument passing working with native
emitter.  Includes named args, keyword args, default args, var args
and var keyword args.  Fully Python compliant.

It reuses the bytecode mp_setup_code_state function to do all the hard
work.  This function is slightly adjusted to accommodate native calls,
and the native emitter is forced a bit to emit similar prelude and
code-info as bytecode.
parent 18bd5170
......@@ -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;
}
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -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] =
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
}
}
......
This diff is collapsed.
......@@ -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,
};
/*
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment