From 39bf055d23be4b0f761af115773c3db1074fc2dd Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Feb 2024 13:25:58 +1100 Subject: [PATCH] py/emitglue: Reorder and resize members of mp_raw_code_t. The mp_raw_code_t struct has been reordered and some members resized. The `n_pos_args` member is renamed to `asm_n_pos_args`, and `type_sig` renamed to `asm_type_sig` to indicate that these are used only for the inline-asm emitters. These two members are also grouped together in the struct. The justifications for resizing the members are: - `fun_data_len` can be 32-bits without issue - `n_children` is already limited to 16-bits by `mp_emit_common_t::ct_cur_child` - `scope_flags` is already limited to 16-bits by `scope_t::scope_flags` - `prelude_offset` is already limited to 16-bits by the argument to `mp_emit_glue_assign_native()` - it's reasonable to limit the maximim number of inline-asm arguments to 12 (24 bits for `asm_type_sig` divided by 2) This change helps to reduce frozen code size (and in some cases RAM usage) in the following cases: - 64-bit targets - builds with MICROPY_PY_SYS_SETTRACE enabled - builds with MICROPY_EMIT_MACHINE_CODE enabled but MICROPY_EMIT_INLINE_ASM disabled With this change, unix 64-bit builds are -4080 bytes in size. Bare-metal ports like rp2 are unchanged (because mp_raw_code_t is still 32 bytes on those 32-bit targets). Signed-off-by: Damien George --- py/emitglue.c | 17 ++++++++++------- py/emitglue.h | 40 ++++++++++++++++++++++------------------ py/persistentcode.c | 6 ++++-- tools/mpy-tool.py | 20 ++++++++++---------- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/py/emitglue.c b/py/emitglue.c index 6ec6d6b885..0ec126fe94 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -66,9 +66,9 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE - size_t n_children, + uint16_t n_children, #endif - mp_uint_t scope_flags) { + uint16_t scope_flags) { rc->kind = MP_CODE_BYTECODE; rc->scope_flags = scope_flags; @@ -99,10 +99,11 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE - size_t n_children, + uint16_t n_children, uint16_t prelude_offset, #endif - mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig) { + uint16_t scope_flags, uint32_t asm_n_pos_args, uint32_t asm_type_sig + ) { assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM); @@ -145,9 +146,11 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void rc->prelude_offset = prelude_offset; #endif + #if MICROPY_EMIT_INLINE_ASM // These two entries are only needed for MP_CODE_NATIVE_ASM. - rc->n_pos_args = n_pos_args; - rc->type_sig = type_sig; + rc->asm_n_pos_args = asm_n_pos_args; + rc->asm_type_sig = asm_type_sig; + #endif #if DEBUG_PRINT DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (uint)scope_flags); @@ -195,7 +198,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module #endif #if MICROPY_EMIT_INLINE_ASM case MP_CODE_NATIVE_ASM: - fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->fun_data, rc->type_sig); + fun = mp_obj_new_fun_asm(rc->asm_n_pos_args, rc->fun_data, rc->asm_type_sig); break; #endif default: diff --git a/py/emitglue.h b/py/emitglue.h index 4ddf74011f..c9c6c8ba1d 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -49,34 +49,38 @@ typedef enum { MP_CODE_NATIVE_ASM, } mp_raw_code_kind_t; +// This mp_raw_code_t struct holds static information about a non-instantiated function. +// A function object is created from this information, and that object can then be executed. +// +// This struct appears in the following places: // compiled bytecode: instance in RAM, referenced by outer scope, usually freed after first (and only) use // mpy file: instance in RAM, created when .mpy file is loaded (same comments as above) // frozen: instance in ROM typedef struct _mp_raw_code_t { - mp_uint_t kind : 3; // of type mp_raw_code_kind_t - mp_uint_t scope_flags : 7; - mp_uint_t n_pos_args : 11; + uint32_t kind : 3; // of type mp_raw_code_kind_t + uint32_t scope_flags : 7; const void *fun_data; - #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS - size_t fun_data_len; // so mp_raw_code_save and mp_bytecode_print work - #endif struct _mp_raw_code_t **children; + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS + uint32_t fun_data_len; // so mp_raw_code_save and mp_bytecode_print work + #endif #if MICROPY_PERSISTENT_CODE_SAVE - size_t n_children; + uint16_t n_children; + #if MICROPY_EMIT_MACHINE_CODE + uint16_t prelude_offset; + #endif #if MICROPY_PY_SYS_SETTRACE - mp_bytecode_prelude_t prelude; // line_of_definition is a Python source line where the raw_code was // created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info // stored in prelude, which provides line number for first statement of // a function. Required to properly implement "call" trace event. - mp_uint_t line_of_definition; - #endif - #if MICROPY_EMIT_MACHINE_CODE - uint16_t prelude_offset; + uint32_t line_of_definition; + mp_bytecode_prelude_t prelude; #endif #endif - #if MICROPY_EMIT_MACHINE_CODE - mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc + #if MICROPY_EMIT_INLINE_ASM + uint32_t asm_n_pos_args : 8; + uint32_t asm_type_sig : 24; // compressed as 2-bit types; ret is MSB, then arg0, arg1, etc #endif } mp_raw_code_t; @@ -88,17 +92,17 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE - size_t n_children, + uint16_t n_children, #endif - mp_uint_t scope_flags); + uint16_t scope_flags); void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, mp_raw_code_t **children, #if MICROPY_PERSISTENT_CODE_SAVE - size_t n_children, + uint16_t n_children, uint16_t prelude_offset, #endif - mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig); + uint16_t scope_flags, uint32_t asm_n_pos_args, uint32_t asm_type_sig); mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args); mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args); diff --git a/py/persistentcode.c b/py/persistentcode.c index df1003babf..83806febb3 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -578,10 +578,12 @@ STATIC void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) { } else if (rc->kind == MP_CODE_NATIVE_VIPER || rc->kind == MP_CODE_NATIVE_ASM) { // Save basic scope info for viper and asm mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG); + #if MICROPY_EMIT_INLINE_ASM if (rc->kind == MP_CODE_NATIVE_ASM) { - mp_print_uint(print, rc->n_pos_args); - mp_print_uint(print, rc->type_sig); + mp_print_uint(print, rc->asm_n_pos_args); + mp_print_uint(print, rc->asm_type_sig); } + #endif } #endif diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index a656cccd69..57411c3fbc 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -909,21 +909,24 @@ class RawCode(object): print("static const mp_raw_code_t raw_code_%s = {" % self.escaped_name) print(" .kind = %s," % RawCode.code_kind_str[self.code_kind]) print(" .scope_flags = 0x%02x," % self.scope_flags) - print(" .n_pos_args = %u," % self.n_pos_args) print(" .fun_data = fun_data_%s," % self.escaped_name) - print(" #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS") - print(" .fun_data_len = %u," % len(self.fun_data)) - print(" #endif") if len(self.children): print(" .children = (void *)&children_%s," % self.escaped_name) elif prelude_ptr: print(" .children = (void *)%s," % prelude_ptr) else: print(" .children = NULL,") + print(" #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS") + print(" .fun_data_len = %u," % len(self.fun_data)) + print(" #endif") print(" #if MICROPY_PERSISTENT_CODE_SAVE") print(" .n_children = %u," % len(self.children)) + print(" #if MICROPY_EMIT_MACHINE_CODE") + print(" .prelude_offset = %u," % self.prelude_offset) + print(" #endif") if self.code_kind == MP_CODE_BYTECODE: print(" #if MICROPY_PY_SYS_SETTRACE") + print(" .line_of_definition = %u," % 0) # TODO print(" .prelude = {") print(" .n_state = %u," % self.prelude_signature[0]) print(" .n_exc_stack = %u," % self.prelude_signature[1]) @@ -944,14 +947,11 @@ class RawCode(object): " .opcodes = fun_data_%s + %u," % (self.escaped_name, self.offset_opcodes) ) print(" },") - print(" .line_of_definition = %u," % 0) # TODO print(" #endif") - print(" #if MICROPY_EMIT_MACHINE_CODE") - print(" .prelude_offset = %u," % self.prelude_offset) print(" #endif") - print(" #endif") - print(" #if MICROPY_EMIT_MACHINE_CODE") - print(" .type_sig = %u," % type_sig) + print(" #if MICROPY_EMIT_INLINE_ASM") + print(" .asm_n_pos_args = %u," % self.n_pos_args) + print(" .asm_type_sig = %u," % type_sig) print(" #endif") print("};")