From e0bed553060e67f9c894cb124666824444f65581 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 15 Nov 2015 18:55:48 +0000 Subject: [PATCH 1/8] py: For native emitter, make pointers in function table constant. --- py/asmarm.c | 2 +- py/asmarm.h | 2 +- py/asmthumb.c | 2 +- py/asmthumb.h | 2 +- py/asmx64.c | 2 +- py/asmx64.h | 2 +- py/asmx86.c | 2 +- py/asmx86.h | 2 +- py/nativeglue.c | 2 +- py/runtime0.h | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/py/asmarm.c b/py/asmarm.c index 552fdfb344325..f34ba5a2baf22 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -350,7 +350,7 @@ void asm_arm_b_label(asm_arm_t *as, uint label) { asm_arm_bcc_label(as, ASM_ARM_CC_AL, label); } -void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp) { +void asm_arm_bl_ind(asm_arm_t *as, const void *fun_ptr, uint fun_id, uint reg_temp) { // If the table offset fits into the ldr instruction if (fun_id < (0x1000 / 4)) { emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_LR, ASM_ARM_REG_PC)); // mov lr, pc diff --git a/py/asmarm.h b/py/asmarm.h index a302b159052d1..96fcc813a81f4 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -120,7 +120,7 @@ void asm_arm_pop(asm_arm_t *as, uint reglist); // control flow void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label); void asm_arm_b_label(asm_arm_t *as, uint label); -void asm_arm_bl_ind(asm_arm_t *as, void *fun_ptr, uint fun_id, uint reg_temp); +void asm_arm_bl_ind(asm_arm_t *as, const void *fun_ptr, uint fun_id, uint reg_temp); #if GENERIC_ASM_API diff --git a/py/asmthumb.c b/py/asmthumb.c index 5316a7efb2336..eb26dc912b4f3 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -356,7 +356,7 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { #define OP_BLX(reg) (0x4780 | ((reg) << 3)) #define OP_SVC(arg) (0xdf00 | (arg)) -void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp) { +void asm_thumb_bl_ind(asm_thumb_t *as, const void *fun_ptr, uint fun_id, uint reg_temp) { /* TODO make this use less bytes uint rlo_base = ASM_THUMB_REG_R3; uint rlo_dest = ASM_THUMB_REG_R7; diff --git a/py/asmthumb.h b/py/asmthumb.h index 7070e03ac254b..e8bb5fa93c20f 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -235,7 +235,7 @@ void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch -void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp); // convenience +void asm_thumb_bl_ind(asm_thumb_t *as, const void *fun_ptr, uint fun_id, uint reg_temp); // convenience #if GENERIC_ASM_API diff --git a/py/asmx64.c b/py/asmx64.c index aa2a8ec7cc1da..aba1df80c2922 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -612,7 +612,7 @@ void asm_x64_call_i1(asm_x64_t *as, void* func, int i1) { } */ -void asm_x64_call_ind(asm_x64_t *as, void *ptr, int temp_r64) { +void asm_x64_call_ind(asm_x64_t *as, const void *ptr, int temp_r64) { assert(temp_r64 < 8); #ifdef __LP64__ asm_x64_mov_i64_to_r64_optimised(as, (int64_t)ptr, temp_r64); diff --git a/py/asmx64.h b/py/asmx64.h index 425bdf2d3971d..3b1a1e0a485b7 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -112,7 +112,7 @@ void asm_x64_exit(asm_x64_t* as); void asm_x64_mov_local_to_r64(asm_x64_t* as, int src_local_num, int dest_r64); void asm_x64_mov_r64_to_local(asm_x64_t* as, int src_r64, int dest_local_num); void asm_x64_mov_local_addr_to_r64(asm_x64_t* as, int local_num, int dest_r64); -void asm_x64_call_ind(asm_x64_t* as, void* ptr, int temp_r32); +void asm_x64_call_ind(asm_x64_t* as, const void* ptr, int temp_r32); #if GENERIC_ASM_API diff --git a/py/asmx86.c b/py/asmx86.c index 6a78fbd5ea4ef..3f8d1f640bbb8 100644 --- a/py/asmx86.c +++ b/py/asmx86.c @@ -469,7 +469,7 @@ void asm_x86_push_local_addr(asm_x86_t *as, int local_num, int temp_r32) } #endif -void asm_x86_call_ind(asm_x86_t *as, void *ptr, mp_uint_t n_args, int temp_r32) { +void asm_x86_call_ind(asm_x86_t *as, const void *ptr, mp_uint_t n_args, int temp_r32) { // TODO align stack on 16-byte boundary before the call assert(n_args <= 5); if (n_args > 4) { diff --git a/py/asmx86.h b/py/asmx86.h index 0a00e2e7c2c35..d65d4406faf19 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -110,7 +110,7 @@ void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32); void asm_x86_mov_local_to_r32(asm_x86_t* as, int src_local_num, int dest_r32); void asm_x86_mov_r32_to_local(asm_x86_t* as, int src_r32, int dest_local_num); void asm_x86_mov_local_addr_to_r32(asm_x86_t* as, int local_num, int dest_r32); -void asm_x86_call_ind(asm_x86_t* as, void* ptr, mp_uint_t n_args, int temp_r32); +void asm_x86_call_ind(asm_x86_t* as, const void* ptr, mp_uint_t n_args, int temp_r32); #if GENERIC_ASM_API diff --git a/py/nativeglue.c b/py/nativeglue.c index e954234c2708a..77a2dd507de2a 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -125,7 +125,7 @@ STATIC mp_obj_t mp_native_iternext(mp_obj_iter_buf_t *iter) { } // these must correspond to the respective enum in runtime0.h -void *const mp_fun_table[MP_F_NUMBER_OF] = { +const void *const mp_fun_table[MP_F_NUMBER_OF] = { mp_convert_obj_to_native, mp_convert_native_to_obj, mp_load_name, diff --git a/py/runtime0.h b/py/runtime0.h index 54bf192d33aa2..aaa3f2bd5bbcf 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -174,6 +174,6 @@ typedef enum { MP_F_NUMBER_OF, } mp_fun_kind_t; -extern void *const mp_fun_table[MP_F_NUMBER_OF]; +extern const void *const mp_fun_table[MP_F_NUMBER_OF]; #endif // MICROPY_INCLUDED_PY_RUNTIME0_H From a43b4ac98adaa9f31bd0a09005880b028369eab4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Nov 2015 15:33:53 +0000 Subject: [PATCH 2/8] py: Add MICROPY_PERSISTENT_NATIVE option for dynamic loadable modules. With this option enabled MicroPython supports loading of .mpy files that contain code compiled directly from C (ie dynamic loadable modules). Position independent code is enabled by having 2 "link" tables: one for runtime functions and constants, and one for qstrs that are local to the loaded code. The runtime function table is shared with that used by the native emitter. The qstr table needs to be populated by the loaded module on loading of this module. --- py/emitglue.c | 17 ++++++ py/emitglue.h | 41 ++++++++++++++ py/mpconfig.h | 7 ++- py/nativeglue.c | 23 +++++++- py/obj.h | 1 + py/objfun.c | 90 ++++++++++++++++++++++++++++++ py/persistentcode.c | 47 +++++++++++++++- py/persistnative.h | 132 ++++++++++++++++++++++++++++++++++++++++++++ py/runtime0.h | 9 +++ tools/mpy-tool.py | 3 + 10 files changed, 366 insertions(+), 4 deletions(-) create mode 100644 py/persistnative.h diff --git a/py/emitglue.c b/py/emitglue.c index d2add988f22ab..a5b102b1cdf11 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -113,6 +113,17 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void } #endif +#if MICROPY_PERSISTENT_NATIVE +void mp_emit_glue_assign_persistent_native(mp_raw_code_t *rc, const void *fun_data, + mp_persistent_native_data_t *per_nat_data, size_t n_pos_args) { + rc->kind = MP_CODE_PERSISTENT_NATIVE; + rc->scope_flags = 0; + rc->n_pos_args = n_pos_args; + rc->data.u_persistent_native.fun_data = fun_data; + rc->data.u_persistent_native.per_nat_data = per_nat_data; +} +#endif + mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args) { DEBUG_OP_printf("make_function_from_raw_code %p\n", rc); assert(rc != NULL); @@ -139,6 +150,12 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig); break; #endif + #if MICROPY_PERSISTENT_NATIVE + case MP_CODE_PERSISTENT_NATIVE: + fun = mp_obj_new_fun_persistent_native(false, rc->n_pos_args, rc->n_pos_args, + rc->data.u_persistent_native.fun_data, rc->data.u_persistent_native.per_nat_data); + break; + #endif default: // rc->kind should always be set and BYTECODE is the only remaining case assert(rc->kind == MP_CODE_BYTECODE); diff --git a/py/emitglue.h b/py/emitglue.h index 43930333d64ac..ebfaf588eb097 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -37,8 +37,19 @@ typedef enum { MP_CODE_NATIVE_PY, MP_CODE_NATIVE_VIPER, MP_CODE_NATIVE_ASM, + MP_CODE_PERSISTENT_NATIVE, } mp_raw_code_kind_t; +#if MICROPY_PERSISTENT_NATIVE +typedef struct _mp_persistent_native_data_t { + const void *fun_table; + qstr *qstr_table; + void *data; +} mp_persistent_native_data_t; + +mp_persistent_native_data_t *mp_new_persistent_native_data(size_t num_qstrs); +#endif + typedef struct _mp_raw_code_t { mp_raw_code_kind_t kind : 3; mp_uint_t scope_flags : 7; @@ -58,6 +69,12 @@ typedef struct _mp_raw_code_t { const mp_uint_t *const_table; mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc } u_native; + #if MICROPY_PERSISTENT_NATIVE + struct { + const void *fun_data; + mp_persistent_native_data_t *per_nat_data; + } u_persistent_native; + #endif } data; } mp_raw_code_t; @@ -70,8 +87,32 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t #endif 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, const mp_uint_t *const_table, mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig); +#if MICROPY_PERSISTENT_NATIVE +void mp_emit_glue_assign_persistent_native(mp_raw_code_t *rc, const void *fun_data, mp_persistent_native_data_t *per_nat_data, size_t n_pos_args); +#endif mp_obj_t mp_make_function_from_raw_code(const 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(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); +#if MICROPY_PERSISTENT_CODE + +#define MP_PERSISTENT_ARCH_X86 (1) +#define MP_PERSISTENT_ARCH_X64 (2) +#define MP_PERSISTENT_ARCH_THUMB2 (3) +#define MP_PERSISTENT_ARCH_ARM (4) + +#if defined(__i386__) +#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_X86) +#elif defined(__x86_64__) +#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_X64) +#elif defined(__thumb2__) +#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_THUMB2) +#elif defined(__arm__) +#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_ARM) +#else +#error unknown machine architecture +#endif + +#endif // MICROPY_PERSISTENT_CODE + #endif // MICROPY_INCLUDED_PY_EMITGLUE_H diff --git a/py/mpconfig.h b/py/mpconfig.h index b93f851d6c449..74dba77e222a6 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -249,7 +249,12 @@ // Whether generated code can persist independently of the VM/runtime instance // This is enabled automatically when needed by other features #ifndef MICROPY_PERSISTENT_CODE -#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY) +#define MICROPY_PERSISTENT_CODE (MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE || MICROPY_MODULE_FROZEN_MPY || MICROPY_PERSISTENT_NATIVE) +#endif + +// Whether to support persistent native code +#ifndef MICROPY_PERSISTENT_NATIVE +#define MICROPY_PERSISTENT_NATIVE (0) #endif // Whether to emit x64 native code diff --git a/py/nativeglue.c b/py/nativeglue.c index 77a2dd507de2a..c27490a3ae79c 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -40,7 +40,7 @@ #define DEBUG_printf(...) (void)0 #endif -#if MICROPY_EMIT_NATIVE +#if MICROPY_EMIT_NATIVE || MICROPY_PERSISTENT_NATIVE // convert a MicroPython object to a valid native value based on type mp_uint_t mp_convert_obj_to_native(mp_obj_t obj, mp_uint_t type) { @@ -172,12 +172,31 @@ const void *const mp_fun_table[MP_F_NUMBER_OF] = { mp_obj_new_cell, mp_make_closure_from_raw_code, mp_setup_code_state, +#if MICROPY_PERSISTENT_NATIVE + qstr_from_str, + qstr_from_strn, + mp_obj_new_fun_persistent_native, + &mp_const_none_obj, + &mp_const_false_obj, + &mp_const_true_obj, + &mp_const_ellipsis_obj, +#endif }; +#if MICROPY_PERSISTENT_NATIVE +mp_persistent_native_data_t *mp_new_persistent_native_data(size_t num_qstrs) { + mp_persistent_native_data_t *data = m_new_obj(mp_persistent_native_data_t); + data->fun_table = mp_fun_table; + data->qstr_table = m_new0(qstr, num_qstrs); + data->data = NULL; + return data; +} +#endif + /* void mp_f_vector(mp_fun_kind_t fun_kind) { (mp_f_table[fun_kind])(); } */ -#endif // MICROPY_EMIT_NATIVE +#endif // MICROPY_EMIT_NATIVE || MICROPY_PERSISTENT_NATIVE diff --git a/py/obj.h b/py/obj.h index 77f0f229850b8..5dff72e1d09e4 100644 --- a/py/obj.h +++ b/py/obj.h @@ -655,6 +655,7 @@ mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte * mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table); mp_obj_t mp_obj_new_fun_viper(size_t n_args, void *fun_data, mp_uint_t type_sig); mp_obj_t mp_obj_new_fun_asm(size_t n_args, void *fun_data, mp_uint_t type_sig); +mp_obj_t mp_obj_new_fun_persistent_native(bool is_kw, mp_uint_t n_args_min, mp_uint_t n_args_max, const void *fun_data, void *per_nat_data); mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun); mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed); mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items); diff --git a/py/objfun.c b/py/objfun.c index 5606511d8a261..8141d304adcd5 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -571,3 +571,93 @@ mp_obj_t mp_obj_new_fun_asm(size_t n_args, void *fun_data, mp_uint_t type_sig) { } #endif // MICROPY_EMIT_INLINE_ASM + +/******************************************************************************/ +/* persistent native functions */ + +#if MICROPY_PERSISTENT_NATIVE + +#include "py/emitglue.h" + +typedef mp_obj_t (*mp_fun_per_nat_0_t)(mp_persistent_native_data_t *et); +typedef mp_obj_t (*mp_fun_per_nat_1_t)(mp_persistent_native_data_t *et, mp_obj_t); +typedef mp_obj_t (*mp_fun_per_nat_2_t)(mp_persistent_native_data_t *et, mp_obj_t, mp_obj_t); +typedef mp_obj_t (*mp_fun_per_nat_3_t)(mp_persistent_native_data_t *et, mp_obj_t, mp_obj_t, mp_obj_t); +typedef mp_obj_t (*mp_fun_per_nat_var_t)(mp_persistent_native_data_t *et, mp_uint_t n, const mp_obj_t *); +typedef mp_obj_t (*mp_fun_per_nat_kw_t)(mp_persistent_native_data_t *et, mp_uint_t n, const mp_obj_t *, mp_map_t *); + +typedef struct _mp_obj_fun_persistent_native_t { + mp_obj_base_t base; + bool is_kw : 1; + mp_uint_t n_args_min : 15; // inclusive + mp_uint_t n_args_max : 16; // inclusive + const void *fun_data; // GC must be able to trace this pointer + mp_obj_dict_t *dict_globals; + mp_persistent_native_data_t *per_nat_data; +} mp_obj_fun_persistent_native_t; + +STATIC const mp_obj_type_t mp_type_fun_persistent_native; + +STATIC mp_obj_t fun_persistent_native_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + assert(MP_OBJ_IS_TYPE(self_in, &mp_type_fun_persistent_native)); + mp_obj_fun_persistent_native_t *self = self_in; + + // check number of arguments + mp_arg_check_num(n_args, n_kw, self->n_args_min, self->n_args_max, self->is_kw); + + const void *fun = MICROPY_MAKE_POINTER_CALLABLE(self->fun_data); + + if (self->is_kw) { + // function allows keywords + + // we create a map directly from the given args array + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + + return ((mp_fun_per_nat_kw_t)fun)(self->per_nat_data, n_args, args, &kw_args); + + } else if (self->n_args_min <= 3 && self->n_args_min == self->n_args_max) { + // function requires a fixed number of arguments + + // dispatch function call + switch (self->n_args_min) { + case 0: + return ((mp_fun_per_nat_0_t)fun)(self->per_nat_data); + + case 1: + return ((mp_fun_per_nat_1_t)fun)(self->per_nat_data, args[0]); + + case 2: + return ((mp_fun_per_nat_2_t)fun)(self->per_nat_data, args[0], args[1]); + + case 3: + default: + return ((mp_fun_per_nat_3_t)fun)(self->per_nat_data, args[0], args[1], args[2]); + } + + } else { + // function takes a variable number of arguments, but no keywords + + return ((mp_fun_per_nat_var_t)fun)(self->per_nat_data, n_args, args); + } +} + +STATIC const mp_obj_type_t mp_type_fun_persistent_native = { + { &mp_type_type }, + .name = MP_QSTR_function, + .call = fun_persistent_native_call, +}; + +mp_obj_t mp_obj_new_fun_persistent_native(bool is_kw, mp_uint_t n_args_min, mp_uint_t n_args_max, const void *f, void *per_nat_data) { + mp_obj_fun_persistent_native_t *self = m_new_obj(mp_obj_fun_persistent_native_t); + self->base.type = &mp_type_fun_persistent_native; + self->is_kw = is_kw; + self->n_args_min = n_args_min; + self->n_args_max = n_args_max; + self->fun_data = f; + self->dict_globals = mp_globals_get(); + self->per_nat_data = per_nat_data; + return self; +} + +#endif // MICROPY_PERSISTENT_NATIVE diff --git a/py/persistentcode.c b/py/persistentcode.c index 2fa8c1df07edd..2011eec1fcd00 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -165,7 +165,9 @@ STATIC void load_bytecode_qstrs(mp_reader_t *reader, byte *ip, byte *ip_top) { } } -STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) { +STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader); + +STATIC mp_raw_code_t *load_raw_code_bytecode(mp_reader_t *reader) { // load bytecode size_t bc_len = read_uint(reader); byte *bytecode = m_new(byte, bc_len); @@ -209,6 +211,46 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) { return rc; } +#if MICROPY_PERSISTENT_NATIVE +mp_raw_code_t *load_raw_code_native(mp_reader_t *reader) { + byte header[11]; + read_bytes(reader, header, 11); + if (header[0] != MP_PERSISTENT_ARCH_CURRENT) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, ".mpy has wrong arch")); + } + uint num_qstrs = header[1] | (header[2] << 8); + + // load machine code + mp_uint_t len = header[3] | (header[4] << 8); + void *data; + size_t alloc; + MP_PLAT_ALLOC_EXEC(len, &data, &alloc); + read_bytes(reader, data, len); + + mp_persistent_native_data_t *per_nat_data = mp_new_persistent_native_data(num_qstrs); + + // create raw_code and return it + mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); + mp_emit_glue_assign_persistent_native(rc, data, per_nat_data, 0); + return rc; +} +#endif + +STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) { + uint code_type = read_byte(reader); + + if (code_type == MP_CODE_BYTECODE) { + return load_raw_code_bytecode(reader); + #if MICROPY_PERSISTENT_NATIVE + } else if (code_type == MP_CODE_PERSISTENT_NATIVE) { + return load_raw_code_native(reader); + #endif + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "unsupported .mpy feature")); + } +} + mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { byte header[4]; read_bytes(reader, header, sizeof(header)); @@ -322,6 +364,9 @@ STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc) { mp_raise_ValueError("can only save bytecode"); } + byte code_type = MP_CODE_BYTECODE; + mp_print_bytes(print, &code_type, 1); + // save bytecode mp_print_uint(print, rc->data.u_byte.bc_len); mp_print_bytes(print, rc->data.u_byte.bytecode, rc->data.u_byte.bc_len); diff --git a/py/persistnative.h b/py/persistnative.h new file mode 100644 index 0000000000000..4f9120c1ddff4 --- /dev/null +++ b/py/persistnative.h @@ -0,0 +1,132 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef __MICROPY_INCLUDED_PY_PERSISTNATIVE_H__ +#define __MICROPY_INCLUDED_PY_PERSISTNATIVE_H__ + +#include "py/mpconfig.h" +#include "py/runtime0.h" +#include "py/runtime.h" +#include "py/emitglue.h" + +// Macros to make it easy to write persistent native code. The intention is +// that these macros can be redefined if such code is to be compiled statically +// into the executable. +#define CONTEXT_ALONE mp_persistent_native_data_t *pnd +#define CONTEXT mp_persistent_native_data_t *pnd, +#define QSTR(id) (pnd->qstr_table[MP_LOCAL_QSTR_ ## id]) +#define RT(id) ((mp_fun_table_t*)pnd->fun_table)->id +#define CONST(id) ((mp_obj_t)((mp_fun_table_t*)pnd->fun_table)->mp_const ## id ## _obj) +#define MAKE_FUN_0(fun_name) RT(mp_obj_new_fun_extern)(false, 0, 0, fun_name, pnd) +#define MAKE_FUN_1(fun_name) RT(mp_obj_new_fun_extern)(false, 1, 1, fun_name, pnd) +#define MAKE_FUN_2(fun_name) RT(mp_obj_new_fun_extern)(false, 2, 2, fun_name, pnd) +#define MAKE_FUN_3(fun_name) RT(mp_obj_new_fun_extern)(false, 3, 3, fun_name, pnd) +#define MAKE_FUN_VAR(mn, fun_name) RT(mp_obj_new_fun_extern)(false, mn, MP_OBJ_FUN_ARGS_MAX, fun_name, pnd) +#define MAKE_FUN_VAR_BETWEEN(mn, mx, fun_name) RT(mp_obj_new_fun_extern)(false, mn, MP_OBJ_FUN_ARGS_MAX, fun_name, pnd) +#define MAKE_FUN_KW(mn, fun_name) RT(mp_obj_new_fun_extern)(true, mn, MP_OBJ_FUN_ARGS_MAX, fun_name, pnd) + +// The linker likes to align the text following this header, so we make +// the header a nice multiple of 8 to prevent padding being inserted. +// TODO: consolidate the first 4 bytes here with stuff in persistentcode.c +#define MP_PERSISTENT_NATIVE_HEADER \ + __attribute__((section(".mpyheader"))) \ + const byte header[16] = { \ + 'M', \ + 2, \ + ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \ + | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1), \ + 16, \ + MP_CODE_PERSISTENT_NATIVE, \ + MP_PERSISTENT_ARCH_CURRENT, \ + (MP_LOCAL_QSTR_number_of & 0xff), (MP_LOCAL_QSTR_number_of >> 8), \ + 0, 0, /* size of text section, to be patched later */ \ + 0, 0, 0, 0, 0, 0, /* padding */ \ + }; + +#define MP_PERSISTENT_NATIVE_INIT \ + __attribute__((section(".mpytext"))) + +struct _mp_raw_code_t; +struct _mp_code_state; + +typedef struct _mp_fun_table_t { + mp_uint_t (*mp_convert_obj_to_native)(mp_obj_t obj, mp_uint_t type); + mp_obj_t (*mp_convert_native_to_obj)(mp_uint_t val, mp_uint_t type); + mp_obj_t (*mp_load_name)(qstr qst); + mp_obj_t (*mp_load_global)(qstr qst); + mp_obj_t (*mp_load_build_class)(void); + mp_obj_t (*mp_load_attr)(mp_obj_t base, qstr attr); + void (*mp_load_method)(mp_obj_t base, qstr attr, mp_obj_t *dest); + void (*mp_load_super_method)(qstr attr, mp_obj_t *dest); + void (*mp_store_name)(qstr qst, mp_obj_t obj); + void (*mp_store_global)(qstr qst, mp_obj_t obj); + void (*mp_store_attr)(mp_obj_t base, qstr attr, mp_obj_t value); + mp_obj_t (*mp_obj_subscr)(mp_obj_t base, mp_obj_t index, mp_obj_t val); + bool (*mp_obj_is_true)(mp_obj_t arg); + mp_obj_t (*mp_unary_op)(mp_uint_t op, mp_obj_t arg); + mp_obj_t (*mp_binary_op)(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs); + mp_obj_t (*mp_obj_new_tuple)(mp_uint_t n, const mp_obj_t *items); + mp_obj_t (*mp_obj_new_list)(mp_uint_t n, mp_obj_t *items); + mp_obj_t (*mp_obj_list_append)(mp_obj_t self_in, mp_obj_t arg); + mp_obj_t (*mp_obj_new_dict)(mp_uint_t n_args); + mp_obj_t (*mp_obj_dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value); +#if MICROPY_PY_BUILTINS_SET + mp_obj_t (*mp_obj_new_set)(mp_uint_t n_args, mp_obj_t *items); + void (*mp_obj_set_store)(mp_obj_t self_in, mp_obj_t item); +#endif + mp_obj_t (*mp_make_function_from_raw_code)(struct _mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args); + mp_obj_t (*mp_native_call_function_n_kw)(mp_obj_t fun_in, mp_uint_t n_args_kw, const mp_obj_t *args); + mp_obj_t (*mp_call_method_n_kw)(mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args); + mp_obj_t (*mp_call_method_n_kw_var)(bool have_self, mp_uint_t n_args_n_kw, const mp_obj_t *args); + mp_obj_t (*mp_getiter)(mp_obj_t o_in); + mp_obj_t (*mp_iternext)(mp_obj_t o_in); + unsigned int (*nlr_push)(nlr_buf_t *); + void (*nlr_pop)(void); + void (*mp_native_raise)(mp_obj_t o); + mp_obj_t (*mp_import_name)(qstr name, mp_obj_t fromlist, mp_obj_t level); + mp_obj_t (*mp_import_from)(mp_obj_t module, qstr name); + void (*mp_import_all)(mp_obj_t module); +#if MICROPY_PY_BUILTINS_SLICE + mp_obj_t (*mp_obj_new_slice)(mp_obj_t start, mp_obj_t stop, mp_obj_t step); +#endif + void (*mp_unpack_sequence)(mp_obj_t seq_in, mp_uint_t num, mp_obj_t *items); + void (*mp_unpack_ex)(mp_obj_t seq_in, mp_uint_t num_in, mp_obj_t *items); + void (*mp_delete_name)(qstr qst); + void (*mp_delete_global)(qstr qst); + mp_obj_t (*mp_obj_new_cell)(mp_obj_t obj); + mp_obj_t (*mp_make_closure_from_raw_code)(struct _mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args); + void (*mp_setup_code_state)(struct _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); + qstr (*qstr_from_str)(const char *str); + qstr (*qstr_from_strn)(const char *str, mp_uint_t len); + mp_obj_t (*mp_obj_new_fun_extern)(bool is_kw, mp_uint_t n_args_min, mp_uint_t n_args_max, void *f, void *per_nat_data); + mp_const_obj_t mp_const_none_obj; + mp_const_obj_t mp_const_false_obj; + mp_const_obj_t mp_const_true_obj; + mp_const_obj_t mp_const_ellipsis_obj; + // probably need to add: malloc, realloc, mp_get_buffer + // could add port specific functions here +} mp_fun_table_t; + +#endif // __MICROPY_INCLUDED_PY_PERSISTNATIVE_H__ diff --git a/py/runtime0.h b/py/runtime0.h index aaa3f2bd5bbcf..484a4c6064532 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -171,6 +171,15 @@ typedef enum { MP_F_NEW_CELL, MP_F_MAKE_CLOSURE_FROM_RAW_CODE, MP_F_SETUP_CODE_STATE, +#if MICROPY_PERSISTENT_NATIVE + MP_F_QSTR_FROM_STR, + MP_F_QSTR_FROM_STRN, + MP_F_NEW_FUN_EXTERN, + MP_F_CONST_NONE, + MP_F_CONST_FALSE, + MP_F_CONST_TRUE, + MP_F_CONST_ELLIPSIS, +#endif MP_F_NUMBER_OF, } mp_fun_kind_t; diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index ded9624878909..09889450a43dd 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -426,6 +426,9 @@ def read_bytecode_qstrs(file, bytecode, ip): ip += sz def read_raw_code(f): + code_type = f.read(1)[0] + if code_type != 2: # must be bytecode + raise Exception('unsupported code type: %u' % code_type) bc_len = read_uint(f) bytecode = bytearray(f.read(bc_len)) ip, ip2, prelude = extract_prelude(bytecode) From be3325e7e1f7c14d1d7d1ade47f9bbb2ed1f999c Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Nov 2015 15:42:39 +0000 Subject: [PATCH 3/8] example/modx: Add example of persistent native module. To build just type "make". To build for Thumb2 target, use "make CROSS=1". Then modx.mpy is ready for importing. --- examples/modx/Makefile | 25 ++++++++ examples/modx/elftompy.py | 24 ++++++++ examples/modx/genhdr/qstrdefs.generated.h | 0 examples/modx/modx.c | 53 +++++++++++++++++ examples/modx/mpconfigport.h | 72 +++++++++++++++++++++++ examples/modx/persistnative.ld | 16 +++++ 6 files changed, 190 insertions(+) create mode 100644 examples/modx/Makefile create mode 100644 examples/modx/elftompy.py create mode 100644 examples/modx/genhdr/qstrdefs.generated.h create mode 100644 examples/modx/modx.c create mode 100644 examples/modx/mpconfigport.h create mode 100644 examples/modx/persistnative.ld diff --git a/examples/modx/Makefile b/examples/modx/Makefile new file mode 100644 index 0000000000000..8960515eebd6c --- /dev/null +++ b/examples/modx/Makefile @@ -0,0 +1,25 @@ +include ../../py/mkenv.mk + +CROSS = 0 + +ifeq ($(CROSS), 1) +CROSS_COMPILE = arm-none-eabi- +endif + +INC = -I. -I../.. + +CFLAGS = $(INC) -Wall -Werror -ansi -std=gnu99 -fPIC -fno-stack-protector -Os + +ifeq ($(CROSS), 1) +CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mabi=aapcs-linux -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -fsingle-precision-constant -Wdouble-promotion +CFLAGS += -nostdlib $(CFLAGS_CORTEX_M4) +endif + +all: modx.mpy + +modx.mpy: modx.elf + $(OBJCOPY) -O binary -j .all $< $@ + $(PYTHON) elftompy.py $@ + +modx.elf: modx.o + $(LD) -T persistnative.ld -o $@ $< diff --git a/examples/modx/elftompy.py b/examples/modx/elftompy.py new file mode 100644 index 0000000000000..8785c029cf927 --- /dev/null +++ b/examples/modx/elftompy.py @@ -0,0 +1,24 @@ +""" +This tool is ultimately intended to convert a .elf to a .mpy file. +But for now all it does is patch the text size within a .mpy file. + +Usage (after building modx.mpy): + + $ python elftompy.py modx.mpy + +This will make modx.mpy ready for importing. +""" + +import sys + +def process_file(filename): + with open(filename, 'rb') as f: + data = bytearray(f.read()) + size = len(data) - 16 + data[8] = size & 0xff + data[9] = size >> 8 + with open(filename, 'wb') as f: + f.write(data) + +if __name__ == '__main__': + process_file(sys.argv[1]) diff --git a/examples/modx/genhdr/qstrdefs.generated.h b/examples/modx/genhdr/qstrdefs.generated.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/examples/modx/modx.c b/examples/modx/modx.c new file mode 100644 index 0000000000000..359b3980845f0 --- /dev/null +++ b/examples/modx/modx.c @@ -0,0 +1,53 @@ +#include "py/persistnative.h" + +#define QSTR_DEFINES \ + QDEF(VAL1, "VAL1") \ + QDEF(VAL2, "VAL2") \ + QDEF(VAL3, "VAL3") \ + QDEF(add1, "add1") \ + QDEF(make_list, "make_list") \ + +enum { + #define QDEF(id, str) MP_LOCAL_QSTR_ ## id, + QSTR_DEFINES + #undef QDEF + MP_LOCAL_QSTR_number_of, +}; + +STATIC mp_obj_t modx_add1(CONTEXT mp_obj_t x) { + return RT(mp_binary_op)(MP_BINARY_OP_ADD, x, MP_OBJ_NEW_SMALL_INT(1)); +} + +STATIC mp_obj_t modx_make_list(CONTEXT mp_obj_t x, mp_obj_t y) { + mp_obj_t list[6] = { + MP_OBJ_NEW_SMALL_INT(1), + MP_OBJ_NEW_SMALL_INT(2), + CONST(_ellipsis), + MP_OBJ_NEW_QSTR(QSTR(make_list)), + x, + y, + }; + return RT(mp_obj_new_list)(6, list); +} + +MP_PERSISTENT_NATIVE_HEADER + +MP_PERSISTENT_NATIVE_INIT +void init(CONTEXT_ALONE) { + // create qstrs + { + qstr *q = pnd->qstr_table; + #define QDEF(id, str) *q++ = RT(qstr_from_str)(str); + QSTR_DEFINES + #undef QDEF + } + + // constants + RT(mp_store_global)(QSTR(VAL1), CONST(_true)); + RT(mp_store_global)(QSTR(VAL2), MP_OBJ_NEW_SMALL_INT(123)); + RT(mp_store_global)(QSTR(VAL3), MP_OBJ_NEW_QSTR(QSTR(VAL3))); + + // functions + RT(mp_store_global)(QSTR(add1), MAKE_FUN_1(modx_add1)); + RT(mp_store_global)(QSTR(make_list), MAKE_FUN_2(modx_make_list)); +} diff --git a/examples/modx/mpconfigport.h b/examples/modx/mpconfigport.h new file mode 100644 index 0000000000000..2d1ceea4ed268 --- /dev/null +++ b/examples/modx/mpconfigport.h @@ -0,0 +1,72 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +// options to control how Micro Python is built + +#define MICROPY_ALLOC_PATH_MAX (PATH_MAX) +#define MICROPY_PERSISTENT_NATIVE (1) +#define MICROPY_COMP_MODULE_CONST (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_MEM_STATS (1) +#define MICROPY_DEBUG_PRINTERS (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_HELPER_LEXER_UNIX (1) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_STREAMS_NON_BLOCK (1) +#define MICROPY_OPT_COMPUTED_GOTO (1) +#define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (1) +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_MODULE_EXTERN (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_COMPILE (1) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_SYS_EXIT (1) +#define MICROPY_PY_SYS_PLATFORM "linux" +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_STDFILES (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_GC_COLLECT_RETVAL (1) + +// type definitions for the specific machine + +typedef intptr_t mp_int_t; +typedef uintptr_t mp_uint_t; + +// Cannot include , as it may lead to symbol name clashes +#if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) +typedef long long mp_off_t; +#else +typedef long mp_off_t; +#endif diff --git a/examples/modx/persistnative.ld b/examples/modx/persistnative.ld new file mode 100644 index 0000000000000..7d21b69c7c302 --- /dev/null +++ b/examples/modx/persistnative.ld @@ -0,0 +1,16 @@ +/* + GNU linker script for MPY external modules +*/ + +SECTIONS +{ + .all : + { + . = ALIGN(4); + KEEP(*(.mpyheader)) + KEEP(*(.mpytext)) + *(.text*) + *(.rodata*) + . = ALIGN(4); + } +} From b02cecca8175e0889d0f66f4a46c860214c97be8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 16 Nov 2015 15:43:53 +0000 Subject: [PATCH 4/8] unix: Enable loading of persistent native .mpy modules. --- ports/unix/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index b557f3d448861..dd10b382c1d13 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -28,6 +28,7 @@ #define MICROPY_ALLOC_PATH_MAX (PATH_MAX) #define MICROPY_PERSISTENT_CODE_LOAD (1) +#define MICROPY_PERSISTENT_NATIVE (1) #if !defined(MICROPY_EMIT_X64) && defined(__x86_64__) #define MICROPY_EMIT_X64 (1) #endif From b19c51c0e1e58c7d18ce7b5eaadd4accf24074fd Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 26 Sep 2017 11:16:30 +1000 Subject: [PATCH 5/8] tools/mpy-tool.py: Make it work with Python2 again. --- tools/mpy-tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 09889450a43dd..26c375e5ec4c6 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -426,7 +426,7 @@ def read_bytecode_qstrs(file, bytecode, ip): ip += sz def read_raw_code(f): - code_type = f.read(1)[0] + code_type = bytes_cons(f.read(1))[0] if code_type != 2: # must be bytecode raise Exception('unsupported code type: %u' % code_type) bc_len = read_uint(f) From f87506b50824bcd8e8136689b8f77cebfc6a81b6 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 16 Oct 2017 05:20:05 +0200 Subject: [PATCH 6/8] examples/modx: Make the modx example work with the Cortex M0 --- examples/modx/Makefile | 35 +++++++++++++++++++++++++---------- py/emitglue.h | 16 +++++++++++----- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/examples/modx/Makefile b/examples/modx/Makefile index 8960515eebd6c..57ea646126832 100644 --- a/examples/modx/Makefile +++ b/examples/modx/Makefile @@ -1,25 +1,40 @@ include ../../py/mkenv.mk -CROSS = 0 - -ifeq ($(CROSS), 1) -CROSS_COMPILE = arm-none-eabi- -endif - INC = -I. -I../.. CFLAGS = $(INC) -Wall -Werror -ansi -std=gnu99 -fPIC -fno-stack-protector -Os -ifeq ($(CROSS), 1) +ARCH = native + +ifeq ($(ARCH), m4) +CROSS_COMPILE = arm-none-eabi- CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mabi=aapcs-linux -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -fsingle-precision-constant -Wdouble-promotion CFLAGS += -nostdlib $(CFLAGS_CORTEX_M4) +else ifeq ($(ARCH), m0) +CROSS_COMPILE = arm-none-eabi- +CFLAGS_CORTEX_M0 = -mthumb --short-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft -fno-builtin +CFLAGS += -nostdlib $(CFLAGS_CORTEX_M0) +else ifeq ($(ARCH), native) +# we're not cross compiling +ARCH = native +else +$(error Unknown architecture) endif -all: modx.mpy +BUILD=build-$(ARCH) + +all: $(BUILD)/modx.mpy -modx.mpy: modx.elf +clean: + $(RM) -rf $(BUILD) + +$(BUILD)/modx.mpy: $(BUILD)/modx.elf $(OBJCOPY) -O binary -j .all $< $@ $(PYTHON) elftompy.py $@ -modx.elf: modx.o +$(BUILD)/modx.elf: $(BUILD)/modx.o $(LD) -T persistnative.ld -o $@ $< + +$(BUILD)/modx.o: modx.c + $(MKDIR) -p $(BUILD) + $(CC) $(CFLAGS) -o $@ -c $< diff --git a/py/emitglue.h b/py/emitglue.h index ebfaf588eb097..79c5ac59e7ed9 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -98,16 +98,22 @@ mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_clos #define MP_PERSISTENT_ARCH_X86 (1) #define MP_PERSISTENT_ARCH_X64 (2) -#define MP_PERSISTENT_ARCH_THUMB2 (3) -#define MP_PERSISTENT_ARCH_ARM (4) +#define MP_PERSISTENT_ARCH_M0 (3) +#define MP_PERSISTENT_ARCH_M4 (4) +#define MP_PERSISTENT_ARCH_ARM (5) + +// Reference for GCC defines: +// http://micro-os-plus.github.io/develop/predefined-macros/ #if defined(__i386__) #define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_X86) #elif defined(__x86_64__) #define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_X64) -#elif defined(__thumb2__) -#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_THUMB2) -#elif defined(__arm__) +#elif defined(__ARM_ARCH_6M__) +#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_M0) +#elif defined(__ARM_ARCH_7EM__) +#define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_M4) +#elif defined(__arm__) && !defined(__thumb__) #define MP_PERSISTENT_ARCH_CURRENT (MP_PERSISTENT_ARCH_ARM) #else #error unknown machine architecture From b7efae3b2daf84809ee24451590fb4f1920a2123 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 22 Oct 2017 16:44:16 +0200 Subject: [PATCH 7/8] examples/modx: Ignore build directories. --- examples/modx/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 examples/modx/.gitignore diff --git a/examples/modx/.gitignore b/examples/modx/.gitignore new file mode 100644 index 0000000000000..414487d53eb83 --- /dev/null +++ b/examples/modx/.gitignore @@ -0,0 +1 @@ +build-*/ From a85c7804105506c8eebb7f824c5415720d1ea6d6 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 1 Nov 2017 21:48:13 +0100 Subject: [PATCH 8/8] py/nativeglue: Fix MICROPY_PERSISTENT_NATIVE without MICROPY_EMIT_NATIVE Smaller microcontrollers may want the former without the latter. --- py/nativeglue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/nativeglue.c b/py/nativeglue.c index c27490a3ae79c..80168a9bf7450 100644 --- a/py/nativeglue.c +++ b/py/nativeglue.c @@ -64,7 +64,7 @@ mp_uint_t mp_convert_obj_to_native(mp_obj_t obj, mp_uint_t type) { #endif -#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM +#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM || MICROPY_PERSISTENT_NATIVE // convert a native value to a MicroPython object based on type mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) { @@ -82,7 +82,7 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) { #endif -#if MICROPY_EMIT_NATIVE +#if MICROPY_EMIT_NATIVE || MICROPY_PERSISTENT_NATIVE // wrapper that accepts n_args and n_kw in one argument // (native emitter can only pass at most 3 arguments to a function)