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-*/ diff --git a/examples/modx/Makefile b/examples/modx/Makefile new file mode 100644 index 0000000000000..57ea646126832 --- /dev/null +++ b/examples/modx/Makefile @@ -0,0 +1,40 @@ +include ../../py/mkenv.mk + +INC = -I. -I../.. + +CFLAGS = $(INC) -Wall -Werror -ansi -std=gnu99 -fPIC -fno-stack-protector -Os + +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 + +BUILD=build-$(ARCH) + +all: $(BUILD)/modx.mpy + +clean: + $(RM) -rf $(BUILD) + +$(BUILD)/modx.mpy: $(BUILD)/modx.elf + $(OBJCOPY) -O binary -j .all $< $@ + $(PYTHON) elftompy.py $@ + +$(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/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); + } +} 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 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/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..79c5ac59e7ed9 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,38 @@ 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_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(__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 +#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 e954234c2708a..80168a9bf7450 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) { @@ -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) @@ -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, @@ -172,12 +172,31 @@ 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 54bf192d33aa2..484a4c6064532 100644 --- a/py/runtime0.h +++ b/py/runtime0.h @@ -171,9 +171,18 @@ 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; -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 diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index ded9624878909..26c375e5ec4c6 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 = 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) bytecode = bytearray(f.read(bc_len)) ip, ip2, prelude = extract_prelude(bytecode)