From 0f3388de1ef738772de0c07e6b05969a48681d48 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Nov 2016 22:53:04 +1100 Subject: py/emitnative: Fix native import emitter when in viper mode. --- py/emitnative.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'py') diff --git a/py/emitnative.c b/py/emitnative.c index b54f263d6..46e1076a9 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -1261,11 +1261,35 @@ STATIC void emit_native_label_assign(emit_t *emit, mp_uint_t l) { STATIC void emit_native_import_name(emit_t *emit, qstr qst) { DEBUG_printf("import_name %s\n", qstr_str(qst)); - vtype_kind_t vtype_fromlist; - vtype_kind_t vtype_level; - emit_pre_pop_reg_reg(emit, &vtype_fromlist, REG_ARG_2, &vtype_level, REG_ARG_3); // arg2 = fromlist, arg3 = level - assert(vtype_fromlist == VTYPE_PYOBJ); - assert(vtype_level == VTYPE_PYOBJ); + + // get arguments from stack: arg2 = fromlist, arg3 = level + // if using viper types these arguments must be converted to proper objects + if (emit->do_viper_types) { + // fromlist should be None or a tuple + stack_info_t *top = peek_stack(emit, 0); + if (top->vtype == VTYPE_PTR_NONE) { + emit_pre_pop_discard(emit); + ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)mp_const_none, REG_ARG_2); + } else { + vtype_kind_t vtype_fromlist; + emit_pre_pop_reg(emit, &vtype_fromlist, REG_ARG_2); + assert(vtype_fromlist == VTYPE_PYOBJ); + } + + // level argument should be an immediate integer + top = peek_stack(emit, 0); + assert(top->vtype == VTYPE_INT && top->kind == STACK_IMM); + ASM_MOV_IMM_TO_REG(emit->as, (mp_uint_t)MP_OBJ_NEW_SMALL_INT(top->data.u_imm), REG_ARG_3); + emit_pre_pop_discard(emit); + + } else { + vtype_kind_t vtype_fromlist; + vtype_kind_t vtype_level; + emit_pre_pop_reg_reg(emit, &vtype_fromlist, REG_ARG_2, &vtype_level, REG_ARG_3); + assert(vtype_fromlist == VTYPE_PYOBJ); + assert(vtype_level == VTYPE_PYOBJ); + } + emit_call_with_imm_arg(emit, MP_F_IMPORT_NAME, qst, REG_ARG_1); // arg1 = import name emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET); } -- cgit v1.2.3 From 59a1201da959ab01895361c0b5bb8bc1409f2b39 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 14 Nov 2016 00:24:22 +0300 Subject: all: Remove readall() method, which is equivalent to read() w/o args. Its addition was due to an early exploration on how to add CPython-like stream interface. It's clear that it's not needed and just takes up bytes in all ports. --- cc3200/mods/modusocket.c | 3 +-- cc3200/mods/pybuart.c | 2 -- esp8266/machine_uart.c | 1 - extmod/modussl_axtls.c | 1 - extmod/modussl_mbedtls.c | 1 - extmod/moduzlib.c | 1 - extmod/vfs_fat_file.c | 1 - py/stream.c | 1 - py/stream.h | 1 - stmhal/pybstdio.c | 1 - stmhal/uart.c | 4 +--- stmhal/usb.c | 1 - unix/file.c | 1 - unix/modsocket.c | 1 - 14 files changed, 2 insertions(+), 18 deletions(-) (limited to 'py') diff --git a/cc3200/mods/modusocket.c b/cc3200/mods/modusocket.c index 81a4c2e19..44e7f7f42 100644 --- a/cc3200/mods/modusocket.c +++ b/cc3200/mods/modusocket.c @@ -434,7 +434,6 @@ STATIC const mp_map_elem_t socket_locals_dict_table[] = { // stream methods { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj }, @@ -446,7 +445,7 @@ STATIC mp_uint_t socket_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *e mod_network_socket_obj_t *self = self_in; mp_int_t ret = wlan_socket_recv(self, buf, size, errcode); if (ret < 0) { - // we need to ignore the socket closed error here because a readall() or read() without params + // we need to ignore the socket closed error here because a read() without params // only returns when the socket is closed by the other end if (*errcode != SL_ESECCLOSED) { ret = MP_STREAM_ERROR; diff --git a/cc3200/mods/pybuart.c b/cc3200/mods/pybuart.c index 493522f83..f6b917ab8 100644 --- a/cc3200/mods/pybuart.c +++ b/cc3200/mods/pybuart.c @@ -570,8 +570,6 @@ STATIC const mp_map_elem_t pyb_uart_locals_dict_table[] = { /// \method read([nbytes]) { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, - /// \method readall() - { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, /// \method readline() { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, /// \method readinto(buf[, nbytes]) diff --git a/esp8266/machine_uart.c b/esp8266/machine_uart.c index 80e10d131..9bc6422cc 100644 --- a/esp8266/machine_uart.c +++ b/esp8266/machine_uart.c @@ -197,7 +197,6 @@ STATIC const mp_rom_map_elem_t pyb_uart_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pyb_uart_init_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, diff --git a/extmod/modussl_axtls.c b/extmod/modussl_axtls.c index 5bc69fe26..416915821 100644 --- a/extmod/modussl_axtls.c +++ b/extmod/modussl_axtls.c @@ -142,7 +142,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, diff --git a/extmod/modussl_mbedtls.c b/extmod/modussl_mbedtls.c index 5a7a745d8..a7b8a1440 100644 --- a/extmod/modussl_mbedtls.c +++ b/extmod/modussl_mbedtls.c @@ -242,7 +242,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, diff --git a/extmod/moduzlib.c b/extmod/moduzlib.c index c5d4c4812..a91405a93 100644 --- a/extmod/moduzlib.c +++ b/extmod/moduzlib.c @@ -124,7 +124,6 @@ STATIC mp_uint_t decompio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er STATIC const mp_rom_map_elem_t decompio_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, }; diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 76ac23685..f2144434e 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -239,7 +239,6 @@ STATIC mp_obj_t file_obj_make_new(const mp_obj_type_t *type, size_t n_args, size STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, diff --git a/py/stream.c b/py/stream.c index dadfcf5d6..c915110e0 100644 --- a/py/stream.c +++ b/py/stream.c @@ -361,7 +361,6 @@ STATIC mp_obj_t stream_readall(mp_obj_t self_in) { vstr.len = total_size; return mp_obj_new_str_from_vstr(STREAM_CONTENT_TYPE(stream_p), &vstr); } -MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_readall_obj, stream_readall); // Unbuffered, inefficient implementation of readline() for raw I/O files. STATIC mp_obj_t stream_unbuffered_readline(size_t n_args, const mp_obj_t *args) { diff --git a/py/stream.h b/py/stream.h index 4cdea11eb..94002e252 100644 --- a/py/stream.h +++ b/py/stream.h @@ -51,7 +51,6 @@ struct mp_stream_seek_t { MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read1_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj); -MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_readall_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj); diff --git a/stmhal/pybstdio.c b/stmhal/pybstdio.c index 9cc787ce2..dec4f227a 100644 --- a/stmhal/pybstdio.c +++ b/stmhal/pybstdio.c @@ -97,7 +97,6 @@ STATIC const mp_map_elem_t stdio_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_buffer), (mp_obj_t)&stdio_buffer_obj }, #endif { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_readlines), (mp_obj_t)&mp_stream_unbuffered_readlines_obj}, diff --git a/stmhal/uart.c b/stmhal/uart.c index cbcb7c059..37c1906c1 100644 --- a/stmhal/uart.c +++ b/stmhal/uart.c @@ -60,7 +60,7 @@ /// using the standard stream methods: /// /// uart.read(10) # read 10 characters, returns a bytes object -/// uart.readall() # read all available characters +/// uart.read() # read all available characters /// uart.readline() # read a line /// uart.readinto(buf) # read and store into the given buffer /// uart.write('abc') # write the 3 characters @@ -799,8 +799,6 @@ STATIC const mp_map_elem_t pyb_uart_locals_dict_table[] = { /// \method read([nbytes]) { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, - /// \method readall() - { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, /// \method readline() { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, /// \method readinto(buf[, nbytes]) diff --git a/stmhal/usb.c b/stmhal/usb.c index 4eb67a8ca..dd6daf8e5 100644 --- a/stmhal/usb.c +++ b/stmhal/usb.c @@ -468,7 +468,6 @@ STATIC const mp_map_elem_t pyb_usb_vcp_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_usb_vcp_send_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_usb_vcp_recv_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_readall), (mp_obj_t)&mp_stream_readall_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, { MP_OBJ_NEW_QSTR(MP_QSTR_readlines), (mp_obj_t)&mp_stream_unbuffered_readlines_obj}, diff --git a/unix/file.c b/unix/file.c index 6d59a736c..96fe78c49 100644 --- a/unix/file.c +++ b/unix/file.c @@ -218,7 +218,6 @@ STATIC mp_obj_t fdfile_make_new(const mp_obj_type_t *type, size_t n_args, size_t STATIC const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&fdfile_fileno_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj) }, diff --git a/unix/modsocket.c b/unix/modsocket.c index 74d22eb73..9ca04b88b 100644 --- a/unix/modsocket.c +++ b/unix/modsocket.c @@ -349,7 +349,6 @@ STATIC const mp_rom_map_elem_t usocket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_fileno), MP_ROM_PTR(&socket_fileno_obj) }, { MP_ROM_QSTR(MP_QSTR_makefile), MP_ROM_PTR(&socket_makefile_obj) }, { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readall), MP_ROM_PTR(&mp_stream_readall_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, -- cgit v1.2.3 From c3d96d387c461c25ca3298dfa73d2a4efb45f1e7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 14 Nov 2016 02:23:30 +0300 Subject: py/objexcept: Allow clearing traceback with 'exc.__traceback__ = None'. We allow 'exc.__traceback__ = None' assignment as a low-level optimization of pre-allocating exception instance and raising it repeatedly - this avoids memory allocation during raise. However, uPy will keep adding traceback entries to such exception instance, so before throwing it, traceback should be cleared like above. 'exc.__traceback__ = None' syntax is CPython compatible. However, unlike it, reading that attribute or setting it to any other value is not supported (and not intended to be supported, again, the only reason for adding this feature is to allow zero-memalloc exception raising). --- py/objexcept.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'py') diff --git a/py/objexcept.c b/py/objexcept.c index 9ccc9288c..c1b992d27 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -152,11 +152,21 @@ mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in) { } STATIC void exception_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] != MP_OBJ_NULL) { - // not load attribute + // store/delete attribute + if (attr == MP_QSTR___traceback__ && dest[1] == mp_const_none) { + // We allow 'exc.__traceback__ = None' assignment as low-level + // optimization of pre-allocating exception instance and raising + // it repeatedly - this avoids memory allocation during raise. + // However, uPy will keep adding traceback entries to such + // exception instance, so before throwing it, traceback should + // be cleared like above. + self->traceback_len = 0; + dest[0] = MP_OBJ_NULL; // indicate success + } return; } - mp_obj_exception_t *self = MP_OBJ_TO_PTR(self_in); if (attr == MP_QSTR_args) { dest[0] = MP_OBJ_FROM_PTR(self->args); } else if (self->base.type == &mp_type_StopIteration && attr == MP_QSTR_value) { -- cgit v1.2.3 From 79d996a57b351e0ef354eb1e2f644b194433cc73 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 15 Nov 2016 01:10:34 +0300 Subject: py/runtime: mp_resume: Handle exceptions in Python __next__(). This includes StopIteration and thus are important to make Python-coded iterables work with yield from/await. Exceptions in Python send() are still not handled and left for future consideration and optimization. --- py/runtime.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'py') diff --git a/py/runtime.c b/py/runtime.c index c25557464..4bab03b80 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1191,17 +1191,31 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th mp_obj_t dest[3]; // Reserve slot for send() arg + // Python instance iterator protocol if (send_value == mp_const_none) { mp_load_method_maybe(self_in, MP_QSTR___next__, dest); if (dest[0] != MP_OBJ_NULL) { - *ret_val = mp_call_method_n_kw(0, 0, dest); - return MP_VM_RETURN_YIELD; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + *ret_val = mp_call_method_n_kw(0, 0, dest); + nlr_pop(); + return MP_VM_RETURN_YIELD; + } else { + *ret_val = nlr.ret_val; + return MP_VM_RETURN_EXCEPTION; + } } } + // Either python instance generator protocol, or native object + // generator protocol. if (send_value != MP_OBJ_NULL) { mp_load_method(self_in, MP_QSTR_send, dest); dest[2] = send_value; + // TODO: This should have exception wrapping like __next__ case + // above. Not done right away to think how to optimize native + // generators better, see: + // https://github.com/micropython/micropython/issues/2628 *ret_val = mp_call_method_n_kw(1, 0, dest); return MP_VM_RETURN_YIELD; } -- cgit v1.2.3 From a0b2c6ad32230d679260eb2a545e5272234254ff Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 15 Nov 2016 01:40:23 +0300 Subject: py/runtime: mp_resume: Fix exception handling for nanbox port. --- py/runtime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/runtime.c b/py/runtime.c index 4bab03b80..0ecccbd87 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1201,7 +1201,7 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th nlr_pop(); return MP_VM_RETURN_YIELD; } else { - *ret_val = nlr.ret_val; + *ret_val = MP_OBJ_FROM_PTR(nlr.ret_val); return MP_VM_RETURN_EXCEPTION; } } -- cgit v1.2.3 From 0400fa45bacbba0f5d0239874e4cfc379f7d9ae2 Mon Sep 17 00:00:00 2001 From: Dave Hylands Date: Mon, 14 Nov 2016 11:02:49 -0800 Subject: py/mkrules.mk: Rework find command so it works on OSX. The Mac version of find doesn't support -printf, so this changes things to use sed to strip off the leading path element instead. --- py/mkrules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/mkrules.mk b/py/mkrules.mk index ea647e86f..6f33630c9 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -108,7 +108,7 @@ endif ifneq ($(FROZEN_MPY_DIR),) # make a list of all the .py files that need compiling and freezing -FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' -printf '%P\n') +FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | sed -e 's=^$(FROZEN_MPY_DIR)/==') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) # to build .mpy files from .py files -- cgit v1.2.3 From 659b06b2501dd15d7efe7758befe30ce960f390d Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 15 Nov 2016 16:09:43 +1100 Subject: py/*.mk: Replace uses of 'sed' with $(SED). --- py/mkrules.mk | 2 +- py/py.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'py') diff --git a/py/mkrules.mk b/py/mkrules.mk index 6f33630c9..d509f7b48 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -108,7 +108,7 @@ endif ifneq ($(FROZEN_MPY_DIR),) # make a list of all the .py files that need compiling and freezing -FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | sed -e 's=^$(FROZEN_MPY_DIR)/==') +FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | $(SED) -e 's=^$(FROZEN_MPY_DIR)/==') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) # to build .mpy files from .py files diff --git a/py/py.mk b/py/py.mk index c71ab7a0c..88c4fafe9 100644 --- a/py/py.mk +++ b/py/py.mk @@ -269,7 +269,7 @@ MPCONFIGPORT_MK = $(wildcard mpconfigport.mk) # the lines in "" and then unwrap after the preprocessor is finished. $(HEADER_BUILD)/qstrdefs.generated.h: $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEFS_COLLECTED) $(PY_SRC)/makeqstrdata.py mpconfigport.h $(MPCONFIGPORT_MK) $(PY_SRC)/mpconfig.h | $(HEADER_BUILD) $(ECHO) "GEN $@" - $(Q)cat $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEFS_COLLECTED) | $(SED) 's/^Q(.*)/"&"/' | $(CPP) $(CFLAGS) - | sed 's/^"\(Q(.*)\)"/\1/' > $(HEADER_BUILD)/qstrdefs.preprocessed.h + $(Q)cat $(PY_QSTR_DEFS) $(QSTR_DEFS) $(QSTR_DEFS_COLLECTED) | $(SED) 's/^Q(.*)/"&"/' | $(CPP) $(CFLAGS) - | $(SED) 's/^"\(Q(.*)\)"/\1/' > $(HEADER_BUILD)/qstrdefs.preprocessed.h $(Q)$(PYTHON) $(PY_SRC)/makeqstrdata.py $(HEADER_BUILD)/qstrdefs.preprocessed.h > $@ # emitters -- cgit v1.2.3 From b0cbfb0492028192a28f0514fba71ec954330108 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 13 Nov 2016 15:32:05 +1100 Subject: py/parse: Move function to check for const parse node to parse.[ch]. --- py/compile.c | 26 ++++++++------------------ py/parse.c | 10 ++++++++++ py/parse.h | 2 ++ 3 files changed, 20 insertions(+), 18 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index f84d5e214..cd38b07f3 100644 --- a/py/compile.c +++ b/py/compile.c @@ -243,23 +243,13 @@ STATIC void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) c_tuple(comp, MP_PARSE_NODE_NULL, pns); } -STATIC bool node_is_const_false(mp_parse_node_t pn) { - return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE) - || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0); -} - -STATIC bool node_is_const_true(mp_parse_node_t pn) { - return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) - || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) != 0); -} - STATIC void c_if_cond(compiler_t *comp, mp_parse_node_t pn, bool jump_if, int label) { - if (node_is_const_false(pn)) { + if (mp_parse_node_is_const_false(pn)) { if (jump_if == false) { EMIT_ARG(jump, label); } return; - } else if (node_is_const_true(pn)) { + } else if (mp_parse_node_is_const_true(pn)) { if (jump_if == true) { EMIT_ARG(jump, label); } @@ -1218,14 +1208,14 @@ STATIC void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { uint l_end = comp_next_label(comp); // optimisation: don't emit anything when "if False" - if (!node_is_const_false(pns->nodes[0])) { + if (!mp_parse_node_is_const_false(pns->nodes[0])) { uint l_fail = comp_next_label(comp); c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition compile_node(comp, pns->nodes[1]); // if block // optimisation: skip everything else when "if True" - if (node_is_const_true(pns->nodes[0])) { + if (mp_parse_node_is_const_true(pns->nodes[0])) { goto done; } @@ -1250,14 +1240,14 @@ STATIC void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { mp_parse_node_struct_t *pns_elif = (mp_parse_node_struct_t*)pn_elif[i]; // optimisation: don't emit anything when "if False" - if (!node_is_const_false(pns_elif->nodes[0])) { + if (!mp_parse_node_is_const_false(pns_elif->nodes[0])) { uint l_fail = comp_next_label(comp); c_if_cond(comp, pns_elif->nodes[0], false, l_fail); // elif condition compile_node(comp, pns_elif->nodes[1]); // elif block // optimisation: skip everything else when "elif True" - if (node_is_const_true(pns_elif->nodes[0])) { + if (mp_parse_node_is_const_true(pns_elif->nodes[0])) { goto done; } @@ -1294,9 +1284,9 @@ done: STATIC void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { START_BREAK_CONTINUE_BLOCK - if (!node_is_const_false(pns->nodes[0])) { // optimisation: don't emit anything for "while False" + if (!mp_parse_node_is_const_false(pns->nodes[0])) { // optimisation: don't emit anything for "while False" uint top_label = comp_next_label(comp); - if (!node_is_const_true(pns->nodes[0])) { // optimisation: don't jump to cond for "while True" + if (!mp_parse_node_is_const_true(pns->nodes[0])) { // optimisation: don't jump to cond for "while True" EMIT_ARG(jump, continue_label); } EMIT_ARG(label_assign, top_label); diff --git a/py/parse.c b/py/parse.c index 397d46d9f..aa6034a6a 100644 --- a/py/parse.c +++ b/py/parse.c @@ -234,6 +234,16 @@ mp_parse_node_t mp_parse_node_new_leaf(size_t kind, mp_int_t arg) { return (mp_parse_node_t)(kind | (arg << 4)); } +bool mp_parse_node_is_const_false(mp_parse_node_t pn) { + return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE) + || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0); +} + +bool mp_parse_node_is_const_true(mp_parse_node_t pn) { + return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) + || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) != 0); +} + bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { *o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn)); diff --git a/py/parse.h b/py/parse.h index f518b9bb6..54bbd2645 100644 --- a/py/parse.h +++ b/py/parse.h @@ -77,6 +77,8 @@ typedef struct _mp_parse_node_struct_t { #define MP_PARSE_NODE_STRUCT_NUM_NODES(pns) ((pns)->kind_num_nodes >> 8) mp_parse_node_t mp_parse_node_new_leaf(size_t kind, mp_int_t arg); +bool mp_parse_node_is_const_false(mp_parse_node_t pn); +bool mp_parse_node_is_const_true(mp_parse_node_t pn); bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o); int mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes); void mp_parse_node_print(mp_parse_node_t pn, size_t indent); -- cgit v1.2.3 From ed9c93f0f13fe2b6b4b0fbc1155b1e38ee1be242 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 13 Nov 2016 15:35:11 +1100 Subject: py/parse: Make mp_parse_node_new_leaf an inline function. It is split into 2 functions, one to make small ints and the other to make a non-small-int leaf node. This reduces code size by 32 bytes on bare-arm, 64 bytes on unix (x64-64) and 144 bytes on stmhal. --- py/compile.c | 6 +++--- py/parse.c | 13 +++---------- py/parse.h | 7 ++++++- 3 files changed, 12 insertions(+), 14 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index cd38b07f3..6efd15eff 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1403,13 +1403,13 @@ STATIC void compile_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { if (1 <= n_args && n_args <= 3) { optimize = true; if (n_args == 1) { - pn_range_start = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 0); + pn_range_start = mp_parse_node_new_small_int(0); pn_range_end = args[0]; - pn_range_step = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 1); + pn_range_step = mp_parse_node_new_small_int(1); } else if (n_args == 2) { pn_range_start = args[0]; pn_range_end = args[1]; - pn_range_step = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, 1); + pn_range_step = mp_parse_node_new_small_int(1); } else { pn_range_start = args[0]; pn_range_end = args[1]; diff --git a/py/parse.c b/py/parse.c index aa6034a6a..698f79617 100644 --- a/py/parse.c +++ b/py/parse.c @@ -227,13 +227,6 @@ STATIC void pop_rule(parser_t *parser, const rule_t **rule, size_t *arg_i, size_ *src_line = parser->rule_stack[parser->rule_stack_top].src_line; } -mp_parse_node_t mp_parse_node_new_leaf(size_t kind, mp_int_t arg) { - if (kind == MP_PARSE_NODE_SMALL_INT) { - return (mp_parse_node_t)(kind | (arg << 1)); - } - return (mp_parse_node_t)(kind | (arg << 4)); -} - bool mp_parse_node_is_const_false(mp_parse_node_t pn) { return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE) || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0); @@ -418,7 +411,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { mp_map_elem_t *elem; if (rule->rule_id == RULE_atom && (elem = mp_map_lookup(&parser->consts, MP_OBJ_NEW_QSTR(id), MP_MAP_LOOKUP)) != NULL) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(elem->value)); + pn = mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(elem->value)); } else { pn = mp_parse_node_new_leaf(MP_PARSE_NODE_ID, id); } @@ -429,7 +422,7 @@ STATIC void push_result_token(parser_t *parser, const rule_t *rule) { } else if (lex->tok_kind == MP_TOKEN_INTEGER) { mp_obj_t o = mp_parse_num_integer(lex->vstr.buf, lex->vstr.len, 0, lex); if (MP_OBJ_IS_SMALL_INT(o)) { - pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(o)); + pn = mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(o)); } else { pn = make_node_const_object(parser, lex->tok_line, o); } @@ -658,7 +651,7 @@ STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args pop_result(parser); } if (MP_OBJ_IS_SMALL_INT(arg0)) { - push_result_node(parser, mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(arg0))); + push_result_node(parser, mp_parse_node_new_small_int(MP_OBJ_SMALL_INT_VALUE(arg0))); } else { // TODO reuse memory for parse node struct? push_result_node(parser, make_node_const_object(parser, 0, arg0)); diff --git a/py/parse.h b/py/parse.h index 54bbd2645..769f5a875 100644 --- a/py/parse.h +++ b/py/parse.h @@ -76,7 +76,12 @@ typedef struct _mp_parse_node_struct_t { #define MP_PARSE_NODE_STRUCT_KIND(pns) ((pns)->kind_num_nodes & 0xff) #define MP_PARSE_NODE_STRUCT_NUM_NODES(pns) ((pns)->kind_num_nodes >> 8) -mp_parse_node_t mp_parse_node_new_leaf(size_t kind, mp_int_t arg); +static inline mp_parse_node_t mp_parse_node_new_small_int(mp_int_t val) { + return (mp_parse_node_t)(MP_PARSE_NODE_SMALL_INT | ((mp_uint_t)val << 1)); +} +static inline mp_parse_node_t mp_parse_node_new_leaf(size_t kind, mp_int_t arg) { + return (mp_parse_node_t)(kind | ((mp_uint_t)arg << 4)); +} bool mp_parse_node_is_const_false(mp_parse_node_t pn); bool mp_parse_node_is_const_true(mp_parse_node_t pn); bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o); -- cgit v1.2.3 From 9b525134d1bf5b3f05b39fe90ddaa4bd33ea3e0d Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 13 Nov 2016 15:36:49 +1100 Subject: py/parse: Add code to fold logical constants in or/and/not operations. Adds about 200 bytes to the code size when constant folding is enabled. --- py/parse.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'py') diff --git a/py/parse.c b/py/parse.c index 698f79617..dc360e40c 100644 --- a/py/parse.c +++ b/py/parse.c @@ -470,6 +470,63 @@ STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *rule, size_t num_args); #if MICROPY_COMP_CONST_FOLDING +STATIC bool fold_logical_constants(parser_t *parser, const rule_t *rule, size_t *num_args) { + if (rule->rule_id == RULE_or_test + || rule->rule_id == RULE_and_test) { + // folding for binary logical ops: or and + size_t copy_to = *num_args; + for (size_t i = copy_to; i > 0;) { + mp_parse_node_t pn = peek_result(parser, --i); + parser->result_stack[parser->result_stack_top - copy_to] = pn; + if (i == 0) { + // always need to keep the last value + break; + } + if (rule->rule_id == RULE_or_test) { + if (mp_parse_node_is_const_true(pn)) { + // + break; + } else if (!mp_parse_node_is_const_false(pn)) { + copy_to -= 1; + } + } else { + // RULE_and_test + if (mp_parse_node_is_const_false(pn)) { + break; + } else if (!mp_parse_node_is_const_true(pn)) { + copy_to -= 1; + } + } + } + copy_to -= 1; // copy_to now contains number of args to pop + + // pop and discard all the short-circuited expressions + for (size_t i = 0; i < copy_to; ++i) { + pop_result(parser); + } + *num_args -= copy_to; + + // we did a complete folding if there's only 1 arg left + return *num_args == 1; + + } else if (rule->rule_id == RULE_not_test_2) { + // folding for unary logical op: not + mp_parse_node_t pn = peek_result(parser, 0); + if (mp_parse_node_is_const_false(pn)) { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, MP_TOKEN_KW_TRUE); + } else if (mp_parse_node_is_const_true(pn)) { + pn = mp_parse_node_new_leaf(MP_PARSE_NODE_TOKEN, MP_TOKEN_KW_FALSE); + } else { + return false; + } + pop_result(parser); + push_result_node(parser, pn); + return true; + } + + return false; +} + STATIC bool fold_constants(parser_t *parser, const rule_t *rule, size_t num_args) { // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 // it does not do partial folding, eg 1 + 2 + x -> 3 + x @@ -677,6 +734,10 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, const rule_t *ru } #if MICROPY_COMP_CONST_FOLDING + if (fold_logical_constants(parser, rule, &num_args)) { + // we folded this rule so return straight away + return; + } if (fold_constants(parser, rule, num_args)) { // we folded this rule so return straight away return; -- cgit v1.2.3 From 6810f2c134f9329e9dc18f4e0d3a1936ca6d8011 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Nov 2016 11:55:41 +1100 Subject: py: Factor persistent code load/save funcs into persistentcode.[ch]. --- esp8266/esp8266.ld | 1 + esp8266/esp8266_512k.ld | 1 + py/builtinimport.c | 1 + py/emitglue.c | 465 -------------------------------------------- py/emitglue.h | 17 -- py/persistentcode.c | 499 ++++++++++++++++++++++++++++++++++++++++++++++++ py/persistentcode.h | 44 +++++ py/py.mk | 1 + 8 files changed, 547 insertions(+), 482 deletions(-) create mode 100644 py/persistentcode.c create mode 100644 py/persistentcode.h (limited to 'py') diff --git a/esp8266/esp8266.ld b/esp8266/esp8266.ld index 20b259dff..546ffd8c5 100644 --- a/esp8266/esp8266.ld +++ b/esp8266/esp8266.ld @@ -88,6 +88,7 @@ SECTIONS *py/builtin*.o*(.literal* .text*) *py/compile.o*(.literal* .text*) *py/emit*.o*(.literal* .text*) + *py/persistentcode*.o*(.literal* .text*) *py/formatfloat.o*(.literal* .text*) *py/frozenmod.o*(.literal* .text*) *py/gc.o*(.literal* .text*) diff --git a/esp8266/esp8266_512k.ld b/esp8266/esp8266_512k.ld index 781cbb985..e744d0f46 100644 --- a/esp8266/esp8266_512k.ld +++ b/esp8266/esp8266_512k.ld @@ -88,6 +88,7 @@ SECTIONS *py/builtin*.o*(.literal* .text*) *py/compile.o*(.literal* .text*) *py/emit*.o*(.literal* .text*) + *py/persistentcode*.o*(.literal* .text*) *py/formatfloat.o*(.literal* .text*) *py/frozenmod.o*(.literal* .text*) *py/gc.o*(.literal* .text*) diff --git a/py/builtinimport.c b/py/builtinimport.c index e72eaf472..e197dc783 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -32,6 +32,7 @@ #include "py/nlr.h" #include "py/compile.h" #include "py/objmodule.h" +#include "py/persistentcode.h" #include "py/runtime.h" #include "py/builtin.h" #include "py/frozenmod.h" diff --git a/py/emitglue.c b/py/emitglue.c index dc5be6e0e..abcf50cdd 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -171,468 +171,3 @@ mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_clos // wrap function in closure object return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2)); } - -#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE - -#include "py/smallint.h" - -// The feature flags byte encodes the compile-time config options that -// affect the generate bytecode. -#define MPY_FEATURE_FLAGS ( \ - ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \ - | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \ - ) -// This is a version of the flags that can be configured at runtime. -#define MPY_FEATURE_FLAGS_DYNAMIC ( \ - ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \ - | ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \ - ) - -#if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER) -// The bytecode will depend on the number of bits in a small-int, and -// this function computes that (could make it a fixed constant, but it -// would need to be defined in mpconfigport.h). -STATIC int mp_small_int_bits(void) { - mp_int_t i = MP_SMALL_INT_MAX; - int n = 1; - while (i != 0) { - i >>= 1; - ++n; - } - return n; -} -#endif - -typedef struct _bytecode_prelude_t { - uint n_state; - uint n_exc_stack; - uint scope_flags; - uint n_pos_args; - uint n_kwonly_args; - uint n_def_pos_args; - uint code_info_size; -} bytecode_prelude_t; - -// ip will point to start of opcodes -// ip2 will point to simple_name, source_file qstrs -STATIC void extract_prelude(const byte **ip, const byte **ip2, bytecode_prelude_t *prelude) { - prelude->n_state = mp_decode_uint(ip); - prelude->n_exc_stack = mp_decode_uint(ip); - prelude->scope_flags = *(*ip)++; - prelude->n_pos_args = *(*ip)++; - prelude->n_kwonly_args = *(*ip)++; - prelude->n_def_pos_args = *(*ip)++; - *ip2 = *ip; - prelude->code_info_size = mp_decode_uint(ip2); - *ip += prelude->code_info_size; - while (*(*ip)++ != 255) { - } -} - -#endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE - -#if MICROPY_PERSISTENT_CODE_LOAD - -#include "py/parsenum.h" -#include "py/bc0.h" - -STATIC int read_byte(mp_reader_t *reader) { - return reader->read_byte(reader->data); -} - -STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { - while (len-- > 0) { - *buf++ = reader->read_byte(reader->data); - } -} - -STATIC mp_uint_t read_uint(mp_reader_t *reader) { - mp_uint_t unum = 0; - for (;;) { - byte b = reader->read_byte(reader->data); - unum = (unum << 7) | (b & 0x7f); - if ((b & 0x80) == 0) { - break; - } - } - return unum; -} - -STATIC qstr load_qstr(mp_reader_t *reader) { - mp_uint_t len = read_uint(reader); - char *str = m_new(char, len); - read_bytes(reader, (byte*)str, len); - qstr qst = qstr_from_strn(str, len); - m_del(char, str, len); - return qst; -} - -STATIC mp_obj_t load_obj(mp_reader_t *reader) { - byte obj_type = read_byte(reader); - if (obj_type == 'e') { - return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); - } else { - size_t len = read_uint(reader); - vstr_t vstr; - vstr_init_len(&vstr, len); - read_bytes(reader, (byte*)vstr.buf, len); - if (obj_type == 's' || obj_type == 'b') { - return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); - } else if (obj_type == 'i') { - return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); - } else { - assert(obj_type == 'f' || obj_type == 'c'); - return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL); - } - } -} - -STATIC void load_bytecode_qstrs(mp_reader_t *reader, byte *ip, byte *ip_top) { - while (ip < ip_top) { - size_t sz; - uint f = mp_opcode_format(ip, &sz); - if (f == MP_OPCODE_QSTR) { - qstr qst = load_qstr(reader); - ip[1] = qst; - ip[2] = qst >> 8; - } - ip += sz; - } -} - -STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) { - // load bytecode - mp_uint_t bc_len = read_uint(reader); - byte *bytecode = m_new(byte, bc_len); - read_bytes(reader, bytecode, bc_len); - - // extract prelude - const byte *ip = bytecode; - const byte *ip2; - bytecode_prelude_t prelude; - extract_prelude(&ip, &ip2, &prelude); - - // load qstrs and link global qstr ids into bytecode - qstr simple_name = load_qstr(reader); - qstr source_file = load_qstr(reader); - ((byte*)ip2)[0] = simple_name; ((byte*)ip2)[1] = simple_name >> 8; - ((byte*)ip2)[2] = source_file; ((byte*)ip2)[3] = source_file >> 8; - load_bytecode_qstrs(reader, (byte*)ip, bytecode + bc_len); - - // load constant table - mp_uint_t n_obj = read_uint(reader); - mp_uint_t n_raw_code = read_uint(reader); - mp_uint_t *const_table = m_new(mp_uint_t, prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code); - mp_uint_t *ct = const_table; - for (mp_uint_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { - *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader)); - } - for (mp_uint_t i = 0; i < n_obj; ++i) { - *ct++ = (mp_uint_t)load_obj(reader); - } - for (mp_uint_t i = 0; i < n_raw_code; ++i) { - *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader); - } - - // create raw_code and return it - mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); - mp_emit_glue_assign_bytecode(rc, bytecode, bc_len, const_table, - #if MICROPY_PERSISTENT_CODE_SAVE - n_obj, n_raw_code, - #endif - prelude.scope_flags); - return rc; -} - -mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { - byte header[4]; - read_bytes(reader, header, sizeof(header)); - if (strncmp((char*)header, "M\x00", 2) != 0) { - mp_raise_ValueError("invalid .mpy file"); - } - if (header[2] != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits()) { - mp_raise_ValueError("incompatible .mpy file"); - } - return load_raw_code(reader); -} - -typedef struct _mp_mem_reader_t { - const byte *cur; - const byte *end; -} mp_mem_reader_t; - -STATIC mp_uint_t mp_mem_reader_next_byte(void *br_in) { - mp_mem_reader_t *br = br_in; - if (br->cur < br->end) { - return *br->cur++; - } else { - return (mp_uint_t)-1; - } -} - -mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) { - mp_mem_reader_t mr = {buf, buf + len}; - mp_reader_t reader = {&mr, mp_mem_reader_next_byte}; - return mp_raw_code_load(&reader); -} - -// here we define mp_raw_code_load_file depending on the port -// TODO abstract this away properly - -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__unix__) -// unix file reader - -#include -#include -#include - -typedef struct _mp_lexer_file_buf_t { - int fd; - byte buf[20]; - mp_uint_t len; - mp_uint_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(void *fb_in) { - mp_lexer_file_buf_t *fb = fb_in; - if (fb->pos >= fb->len) { - if (fb->len == 0) { - return (mp_uint_t)-1; - } else { - int n = read(fb->fd, fb->buf, sizeof(fb->buf)); - if (n <= 0) { - fb->len = 0; - return (mp_uint_t)-1; - } - fb->len = n; - fb->pos = 0; - } - } - return fb->buf[fb->pos++]; -} - -mp_raw_code_t *mp_raw_code_load_file(const char *filename) { - mp_lexer_file_buf_t fb; - fb.fd = open(filename, O_RDONLY, 0644); - int n = read(fb.fd, fb.buf, sizeof(fb.buf)); - fb.len = n; - fb.pos = 0; - mp_reader_t reader; - reader.data = &fb; - reader.read_byte = file_buf_next_byte; - mp_raw_code_t *rc = mp_raw_code_load(&reader); - close(fb.fd); - return rc; -} - -#elif defined(__thumb2__) || defined(__xtensa__) -// fatfs file reader (assume thumb2 arch uses fatfs...) - -#include "lib/fatfs/ff.h" - -typedef struct _mp_lexer_file_buf_t { - FIL fp; - byte buf[20]; - uint16_t len; - uint16_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(void *fb_in) { - mp_lexer_file_buf_t *fb = fb_in; - if (fb->pos >= fb->len) { - if (fb->len < sizeof(fb->buf)) { - return (mp_uint_t)-1; - } else { - UINT n; - f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); - if (n == 0) { - return (mp_uint_t)-1; - } - fb->len = n; - fb->pos = 0; - } - } - return fb->buf[fb->pos++]; -} - -mp_raw_code_t *mp_raw_code_load_file(const char *filename) { - mp_lexer_file_buf_t fb; - /*FRESULT res =*/ f_open(&fb.fp, filename, FA_READ); - UINT n; - f_read(&fb.fp, fb.buf, sizeof(fb.buf), &n); - fb.len = n; - fb.pos = 0; - - mp_reader_t reader; - reader.data = &fb; - reader.read_byte = file_buf_next_byte; - mp_raw_code_t *rc = mp_raw_code_load(&reader); - - f_close(&fb.fp); - - return rc; -} - -#endif - -#endif // MICROPY_PERSISTENT_CODE_LOAD - -#if MICROPY_PERSISTENT_CODE_SAVE - -#include "py/objstr.h" - -STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) { - print->print_strn(print->data, (const char*)data, len); -} - -#define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7) -STATIC void mp_print_uint(mp_print_t *print, mp_uint_t n) { - byte buf[BYTES_FOR_INT]; - byte *p = buf + sizeof(buf); - *--p = n & 0x7f; - n >>= 7; - for (; n != 0; n >>= 7) { - *--p = 0x80 | (n & 0x7f); - } - print->print_strn(print->data, (char*)p, buf + sizeof(buf) - p); -} - -STATIC void save_qstr(mp_print_t *print, qstr qst) { - size_t len; - const byte *str = qstr_data(qst, &len); - mp_print_uint(print, len); - mp_print_bytes(print, str, len); -} - -STATIC void save_obj(mp_print_t *print, mp_obj_t o) { - if (MP_OBJ_IS_STR_OR_BYTES(o)) { - byte obj_type; - if (MP_OBJ_IS_STR(o)) { - obj_type = 's'; - } else { - obj_type = 'b'; - } - mp_uint_t len; - const char *str = mp_obj_str_get_data(o, &len); - mp_print_bytes(print, &obj_type, 1); - mp_print_uint(print, len); - mp_print_bytes(print, (const byte*)str, len); - } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { - byte obj_type = 'e'; - mp_print_bytes(print, &obj_type, 1); - } else { - // we save numbers using a simplistic text representation - // TODO could be improved - byte obj_type; - if (MP_OBJ_IS_TYPE(o, &mp_type_int)) { - obj_type = 'i'; - } else if (mp_obj_is_float(o)) { - obj_type = 'f'; - } else { - assert(MP_OBJ_IS_TYPE(o, &mp_type_complex)); - obj_type = 'c'; - } - vstr_t vstr; - mp_print_t pr; - vstr_init_print(&vstr, 10, &pr); - mp_obj_print_helper(&pr, o, PRINT_REPR); - mp_print_bytes(print, &obj_type, 1); - mp_print_uint(print, vstr.len); - mp_print_bytes(print, (const byte*)vstr.buf, vstr.len); - vstr_clear(&vstr); - } -} - -STATIC void save_bytecode_qstrs(mp_print_t *print, const byte *ip, const byte *ip_top) { - while (ip < ip_top) { - size_t sz; - uint f = mp_opcode_format(ip, &sz); - if (f == MP_OPCODE_QSTR) { - qstr qst = ip[1] | (ip[2] << 8); - save_qstr(print, qst); - } - ip += sz; - } -} - -STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc) { - if (rc->kind != MP_CODE_BYTECODE) { - mp_raise_ValueError("can only save bytecode"); - } - - // 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); - - // extract prelude - const byte *ip = rc->data.u_byte.bytecode; - const byte *ip2; - bytecode_prelude_t prelude; - extract_prelude(&ip, &ip2, &prelude); - - // save qstrs - save_qstr(print, ip2[0] | (ip2[1] << 8)); // simple_name - save_qstr(print, ip2[2] | (ip2[3] << 8)); // source_file - save_bytecode_qstrs(print, ip, rc->data.u_byte.bytecode + rc->data.u_byte.bc_len); - - // save constant table - mp_print_uint(print, rc->data.u_byte.n_obj); - mp_print_uint(print, rc->data.u_byte.n_raw_code); - const mp_uint_t *const_table = rc->data.u_byte.const_table; - for (uint i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { - mp_obj_t o = (mp_obj_t)*const_table++; - save_qstr(print, MP_OBJ_QSTR_VALUE(o)); - } - for (uint i = 0; i < rc->data.u_byte.n_obj; ++i) { - save_obj(print, (mp_obj_t)*const_table++); - } - for (uint i = 0; i < rc->data.u_byte.n_raw_code; ++i) { - save_raw_code(print, (mp_raw_code_t*)(uintptr_t)*const_table++); - } -} - -void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { - // header contains: - // byte 'M' - // byte version - // byte feature flags - // byte number of bits in a small int - byte header[4] = {'M', 0, MPY_FEATURE_FLAGS_DYNAMIC, - #if MICROPY_DYNAMIC_COMPILER - mp_dynamic_compiler.small_int_bits, - #else - mp_small_int_bits(), - #endif - }; - mp_print_bytes(print, header, sizeof(header)); - - save_raw_code(print, rc); -} - -// here we define mp_raw_code_save_file depending on the port -// TODO abstract this away properly - -#if defined(__i386__) || defined(__x86_64__) || (defined(__arm__) && (defined(__unix__))) - -#include -#include -#include - -STATIC void fd_print_strn(void *env, const char *str, size_t len) { - int fd = (intptr_t)env; - ssize_t ret = write(fd, str, len); - (void)ret; -} - -void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) { - int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); - mp_print_t fd_print = {(void*)(intptr_t)fd, fd_print_strn}; - mp_raw_code_save(rc, &fd_print); - close(fd); -} - -#else -#error mp_raw_code_save_file not implemented for this platform -#endif - -#endif // MICROPY_PERSISTENT_CODE_SAVE diff --git a/py/emitglue.h b/py/emitglue.h index f5618577d..37c4f1b18 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -74,21 +74,4 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void 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_LOAD -typedef struct _mp_reader_t { - void *data; - mp_uint_t (*read_byte)(void *data); - void (*close)(void *data); -} mp_reader_t; - -mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader); -mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len); -mp_raw_code_t *mp_raw_code_load_file(const char *filename); -#endif - -#if MICROPY_PERSISTENT_CODE_SAVE -void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print); -void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename); -#endif - #endif // __MICROPY_INCLUDED_PY_EMITGLUE_H__ diff --git a/py/persistentcode.c b/py/persistentcode.c new file mode 100644 index 000000000..31f147a09 --- /dev/null +++ b/py/persistentcode.c @@ -0,0 +1,499 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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 +#include +#include +#include + +#include "py/emitglue.h" +#include "py/persistentcode.h" +#include "py/bc.h" + +#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE + +#include "py/smallint.h" + +// The feature flags byte encodes the compile-time config options that +// affect the generate bytecode. +#define MPY_FEATURE_FLAGS ( \ + ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \ + | ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \ + ) +// This is a version of the flags that can be configured at runtime. +#define MPY_FEATURE_FLAGS_DYNAMIC ( \ + ((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \ + | ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \ + ) + +#if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER) +// The bytecode will depend on the number of bits in a small-int, and +// this function computes that (could make it a fixed constant, but it +// would need to be defined in mpconfigport.h). +STATIC int mp_small_int_bits(void) { + mp_int_t i = MP_SMALL_INT_MAX; + int n = 1; + while (i != 0) { + i >>= 1; + ++n; + } + return n; +} +#endif + +typedef struct _bytecode_prelude_t { + uint n_state; + uint n_exc_stack; + uint scope_flags; + uint n_pos_args; + uint n_kwonly_args; + uint n_def_pos_args; + uint code_info_size; +} bytecode_prelude_t; + +// ip will point to start of opcodes +// ip2 will point to simple_name, source_file qstrs +STATIC void extract_prelude(const byte **ip, const byte **ip2, bytecode_prelude_t *prelude) { + prelude->n_state = mp_decode_uint(ip); + prelude->n_exc_stack = mp_decode_uint(ip); + prelude->scope_flags = *(*ip)++; + prelude->n_pos_args = *(*ip)++; + prelude->n_kwonly_args = *(*ip)++; + prelude->n_def_pos_args = *(*ip)++; + *ip2 = *ip; + prelude->code_info_size = mp_decode_uint(ip2); + *ip += prelude->code_info_size; + while (*(*ip)++ != 255) { + } +} + +#endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE + +#if MICROPY_PERSISTENT_CODE_LOAD + +#include "py/parsenum.h" +#include "py/bc0.h" + +STATIC int read_byte(mp_reader_t *reader) { + return reader->read_byte(reader->data); +} + +STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { + while (len-- > 0) { + *buf++ = reader->read_byte(reader->data); + } +} + +STATIC mp_uint_t read_uint(mp_reader_t *reader) { + mp_uint_t unum = 0; + for (;;) { + byte b = reader->read_byte(reader->data); + unum = (unum << 7) | (b & 0x7f); + if ((b & 0x80) == 0) { + break; + } + } + return unum; +} + +STATIC qstr load_qstr(mp_reader_t *reader) { + mp_uint_t len = read_uint(reader); + char *str = m_new(char, len); + read_bytes(reader, (byte*)str, len); + qstr qst = qstr_from_strn(str, len); + m_del(char, str, len); + return qst; +} + +STATIC mp_obj_t load_obj(mp_reader_t *reader) { + byte obj_type = read_byte(reader); + if (obj_type == 'e') { + return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); + } else { + size_t len = read_uint(reader); + vstr_t vstr; + vstr_init_len(&vstr, len); + read_bytes(reader, (byte*)vstr.buf, len); + if (obj_type == 's' || obj_type == 'b') { + return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); + } else if (obj_type == 'i') { + return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); + } else { + assert(obj_type == 'f' || obj_type == 'c'); + return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL); + } + } +} + +STATIC void load_bytecode_qstrs(mp_reader_t *reader, byte *ip, byte *ip_top) { + while (ip < ip_top) { + size_t sz; + uint f = mp_opcode_format(ip, &sz); + if (f == MP_OPCODE_QSTR) { + qstr qst = load_qstr(reader); + ip[1] = qst; + ip[2] = qst >> 8; + } + ip += sz; + } +} + +STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader) { + // load bytecode + mp_uint_t bc_len = read_uint(reader); + byte *bytecode = m_new(byte, bc_len); + read_bytes(reader, bytecode, bc_len); + + // extract prelude + const byte *ip = bytecode; + const byte *ip2; + bytecode_prelude_t prelude; + extract_prelude(&ip, &ip2, &prelude); + + // load qstrs and link global qstr ids into bytecode + qstr simple_name = load_qstr(reader); + qstr source_file = load_qstr(reader); + ((byte*)ip2)[0] = simple_name; ((byte*)ip2)[1] = simple_name >> 8; + ((byte*)ip2)[2] = source_file; ((byte*)ip2)[3] = source_file >> 8; + load_bytecode_qstrs(reader, (byte*)ip, bytecode + bc_len); + + // load constant table + mp_uint_t n_obj = read_uint(reader); + mp_uint_t n_raw_code = read_uint(reader); + mp_uint_t *const_table = m_new(mp_uint_t, prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code); + mp_uint_t *ct = const_table; + for (mp_uint_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { + *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader)); + } + for (mp_uint_t i = 0; i < n_obj; ++i) { + *ct++ = (mp_uint_t)load_obj(reader); + } + for (mp_uint_t i = 0; i < n_raw_code; ++i) { + *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader); + } + + // create raw_code and return it + mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); + mp_emit_glue_assign_bytecode(rc, bytecode, bc_len, const_table, + #if MICROPY_PERSISTENT_CODE_SAVE + n_obj, n_raw_code, + #endif + prelude.scope_flags); + return rc; +} + +mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { + byte header[4]; + read_bytes(reader, header, sizeof(header)); + if (strncmp((char*)header, "M\x00", 2) != 0) { + mp_raise_ValueError("invalid .mpy file"); + } + if (header[2] != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits()) { + mp_raise_ValueError("incompatible .mpy file"); + } + return load_raw_code(reader); +} + +typedef struct _mp_mem_reader_t { + const byte *cur; + const byte *end; +} mp_mem_reader_t; + +STATIC mp_uint_t mp_mem_reader_next_byte(void *br_in) { + mp_mem_reader_t *br = br_in; + if (br->cur < br->end) { + return *br->cur++; + } else { + return (mp_uint_t)-1; + } +} + +mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) { + mp_mem_reader_t mr = {buf, buf + len}; + mp_reader_t reader = {&mr, mp_mem_reader_next_byte}; + return mp_raw_code_load(&reader); +} + +// here we define mp_raw_code_load_file depending on the port +// TODO abstract this away properly + +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__unix__) +// unix file reader + +#include +#include +#include + +typedef struct _mp_lexer_file_buf_t { + int fd; + byte buf[20]; + mp_uint_t len; + mp_uint_t pos; +} mp_lexer_file_buf_t; + +STATIC mp_uint_t file_buf_next_byte(void *fb_in) { + mp_lexer_file_buf_t *fb = fb_in; + if (fb->pos >= fb->len) { + if (fb->len == 0) { + return (mp_uint_t)-1; + } else { + int n = read(fb->fd, fb->buf, sizeof(fb->buf)); + if (n <= 0) { + fb->len = 0; + return (mp_uint_t)-1; + } + fb->len = n; + fb->pos = 0; + } + } + return fb->buf[fb->pos++]; +} + +mp_raw_code_t *mp_raw_code_load_file(const char *filename) { + mp_lexer_file_buf_t fb; + fb.fd = open(filename, O_RDONLY, 0644); + int n = read(fb.fd, fb.buf, sizeof(fb.buf)); + fb.len = n; + fb.pos = 0; + mp_reader_t reader; + reader.data = &fb; + reader.read_byte = file_buf_next_byte; + mp_raw_code_t *rc = mp_raw_code_load(&reader); + close(fb.fd); + return rc; +} + +#elif defined(__thumb2__) || defined(__xtensa__) +// fatfs file reader (assume thumb2 arch uses fatfs...) + +#include "lib/fatfs/ff.h" + +typedef struct _mp_lexer_file_buf_t { + FIL fp; + byte buf[20]; + uint16_t len; + uint16_t pos; +} mp_lexer_file_buf_t; + +STATIC mp_uint_t file_buf_next_byte(void *fb_in) { + mp_lexer_file_buf_t *fb = fb_in; + if (fb->pos >= fb->len) { + if (fb->len < sizeof(fb->buf)) { + return (mp_uint_t)-1; + } else { + UINT n; + f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); + if (n == 0) { + return (mp_uint_t)-1; + } + fb->len = n; + fb->pos = 0; + } + } + return fb->buf[fb->pos++]; +} + +mp_raw_code_t *mp_raw_code_load_file(const char *filename) { + mp_lexer_file_buf_t fb; + /*FRESULT res =*/ f_open(&fb.fp, filename, FA_READ); + UINT n; + f_read(&fb.fp, fb.buf, sizeof(fb.buf), &n); + fb.len = n; + fb.pos = 0; + + mp_reader_t reader; + reader.data = &fb; + reader.read_byte = file_buf_next_byte; + mp_raw_code_t *rc = mp_raw_code_load(&reader); + + f_close(&fb.fp); + + return rc; +} + +#endif + +#endif // MICROPY_PERSISTENT_CODE_LOAD + +#if MICROPY_PERSISTENT_CODE_SAVE + +#include "py/objstr.h" + +STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) { + print->print_strn(print->data, (const char*)data, len); +} + +#define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7) +STATIC void mp_print_uint(mp_print_t *print, mp_uint_t n) { + byte buf[BYTES_FOR_INT]; + byte *p = buf + sizeof(buf); + *--p = n & 0x7f; + n >>= 7; + for (; n != 0; n >>= 7) { + *--p = 0x80 | (n & 0x7f); + } + print->print_strn(print->data, (char*)p, buf + sizeof(buf) - p); +} + +STATIC void save_qstr(mp_print_t *print, qstr qst) { + size_t len; + const byte *str = qstr_data(qst, &len); + mp_print_uint(print, len); + mp_print_bytes(print, str, len); +} + +STATIC void save_obj(mp_print_t *print, mp_obj_t o) { + if (MP_OBJ_IS_STR_OR_BYTES(o)) { + byte obj_type; + if (MP_OBJ_IS_STR(o)) { + obj_type = 's'; + } else { + obj_type = 'b'; + } + mp_uint_t len; + const char *str = mp_obj_str_get_data(o, &len); + mp_print_bytes(print, &obj_type, 1); + mp_print_uint(print, len); + mp_print_bytes(print, (const byte*)str, len); + } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { + byte obj_type = 'e'; + mp_print_bytes(print, &obj_type, 1); + } else { + // we save numbers using a simplistic text representation + // TODO could be improved + byte obj_type; + if (MP_OBJ_IS_TYPE(o, &mp_type_int)) { + obj_type = 'i'; + } else if (mp_obj_is_float(o)) { + obj_type = 'f'; + } else { + assert(MP_OBJ_IS_TYPE(o, &mp_type_complex)); + obj_type = 'c'; + } + vstr_t vstr; + mp_print_t pr; + vstr_init_print(&vstr, 10, &pr); + mp_obj_print_helper(&pr, o, PRINT_REPR); + mp_print_bytes(print, &obj_type, 1); + mp_print_uint(print, vstr.len); + mp_print_bytes(print, (const byte*)vstr.buf, vstr.len); + vstr_clear(&vstr); + } +} + +STATIC void save_bytecode_qstrs(mp_print_t *print, const byte *ip, const byte *ip_top) { + while (ip < ip_top) { + size_t sz; + uint f = mp_opcode_format(ip, &sz); + if (f == MP_OPCODE_QSTR) { + qstr qst = ip[1] | (ip[2] << 8); + save_qstr(print, qst); + } + ip += sz; + } +} + +STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc) { + if (rc->kind != MP_CODE_BYTECODE) { + mp_raise_ValueError("can only save bytecode"); + } + + // 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); + + // extract prelude + const byte *ip = rc->data.u_byte.bytecode; + const byte *ip2; + bytecode_prelude_t prelude; + extract_prelude(&ip, &ip2, &prelude); + + // save qstrs + save_qstr(print, ip2[0] | (ip2[1] << 8)); // simple_name + save_qstr(print, ip2[2] | (ip2[3] << 8)); // source_file + save_bytecode_qstrs(print, ip, rc->data.u_byte.bytecode + rc->data.u_byte.bc_len); + + // save constant table + mp_print_uint(print, rc->data.u_byte.n_obj); + mp_print_uint(print, rc->data.u_byte.n_raw_code); + const mp_uint_t *const_table = rc->data.u_byte.const_table; + for (uint i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { + mp_obj_t o = (mp_obj_t)*const_table++; + save_qstr(print, MP_OBJ_QSTR_VALUE(o)); + } + for (uint i = 0; i < rc->data.u_byte.n_obj; ++i) { + save_obj(print, (mp_obj_t)*const_table++); + } + for (uint i = 0; i < rc->data.u_byte.n_raw_code; ++i) { + save_raw_code(print, (mp_raw_code_t*)(uintptr_t)*const_table++); + } +} + +void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { + // header contains: + // byte 'M' + // byte version + // byte feature flags + // byte number of bits in a small int + byte header[4] = {'M', 0, MPY_FEATURE_FLAGS_DYNAMIC, + #if MICROPY_DYNAMIC_COMPILER + mp_dynamic_compiler.small_int_bits, + #else + mp_small_int_bits(), + #endif + }; + mp_print_bytes(print, header, sizeof(header)); + + save_raw_code(print, rc); +} + +// here we define mp_raw_code_save_file depending on the port +// TODO abstract this away properly + +#if defined(__i386__) || defined(__x86_64__) || (defined(__arm__) && (defined(__unix__))) + +#include +#include +#include + +STATIC void fd_print_strn(void *env, const char *str, size_t len) { + int fd = (intptr_t)env; + ssize_t ret = write(fd, str, len); + (void)ret; +} + +void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) { + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); + mp_print_t fd_print = {(void*)(intptr_t)fd, fd_print_strn}; + mp_raw_code_save(rc, &fd_print); + close(fd); +} + +#else +#error mp_raw_code_save_file not implemented for this platform +#endif + +#endif // MICROPY_PERSISTENT_CODE_SAVE diff --git a/py/persistentcode.h b/py/persistentcode.h new file mode 100644 index 000000000..8859bc577 --- /dev/null +++ b/py/persistentcode.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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_PERSISTENTCODE_H +#define MICROPY_INCLUDED_PY_PERSISTENTCODE_H + +#include "py/obj.h" + +typedef struct _mp_reader_t { + void *data; + mp_uint_t (*read_byte)(void *data); + void (*close)(void *data); +} mp_reader_t; + +mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader); +mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len); +mp_raw_code_t *mp_raw_code_load_file(const char *filename); + +void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print); +void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename); + +#endif // MICROPY_INCLUDED_PY_PERSISTENTCODE_H diff --git a/py/py.mk b/py/py.mk index 88c4fafe9..b0962d178 100644 --- a/py/py.mk +++ b/py/py.mk @@ -134,6 +134,7 @@ PY_O_BASENAME = \ parsenumbase.o \ parsenum.o \ emitglue.o \ + persistentcode.o \ runtime.o \ runtime_utils.o \ nativeglue.o \ -- cgit v1.2.3 From 6b239c271c9c7041f9741c2a0f348f350808ad8a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Nov 2016 16:04:57 +1100 Subject: py: Factor out persistent-code reader into separate files. Implementations of persistent-code reader are provided for POSIX systems and systems using FatFS. Macros to use these are MICROPY_READER_POSIX and MICROPY_READER_FATFS respectively. If an alternative implementation is needed then a port can define the function mp_reader_new_file. --- cc3200/mpconfigport.h | 1 + esp8266/esp8266.ld | 1 + esp8266/esp8266_512k.ld | 1 + esp8266/mpconfigport.h | 1 + examples/embedding/mpconfigport_minimal.h | 1 + extmod/vfs_fat_reader.c | 88 ++++++++++++++++++ py/mpconfig.h | 10 ++ py/persistentcode.c | 129 +++----------------------- py/persistentcode.h | 10 +- py/py.mk | 2 + py/reader.c | 146 ++++++++++++++++++++++++++++++ py/reader.h | 45 +++++++++ stmhal/mpconfigport.h | 1 + unix/mpconfigport.h | 1 + 14 files changed, 316 insertions(+), 121 deletions(-) create mode 100644 extmod/vfs_fat_reader.c create mode 100644 py/reader.c create mode 100644 py/reader.h (limited to 'py') diff --git a/cc3200/mpconfigport.h b/cc3200/mpconfigport.h index bf3e691bd..08f926f40 100644 --- a/cc3200/mpconfigport.h +++ b/cc3200/mpconfigport.h @@ -55,6 +55,7 @@ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) #define MICROPY_OPT_COMPUTED_GOTO (0) #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (0) +#define MICROPY_READER_FATFS (1) #ifndef DEBUG // we need ram on the launchxl while debugging #define MICROPY_CPYTHON_COMPAT (1) #else diff --git a/esp8266/esp8266.ld b/esp8266/esp8266.ld index 546ffd8c5..a6a4b930b 100644 --- a/esp8266/esp8266.ld +++ b/esp8266/esp8266.ld @@ -92,6 +92,7 @@ SECTIONS *py/formatfloat.o*(.literal* .text*) *py/frozenmod.o*(.literal* .text*) *py/gc.o*(.literal* .text*) + *py/reader*.o*(.literal* .text*) *py/lexer*.o*(.literal* .text*) *py/malloc*.o*(.literal* .text*) *py/map*.o*(.literal* .text*) diff --git a/esp8266/esp8266_512k.ld b/esp8266/esp8266_512k.ld index e744d0f46..2bffcab80 100644 --- a/esp8266/esp8266_512k.ld +++ b/esp8266/esp8266_512k.ld @@ -92,6 +92,7 @@ SECTIONS *py/formatfloat.o*(.literal* .text*) *py/frozenmod.o*(.literal* .text*) *py/gc.o*(.literal* .text*) + *py/reader*.o*(.literal* .text*) *py/lexer*.o*(.literal* .text*) *py/malloc*.o*(.literal* .text*) *py/map*.o*(.literal* .text*) diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h index 602b3e9c8..adbec5d0d 100644 --- a/esp8266/mpconfigport.h +++ b/esp8266/mpconfigport.h @@ -16,6 +16,7 @@ #define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTERS (1) #define MICROPY_DEBUG_PRINTER_DEST mp_debug_print +#define MICROPY_READER_FATFS (1) #define MICROPY_ENABLE_GC (1) #define MICROPY_STACK_CHECK (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) diff --git a/examples/embedding/mpconfigport_minimal.h b/examples/embedding/mpconfigport_minimal.h index 90011b3f8..48d200858 100644 --- a/examples/embedding/mpconfigport_minimal.h +++ b/examples/embedding/mpconfigport_minimal.h @@ -33,6 +33,7 @@ #define MICROPY_COMP_CONST (0) #define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTERS (0) +#define MICROPY_READER_POSIX (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_HELPER_LEXER_UNIX (1) #define MICROPY_ENABLE_SOURCE_LINE (0) diff --git a/extmod/vfs_fat_reader.c b/extmod/vfs_fat_reader.c new file mode 100644 index 000000000..7a00f18de --- /dev/null +++ b/extmod/vfs_fat_reader.c @@ -0,0 +1,88 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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 +#include + +#include "py/mperrno.h" +#include "py/reader.h" + +#if MICROPY_READER_FATFS + +#include "lib/fatfs/ff.h" +#include "extmod/vfs_fat_file.h" + +typedef struct _mp_reader_fatfs_t { + FIL fp; + uint16_t len; + uint16_t pos; + byte buf[20]; +} mp_reader_fatfs_t; + +STATIC mp_uint_t mp_reader_fatfs_readbyte(void *data) { + mp_reader_fatfs_t *reader = (mp_reader_fatfs_t*)data; + if (reader->pos >= reader->len) { + if (reader->len < sizeof(reader->buf)) { + return MP_READER_EOF; + } else { + UINT n; + f_read(&reader->fp, reader->buf, sizeof(reader->buf), &n); + if (n == 0) { + return MP_READER_EOF; + } + reader->len = n; + reader->pos = 0; + } + } + return reader->buf[reader->pos++]; +} + +STATIC void mp_reader_fatfs_close(void *data) { + mp_reader_fatfs_t *reader = (mp_reader_fatfs_t*)data; + f_close(&reader->fp); + m_del_obj(mp_reader_fatfs_t, reader); +} + +int mp_reader_new_file(mp_reader_t *reader, const char *filename) { + mp_reader_fatfs_t *rf = m_new_obj_maybe(mp_reader_fatfs_t); + if (rf == NULL) { + return MP_ENOMEM; + } + FRESULT res = f_open(&rf->fp, filename, FA_READ); + if (res != FR_OK) { + return fresult_to_errno_table[res]; + } + UINT n; + f_read(&rf->fp, rf->buf, sizeof(rf->buf), &n); + rf->len = n; + rf->pos = 0; + reader->data = rf; + reader->readbyte = mp_reader_fatfs_readbyte; + reader->close = mp_reader_fatfs_close; + return 0; // success +} + +#endif diff --git a/py/mpconfig.h b/py/mpconfig.h index 3945a1a5a..1980e649c 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -381,6 +381,16 @@ /*****************************************************************************/ /* Python internal features */ +// Whether to use the POSIX reader for importing files +#ifndef MICROPY_READER_POSIX +#define MICROPY_READER_POSIX (0) +#endif + +// Whether to use the FatFS reader for importing files +#ifndef MICROPY_READER_FATFS +#define MICROPY_READER_FATFS (0) +#endif + // Hook for the VM at the start of the opcode loop (can contain variable // definitions usable by the other hook functions) #ifndef MICROPY_VM_HOOK_INIT diff --git a/py/persistentcode.c b/py/persistentcode.c index 31f147a09..99b01f8e2 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -29,6 +29,7 @@ #include #include +#include "py/reader.h" #include "py/emitglue.h" #include "py/persistentcode.h" #include "py/bc.h" @@ -98,19 +99,19 @@ STATIC void extract_prelude(const byte **ip, const byte **ip2, bytecode_prelude_ #include "py/bc0.h" STATIC int read_byte(mp_reader_t *reader) { - return reader->read_byte(reader->data); + return reader->readbyte(reader->data); } STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { while (len-- > 0) { - *buf++ = reader->read_byte(reader->data); + *buf++ = reader->readbyte(reader->data); } } STATIC mp_uint_t read_uint(mp_reader_t *reader) { mp_uint_t unum = 0; for (;;) { - byte b = reader->read_byte(reader->data); + byte b = reader->readbyte(reader->data); unum = (unum << 7) | (b & 0x7f); if ((b & 0x80) == 0) { break; @@ -214,128 +215,28 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { if (header[2] != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits()) { mp_raise_ValueError("incompatible .mpy file"); } - return load_raw_code(reader); -} - -typedef struct _mp_mem_reader_t { - const byte *cur; - const byte *end; -} mp_mem_reader_t; - -STATIC mp_uint_t mp_mem_reader_next_byte(void *br_in) { - mp_mem_reader_t *br = br_in; - if (br->cur < br->end) { - return *br->cur++; - } else { - return (mp_uint_t)-1; - } + mp_raw_code_t *rc = load_raw_code(reader); + reader->close(reader->data); + return rc; } mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) { - mp_mem_reader_t mr = {buf, buf + len}; - mp_reader_t reader = {&mr, mp_mem_reader_next_byte}; - return mp_raw_code_load(&reader); -} - -// here we define mp_raw_code_load_file depending on the port -// TODO abstract this away properly - -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__unix__) -// unix file reader - -#include -#include -#include - -typedef struct _mp_lexer_file_buf_t { - int fd; - byte buf[20]; - mp_uint_t len; - mp_uint_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(void *fb_in) { - mp_lexer_file_buf_t *fb = fb_in; - if (fb->pos >= fb->len) { - if (fb->len == 0) { - return (mp_uint_t)-1; - } else { - int n = read(fb->fd, fb->buf, sizeof(fb->buf)); - if (n <= 0) { - fb->len = 0; - return (mp_uint_t)-1; - } - fb->len = n; - fb->pos = 0; - } - } - return fb->buf[fb->pos++]; -} - -mp_raw_code_t *mp_raw_code_load_file(const char *filename) { - mp_lexer_file_buf_t fb; - fb.fd = open(filename, O_RDONLY, 0644); - int n = read(fb.fd, fb.buf, sizeof(fb.buf)); - fb.len = n; - fb.pos = 0; mp_reader_t reader; - reader.data = &fb; - reader.read_byte = file_buf_next_byte; - mp_raw_code_t *rc = mp_raw_code_load(&reader); - close(fb.fd); - return rc; -} - -#elif defined(__thumb2__) || defined(__xtensa__) -// fatfs file reader (assume thumb2 arch uses fatfs...) - -#include "lib/fatfs/ff.h" - -typedef struct _mp_lexer_file_buf_t { - FIL fp; - byte buf[20]; - uint16_t len; - uint16_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(void *fb_in) { - mp_lexer_file_buf_t *fb = fb_in; - if (fb->pos >= fb->len) { - if (fb->len < sizeof(fb->buf)) { - return (mp_uint_t)-1; - } else { - UINT n; - f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); - if (n == 0) { - return (mp_uint_t)-1; - } - fb->len = n; - fb->pos = 0; - } + if (!mp_reader_new_mem(&reader, buf, len, 0)) { + m_malloc_fail(BYTES_PER_WORD); // we need to raise a MemoryError } - return fb->buf[fb->pos++]; + return mp_raw_code_load(&reader); } mp_raw_code_t *mp_raw_code_load_file(const char *filename) { - mp_lexer_file_buf_t fb; - /*FRESULT res =*/ f_open(&fb.fp, filename, FA_READ); - UINT n; - f_read(&fb.fp, fb.buf, sizeof(fb.buf), &n); - fb.len = n; - fb.pos = 0; - mp_reader_t reader; - reader.data = &fb; - reader.read_byte = file_buf_next_byte; - mp_raw_code_t *rc = mp_raw_code_load(&reader); - - f_close(&fb.fp); - - return rc; + int ret = mp_reader_new_file(&reader, filename); + if (ret != 0) { + mp_raise_OSError(ret); + } + return mp_raw_code_load(&reader); } -#endif - #endif // MICROPY_PERSISTENT_CODE_LOAD #if MICROPY_PERSISTENT_CODE_SAVE diff --git a/py/persistentcode.h b/py/persistentcode.h index 8859bc577..d04e0b633 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -26,13 +26,9 @@ #ifndef MICROPY_INCLUDED_PY_PERSISTENTCODE_H #define MICROPY_INCLUDED_PY_PERSISTENTCODE_H -#include "py/obj.h" - -typedef struct _mp_reader_t { - void *data; - mp_uint_t (*read_byte)(void *data); - void (*close)(void *data); -} mp_reader_t; +#include "py/mpprint.h" +#include "py/reader.h" +#include "py/emitglue.h" mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader); mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len); diff --git a/py/py.mk b/py/py.mk index b0962d178..4630c21a9 100644 --- a/py/py.mk +++ b/py/py.mk @@ -113,6 +113,7 @@ PY_O_BASENAME = \ mpprint.o \ unicode.o \ mpz.o \ + reader.o \ lexer.o \ lexerstr.o \ lexerunix.o \ @@ -228,6 +229,7 @@ PY_O_BASENAME = \ ../extmod/vfs_fat_ffconf.o \ ../extmod/vfs_fat_diskio.o \ ../extmod/vfs_fat_file.o \ + ../extmod/vfs_fat_reader.o \ ../extmod/vfs_fat_lexer.o \ ../extmod/vfs_fat_misc.o \ ../extmod/utime_mphal.o \ diff --git a/py/reader.c b/py/reader.c new file mode 100644 index 000000000..d14fc416c --- /dev/null +++ b/py/reader.c @@ -0,0 +1,146 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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 +#include + +#include "py/mperrno.h" +#include "py/reader.h" + +typedef struct _mp_reader_mem_t { + size_t free_len; // if >0 mem is freed on close by: m_free(beg, free_len) + const byte *beg; + const byte *cur; + const byte *end; +} mp_reader_mem_t; + +STATIC mp_uint_t mp_reader_mem_readbyte(void *data) { + mp_reader_mem_t *reader = (mp_reader_mem_t*)data; + if (reader->cur < reader->end) { + return *reader->cur++; + } else { + return MP_READER_EOF; + } +} + +STATIC void mp_reader_mem_close(void *data) { + mp_reader_mem_t *reader = (mp_reader_mem_t*)data; + if (reader->free_len > 0) { + m_del(char, (char*)reader->beg, reader->free_len); + } + m_del_obj(mp_reader_mem_t, reader); +} + +bool mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len) { + mp_reader_mem_t *rm = m_new_obj_maybe(mp_reader_mem_t); + if (rm == NULL) { + return false; + } + rm->free_len = free_len; + rm->beg = buf; + rm->cur = buf; + rm->end = buf + len; + reader->data = rm; + reader->readbyte = mp_reader_mem_readbyte; + reader->close = mp_reader_mem_close; + return true; +} + +#if MICROPY_READER_POSIX + +#include +#include +#include +#include + +typedef struct _mp_reader_posix_t { + bool close_fd; + int fd; + size_t len; + size_t pos; + byte buf[20]; +} mp_reader_posix_t; + +STATIC mp_uint_t mp_reader_posix_readbyte(void *data) { + mp_reader_posix_t *reader = (mp_reader_posix_t*)data; + if (reader->pos >= reader->len) { + if (reader->len == 0) { + return MP_READER_EOF; + } else { + int n = read(reader->fd, reader->buf, sizeof(reader->buf)); + if (n <= 0) { + reader->len = 0; + return MP_READER_EOF; + } + reader->len = n; + reader->pos = 0; + } + } + return reader->buf[reader->pos++]; +} + +STATIC void mp_reader_posix_close(void *data) { + mp_reader_posix_t *reader = (mp_reader_posix_t*)data; + if (reader->close_fd) { + close(reader->fd); + } + m_del_obj(mp_reader_posix_t, reader); +} + +STATIC int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { + mp_reader_posix_t *rp = m_new_obj_maybe(mp_reader_posix_t); + if (rp == NULL) { + if (close_fd) { + close(fd); + } + return MP_ENOMEM; + } + rp->close_fd = close_fd; + rp->fd = fd; + int n = read(rp->fd, rp->buf, sizeof(rp->buf)); + if (n == -1) { + if (close_fd) { + close(fd); + } + return errno; + } + rp->len = n; + rp->pos = 0; + reader->data = rp; + reader->readbyte = mp_reader_posix_readbyte; + reader->close = mp_reader_posix_close; + return 0; // success +} + +int mp_reader_new_file(mp_reader_t *reader, const char *filename) { + int fd = open(filename, O_RDONLY, 0644); + if (fd < 0) { + return errno; + } + return mp_reader_new_file_from_fd(reader, fd, true); +} + +#endif diff --git a/py/reader.h b/py/reader.h new file mode 100644 index 000000000..f39cc90f8 --- /dev/null +++ b/py/reader.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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_READER_H +#define MICROPY_INCLUDED_PY_READER_H + +#include "py/obj.h" + +// the readbyte function must return the next byte in the input stream +// it must return MP_READER_EOF if end of stream +// it can be called again after returning MP_READER_EOF, and in that case must return MP_READER_EOF +#define MP_READER_EOF ((mp_uint_t)(-1)) + +typedef struct _mp_reader_t { + void *data; + mp_uint_t (*readbyte)(void *data); + void (*close)(void *data); +} mp_reader_t; + +bool mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len); +int mp_reader_new_file(mp_reader_t *reader, const char *filename); + +#endif // MICROPY_INCLUDED_PY_READER_H diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h index 264db3029..3eec0fcce 100644 --- a/stmhal/mpconfigport.h +++ b/stmhal/mpconfigport.h @@ -51,6 +51,7 @@ #define MICROPY_OPT_COMPUTED_GOTO (1) #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (0) #define MICROPY_OPT_MPZ_BITWISE (1) +#define MICROPY_READER_FATFS (1) // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (1) diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index f4f8d2d20..86e460566 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -54,6 +54,7 @@ // Printing debug to stderr may give tests which // check stdout a chance to pass, etc. #define MICROPY_DEBUG_PRINTER_DEST mp_stderr_print +#define MICROPY_READER_POSIX (1) #define MICROPY_USE_READLINE_HISTORY (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_REPL_EMACS_KEYS (1) -- cgit v1.2.3 From 511c083811ca538753dfccc7c908048b2c23bea4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Nov 2016 16:22:08 +1100 Subject: py/lexer: Rewrite mp_lexer_new_from_str_len in terms of mp_reader_mem. --- py/lexer.c | 9 +++++++++ py/lexerstr.c | 65 ----------------------------------------------------------- py/py.mk | 1 - 3 files changed, 9 insertions(+), 66 deletions(-) delete mode 100644 py/lexerstr.c (limited to 'py') diff --git a/py/lexer.c b/py/lexer.c index 4a7c8f580..f1be77805 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -28,6 +28,7 @@ #include #include "py/mpstate.h" +#include "py/reader.h" #include "py/lexer.h" #include "py/runtime.h" @@ -750,6 +751,14 @@ mp_lexer_t *mp_lexer_new(qstr src_name, void *stream_data, mp_lexer_stream_next_ return lex; } +mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len) { + mp_reader_t reader; + if (!mp_reader_new_mem(&reader, (const byte*)str, len, free_len)) { + return NULL; + } + return mp_lexer_new(src_name, reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); +} + void mp_lexer_free(mp_lexer_t *lex) { if (lex) { if (lex->stream_close) { diff --git a/py/lexerstr.c b/py/lexerstr.c deleted file mode 100644 index 9fdf4c1eb..000000000 --- a/py/lexerstr.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 "py/lexer.h" - -#if MICROPY_ENABLE_COMPILER - -typedef struct _mp_lexer_str_buf_t { - mp_uint_t free_len; // if > 0, src_beg will be freed when done by: m_free(src_beg, free_len) - const char *src_beg; // beginning of source - const char *src_cur; // current location in source - const char *src_end; // end (exclusive) of source -} mp_lexer_str_buf_t; - -STATIC mp_uint_t str_buf_next_byte(mp_lexer_str_buf_t *sb) { - if (sb->src_cur < sb->src_end) { - return *sb->src_cur++; - } else { - return MP_LEXER_EOF; - } -} - -STATIC void str_buf_free(mp_lexer_str_buf_t *sb) { - if (sb->free_len > 0) { - m_del(char, (char*)sb->src_beg, sb->free_len); - } - m_del_obj(mp_lexer_str_buf_t, sb); -} - -mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len) { - mp_lexer_str_buf_t *sb = m_new_obj_maybe(mp_lexer_str_buf_t); - if (sb == NULL) { - return NULL; - } - sb->free_len = free_len; - sb->src_beg = str; - sb->src_cur = str; - sb->src_end = str + len; - return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_byte_t)str_buf_next_byte, (mp_lexer_stream_close_t)str_buf_free); -} - -#endif // MICROPY_ENABLE_COMPILER diff --git a/py/py.mk b/py/py.mk index 4630c21a9..1557e07b7 100644 --- a/py/py.mk +++ b/py/py.mk @@ -115,7 +115,6 @@ PY_O_BASENAME = \ mpz.o \ reader.o \ lexer.o \ - lexerstr.o \ lexerunix.o \ parse.o \ scope.o \ -- cgit v1.2.3 From e5ef15a9d7bead2a60009caedbc128a8906c7ecd Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Nov 2016 16:25:06 +1100 Subject: py/lexer: Provide generic mp_lexer_new_from_file based on mp_reader. If a port defines MICROPY_READER_POSIX or MICROPY_READER_FATFS then lexer.c now provides an implementation of mp_lexer_new_from_file using the mp_reader_new_file function. --- cc3200/application.mk | 1 - esp8266/main.c | 8 ++--- esp8266/mpconfigport.h | 2 +- extmod/vfs_fat_lexer.c | 83 -------------------------------------------------- py/lexer.c | 13 ++++++++ py/lexerunix.c | 8 ----- py/py.mk | 1 - stmhal/Makefile | 1 - stmhal/lexerfatfs.c | 35 --------------------- teensy/lexerfatfs.c | 15 --------- 10 files changed, 16 insertions(+), 151 deletions(-) delete mode 100644 extmod/vfs_fat_lexer.c delete mode 100644 stmhal/lexerfatfs.c delete mode 100644 teensy/lexerfatfs.c (limited to 'py') diff --git a/cc3200/application.mk b/cc3200/application.mk index 300262c97..3ebc4b0d3 100644 --- a/cc3200/application.mk +++ b/cc3200/application.mk @@ -162,7 +162,6 @@ APP_STM_SRC_C = $(addprefix stmhal/,\ import.c \ input.c \ irq.c \ - lexerfatfs.c \ moduselect.c \ pybstdio.c \ ) diff --git a/esp8266/main.c b/esp8266/main.c index a2e747d21..5087c7d6a 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -109,17 +109,13 @@ void user_init(void) { system_init_done_cb(init_done); } -mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename); mp_import_stat_t fat_vfs_import_stat(const char *path); +#if !MICROPY_VFS_FAT mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - #if MICROPY_VFS_FAT - return fat_vfs_lexer_new_from_file(filename); - #else - (void)filename; return NULL; - #endif } +#endif mp_import_stat_t mp_import_stat(const char *path) { #if MICROPY_VFS_FAT diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h index adbec5d0d..cc0b22986 100644 --- a/esp8266/mpconfigport.h +++ b/esp8266/mpconfigport.h @@ -16,7 +16,7 @@ #define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTERS (1) #define MICROPY_DEBUG_PRINTER_DEST mp_debug_print -#define MICROPY_READER_FATFS (1) +#define MICROPY_READER_FATFS (MICROPY_VFS_FAT) #define MICROPY_ENABLE_GC (1) #define MICROPY_STACK_CHECK (1) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) diff --git a/extmod/vfs_fat_lexer.c b/extmod/vfs_fat_lexer.c deleted file mode 100644 index 91acdb830..000000000 --- a/extmod/vfs_fat_lexer.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 "py/mpconfig.h" -// *_ADHOC part is for cc3200 port which doesn't use general uPy -// infrastructure and instead duplicates code. TODO: Resolve. -#if MICROPY_VFS_FAT || MICROPY_FSUSERMOUNT || MICROPY_FSUSERMOUNT_ADHOC - -#include "py/lexer.h" -#include "lib/fatfs/ff.h" - -typedef struct _mp_lexer_file_buf_t { - FIL fp; - byte buf[20]; - uint16_t len; - uint16_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(mp_lexer_file_buf_t *fb) { - if (fb->pos >= fb->len) { - if (fb->len < sizeof(fb->buf)) { - return MP_LEXER_EOF; - } else { - UINT n; - f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); - if (n == 0) { - return MP_LEXER_EOF; - } - fb->len = n; - fb->pos = 0; - } - } - return fb->buf[fb->pos++]; -} - -STATIC void file_buf_close(mp_lexer_file_buf_t *fb) { - f_close(&fb->fp); - m_del_obj(mp_lexer_file_buf_t, fb); -} - -mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename); - -mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename) { - mp_lexer_file_buf_t *fb = m_new_obj_maybe(mp_lexer_file_buf_t); - if (fb == NULL) { - return NULL; - } - FRESULT res = f_open(&fb->fp, filename, FA_READ); - if (res != FR_OK) { - m_del_obj(mp_lexer_file_buf_t, fb); - return NULL; - } - UINT n; - f_read(&fb->fp, fb->buf, sizeof(fb->buf), &n); - fb->len = n; - fb->pos = 0; - return mp_lexer_new(qstr_from_str(filename), fb, (mp_lexer_stream_next_byte_t)file_buf_next_byte, (mp_lexer_stream_close_t)file_buf_close); -} - -#endif // MICROPY_VFS_FAT diff --git a/py/lexer.c b/py/lexer.c index f1be77805..d3e61b3b4 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -759,6 +759,19 @@ mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, mp_uint_t return mp_lexer_new(src_name, reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); } +#if MICROPY_READER_POSIX || MICROPY_READER_FATFS + +mp_lexer_t *mp_lexer_new_from_file(const char *filename) { + mp_reader_t reader; + int ret = mp_reader_new_file(&reader, filename); + if (ret != 0) { + return NULL; + } + return mp_lexer_new(qstr_from_str(filename), reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); +} + +#endif + void mp_lexer_free(mp_lexer_t *lex) { if (lex) { if (lex->stream_close) { diff --git a/py/lexerunix.c b/py/lexerunix.c index e8f8994a6..9e3755e76 100644 --- a/py/lexerunix.c +++ b/py/lexerunix.c @@ -85,12 +85,4 @@ mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) { return mp_lexer_new(filename, fb, (mp_lexer_stream_next_byte_t)file_buf_next_byte, (mp_lexer_stream_close_t)file_buf_close); } -mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - int fd = open(filename, O_RDONLY); - if (fd < 0) { - return NULL; - } - return mp_lexer_new_from_fd(qstr_from_str(filename), fd, true); -} - #endif // MICROPY_HELPER_LEXER_UNIX diff --git a/py/py.mk b/py/py.mk index 1557e07b7..1683b7b4b 100644 --- a/py/py.mk +++ b/py/py.mk @@ -229,7 +229,6 @@ PY_O_BASENAME = \ ../extmod/vfs_fat_diskio.o \ ../extmod/vfs_fat_file.o \ ../extmod/vfs_fat_reader.o \ - ../extmod/vfs_fat_lexer.o \ ../extmod/vfs_fat_misc.o \ ../extmod/utime_mphal.o \ ../extmod/uos_dupterm.o \ diff --git a/stmhal/Makefile b/stmhal/Makefile index 59199ea0a..c08484db1 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -154,7 +154,6 @@ SRC_C = \ modusocket.c \ modnetwork.c \ import.c \ - lexerfatfs.c \ extint.c \ usrsw.c \ rng.c \ diff --git a/stmhal/lexerfatfs.c b/stmhal/lexerfatfs.c deleted file mode 100644 index fd7f62dfd..000000000 --- a/stmhal/lexerfatfs.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of the MicroPython 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 "py/lexer.h" - -mp_lexer_t *fat_vfs_lexer_new_from_file(const char *filename); - -// TODO: Instead of such shims, probably better to let port #define -// mp_lexer_new_from_file to a function it wants to use. -mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - return fat_vfs_lexer_new_from_file(filename); -} diff --git a/teensy/lexerfatfs.c b/teensy/lexerfatfs.c deleted file mode 100644 index 84245f887..000000000 --- a/teensy/lexerfatfs.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -#include "misc.h" -#include "mpconfig.h" -#include "qstr.h" -#include "lexer.h" -typedef int FIL; -#include "../stmhal/lexerfatfs.h" - -mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - printf("import not implemented\n"); - return NULL; -} - -- cgit v1.2.3 From 66d955c218b66076a3d4300f70388c634c0d3099 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Nov 2016 18:12:55 +1100 Subject: py/lexer: Rewrite mp_lexer_new_from_fd in terms of mp_reader. --- py/lexer.c | 13 +++++++++ py/lexerunix.c | 88 ---------------------------------------------------------- py/py.mk | 1 - py/reader.c | 2 +- py/reader.h | 1 + 5 files changed, 15 insertions(+), 90 deletions(-) delete mode 100644 py/lexerunix.c (limited to 'py') diff --git a/py/lexer.c b/py/lexer.c index d3e61b3b4..9342ce8cc 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -770,6 +770,19 @@ mp_lexer_t *mp_lexer_new_from_file(const char *filename) { return mp_lexer_new(qstr_from_str(filename), reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); } +#if MICROPY_HELPER_LEXER_UNIX + +mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) { + mp_reader_t reader; + int ret = mp_reader_new_file_from_fd(&reader, fd, close_fd); + if (ret != 0) { + return NULL; + } + return mp_lexer_new(filename, reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); +} + +#endif + #endif void mp_lexer_free(mp_lexer_t *lex) { diff --git a/py/lexerunix.c b/py/lexerunix.c deleted file mode 100644 index 9e3755e76..000000000 --- a/py/lexerunix.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 "py/mpconfig.h" - -#if MICROPY_HELPER_LEXER_UNIX - -#include -#include -#include -#include -#include -#include - -#include "py/lexer.h" - -typedef struct _mp_lexer_file_buf_t { - int fd; - bool close_fd; - byte buf[20]; - mp_uint_t len; - mp_uint_t pos; -} mp_lexer_file_buf_t; - -STATIC mp_uint_t file_buf_next_byte(mp_lexer_file_buf_t *fb) { - if (fb->pos >= fb->len) { - if (fb->len == 0) { - return MP_LEXER_EOF; - } else { - int n = read(fb->fd, fb->buf, sizeof(fb->buf)); - if (n <= 0) { - fb->len = 0; - return MP_LEXER_EOF; - } - fb->len = n; - fb->pos = 0; - } - } - return fb->buf[fb->pos++]; -} - -STATIC void file_buf_close(mp_lexer_file_buf_t *fb) { - if (fb->close_fd) { - close(fb->fd); - } - m_del_obj(mp_lexer_file_buf_t, fb); -} - -mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) { - mp_lexer_file_buf_t *fb = m_new_obj_maybe(mp_lexer_file_buf_t); - if (fb == NULL) { - if (close_fd) { - close(fd); - } - return NULL; - } - fb->fd = fd; - fb->close_fd = close_fd; - int n = read(fb->fd, fb->buf, sizeof(fb->buf)); - fb->len = n; - fb->pos = 0; - return mp_lexer_new(filename, fb, (mp_lexer_stream_next_byte_t)file_buf_next_byte, (mp_lexer_stream_close_t)file_buf_close); -} - -#endif // MICROPY_HELPER_LEXER_UNIX diff --git a/py/py.mk b/py/py.mk index 1683b7b4b..82e0661ef 100644 --- a/py/py.mk +++ b/py/py.mk @@ -115,7 +115,6 @@ PY_O_BASENAME = \ mpz.o \ reader.o \ lexer.o \ - lexerunix.o \ parse.o \ scope.o \ compile.o \ diff --git a/py/reader.c b/py/reader.c index d14fc416c..d7de7aa6c 100644 --- a/py/reader.c +++ b/py/reader.c @@ -110,7 +110,7 @@ STATIC void mp_reader_posix_close(void *data) { m_del_obj(mp_reader_posix_t, reader); } -STATIC int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { +int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd) { mp_reader_posix_t *rp = m_new_obj_maybe(mp_reader_posix_t); if (rp == NULL) { if (close_fd) { diff --git a/py/reader.h b/py/reader.h index f39cc90f8..b02d96149 100644 --- a/py/reader.h +++ b/py/reader.h @@ -41,5 +41,6 @@ typedef struct _mp_reader_t { bool mp_reader_new_mem(mp_reader_t *reader, const byte *buf, size_t len, size_t free_len); int mp_reader_new_file(mp_reader_t *reader, const char *filename); +int mp_reader_new_file_from_fd(mp_reader_t *reader, int fd, bool close_fd); #endif // MICROPY_INCLUDED_PY_READER_H -- cgit v1.2.3 From 5bdf1650de782d766a648f992270306269cc985a Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 16 Nov 2016 18:27:20 +1100 Subject: py/lexer: Make lexer use an mp_reader as its source. --- esp8266/lexerstr32.c | 11 +++++++---- py/lexer.c | 31 +++++++++++++------------------ py/lexer.h | 15 +++------------ 3 files changed, 23 insertions(+), 34 deletions(-) (limited to 'py') diff --git a/esp8266/lexerstr32.c b/esp8266/lexerstr32.c index 669df8ea7..3fc62399e 100644 --- a/esp8266/lexerstr32.c +++ b/esp8266/lexerstr32.c @@ -35,10 +35,11 @@ typedef struct _mp_lexer_str32_buf_t { uint8_t byte_off; } mp_lexer_str32_buf_t; -STATIC mp_uint_t str32_buf_next_byte(mp_lexer_str32_buf_t *sb) { +STATIC mp_uint_t str32_buf_next_byte(void *sb_in) { + mp_lexer_str32_buf_t *sb = (mp_lexer_str32_buf_t*)sb_in; byte c = sb->val & 0xff; if (c == 0) { - return MP_LEXER_EOF; + return MP_READER_EOF; } if (++sb->byte_off > 3) { @@ -51,7 +52,8 @@ STATIC mp_uint_t str32_buf_next_byte(mp_lexer_str32_buf_t *sb) { return c; } -STATIC void str32_buf_free(mp_lexer_str32_buf_t *sb) { +STATIC void str32_buf_free(void *sb_in) { + mp_lexer_str32_buf_t *sb = (mp_lexer_str32_buf_t*)sb_in; m_del_obj(mp_lexer_str32_buf_t, sb); } @@ -63,7 +65,8 @@ mp_lexer_t *mp_lexer_new_from_str32(qstr src_name, const char *str, mp_uint_t le sb->byte_off = (uint32_t)str & 3; sb->src_cur = (uint32_t*)(str - sb->byte_off); sb->val = *sb->src_cur++ >> sb->byte_off * 8; - return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_byte_t)str32_buf_next_byte, (mp_lexer_stream_close_t)str32_buf_free); + mp_reader_t reader = {sb, str32_buf_next_byte, str32_buf_free}; + return mp_lexer_new(src_name, reader); } #endif // MICROPY_ENABLE_COMPILER diff --git a/py/lexer.c b/py/lexer.c index 9342ce8cc..c6ecdf1f8 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -52,6 +52,7 @@ STATIC bool str_strn_equal(const char *str, const char *strn, mp_uint_t len) { return i == len && *str == 0; } +#define MP_LEXER_EOF ((unichar)MP_READER_EOF) #define CUR_CHAR(lex) ((lex)->chr0) STATIC bool is_end(mp_lexer_t *lex) { @@ -145,7 +146,7 @@ STATIC void next_char(mp_lexer_t *lex) { lex->chr0 = lex->chr1; lex->chr1 = lex->chr2; - lex->chr2 = lex->stream_next_byte(lex->stream_data); + lex->chr2 = lex->reader.readbyte(lex->reader.data); if (lex->chr0 == '\r') { // CR is a new line, converted to LF @@ -153,7 +154,7 @@ STATIC void next_char(mp_lexer_t *lex) { if (lex->chr1 == '\n') { // CR LF is a single new line lex->chr1 = lex->chr2; - lex->chr2 = lex->stream_next_byte(lex->stream_data); + lex->chr2 = lex->reader.readbyte(lex->reader.data); } } @@ -689,21 +690,17 @@ STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, bool first_token) { } } -mp_lexer_t *mp_lexer_new(qstr src_name, void *stream_data, mp_lexer_stream_next_byte_t stream_next_byte, mp_lexer_stream_close_t stream_close) { +mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) { mp_lexer_t *lex = m_new_obj_maybe(mp_lexer_t); // check for memory allocation error if (lex == NULL) { - if (stream_close) { - stream_close(stream_data); - } + reader.close(reader.data); return NULL; } lex->source_name = src_name; - lex->stream_data = stream_data; - lex->stream_next_byte = stream_next_byte; - lex->stream_close = stream_close; + lex->reader = reader; lex->line = 1; lex->column = 1; lex->emit_dent = 0; @@ -724,9 +721,9 @@ mp_lexer_t *mp_lexer_new(qstr src_name, void *stream_data, mp_lexer_stream_next_ lex->indent_level[0] = 0; // preload characters - lex->chr0 = stream_next_byte(stream_data); - lex->chr1 = stream_next_byte(stream_data); - lex->chr2 = stream_next_byte(stream_data); + lex->chr0 = reader.readbyte(reader.data); + lex->chr1 = reader.readbyte(reader.data); + lex->chr2 = reader.readbyte(reader.data); // if input stream is 0, 1 or 2 characters long and doesn't end in a newline, then insert a newline at the end if (lex->chr0 == MP_LEXER_EOF) { @@ -756,7 +753,7 @@ mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, mp_uint_t if (!mp_reader_new_mem(&reader, (const byte*)str, len, free_len)) { return NULL; } - return mp_lexer_new(src_name, reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); + return mp_lexer_new(src_name, reader); } #if MICROPY_READER_POSIX || MICROPY_READER_FATFS @@ -767,7 +764,7 @@ mp_lexer_t *mp_lexer_new_from_file(const char *filename) { if (ret != 0) { return NULL; } - return mp_lexer_new(qstr_from_str(filename), reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); + return mp_lexer_new(qstr_from_str(filename), reader); } #if MICROPY_HELPER_LEXER_UNIX @@ -778,7 +775,7 @@ mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) { if (ret != 0) { return NULL; } - return mp_lexer_new(filename, reader.data, (mp_lexer_stream_next_byte_t)reader.readbyte, (mp_lexer_stream_close_t)reader.close); + return mp_lexer_new(filename, reader); } #endif @@ -787,9 +784,7 @@ mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) { void mp_lexer_free(mp_lexer_t *lex) { if (lex) { - if (lex->stream_close) { - lex->stream_close(lex->stream_data); - } + lex->reader.close(lex->reader.data); vstr_clear(&lex->vstr); m_del(uint16_t, lex->indent_level, lex->alloc_indent_level); m_del_obj(mp_lexer_t, lex); diff --git a/py/lexer.h b/py/lexer.h index 463be5fff..1461f9c8c 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -30,6 +30,7 @@ #include "py/mpconfig.h" #include "py/qstr.h" +#include "py/reader.h" /* lexer.h -- simple tokeniser for Micro Python * @@ -142,21 +143,11 @@ typedef enum _mp_token_kind_t { MP_TOKEN_DEL_MINUS_MORE, } mp_token_kind_t; -// the next-byte function must return the next byte in the stream -// it must return MP_LEXER_EOF if end of stream -// it can be called again after returning MP_LEXER_EOF, and in that case must return MP_LEXER_EOF -#define MP_LEXER_EOF ((unichar)(-1)) - -typedef mp_uint_t (*mp_lexer_stream_next_byte_t)(void*); -typedef void (*mp_lexer_stream_close_t)(void*); - // this data structure is exposed for efficiency // public members are: source_name, tok_line, tok_column, tok_kind, vstr typedef struct _mp_lexer_t { qstr source_name; // name of source - void *stream_data; // data for stream - mp_lexer_stream_next_byte_t stream_next_byte; // stream callback to get next byte - mp_lexer_stream_close_t stream_close; // stream callback to free + mp_reader_t reader; // stream source unichar chr0, chr1, chr2; // current cached characters from source @@ -176,7 +167,7 @@ typedef struct _mp_lexer_t { vstr_t vstr; // token data } mp_lexer_t; -mp_lexer_t *mp_lexer_new(qstr src_name, void *stream_data, mp_lexer_stream_next_byte_t stream_next_byte, mp_lexer_stream_close_t stream_close); +mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader); mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len); void mp_lexer_free(mp_lexer_t *lex); -- cgit v1.2.3 From 8f5bc3ffc0603bbc338f9a24bd7d278b72d5c9d7 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Sun, 20 Nov 2016 23:49:45 +0300 Subject: stmhal/moduselect: Move to extmod/ for reuse by other ports. --- extmod/moduselect.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++ py/builtin.h | 1 + py/mpconfig.h | 5 + py/objmodule.c | 3 + py/py.mk | 1 + stmhal/Makefile | 1 - stmhal/moduselect.c | 311 ------------------------------------------------- stmhal/mpconfigport.h | 3 +- stmhal/portmodules.h | 1 - 9 files changed, 327 insertions(+), 315 deletions(-) create mode 100644 extmod/moduselect.c delete mode 100644 stmhal/moduselect.c (limited to 'py') diff --git a/extmod/moduselect.c b/extmod/moduselect.c new file mode 100644 index 000000000..6cf1e70a2 --- /dev/null +++ b/extmod/moduselect.c @@ -0,0 +1,316 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 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 "py/mpconfig.h" +#if MICROPY_PY_USELECT + +#include + +#include "py/runtime.h" +#include "py/obj.h" +#include "py/objlist.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "pybioctl.h" + +// Flags for poll() +#define FLAG_ONESHOT (1) + +/// \module select - Provides select function to wait for events on a stream +/// +/// This module provides the select function. + +typedef struct _poll_obj_t { + mp_obj_t obj; + mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, mp_uint_t arg, int *errcode); + mp_uint_t flags; + mp_uint_t flags_ret; +} poll_obj_t; + +STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { + for (mp_uint_t i = 0; i < obj_len; i++) { + mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); + if (elem->value == NULL) { + // object not found; get its ioctl and add it to the poll list + mp_obj_type_t *type = mp_obj_get_type(obj[i]); + const mp_stream_p_t *stream_p = type->protocol; + if (stream_p == NULL || stream_p->ioctl == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "object with stream.ioctl required")); + } + poll_obj_t *poll_obj = m_new_obj(poll_obj_t); + poll_obj->obj = obj[i]; + poll_obj->ioctl = stream_p->ioctl; + poll_obj->flags = flags; + poll_obj->flags_ret = 0; + elem->value = poll_obj; + } else { + // object exists; update its flags + if (or_flags) { + ((poll_obj_t*)elem->value)->flags |= flags; + } else { + ((poll_obj_t*)elem->value)->flags = flags; + } + } + } +} + +// poll each object in the map +STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { + mp_uint_t n_ready = 0; + for (mp_uint_t i = 0; i < poll_map->alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) { + continue; + } + + poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value; + int errcode; + mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_IOCTL_POLL, poll_obj->flags, &errcode); + poll_obj->flags_ret = ret; + + if (ret == -1) { + // error doing ioctl + mp_raise_OSError(errcode); + } + + if (ret != 0) { + // object is ready + n_ready += 1; + if (rwx_num != NULL) { + if (ret & MP_IOCTL_POLL_RD) { + rwx_num[0] += 1; + } + if (ret & MP_IOCTL_POLL_WR) { + rwx_num[1] += 1; + } + if ((ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { + rwx_num[2] += 1; + } + } + } + } + return n_ready; +} + +/// \function select(rlist, wlist, xlist[, timeout]) +STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { + // get array data from tuple/list arguments + mp_uint_t rwx_len[3]; + mp_obj_t *r_array, *w_array, *x_array; + mp_obj_get_array(args[0], &rwx_len[0], &r_array); + mp_obj_get_array(args[1], &rwx_len[1], &w_array); + mp_obj_get_array(args[2], &rwx_len[2], &x_array); + + // get timeout + mp_uint_t timeout = -1; + if (n_args == 4) { + if (args[3] != mp_const_none) { + #if MICROPY_PY_BUILTINS_FLOAT + float timeout_f = mp_obj_get_float(args[3]); + if (timeout_f >= 0) { + timeout = (mp_uint_t)(timeout_f * 1000); + } + #else + timeout = mp_obj_get_int(args[3]) * 1000; + #endif + } + } + + // merge separate lists and get the ioctl function for each object + mp_map_t poll_map; + mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); + poll_map_add(&poll_map, r_array, rwx_len[0], MP_IOCTL_POLL_RD, true); + poll_map_add(&poll_map, w_array, rwx_len[1], MP_IOCTL_POLL_WR, true); + poll_map_add(&poll_map, x_array, rwx_len[2], MP_IOCTL_POLL_ERR | MP_IOCTL_POLL_HUP, true); + + mp_uint_t start_tick = mp_hal_ticks_ms(); + rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; + for (;;) { + // poll the objects + mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); + + if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { + // one or more objects are ready, or we had a timeout + mp_obj_t list_array[3]; + list_array[0] = mp_obj_new_list(rwx_len[0], NULL); + list_array[1] = mp_obj_new_list(rwx_len[1], NULL); + list_array[2] = mp_obj_new_list(rwx_len[2], NULL); + rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; + for (mp_uint_t i = 0; i < poll_map.alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) { + continue; + } + poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value; + if (poll_obj->flags_ret & MP_IOCTL_POLL_RD) { + ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj; + } + if (poll_obj->flags_ret & MP_IOCTL_POLL_WR) { + ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj; + } + if ((poll_obj->flags_ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { + ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj; + } + } + mp_map_deinit(&poll_map); + return mp_obj_new_tuple(3, list_array); + } + __WFI(); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); + +/// \class Poll - poll class + +typedef struct _mp_obj_poll_t { + mp_obj_base_t base; + mp_map_t poll_map; +} mp_obj_poll_t; + +/// \method register(obj[, eventmask]) +STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) { + mp_obj_poll_t *self = args[0]; + mp_uint_t flags; + if (n_args == 3) { + flags = mp_obj_get_int(args[2]); + } else { + flags = MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR; + } + poll_map_add(&self->poll_map, &args[1], 1, flags, false); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); + +/// \method unregister(obj) +STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { + mp_obj_poll_t *self = self_in; + mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); + // TODO raise KeyError if obj didn't exist in map + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); + +/// \method modify(obj, eventmask) +STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { + mp_obj_poll_t *self = self_in; + mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); + if (elem == NULL) { + mp_raise_OSError(MP_ENOENT); + } + ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify); + +/// \method poll([timeout]) +/// Timeout is in milliseconds. +STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) { + mp_obj_poll_t *self = args[0]; + + // work out timeout (its given already in ms) + mp_uint_t timeout = -1; + int flags = 0; + if (n_args >= 2) { + if (args[1] != mp_const_none) { + mp_int_t timeout_i = mp_obj_get_int(args[1]); + if (timeout_i >= 0) { + timeout = timeout_i; + } + } + if (n_args >= 3) { + flags = mp_obj_get_int(args[2]); + } + } + + mp_uint_t start_tick = mp_hal_ticks_ms(); + for (;;) { + // poll the objects + mp_uint_t n_ready = poll_map_poll(&self->poll_map, NULL); + + if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { + // one or more objects are ready, or we had a timeout + mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL); + n_ready = 0; + for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) { + if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) { + continue; + } + poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value; + if (poll_obj->flags_ret != 0) { + mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)}; + ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple); + if (flags & FLAG_ONESHOT) { + // Don't poll next time, until new event flags will be set explicitly + poll_obj->flags = 0; + } + } + } + return ret_list; + } + __WFI(); + } +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll); + +STATIC const mp_map_elem_t poll_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_register), (mp_obj_t)&poll_register_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_unregister), (mp_obj_t)&poll_unregister_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_modify), (mp_obj_t)&poll_modify_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&poll_poll_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); + +STATIC const mp_obj_type_t mp_type_poll = { + { &mp_type_type }, + .name = MP_QSTR_poll, + .locals_dict = (mp_obj_t)&poll_locals_dict, +}; + +/// \function poll() +STATIC mp_obj_t select_poll(void) { + mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t); + poll->base.type = &mp_type_poll; + mp_map_init(&poll->poll_map, 0); + return poll; +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll); + +STATIC const mp_map_elem_t mp_module_select_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uselect) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_select_select_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&mp_select_poll_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_RD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_WR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_ERR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_HUP) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table); + +const mp_obj_module_t mp_module_uselect = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_select_globals, +}; + +#endif // MICROPY_PY_WEBSOCKET diff --git a/py/builtin.h b/py/builtin.h index 4477fd246..893e47104 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -108,6 +108,7 @@ extern const mp_obj_module_t mp_module_uheapq; extern const mp_obj_module_t mp_module_uhashlib; extern const mp_obj_module_t mp_module_ubinascii; extern const mp_obj_module_t mp_module_urandom; +extern const mp_obj_module_t mp_module_uselect; extern const mp_obj_module_t mp_module_ussl; extern const mp_obj_module_t mp_module_machine; extern const mp_obj_module_t mp_module_lwip; diff --git a/py/mpconfig.h b/py/mpconfig.h index 1980e649c..4572fc4cc 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -873,6 +873,11 @@ typedef double mp_float_t; #define MICROPY_PY_UERRNO (0) #endif +// Whether to provide "uselect" module (baremetal implementation) +#ifndef MICROPY_PY_USELECT +#define MICROPY_PY_USELECT (0) +#endif + // Whether to provide "utime" module functions implementation // in terms of mp_hal_* functions. #ifndef MICROPY_PY_UTIME_MP_HAL diff --git a/py/objmodule.c b/py/objmodule.c index 9b06e3b7b..6f7d35d42 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -198,6 +198,9 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #if MICROPY_PY_URANDOM { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&mp_module_urandom) }, #endif +#if MICROPY_PY_USELECT + { MP_ROM_QSTR(MP_QSTR_uselect), MP_ROM_PTR(&mp_module_uselect) }, +#endif #if MICROPY_PY_USSL { MP_ROM_QSTR(MP_QSTR_ussl), MP_ROM_PTR(&mp_module_ussl) }, #endif diff --git a/py/py.mk b/py/py.mk index 82e0661ef..ec67ac2a0 100644 --- a/py/py.mk +++ b/py/py.mk @@ -219,6 +219,7 @@ PY_O_BASENAME = \ ../extmod/modussl_axtls.o \ ../extmod/modussl_mbedtls.o \ ../extmod/modurandom.o \ + ../extmod/moduselect.o \ ../extmod/modwebsocket.o \ ../extmod/modwebrepl.o \ ../extmod/modframebuf.o \ diff --git a/stmhal/Makefile b/stmhal/Makefile index c08484db1..3b70f6b93 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -150,7 +150,6 @@ SRC_C = \ modstm.c \ moduos.c \ modutime.c \ - moduselect.c \ modusocket.c \ modnetwork.c \ import.c \ diff --git a/stmhal/moduselect.c b/stmhal/moduselect.c deleted file mode 100644 index 0d76953c6..000000000 --- a/stmhal/moduselect.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * This file is part of the Micro Python project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 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 - -#include "py/runtime.h" -#include "py/obj.h" -#include "py/objlist.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "pybioctl.h" - -// Flags for poll() -#define FLAG_ONESHOT (1) - -/// \module select - Provides select function to wait for events on a stream -/// -/// This module provides the select function. - -typedef struct _poll_obj_t { - mp_obj_t obj; - mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, mp_uint_t arg, int *errcode); - mp_uint_t flags; - mp_uint_t flags_ret; -} poll_obj_t; - -STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) { - for (mp_uint_t i = 0; i < obj_len; i++) { - mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); - if (elem->value == NULL) { - // object not found; get its ioctl and add it to the poll list - mp_obj_type_t *type = mp_obj_get_type(obj[i]); - const mp_stream_p_t *stream_p = type->protocol; - if (stream_p == NULL || stream_p->ioctl == NULL) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "object with stream.ioctl required")); - } - poll_obj_t *poll_obj = m_new_obj(poll_obj_t); - poll_obj->obj = obj[i]; - poll_obj->ioctl = stream_p->ioctl; - poll_obj->flags = flags; - poll_obj->flags_ret = 0; - elem->value = poll_obj; - } else { - // object exists; update its flags - if (or_flags) { - ((poll_obj_t*)elem->value)->flags |= flags; - } else { - ((poll_obj_t*)elem->value)->flags = flags; - } - } - } -} - -// poll each object in the map -STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { - mp_uint_t n_ready = 0; - for (mp_uint_t i = 0; i < poll_map->alloc; ++i) { - if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) { - continue; - } - - poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value; - int errcode; - mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_IOCTL_POLL, poll_obj->flags, &errcode); - poll_obj->flags_ret = ret; - - if (ret == -1) { - // error doing ioctl - mp_raise_OSError(errcode); - } - - if (ret != 0) { - // object is ready - n_ready += 1; - if (rwx_num != NULL) { - if (ret & MP_IOCTL_POLL_RD) { - rwx_num[0] += 1; - } - if (ret & MP_IOCTL_POLL_WR) { - rwx_num[1] += 1; - } - if ((ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { - rwx_num[2] += 1; - } - } - } - } - return n_ready; -} - -/// \function select(rlist, wlist, xlist[, timeout]) -STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { - // get array data from tuple/list arguments - mp_uint_t rwx_len[3]; - mp_obj_t *r_array, *w_array, *x_array; - mp_obj_get_array(args[0], &rwx_len[0], &r_array); - mp_obj_get_array(args[1], &rwx_len[1], &w_array); - mp_obj_get_array(args[2], &rwx_len[2], &x_array); - - // get timeout - mp_uint_t timeout = -1; - if (n_args == 4) { - if (args[3] != mp_const_none) { - #if MICROPY_PY_BUILTINS_FLOAT - float timeout_f = mp_obj_get_float(args[3]); - if (timeout_f >= 0) { - timeout = (mp_uint_t)(timeout_f * 1000); - } - #else - timeout = mp_obj_get_int(args[3]) * 1000; - #endif - } - } - - // merge separate lists and get the ioctl function for each object - mp_map_t poll_map; - mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); - poll_map_add(&poll_map, r_array, rwx_len[0], MP_IOCTL_POLL_RD, true); - poll_map_add(&poll_map, w_array, rwx_len[1], MP_IOCTL_POLL_WR, true); - poll_map_add(&poll_map, x_array, rwx_len[2], MP_IOCTL_POLL_ERR | MP_IOCTL_POLL_HUP, true); - - mp_uint_t start_tick = mp_hal_ticks_ms(); - rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; - for (;;) { - // poll the objects - mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len); - - if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { - // one or more objects are ready, or we had a timeout - mp_obj_t list_array[3]; - list_array[0] = mp_obj_new_list(rwx_len[0], NULL); - list_array[1] = mp_obj_new_list(rwx_len[1], NULL); - list_array[2] = mp_obj_new_list(rwx_len[2], NULL); - rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; - for (mp_uint_t i = 0; i < poll_map.alloc; ++i) { - if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) { - continue; - } - poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value; - if (poll_obj->flags_ret & MP_IOCTL_POLL_RD) { - ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj; - } - if (poll_obj->flags_ret & MP_IOCTL_POLL_WR) { - ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj; - } - if ((poll_obj->flags_ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { - ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj; - } - } - mp_map_deinit(&poll_map); - return mp_obj_new_tuple(3, list_array); - } - __WFI(); - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select); - -/// \class Poll - poll class - -typedef struct _mp_obj_poll_t { - mp_obj_base_t base; - mp_map_t poll_map; -} mp_obj_poll_t; - -/// \method register(obj[, eventmask]) -STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) { - mp_obj_poll_t *self = args[0]; - mp_uint_t flags; - if (n_args == 3) { - flags = mp_obj_get_int(args[2]); - } else { - flags = MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR; - } - poll_map_add(&self->poll_map, &args[1], 1, flags, false); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register); - -/// \method unregister(obj) -STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) { - mp_obj_poll_t *self = self_in; - mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND); - // TODO raise KeyError if obj didn't exist in map - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister); - -/// \method modify(obj, eventmask) -STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) { - mp_obj_poll_t *self = self_in; - mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP); - if (elem == NULL) { - mp_raise_OSError(MP_ENOENT); - } - ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify); - -/// \method poll([timeout]) -/// Timeout is in milliseconds. -STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) { - mp_obj_poll_t *self = args[0]; - - // work out timeout (its given already in ms) - mp_uint_t timeout = -1; - int flags = 0; - if (n_args >= 2) { - if (args[1] != mp_const_none) { - mp_int_t timeout_i = mp_obj_get_int(args[1]); - if (timeout_i >= 0) { - timeout = timeout_i; - } - } - if (n_args >= 3) { - flags = mp_obj_get_int(args[2]); - } - } - - mp_uint_t start_tick = mp_hal_ticks_ms(); - for (;;) { - // poll the objects - mp_uint_t n_ready = poll_map_poll(&self->poll_map, NULL); - - if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) { - // one or more objects are ready, or we had a timeout - mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL); - n_ready = 0; - for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) { - if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) { - continue; - } - poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value; - if (poll_obj->flags_ret != 0) { - mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)}; - ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple); - if (flags & FLAG_ONESHOT) { - // Don't poll next time, until new event flags will be set explicitly - poll_obj->flags = 0; - } - } - } - return ret_list; - } - __WFI(); - } -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll); - -STATIC const mp_map_elem_t poll_locals_dict_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR_register), (mp_obj_t)&poll_register_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_unregister), (mp_obj_t)&poll_unregister_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_modify), (mp_obj_t)&poll_modify_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&poll_poll_obj }, -}; -STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table); - -STATIC const mp_obj_type_t mp_type_poll = { - { &mp_type_type }, - .name = MP_QSTR_poll, - .locals_dict = (mp_obj_t)&poll_locals_dict, -}; - -/// \function poll() -STATIC mp_obj_t select_poll(void) { - mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t); - poll->base.type = &mp_type_poll; - mp_map_init(&poll->poll_map, 0); - return poll; -} -MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll); - -STATIC const mp_map_elem_t mp_module_select_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uselect) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_select_select_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&mp_select_poll_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_RD) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_WR) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_ERR) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_HUP) }, -}; - -STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table); - -const mp_obj_module_t mp_module_uselect = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t*)&mp_module_select_globals, -}; diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h index 3eec0fcce..66ec36472 100644 --- a/stmhal/mpconfigport.h +++ b/stmhal/mpconfigport.h @@ -92,6 +92,7 @@ #define MICROPY_PY_UBINASCII (1) #define MICROPY_PY_URANDOM (1) #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_USELECT (1) #define MICROPY_PY_UCTYPES (1) #define MICROPY_PY_UZLIB (1) #define MICROPY_PY_UJSON (1) @@ -135,7 +136,6 @@ extern const struct _mp_obj_module_t mp_module_uheapq; extern const struct _mp_obj_module_t mp_module_uhashlib; extern const struct _mp_obj_module_t mp_module_uos; extern const struct _mp_obj_module_t mp_module_utime; -extern const struct _mp_obj_module_t mp_module_uselect; extern const struct _mp_obj_module_t mp_module_usocket; extern const struct _mp_obj_module_t mp_module_network; @@ -159,7 +159,6 @@ extern const struct _mp_obj_module_t mp_module_network; { MP_OBJ_NEW_QSTR(MP_QSTR_stm), (mp_obj_t)&stm_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&mp_module_uos }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&mp_module_utime }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_uselect), (mp_obj_t)&mp_module_uselect }, \ SOCKET_BUILTIN_MODULE \ NETWORK_BUILTIN_MODULE \ diff --git a/stmhal/portmodules.h b/stmhal/portmodules.h index 173d53cc6..0b460f38c 100644 --- a/stmhal/portmodules.h +++ b/stmhal/portmodules.h @@ -28,7 +28,6 @@ extern const mp_obj_module_t pyb_module; extern const mp_obj_module_t stm_module; extern const mp_obj_module_t mp_module_uos; extern const mp_obj_module_t mp_module_utime; -extern const mp_obj_module_t mp_module_uselect; extern const mp_obj_module_t mp_module_usocket; // additional helper functions exported by the modules -- cgit v1.2.3 From 037e6912c60f4f05a3866d525392f9c76914d0f1 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 22 Nov 2016 01:33:55 +0300 Subject: py/objtype: Implement __call__ handling for an instance w/o heap alloc. By refactoring and reusing code from objboundmeth. --- py/objboundmeth.c | 16 +++++++++------- py/objtype.c | 15 ++++++++------- py/runtime.h | 1 + 3 files changed, 18 insertions(+), 14 deletions(-) (limited to 'py') diff --git a/py/objboundmeth.c b/py/objboundmeth.c index e32caba33..57be6a6cf 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -47,11 +47,8 @@ STATIC void bound_meth_print(const mp_print_t *print, mp_obj_t o_in, mp_print_ki } #endif -STATIC mp_obj_t bound_meth_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in); - - // need to insert self->self before all other args and then call self->meth - +mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args) { + // need to insert self before all other args and then call meth size_t n_total = n_args + 2 * n_kw; mp_obj_t *args2 = NULL; mp_obj_t *free_args2 = NULL; @@ -64,15 +61,20 @@ STATIC mp_obj_t bound_meth_call(mp_obj_t self_in, size_t n_args, size_t n_kw, co // (fallback to) use stack to allocate temporary args array args2 = alloca(sizeof(mp_obj_t) * (1 + n_total)); } - args2[0] = self->self; + args2[0] = self; memcpy(args2 + 1, args, n_total * sizeof(mp_obj_t)); - mp_obj_t res = mp_call_function_n_kw(self->meth, n_args + 1, n_kw, &args2[0]); + mp_obj_t res = mp_call_function_n_kw(meth, n_args + 1, n_kw, args2); if (free_args2 != NULL) { m_del(mp_obj_t, free_args2, 1 + n_total); } return res; } +STATIC mp_obj_t bound_meth_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in); + return mp_call_method_self_n_kw(self->meth, self->self, n_args, n_kw, args); +} + #if MICROPY_PY_FUNCTION_ATTRS STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { diff --git a/py/objtype.c b/py/objtype.c index 8b46c5400..c20b0693e 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2014 Paul Sokolovsky + * Copyright (c) 2014-2016 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -687,9 +687,8 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value } } -STATIC mp_obj_t mp_obj_instance_get_call(mp_obj_t self_in) { +STATIC mp_obj_t mp_obj_instance_get_call(mp_obj_t self_in, mp_obj_t *member) { mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL}; struct class_lookup_data lookup = { .obj = self, .attr = MP_QSTR___call__, @@ -702,11 +701,13 @@ STATIC mp_obj_t mp_obj_instance_get_call(mp_obj_t self_in) { } bool mp_obj_instance_is_callable(mp_obj_t self_in) { - return mp_obj_instance_get_call(self_in) != MP_OBJ_NULL; + mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL}; + return mp_obj_instance_get_call(self_in, member) != MP_OBJ_NULL; } mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_obj_t call = mp_obj_instance_get_call(self_in); + mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL}; + mp_obj_t call = mp_obj_instance_get_call(self_in, member); if (call == MP_OBJ_NULL) { if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) { mp_raise_msg(&mp_type_TypeError, "object not callable"); @@ -719,8 +720,8 @@ mp_obj_t mp_obj_instance_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons if (call == MP_OBJ_SENTINEL) { return mp_call_function_n_kw(self->subobj[0], n_args, n_kw, args); } - mp_obj_t meth = mp_obj_new_bound_meth(call, self_in); - return mp_call_function_n_kw(meth, n_args, n_kw, args); + + return mp_call_method_self_n_kw(member[0], member[1], n_args, n_kw, args); } STATIC mp_obj_t instance_getiter(mp_obj_t self_in) { diff --git a/py/runtime.h b/py/runtime.h index 80488098a..3532b838d 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -95,6 +95,7 @@ mp_obj_t mp_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2); mp_obj_t mp_call_function_n_kw(mp_obj_t fun, mp_uint_t n_args, mp_uint_t n_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_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args); // Call function and catch/dump exception - for Python callbacks from C code void mp_call_function_1_protected(mp_obj_t fun, mp_obj_t arg); void mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2); -- cgit v1.2.3 From e6cf5fb2cc43a94210203f4d45d38f29f33a82a8 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sat, 26 Nov 2016 16:15:55 +1100 Subject: py/compile: Remove comment about TODO for short circuiting for if-stmt. Short circuiting is handled correctly by c_if_cond, and constants within short-circuit expressions are optimised by the parser. --- py/compile.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index 6efd15eff..bc8d8d16d 100644 --- a/py/compile.c +++ b/py/compile.c @@ -1203,8 +1203,6 @@ STATIC void compile_assert_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { } STATIC void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) { - // TODO proper and/or short circuiting - uint l_end = comp_next_label(comp); // optimisation: don't emit anything when "if False" -- cgit v1.2.3 From 612599587bef71e1deca9a77a8e2e16e69b045de Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 28 Nov 2016 09:24:50 +1100 Subject: py: Factor out common code from assemblers into asmbase.[ch]. All assemblers should "derive" from mp_asm_base_t. --- py/asmarm.c | 99 ------------------------------------- py/asmarm.h | 20 +++----- py/asmbase.c | 104 +++++++++++++++++++++++++++++++++++++++ py/asmbase.h | 68 ++++++++++++++++++++++++++ py/asmthumb.c | 134 +++++++-------------------------------------------- py/asmthumb.h | 21 +++----- py/asmx64.c | 114 +++---------------------------------------- py/asmx64.h | 24 ++++----- py/asmx86.c | 102 --------------------------------------- py/asmx86.h | 24 ++++----- py/emitinlinethumb.c | 18 ++++--- py/emitnative.c | 98 +++++++++---------------------------- py/py.mk | 1 + 13 files changed, 262 insertions(+), 565 deletions(-) create mode 100644 py/asmbase.c create mode 100644 py/asmbase.h (limited to 'py') diff --git a/py/asmarm.c b/py/asmarm.c index 2c389ac8c..663f86156 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -38,50 +38,6 @@ #define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000) -struct _asm_arm_t { - uint pass; - mp_uint_t code_offset; - mp_uint_t code_size; - byte *code_base; - byte dummy_data[4]; - - mp_uint_t max_num_labels; - mp_uint_t *label_offsets; - uint push_reglist; - uint stack_adjust; -}; - -asm_arm_t *asm_arm_new(uint max_num_labels) { - asm_arm_t *as; - - as = m_new0(asm_arm_t, 1); - as->max_num_labels = max_num_labels; - as->label_offsets = m_new(mp_uint_t, max_num_labels); - - return as; -} - -void asm_arm_free(asm_arm_t *as, bool free_code) { - if (free_code) { - MP_PLAT_FREE_EXEC(as->code_base, as->code_size); - } - m_del(mp_uint_t, as->label_offsets, as->max_num_labels); - m_del_obj(asm_arm_t, as); -} - -void asm_arm_start_pass(asm_arm_t *as, uint pass) { - if (pass == ASM_ARM_PASS_COMPUTE) { - memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t)); - } else if (pass == ASM_ARM_PASS_EMIT) { - MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); - if (as->code_base == NULL) { - assert(0); - } - } - as->pass = pass; - as->code_offset = 0; -} - void asm_arm_end_pass(asm_arm_t *as) { if (as->pass == ASM_ARM_PASS_EMIT) { #ifdef __arm__ @@ -97,32 +53,6 @@ void asm_arm_end_pass(asm_arm_t *as) { } } -// all functions must go through this one to emit bytes -// if as->pass < ASM_ARM_PASS_EMIT, then this function only returns a buffer of 4 bytes length -STATIC byte *asm_arm_get_cur_to_write_bytes(asm_arm_t *as, int num_bytes_to_write) { - if (as->pass < ASM_ARM_PASS_EMIT) { - as->code_offset += num_bytes_to_write; - return as->dummy_data; - } else { - assert(as->code_offset + num_bytes_to_write <= as->code_size); - byte *c = as->code_base + as->code_offset; - as->code_offset += num_bytes_to_write; - return c; - } -} - -uint asm_arm_get_code_pos(asm_arm_t *as) { - return as->code_offset; -} - -uint asm_arm_get_code_size(asm_arm_t *as) { - return as->code_size; -} - -void *asm_arm_get_code(asm_arm_t *as) { - return as->code_base; -} - // Insert word into instruction flow STATIC void emit(asm_arm_t *as, uint op) { *(uint*)asm_arm_get_cur_to_write_bytes(as, 4) = op; @@ -263,35 +193,6 @@ void asm_arm_pop(asm_arm_t *as, uint reglist) { emit_al(as, asm_arm_op_pop(reglist)); } -void asm_arm_label_assign(asm_arm_t *as, uint label) { - assert(label < as->max_num_labels); - if (as->pass < ASM_ARM_PASS_EMIT) { - // assign label offset - assert(as->label_offsets[label] == -1); - as->label_offsets[label] = as->code_offset; - } else { - // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT - assert(as->label_offsets[label] == as->code_offset); - } -} - -void asm_arm_align(asm_arm_t* as, uint align) { - // TODO fill unused data with NOPs? - as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); -} - -void asm_arm_data(asm_arm_t* as, uint bytesize, uint val) { - byte *c = asm_arm_get_cur_to_write_bytes(as, bytesize); - // only write to the buffer in the emit pass (otherwise we overflow dummy_data) - if (as->pass == ASM_ARM_PASS_EMIT) { - // little endian - for (uint i = 0; i < bytesize; i++) { - *c++ = val; - val >>= 8; - } - } -} - void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src) { emit_al(as, asm_arm_op_mov_reg(reg_dest, reg_src)); } diff --git a/py/asmarm.h b/py/asmarm.h index 15e7a047b..263721057 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -28,9 +28,7 @@ #define __MICROPY_INCLUDED_PY_ASMARM_H__ #include "py/misc.h" - -#define ASM_ARM_PASS_COMPUTE (1) -#define ASM_ARM_PASS_EMIT (2) +#include "py/asmbase.h" #define ASM_ARM_REG_R0 (0) #define ASM_ARM_REG_R1 (1) @@ -68,22 +66,16 @@ #define ASM_ARM_CC_LE (0xd << 28) #define ASM_ARM_CC_AL (0xe << 28) -typedef struct _asm_arm_t asm_arm_t; +typedef struct _asm_arm_t { + mp_asm_base_t base; + uint push_reglist; + uint stack_adjust; +} asm_arm_t; -asm_arm_t *asm_arm_new(uint max_num_labels); -void asm_arm_free(asm_arm_t *as, bool free_code); -void asm_arm_start_pass(asm_arm_t *as, uint pass); void asm_arm_end_pass(asm_arm_t *as); -uint asm_arm_get_code_pos(asm_arm_t *as); -uint asm_arm_get_code_size(asm_arm_t *as); -void *asm_arm_get_code(asm_arm_t *as); void asm_arm_entry(asm_arm_t *as, int num_locals); void asm_arm_exit(asm_arm_t *as); -void asm_arm_label_assign(asm_arm_t *as, uint label); - -void asm_arm_align(asm_arm_t* as, uint align); -void asm_arm_data(asm_arm_t* as, uint bytesize, uint val); void asm_arm_bkpt(asm_arm_t *as); diff --git a/py/asmbase.c b/py/asmbase.c new file mode 100644 index 000000000..916f7c5ec --- /dev/null +++ b/py/asmbase.c @@ -0,0 +1,104 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 +#include + +#include "py/obj.h" +#include "py/misc.h" +#include "py/asmbase.h" + +#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB + +void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) { + as->max_num_labels = max_num_labels; + as->label_offsets = m_new(size_t, max_num_labels); +} + +void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) { + if (free_code) { + MP_PLAT_FREE_EXEC(as->code_base, as->code_size); + } + m_del(size_t, as->label_offsets, as->max_num_labels); +} + +void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { + if (pass == MP_ASM_PASS_COMPUTE) { + // reset all labels + memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t)); + } else if (pass == MP_ASM_PASS_EMIT) { + // allocating executable RAM is platform specific + MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); + assert(as->code_base != NULL); + } + as->pass = pass; + as->code_offset = 0; +} + +// all functions must go through this one to emit bytes +// if as->pass < MP_ASM_PASS_EMIT, then this function returns dummy_data +uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) { + if (as->pass < MP_ASM_PASS_EMIT) { + as->code_offset += num_bytes_to_write; + return as->dummy_data; + } else { + assert(as->code_offset + num_bytes_to_write <= as->code_size); + uint8_t *c = as->code_base + as->code_offset; + as->code_offset += num_bytes_to_write; + return c; + } +} + +void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { + assert(label < as->max_num_labels); + if (as->pass < MP_ASM_PASS_EMIT) { + // assign label offset + assert(as->label_offsets[label] == (size_t)-1); + as->label_offsets[label] = as->code_offset; + } else { + // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT + assert(as->label_offsets[label] == as->code_offset); + } +} + +// align must be a multiple of 2 +void mp_asm_base_align(mp_asm_base_t* as, unsigned int align) { + as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); +} + +// this function assumes a little endian machine +void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(as, bytesize); + // only write to the buffer in the emit pass (otherwise we may overflow dummy_data) + if (as->pass == MP_ASM_PASS_EMIT) { + for (unsigned int i = 0; i < bytesize; i++) { + *c++ = val; + val >>= 8; + } + } +} + +#endif // MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB diff --git a/py/asmbase.h b/py/asmbase.h new file mode 100644 index 000000000..bfc8ac7ff --- /dev/null +++ b/py/asmbase.h @@ -0,0 +1,68 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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_ASMBASE_H +#define MICROPY_INCLUDED_PY_ASMBASE_H + +#include +#include + +#define MP_ASM_PASS_COMPUTE (1) +#define MP_ASM_PASS_EMIT (2) + +typedef struct _mp_asm_base_t { + int pass; + size_t code_offset; + size_t code_size; + uint8_t *code_base; + + size_t max_num_labels; + size_t *label_offsets; + + // must be last in struct + uint8_t dummy_data[4]; +} mp_asm_base_t; + +void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels); +void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code); +void mp_asm_base_start_pass(mp_asm_base_t *as, int pass); +uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write); +void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label); +void mp_asm_base_align(mp_asm_base_t* as, unsigned int align); +void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val); + +static inline size_t mp_asm_base_get_code_pos(mp_asm_base_t *as) { + return as->code_offset; +} + +static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) { + return as->code_size; +} + +static inline void *mp_asm_base_get_code(mp_asm_base_t *as) { + return as->code_base; +} + +#endif // MICROPY_INCLUDED_PY_ASMBASE_H diff --git a/py/asmthumb.c b/py/asmthumb.c index 1aae3d38e..705c15a18 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -42,49 +42,8 @@ #define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800) #define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000) -struct _asm_thumb_t { - mp_uint_t pass; - mp_uint_t code_offset; - mp_uint_t code_size; - byte *code_base; - byte dummy_data[4]; - - mp_uint_t max_num_labels; - mp_uint_t *label_offsets; - mp_uint_t push_reglist; - mp_uint_t stack_adjust; -}; - -asm_thumb_t *asm_thumb_new(uint max_num_labels) { - asm_thumb_t *as; - - as = m_new0(asm_thumb_t, 1); - as->max_num_labels = max_num_labels; - as->label_offsets = m_new(mp_uint_t, max_num_labels); - - return as; -} - -void asm_thumb_free(asm_thumb_t *as, bool free_code) { - if (free_code) { - MP_PLAT_FREE_EXEC(as->code_base, as->code_size); - } - m_del(mp_uint_t, as->label_offsets, as->max_num_labels); - m_del_obj(asm_thumb_t, as); -} - -void asm_thumb_start_pass(asm_thumb_t *as, uint pass) { - if (pass == ASM_THUMB_PASS_COMPUTE) { - memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t)); - } else if (pass == ASM_THUMB_PASS_EMIT) { - MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); - if (as->code_base == NULL) { - assert(0); - } - //printf("code_size: %u\n", as->code_size); - } - as->pass = pass; - as->code_offset = 0; +static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { + return mp_asm_base_get_cur_to_write_bytes(&as->base, n); } void asm_thumb_end_pass(asm_thumb_t *as) { @@ -92,7 +51,7 @@ void asm_thumb_end_pass(asm_thumb_t *as) { // could check labels are resolved... #if defined(MCU_SERIES_F7) - if (as->pass == ASM_THUMB_PASS_EMIT) { + if (as->base.pass == MP_ASM_PASS_EMIT) { // flush D-cache, so the code emited is stored in memory SCB_CleanDCache_by_Addr((uint32_t*)as->code_base, as->code_size); // invalidate I-cache @@ -101,33 +60,6 @@ void asm_thumb_end_pass(asm_thumb_t *as) { #endif } -// all functions must go through this one to emit bytes -// if as->pass < ASM_THUMB_PASS_EMIT, then this function only returns a buffer of 4 bytes length -STATIC byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int num_bytes_to_write) { - //printf("emit %d\n", num_bytes_to_write); - if (as->pass < ASM_THUMB_PASS_EMIT) { - as->code_offset += num_bytes_to_write; - return as->dummy_data; - } else { - assert(as->code_offset + num_bytes_to_write <= as->code_size); - byte *c = as->code_base + as->code_offset; - as->code_offset += num_bytes_to_write; - return c; - } -} - -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; -} - -void *asm_thumb_get_code(asm_thumb_t *as) { - return as->code_base; -} - /* STATIC void asm_thumb_write_byte_1(asm_thumb_t *as, byte b1) { byte *c = asm_thumb_get_cur_to_write_bytes(as, 1); @@ -223,39 +155,9 @@ void asm_thumb_exit(asm_thumb_t *as) { asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist)); } -void asm_thumb_label_assign(asm_thumb_t *as, uint label) { - assert(label < as->max_num_labels); - if (as->pass < ASM_THUMB_PASS_EMIT) { - // assign label offset - assert(as->label_offsets[label] == -1); - as->label_offsets[label] = as->code_offset; - } else { - // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT - //printf("l%d: (at %d=%ld)\n", label, as->label_offsets[label], as->code_offset); - assert(as->label_offsets[label] == as->code_offset); - } -} - -void asm_thumb_align(asm_thumb_t* as, uint align) { - // TODO fill unused data with NOPs? - as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); -} - -void asm_thumb_data(asm_thumb_t* as, uint bytesize, uint val) { - byte *c = asm_thumb_get_cur_to_write_bytes(as, bytesize); - // only write to the buffer in the emit pass (otherwise we overflow dummy_data) - if (as->pass == ASM_THUMB_PASS_EMIT) { - // little endian - for (uint i = 0; i < bytesize; i++) { - *c++ = val; - val >>= 8; - } - } -} - STATIC mp_uint_t get_label_dest(asm_thumb_t *as, uint label) { - assert(label < as->max_num_labels); - return as->label_offsets[label]; + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; } void asm_thumb_op16(asm_thumb_t *as, uint op) { @@ -309,10 +211,10 @@ void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_ bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction asm_thumb_op16(as, OP_B_N(rel)); - return as->pass != ASM_THUMB_PASS_EMIT || SIGNED_FIT12(rel); + return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT12(rel); } #define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff)) @@ -323,11 +225,11 @@ bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction if (!wide) { asm_thumb_op16(as, OP_BCC_N(cond, rel)); - return as->pass != ASM_THUMB_PASS_EMIT || SIGNED_FIT9(rel); + return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel); } else { asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); return true; @@ -339,10 +241,10 @@ bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { bool asm_thumb_bl_label(asm_thumb_t *as, uint label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction asm_thumb_op32(as, OP_BL_HI(rel), OP_BL_LO(rel)); - return as->pass != ASM_THUMB_PASS_EMIT || SIGNED_FIT23(rel); + return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT23(rel); } void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) { @@ -367,13 +269,13 @@ void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) { // TODO this is very inefficient, improve it! void asm_thumb_mov_reg_i32_aligned(asm_thumb_t *as, uint reg_dest, int i32) { // align on machine-word + 2 - if ((as->code_offset & 3) == 0) { + if ((as->base.code_offset & 3) == 0) { asm_thumb_op16(as, ASM_THUMB_OP_NOP); } // jump over the i32 value (instruction prefetch adds 2 to PC) asm_thumb_op16(as, OP_B_N(2)); // store i32 on machine-word aligned boundary - asm_thumb_data(as, 4, i32); + mp_asm_base_data(&as->base, 4, i32); // do the actual load of the i32 value asm_thumb_mov_reg_i32_optimised(as, reg_dest, i32); } @@ -384,14 +286,14 @@ void asm_thumb_mov_reg_i32_aligned(asm_thumb_t *as, uint reg_dest, int i32) { void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) { assert(rlo_src < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->pass < ASM_THUMB_PASS_EMIT || word_offset >= 0); + assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset)); } void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) { assert(rlo_dest < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->pass < ASM_THUMB_PASS_EMIT || word_offset >= 0); + assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset)); } @@ -400,7 +302,7 @@ void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) { void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) { assert(rlo_dest < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->pass < ASM_THUMB_PASS_EMIT || word_offset >= 0); + assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); asm_thumb_op16(as, OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset)); } @@ -410,7 +312,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) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction if (dest != (mp_uint_t)-1 && rel <= -4) { // is a backwards jump, so we know the size of the jump on the first pass @@ -429,7 +331,7 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) { void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction if (dest != (mp_uint_t)-1 && rel <= -4) { // is a backwards jump, so we know the size of the jump on the first pass diff --git a/py/asmthumb.h b/py/asmthumb.h index 43d6c4286..c03b00da3 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -27,9 +27,7 @@ #define __MICROPY_INCLUDED_PY_ASMTHUMB_H__ #include "py/misc.h" - -#define ASM_THUMB_PASS_COMPUTE (1) -#define ASM_THUMB_PASS_EMIT (2) +#include "py/asmbase.h" #define ASM_THUMB_REG_R0 (0) #define ASM_THUMB_REG_R1 (1) @@ -64,24 +62,17 @@ #define ASM_THUMB_CC_GT (0xc) #define ASM_THUMB_CC_LE (0xd) -typedef struct _asm_thumb_t asm_thumb_t; +typedef struct _asm_thumb_t { + mp_asm_base_t base; + uint32_t push_reglist; + uint32_t stack_adjust; +} asm_thumb_t; -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); void asm_thumb_entry(asm_thumb_t *as, int num_locals); void asm_thumb_exit(asm_thumb_t *as); -void asm_thumb_label_assign(asm_thumb_t *as, uint label); - -void asm_thumb_align(asm_thumb_t* as, uint align); -void asm_thumb_data(asm_thumb_t* as, uint bytesize, uint val); - // argument order follows ARM, in general dest is first // note there is a difference between movw and mov.w, and many others! diff --git a/py/asmx64.c b/py/asmx64.c index 5e23a594e..c9dad2a66 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -116,80 +116,8 @@ #define UNSIGNED_FIT32(x) (((x) & 0xffffffff00000000) == 0) #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80) -struct _asm_x64_t { - uint pass; - mp_uint_t code_offset; - mp_uint_t code_size; - byte *code_base; - byte dummy_data[8]; - - mp_uint_t max_num_labels; - mp_uint_t *label_offsets; - int num_locals; -}; - -asm_x64_t *asm_x64_new(mp_uint_t max_num_labels) { - asm_x64_t *as; - - as = m_new0(asm_x64_t, 1); - as->max_num_labels = max_num_labels; - as->label_offsets = m_new(mp_uint_t, max_num_labels); - - return as; -} - -void asm_x64_free(asm_x64_t *as, bool free_code) { - if (free_code) { - MP_PLAT_FREE_EXEC(as->code_base, as->code_size); - } - m_del(mp_uint_t, as->label_offsets, as->max_num_labels); - m_del_obj(asm_x64_t, as); -} - -void asm_x64_start_pass(asm_x64_t *as, uint pass) { - if (pass == ASM_X64_PASS_COMPUTE) { - // reset all labels - memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t)); - } if (pass == ASM_X64_PASS_EMIT) { - MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); - if (as->code_base == NULL) { - assert(0); - } - //printf("code_size: %u\n", as->code_size); - } - as->pass = pass; - as->code_offset = 0; -} - -void asm_x64_end_pass(asm_x64_t *as) { - // could check labels are resolved... - (void)as; -} - -// all functions must go through this one to emit bytes -STATIC byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int num_bytes_to_write) { - //printf("emit %d\n", num_bytes_to_write); - if (as->pass < ASM_X64_PASS_EMIT) { - as->code_offset += num_bytes_to_write; - return as->dummy_data; - } else { - assert(as->code_offset + num_bytes_to_write <= as->code_size); - byte *c = as->code_base + as->code_offset; - as->code_offset += num_bytes_to_write; - return c; - } -} - -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; -} - -void *asm_x64_get_code(asm_x64_t *as) { - return as->code_base; +static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) { + return mp_asm_base_get_cur_to_write_bytes(&as->base, n); } STATIC void asm_x64_write_byte_1(asm_x64_t *as, byte b1) { @@ -230,21 +158,6 @@ 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; @@ -440,7 +353,7 @@ void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r // src_i64 is stored as a full word in the code, and aligned to machine-word boundary void asm_x64_mov_i64_to_r64_aligned(asm_x64_t *as, int64_t src_i64, int dest_r64) { // mov instruction uses 2 bytes for the instruction, before the i64 - while (((as->code_offset + 2) & (WORD_SIZE - 1)) != 0) { + while (((as->base.code_offset + 2) & (WORD_SIZE - 1)) != 0) { asm_x64_nop(as); } asm_x64_mov_i64_to_r64(as, src_i64, dest_r64); @@ -552,27 +465,14 @@ void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8) { asm_x64_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R64(0) | MODRM_RM_REG | MODRM_RM_R64(dest_r8)); } -void asm_x64_label_assign(asm_x64_t *as, mp_uint_t label) { - assert(label < as->max_num_labels); - if (as->pass < ASM_X64_PASS_EMIT) { - // assign label offset - assert(as->label_offsets[label] == (mp_uint_t)-1); - as->label_offsets[label] = as->code_offset; - } else { - // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT - //printf("l%d: (at %ld=%ld)\n", label, as->label_offsets[label], as->code_offset); - assert(as->label_offsets[label] == as->code_offset); - } -} - STATIC mp_uint_t get_label_dest(asm_x64_t *as, mp_uint_t label) { - assert(label < as->max_num_labels); - return as->label_offsets[label]; + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; } void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; if (dest != (mp_uint_t)-1 && rel < 0) { // is a backwards jump, so we know the size of the jump on the first pass // calculate rel assuming 8 bit relative jump @@ -594,7 +494,7 @@ void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) { void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; if (dest != (mp_uint_t)-1 && rel < 0) { // is a backwards jump, so we know the size of the jump on the first pass // calculate rel assuming 8 bit relative jump diff --git a/py/asmx64.h b/py/asmx64.h index 6fbc2c906..daf5b3d0b 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -28,6 +28,7 @@ #include "py/mpconfig.h" #include "py/misc.h" +#include "py/asmbase.h" // AMD64 calling convention is: // - args pass in: RDI, RSI, RDX, RCX, R08, R09 @@ -41,9 +42,6 @@ // NOTE: this is a change from the old convention used in this file and // some functions still use the old (reverse) convention. -#define ASM_X64_PASS_COMPUTE (1) -#define ASM_X64_PASS_EMIT (2) - #define ASM_X64_REG_RAX (0) #define ASM_X64_REG_RCX (1) #define ASM_X64_REG_RDX (2) @@ -72,18 +70,15 @@ #define ASM_X64_CC_JLE (0xe) // less or equal, signed #define ASM_X64_CC_JG (0xf) // greater, signed -typedef struct _asm_x64_t asm_x64_t; - -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); +typedef struct _asm_x64_t { + mp_asm_base_t base; + byte dummy_data[4]; // in addition to dummy_data in base + int num_locals; +} asm_x64_t; -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); +static inline void asm_x64_end_pass(asm_x64_t *as) { + (void)as; +} void asm_x64_nop(asm_x64_t* as); void asm_x64_push_r64(asm_x64_t* as, int src_r64); @@ -111,7 +106,6 @@ void asm_x64_mul_r64_r64(asm_x64_t* as, int dest_r64, int src_r64); void asm_x64_cmp_r64_with_r64(asm_x64_t* as, int src_r64_a, int src_r64_b); void asm_x64_test_r8_with_r8(asm_x64_t* as, int src_r64_a, int src_r64_b); void asm_x64_setcc_r8(asm_x64_t* as, int jcc_type, int dest_r8); -void asm_x64_label_assign(asm_x64_t* as, mp_uint_t label); void asm_x64_jmp_label(asm_x64_t* as, mp_uint_t label); void asm_x64_jcc_label(asm_x64_t* as, int jcc_type, mp_uint_t label); void asm_x64_entry(asm_x64_t* as, int num_locals); diff --git a/py/asmx86.c b/py/asmx86.c index 40958826f..cb9b30d40 100644 --- a/py/asmx86.c +++ b/py/asmx86.c @@ -100,80 +100,6 @@ #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80) -struct _asm_x86_t { - uint pass; - mp_uint_t code_offset; - mp_uint_t code_size; - byte *code_base; - byte dummy_data[8]; - - mp_uint_t max_num_labels; - mp_uint_t *label_offsets; - int num_locals; -}; - -asm_x86_t *asm_x86_new(mp_uint_t max_num_labels) { - asm_x86_t *as; - - as = m_new0(asm_x86_t, 1); - as->max_num_labels = max_num_labels; - as->label_offsets = m_new(mp_uint_t, max_num_labels); - - return as; -} - -void asm_x86_free(asm_x86_t *as, bool free_code) { - if (free_code) { - MP_PLAT_FREE_EXEC(as->code_base, as->code_size); - } - m_del(mp_uint_t, as->label_offsets, as->max_num_labels); - m_del_obj(asm_x86_t, as); -} - -void asm_x86_start_pass(asm_x86_t *as, mp_uint_t pass) { - if (pass == ASM_X86_PASS_COMPUTE) { - // reset all labels - memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t)); - } else if (pass == ASM_X86_PASS_EMIT) { - MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size); - if (as->code_base == NULL) { - assert(0); - } - } - as->pass = pass; - as->code_offset = 0; -} - -void asm_x86_end_pass(asm_x86_t *as) { - (void)as; -} - -// all functions must go through this one to emit bytes -STATIC byte *asm_x86_get_cur_to_write_bytes(asm_x86_t *as, int num_bytes_to_write) { - //printf("emit %d\n", num_bytes_to_write); - if (as->pass < ASM_X86_PASS_EMIT) { - as->code_offset += num_bytes_to_write; - return as->dummy_data; - } else { - assert(as->code_offset + num_bytes_to_write <= as->code_size); - byte *c = as->code_base + as->code_offset; - as->code_offset += num_bytes_to_write; - return c; - } -} - -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; -} - -void *asm_x86_get_code(asm_x86_t *as) { - return as->code_base; -} - STATIC void asm_x86_write_byte_1(asm_x86_t *as, byte b1) { byte* c = asm_x86_get_cur_to_write_bytes(as, 1); c[0] = b1; @@ -200,21 +126,6 @@ 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); @@ -419,19 +330,6 @@ void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) { asm_x86_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r8)); } -void asm_x86_label_assign(asm_x86_t *as, mp_uint_t label) { - assert(label < as->max_num_labels); - if (as->pass < ASM_X86_PASS_EMIT) { - // assign label offset - assert(as->label_offsets[label] == (mp_uint_t)-1); - as->label_offsets[label] = as->code_offset; - } else { - // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT - //printf("l%d: (at %d=%ld)\n", label, as->label_offsets[label], as->code_offset); - assert(as->label_offsets[label] == as->code_offset); - } -} - STATIC mp_uint_t get_label_dest(asm_x86_t *as, mp_uint_t label) { assert(label < as->max_num_labels); return as->label_offsets[label]; diff --git a/py/asmx86.h b/py/asmx86.h index e0c57722a..85352fff3 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -28,6 +28,7 @@ #include "py/mpconfig.h" #include "py/misc.h" +#include "py/asmbase.h" // x86 cdecl calling convention is: // - args passed on the stack in reverse order @@ -42,9 +43,6 @@ // NOTE: this is a change from the old convention used in this file and // some functions still use the old (reverse) convention. -#define ASM_X86_PASS_COMPUTE (1) -#define ASM_X86_PASS_EMIT (2) - #define ASM_X86_REG_EAX (0) #define ASM_X86_REG_ECX (1) #define ASM_X86_REG_EDX (2) @@ -75,18 +73,15 @@ #define ASM_X86_CC_JLE (0xe) // less or equal, signed #define ASM_X86_CC_JG (0xf) // greater, signed -typedef struct _asm_x86_t asm_x86_t; - -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); +typedef struct _asm_x86_t { + mp_asm_base_t base; + byte dummy_data[4]; // in addition to dummy_data in base + int num_locals; +} asm_x86_t; -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); +static inline void asm_x86_end_pass(asm_x86_t *as) { + (void)as; +} 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); @@ -108,7 +103,6 @@ void asm_x86_mul_r32_r32(asm_x86_t* as, int dest_r32, int src_r32); void asm_x86_cmp_r32_with_r32(asm_x86_t* as, int src_r32_a, int src_r32_b); void asm_x86_test_r8_with_r8(asm_x86_t* as, int src_r32_a, int src_r32_b); void asm_x86_setcc_r8(asm_x86_t* as, mp_uint_t jcc_type, int dest_r8); -void asm_x86_label_assign(asm_x86_t* as, mp_uint_t label); void asm_x86_jmp_label(asm_x86_t* as, mp_uint_t label); void asm_x86_jcc_label(asm_x86_t* as, mp_uint_t jcc_type, mp_uint_t label); void asm_x86_entry(asm_x86_t* as, mp_uint_t num_locals); diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 90a68caa6..760ec8cc0 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -63,13 +63,15 @@ emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) { emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); emit->max_num_labels = max_num_labels; emit->label_lookup = m_new(qstr, max_num_labels); - emit->as = asm_thumb_new(max_num_labels); + emit->as = m_new0(asm_thumb_t, 1); + mp_asm_base_init(&emit->as->base, max_num_labels); return emit; } void emit_inline_thumb_free(emit_inline_asm_t *emit) { m_del(qstr, emit->label_lookup, emit->max_num_labels); - asm_thumb_free(emit->as, false); + mp_asm_base_deinit(&emit->as->base, false); + m_del_obj(asm_thumb_t, emit->as); m_del_obj(emit_inline_asm_t, emit); } @@ -80,7 +82,7 @@ STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pa if (emit->pass == MP_PASS_CODE_SIZE) { memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); } - asm_thumb_start_pass(emit->as, pass == MP_PASS_EMIT ? ASM_THUMB_PASS_EMIT : ASM_THUMB_PASS_COMPUTE); + mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); asm_thumb_entry(emit->as, 0); } @@ -89,9 +91,9 @@ STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_s asm_thumb_end_pass(emit->as); if (emit->pass == MP_PASS_EMIT) { - void *f = asm_thumb_get_code(emit->as); + void *f = mp_asm_base_get_code(&emit->as->base); mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, - asm_thumb_get_code_size(emit->as), NULL, emit->scope->num_pos_args, 0, type_sig); + mp_asm_base_get_code_size(&emit->as->base), NULL, emit->scope->num_pos_args, 0, type_sig); } } @@ -125,16 +127,16 @@ STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num } } emit->label_lookup[label_num] = label_id; - asm_thumb_label_assign(emit->as, label_num); + mp_asm_base_label_assign(&emit->as->base, label_num); return true; } STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) { - asm_thumb_align(emit->as, align); + mp_asm_base_align(&emit->as->base, align); } STATIC void emit_inline_thumb_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) { - asm_thumb_data(emit->as, bytesize, val); + mp_asm_base_data(&emit->as->base, bytesize, val); } typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; diff --git a/py/emitnative.c b/py/emitnative.c index 46e1076a9..d19dfe467 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -91,24 +91,11 @@ #define REG_LOCAL_3 ASM_X64_REG_R13 #define REG_LOCAL_NUM (3) -#define ASM_PASS_COMPUTE ASM_X64_PASS_COMPUTE -#define ASM_PASS_EMIT ASM_X64_PASS_EMIT - #define ASM_T asm_x64_t -#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) \ do { \ @@ -236,24 +223,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #define REG_LOCAL_3 ASM_X86_REG_EDI #define REG_LOCAL_NUM (3) -#define ASM_PASS_COMPUTE ASM_X86_PASS_COMPUTE -#define ASM_PASS_EMIT ASM_X86_PASS_EMIT - #define ASM_T asm_x86_t -#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) \ do { \ @@ -331,24 +305,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #define REG_LOCAL_3 ASM_THUMB_REG_R6 #define REG_LOCAL_NUM (3) -#define ASM_PASS_COMPUTE ASM_THUMB_PASS_COMPUTE -#define ASM_PASS_EMIT ASM_THUMB_PASS_EMIT - #define ASM_T asm_thumb_t -#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) \ do { \ @@ -425,24 +386,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #define REG_LOCAL_3 ASM_ARM_REG_R6 #define REG_LOCAL_NUM (3) -#define ASM_PASS_COMPUTE ASM_ARM_PASS_COMPUTE -#define ASM_PASS_EMIT ASM_ARM_PASS_EMIT - #define ASM_T asm_arm_t -#define ASM_NEW asm_arm_new -#define ASM_FREE asm_arm_free -#define ASM_GET_CODE asm_arm_get_code -#define ASM_GET_CODE_POS asm_arm_get_code_pos -#define ASM_GET_CODE_SIZE asm_arm_get_code_size -#define ASM_START_PASS asm_arm_start_pass #define ASM_END_PASS asm_arm_end_pass #define ASM_ENTRY asm_arm_entry #define ASM_EXIT asm_arm_exit -#define ASM_ALIGN asm_arm_align -#define ASM_DATA asm_arm_data - -#define ASM_LABEL_ASSIGN asm_arm_label_assign #define ASM_JUMP asm_arm_b_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ do { \ @@ -582,12 +530,14 @@ struct _emit_t { emit_t *EXPORT_FUN(new)(mp_obj_t *error_slot, mp_uint_t max_num_labels) { emit_t *emit = m_new0(emit_t, 1); emit->error_slot = error_slot; - emit->as = ASM_NEW(max_num_labels); + emit->as = m_new0(ASM_T, 1); + mp_asm_base_init(&emit->as->base, max_num_labels); return emit; } void EXPORT_FUN(free)(emit_t *emit) { - ASM_FREE(emit->as, false); + mp_asm_base_deinit(&emit->as->base, false); + m_del_obj(ASM_T, emit->as); m_del(vtype_kind_t, emit->local_vtype, emit->local_vtype_alloc); m_del(stack_info_t, emit->stack_info, emit->stack_info_alloc); m_del_obj(emit_t, emit); @@ -679,7 +629,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_info[i].vtype = VTYPE_UNBOUND; } - ASM_START_PASS(emit->as, pass == MP_PASS_EMIT ? ASM_PASS_EMIT : ASM_PASS_COMPUTE); + mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); // generate code for entry to function @@ -824,21 +774,21 @@ STATIC void emit_native_end_pass(emit_t *emit) { } if (!emit->do_viper_types) { - emit->prelude_offset = ASM_GET_CODE_POS(emit->as); - ASM_DATA(emit->as, 1, emit->scope->scope_flags); - ASM_DATA(emit->as, 1, emit->scope->num_pos_args); - ASM_DATA(emit->as, 1, emit->scope->num_kwonly_args); - ASM_DATA(emit->as, 1, emit->scope->num_def_pos_args); + emit->prelude_offset = mp_asm_base_get_code_pos(&emit->as->base); + mp_asm_base_data(&emit->as->base, 1, emit->scope->scope_flags); + mp_asm_base_data(&emit->as->base, 1, emit->scope->num_pos_args); + mp_asm_base_data(&emit->as->base, 1, emit->scope->num_kwonly_args); + mp_asm_base_data(&emit->as->base, 1, emit->scope->num_def_pos_args); // write code info #if MICROPY_PERSISTENT_CODE - ASM_DATA(emit->as, 1, 5); - ASM_DATA(emit->as, 1, emit->scope->simple_name); - ASM_DATA(emit->as, 1, emit->scope->simple_name >> 8); - ASM_DATA(emit->as, 1, emit->scope->source_file); - ASM_DATA(emit->as, 1, emit->scope->source_file >> 8); + mp_asm_base_data(&emit->as->base, 1, 5); + mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name); + mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name >> 8); + mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file); + mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file >> 8); #else - ASM_DATA(emit->as, 1, 1); + mp_asm_base_data(&emit->as->base, 1, 1); #endif // bytecode prelude: initialise closed over variables @@ -846,13 +796,13 @@ STATIC void emit_native_end_pass(emit_t *emit) { 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 + mp_asm_base_data(&emit->as->base, 1, id->local_num); // write the local which should be converted to a cell } } - ASM_DATA(emit->as, 1, 255); // end of list sentinel + mp_asm_base_data(&emit->as->base, 1, 255); // end of list sentinel - ASM_ALIGN(emit->as, ASM_WORD_SIZE); - emit->const_table_offset = ASM_GET_CODE_POS(emit->as); + mp_asm_base_align(&emit->as->base, ASM_WORD_SIZE); + emit->const_table_offset = mp_asm_base_get_code_pos(&emit->as->base); // write argument names as qstr objects // see comment in corresponding part of emitbc.c about the logic here @@ -865,7 +815,7 @@ STATIC void emit_native_end_pass(emit_t *emit) { break; } } - ASM_DATA(emit->as, ASM_WORD_SIZE, (mp_uint_t)MP_OBJ_NEW_QSTR(qst)); + mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (mp_uint_t)MP_OBJ_NEW_QSTR(qst)); } } @@ -878,8 +828,8 @@ STATIC void emit_native_end_pass(emit_t *emit) { } if (emit->pass == MP_PASS_EMIT) { - void *f = ASM_GET_CODE(emit->as); - mp_uint_t f_len = ASM_GET_CODE_SIZE(emit->as); + void *f = mp_asm_base_get_code(&emit->as->base); + mp_uint_t f_len = mp_asm_base_get_code_size(&emit->as->base); // compute type signature // note that the lower 4 bits of a vtype are tho correct MP_NATIVE_TYPE_xxx @@ -1255,7 +1205,7 @@ STATIC void emit_native_label_assign(emit_t *emit, mp_uint_t l) { emit_native_pre(emit); // need to commit stack because we can jump here from elsewhere need_stack_settled(emit); - ASM_LABEL_ASSIGN(emit->as, l); + mp_asm_base_label_assign(&emit->as->base, l); emit_post(emit); } diff --git a/py/py.mk b/py/py.mk index ec67ac2a0..87a860f75 100644 --- a/py/py.mk +++ b/py/py.mk @@ -120,6 +120,7 @@ PY_O_BASENAME = \ compile.o \ emitcommon.o \ emitbc.o \ + asmbase.o \ asmx64.o \ emitnx64.o \ asmx86.o \ -- cgit v1.2.3 From 63e82dcdfe33e3805958203cdff0ed4d87a8b536 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 30 Nov 2016 00:11:25 +1100 Subject: py/asmthumb: Fix build for F7 MCUs after recent code refactoring. --- py/asmthumb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/asmthumb.c b/py/asmthumb.c index 705c15a18..82a226b62 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -53,7 +53,7 @@ void asm_thumb_end_pass(asm_thumb_t *as) { #if defined(MCU_SERIES_F7) if (as->base.pass == MP_ASM_PASS_EMIT) { // flush D-cache, so the code emited is stored in memory - SCB_CleanDCache_by_Addr((uint32_t*)as->code_base, as->code_size); + SCB_CleanDCache_by_Addr((uint32_t*)as->base.code_base, as->base.code_size); // invalidate I-cache SCB_InvalidateICache(); } -- cgit v1.2.3 From 304cfda8c466920b1d29903bfca8bca5ed110f3b Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 2 Dec 2016 16:37:29 +1100 Subject: py/stream: Move ad-hoc ioctl constants to stream.h and rename them. The constants MP_IOCTL_POLL_xxx, which were stmhal-specific, are moved from stmhal/pybioctl.h (now deleted) to py/stream.h. And they are renamed to MP_STREAM_POLL_xxx to be consistent with other such constants. All uses of these constants have been updated. --- cc3200/mods/modwlan.c | 15 +++++++-------- cc3200/mods/pybuart.c | 11 +++++------ extmod/moduselect.c | 32 ++++++++++++++++---------------- py/stream.h | 6 ++++++ stmhal/can.c | 12 ++++++------ stmhal/modnwcc3k.c | 17 ++++++++--------- stmhal/pybioctl.h | 8 -------- stmhal/uart.c | 11 +++++------ stmhal/usb.c | 17 ++++++++--------- 9 files changed, 61 insertions(+), 68 deletions(-) delete mode 100644 stmhal/pybioctl.h (limited to 'py') diff --git a/cc3200/mods/modwlan.c b/cc3200/mods/modwlan.c index 4b3dd4ec3..c1af5879f 100644 --- a/cc3200/mods/modwlan.c +++ b/cc3200/mods/modwlan.c @@ -45,7 +45,6 @@ #include "modnetwork.h" #include "modusocket.h" #include "modwlan.h" -#include "pybioctl.h" #include "pybrtc.h" #include "debug.h" #if (MICROPY_PORT_HAS_TELNET || MICROPY_PORT_HAS_FTP) @@ -1461,7 +1460,7 @@ int wlan_socket_settimeout(mod_network_socket_obj_t *s, mp_uint_t timeout_s, int int wlan_socket_ioctl (mod_network_socket_obj_t *s, mp_uint_t request, mp_uint_t arg, int *_errno) { mp_int_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; int32_t sd = s->sock_base.sd; @@ -1473,13 +1472,13 @@ int wlan_socket_ioctl (mod_network_socket_obj_t *s, mp_uint_t request, mp_uint_t FD_ZERO(&xfds); // set fds if needed - if (flags & MP_IOCTL_POLL_RD) { + if (flags & MP_STREAM_POLL_RD) { FD_SET(sd, &rfds); } - if (flags & MP_IOCTL_POLL_WR) { + if (flags & MP_STREAM_POLL_WR) { FD_SET(sd, &wfds); } - if (flags & MP_IOCTL_POLL_HUP) { + if (flags & MP_STREAM_POLL_HUP) { FD_SET(sd, &xfds); } @@ -1497,13 +1496,13 @@ int wlan_socket_ioctl (mod_network_socket_obj_t *s, mp_uint_t request, mp_uint_t // check return of select if (FD_ISSET(sd, &rfds)) { - ret |= MP_IOCTL_POLL_RD; + ret |= MP_STREAM_POLL_RD; } if (FD_ISSET(sd, &wfds)) { - ret |= MP_IOCTL_POLL_WR; + ret |= MP_STREAM_POLL_WR; } if (FD_ISSET(sd, &xfds)) { - ret |= MP_IOCTL_POLL_HUP; + ret |= MP_STREAM_POLL_HUP; } } else { *_errno = EINVAL; diff --git a/cc3200/mods/pybuart.c b/cc3200/mods/pybuart.c index f6b917ab8..9dc4f0a00 100644 --- a/cc3200/mods/pybuart.c +++ b/cc3200/mods/pybuart.c @@ -46,7 +46,6 @@ #include "uart.h" #include "pybuart.h" #include "mpirq.h" -#include "pybioctl.h" #include "pybsleep.h" #include "mpexception.h" #include "py/mpstate.h" @@ -630,14 +629,14 @@ STATIC mp_uint_t pyb_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t a mp_uint_t ret; uart_check_init(self); - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_IOCTL_POLL_RD) && uart_rx_any(self)) { - ret |= MP_IOCTL_POLL_RD; + if ((flags & MP_STREAM_POLL_RD) && uart_rx_any(self)) { + ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_IOCTL_POLL_WR) && MAP_UARTSpaceAvail(self->reg)) { - ret |= MP_IOCTL_POLL_WR; + if ((flags & MP_STREAM_POLL_WR) && MAP_UARTSpaceAvail(self->reg)) { + ret |= MP_STREAM_POLL_WR; } } else { *errcode = EINVAL; diff --git a/extmod/moduselect.c b/extmod/moduselect.c index 8765ca2b0..2fc19cdf0 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -32,9 +32,9 @@ #include "py/runtime.h" #include "py/obj.h" #include "py/objlist.h" +#include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" -#include "pybioctl.h" // Flags for poll() #define FLAG_ONESHOT (1) @@ -87,7 +87,7 @@ STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value; int errcode; - mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_IOCTL_POLL, poll_obj->flags, &errcode); + mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj->flags, &errcode); poll_obj->flags_ret = ret; if (ret == -1) { @@ -99,13 +99,13 @@ STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) { // object is ready n_ready += 1; if (rwx_num != NULL) { - if (ret & MP_IOCTL_POLL_RD) { + if (ret & MP_STREAM_POLL_RD) { rwx_num[0] += 1; } - if (ret & MP_IOCTL_POLL_WR) { + if (ret & MP_STREAM_POLL_WR) { rwx_num[1] += 1; } - if ((ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { + if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) { rwx_num[2] += 1; } } @@ -141,9 +141,9 @@ STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { // merge separate lists and get the ioctl function for each object mp_map_t poll_map; mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]); - poll_map_add(&poll_map, r_array, rwx_len[0], MP_IOCTL_POLL_RD, true); - poll_map_add(&poll_map, w_array, rwx_len[1], MP_IOCTL_POLL_WR, true); - poll_map_add(&poll_map, x_array, rwx_len[2], MP_IOCTL_POLL_ERR | MP_IOCTL_POLL_HUP, true); + poll_map_add(&poll_map, r_array, rwx_len[0], MP_STREAM_POLL_RD, true); + poll_map_add(&poll_map, w_array, rwx_len[1], MP_STREAM_POLL_WR, true); + poll_map_add(&poll_map, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true); mp_uint_t start_tick = mp_hal_ticks_ms(); rwx_len[0] = rwx_len[1] = rwx_len[2] = 0; @@ -163,13 +163,13 @@ STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { continue; } poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value; - if (poll_obj->flags_ret & MP_IOCTL_POLL_RD) { + if (poll_obj->flags_ret & MP_STREAM_POLL_RD) { ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj; } - if (poll_obj->flags_ret & MP_IOCTL_POLL_WR) { + if (poll_obj->flags_ret & MP_STREAM_POLL_WR) { ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj; } - if ((poll_obj->flags_ret & ~(MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR)) != 0) { + if ((poll_obj->flags_ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) { ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj; } } @@ -195,7 +195,7 @@ STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) { if (n_args == 3) { flags = mp_obj_get_int(args[2]); } else { - flags = MP_IOCTL_POLL_RD | MP_IOCTL_POLL_WR; + flags = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR; } poll_map_add(&self->poll_map, &args[1], 1, flags, false); return mp_const_none; @@ -300,10 +300,10 @@ STATIC const mp_map_elem_t mp_module_select_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_uselect) }, { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_select_select_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_poll), (mp_obj_t)&mp_select_poll_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_RD) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_WR) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_ERR) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_IOCTL_POLL_HUP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLIN), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_RD) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLOUT), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_WR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLERR), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_ERR) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_POLLHUP), MP_OBJ_NEW_SMALL_INT(MP_STREAM_POLL_HUP) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table); diff --git a/py/stream.h b/py/stream.h index 94002e252..01199ab60 100644 --- a/py/stream.h +++ b/py/stream.h @@ -42,6 +42,12 @@ #define MP_STREAM_GET_DATA_OPTS (8) // Get data/message options #define MP_STREAM_SET_DATA_OPTS (9) // Set data/message options +// These poll ioctl values are compatible with Linux +#define MP_STREAM_POLL_RD (0x0001) +#define MP_STREAM_POLL_WR (0x0004) +#define MP_STREAM_POLL_ERR (0x0008) +#define MP_STREAM_POLL_HUP (0x0010) + // Argument structure for MP_STREAM_SEEK struct mp_stream_seek_t { mp_off_t offset; diff --git a/stmhal/can.c b/stmhal/can.c index a8ec7930e..3157bfadc 100644 --- a/stmhal/can.c +++ b/stmhal/can.c @@ -32,11 +32,11 @@ #include "py/objtuple.h" #include "py/runtime.h" #include "py/gc.h" +#include "py/stream.h" #include "py/mperrno.h" #include "py/mphal.h" #include "bufhelper.h" #include "can.h" -#include "pybioctl.h" #include "irq.h" #if MICROPY_HW_ENABLE_CAN @@ -807,16 +807,16 @@ STATIC MP_DEFINE_CONST_DICT(pyb_can_locals_dict, pyb_can_locals_dict_table); mp_uint_t can_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { pyb_can_obj_t *self = self_in; mp_uint_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_IOCTL_POLL_RD) + if ((flags & MP_STREAM_POLL_RD) && ((__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO0) != 0) || (__HAL_CAN_MSG_PENDING(&self->can, CAN_FIFO1) != 0))) { - ret |= MP_IOCTL_POLL_RD; + ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_IOCTL_POLL_WR) && (self->can.Instance->TSR & CAN_TSR_TME)) { - ret |= MP_IOCTL_POLL_WR; + if ((flags & MP_STREAM_POLL_WR) && (self->can.Instance->TSR & CAN_TSR_TME)) { + ret |= MP_STREAM_POLL_WR; } } else { *errcode = MP_EINVAL; diff --git a/stmhal/modnwcc3k.c b/stmhal/modnwcc3k.c index 591057602..dcef1f99b 100644 --- a/stmhal/modnwcc3k.c +++ b/stmhal/modnwcc3k.c @@ -41,7 +41,6 @@ #include "pin.h" #include "genhdr/pins.h" #include "spi.h" -#include "pybioctl.h" #include "hci.h" #include "socket.h" @@ -354,7 +353,7 @@ STATIC int cc3k_socket_settimeout(mod_network_socket_obj_t *socket, mp_uint_t ti STATIC int cc3k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request, mp_uint_t arg, int *_errno) { mp_uint_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; int fd = socket->u_state; @@ -366,19 +365,19 @@ STATIC int cc3k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request FD_ZERO(&xfds); // set fds if needed - if (flags & MP_IOCTL_POLL_RD) { + if (flags & MP_STREAM_POLL_RD) { FD_SET(fd, &rfds); // A socked that just closed is available for reading. A call to // recv() returns 0 which is consistent with BSD. if (cc3k_get_fd_closed_state(fd)) { - ret |= MP_IOCTL_POLL_RD; + ret |= MP_STREAM_POLL_RD; } } - if (flags & MP_IOCTL_POLL_WR) { + if (flags & MP_STREAM_POLL_WR) { FD_SET(fd, &wfds); } - if (flags & MP_IOCTL_POLL_HUP) { + if (flags & MP_STREAM_POLL_HUP) { FD_SET(fd, &xfds); } @@ -396,13 +395,13 @@ STATIC int cc3k_socket_ioctl(mod_network_socket_obj_t *socket, mp_uint_t request // check return of select if (FD_ISSET(fd, &rfds)) { - ret |= MP_IOCTL_POLL_RD; + ret |= MP_STREAM_POLL_RD; } if (FD_ISSET(fd, &wfds)) { - ret |= MP_IOCTL_POLL_WR; + ret |= MP_STREAM_POLL_WR; } if (FD_ISSET(fd, &xfds)) { - ret |= MP_IOCTL_POLL_HUP; + ret |= MP_STREAM_POLL_HUP; } } else { *_errno = MP_EINVAL; diff --git a/stmhal/pybioctl.h b/stmhal/pybioctl.h deleted file mode 100644 index b71e04ed5..000000000 --- a/stmhal/pybioctl.h +++ /dev/null @@ -1,8 +0,0 @@ -#define MP_IOCTL_POLL (0x100 | 1) - -// These values are compatible with Linux, which are in turn -// compatible with iBCS2 spec. -#define MP_IOCTL_POLL_RD (0x0001) -#define MP_IOCTL_POLL_WR (0x0004) -#define MP_IOCTL_POLL_ERR (0x0008) -#define MP_IOCTL_POLL_HUP (0x0010) diff --git a/stmhal/uart.c b/stmhal/uart.c index 37c1906c1..b16cf3481 100644 --- a/stmhal/uart.c +++ b/stmhal/uart.c @@ -34,7 +34,6 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "uart.h" -#include "pybioctl.h" #include "irq.h" //TODO: Add UART7/8 support for MCU_SERIES_F7 @@ -901,14 +900,14 @@ STATIC mp_uint_t pyb_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t STATIC mp_uint_t pyb_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { pyb_uart_obj_t *self = self_in; mp_uint_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_IOCTL_POLL_RD) && uart_rx_any(self)) { - ret |= MP_IOCTL_POLL_RD; + if ((flags & MP_STREAM_POLL_RD) && uart_rx_any(self)) { + ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_IOCTL_POLL_WR) && __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_TXE)) { - ret |= MP_IOCTL_POLL_WR; + if ((flags & MP_STREAM_POLL_WR) && __HAL_UART_GET_FLAG(&self->uart, UART_FLAG_TXE)) { + ret |= MP_STREAM_POLL_WR; } } else { *errcode = MP_EINVAL; diff --git a/stmhal/usb.c b/stmhal/usb.c index dd6daf8e5..b0a66ef9b 100644 --- a/stmhal/usb.c +++ b/stmhal/usb.c @@ -40,7 +40,6 @@ #include "py/mperrno.h" #include "bufhelper.h" #include "usb.h" -#include "pybioctl.h" #if defined(USE_USB_FS) #define USB_PHY_ID USB_PHY_FS_ID @@ -502,14 +501,14 @@ STATIC mp_uint_t pyb_usb_vcp_write(mp_obj_t self_in, const void *buf, mp_uint_t STATIC mp_uint_t pyb_usb_vcp_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { mp_uint_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_IOCTL_POLL_RD) && USBD_CDC_RxNum() > 0) { - ret |= MP_IOCTL_POLL_RD; + if ((flags & MP_STREAM_POLL_RD) && USBD_CDC_RxNum() > 0) { + ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_IOCTL_POLL_WR) && USBD_CDC_TxHalfEmpty()) { - ret |= MP_IOCTL_POLL_WR; + if ((flags & MP_STREAM_POLL_WR) && USBD_CDC_TxHalfEmpty()) { + ret |= MP_STREAM_POLL_WR; } } else { *errcode = MP_EINVAL; @@ -632,11 +631,11 @@ STATIC MP_DEFINE_CONST_DICT(pyb_usb_hid_locals_dict, pyb_usb_hid_locals_dict_tab STATIC mp_uint_t pyb_usb_hid_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { mp_uint_t ret; - if (request == MP_IOCTL_POLL) { + if (request == MP_STREAM_POLL) { mp_uint_t flags = arg; ret = 0; - if ((flags & MP_IOCTL_POLL_WR) && USBD_HID_CanSendReport(&hUSBDDevice)) { - ret |= MP_IOCTL_POLL_WR; + if ((flags & MP_STREAM_POLL_WR) && USBD_HID_CanSendReport(&hUSBDDevice)) { + ret |= MP_STREAM_POLL_WR; } } else { *errcode = MP_EINVAL; -- cgit v1.2.3 From 080a78b15edc465010864002d1c83a5730a85dd0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 7 Dec 2016 11:17:17 +1100 Subject: py/compile: Simplify configuration of native emitter. --- py/compile.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index bc8d8d16d..418c1100b 100644 --- a/py/compile.c +++ b/py/compile.c @@ -69,6 +69,21 @@ typedef enum { #endif +#if MICROPY_EMIT_NATIVE +// define a macro to access external native emitter +#if MICROPY_EMIT_X64 +#define NATIVE_EMITTER(f) emit_native_x64_##f +#elif MICROPY_EMIT_X86 +#define NATIVE_EMITTER(f) emit_native_x86_##f +#elif MICROPY_EMIT_THUMB +#define NATIVE_EMITTER(f) emit_native_thumb_##f +#elif MICROPY_EMIT_ARM +#define NATIVE_EMITTER(f) emit_native_arm_##f +#else +#error "unknown native emitter" +#endif +#endif + #define EMIT_INLINE_ASM(fun) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm)) #define EMIT_INLINE_ASM_ARG(fun, ...) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm, __VA_ARGS__)) @@ -3397,27 +3412,10 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f #if MICROPY_EMIT_NATIVE case MP_EMIT_OPT_NATIVE_PYTHON: case MP_EMIT_OPT_VIPER: -#if MICROPY_EMIT_X64 - if (emit_native == NULL) { - emit_native = emit_native_x64_new(&comp->compile_error, max_num_labels); - } - comp->emit_method_table = &emit_native_x64_method_table; -#elif MICROPY_EMIT_X86 if (emit_native == NULL) { - emit_native = emit_native_x86_new(&comp->compile_error, max_num_labels); + emit_native = NATIVE_EMITTER(new)(&comp->compile_error, max_num_labels); } - comp->emit_method_table = &emit_native_x86_method_table; -#elif MICROPY_EMIT_THUMB - if (emit_native == NULL) { - emit_native = emit_native_thumb_new(&comp->compile_error, max_num_labels); - } - comp->emit_method_table = &emit_native_thumb_method_table; -#elif MICROPY_EMIT_ARM - if (emit_native == NULL) { - emit_native = emit_native_arm_new(&comp->compile_error, max_num_labels); - } - comp->emit_method_table = &emit_native_arm_method_table; -#endif + comp->emit_method_table = &NATIVE_EMITTER(method_table); comp->emit = emit_native; EMIT_ARG(set_native_type, MP_EMIT_NATIVE_TYPE_ENABLE, s->emit_options == MP_EMIT_OPT_VIPER, 0); break; @@ -3460,15 +3458,7 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f emit_bc_free(emit_bc); #if MICROPY_EMIT_NATIVE if (emit_native != NULL) { -#if MICROPY_EMIT_X64 - emit_native_x64_free(emit_native); -#elif MICROPY_EMIT_X86 - emit_native_x86_free(emit_native); -#elif MICROPY_EMIT_THUMB - emit_native_thumb_free(emit_native); -#elif MICROPY_EMIT_ARM - emit_native_arm_free(emit_native); -#endif + NATIVE_EMITTER(free)(emit_native); } #endif #if MICROPY_EMIT_INLINE_THUMB -- cgit v1.2.3 From c2713592bc5dd2c9bcd9c306ec8c907c0125c8f7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 13:28:25 +1100 Subject: py/emit.h: Remove long-obsolete declarations for cpython emitter. --- py/emit.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'py') diff --git a/py/emit.h b/py/emit.h index 2b87e2c77..885033e3d 100644 --- a/py/emit.h +++ b/py/emit.h @@ -149,7 +149,6 @@ void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst); void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst); void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst); -extern const emit_method_table_t emit_cpython_method_table; extern const emit_method_table_t emit_bc_method_table; extern const emit_method_table_t emit_native_x64_method_table; extern const emit_method_table_t emit_native_x86_method_table; @@ -160,17 +159,14 @@ extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops; -emit_t *emit_cpython_new(void); emit_t *emit_bc_new(void); emit_t *emit_native_x64_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); emit_t *emit_native_x86_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); emit_t *emit_native_thumb_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); emit_t *emit_native_arm_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); -void emit_cpython_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels); void emit_bc_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels); -void emit_cpython_free(emit_t *emit); void emit_bc_free(emit_t *emit); void emit_native_x64_free(emit_t *emit); void emit_native_x86_free(emit_t *emit); -- cgit v1.2.3 From c8746e1e7243f27f2824a67c2ac9921839784857 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 8 Dec 2016 17:47:17 +1100 Subject: py: Move arch-specific assembler macros from emitnative to asmXXX.h. --- py/asmarm.h | 80 +++++++++++++++ py/asmthumb.h | 81 +++++++++++++++ py/asmx64.h | 83 +++++++++++++++ py/asmx86.h | 83 +++++++++++++++ py/emitnative.c | 311 +------------------------------------------------------- 5 files changed, 331 insertions(+), 307 deletions(-) (limited to 'py') diff --git a/py/asmarm.h b/py/asmarm.h index 263721057..e273b98d7 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -122,4 +122,84 @@ 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); +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (4) + +#define REG_RET ASM_ARM_REG_R0 +#define REG_ARG_1 ASM_ARM_REG_R0 +#define REG_ARG_2 ASM_ARM_REG_R1 +#define REG_ARG_3 ASM_ARM_REG_R2 +#define REG_ARG_4 ASM_ARM_REG_R3 + +#define REG_TEMP0 ASM_ARM_REG_R0 +#define REG_TEMP1 ASM_ARM_REG_R1 +#define REG_TEMP2 ASM_ARM_REG_R2 + +#define REG_LOCAL_1 ASM_ARM_REG_R4 +#define REG_LOCAL_2 ASM_ARM_REG_R5 +#define REG_LOCAL_3 ASM_ARM_REG_R6 +#define REG_LOCAL_NUM (3) + +#define ASM_T asm_arm_t +#define ASM_END_PASS asm_arm_end_pass +#define ASM_ENTRY asm_arm_entry +#define ASM_EXIT asm_arm_exit + +#define ASM_JUMP asm_arm_b_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ + do { \ + asm_arm_cmp_reg_i8(as, reg, 0); \ + asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ + do { \ + asm_arm_cmp_reg_i8(as, reg, 0); \ + asm_arm_bcc_label(as, ASM_ARM_CC_NE, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_arm_cmp_reg_reg(as, reg1, reg2); \ + asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \ + } while (0) +#define ASM_CALL_IND(as, ptr, idx) asm_arm_bl_ind(as, ptr, idx, ASM_ARM_REG_R3) + +#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_arm_mov_local_reg(as, (local_num), (reg)) +#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm)) +#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm)) +#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ + do { \ + asm_arm_mov_reg_i32(as, (reg_temp), (imm)); \ + asm_arm_mov_local_reg(as, (local_num), (reg_temp)); \ + } while (false) +#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_arm_mov_reg_local(as, (reg), (local_num)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src)) +#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_arm_mov_reg_local_addr(as, (reg), (local_num)) + +#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift)) +#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_arm_orr_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_arm_and_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_arm_add_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) + +#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base)) +#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base)) +#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) + +#endif // GENERIC_ASM_API + #endif // __MICROPY_INCLUDED_PY_ASMARM_H__ diff --git a/py/asmthumb.h b/py/asmthumb.h index c03b00da3..52e663b3b 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -237,4 +237,85 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narro 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 +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#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 +#define REG_TEMP2 ASM_THUMB_REG_R2 + +#define REG_LOCAL_1 ASM_THUMB_REG_R4 +#define REG_LOCAL_2 ASM_THUMB_REG_R5 +#define REG_LOCAL_3 ASM_THUMB_REG_R6 +#define REG_LOCAL_NUM (3) + +#define ASM_T asm_thumb_t +#define ASM_END_PASS asm_thumb_end_pass +#define ASM_ENTRY asm_thumb_entry +#define ASM_EXIT asm_thumb_exit + +#define ASM_JUMP asm_thumb_b_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ + do { \ + asm_thumb_cmp_rlo_i8(as, reg, 0); \ + asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ + do { \ + asm_thumb_cmp_rlo_i8(as, reg, 0); \ + asm_thumb_bcc_label(as, ASM_THUMB_CC_NE, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_thumb_cmp_rlo_rlo(as, reg1, reg2); \ + asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \ + } while (0) +#define ASM_CALL_IND(as, ptr, idx) asm_thumb_bl_ind(as, ptr, idx, ASM_THUMB_REG_R3) + +#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_thumb_mov_local_reg(as, (local_num), (reg)) +#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_optimised(as, (reg), (imm)) +#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_aligned(as, (reg), (imm)) +#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ + do { \ + asm_thumb_mov_reg_i32_optimised(as, (reg_temp), (imm)); \ + asm_thumb_mov_local_reg(as, (local_num), (reg_temp)); \ + } while (false) +#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local(as, (reg), (local_num)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) +#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local_addr(as, (reg), (local_num)) + +#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift)) +#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ORR, (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_EOR, (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_AND, (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_thumb_add_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + #endif // __MICROPY_INCLUDED_PY_ASMTHUMB_H__ diff --git a/py/asmx64.h b/py/asmx64.h index daf5b3d0b..f80c8da8b 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -115,4 +115,87 @@ 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); +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#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 +#define REG_TEMP1 ASM_X64_REG_RDI +#define REG_TEMP2 ASM_X64_REG_RSI + +// callee-save +#define REG_LOCAL_1 ASM_X64_REG_RBX +#define REG_LOCAL_2 ASM_X64_REG_R12 +#define REG_LOCAL_3 ASM_X64_REG_R13 +#define REG_LOCAL_NUM (3) + +#define ASM_T asm_x64_t +#define ASM_END_PASS asm_x64_end_pass +#define ASM_ENTRY asm_x64_entry +#define ASM_EXIT asm_x64_exit + +#define ASM_JUMP asm_x64_jmp_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ + do { \ + asm_x64_test_r8_with_r8(as, reg, reg); \ + asm_x64_jcc_label(as, ASM_X64_CC_JZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ + do { \ + asm_x64_test_r8_with_r8(as, reg, reg); \ + asm_x64_jcc_label(as, ASM_X64_CC_JNZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_x64_cmp_r64_with_r64(as, reg1, reg2); \ + asm_x64_jcc_label(as, ASM_X64_CC_JE, label); \ + } while (0) +#define ASM_CALL_IND(as, ptr, idx) asm_x64_call_ind(as, ptr, ASM_X64_REG_RAX) + +#define ASM_MOV_REG_TO_LOCAL asm_x64_mov_r64_to_local +#define ASM_MOV_IMM_TO_REG asm_x64_mov_i64_to_r64_optimised +#define ASM_MOV_ALIGNED_IMM_TO_REG asm_x64_mov_i64_to_r64_aligned +#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ + do { \ + asm_x64_mov_i64_to_r64_optimised(as, (imm), (reg_temp)); \ + asm_x64_mov_r64_to_local(as, (reg_temp), (local_num)); \ + } while (false) +#define ASM_MOV_LOCAL_TO_REG asm_x64_mov_local_to_r64 +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_MOV_LOCAL_ADDR_TO_REG asm_x64_mov_local_addr_to_r64 + +#define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg)) +#define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x64_or_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x64_xor_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x64_and_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x64_add_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest)) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + #endif // __MICROPY_INCLUDED_PY_ASMX64_H__ diff --git a/py/asmx86.h b/py/asmx86.h index 85352fff3..ac4b2ecd0 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -113,4 +113,87 @@ 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); +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#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 +#define REG_TEMP1 ASM_X86_REG_ECX +#define REG_TEMP2 ASM_X86_REG_EDX + +// callee-save, so can be used as locals +#define REG_LOCAL_1 ASM_X86_REG_EBX +#define REG_LOCAL_2 ASM_X86_REG_ESI +#define REG_LOCAL_3 ASM_X86_REG_EDI +#define REG_LOCAL_NUM (3) + +#define ASM_T asm_x86_t +#define ASM_END_PASS asm_x86_end_pass +#define ASM_ENTRY asm_x86_entry +#define ASM_EXIT asm_x86_exit + +#define ASM_JUMP asm_x86_jmp_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ + do { \ + asm_x86_test_r8_with_r8(as, reg, reg); \ + asm_x86_jcc_label(as, ASM_X86_CC_JZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ + do { \ + asm_x86_test_r8_with_r8(as, reg, reg); \ + asm_x86_jcc_label(as, ASM_X86_CC_JNZ, label); \ + } while (0) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + do { \ + asm_x86_cmp_r32_with_r32(as, reg1, reg2); \ + asm_x86_jcc_label(as, ASM_X86_CC_JE, label); \ + } while (0) +#define ASM_CALL_IND(as, ptr, idx) asm_x86_call_ind(as, ptr, mp_f_n_args[idx], ASM_X86_REG_EAX) + +#define ASM_MOV_REG_TO_LOCAL asm_x86_mov_r32_to_local +#define ASM_MOV_IMM_TO_REG asm_x86_mov_i32_to_r32 +#define ASM_MOV_ALIGNED_IMM_TO_REG asm_x86_mov_i32_to_r32_aligned +#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ + do { \ + asm_x86_mov_i32_to_r32(as, (imm), (reg_temp)); \ + asm_x86_mov_r32_to_local(as, (reg_temp), (local_num)); \ + } while (false) +#define ASM_MOV_LOCAL_TO_REG asm_x86_mov_local_to_r32 +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_MOV_LOCAL_ADDR_TO_REG asm_x86_mov_local_addr_to_r32 + +#define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg)) +#define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg)) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x86_or_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x86_xor_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x86_and_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x86_add_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) + +#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + #endif // __MICROPY_INCLUDED_PY_ASMX86_H__ diff --git a/py/emitnative.c b/py/emitnative.c index d19dfe467..5a7e0ea34 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -63,96 +63,19 @@ || (MICROPY_EMIT_THUMB && N_THUMB) \ || (MICROPY_EMIT_ARM && N_ARM) +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) + #if N_X64 // x64 specific stuff - #include "py/asmx64.h" - #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 -#define REG_TEMP1 ASM_X64_REG_RDI -#define REG_TEMP2 ASM_X64_REG_RSI - -// callee-save -#define REG_LOCAL_1 ASM_X64_REG_RBX -#define REG_LOCAL_2 ASM_X64_REG_R12 -#define REG_LOCAL_3 ASM_X64_REG_R13 -#define REG_LOCAL_NUM (3) - -#define ASM_T asm_x64_t -#define ASM_END_PASS asm_x64_end_pass -#define ASM_ENTRY asm_x64_entry -#define ASM_EXIT asm_x64_exit - -#define ASM_JUMP asm_x64_jmp_label -#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ - do { \ - asm_x64_test_r8_with_r8(as, reg, reg); \ - asm_x64_jcc_label(as, ASM_X64_CC_JZ, label); \ - } while (0) -#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ - do { \ - asm_x64_test_r8_with_r8(as, reg, reg); \ - asm_x64_jcc_label(as, ASM_X64_CC_JNZ, label); \ - } while (0) -#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ - do { \ - asm_x64_cmp_r64_with_r64(as, reg1, reg2); \ - asm_x64_jcc_label(as, ASM_X64_CC_JE, label); \ - } while (0) -#define ASM_CALL_IND(as, ptr, idx) asm_x64_call_ind(as, ptr, ASM_X64_REG_RAX) - -#define ASM_MOV_REG_TO_LOCAL asm_x64_mov_r64_to_local -#define ASM_MOV_IMM_TO_REG asm_x64_mov_i64_to_r64_optimised -#define ASM_MOV_ALIGNED_IMM_TO_REG asm_x64_mov_i64_to_r64_aligned -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_x64_mov_i64_to_r64_optimised(as, (imm), (reg_temp)); \ - asm_x64_mov_r64_to_local(as, (reg_temp), (local_num)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG asm_x64_mov_local_to_r64 -#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG asm_x64_mov_local_addr_to_r64 - -#define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg)) -#define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg)) -#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x64_or_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x64_xor_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x64_and_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x64_add_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src)) - -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest)) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) - #elif N_X86 // x86 specific stuff -#include "py/asmx86.h" - STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_CONVERT_OBJ_TO_NATIVE] = 2, [MP_F_CONVERT_NATIVE_TO_OBJ] = 2, @@ -201,247 +124,21 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { [MP_F_SETUP_CODE_STATE] = 5, }; +#include "py/asmx86.h" #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 -#define REG_TEMP1 ASM_X86_REG_ECX -#define REG_TEMP2 ASM_X86_REG_EDX - -// callee-save, so can be used as locals -#define REG_LOCAL_1 ASM_X86_REG_EBX -#define REG_LOCAL_2 ASM_X86_REG_ESI -#define REG_LOCAL_3 ASM_X86_REG_EDI -#define REG_LOCAL_NUM (3) - -#define ASM_T asm_x86_t -#define ASM_END_PASS asm_x86_end_pass -#define ASM_ENTRY asm_x86_entry -#define ASM_EXIT asm_x86_exit - -#define ASM_JUMP asm_x86_jmp_label -#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ - do { \ - asm_x86_test_r8_with_r8(as, reg, reg); \ - asm_x86_jcc_label(as, ASM_X86_CC_JZ, label); \ - } while (0) -#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ - do { \ - asm_x86_test_r8_with_r8(as, reg, reg); \ - asm_x86_jcc_label(as, ASM_X86_CC_JNZ, label); \ - } while (0) -#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ - do { \ - asm_x86_cmp_r32_with_r32(as, reg1, reg2); \ - asm_x86_jcc_label(as, ASM_X86_CC_JE, label); \ - } while (0) -#define ASM_CALL_IND(as, ptr, idx) asm_x86_call_ind(as, ptr, mp_f_n_args[idx], ASM_X86_REG_EAX) - -#define ASM_MOV_REG_TO_LOCAL asm_x86_mov_r32_to_local -#define ASM_MOV_IMM_TO_REG asm_x86_mov_i32_to_r32 -#define ASM_MOV_ALIGNED_IMM_TO_REG asm_x86_mov_i32_to_r32_aligned -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_x86_mov_i32_to_r32(as, (imm), (reg_temp)); \ - asm_x86_mov_r32_to_local(as, (reg_temp), (local_num)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG asm_x86_mov_local_to_r32 -#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG asm_x86_mov_local_addr_to_r32 - -#define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg)) -#define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg)) -#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x86_or_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x86_xor_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x86_and_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x86_add_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src)) - -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) - #elif N_THUMB // thumb specific stuff - #include "py/asmthumb.h" - #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 -#define REG_TEMP2 ASM_THUMB_REG_R2 - -#define REG_LOCAL_1 ASM_THUMB_REG_R4 -#define REG_LOCAL_2 ASM_THUMB_REG_R5 -#define REG_LOCAL_3 ASM_THUMB_REG_R6 -#define REG_LOCAL_NUM (3) - -#define ASM_T asm_thumb_t -#define ASM_END_PASS asm_thumb_end_pass -#define ASM_ENTRY asm_thumb_entry -#define ASM_EXIT asm_thumb_exit - -#define ASM_JUMP asm_thumb_b_label -#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ - do { \ - asm_thumb_cmp_rlo_i8(as, reg, 0); \ - asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \ - } while (0) -#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ - do { \ - asm_thumb_cmp_rlo_i8(as, reg, 0); \ - asm_thumb_bcc_label(as, ASM_THUMB_CC_NE, label); \ - } while (0) -#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ - do { \ - asm_thumb_cmp_rlo_rlo(as, reg1, reg2); \ - asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \ - } while (0) -#define ASM_CALL_IND(as, ptr, idx) asm_thumb_bl_ind(as, ptr, idx, ASM_THUMB_REG_R3) - -#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_thumb_mov_local_reg(as, (local_num), (reg)) -#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_optimised(as, (reg), (imm)) -#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_thumb_mov_reg_i32_aligned(as, (reg), (imm)) -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_thumb_mov_reg_i32_optimised(as, (reg_temp), (imm)); \ - asm_thumb_mov_local_reg(as, (local_num), (reg_temp)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local(as, (reg), (local_num)) -#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_thumb_mov_reg_local_addr(as, (reg), (local_num)) - -#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift)) -#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift)) -#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ORR, (reg_dest), (reg_src)) -#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_EOR, (reg_dest), (reg_src)) -#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_AND, (reg_dest), (reg_src)) -#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_thumb_add_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src)) - -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) - #elif N_ARM // ARM specific stuff - #include "py/asmarm.h" - -#define ASM_WORD_SIZE (4) - #define EXPORT_FUN(name) emit_native_arm_##name -#define REG_RET ASM_ARM_REG_R0 -#define REG_ARG_1 ASM_ARM_REG_R0 -#define REG_ARG_2 ASM_ARM_REG_R1 -#define REG_ARG_3 ASM_ARM_REG_R2 -#define REG_ARG_4 ASM_ARM_REG_R3 - -#define REG_TEMP0 ASM_ARM_REG_R0 -#define REG_TEMP1 ASM_ARM_REG_R1 -#define REG_TEMP2 ASM_ARM_REG_R2 - -#define REG_LOCAL_1 ASM_ARM_REG_R4 -#define REG_LOCAL_2 ASM_ARM_REG_R5 -#define REG_LOCAL_3 ASM_ARM_REG_R6 -#define REG_LOCAL_NUM (3) - -#define ASM_T asm_arm_t -#define ASM_END_PASS asm_arm_end_pass -#define ASM_ENTRY asm_arm_entry -#define ASM_EXIT asm_arm_exit - -#define ASM_JUMP asm_arm_b_label -#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ - do { \ - asm_arm_cmp_reg_i8(as, reg, 0); \ - asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \ - } while (0) -#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ - do { \ - asm_arm_cmp_reg_i8(as, reg, 0); \ - asm_arm_bcc_label(as, ASM_ARM_CC_NE, label); \ - } while (0) -#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ - do { \ - asm_arm_cmp_reg_reg(as, reg1, reg2); \ - asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \ - } while (0) -#define ASM_CALL_IND(as, ptr, idx) asm_arm_bl_ind(as, ptr, idx, ASM_ARM_REG_R3) - -#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_arm_mov_local_reg(as, (local_num), (reg)) -#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm)) -#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_arm_mov_reg_i32(as, (reg), (imm)) -#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ - do { \ - asm_arm_mov_reg_i32(as, (reg_temp), (imm)); \ - asm_arm_mov_local_reg(as, (local_num), (reg_temp)); \ - } while (false) -#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_arm_mov_reg_local(as, (reg), (local_num)) -#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src)) -#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_arm_mov_reg_local_addr(as, (reg), (local_num)) - -#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift)) -#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift)) -#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_arm_orr_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_arm_and_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_arm_add_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) - -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) - -#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base)) -#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base)) -#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) - #else #error unknown native emitter -- cgit v1.2.3 From 81316fa41195e648c32ac0bc6859d4527b5ee9f1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 16:27:17 +1100 Subject: py/asmbase: Add MP_PLAT_COMMIT_EXEC option for handling exec code. If a port defines MP_PLAT_COMMIT_EXEC then this function is used to turn RAM data into executable code. For example a port may want to write the data to flash for execution. The function must return a pointer to the executable data. --- py/asmbase.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'py') diff --git a/py/asmbase.h b/py/asmbase.h index bfc8ac7ff..06fdd4b90 100644 --- a/py/asmbase.h +++ b/py/asmbase.h @@ -62,7 +62,11 @@ static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) { } static inline void *mp_asm_base_get_code(mp_asm_base_t *as) { + #if defined(MP_PLAT_COMMIT_EXEC) + return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size); + #else return as->code_base; + #endif } #endif // MICROPY_INCLUDED_PY_ASMBASE_H -- cgit v1.2.3 From fcac4b07f1154ae5a354de3cd1fe5297edb75bff Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 16:32:30 +1100 Subject: py/asmxtensa: Add low-level Xtensa assembler. --- py/asmxtensa.c | 170 ++++++++++++++++++++++++++++++ py/asmxtensa.h | 324 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 494 insertions(+) create mode 100644 py/asmxtensa.c create mode 100644 py/asmxtensa.h (limited to 'py') diff --git a/py/asmxtensa.c b/py/asmxtensa.c new file mode 100644 index 000000000..63aad02c0 --- /dev/null +++ b/py/asmxtensa.c @@ -0,0 +1,170 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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 +#include + +#include "py/mpconfig.h" + +// wrapper around everything in this file +#if MICROPY_EMIT_XTENSA + +#include "py/asmxtensa.h" + +#define WORD_SIZE (4) +#define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)) +#define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)) + +void asm_xtensa_end_pass(asm_xtensa_t *as) { + as->num_const = as->cur_const; + as->cur_const = 0; + + #if 0 + // make a hex dump of the machine code + if (as->base.pass == MP_ASM_PASS_EMIT) { + uint8_t *d = as->base.code_base; + printf("XTENSA ASM:"); + for (int i = 0; i < ((as->base.code_size + 15) & ~15); ++i) { + if (i % 16 == 0) { + printf("\n%08x:", (uint32_t)&d[i]); + } + if (i % 2 == 0) { + printf(" "); + } + printf("%02x", d[i]); + } + printf("\n"); + } + #endif +} + +void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) { + // jump over the constants + asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); + mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte + as->const_table = (uint32_t*)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + + // adjust the stack-pointer to store a0, a12, a13, a14 and locals, 16-byte aligned + as->stack_adjust = (((4 + num_locals) * WORD_SIZE) + 15) & ~15; + asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, -as->stack_adjust); + + // save return value (a0) and callee-save registers (a12, a13, a14) + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A12, ASM_XTENSA_REG_A1, 1); + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A13, ASM_XTENSA_REG_A1, 2); + asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A14, ASM_XTENSA_REG_A1, 3); +} + +void asm_xtensa_exit(asm_xtensa_t *as) { + // restore registers + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A14, ASM_XTENSA_REG_A1, 3); + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A13, ASM_XTENSA_REG_A1, 2); + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A12, ASM_XTENSA_REG_A1, 1); + asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0); + + // restore stack-pointer and return + asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, as->stack_adjust); + asm_xtensa_op_ret_n(as); +} + +STATIC uint32_t get_label_dest(asm_xtensa_t *as, uint label) { + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; +} + +void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2); + c[0] = op; + c[1] = op >> 8; +} + +void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op) { + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3); + c[0] = op; + c[1] = op >> 8; + c[2] = op >> 16; +} + +void asm_xtensa_j_label(asm_xtensa_t *as, uint label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + // we assume rel, as a signed int, fits in 18-bits + asm_xtensa_op_j(as, rel); +} + +void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) { + printf("ERROR: xtensa bccz out of range\n"); + } + asm_xtensa_op_bccz(as, cond, reg, rel); +} + +void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) { + printf("ERROR: xtensa bcc out of range\n"); + } + asm_xtensa_op_bcc(as, cond, reg1, reg2, rel); +} + +// convenience function; reg_dest must be different from reg_src[12] +void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2) { + asm_xtensa_op_movi_n(as, reg_dest, 1); + asm_xtensa_op_bcc(as, cond, reg_src1, reg_src2, 1); + asm_xtensa_op_movi_n(as, reg_dest, 0); +} + +void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) { + if (SIGNED_FIT12(i32)) { + asm_xtensa_op_movi(as, reg_dest, i32); + } else { + // load the constant + asm_xtensa_op_l32r(as, reg_dest, as->base.code_offset, 4 + as->cur_const * WORD_SIZE); + // store the constant in the table + if (as->base.pass == MP_ASM_PASS_EMIT) { + as->const_table[as->cur_const] = i32; + } + ++as->cur_const; + } +} + +void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src) { + asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, 4 + local_num); +} + +void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num) { + asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, 4 + local_num); +} + +void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num) { + asm_xtensa_op_mov_n(as, reg_dest, ASM_XTENSA_REG_A1); + asm_xtensa_op_addi(as, reg_dest, reg_dest, (4 + local_num) * WORD_SIZE); +} + +#endif // MICROPY_EMIT_XTENSA diff --git a/py/asmxtensa.h b/py/asmxtensa.h new file mode 100644 index 000000000..12083252e --- /dev/null +++ b/py/asmxtensa.h @@ -0,0 +1,324 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 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_ASMXTENSA_H +#define MICROPY_INCLUDED_PY_ASMXTENSA_H + +#include "py/asmbase.h" + +// calling conventions: +// up to 6 args in a2-a7 +// return value in a2 +// PC stored in a0 +// stack pointer is a1, stack full descending, is aligned to 16 bytes +// callee save: a1, a12, a13, a14, a15 +// caller save: a3 + +#define ASM_XTENSA_REG_A0 (0) +#define ASM_XTENSA_REG_A1 (1) +#define ASM_XTENSA_REG_A2 (2) +#define ASM_XTENSA_REG_A3 (3) +#define ASM_XTENSA_REG_A4 (4) +#define ASM_XTENSA_REG_A5 (5) +#define ASM_XTENSA_REG_A6 (6) +#define ASM_XTENSA_REG_A7 (7) +#define ASM_XTENSA_REG_A8 (8) +#define ASM_XTENSA_REG_A9 (9) +#define ASM_XTENSA_REG_A10 (10) +#define ASM_XTENSA_REG_A11 (11) +#define ASM_XTENSA_REG_A12 (12) +#define ASM_XTENSA_REG_A13 (13) +#define ASM_XTENSA_REG_A14 (14) +#define ASM_XTENSA_REG_A15 (15) + +// for bccz +#define ASM_XTENSA_CCZ_EQ (0) +#define ASM_XTENSA_CCZ_NE (1) + +// for bcc and setcc +#define ASM_XTENSA_CC_NONE (0) +#define ASM_XTENSA_CC_EQ (1) +#define ASM_XTENSA_CC_LT (2) +#define ASM_XTENSA_CC_LTU (3) +#define ASM_XTENSA_CC_ALL (4) +#define ASM_XTENSA_CC_BC (5) +#define ASM_XTENSA_CC_ANY (8) +#define ASM_XTENSA_CC_NE (9) +#define ASM_XTENSA_CC_GE (10) +#define ASM_XTENSA_CC_GEU (11) +#define ASM_XTENSA_CC_NALL (12) +#define ASM_XTENSA_CC_BS (13) + +// macros for encoding instructions (little endian versions) +#define ASM_XTENSA_ENCODE_RRR(op0, op1, op2, r, s, t) \ + (((op2) << 20) | ((op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RRI4(op0, op1, r, s, t, imm4) \ + (((imm4) << 20) | ((op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RRI8(op0, r, s, t, imm8) \ + (((imm8) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RI16(op0, t, imm16) \ + (((imm16) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RSR(op0, op1, op2, rs, t) \ + (((op2) << 20) | ((op1) << 16) | ((rs) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_CALL(op0, n, offset) \ + (((offset) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_CALLX(op0, op1, op2, r, s, m, n) \ + (((op2) << 20) | ((op1) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_BRI8(op0, r, s, m, n, imm8) \ + (((imm8) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_BRI12(op0, s, m, n, imm12) \ + (((imm12) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RRRN(op0, r, s, t) \ + (((r) << 12) | ((s) << 8) | ((t) << 4) | (op0)) +#define ASM_XTENSA_ENCODE_RI7(op0, s, imm7) \ + ((((imm7) & 0xf) << 12) | ((s) << 8) | ((imm7) & 0x70) | (op0)) + +typedef struct _asm_xtensa_t { + mp_asm_base_t base; + uint32_t cur_const; + uint32_t num_const; + uint32_t *const_table; + uint32_t stack_adjust; +} asm_xtensa_t; + +void asm_xtensa_end_pass(asm_xtensa_t *as); + +void asm_xtensa_entry(asm_xtensa_t *as, int num_locals); +void asm_xtensa_exit(asm_xtensa_t *as); + +void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op); +void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op); + +// raw instructions + +static inline void asm_xtensa_op_add(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 8, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_addi(asm_xtensa_t *as, uint reg_dest, uint reg_src, int imm8) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 12, reg_dest, reg_src, imm8 & 0xff)); +} + +static inline void asm_xtensa_op_and(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 1, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_bcc(asm_xtensa_t *as, uint cond, uint reg_src1, uint reg_src2, int32_t rel8) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(7, cond, reg_src1, reg_src2, rel8 & 0xff)); +} + +static inline void asm_xtensa_op_bccz(asm_xtensa_t *as, uint cond, uint reg_src, int32_t rel12) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, cond, 1, rel12 & 0xfff)); +} + +static inline void asm_xtensa_op_callx0(asm_xtensa_t *as, uint reg) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 0)); +} + +static inline void asm_xtensa_op_j(asm_xtensa_t *as, int32_t rel18) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(6, 0, rel18 & 0x3ffff)); +} + +static inline void asm_xtensa_op_jx(asm_xtensa_t *as, uint reg) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 2, 2)); +} + +static inline void asm_xtensa_op_l8ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint byte_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0, reg_base, reg_dest, byte_offset & 0xff)); +} + +static inline void asm_xtensa_op_l16ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint half_word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 1, reg_base, reg_dest, half_word_offset & 0xff)); +} + +static inline void asm_xtensa_op_l32i(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 2, reg_base, reg_dest, word_offset & 0xff)); +} + +static inline void asm_xtensa_op_l32i_n(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(8, word_offset & 0xf, reg_base, reg_dest)); +} + +static inline void asm_xtensa_op_l32r(asm_xtensa_t *as, uint reg_dest, uint32_t op_off, uint32_t dest_off) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RI16(1, reg_dest, ((dest_off - ((op_off + 3) & ~3)) >> 2) & 0xffff)); +} + +static inline void asm_xtensa_op_mov_n(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 0, reg_src, reg_dest)); +} + +static inline void asm_xtensa_op_movi(asm_xtensa_t *as, uint reg_dest, int32_t imm12) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 10, (imm12 >> 8) & 0xf, reg_dest, imm12 & 0xff)); +} + +static inline void asm_xtensa_op_movi_n(asm_xtensa_t *as, uint reg_dest, int imm4) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RI7(12, reg_dest, imm4)); +} + +static inline void asm_xtensa_op_mull(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 2, 8, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_or(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 2, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_ret_n(asm_xtensa_t *as) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 0)); +} + +static inline void asm_xtensa_op_s8i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint byte_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 4, reg_base, reg_src, byte_offset & 0xff)); +} + +static inline void asm_xtensa_op_s16i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint half_word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 5, reg_base, reg_src, half_word_offset & 0xff)); +} + +static inline void asm_xtensa_op_s32i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 6, reg_base, reg_src, word_offset & 0xff)); +} + +static inline void asm_xtensa_op_s32i_n(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { + asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(9, word_offset & 0xf, reg_base, reg_src)); +} + +static inline void asm_xtensa_op_sll(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, reg_dest, reg_src, 0)); +} + +static inline void asm_xtensa_op_sra(asm_xtensa_t *as, uint reg_dest, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, reg_dest, 0, reg_src)); +} + +static inline void asm_xtensa_op_ssl(asm_xtensa_t *as, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 1, reg_src, 0)); +} + +static inline void asm_xtensa_op_ssr(asm_xtensa_t *as, uint reg_src) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 0, reg_src, 0)); +} + +static inline void asm_xtensa_op_sub(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 12, reg_dest, reg_src_a, reg_src_b)); +} + +static inline void asm_xtensa_op_xor(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 3, reg_dest, reg_src_a, reg_src_b)); +} + +// convenience functions +void asm_xtensa_j_label(asm_xtensa_t *as, uint label); +void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label); +void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label); +void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2); +void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32); +void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src); +void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num); +void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num); + +#if GENERIC_ASM_API + +// The following macros provide a (mostly) arch-independent API to +// generate native code, and are used by the native emitter. + +#define ASM_WORD_SIZE (4) + +#define REG_RET ASM_XTENSA_REG_A2 +#define REG_ARG_1 ASM_XTENSA_REG_A2 +#define REG_ARG_2 ASM_XTENSA_REG_A3 +#define REG_ARG_3 ASM_XTENSA_REG_A4 +#define REG_ARG_4 ASM_XTENSA_REG_A5 +#define REG_ARG_5 ASM_XTENSA_REG_A6 + +#define REG_TEMP0 ASM_XTENSA_REG_A2 +#define REG_TEMP1 ASM_XTENSA_REG_A3 +#define REG_TEMP2 ASM_XTENSA_REG_A4 + +#define REG_LOCAL_1 ASM_XTENSA_REG_A12 +#define REG_LOCAL_2 ASM_XTENSA_REG_A13 +#define REG_LOCAL_3 ASM_XTENSA_REG_A14 +#define REG_LOCAL_NUM (3) + +#define ASM_T asm_xtensa_t +#define ASM_END_PASS asm_xtensa_end_pass +#define ASM_ENTRY asm_xtensa_entry +#define ASM_EXIT asm_xtensa_exit + +#define ASM_JUMP asm_xtensa_j_label +#define ASM_JUMP_IF_REG_ZERO(as, reg, label) \ + asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_EQ, reg, label) +#define ASM_JUMP_IF_REG_NONZERO(as, reg, label) \ + asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_NE, reg, label) +#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \ + asm_xtensa_bcc_reg_reg_label(as, ASM_XTENSA_CC_EQ, reg1, reg2, label) +#define ASM_CALL_IND(as, ptr, idx) \ + do { \ + asm_xtensa_mov_reg_i32(as, ASM_XTENSA_REG_A0, (uint32_t)ptr); \ + asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0); \ + } while (0) + +#define ASM_MOV_REG_TO_LOCAL(as, reg, local_num) asm_xtensa_mov_local_reg(as, (local_num), (reg)) +#define ASM_MOV_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm)) +#define ASM_MOV_ALIGNED_IMM_TO_REG(as, imm, reg) asm_xtensa_mov_reg_i32(as, (reg), (imm)) +#define ASM_MOV_IMM_TO_LOCAL_USING(as, imm, local_num, reg_temp) \ + do { \ + asm_xtensa_mov_reg_i32(as, (reg_temp), (imm)); \ + asm_xtensa_mov_local_reg(as, (local_num), (reg_temp)); \ + } while (0) +#define ASM_MOV_LOCAL_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local(as, (reg), (local_num)) +#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src)) +#define ASM_MOV_LOCAL_ADDR_TO_REG(as, local_num, reg) asm_xtensa_mov_reg_local_addr(as, (reg), (local_num)) + +#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \ + do { \ + asm_xtensa_op_ssl((as), (reg_shift)); \ + asm_xtensa_op_sll((as), (reg_dest), (reg_dest)); \ + } while (0) +#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) \ + do { \ + asm_xtensa_op_ssr((as), (reg_shift)); \ + asm_xtensa_op_sra((as), (reg_dest), (reg_dest)); \ + } while (0) +#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_or((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_xor((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_and((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_add((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src)) +#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src)) + +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_s32i_n((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0) + +#endif // GENERIC_ASM_API + +#endif // MICROPY_INCLUDED_PY_ASMXTENSA_H -- cgit v1.2.3 From 8e5aced1fd4845b23b203d8d15f9b34b6b022ceb Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 16:39:39 +1100 Subject: py: Integrate Xtensa assembler into native emitter. The config option MICROPY_EMIT_XTENSA can now be enabled to target the Xtensa architecture with @micropython.native and @micropython.viper decorators. --- py/compile.c | 2 ++ py/emit.h | 3 +++ py/emitnative.c | 24 +++++++++++++++++++++++- py/mkrules.mk | 2 +- py/mpconfig.h | 7 ++++++- py/py.mk | 8 +++++++- 6 files changed, 42 insertions(+), 4 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index 418c1100b..7c2c89fb5 100644 --- a/py/compile.c +++ b/py/compile.c @@ -79,6 +79,8 @@ typedef enum { #define NATIVE_EMITTER(f) emit_native_thumb_##f #elif MICROPY_EMIT_ARM #define NATIVE_EMITTER(f) emit_native_arm_##f +#elif MICROPY_EMIT_XTENSA +#define NATIVE_EMITTER(f) emit_native_xtensa_##f #else #error "unknown native emitter" #endif diff --git a/py/emit.h b/py/emit.h index 885033e3d..122df8e7d 100644 --- a/py/emit.h +++ b/py/emit.h @@ -154,6 +154,7 @@ extern const emit_method_table_t emit_native_x64_method_table; extern const emit_method_table_t emit_native_x86_method_table; extern const emit_method_table_t emit_native_thumb_method_table; extern const emit_method_table_t emit_native_arm_method_table; +extern const emit_method_table_t emit_native_xtensa_method_table; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops; extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops; @@ -164,6 +165,7 @@ emit_t *emit_native_x64_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); emit_t *emit_native_x86_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); emit_t *emit_native_thumb_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); emit_t *emit_native_arm_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); +emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, mp_uint_t max_num_labels); void emit_bc_set_max_num_labels(emit_t* emit, mp_uint_t max_num_labels); @@ -172,6 +174,7 @@ void emit_native_x64_free(emit_t *emit); void emit_native_x86_free(emit_t *emit); void emit_native_thumb_free(emit_t *emit); void emit_native_arm_free(emit_t *emit); +void emit_native_xtensa_free(emit_t *emit); void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope); void mp_emit_bc_end_pass(emit_t *emit); diff --git a/py/emitnative.c b/py/emitnative.c index 5a7e0ea34..2e18d26b4 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -61,7 +61,8 @@ #if (MICROPY_EMIT_X64 && N_X64) \ || (MICROPY_EMIT_X86 && N_X86) \ || (MICROPY_EMIT_THUMB && N_THUMB) \ - || (MICROPY_EMIT_ARM && N_ARM) + || (MICROPY_EMIT_ARM && N_ARM) \ + || (MICROPY_EMIT_XTENSA && N_XTENSA) \ // this is defined so that the assembler exports generic assembler API macros #define GENERIC_ASM_API (1) @@ -139,6 +140,12 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { #include "py/asmarm.h" #define EXPORT_FUN(name) emit_native_arm_##name +#elif N_XTENSA + +// Xtensa specific stuff +#include "py/asmxtensa.h" +#define EXPORT_FUN(name) emit_native_xtensa_##name + #else #error unknown native emitter @@ -1965,6 +1972,21 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { ASM_ARM_CC_NE, }; asm_arm_setcc_reg(emit->as, REG_RET, ccs[op - MP_BINARY_OP_LESS]); + #elif N_XTENSA + static uint8_t ccs[6] = { + ASM_XTENSA_CC_LT, + 0x80 | ASM_XTENSA_CC_LT, // for GT we'll swap args + ASM_XTENSA_CC_EQ, + 0x80 | ASM_XTENSA_CC_GE, // for LE we'll swap args + ASM_XTENSA_CC_GE, + ASM_XTENSA_CC_NE, + }; + uint8_t cc = ccs[op - MP_BINARY_OP_LESS]; + if ((cc & 0x80) == 0) { + asm_xtensa_setcc_reg_reg_reg(emit->as, cc, REG_RET, REG_ARG_2, reg_rhs); + } else { + asm_xtensa_setcc_reg_reg_reg(emit->as, cc & ~0x80, REG_RET, reg_rhs, REG_ARG_2); + } #else #error not implemented #endif diff --git a/py/mkrules.mk b/py/mkrules.mk index d509f7b48..f4a2363a7 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -49,7 +49,7 @@ $(BUILD)/%.o: %.c # List all native flags since the current build system doesn't have # the micropython configuration available. However, these flags are # needed to extract all qstrings -QSTR_GEN_EXTRA_CFLAGS += -DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB -DN_ARM +QSTR_GEN_EXTRA_CFLAGS += -DNO_QSTR -DN_X64 -DN_X86 -DN_THUMB -DN_ARM -DN_XTENSA QSTR_GEN_EXTRA_CFLAGS += -I$(BUILD)/tmp vpath %.c . $(TOP) diff --git a/py/mpconfig.h b/py/mpconfig.h index 4572fc4cc..15f770573 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -288,8 +288,13 @@ #define MICROPY_EMIT_ARM (0) #endif +// Whether to emit Xtensa native code +#ifndef MICROPY_EMIT_XTENSA +#define MICROPY_EMIT_XTENSA (0) +#endif + // Convenience definition for whether any native emitter is enabled -#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM) +#define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA) /*****************************************************************************/ /* Compiler configuration */ diff --git a/py/py.mk b/py/py.mk index 87a860f75..9d2a188f1 100644 --- a/py/py.mk +++ b/py/py.mk @@ -130,6 +130,8 @@ PY_O_BASENAME = \ emitinlinethumb.o \ asmarm.o \ emitnarm.o \ + asmxtensa.o \ + emitnxtensa.o \ formatfloat.o \ parsenumbase.o \ parsenum.o \ @@ -250,7 +252,7 @@ PY_O += $(BUILD)/$(BUILD)/frozen_mpy.o endif # Sources that may contain qstrings -SRC_QSTR_IGNORE = nlr% emitnx% emitnthumb% emitnarm% +SRC_QSTR_IGNORE = nlr% emitnx86% emitnx64% emitnthumb% emitnarm% emitnxtensa% SRC_QSTR = $(SRC_MOD) $(addprefix py/,$(filter-out $(SRC_QSTR_IGNORE),$(PY_O_BASENAME:.o=.c)) emitnative.c) # Anything that depends on FORCE will be considered out-of-date @@ -292,6 +294,10 @@ $(PY_BUILD)/emitnarm.o: CFLAGS += -DN_ARM $(PY_BUILD)/emitnarm.o: py/emitnative.c $(call compile_c) +$(PY_BUILD)/emitnxtensa.o: CFLAGS += -DN_XTENSA +$(PY_BUILD)/emitnxtensa.o: py/emitnative.c + $(call compile_c) + # optimising gc for speed; 5ms down to 4ms on pybv2 $(PY_BUILD)/gc.o: CFLAGS += $(CSUPEROPT) -- cgit v1.2.3 From ad297a1950c74c35c90dd655ae9a69d33ed28dc0 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 13:17:49 +1100 Subject: py: Allow inline-assembler emitter to be generic. This patch refactors some code so that it is easier to integrate new inline assemblers for different architectures other than ARM Thumb. --- py/asmbase.c | 4 ++-- py/compile.c | 54 ++++++++++++++++++++++++++++++------------------------ py/compile.h | 2 +- py/emitglue.c | 4 ++-- py/mpconfig.h | 3 +++ py/nativeglue.c | 2 +- py/objfun.c | 4 ++-- 7 files changed, 41 insertions(+), 32 deletions(-) (limited to 'py') diff --git a/py/asmbase.c b/py/asmbase.c index 916f7c5ec..848730593 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -31,7 +31,7 @@ #include "py/misc.h" #include "py/asmbase.h" -#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB +#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) { as->max_num_labels = max_num_labels; @@ -101,4 +101,4 @@ void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val) { } } -#endif // MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB +#endif // MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM diff --git a/py/compile.c b/py/compile.c index 7c2c89fb5..b85e659e7 100644 --- a/py/compile.c +++ b/py/compile.c @@ -86,6 +86,16 @@ typedef enum { #endif #endif +#if MICROPY_EMIT_INLINE_ASM +// define macros for inline assembler +#if MICROPY_EMIT_INLINE_THUMB +#define ASM_DECORATOR_QSTR MP_QSTR_asm_thumb +#define ASM_EMITTER(f) emit_inline_thumb_##f +#else +#error "unknown asm emitter" +#endif +#endif + #define EMIT_INLINE_ASM(fun) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm)) #define EMIT_INLINE_ASM_ARG(fun, ...) (comp->emit_inline_asm_method_table->fun(comp->emit_inline_asm, __VA_ARGS__)) @@ -120,7 +130,7 @@ typedef struct _compiler_t { const emit_method_table_t *emit_method_table; // current emit method table #endif - #if MICROPY_EMIT_INLINE_THUMB + #if MICROPY_EMIT_INLINE_ASM emit_inline_asm_t *emit_inline_asm; // current emitter for inline asm const emit_inline_asm_method_table_t *emit_inline_asm_method_table; // current emit method table for inline asm #endif @@ -767,10 +777,10 @@ STATIC bool compile_built_in_decorator(compiler_t *comp, int name_len, mp_parse_ } else if (attr == MP_QSTR_viper) { *emit_options = MP_EMIT_OPT_VIPER; #endif -#if MICROPY_EMIT_INLINE_THUMB - } else if (attr == MP_QSTR_asm_thumb) { - *emit_options = MP_EMIT_OPT_ASM_THUMB; -#endif + #if MICROPY_EMIT_INLINE_ASM + } else if (attr == ASM_DECORATOR_QSTR) { + *emit_options = MP_EMIT_OPT_ASM; + #endif } else { compile_syntax_error(comp, name_nodes[1], "invalid micropython decorator"); } @@ -3100,7 +3110,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) { assert(comp->cur_except_level == 0); } -#if MICROPY_EMIT_INLINE_THUMB +#if MICROPY_EMIT_INLINE_ASM // requires 3 passes: SCOPE, CODE_SIZE, EMIT STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind_t pass) { comp->pass = pass; @@ -3357,10 +3367,10 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f uint max_num_labels = 0; for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { if (false) { -#if MICROPY_EMIT_INLINE_THUMB - } else if (s->emit_options == MP_EMIT_OPT_ASM_THUMB) { + #if MICROPY_EMIT_INLINE_ASM + } else if (s->emit_options == MP_EMIT_OPT_ASM) { compile_scope_inline_asm(comp, s, MP_PASS_SCOPE); -#endif + #endif } else { compile_scope(comp, s, MP_PASS_SCOPE); } @@ -3382,28 +3392,24 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f // compile pass 2 and 3 #if MICROPY_EMIT_NATIVE emit_t *emit_native = NULL; -#endif -#if MICROPY_EMIT_INLINE_THUMB - emit_inline_asm_t *emit_inline_thumb = NULL; #endif for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) { if (false) { // dummy -#if MICROPY_EMIT_INLINE_THUMB - } else if (s->emit_options == MP_EMIT_OPT_ASM_THUMB) { - // inline assembly for thumb - if (emit_inline_thumb == NULL) { - emit_inline_thumb = emit_inline_thumb_new(max_num_labels); + #if MICROPY_EMIT_INLINE_ASM + } else if (s->emit_options == MP_EMIT_OPT_ASM) { + // inline assembly + if (comp->emit_inline_asm == NULL) { + comp->emit_inline_asm = ASM_EMITTER(new)(max_num_labels); } comp->emit = NULL; - comp->emit_inline_asm = emit_inline_thumb; - comp->emit_inline_asm_method_table = &emit_inline_thumb_method_table; + comp->emit_inline_asm_method_table = &ASM_EMITTER(method_table); compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE); if (comp->compile_error == MP_OBJ_NULL) { compile_scope_inline_asm(comp, s, MP_PASS_EMIT); } -#endif + #endif } else { @@ -3463,11 +3469,11 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f NATIVE_EMITTER(free)(emit_native); } #endif -#if MICROPY_EMIT_INLINE_THUMB - if (emit_inline_thumb != NULL) { - emit_inline_thumb_free(emit_inline_thumb); + #if MICROPY_EMIT_INLINE_ASM + if (comp->emit_inline_asm != NULL) { + ASM_EMITTER(free)(comp->emit_inline_asm); } -#endif + #endif // free the parse tree mp_parse_tree_clear(parse_tree); diff --git a/py/compile.h b/py/compile.h index 3cca4cb30..45a98588d 100644 --- a/py/compile.h +++ b/py/compile.h @@ -36,7 +36,7 @@ enum { MP_EMIT_OPT_BYTECODE, MP_EMIT_OPT_NATIVE_PYTHON, MP_EMIT_OPT_VIPER, - MP_EMIT_OPT_ASM_THUMB, + MP_EMIT_OPT_ASM, }; // the compiler will raise an exception if an error occurred diff --git a/py/emitglue.c b/py/emitglue.c index abcf50cdd..795c738d3 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -82,7 +82,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, mp_uint_t #endif } -#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB +#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM 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) { assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); rc->kind = kind; @@ -138,7 +138,7 @@ 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_viper(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig); break; #endif - #if MICROPY_EMIT_INLINE_THUMB + #if MICROPY_EMIT_INLINE_ASM case MP_CODE_NATIVE_ASM: fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->data.u_native.fun_data, rc->data.u_native.type_sig); break; diff --git a/py/mpconfig.h b/py/mpconfig.h index 15f770573..5234cc7f9 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -296,6 +296,9 @@ // Convenience definition for whether any native emitter is enabled #define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA) +// Convenience definition for whether any inline assembler emitter is enabled +#define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB) + /*****************************************************************************/ /* Compiler configuration */ diff --git a/py/nativeglue.c b/py/nativeglue.c index bc2f4ff5e..5f2164ee0 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_THUMB +#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_ASM // convert a native value to a Micro Python object based on type mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) { diff --git a/py/objfun.c b/py/objfun.c index 6b8fe6d38..207e68a77 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -471,7 +471,7 @@ mp_obj_t mp_obj_new_fun_viper(mp_uint_t n_args, void *fun_data, mp_uint_t type_s /******************************************************************************/ /* inline assembler functions */ -#if MICROPY_EMIT_INLINE_THUMB +#if MICROPY_EMIT_INLINE_ASM typedef struct _mp_obj_fun_asm_t { mp_obj_base_t base; @@ -582,4 +582,4 @@ mp_obj_t mp_obj_new_fun_asm(mp_uint_t n_args, void *fun_data, mp_uint_t type_sig return o; } -#endif // MICROPY_EMIT_INLINE_THUMB +#endif // MICROPY_EMIT_INLINE_ASM -- cgit v1.2.3 From f76b1bfa9f59fcfa03837c6934ad51d2db3ff4a3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 17:03:33 +1100 Subject: py: Add inline Xtensa assembler. This patch adds the MICROPY_EMIT_INLINE_XTENSA option, which, when enabled, allows the @micropython.asm_xtensa decorator to be used. The following opcodes are currently supported (ax is a register, a0-a15): ret_n() callx0(ax) j(label) jx(ax) beqz(ax, label) bnez(ax, label) mov(ax, ay) movi(ax, imm) # imm can be full 32-bit, uses l32r if needed and_(ax, ay, az) or_(ax, ay, az) xor(ax, ay, az) add(ax, ay, az) sub(ax, ay, az) mull(ax, ay, az) l8ui(ax, ay, imm) l16ui(ax, ay, imm) l32i(ax, ay, imm) s8i(ax, ay, imm) s16i(ax, ay, imm) s32i(ax, ay, imm) l16si(ax, ay, imm) addi(ax, ay, imm) ball(ax, ay, label) bany(ax, ay, label) bbc(ax, ay, label) bbs(ax, ay, label) beq(ax, ay, label) bge(ax, ay, label) bgeu(ax, ay, label) blt(ax, ay, label) bnall(ax, ay, label) bne(ax, ay, label) bnone(ax, ay, label) Upon entry to the assembly function the registers a0, a12, a13, a14 are pushed to the stack and the stack pointer (a1) decreased by 16. Upon exit, these registers and the stack pointer are restored, and ret.n is executed to return to the caller (caller address is in a0). Note that the ABI for the Xtensa emitters is non-windowing. --- py/asmxtensa.c | 4 +- py/compile.c | 3 + py/emit.h | 4 + py/emitinlinextensa.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++ py/mpconfig.h | 7 +- py/py.mk | 1 + 6 files changed, 380 insertions(+), 3 deletions(-) create mode 100644 py/emitinlinextensa.c (limited to 'py') diff --git a/py/asmxtensa.c b/py/asmxtensa.c index 63aad02c0..00df432ce 100644 --- a/py/asmxtensa.c +++ b/py/asmxtensa.c @@ -30,7 +30,7 @@ #include "py/mpconfig.h" // wrapper around everything in this file -#if MICROPY_EMIT_XTENSA +#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA #include "py/asmxtensa.h" @@ -167,4 +167,4 @@ void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_nu asm_xtensa_op_addi(as, reg_dest, reg_dest, (4 + local_num) * WORD_SIZE); } -#endif // MICROPY_EMIT_XTENSA +#endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA diff --git a/py/compile.c b/py/compile.c index b85e659e7..6c9b6abfe 100644 --- a/py/compile.c +++ b/py/compile.c @@ -91,6 +91,9 @@ typedef enum { #if MICROPY_EMIT_INLINE_THUMB #define ASM_DECORATOR_QSTR MP_QSTR_asm_thumb #define ASM_EMITTER(f) emit_inline_thumb_##f +#elif MICROPY_EMIT_INLINE_XTENSA +#define ASM_DECORATOR_QSTR MP_QSTR_asm_xtensa +#define ASM_EMITTER(f) emit_inline_xtensa_##f #else #error "unknown asm emitter" #endif diff --git a/py/emit.h b/py/emit.h index 122df8e7d..6080b83c4 100644 --- a/py/emit.h +++ b/py/emit.h @@ -272,9 +272,13 @@ typedef struct _emit_inline_asm_method_table_t { } emit_inline_asm_method_table_t; extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table; +extern const emit_inline_asm_method_table_t emit_inline_xtensa_method_table; emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels); +emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels); + void emit_inline_thumb_free(emit_inline_asm_t *emit); +void emit_inline_xtensa_free(emit_inline_asm_t *emit); #if MICROPY_WARNINGS void mp_emitter_warning(pass_kind_t pass, const char *msg); diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c new file mode 100644 index 000000000..bd1a7a3ec --- /dev/null +++ b/py/emitinlinextensa.c @@ -0,0 +1,364 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 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 +#include +#include +#include +#include + +#include "py/emit.h" +#include "py/asmxtensa.h" + +#if MICROPY_EMIT_INLINE_XTENSA + +struct _emit_inline_asm_t { + uint16_t pass; + scope_t *scope; + mp_obj_t *error_slot; + mp_uint_t max_num_labels; + qstr *label_lookup; + asm_xtensa_t *as; +}; + +STATIC void emit_inline_xtensa_error_msg(emit_inline_asm_t *emit, const char *msg) { + *emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); +} + +STATIC void emit_inline_xtensa_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) { + *emit->error_slot = exc; +} + +emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels) { + emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); + emit->max_num_labels = max_num_labels; + emit->label_lookup = m_new(qstr, max_num_labels); + emit->as = m_new0(asm_xtensa_t, 1); + mp_asm_base_init(&emit->as->base, max_num_labels); + return emit; +} + +void emit_inline_xtensa_free(emit_inline_asm_t *emit) { + m_del(qstr, emit->label_lookup, emit->max_num_labels); + mp_asm_base_deinit(&emit->as->base, false); + m_del_obj(asm_xtensa_t, emit->as); + m_del_obj(emit_inline_asm_t, emit); +} + +STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope, mp_obj_t *error_slot) { + emit->pass = pass; + emit->scope = scope; + emit->error_slot = error_slot; + if (emit->pass == MP_PASS_CODE_SIZE) { + memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); + } + mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); + asm_xtensa_entry(emit->as, 0); +} + +STATIC void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { + asm_xtensa_exit(emit->as); + asm_xtensa_end_pass(emit->as); + + if (emit->pass == MP_PASS_EMIT) { + void *f = mp_asm_base_get_code(&emit->as->base); + mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, + mp_asm_base_get_code_size(&emit->as->base), NULL, emit->scope->num_pos_args, 0, type_sig); + } +} + +STATIC mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) { + if (n_params > 4) { + emit_inline_xtensa_error_msg(emit, "can only have up to 4 parameters to Xtensa assembly"); + return 0; + } + for (mp_uint_t i = 0; i < n_params; i++) { + if (!MP_PARSE_NODE_IS_ID(pn_params[i])) { + emit_inline_xtensa_error_msg(emit, "parameters must be registers in sequence a2 to a5"); + return 0; + } + const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); + if (!(strlen(p) == 2 && p[0] == 'a' && p[1] == '2' + i)) { + emit_inline_xtensa_error_msg(emit, "parameters must be registers in sequence a2 to a5"); + return 0; + } + } + return n_params; +} + +STATIC bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) { + assert(label_num < emit->max_num_labels); + if (emit->pass == MP_PASS_CODE_SIZE) { + // check for duplicate label on first pass + for (uint i = 0; i < emit->max_num_labels; i++) { + if (emit->label_lookup[i] == label_id) { + return false; + } + } + } + emit->label_lookup[label_num] = label_id; + mp_asm_base_label_assign(&emit->as->base, label_num); + return true; +} + +STATIC void emit_inline_xtensa_align(emit_inline_asm_t *emit, mp_uint_t align) { + mp_asm_base_align(&emit->as->base, align); +} + +STATIC void emit_inline_xtensa_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) { + mp_asm_base_data(&emit->as->base, bytesize, val); +} + +typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; +STATIC const reg_name_t reg_name_table[] = { + {0, "a0\0"}, + {1, "a1\0"}, + {2, "a2\0"}, + {3, "a3\0"}, + {4, "a4\0"}, + {5, "a5\0"}, + {6, "a6\0"}, + {7, "a7\0"}, + {8, "a8\0"}, + {9, "a9\0"}, + {10, "a10"}, + {11, "a11"}, + {12, "a12"}, + {13, "a13"}, + {14, "a14"}, + {15, "a15"}, +}; + +// return empty string in case of error, so we can attempt to parse the string +// without a special check if it was in fact a string +STATIC const char *get_arg_str(mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_ID(pn)) { + qstr qst = MP_PARSE_NODE_LEAF_ARG(pn); + return qstr_str(qst); + } else { + return ""; + } +} + +STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + const char *reg_str = get_arg_str(pn); + for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) { + const reg_name_t *r = ®_name_table[i]; + if (reg_str[0] == r->name[0] + && reg_str[1] == r->name[1] + && reg_str[2] == r->name[2] + && (reg_str[2] == '\0' || reg_str[3] == '\0')) { + return r->reg; + } + } + emit_inline_xtensa_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + "'%s' expects a register", op)); + return 0; +} + +STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int min, int max) { + mp_obj_t o; + if (!mp_parse_node_get_int_maybe(pn, &o)) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects an integer", op)); + return 0; + } + uint32_t i = mp_obj_get_int_truncated(o); + if (min != max && ((int)i < min || (int)i > max)) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' integer %d is not within range %d..%d", op, i, min, max)); + return 0; + } + return i; +} + +STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) { + if (!MP_PARSE_NODE_IS_ID(pn)) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "'%s' expects a label", op)); + return 0; + } + qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn); + for (uint i = 0; i < emit->max_num_labels; i++) { + if (emit->label_lookup[i] == label_qstr) { + return i; + } + } + // only need to have the labels on the last pass + if (emit->pass == MP_PASS_EMIT) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "label '%q' not defined", label_qstr)); + } + return 0; +} + +#define RRR (0) +#define RRI8 (1) +#define RRI8_B (2) + +typedef struct _opcode_table_3arg_t { + uint16_t name; // actually a qstr, which should fit in 16 bits + uint8_t type; + uint8_t a0 : 4; + uint8_t a1 : 4; +} opcode_table_3arg_t; + +STATIC const opcode_table_3arg_t opcode_table_3arg[] = { + // arithmetic opcodes: reg, reg, reg + {MP_QSTR_and_, RRR, 0, 1}, + {MP_QSTR_or_, RRR, 0, 2}, + {MP_QSTR_xor, RRR, 0, 3}, + {MP_QSTR_add, RRR, 0, 8}, + {MP_QSTR_sub, RRR, 0, 12}, + {MP_QSTR_mull, RRR, 2, 8}, + + // load/store/addi opcodes: reg, reg, imm + // upper nibble of type encodes the range of the immediate arg + {MP_QSTR_l8ui, RRI8 | 0x10, 2, 0}, + {MP_QSTR_l16ui, RRI8 | 0x30, 2, 1}, + {MP_QSTR_l32i, RRI8 | 0x50, 2, 2}, + {MP_QSTR_s8i, RRI8 | 0x10, 2, 4}, + {MP_QSTR_s16i, RRI8 | 0x30, 2, 5}, + {MP_QSTR_s32i, RRI8 | 0x50, 2, 6}, + {MP_QSTR_l16si, RRI8 | 0x30, 2, 9}, + {MP_QSTR_addi, RRI8 | 0x00, 2, 12}, + + // branch opcodes: reg, reg, label + {MP_QSTR_ball, RRI8_B, ASM_XTENSA_CC_ALL, 0}, + {MP_QSTR_bany, RRI8_B, ASM_XTENSA_CC_ANY, 0}, + {MP_QSTR_bbc, RRI8_B, ASM_XTENSA_CC_BC, 0}, + {MP_QSTR_bbs, RRI8_B, ASM_XTENSA_CC_BS, 0}, + {MP_QSTR_beq, RRI8_B, ASM_XTENSA_CC_EQ, 0}, + {MP_QSTR_bge, RRI8_B, ASM_XTENSA_CC_GE, 0}, + {MP_QSTR_bgeu, RRI8_B, ASM_XTENSA_CC_GEU, 0}, + {MP_QSTR_blt, RRI8_B, ASM_XTENSA_CC_LT, 0}, + {MP_QSTR_bnall, RRI8_B, ASM_XTENSA_CC_NALL, 0}, + {MP_QSTR_bne, RRI8_B, ASM_XTENSA_CC_NE, 0}, + {MP_QSTR_bnone, RRI8_B, ASM_XTENSA_CC_NONE, 0}, +}; + +STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) { + size_t op_len; + const char *op_str = (const char*)qstr_data(op, &op_len); + + if (n_args == 0) { + if (op == MP_QSTR_ret_n) { + asm_xtensa_op_ret_n(emit->as); + } else { + goto unknown_op; + } + + } else if (n_args == 1) { + if (op == MP_QSTR_callx0) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_callx0(emit->as, r0); + } else if (op == MP_QSTR_j) { + int label = get_arg_label(emit, op_str, pn_args[0]); + asm_xtensa_j_label(emit->as, label); + } else if (op == MP_QSTR_jx) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_jx(emit->as, r0); + } else { + goto unknown_op; + } + + } else if (n_args == 2) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + if (op == MP_QSTR_beqz) { + int label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_bccz_reg_label(emit->as, ASM_XTENSA_CCZ_EQ, r0, label); + } else if (op == MP_QSTR_bnez) { + int label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_bccz_reg_label(emit->as, ASM_XTENSA_CCZ_NE, r0, label); + } else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { + // we emit mov.n for both "mov" and "mov_n" opcodes + uint r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op_mov_n(emit->as, r0, r1); + } else if (op == MP_QSTR_movi) { + // for convenience we emit l32r if the integer doesn't fit in movi + uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0); + asm_xtensa_mov_reg_i32(emit->as, r0, imm); + } else { + goto unknown_op; + } + + } else if (n_args == 3) { + // search table for 3 arg instructions + for (uint i = 0; i < MP_ARRAY_SIZE(opcode_table_3arg); i++) { + const opcode_table_3arg_t *o = &opcode_table_3arg[i]; + if (op == o->name) { + uint r0 = get_arg_reg(emit, op_str, pn_args[0]); + uint r1 = get_arg_reg(emit, op_str, pn_args[1]); + if (o->type == RRR) { + uint r2 = get_arg_reg(emit, op_str, pn_args[2]); + asm_xtensa_op24(emit->as, ASM_XTENSA_ENCODE_RRR(0, o->a0, o->a1, r0, r1, r2)); + } else if (o->type == RRI8_B) { + int label = get_arg_label(emit, op_str, pn_args[2]); + asm_xtensa_bcc_reg_reg_label(emit->as, o->a0, r0, r1, label); + } else { + int shift, min, max; + if ((o->type & 0xf0) == 0) { + shift = 0; + min = -128; + max = 127; + } else { + shift = (o->type & 0xf0) >> 5; + min = 0; + max = 0xff << shift; + } + uint32_t imm = get_arg_i(emit, op_str, pn_args[2], min, max); + asm_xtensa_op24(emit->as, ASM_XTENSA_ENCODE_RRI8(o->a0, o->a1, r1, r0, (imm >> shift) & 0xff)); + } + return; + } + } + goto unknown_op; + + } else { + goto unknown_op; + } + + return; + +unknown_op: + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, "unsupported Xtensa instruction '%s' with %d arguments", op_str, n_args)); + return; + + /* +branch_not_in_range: + emit_inline_xtensa_error_msg(emit, "branch not in range"); + return; + */ +} + +const emit_inline_asm_method_table_t emit_inline_xtensa_method_table = { + emit_inline_xtensa_start_pass, + emit_inline_xtensa_end_pass, + emit_inline_xtensa_count_params, + emit_inline_xtensa_label, + emit_inline_xtensa_align, + emit_inline_xtensa_data, + emit_inline_xtensa_op, +}; + +#endif // MICROPY_EMIT_INLINE_XTENSA diff --git a/py/mpconfig.h b/py/mpconfig.h index 5234cc7f9..3612e6bcb 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -293,11 +293,16 @@ #define MICROPY_EMIT_XTENSA (0) #endif +// Whether to enable the Xtensa inline assembler +#ifndef MICROPY_EMIT_INLINE_XTENSA +#define MICROPY_EMIT_INLINE_XTENSA (0) +#endif + // Convenience definition for whether any native emitter is enabled #define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA) // Convenience definition for whether any inline assembler emitter is enabled -#define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB) +#define MICROPY_EMIT_INLINE_ASM (MICROPY_EMIT_INLINE_THUMB || MICROPY_EMIT_INLINE_XTENSA) /*****************************************************************************/ /* Compiler configuration */ diff --git a/py/py.mk b/py/py.mk index 9d2a188f1..79436aa9e 100644 --- a/py/py.mk +++ b/py/py.mk @@ -132,6 +132,7 @@ PY_O_BASENAME = \ emitnarm.o \ asmxtensa.o \ emitnxtensa.o \ + emitinlinextensa.o \ formatfloat.o \ parsenumbase.o \ parsenum.o \ -- cgit v1.2.3 From a7fd786a1f8a0bd13ec97d9742f8705b90cd1c46 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 20:35:21 +1100 Subject: py/emitinline: Embed entire asm struct instead of a pointer to it. This reduces fragmentation, and memory use by 1 word. But more importantly it means the emit_inline_asm_t struct now "derives" from mp_asm_base. --- py/emitinlinethumb.c | 91 +++++++++++++++++++++++++-------------------------- py/emitinlinextensa.c | 49 ++++++++++++++------------- 2 files changed, 69 insertions(+), 71 deletions(-) (limited to 'py') diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 760ec8cc0..1373e173d 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -43,12 +43,12 @@ typedef enum { } pn_kind_t; struct _emit_inline_asm_t { + asm_thumb_t as; uint16_t pass; scope_t *scope; mp_obj_t *error_slot; mp_uint_t max_num_labels; qstr *label_lookup; - asm_thumb_t *as; }; STATIC void emit_inline_thumb_error_msg(emit_inline_asm_t *emit, const char *msg) { @@ -61,17 +61,16 @@ STATIC void emit_inline_thumb_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) { emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) { emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); + memset(&emit->as, 0, sizeof(emit->as)); + mp_asm_base_init(&emit->as.base, max_num_labels); emit->max_num_labels = max_num_labels; emit->label_lookup = m_new(qstr, max_num_labels); - emit->as = m_new0(asm_thumb_t, 1); - mp_asm_base_init(&emit->as->base, max_num_labels); return emit; } void emit_inline_thumb_free(emit_inline_asm_t *emit) { m_del(qstr, emit->label_lookup, emit->max_num_labels); - mp_asm_base_deinit(&emit->as->base, false); - m_del_obj(asm_thumb_t, emit->as); + mp_asm_base_deinit(&emit->as.base, false); m_del_obj(emit_inline_asm_t, emit); } @@ -82,18 +81,18 @@ STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pa if (emit->pass == MP_PASS_CODE_SIZE) { memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); } - mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); - asm_thumb_entry(emit->as, 0); + mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); + asm_thumb_entry(&emit->as, 0); } STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { - asm_thumb_exit(emit->as); - asm_thumb_end_pass(emit->as); + asm_thumb_exit(&emit->as); + asm_thumb_end_pass(&emit->as); if (emit->pass == MP_PASS_EMIT) { - void *f = mp_asm_base_get_code(&emit->as->base); + void *f = mp_asm_base_get_code(&emit->as.base); mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, - mp_asm_base_get_code_size(&emit->as->base), NULL, emit->scope->num_pos_args, 0, type_sig); + mp_asm_base_get_code_size(&emit->as.base), NULL, emit->scope->num_pos_args, 0, type_sig); } } @@ -127,16 +126,16 @@ STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num } } emit->label_lookup[label_num] = label_id; - mp_asm_base_label_assign(&emit->as->base, label_num); + mp_asm_base_label_assign(&emit->as.base, label_num); return true; } STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) { - mp_asm_base_align(&emit->as->base, align); + mp_asm_base_align(&emit->as.base, align); } STATIC void emit_inline_thumb_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) { - mp_asm_base_data(&emit->as->base, bytesize, val); + mp_asm_base_data(&emit->as.base, bytesize, val); } typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; @@ -445,7 +444,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a op_vfp_twoargs:; mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]); mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]); - asm_thumb_op32(emit->as, + asm_thumb_op32(&emit->as, op_code_hi | ((vd & 1) << 6), op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1); } else if (op == MP_QSTR_vsqrt) { @@ -472,7 +471,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a const char *reg_str1 = get_arg_str(pn_args[1]); if (strcmp(reg_str1, "FPSCR") == 0) { // FP status to ARM reg - asm_thumb_op32(emit->as, 0xeef1, 0x0a10 | (reg_dest << 12)); + asm_thumb_op32(&emit->as, 0xeef1, 0x0a10 | (reg_dest << 12)); } else { goto unknown_op; } @@ -488,7 +487,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a vm = get_arg_vfpreg(emit, op_str, pn_args[0]); r_arm = get_arg_reg(emit, op_str, pn_args[1], 15); } - asm_thumb_op32(emit->as, + asm_thumb_op32(&emit->as, op_code_hi | ((vm & 0x1e) >> 1), 0x0a10 | (r_arm << 12) | ((vm & 1) << 7)); } else if (op == MP_QSTR_vldr) { @@ -500,7 +499,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7); mp_uint_t i8; i8 = get_arg_i(emit, op_str, pn_offset, 0x3fc) >> 2; - asm_thumb_op32(emit->as, + asm_thumb_op32(&emit->as, op_code_hi | rlo_base | ((vd & 1) << 6), 0x0a00 | ((vd & 0x1e) << 11) | i8); } @@ -519,7 +518,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]); mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]); mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]); - asm_thumb_op32(emit->as, + asm_thumb_op32(&emit->as, op_code_hi | ((vd & 1) << 6) | (vn >> 1), op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7)); return; @@ -533,9 +532,9 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a #endif if (n_args == 0) { if (op == MP_QSTR_nop) { - asm_thumb_op16(emit->as, ASM_THUMB_OP_NOP); + asm_thumb_op16(&emit->as, ASM_THUMB_OP_NOP); } else if (op == MP_QSTR_wfi) { - asm_thumb_op16(emit->as, ASM_THUMB_OP_WFI); + asm_thumb_op16(&emit->as, ASM_THUMB_OP_WFI); } else { goto unknown_op; } @@ -543,17 +542,17 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a } else if (n_args == 1) { if (op == MP_QSTR_b) { int label_num = get_arg_label(emit, op_str, pn_args[0]); - if (!asm_thumb_b_n_label(emit->as, label_num)) { + if (!asm_thumb_b_n_label(&emit->as, label_num)) { goto branch_not_in_range; } } else if (op == MP_QSTR_bl) { int label_num = get_arg_label(emit, op_str, pn_args[0]); - if (!asm_thumb_bl_label(emit->as, label_num)) { + if (!asm_thumb_bl_label(&emit->as, label_num)) { goto branch_not_in_range; } } else if (op == MP_QSTR_bx) { mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15); - asm_thumb_op16(emit->as, 0x4700 | (r << 3)); + asm_thumb_op16(&emit->as, 0x4700 | (r << 3)); } else if (op_str[0] == 'b' && (op_len == 3 || (op_len == 5 && op_str[3] == '_' && (op_str[4] == 'n' || (ARMV7M && op_str[4] == 'w'))))) { @@ -567,7 +566,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a goto unknown_op; } int label_num = get_arg_label(emit, op_str, pn_args[0]); - if (!asm_thumb_bcc_nw_label(emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) { + if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) { goto branch_not_in_range; } } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') { @@ -602,32 +601,32 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a goto unknown_op; } } - asm_thumb_it_cc(emit->as, cc, it_mask); + asm_thumb_it_cc(&emit->as, cc, it_mask); } else if (op == MP_QSTR_cpsid) { // TODO check pn_args[0] == i - asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSID_I); + asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSID_I); } else if (op == MP_QSTR_cpsie) { // TODO check pn_args[0] == i - asm_thumb_op16(emit->as, ASM_THUMB_OP_CPSIE_I); + asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSIE_I); } else if (op == MP_QSTR_push) { mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]); if ((reglist & 0xff00) == 0) { - asm_thumb_op16(emit->as, 0xb400 | reglist); + asm_thumb_op16(&emit->as, 0xb400 | reglist); } else { if (!ARMV7M) { goto unknown_op; } - asm_thumb_op32(emit->as, 0xe92d, reglist); + asm_thumb_op32(&emit->as, 0xe92d, reglist); } } else if (op == MP_QSTR_pop) { mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]); if ((reglist & 0xff00) == 0) { - asm_thumb_op16(emit->as, 0xbc00 | reglist); + asm_thumb_op16(&emit->as, 0xbc00 | reglist); } else { if (!ARMV7M) { goto unknown_op; } - asm_thumb_op32(emit->as, 0xe8bd, reglist); + asm_thumb_op32(&emit->as, 0xe8bd, reglist); } } else { goto unknown_op; @@ -640,7 +639,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a if (op == MP_QSTR_mov) { mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15); - asm_thumb_mov_reg_reg(emit->as, reg_dest, reg_src); + asm_thumb_mov_reg_reg(&emit->as, reg_dest, reg_src); } else if (ARMV7M && op == MP_QSTR_clz) { op_code_hi = 0xfab0; op_code = 0xf080; @@ -648,7 +647,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a op_clz_rbit: rd = get_arg_reg(emit, op_str, pn_args[0], 15); rm = get_arg_reg(emit, op_str, pn_args[1], 15); - asm_thumb_op32(emit->as, op_code_hi | rm, op_code | (rd << 8) | rm); + asm_thumb_op32(&emit->as, op_code_hi | rm, op_code | (rd << 8) | rm); } else if (ARMV7M && op == MP_QSTR_rbit) { op_code_hi = 0xfa90; op_code = 0xf0a0; @@ -656,7 +655,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a } else if (ARMV7M && op == MP_QSTR_mrs){ mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 12); mp_uint_t reg_src = get_arg_special_reg(emit, op_str, pn_args[1]); - asm_thumb_op32(emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src); + asm_thumb_op32(&emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src); } else { if (op == MP_QSTR_and_) { op_code = ASM_THUMB_FORMAT_4_AND; @@ -664,7 +663,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a op_format_4: reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7); reg_src = get_arg_reg(emit, op_str, pn_args[1], 7); - asm_thumb_format_4(emit->as, op_code, reg_dest, reg_src); + asm_thumb_format_4(&emit->as, op_code, reg_dest, reg_src); return; } // search table for ALU ops @@ -685,7 +684,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a op_format_3: rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7); i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff); - asm_thumb_format_3(emit->as, op_code, rlo_dest, i8_src); + asm_thumb_format_3(&emit->as, op_code, rlo_dest, i8_src); } else if (op == MP_QSTR_cmp) { op_code = ASM_THUMB_FORMAT_3_CMP; goto op_format_3; @@ -701,7 +700,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a op_movw_movt: reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff); - asm_thumb_mov_reg_i16(emit->as, op_code, reg_dest, i_src); + asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src); } else if (ARMV7M && op == MP_QSTR_movt) { op_code = ASM_THUMB_OP_MOVT; goto op_movw_movt; @@ -709,15 +708,15 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a // this is a convenience instruction mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff); - asm_thumb_mov_reg_i16(emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff); - asm_thumb_mov_reg_i16(emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff); + asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff); + asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff); } else if (ARMV7M && op == MP_QSTR_ldrex) { mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15); mp_parse_node_t pn_base, pn_offset; if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) { mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15); mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2; - asm_thumb_op32(emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8); + asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8); } } else { // search table for ldr/str instructions @@ -736,7 +735,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a } else { i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2; } - asm_thumb_format_9_10(emit->as, op_code, rlo_dest, rlo_base, i5); + asm_thumb_format_9_10(&emit->as, op_code, rlo_dest, rlo_base, i5); return; } break; @@ -755,7 +754,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7); rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7); i5 = get_arg_i(emit, op_str, pn_args[2], 0x1f); - asm_thumb_format_1(emit->as, op_code, rlo_dest, rlo_src, i5); + asm_thumb_format_1(&emit->as, op_code, rlo_dest, rlo_src, i5); } else if (op == MP_QSTR_lsr) { op_code = ASM_THUMB_FORMAT_1_LSR; goto op_format_1; @@ -776,7 +775,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND; src_b = get_arg_i(emit, op_str, pn_args[2], 0x7); } - asm_thumb_format_2(emit->as, op_code, rlo_dest, rlo_src, src_b); + asm_thumb_format_2(&emit->as, op_code, rlo_dest, rlo_src, src_b); } else if (ARMV7M && op == MP_QSTR_sdiv) { op_code = 0xfb90; // sdiv high part mp_uint_t rd, rn, rm; @@ -784,7 +783,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a rd = get_arg_reg(emit, op_str, pn_args[0], 15); rn = get_arg_reg(emit, op_str, pn_args[1], 15); rm = get_arg_reg(emit, op_str, pn_args[2], 15); - asm_thumb_op32(emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm); + asm_thumb_op32(&emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm); } else if (ARMV7M && op == MP_QSTR_udiv) { op_code = 0xfbb0; // udiv high part goto op_sdiv_udiv; @@ -798,7 +797,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) { mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15); mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2; - asm_thumb_op32(emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8); + asm_thumb_op32(&emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8); } } else { goto unknown_op; diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index bd1a7a3ec..284624e45 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -36,12 +36,12 @@ #if MICROPY_EMIT_INLINE_XTENSA struct _emit_inline_asm_t { + asm_xtensa_t as; uint16_t pass; scope_t *scope; mp_obj_t *error_slot; mp_uint_t max_num_labels; qstr *label_lookup; - asm_xtensa_t *as; }; STATIC void emit_inline_xtensa_error_msg(emit_inline_asm_t *emit, const char *msg) { @@ -54,17 +54,16 @@ STATIC void emit_inline_xtensa_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels) { emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t); + memset(&emit->as, 0, sizeof(emit->as)); + mp_asm_base_init(&emit->as.base, max_num_labels); emit->max_num_labels = max_num_labels; emit->label_lookup = m_new(qstr, max_num_labels); - emit->as = m_new0(asm_xtensa_t, 1); - mp_asm_base_init(&emit->as->base, max_num_labels); return emit; } void emit_inline_xtensa_free(emit_inline_asm_t *emit) { m_del(qstr, emit->label_lookup, emit->max_num_labels); - mp_asm_base_deinit(&emit->as->base, false); - m_del_obj(asm_xtensa_t, emit->as); + mp_asm_base_deinit(&emit->as.base, false); m_del_obj(emit_inline_asm_t, emit); } @@ -75,18 +74,18 @@ STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t p if (emit->pass == MP_PASS_CODE_SIZE) { memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); } - mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); - asm_xtensa_entry(emit->as, 0); + mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); + asm_xtensa_entry(&emit->as, 0); } STATIC void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { - asm_xtensa_exit(emit->as); - asm_xtensa_end_pass(emit->as); + asm_xtensa_exit(&emit->as); + asm_xtensa_end_pass(&emit->as); if (emit->pass == MP_PASS_EMIT) { - void *f = mp_asm_base_get_code(&emit->as->base); + void *f = mp_asm_base_get_code(&emit->as.base); mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, - mp_asm_base_get_code_size(&emit->as->base), NULL, emit->scope->num_pos_args, 0, type_sig); + mp_asm_base_get_code_size(&emit->as.base), NULL, emit->scope->num_pos_args, 0, type_sig); } } @@ -120,16 +119,16 @@ STATIC bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_nu } } emit->label_lookup[label_num] = label_id; - mp_asm_base_label_assign(&emit->as->base, label_num); + mp_asm_base_label_assign(&emit->as.base, label_num); return true; } STATIC void emit_inline_xtensa_align(emit_inline_asm_t *emit, mp_uint_t align) { - mp_asm_base_align(&emit->as->base, align); + mp_asm_base_align(&emit->as.base, align); } STATIC void emit_inline_xtensa_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) { - mp_asm_base_data(&emit->as->base, bytesize, val); + mp_asm_base_data(&emit->as.base, bytesize, val); } typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; @@ -263,7 +262,7 @@ STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ if (n_args == 0) { if (op == MP_QSTR_ret_n) { - asm_xtensa_op_ret_n(emit->as); + asm_xtensa_op_ret_n(&emit->as); } else { goto unknown_op; } @@ -271,13 +270,13 @@ STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ } else if (n_args == 1) { if (op == MP_QSTR_callx0) { uint r0 = get_arg_reg(emit, op_str, pn_args[0]); - asm_xtensa_op_callx0(emit->as, r0); + asm_xtensa_op_callx0(&emit->as, r0); } else if (op == MP_QSTR_j) { int label = get_arg_label(emit, op_str, pn_args[0]); - asm_xtensa_j_label(emit->as, label); + asm_xtensa_j_label(&emit->as, label); } else if (op == MP_QSTR_jx) { uint r0 = get_arg_reg(emit, op_str, pn_args[0]); - asm_xtensa_op_jx(emit->as, r0); + asm_xtensa_op_jx(&emit->as, r0); } else { goto unknown_op; } @@ -286,18 +285,18 @@ STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ uint r0 = get_arg_reg(emit, op_str, pn_args[0]); if (op == MP_QSTR_beqz) { int label = get_arg_label(emit, op_str, pn_args[1]); - asm_xtensa_bccz_reg_label(emit->as, ASM_XTENSA_CCZ_EQ, r0, label); + asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_EQ, r0, label); } else if (op == MP_QSTR_bnez) { int label = get_arg_label(emit, op_str, pn_args[1]); - asm_xtensa_bccz_reg_label(emit->as, ASM_XTENSA_CCZ_NE, r0, label); + asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_NE, r0, label); } else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { // we emit mov.n for both "mov" and "mov_n" opcodes uint r1 = get_arg_reg(emit, op_str, pn_args[1]); - asm_xtensa_op_mov_n(emit->as, r0, r1); + asm_xtensa_op_mov_n(&emit->as, r0, r1); } else if (op == MP_QSTR_movi) { // for convenience we emit l32r if the integer doesn't fit in movi uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0); - asm_xtensa_mov_reg_i32(emit->as, r0, imm); + asm_xtensa_mov_reg_i32(&emit->as, r0, imm); } else { goto unknown_op; } @@ -311,10 +310,10 @@ STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ uint r1 = get_arg_reg(emit, op_str, pn_args[1]); if (o->type == RRR) { uint r2 = get_arg_reg(emit, op_str, pn_args[2]); - asm_xtensa_op24(emit->as, ASM_XTENSA_ENCODE_RRR(0, o->a0, o->a1, r0, r1, r2)); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, o->a0, o->a1, r0, r1, r2)); } else if (o->type == RRI8_B) { int label = get_arg_label(emit, op_str, pn_args[2]); - asm_xtensa_bcc_reg_reg_label(emit->as, o->a0, r0, r1, label); + asm_xtensa_bcc_reg_reg_label(&emit->as, o->a0, r0, r1, label); } else { int shift, min, max; if ((o->type & 0xf0) == 0) { @@ -327,7 +326,7 @@ STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ max = 0xff << shift; } uint32_t imm = get_arg_i(emit, op_str, pn_args[2], min, max); - asm_xtensa_op24(emit->as, ASM_XTENSA_ENCODE_RRI8(o->a0, o->a1, r1, r0, (imm >> shift) & 0xff)); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRI8(o->a0, o->a1, r1, r0, (imm >> shift) & 0xff)); } return; } -- cgit v1.2.3 From dd53b12193dca4800ab207170fcc883142dd0f22 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 20:54:54 +1100 Subject: py/emitinline: Move inline-asm align and data methods to compiler. These are generic methods that don't depend on the architecture and so can be handled directly by the compiler. --- py/compile.c | 7 +++++-- py/emit.h | 2 -- py/emitinlinethumb.c | 10 ---------- py/emitinlinextensa.c | 10 ---------- 4 files changed, 5 insertions(+), 24 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index 6c9b6abfe..f98b783b5 100644 --- a/py/compile.c +++ b/py/compile.c @@ -34,6 +34,7 @@ #include "py/emit.h" #include "py/compile.h" #include "py/runtime.h" +#include "py/asmbase.h" #if MICROPY_ENABLE_COMPILER @@ -3224,7 +3225,8 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind return; } if (pass > MP_PASS_SCOPE) { - EMIT_INLINE_ASM_ARG(align, MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0])); + mp_asm_base_align((mp_asm_base_t*)comp->emit_inline_asm, + MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[0])); } } else if (op == MP_QSTR_data) { if (!(n_args >= 2 && MP_PARSE_NODE_IS_SMALL_INT(pn_arg[0]))) { @@ -3238,7 +3240,8 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind compile_syntax_error(comp, nodes[i], "'data' requires integer arguments"); return; } - EMIT_INLINE_ASM_ARG(data, bytesize, MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[j])); + mp_asm_base_data((mp_asm_base_t*)comp->emit_inline_asm, + bytesize, MP_PARSE_NODE_LEAF_SMALL_INT(pn_arg[j])); } } } else { diff --git a/py/emit.h b/py/emit.h index 6080b83c4..41cb2162d 100644 --- a/py/emit.h +++ b/py/emit.h @@ -266,8 +266,6 @@ typedef struct _emit_inline_asm_method_table_t { void (*end_pass)(emit_inline_asm_t *emit, mp_uint_t type_sig); mp_uint_t (*count_params)(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params); bool (*label)(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id); - void (*align)(emit_inline_asm_t *emit, mp_uint_t align); - void (*data)(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val); void (*op)(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args); } emit_inline_asm_method_table_t; diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 1373e173d..24ba3fa34 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -130,14 +130,6 @@ STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num return true; } -STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) { - mp_asm_base_align(&emit->as.base, align); -} - -STATIC void emit_inline_thumb_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) { - mp_asm_base_data(&emit->as.base, bytesize, val); -} - typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; STATIC const reg_name_t reg_name_table[] = { {0, "r0\0"}, @@ -823,8 +815,6 @@ const emit_inline_asm_method_table_t emit_inline_thumb_method_table = { emit_inline_thumb_end_pass, emit_inline_thumb_count_params, emit_inline_thumb_label, - emit_inline_thumb_align, - emit_inline_thumb_data, emit_inline_thumb_op, }; diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index 284624e45..38a8629e1 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -123,14 +123,6 @@ STATIC bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_nu return true; } -STATIC void emit_inline_xtensa_align(emit_inline_asm_t *emit, mp_uint_t align) { - mp_asm_base_align(&emit->as.base, align); -} - -STATIC void emit_inline_xtensa_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) { - mp_asm_base_data(&emit->as.base, bytesize, val); -} - typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; STATIC const reg_name_t reg_name_table[] = { {0, "a0\0"}, @@ -355,8 +347,6 @@ const emit_inline_asm_method_table_t emit_inline_xtensa_method_table = { emit_inline_xtensa_end_pass, emit_inline_xtensa_count_params, emit_inline_xtensa_label, - emit_inline_xtensa_align, - emit_inline_xtensa_data, emit_inline_xtensa_op, }; -- cgit v1.2.3 From e920bab9768f71c7e22fcfc5af3e1c40f2db8eeb Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 21:23:17 +1100 Subject: py/emitinline: Move common code for end of final pass to compiler. This patch moves some common code from the individual inline assemblers to the compiler, the code that calls the emit-glue to assign the machine code to the functions scope. --- py/compile.c | 9 ++++++++- py/emit.h | 2 +- py/emitinlinethumb.c | 10 +--------- py/emitinlinextensa.c | 10 +--------- 4 files changed, 11 insertions(+), 20 deletions(-) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index f98b783b5..43a0bf454 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3127,7 +3127,7 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind } if (comp->pass > MP_PASS_SCOPE) { - EMIT_INLINE_ASM_ARG(start_pass, comp->pass, comp->scope_cur, &comp->compile_error); + EMIT_INLINE_ASM_ARG(start_pass, comp->pass, &comp->compile_error); } // get the function definition parse node @@ -3258,6 +3258,13 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind if (comp->pass > MP_PASS_SCOPE) { EMIT_INLINE_ASM_ARG(end_pass, type_sig); + + if (comp->pass == MP_PASS_EMIT) { + void *f = mp_asm_base_get_code((mp_asm_base_t*)comp->emit_inline_asm); + mp_emit_glue_assign_native(comp->scope_cur->raw_code, MP_CODE_NATIVE_ASM, + f, mp_asm_base_get_code_size((mp_asm_base_t*)comp->emit_inline_asm), + NULL, comp->scope_cur->num_pos_args, 0, type_sig); + } } if (comp->compile_error != MP_OBJ_NULL) { diff --git a/py/emit.h b/py/emit.h index 41cb2162d..7d00f11f9 100644 --- a/py/emit.h +++ b/py/emit.h @@ -262,7 +262,7 @@ void mp_emit_bc_end_except_handler(emit_t *emit); typedef struct _emit_inline_asm_t emit_inline_asm_t; typedef struct _emit_inline_asm_method_table_t { - void (*start_pass)(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope, mp_obj_t *error_slot); + void (*start_pass)(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot); void (*end_pass)(emit_inline_asm_t *emit, mp_uint_t type_sig); mp_uint_t (*count_params)(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params); bool (*label)(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id); diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 24ba3fa34..406272813 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -45,7 +45,6 @@ typedef enum { struct _emit_inline_asm_t { asm_thumb_t as; uint16_t pass; - scope_t *scope; mp_obj_t *error_slot; mp_uint_t max_num_labels; qstr *label_lookup; @@ -74,9 +73,8 @@ void emit_inline_thumb_free(emit_inline_asm_t *emit) { m_del_obj(emit_inline_asm_t, emit); } -STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope, mp_obj_t *error_slot) { +STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) { emit->pass = pass; - emit->scope = scope; emit->error_slot = error_slot; if (emit->pass == MP_PASS_CODE_SIZE) { memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); @@ -88,12 +86,6 @@ STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pa STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { asm_thumb_exit(&emit->as); asm_thumb_end_pass(&emit->as); - - if (emit->pass == MP_PASS_EMIT) { - void *f = mp_asm_base_get_code(&emit->as.base); - mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, - mp_asm_base_get_code_size(&emit->as.base), NULL, emit->scope->num_pos_args, 0, type_sig); - } } STATIC mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) { diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index 38a8629e1..3d3217f5b 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -38,7 +38,6 @@ struct _emit_inline_asm_t { asm_xtensa_t as; uint16_t pass; - scope_t *scope; mp_obj_t *error_slot; mp_uint_t max_num_labels; qstr *label_lookup; @@ -67,9 +66,8 @@ void emit_inline_xtensa_free(emit_inline_asm_t *emit) { m_del_obj(emit_inline_asm_t, emit); } -STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, scope_t *scope, mp_obj_t *error_slot) { +STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) { emit->pass = pass; - emit->scope = scope; emit->error_slot = error_slot; if (emit->pass == MP_PASS_CODE_SIZE) { memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr)); @@ -81,12 +79,6 @@ STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t p STATIC void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) { asm_xtensa_exit(&emit->as); asm_xtensa_end_pass(&emit->as); - - if (emit->pass == MP_PASS_EMIT) { - void *f = mp_asm_base_get_code(&emit->as.base); - mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f, - mp_asm_base_get_code_size(&emit->as.base), NULL, emit->scope->num_pos_args, 0, type_sig); - } } STATIC mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) { -- cgit v1.2.3 From 155fdc74d5864266441887d6c88111159f401a62 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 22:50:58 +1100 Subject: py/asm: Remove need for dummy_data when doing initial assembler passes. For all but the last pass the assembler only needs to count how much space is needed for the machine code, it doesn't actually need to emit anything. The dummy_data just uses unnecessary RAM and without it the code is not any more complex (and code size does not increase for Thumb and Xtensa archs). --- py/asmarm.c | 5 ++++- py/asmbase.c | 18 ++++++++---------- py/asmbase.h | 3 --- py/asmthumb.c | 20 ++++++++++++-------- py/asmx64.c | 46 ++++++++++++++++++++++++++++------------------ py/asmx64.h | 1 - py/asmx86.c | 36 ++++++++++++++++++++++-------------- py/asmx86.h | 1 - py/asmxtensa.c | 16 ++++++++++------ 9 files changed, 84 insertions(+), 62 deletions(-) (limited to 'py') diff --git a/py/asmarm.c b/py/asmarm.c index 663f86156..63963d2c0 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -55,7 +55,10 @@ void asm_arm_end_pass(asm_arm_t *as) { // Insert word into instruction flow STATIC void emit(asm_arm_t *as, uint op) { - *(uint*)asm_arm_get_cur_to_write_bytes(as, 4) = op; + uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4); + if (c != NULL) { + *(uint32_t*)c = op; + } } // Insert word into instruction flow, add "ALWAYS" condition code diff --git a/py/asmbase.c b/py/asmbase.c index 848730593..c941e917b 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -59,17 +59,16 @@ void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { } // all functions must go through this one to emit bytes -// if as->pass < MP_ASM_PASS_EMIT, then this function returns dummy_data +// if as->pass < MP_ASM_PASS_EMIT, then this function just counts the number +// of bytes needed and returns NULL, and callers should not store any data uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) { - if (as->pass < MP_ASM_PASS_EMIT) { - as->code_offset += num_bytes_to_write; - return as->dummy_data; - } else { + uint8_t *c = NULL; + if (as->pass == MP_ASM_PASS_EMIT) { assert(as->code_offset + num_bytes_to_write <= as->code_size); - uint8_t *c = as->code_base + as->code_offset; - as->code_offset += num_bytes_to_write; - return c; + c = as->code_base + as->code_offset; } + as->code_offset += num_bytes_to_write; + return c; } void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { @@ -92,8 +91,7 @@ void mp_asm_base_align(mp_asm_base_t* as, unsigned int align) { // this function assumes a little endian machine void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val) { uint8_t *c = mp_asm_base_get_cur_to_write_bytes(as, bytesize); - // only write to the buffer in the emit pass (otherwise we may overflow dummy_data) - if (as->pass == MP_ASM_PASS_EMIT) { + if (c != NULL) { for (unsigned int i = 0; i < bytesize; i++) { *c++ = val; val >>= 8; diff --git a/py/asmbase.h b/py/asmbase.h index 06fdd4b90..d2b403893 100644 --- a/py/asmbase.h +++ b/py/asmbase.h @@ -40,9 +40,6 @@ typedef struct _mp_asm_base_t { size_t max_num_labels; size_t *label_offsets; - - // must be last in struct - uint8_t dummy_data[4]; } mp_asm_base_t; void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels); diff --git a/py/asmthumb.c b/py/asmthumb.c index 82a226b62..749c1e405 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -162,18 +162,22 @@ STATIC mp_uint_t get_label_dest(asm_thumb_t *as, uint label) { void asm_thumb_op16(asm_thumb_t *as, uint op) { byte *c = asm_thumb_get_cur_to_write_bytes(as, 2); - // little endian - c[0] = op; - c[1] = op >> 8; + if (c != NULL) { + // little endian + c[0] = op; + c[1] = op >> 8; + } } void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2) { byte *c = asm_thumb_get_cur_to_write_bytes(as, 4); - // little endian, op1 then op2 - c[0] = op1; - c[1] = op1 >> 8; - c[2] = op2; - c[3] = op2 >> 8; + if (c != NULL) { + // little endian, op1 then op2 + c[0] = op1; + c[1] = op1 >> 8; + c[2] = op2; + c[3] = op2 >> 8; + } } #define OP_FORMAT_4(op, rlo_dest, rlo_src) ((op) | ((rlo_src) << 3) | (rlo_dest)) diff --git a/py/asmx64.c b/py/asmx64.c index c9dad2a66..cf1a86b3f 100644 --- a/py/asmx64.c +++ b/py/asmx64.c @@ -122,40 +122,50 @@ static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) { STATIC void asm_x64_write_byte_1(asm_x64_t *as, byte b1) { byte* c = asm_x64_get_cur_to_write_bytes(as, 1); - c[0] = b1; + if (c != NULL) { + c[0] = b1; + } } STATIC void asm_x64_write_byte_2(asm_x64_t *as, byte b1, byte b2) { byte* c = asm_x64_get_cur_to_write_bytes(as, 2); - c[0] = b1; - c[1] = b2; + if (c != NULL) { + c[0] = b1; + c[1] = b2; + } } STATIC void asm_x64_write_byte_3(asm_x64_t *as, byte b1, byte b2, byte b3) { byte* c = asm_x64_get_cur_to_write_bytes(as, 3); - c[0] = b1; - c[1] = b2; - c[2] = b3; + if (c != NULL) { + c[0] = b1; + c[1] = b2; + c[2] = b3; + } } STATIC void asm_x64_write_word32(asm_x64_t *as, int w32) { byte* c = asm_x64_get_cur_to_write_bytes(as, 4); - c[0] = IMM32_L0(w32); - c[1] = IMM32_L1(w32); - c[2] = IMM32_L2(w32); - c[3] = IMM32_L3(w32); + if (c != NULL) { + c[0] = IMM32_L0(w32); + c[1] = IMM32_L1(w32); + c[2] = IMM32_L2(w32); + c[3] = IMM32_L3(w32); + } } STATIC void asm_x64_write_word64(asm_x64_t *as, int64_t w64) { byte* c = asm_x64_get_cur_to_write_bytes(as, 8); - c[0] = IMM32_L0(w64); - c[1] = IMM32_L1(w64); - c[2] = IMM32_L2(w64); - c[3] = IMM32_L3(w64); - c[4] = IMM64_L4(w64); - c[5] = IMM64_L5(w64); - c[6] = IMM64_L6(w64); - c[7] = IMM64_L7(w64); + if (c != NULL) { + c[0] = IMM32_L0(w64); + c[1] = IMM32_L1(w64); + c[2] = IMM32_L2(w64); + c[3] = IMM32_L3(w64); + c[4] = IMM64_L4(w64); + c[5] = IMM64_L5(w64); + c[6] = IMM64_L6(w64); + c[7] = IMM64_L7(w64); + } } /* unused diff --git a/py/asmx64.h b/py/asmx64.h index f80c8da8b..4499c53c3 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -72,7 +72,6 @@ typedef struct _asm_x64_t { mp_asm_base_t base; - byte dummy_data[4]; // in addition to dummy_data in base int num_locals; } asm_x64_t; diff --git a/py/asmx86.c b/py/asmx86.c index cb9b30d40..ef315bf43 100644 --- a/py/asmx86.c +++ b/py/asmx86.c @@ -101,29 +101,37 @@ #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80) STATIC void asm_x86_write_byte_1(asm_x86_t *as, byte b1) { - byte* c = asm_x86_get_cur_to_write_bytes(as, 1); - c[0] = b1; + byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 1); + if (c != NULL) { + c[0] = b1; + } } STATIC void asm_x86_write_byte_2(asm_x86_t *as, byte b1, byte b2) { - byte* c = asm_x86_get_cur_to_write_bytes(as, 2); - c[0] = b1; - c[1] = b2; + byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2); + if (c != NULL) { + c[0] = b1; + c[1] = b2; + } } STATIC void asm_x86_write_byte_3(asm_x86_t *as, byte b1, byte b2, byte b3) { - byte* c = asm_x86_get_cur_to_write_bytes(as, 3); - c[0] = b1; - c[1] = b2; - c[2] = b3; + byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3); + if (c != NULL) { + c[0] = b1; + c[1] = b2; + c[2] = b3; + } } STATIC void asm_x86_write_word32(asm_x86_t *as, int w32) { - byte* c = asm_x86_get_cur_to_write_bytes(as, 4); - c[0] = IMM32_L0(w32); - c[1] = IMM32_L1(w32); - c[2] = IMM32_L2(w32); - c[3] = IMM32_L3(w32); + byte* c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4); + if (c != NULL) { + c[0] = IMM32_L0(w32); + c[1] = IMM32_L1(w32); + c[2] = IMM32_L2(w32); + c[3] = IMM32_L3(w32); + } } STATIC void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) { diff --git a/py/asmx86.h b/py/asmx86.h index ac4b2ecd0..0b44af663 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -75,7 +75,6 @@ typedef struct _asm_x86_t { mp_asm_base_t base; - byte dummy_data[4]; // in addition to dummy_data in base int num_locals; } asm_x86_t; diff --git a/py/asmxtensa.c b/py/asmxtensa.c index 00df432ce..00448dfc5 100644 --- a/py/asmxtensa.c +++ b/py/asmxtensa.c @@ -97,15 +97,19 @@ STATIC uint32_t get_label_dest(asm_xtensa_t *as, uint label) { void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op) { uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2); - c[0] = op; - c[1] = op >> 8; + if (c != NULL) { + c[0] = op; + c[1] = op >> 8; + } } void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op) { uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3); - c[0] = op; - c[1] = op >> 8; - c[2] = op >> 16; + if (c != NULL) { + c[0] = op; + c[1] = op >> 8; + c[2] = op >> 16; + } } void asm_xtensa_j_label(asm_xtensa_t *as, uint label) { @@ -147,7 +151,7 @@ void asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) { // load the constant asm_xtensa_op_l32r(as, reg_dest, as->base.code_offset, 4 + as->cur_const * WORD_SIZE); // store the constant in the table - if (as->base.pass == MP_ASM_PASS_EMIT) { + if (as->const_table != NULL) { as->const_table[as->cur_const] = i32; } ++as->cur_const; -- cgit v1.2.3 From 93ee6603b1a18401ae2968ee8f3012fd8b36a78c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Dec 2016 22:54:45 +1100 Subject: py/asm: Fix x86 and ARM assemblers due to recent code refactoring. --- py/asmarm.c | 8 ++++---- py/asmx86.c | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'py') diff --git a/py/asmarm.c b/py/asmarm.c index 63963d2c0..56e05cf56 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -39,7 +39,7 @@ #define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000) void asm_arm_end_pass(asm_arm_t *as) { - if (as->pass == ASM_ARM_PASS_EMIT) { + if (as->base.pass == ASM_ARM_PASS_EMIT) { #ifdef __arm__ // flush I- and D-cache asm volatile( @@ -333,9 +333,9 @@ void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { } void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) { - assert(label < as->max_num_labels); - mp_uint_t dest = as->label_offsets[label]; - mp_int_t rel = dest - as->code_offset; + assert(label < as->base.max_num_labels); + mp_uint_t dest = as->base.label_offsets[label]; + mp_int_t rel = dest - as->base.code_offset; rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted diff --git a/py/asmx86.c b/py/asmx86.c index ef315bf43..dd3ad0224 100644 --- a/py/asmx86.c +++ b/py/asmx86.c @@ -232,7 +232,7 @@ void asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32) { // src_i32 is stored as a full word in the code, and aligned to machine-word boundary void asm_x86_mov_i32_to_r32_aligned(asm_x86_t *as, int32_t src_i32, int dest_r32) { // mov instruction uses 1 byte for the instruction, before the i32 - while (((as->code_offset + 1) & (WORD_SIZE - 1)) != 0) { + while (((as->base.code_offset + 1) & (WORD_SIZE - 1)) != 0) { asm_x86_nop(as); } asm_x86_mov_i32_to_r32(as, src_i32, dest_r32); @@ -339,13 +339,13 @@ void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) { } STATIC mp_uint_t get_label_dest(asm_x86_t *as, mp_uint_t label) { - assert(label < as->max_num_labels); - return as->label_offsets[label]; + assert(label < as->base.max_num_labels); + return as->base.label_offsets[label]; } void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; if (dest != (mp_uint_t)-1 && rel < 0) { // is a backwards jump, so we know the size of the jump on the first pass // calculate rel assuming 8 bit relative jump @@ -367,7 +367,7 @@ void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label) { void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label) { mp_uint_t dest = get_label_dest(as, label); - mp_int_t rel = dest - as->code_offset; + mp_int_t rel = dest - as->base.code_offset; if (dest != (mp_uint_t)-1 && rel < 0) { // is a backwards jump, so we know the size of the jump on the first pass // calculate rel assuming 8 bit relative jump @@ -499,7 +499,7 @@ void asm_x86_call_ind(asm_x86_t *as, void *ptr, mp_uint_t n_args, int temp_r32) // this reduces code size by 2 bytes per call, but doesn't seem to speed it up at all /* asm_x86_write_byte_1(as, OPCODE_CALL_REL32); - asm_x86_write_word32(as, ptr - (void*)(as->code_base + as->code_offset + 4)); + asm_x86_write_word32(as, ptr - (void*)(as->code_base + as->base.code_offset + 4)); */ // the caller must clean up the stack -- cgit v1.2.3 From 9d787de2a17f99cfeb715507857b92d54b023715 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Fri, 9 Dec 2016 21:15:16 +0300 Subject: py/objint: from_bytes, to_bytes: Require byteorder arg, require "little". CPython requires byteorder arg, make uPy compatible. As we support only "little", error out on anything else. --- py/objint.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'py') diff --git a/py/objint.c b/py/objint.c index f8988d6c9..3af509b53 100644 --- a/py/objint.c +++ b/py/objint.c @@ -383,10 +383,14 @@ mp_obj_t mp_obj_int_binary_op_extra_cases(mp_uint_t op, mp_obj_t lhs_in, mp_obj_ // this is a classmethod STATIC mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { // TODO: Support long ints - // TODO: Support byteorder param (assumes 'little' at the moment) + // TODO: Support byteorder param // TODO: Support signed param (assumes signed=False at the moment) (void)n_args; + if (args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little)) { + mp_not_implemented(""); + } + // get the buffer info mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); @@ -400,14 +404,18 @@ STATIC mp_obj_t int_from_bytes(size_t n_args, const mp_obj_t *args) { return mp_obj_new_int_from_uint(value); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 2, 3, int_from_bytes); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_from_bytes_fun_obj, 3, 4, int_from_bytes); STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_bytes_fun_obj)); STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { - // TODO: Support byteorder param (assumes 'little') + // TODO: Support byteorder param // TODO: Support signed param (assumes signed=False) (void)n_args; + if (args[2] != MP_OBJ_NEW_QSTR(MP_QSTR_little)) { + mp_not_implemented(""); + } + mp_uint_t len = MP_OBJ_SMALL_INT_VALUE(args[1]); vstr_t vstr; @@ -427,7 +435,7 @@ STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 2, 4, int_to_bytes); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes); STATIC const mp_rom_map_elem_t int_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, -- cgit v1.2.3 From a3c61004c2139091b24c81e22fa5afa9fa5f4979 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 12 Dec 2016 15:00:06 +1100 Subject: py/binary: Do zero extension when storing a value larger than word size. --- py/binary.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'py') diff --git a/py/binary.c b/py/binary.c index 699324bc6..d22e0f342 100644 --- a/py/binary.c +++ b/py/binary.c @@ -294,9 +294,10 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte ** #endif { val = mp_obj_get_int(val_in); - // sign extend if needed - if (BYTES_PER_WORD < 8 && size > sizeof(val) && is_signed(val_type) && (mp_int_t)val < 0) { - memset(p + sizeof(val), 0xff, size - sizeof(val)); + // zero/sign extend if needed + if (BYTES_PER_WORD < 8 && size > sizeof(val)) { + int c = (is_signed(val_type) && (mp_int_t)val < 0) ? 0xff : 0x00; + memset(p + sizeof(val), c, size - sizeof(val)); } } } -- cgit v1.2.3 From d9047d3c8a99603884db25076c37778f50633ca6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 13 Dec 2016 15:09:48 +1100 Subject: py/builtinimport: Support importing packages from compiled .mpy files. This patch ensures that __init__.mpy files are imported if their containing directory is imported as a package. --- py/builtinimport.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'py') diff --git a/py/builtinimport.c b/py/builtinimport.c index e197dc783..342bad079 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -73,15 +73,8 @@ STATIC mp_import_stat_t mp_import_stat_any(const char *path) { return mp_import_stat(path); } -STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) { +STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) { mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path)); - DEBUG_printf("stat %s: %d\n", vstr_str(path), stat); - if (stat == MP_IMPORT_STAT_DIR) { - return stat; - } - - vstr_add_str(path, ".py"); - stat = mp_import_stat_any(vstr_null_terminated_str(path)); if (stat == MP_IMPORT_STAT_FILE) { return stat; } @@ -97,6 +90,18 @@ STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) { return MP_IMPORT_STAT_NO_EXIST; } +STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) { + mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path)); + DEBUG_printf("stat %s: %d\n", vstr_str(path), stat); + if (stat == MP_IMPORT_STAT_DIR) { + return stat; + } + + // not a directory, add .py and try as a file + vstr_add_str(path, ".py"); + return stat_file_py_or_mpy(path); +} + STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *dest) { #if MICROPY_PY_SYS // extract the list of paths @@ -463,7 +468,7 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false)); vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); - if (mp_import_stat_any(vstr_null_terminated_str(&path)) != MP_IMPORT_STAT_FILE) { + if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) { vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py //mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { -- cgit v1.2.3 From e83f140463f2144b84e2fe4fa249ab8c8426b5e9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Dec 2016 10:39:41 +1100 Subject: py/mpz: Remove unreachable code in mpn_or_neg functions. --- py/mpz.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'py') diff --git a/py/mpz.c b/py/mpz.c index 1dd177b8e..1861840e8 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -308,9 +308,13 @@ STATIC mp_uint_t mpn_or_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jl carryi >>= DIG_SIZE; } - if (0 != carryi) { - *idig++ = carryi; - } + // At least one of j,k must be negative so the above for-loop runs at least + // once. For carryi to be non-zero here it must be equal to 1 at the end of + // each iteration of the loop. So the accumulation of carryi must overflow + // each time, ie carryi += 0xff..ff. So carryj|carryk must be 0 in the + // DIG_MASK bits on each iteration. But considering all cases of signs of + // j,k one sees that this is not possible. + assert(carryi == 0); return mpn_remove_trailing_zeros(oidig, idig); } @@ -334,9 +338,8 @@ STATIC mp_uint_t mpn_or_neg(mpz_dig_t *idig, const mpz_dig_t *jdig, mp_uint_t jl carryi >>= DIG_SIZE; } - if (0 != carryi) { - *idig++ = carryi; - } + // See comment in above mpn_or_neg for why carryi must be 0. + assert(carryi == 0); return mpn_remove_trailing_zeros(oidig, idig); } -- cgit v1.2.3 From e8f2db7da3f0e60901b09ee6eada99a9e875497f Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 14 Dec 2016 11:40:11 +1100 Subject: py/runtime: Zero out fs_user_mount array in mp_init. There's no need to force ports to copy-and-paste this initialisation code. If FSUSERMOUNT is enabled then this zeroing out must be done. --- esp8266/main.c | 3 --- py/runtime.c | 5 +++++ stmhal/main.c | 3 --- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'py') diff --git a/esp8266/main.c b/esp8266/main.c index 9883f9f96..6814248aa 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -52,9 +52,6 @@ STATIC void mp_reset(void) { mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_)); mp_obj_list_init(mp_sys_argv, 0); - #if MICROPY_VFS_FAT - memset(MP_STATE_PORT(fs_user_mount), 0, sizeof(MP_STATE_PORT(fs_user_mount))); - #endif MP_STATE_PORT(mp_kbd_exception) = mp_obj_new_exception(&mp_type_KeyboardInterrupt); MP_STATE_PORT(term_obj) = MP_OBJ_NULL; MP_STATE_PORT(dupterm_arr_obj) = MP_OBJ_NULL; diff --git a/py/runtime.c b/py/runtime.c index 0ecccbd87..e7e35a081 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -91,6 +91,11 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif + #if MICROPY_FSUSERMOUNT + // zero out the pointers to the user-mounted devices + memset(MP_STATE_VM(fs_user_mount), 0, sizeof(MP_STATE_VM(fs_user_mount))); + #endif + #if MICROPY_PY_THREAD_GIL mp_thread_mutex_init(&MP_STATE_VM(gil_mutex)); #endif diff --git a/stmhal/main.c b/stmhal/main.c index b5d0916f3..78afe54ef 100644 --- a/stmhal/main.c +++ b/stmhal/main.c @@ -439,9 +439,6 @@ soft_reset: mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_flash_slash_lib)); mp_obj_list_init(mp_sys_argv, 0); - // zero out the pointers to the mounted devices - memset(MP_STATE_PORT(fs_user_mount), 0, sizeof(MP_STATE_PORT(fs_user_mount))); - // Initialise low-level sub-systems. Here we need to very basic things like // zeroing out memory and resetting any of the sub-systems. Following this // we can run Python scripts (eg boot.py), but anything that is configurable -- cgit v1.2.3 From 403c93053e6a9897ad397ef325e451c4cc671ce1 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Wed, 14 Dec 2016 21:10:22 +0300 Subject: py/mpconfig.h: Enable MICROPY_PY_SYS_EXIT by default. sys.exit() is an important function to terminate a program. In particular, the testsuite relies on it to skip tests (i.e. any other functionality may be disabled, but sys.exit() is required to at least report that properly). --- py/mpconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/mpconfig.h b/py/mpconfig.h index 3612e6bcb..ea90ec984 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -867,7 +867,7 @@ typedef double mp_float_t; // Whether to provide "sys.exit" function #ifndef MICROPY_PY_SYS_EXIT -#define MICROPY_PY_SYS_EXIT (0) +#define MICROPY_PY_SYS_EXIT (1) #endif // Whether to provide sys.{stdin,stdout,stderr} objects -- cgit v1.2.3 From 7f1da0a03b1559080ca4d054bd38f104bde168e6 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 15 Dec 2016 13:00:19 +1100 Subject: py: Add MICROPY_KBD_EXCEPTION config option to provide mp_kbd_exception. Defining and initialising mp_kbd_exception is boiler-plate code and so the core runtime can provide it, instead of each port needing to do it themselves. The exception object is placed in the VM state rather than on the heap. --- py/mpconfig.h | 5 +++++ py/mpstate.h | 5 +++++ py/runtime.c | 9 +++++++++ 3 files changed, 19 insertions(+) (limited to 'py') diff --git a/py/mpconfig.h b/py/mpconfig.h index ea90ec984..7a71ebd95 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -446,6 +446,11 @@ # endif #endif +// Whether to provide the mp_kbd_exception object +#ifndef MICROPY_KBD_EXCEPTION +#define MICROPY_KBD_EXCEPTION (0) +#endif + // Prefer to raise KeyboardInterrupt asynchronously (from signal or interrupt // handler) - if supported by a particular port. #ifndef MICROPY_ASYNC_KBD_INTR diff --git a/py/mpstate.h b/py/mpstate.h index 439ed6606..91fb68b3a 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -118,6 +118,11 @@ typedef struct _mp_state_vm_t { #endif #endif + #if MICROPY_KBD_EXCEPTION + // exception object of type KeyboardInterrupt + mp_obj_exception_t mp_kbd_exception; + #endif + // dictionary with loaded modules (may be exposed as sys.modules) mp_obj_dict_t mp_loaded_modules_dict; diff --git a/py/runtime.c b/py/runtime.c index e7e35a081..8b4420926 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -68,6 +68,15 @@ void mp_init(void) { mp_init_emergency_exception_buf(); #endif + #if MICROPY_KBD_EXCEPTION + // initialise the exception object for raising KeyboardInterrupt + MP_STATE_VM(mp_kbd_exception).base.type = &mp_type_KeyboardInterrupt; + MP_STATE_VM(mp_kbd_exception).traceback_alloc = 0; + MP_STATE_VM(mp_kbd_exception).traceback_len = 0; + MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + MP_STATE_VM(mp_kbd_exception).args = mp_const_empty_tuple; + #endif + // call port specific initialization if any #ifdef MICROPY_PORT_INIT_FUNC MICROPY_PORT_INIT_FUNC; -- cgit v1.2.3 From de9cd00b39fbd66279dda69bc642ac2f3c459fa1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 19 Dec 2016 17:42:25 +1100 Subject: py/compile: Add an extra pass for Xtensa inline assembler. It needs an extra pass to compute the size of the constant table for the l32r instructions. --- py/compile.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'py') diff --git a/py/compile.c b/py/compile.c index 43a0bf454..b84793d10 100644 --- a/py/compile.c +++ b/py/compile.c @@ -3419,6 +3419,12 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f comp->emit = NULL; comp->emit_inline_asm_method_table = &ASM_EMITTER(method_table); compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE); + #if MICROPY_EMIT_INLINE_XTENSA + // Xtensa requires an extra pass to compute size of l32r const table + // TODO this can be improved by calculating it during SCOPE pass + // but that requires some other structural changes to the asm emitters + compile_scope_inline_asm(comp, s, MP_PASS_CODE_SIZE); + #endif if (comp->compile_error == MP_OBJ_NULL) { compile_scope_inline_asm(comp, s, MP_PASS_EMIT); } -- cgit v1.2.3 From 7318949c467aaf5a29d045b4e9f38ac5ac20aadb Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 20 Dec 2016 14:00:59 +1100 Subject: py/modbuiltins: Remove unreachable code. --- py/modbuiltins.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'py') diff --git a/py/modbuiltins.c b/py/modbuiltins.c index cbdcc9aae..b7c8ff260 100644 --- a/py/modbuiltins.c +++ b/py/modbuiltins.c @@ -475,9 +475,6 @@ STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) { } else if (val - rounded == -0.5) { r &= ~1; } - if (n_args > 1) { - return mp_obj_new_float(r); - } #else mp_int_t r = mp_obj_get_int(o_in); #endif -- cgit v1.2.3 From e4af71212550d950269f6991be187f41dcf64eee Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 21 Dec 2016 11:46:27 +1100 Subject: py/objint: Rename mp_obj_int_as_float to mp_obj_int_as_float_impl. And also simplify it to remove the check for small int. This can be done because this function is only ever called if the argument is not a small int. --- py/obj.c | 8 ++++++-- py/obj.h | 3 --- py/objint.c | 6 ------ py/objint.h | 1 + py/objint_longlong.c | 11 ++++------- py/objint_mpz.c | 11 ++++------- 6 files changed, 15 insertions(+), 25 deletions(-) (limited to 'py') diff --git a/py/obj.c b/py/obj.c index 5601a73fe..1d0c80ab9 100644 --- a/py/obj.c +++ b/py/obj.c @@ -271,8 +271,10 @@ mp_float_t mp_obj_get_float(mp_obj_t arg) { return 1; } else if (MP_OBJ_IS_SMALL_INT(arg)) { return MP_OBJ_SMALL_INT_VALUE(arg); + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE } else if (MP_OBJ_IS_TYPE(arg, &mp_type_int)) { - return mp_obj_int_as_float(arg); + return mp_obj_int_as_float_impl(arg); + #endif } else if (mp_obj_is_float(arg)) { return mp_obj_float_get(arg); } else { @@ -296,9 +298,11 @@ void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) { } else if (MP_OBJ_IS_SMALL_INT(arg)) { *real = MP_OBJ_SMALL_INT_VALUE(arg); *imag = 0; + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE } else if (MP_OBJ_IS_TYPE(arg, &mp_type_int)) { - *real = mp_obj_int_as_float(arg); + *real = mp_obj_int_as_float_impl(arg); *imag = 0; + #endif } else if (mp_obj_is_float(arg)) { *real = mp_obj_float_get(arg); *imag = 0; diff --git a/py/obj.h b/py/obj.h index 61db65a9a..6106bbe19 100644 --- a/py/obj.h +++ b/py/obj.h @@ -680,9 +680,6 @@ void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj); mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in); // Will raise exception if value doesn't fit into mp_int_t mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in); -#if MICROPY_PY_BUILTINS_FLOAT -mp_float_t mp_obj_int_as_float(mp_obj_t self_in); -#endif // exception #define mp_obj_is_native_exception_instance(o) (mp_obj_get_type(o)->make_new == mp_obj_exception_make_new) diff --git a/py/objint.c b/py/objint.c index 3af509b53..1c73141bd 100644 --- a/py/objint.c +++ b/py/objint.c @@ -354,12 +354,6 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { return MP_OBJ_SMALL_INT_VALUE(self_in); } -#if MICROPY_PY_BUILTINS_FLOAT -mp_float_t mp_obj_int_as_float(mp_obj_t self_in) { - return MP_OBJ_SMALL_INT_VALUE(self_in); -} -#endif - #endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE // This dispatcher function is expected to be independent of the implementation of long int diff --git a/py/objint.h b/py/objint.h index 6e627f1bd..f418c329e 100644 --- a/py/objint.h +++ b/py/objint.h @@ -48,6 +48,7 @@ typedef enum { } mp_fp_as_int_class_t; mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val); +mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in); #endif // MICROPY_PY_BUILTINS_FLOAT size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma); diff --git a/py/objint_longlong.c b/py/objint_longlong.c index b051cfbe6..f5b5d9c93 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -295,13 +295,10 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { } #if MICROPY_PY_BUILTINS_FLOAT -mp_float_t mp_obj_int_as_float(mp_obj_t self_in) { - if (MP_OBJ_IS_SMALL_INT(self_in)) { - return MP_OBJ_SMALL_INT_VALUE(self_in); - } else { - mp_obj_int_t *self = self_in; - return self->val; - } +mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int)); + mp_obj_int_t *self = self_in; + return self->val; } #endif diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 0a1d68598..eadf64fce 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -403,13 +403,10 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { } #if MICROPY_PY_BUILTINS_FLOAT -mp_float_t mp_obj_int_as_float(mp_obj_t self_in) { - if (MP_OBJ_IS_SMALL_INT(self_in)) { - return MP_OBJ_SMALL_INT_VALUE(self_in); - } else { - mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); - return mpz_as_float(&self->mpz); - } +mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { + assert(MP_OBJ_IS_TYPE(self_in, &mp_type_int)); + mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + return mpz_as_float(&self->mpz); } #endif -- cgit v1.2.3 From 46a6592f9ac072386ee2a106c8928d2a1d955cbb Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 21 Dec 2016 11:52:05 +1100 Subject: py/emitglue: Refactor to remove assert(0), to improve coverage. --- py/emitglue.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'py') diff --git a/py/emitglue.c b/py/emitglue.c index 795c738d3..fb7a54926 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -126,10 +126,6 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar // make the function, depending on the raw code kind mp_obj_t fun; switch (rc->kind) { - case MP_CODE_BYTECODE: - no_other_choice: - fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.bytecode, rc->data.u_byte.const_table); - break; #if MICROPY_EMIT_NATIVE case MP_CODE_NATIVE_PY: fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->data.u_native.fun_data, rc->data.u_native.const_table); @@ -144,9 +140,10 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar break; #endif default: - // raw code was never set (this should not happen) - assert(0); - goto no_other_choice; // to help flow control analysis + // rc->kind should always be set and BYTECODE is the only remaining case + assert(rc->kind == MP_CODE_BYTECODE); + fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->data.u_byte.bytecode, rc->data.u_byte.const_table); + break; } // check for generator functions and if so wrap in generator object -- cgit v1.2.3 From d02f6a9956760425188f517f3708c76123e53d2a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Thu, 22 Dec 2016 00:29:32 +0300 Subject: extmod/modutimeq: Refactor into optimized class. import utimeq, utime # Max queue size, the queue allocated statically on creation q = utimeq.utimeq(10) q.push(utime.ticks_ms(), data1, data2) res = [0, 0, 0] # Items in res are filled up with results q.pop(res) --- extmod/modutimeq.c | 165 +++++++++++++++++++++++++++++++++++------------------ py/builtin.h | 1 + py/mpconfig.h | 5 ++ py/objmodule.c | 3 + py/py.mk | 1 + 5 files changed, 119 insertions(+), 56 deletions(-) (limited to 'py') diff --git a/extmod/modutimeq.c b/extmod/modutimeq.c index 65d5a70f2..a3863b755 100644 --- a/extmod/modutimeq.c +++ b/extmod/modutimeq.c @@ -1,9 +1,10 @@ /* - * This file is part of the Micro Python project, http://micropython.org/ + * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2014 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,33 +25,43 @@ * THE SOFTWARE. */ +#include + #include "py/nlr.h" #include "py/objlist.h" #include "py/runtime0.h" #include "py/runtime.h" #include "py/smallint.h" -#if MICROPY_PY_UHEAPQ +#if MICROPY_PY_UTIMEQ #define MODULO MICROPY_PY_UTIME_TICKS_PERIOD +#define DEBUG 0 + // the algorithm here is modelled on CPython's heapq.py -STATIC mp_obj_list_t *get_heap(mp_obj_t heap_in) { - if (!MP_OBJ_IS_TYPE(heap_in, &mp_type_list)) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "heap must be a list")); - } +struct qentry { + mp_uint_t time; + mp_obj_t callback; + mp_obj_t args; +}; + +typedef struct _mp_obj_utimeq_t { + mp_obj_base_t base; + mp_uint_t alloc; + mp_uint_t len; + struct qentry items[]; +} mp_obj_utimeq_t; + + +STATIC mp_obj_utimeq_t *get_heap(mp_obj_t heap_in) { return MP_OBJ_TO_PTR(heap_in); } -STATIC bool time_less_than(mp_obj_t item, mp_obj_t parent) { - if (!MP_OBJ_IS_TYPE(item, &mp_type_tuple) || !MP_OBJ_IS_TYPE(parent, &mp_type_tuple)) { - mp_raise_TypeError(""); - } - mp_obj_tuple_t *item_p = MP_OBJ_TO_PTR(item); - mp_obj_tuple_t *parent_p = MP_OBJ_TO_PTR(parent); - mp_uint_t item_tm = MP_OBJ_SMALL_INT_VALUE(item_p->items[0]); - mp_uint_t parent_tm = MP_OBJ_SMALL_INT_VALUE(parent_p->items[0]); +STATIC bool time_less_than(struct qentry *item, struct qentry *parent) { + mp_uint_t item_tm = item->time; + mp_uint_t parent_tm = parent->time; mp_uint_t res = parent_tm - item_tm; if ((mp_int_t)res < 0) { res += MODULO; @@ -58,19 +69,25 @@ STATIC bool time_less_than(mp_obj_t item, mp_obj_t parent) { return res < (MODULO / 2); } -STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t pos, bool timecmp) { - mp_obj_t item = heap->items[pos]; +STATIC mp_obj_t utimeq_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + mp_uint_t alloc = mp_obj_get_int(args[0]); + mp_obj_utimeq_t *o = m_new_obj_var(mp_obj_utimeq_t, struct qentry, alloc); + o->base.type = type; + memset(o->items, 0, sizeof(*o->items) * alloc); + o->alloc = alloc; + o->len = 0; + return MP_OBJ_FROM_PTR(o); +} + +STATIC void heap_siftdown(mp_obj_utimeq_t *heap, mp_uint_t start_pos, mp_uint_t pos) { + struct qentry item = heap->items[pos]; while (pos > start_pos) { mp_uint_t parent_pos = (pos - 1) >> 1; - mp_obj_t parent = heap->items[parent_pos]; - bool lessthan; - if (MP_UNLIKELY(timecmp)) { - lessthan = time_less_than(item, parent); - } else { - lessthan = (mp_binary_op(MP_BINARY_OP_LESS, item, parent) == mp_const_true); - } + struct qentry *parent = &heap->items[parent_pos]; + bool lessthan = time_less_than(&item, parent); if (lessthan) { - heap->items[pos] = parent; + heap->items[pos] = *parent; pos = parent_pos; } else { break; @@ -79,19 +96,14 @@ STATIC void heap_siftdown(mp_obj_list_t *heap, mp_uint_t start_pos, mp_uint_t po heap->items[pos] = item; } -STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos, bool timecmp) { +STATIC void heap_siftup(mp_obj_utimeq_t *heap, mp_uint_t pos) { mp_uint_t start_pos = pos; mp_uint_t end_pos = heap->len; - mp_obj_t item = heap->items[pos]; + struct qentry item = heap->items[pos]; for (mp_uint_t child_pos = 2 * pos + 1; child_pos < end_pos; child_pos = 2 * pos + 1) { // choose right child if it's <= left child if (child_pos + 1 < end_pos) { - bool lessthan; - if (MP_UNLIKELY(timecmp)) { - lessthan = time_less_than(heap->items[child_pos], heap->items[child_pos + 1]); - } else { - lessthan = (mp_binary_op(MP_BINARY_OP_LESS, heap->items[child_pos], heap->items[child_pos + 1]) == mp_const_true); - } + bool lessthan = time_less_than(&heap->items[child_pos], &heap->items[child_pos + 1]); if (!lessthan) { child_pos += 1; } @@ -101,51 +113,92 @@ STATIC void heap_siftup(mp_obj_list_t *heap, mp_uint_t pos, bool timecmp) { pos = child_pos; } heap->items[pos] = item; - heap_siftdown(heap, start_pos, pos, timecmp); + heap_siftdown(heap, start_pos, pos); } STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { mp_obj_t heap_in = args[0]; - mp_obj_list_t *heap = get_heap(heap_in); - mp_obj_list_append(heap_in, args[1]); - bool is_timeq = (n_args > 2 && args[2] == mp_const_true); - heap_siftdown(heap, 0, heap->len - 1, is_timeq); + mp_obj_utimeq_t *heap = get_heap(heap_in); + if (heap->len == heap->alloc) { + mp_raise_msg(&mp_type_IndexError, "queue overflow"); + } + mp_uint_t l = heap->len; + heap->items[l].time = MP_OBJ_SMALL_INT_VALUE(args[1]); + heap->items[l].callback = args[2]; + heap->items[l].args = args[3]; + heap_siftdown(heap, 0, heap->len); + heap->len++; return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 2, 3, mod_utimeq_heappush); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappush_obj, 4, 4, mod_utimeq_heappush); -STATIC mp_obj_t mod_utimeq_heappop(size_t n_args, const mp_obj_t *args) { - mp_obj_t heap_in = args[0]; - mp_obj_list_t *heap = get_heap(heap_in); +STATIC mp_obj_t mod_utimeq_heappop(mp_obj_t heap_in, mp_obj_t list_ref) { + mp_obj_utimeq_t *heap = get_heap(heap_in); if (heap->len == 0) { nlr_raise(mp_obj_new_exception_msg(&mp_type_IndexError, "empty heap")); } - mp_obj_t item = heap->items[0]; + mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref); + if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 3) { + mp_raise_TypeError(""); + } + + struct qentry *item = &heap->items[0]; + ret->items[0] = MP_OBJ_NEW_SMALL_INT(item->time); + ret->items[1] = item->callback; + ret->items[2] = item->args; heap->len -= 1; heap->items[0] = heap->items[heap->len]; - heap->items[heap->len] = MP_OBJ_NULL; // so we don't retain a pointer + heap->items[heap->len].callback = MP_OBJ_NULL; // so we don't retain a pointer + heap->items[heap->len].args = MP_OBJ_NULL; if (heap->len) { - bool is_timeq = (n_args > 1 && args[1] == mp_const_true); - heap_siftup(heap, 0, is_timeq); + heap_siftup(heap, 0); } - return item; + return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_utimeq_heappop_obj, 1, 2, mod_utimeq_heappop); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_utimeq_heappop_obj, mod_utimeq_heappop); -STATIC mp_obj_t mod_utimeq_heapify(mp_obj_t heap_in) { - mp_obj_list_t *heap = get_heap(heap_in); - for (mp_uint_t i = heap->len / 2; i > 0;) { - heap_siftup(heap, --i, false); +#if DEBUG +STATIC mp_obj_t mod_utimeq_dump(mp_obj_t heap_in) { + mp_obj_utimeq_t *heap = get_heap(heap_in); + for (int i = 0; i < heap->len; i++) { + printf(UINT_FMT "\t%p\t%p(%p)\n", heap->items[i].time, + MP_OBJ_TO_PTR(heap->items[i].callback), MP_OBJ_TO_PTR(heap->items[i].args)); } return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_heapify_obj, mod_utimeq_heapify); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_utimeq_dump_obj, mod_utimeq_dump); +#endif + +STATIC mp_obj_t utimeq_unary_op(mp_uint_t op, mp_obj_t self_in) { + mp_obj_utimeq_t *self = MP_OBJ_TO_PTR(self_in); + switch (op) { + case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0); + case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len); + default: return MP_OBJ_NULL; // op not supported + } +} + +STATIC const mp_rom_map_elem_t utimeq_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&mod_utimeq_heappush_obj) }, + { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&mod_utimeq_heappop_obj) }, + #if DEBUG + { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&mod_utimeq_dump_obj) }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(utimeq_locals_dict, utimeq_locals_dict_table); + +STATIC const mp_obj_type_t utimeq_type = { + { &mp_type_type }, + .name = MP_QSTR_utimeq, + .make_new = utimeq_make_new, + .unary_op = utimeq_unary_op, + .locals_dict = (void*)&utimeq_locals_dict, +}; STATIC const mp_rom_map_elem_t mp_module_utimeq_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utimeq) }, - { MP_ROM_QSTR(MP_QSTR_heappush), MP_ROM_PTR(&mod_utimeq_heappush_obj) }, - { MP_ROM_QSTR(MP_QSTR_heappop), MP_ROM_PTR(&mod_utimeq_heappop_obj) }, - { MP_ROM_QSTR(MP_QSTR_heapify), MP_ROM_PTR(&mod_utimeq_heapify_obj) }, + { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&utimeq_type) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_utimeq_globals, mp_module_utimeq_globals_table); @@ -155,4 +208,4 @@ const mp_obj_module_t mp_module_utimeq = { .globals = (mp_obj_dict_t*)&mp_module_utimeq_globals, }; -#endif //MICROPY_PY_UHEAPQ +#endif //MICROPY_PY_UTIMEQ diff --git a/py/builtin.h b/py/builtin.h index 893e47104..282eb1cc9 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -110,6 +110,7 @@ extern const mp_obj_module_t mp_module_ubinascii; extern const mp_obj_module_t mp_module_urandom; extern const mp_obj_module_t mp_module_uselect; extern const mp_obj_module_t mp_module_ussl; +extern const mp_obj_module_t mp_module_utimeq; extern const mp_obj_module_t mp_module_machine; extern const mp_obj_module_t mp_module_lwip; extern const mp_obj_module_t mp_module_websocket; diff --git a/py/mpconfig.h b/py/mpconfig.h index 7a71ebd95..8cf4bbb77 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -945,6 +945,11 @@ typedef double mp_float_t; #define MICROPY_PY_UHEAPQ (0) #endif +// Optimized heap queue for relative timestamps +#ifndef MICROPY_PY_UTIMEQ +#define MICROPY_PY_UTIMEQ (0) +#endif + #ifndef MICROPY_PY_UHASHLIB #define MICROPY_PY_UHASHLIB (0) #endif diff --git a/py/objmodule.c b/py/objmodule.c index 6f7d35d42..1c79e1a18 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -189,6 +189,9 @@ STATIC const mp_rom_map_elem_t mp_builtin_module_table[] = { #if MICROPY_PY_UHEAPQ { MP_ROM_QSTR(MP_QSTR_uheapq), MP_ROM_PTR(&mp_module_uheapq) }, #endif +#if MICROPY_PY_UTIMEQ + { MP_ROM_QSTR(MP_QSTR_utimeq), MP_ROM_PTR(&mp_module_utimeq) }, +#endif #if MICROPY_PY_UHASHLIB { MP_ROM_QSTR(MP_QSTR_uhashlib), MP_ROM_PTR(&mp_module_uhashlib) }, #endif diff --git a/py/py.mk b/py/py.mk index 79436aa9e..ec22fbe63 100644 --- a/py/py.mk +++ b/py/py.mk @@ -212,6 +212,7 @@ PY_O_BASENAME = \ ../extmod/modure.o \ ../extmod/moduzlib.o \ ../extmod/moduheapq.o \ + ../extmod/modutimeq.o \ ../extmod/moduhashlib.o \ ../extmod/modubinascii.o \ ../extmod/virtpin.o \ -- cgit v1.2.3 From adccafb42a892df87e3ca45a8fcc01535b39b055 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Dec 2016 10:32:06 +1100 Subject: tests/basics/lexer: Add a test for newline-escaping within a string. --- py/lexer.c | 2 +- tests/basics/lexer.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'py') diff --git a/py/lexer.c b/py/lexer.c index c6ecdf1f8..7db49de89 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -431,7 +431,7 @@ STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, bool first_token) { } else { switch (c) { case MP_LEXER_EOF: break; // TODO a proper error message? - case '\n': c = MP_LEXER_EOF; break; // TODO check this works correctly (we are supposed to ignore it + case '\n': c = MP_LEXER_EOF; break; // backslash escape the newline, just ignore it case '\\': break; case '\'': break; case '"': break; diff --git a/tests/basics/lexer.py b/tests/basics/lexer.py index 70a9cce99..5f12afa70 100644 --- a/tests/basics/lexer.py +++ b/tests/basics/lexer.py @@ -19,6 +19,10 @@ print(eval("1\r")) print(eval("12\r")) print(eval("123\r")) +# backslash used to escape a line-break in a string +print('a\ +b') + # lots of indentation def a(x): if x: -- cgit v1.2.3 From b9c4783273bfe794a6be2be6b02aedcd9bd80d64 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Dec 2016 10:37:13 +1100 Subject: py/lexer: Remove unreachable code in string tokeniser. --- py/lexer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'py') diff --git a/py/lexer.c b/py/lexer.c index 7db49de89..0611a727b 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -430,7 +430,8 @@ STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, bool first_token) { vstr_add_char(&lex->vstr, '\\'); } else { switch (c) { - case MP_LEXER_EOF: break; // TODO a proper error message? + // note: "c" can never be MP_LEXER_EOF because next_char + // always inserts a newline at the end of the input stream case '\n': c = MP_LEXER_EOF; break; // backslash escape the newline, just ignore it case '\\': break; case '\'': break; -- cgit v1.2.3 From f4aebafe7ae033d6bcde87d71108f488ed4a3251 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Dec 2016 10:39:06 +1100 Subject: py/lexer: Remove unnecessary check for EOF in lexer's next_char func. This check always fails (ie chr0 is never EOF) because the callers of this function never call it past the end of the input stream. And even if they did it would be harmless because 1) reader.readbyte must continue to return an EOF char if the stream is exhausted; 2) next_char would just count the subsequent EOF's as characters worth 1 column. --- py/lexer.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'py') diff --git a/py/lexer.c b/py/lexer.c index 0611a727b..6c8ac9aee 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -128,10 +128,6 @@ STATIC bool is_tail_of_identifier(mp_lexer_t *lex) { } STATIC void next_char(mp_lexer_t *lex) { - if (lex->chr0 == MP_LEXER_EOF) { - return; - } - if (lex->chr0 == '\n') { // a new line ++lex->line; -- cgit v1.2.3 From c305ae32436e37327d33df979d57d9ac1fb822c1 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 22 Dec 2016 10:49:54 +1100 Subject: py/lexer: Permanently disable the mp_lexer_show_token function. The lexer is very mature and this debug function is no longer used. If it's really needed one can uncomment it and recompile. --- py/lexer.c | 4 +++- py/lexer.h | 1 - py/mpconfig.h | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'py') diff --git a/py/lexer.c b/py/lexer.c index 6c8ac9aee..458fba090 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -792,7 +792,9 @@ void mp_lexer_to_next(mp_lexer_t *lex) { mp_lexer_next_token_into(lex, false); } -#if MICROPY_DEBUG_PRINTERS +#if 0 +// This function is used to print the current token and should only be +// needed to debug the lexer, so it's not available via a config option. void mp_lexer_show_token(const mp_lexer_t *lex) { printf("(" UINT_FMT ":" UINT_FMT ") kind:%u str:%p len:%zu", lex->tok_line, lex->tok_column, lex->tok_kind, lex->vstr.buf, lex->vstr.len); if (lex->vstr.len > 0) { diff --git a/py/lexer.h b/py/lexer.h index 1461f9c8c..32aef9626 100644 --- a/py/lexer.h +++ b/py/lexer.h @@ -172,7 +172,6 @@ mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, mp_uint_t void mp_lexer_free(mp_lexer_t *lex); void mp_lexer_to_next(mp_lexer_t *lex); -void mp_lexer_show_token(const mp_lexer_t *lex); /******************************************************************/ // platform specific import function; must be implemented for a specific port diff --git a/py/mpconfig.h b/py/mpconfig.h index 8cf4bbb77..6d1893717 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -362,7 +362,6 @@ #endif // Whether to build functions that print debugging info: -// mp_lexer_show_token // mp_bytecode_print // mp_parse_node_print #ifndef MICROPY_DEBUG_PRINTERS -- cgit v1.2.3 From cf96be60dc225603b14c97570596db4f482ef070 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 27 Dec 2016 00:55:42 +0300 Subject: py/misc.h: Typo fix in comment. --- py/misc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/misc.h b/py/misc.h index e60665e59..7584bc017 100644 --- a/py/misc.h +++ b/py/misc.h @@ -46,7 +46,7 @@ typedef unsigned int uint; #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif -/** memomry allocation ******************************************/ +/** memory allocation ******************************************/ // TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element) -- cgit v1.2.3 From 25f44c19f17085a09ad0f857c6ccc0d33d7c0344 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Tue, 27 Dec 2016 01:00:12 +0300 Subject: cc3200: Re-add support for UART REPL (MICROPY_STDIO_UART setting). UART REPL support was lost in os.dupterm() refactorings, etc. As os.dupterm() is there, implement UART REPL support at the high level - if MICROPY_STDIO_UART is set, make default boot.py contain os.dupterm() call for a UART. This means that changing MICROPY_STDIO_UART value will also require erasing flash on a module to force boot.py re-creation. --- cc3200/mptask.c | 7 ++++++- py/misc.h | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'py') diff --git a/cc3200/mptask.c b/cc3200/mptask.c index eb673b08c..3c34ceeca 100644 --- a/cc3200/mptask.c +++ b/cc3200/mptask.c @@ -98,7 +98,12 @@ static FATFS *sflash_fatfs; static const char fresh_main_py[] = "# main.py -- put your code here!\r\n"; static const char fresh_boot_py[] = "# boot.py -- run on boot-up\r\n" - "# can run arbitrary Python, but best to keep it minimal\r\n"; + "# can run arbitrary Python, but best to keep it minimal\r\n" + #if MICROPY_STDIO_UART + "import os, machine\r\n" + "os.dupterm(machine.UART(0, " MP_STRINGIFY(MICROPY_STDIO_UART_BAUD) "))\r\n" + #endif + ; /****************************************************************************** DECLARE PUBLIC FUNCTIONS diff --git a/py/misc.h b/py/misc.h index 7584bc017..146b9a8e4 100644 --- a/py/misc.h +++ b/py/misc.h @@ -46,6 +46,10 @@ typedef unsigned int uint; #define MAX(x, y) ((x) > (y) ? (x) : (y)) #endif +// Classical double-indirection stringification of preprocessor macro's value +#define _MP_STRINGIFY(x) #x +#define MP_STRINGIFY(x) _MP_STRINGIFY(x) + /** memory allocation ******************************************/ // TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element) -- cgit v1.2.3 From c2dd494bd989ed1a32bf09816f211c04ad243a8b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Dec 2016 12:02:49 +1100 Subject: py/parsenum: Simplify and generalise decoding of digit values. This function should be able to parse integers with any value for the base, because it is called by int('xxx', base). --- py/parsenum.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'py') diff --git a/py/parsenum.c b/py/parsenum.c index b1c449c9b..0be7453dd 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -81,20 +81,18 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m for (; str < top; str++) { // get next digit as a value mp_uint_t dig = *str; - if (unichar_isdigit(dig) && (int)dig - '0' < base) { - // 0-9 digit - dig = dig - '0'; - } else if (base == 16) { - dig |= 0x20; - if ('a' <= dig && dig <= 'f') { - // a-f hex digit - dig = dig - 'a' + 10; + if ('0' <= dig && dig <= '9') { + dig -= '0'; + } else { + dig |= 0x20; // make digit lower-case + if ('a' <= dig && dig <= 'z') { + dig -= 'a' - 10; } else { // unknown character break; } - } else { - // unknown character + } + if (dig >= base) { break; } -- cgit v1.2.3 From 2d9440e2d18775636cf6cfafe1aa1440b21d026b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Dec 2016 12:04:19 +1100 Subject: py/mpz: Fix assertion in mpz_set_from_str which checks value of base. --- py/mpz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/mpz.c b/py/mpz.c index 1861840e8..e503927d0 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -874,7 +874,7 @@ typedef uint32_t mp_float_int_t; // returns number of bytes from str that were processed mp_uint_t mpz_set_from_str(mpz_t *z, const char *str, mp_uint_t len, bool neg, mp_uint_t base) { - assert(base < 36); + assert(base <= 36); const char *cur = str; const char *top = str + len; -- cgit v1.2.3 From ca7af9a77864c09f9461563cfcf48323abe8ea25 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Dec 2016 12:25:00 +1100 Subject: py/parsenum: Fix warning for signed/unsigned comparison. --- py/parsenum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/parsenum.c b/py/parsenum.c index 0be7453dd..2e41801ee 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -92,7 +92,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m break; } } - if (dig >= base) { + if (dig >= (mp_uint_t)base) { break; } -- cgit v1.2.3 From 44bf8e1f2b3a66e8da09331ac016fa7922d8453c Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Dec 2016 12:45:33 +1100 Subject: py/mpprint: Add assertion for, and comment about, valid base values. --- py/mpprint.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'py') diff --git a/py/mpprint.c b/py/mpprint.c index 9ad0f3f9a..72d1c55ca 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -202,6 +202,11 @@ STATIC int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, } int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) { + // These are the only values for "base" that are required to be supported by this + // function, since Python only allows the user to format integers in these bases. + // If needed this function could be generalised to handle other values. + assert(base == 2 || base == 8 || base == 10 || base == 16); + if (!MP_OBJ_IS_INT(x)) { // This will convert booleans to int, or raise an error for // non-integer types. -- cgit v1.2.3 From ea6a958393eff6999040dd3852d505ae78b96f5b Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Dec 2016 12:46:20 +1100 Subject: py/objint: Simplify mp_int_format_size and remove unreachable code. One never needs to format integers with a base larger than 16 (but code can be easily extended beyond this value if needed in the future). --- py/objint.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'py') diff --git a/py/objint.c b/py/objint.c index 1c73141bd..5842a00a4 100644 --- a/py/objint.c +++ b/py/objint.c @@ -151,23 +151,21 @@ typedef mp_int_t fmt_int_t; #endif STATIC const uint8_t log_base2_floor[] = { - 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, + /* if needed, these are the values for higher bases 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5 + */ }; size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma) { - if (base < 2 || base > 32) { - return 0; - } - - size_t num_digits = num_bits / log_base2_floor[base] + 1; + assert(2 <= base && base <= 16); + size_t num_digits = num_bits / log_base2_floor[base - 1] + 1; size_t num_commas = comma ? num_digits / 3 : 0; size_t prefix_len = prefix ? strlen(prefix) : 0; return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte -- cgit v1.2.3 From afc50635396cc8c0a686094993e42afb775d4a99 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Dec 2016 17:50:10 +1100 Subject: py/unicode: Comment-out unused function unichar_isprint. --- py/unicode.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'py') diff --git a/py/unicode.c b/py/unicode.c index 8be63f217..c6f872038 100644 --- a/py/unicode.c +++ b/py/unicode.c @@ -133,9 +133,11 @@ bool unichar_isalpha(unichar c) { return c < 128 && (attr[c] & FL_ALPHA) != 0; } +/* unused bool unichar_isprint(unichar c) { return c < 128 && (attr[c] & FL_PRINT) != 0; } +*/ bool unichar_isdigit(unichar c) { return c < 128 && (attr[c] & FL_DIGIT) != 0; -- cgit v1.2.3 From 3f9c45efd126810cb30986cc4a4f49c2baa62caf Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 3 Jan 2017 15:40:50 +1100 Subject: py/asmarm: Fix assembler's PASS_EMIT constant name. --- py/asmarm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/asmarm.c b/py/asmarm.c index 56e05cf56..da07680e3 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -39,7 +39,7 @@ #define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000) void asm_arm_end_pass(asm_arm_t *as) { - if (as->base.pass == ASM_ARM_PASS_EMIT) { + if (as->base.pass == MP_ASM_PASS_EMIT) { #ifdef __arm__ // flush I- and D-cache asm volatile( -- cgit v1.2.3 From 343b4189b0b2a61d4db4e6ca2c6ddf82150d3b6d Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 5 Jan 2017 15:51:36 +1100 Subject: py/mkrules.mk: Add MPY_CROSS_FLAGS option to pass flags to mpy-cross. So that ports can pass their own custom options to mpy-cross. --- py/mkrules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'py') diff --git a/py/mkrules.mk b/py/mkrules.mk index f4a2363a7..b71450a21 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -115,7 +115,7 @@ FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:. $(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py @$(ECHO) "MPY $<" $(Q)$(MKDIR) -p $(dir $@) - $(Q)$(MPY_CROSS) -o $@ -s $(^:$(FROZEN_MPY_DIR)/%=%) $^ + $(Q)$(MPY_CROSS) -o $@ -s $(^:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $^ # to build frozen_mpy.c from all .mpy files $(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h -- cgit v1.2.3 From b528e9a428f92241d2cf3bb30f78b0d65e1a8428 Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 8 Jan 2017 20:17:23 +1100 Subject: py/builtinimport: Fix bug when importing names from frozen packages. The commit d9047d3c8a99603884db25076c37778f50633ca6 introduced a bug whereby "from a.b import c" stopped working for frozen packages. This is because the path was not properly truncated and became "a//b". Such a path resolves correctly for a "real" filesystem, but not for a search in the list of frozen modules. --- py/builtinimport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'py') diff --git a/py/builtinimport.c b/py/builtinimport.c index 342bad079..4024c5d59 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -466,15 +466,15 @@ mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) { // https://docs.python.org/3/reference/import.html // "Specifically, any module that contains a __path__ attribute is considered a package." mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path), false)); + size_t orig_path_len = path.len; vstr_add_char(&path, PATH_SEP_CHAR); vstr_add_str(&path, "__init__.py"); if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) { - vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py //mp_warning("%s is imported as namespace package", vstr_str(&path)); } else { do_load(module_obj, &path); - vstr_cut_tail_bytes(&path, sizeof("/__init__.py") - 1); // cut off /__init__.py } + path.len = orig_path_len; } else { // MP_IMPORT_STAT_FILE do_load(module_obj, &path); // TODO: We cannot just break here, at the very least, we must execute -- cgit v1.2.3