diff --git a/docs/reference/constrained.rst b/docs/reference/constrained.rst index 816a20f9da80e..8d3ba0e3af46b 100644 --- a/docs/reference/constrained.rst +++ b/docs/reference/constrained.rst @@ -126,8 +126,9 @@ that the objects remain in flash memory rather than being copied to RAM. The Python built-in types. When considering the implications of frozen bytecode, note that in Python -strings, floats, bytes, integers and complex numbers are immutable. Accordingly -these will be frozen into flash. Thus, in the line +strings, floats, bytes, integers, complex numbers and tuples are immutable. +Accordingly these will be frozen into flash (for tuples, only if all their +elements are immutable). Thus, in the line .. code:: @@ -146,16 +147,16 @@ As in the string example, at runtime a reference to the arbitrarily large integer is assigned to the variable ``bar``. That reference occupies a single machine word. -It might be expected that tuples of integers could be employed for the purpose -of storing constant data with minimal RAM use. With the current compiler this -is ineffective (the code works, but RAM is not saved). +Tuples of constant objects are themselves constant. Such constant tuples are +optimised by the compiler so they do not need to be created at runtime each time +they are used. For example: .. code:: - foo = (1, 2, 3, 4, 5, 6, 100000) + foo = (1, 2, 3, 4, 5, 6, 100000, ("string", b"bytes", False, True)) -At runtime the tuple will be located in RAM. This may be subject to future -improvement. +This entire tuple will exist as a single object (potentially in flash if the +code is frozen) and referenced each time it is needed. **Needless object creation** @@ -214,7 +215,7 @@ buffer; this is both faster and more efficient in terms of memory fragmentation. **Bytes are smaller than ints** -On most platforms an integer consumes four bytes. Consider the two calls to the +On most platforms an integer consumes four bytes. Consider the three calls to the function ``foo()``: .. code:: @@ -222,12 +223,16 @@ function ``foo()``: def foo(bar): for x in bar: print(x) + foo([1, 2, 0xff]) foo((1, 2, 0xff)) foo(b'\1\2\xff') -In the first call a tuple of integers is created in RAM. The second efficiently +In the first call a `list` of integers is created in RAM each time the code is +executed. The second call creates a constant `tuple` object (a `tuple` containing +only constant objects) as part of the compilation phase, so it is only created +once and is more efficient than the `list`. The third call efficiently creates a `bytes` object consuming the minimum amount of RAM. If the module -were frozen as bytecode, the `bytes` object would reside in flash. +were frozen as bytecode, both the `tuple` and `bytes` object would reside in flash. **Strings Versus Bytes** diff --git a/ports/minimal/frozentest.mpy b/ports/minimal/frozentest.mpy index cd75d65a8bad1..99581617ac3d1 100644 Binary files a/ports/minimal/frozentest.mpy and b/ports/minimal/frozentest.mpy differ diff --git a/ports/powerpc/frozentest.mpy b/ports/powerpc/frozentest.mpy index cd75d65a8bad1..99581617ac3d1 100644 Binary files a/ports/powerpc/frozentest.mpy and b/ports/powerpc/frozentest.mpy differ diff --git a/ports/qemu-arm/test-frzmpy/frozen_const.py b/ports/qemu-arm/test-frzmpy/frozen_const.py new file mode 100644 index 0000000000000..19af4a3e2a3da --- /dev/null +++ b/ports/qemu-arm/test-frzmpy/frozen_const.py @@ -0,0 +1,11 @@ +# Test freezing constants. + +x0 = (1,) +x1 = (1, 2) +x2 = (1, 1 << 100) +x3 = (None, False, True, ...) +x4 = ("str", b"bytes") +x5 = ((),) +x6 = ((1,),) +x7 = ((1, 2),) +x8 = (1, "str", (), ("nested", 2, ((False, True), None, ...))) diff --git a/py/compile.c b/py/compile.c index e4ead7f1ac2ac..6bb601b9252a6 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2763,12 +2763,7 @@ STATIC void compile_atom_expr_await(compiler_t *comp, mp_parse_node_struct_t *pn #endif STATIC mp_obj_t get_const_object(mp_parse_node_struct_t *pns) { - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D - // nodes are 32-bit pointers, but need to extract 64-bit object - return (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32); - #else - return (mp_obj_t)pns->nodes[0]; - #endif + return mp_parse_node_extract_const_object(pns); } STATIC void compile_const_object(compiler_t *comp, mp_parse_node_struct_t *pns) { diff --git a/py/mpconfig.h b/py/mpconfig.h index 22dfbb867547f..c308c31bda562 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -441,6 +441,12 @@ #define MICROPY_COMP_CONST_FOLDING (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether to compile constant tuples immediately to their respective objects; eg (1, True) +// Otherwise the tuple will be built at runtime +#ifndef MICROPY_COMP_CONST_TUPLE +#define MICROPY_COMP_CONST_TUPLE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether to enable optimisations for constant literals, eg OrderedDict #ifndef MICROPY_COMP_CONST_LITERAL #define MICROPY_COMP_CONST_LITERAL (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) diff --git a/py/parse.c b/py/parse.c index 233cba11b46ca..b8a40f4bb5062 100644 --- a/py/parse.c +++ b/py/parse.c @@ -291,6 +291,16 @@ STATIC void *parser_alloc(parser_t *parser, size_t num_bytes) { return ret; } +#if MICROPY_COMP_CONST_TUPLE +STATIC void parser_free_parse_node_struct(parser_t *parser, mp_parse_node_struct_t *pns) { + mp_parse_chunk_t *chunk = parser->cur_chunk; + if (chunk->data <= (byte *)pns && (byte *)pns < chunk->data + chunk->union_.used) { + size_t num_bytes = sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * MP_PARSE_NODE_STRUCT_NUM_NODES(pns); + chunk->union_.used -= num_bytes; + } +} +#endif + STATIC void push_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t arg_i) { if (parser->rule_stack_top >= parser->rule_stack_alloc) { rule_stack_t *rs = m_renew(rule_stack_t, parser->rule_stack, parser->rule_stack_alloc, parser->rule_stack_alloc + MICROPY_ALLOC_PARSE_RULE_INC); @@ -317,6 +327,13 @@ STATIC uint8_t pop_rule(parser_t *parser, size_t *arg_i, size_t *src_line) { return rule_id; } +#if MICROPY_COMP_CONST_TUPLE +STATIC uint8_t peek_rule(parser_t *parser, size_t n) { + assert(parser->rule_stack_top > n); + return parser->rule_stack[parser->rule_stack_top - 1 - n].rule_id; +} +#endif + 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); @@ -333,18 +350,83 @@ bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { return true; } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D - // nodes are 32-bit pointers, but need to extract 64-bit object - *o = (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32); - #else - *o = (mp_obj_t)pns->nodes[0]; - #endif + *o = mp_parse_node_extract_const_object(pns); return mp_obj_is_int(*o); } else { return false; } } +#if MICROPY_COMP_CONST_TUPLE +STATIC bool mp_parse_node_is_const(mp_parse_node_t pn) { + if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + // Small integer. + return true; + } else if (MP_PARSE_NODE_IS_LEAF(pn)) { + // Possible str, or constant literal. + uintptr_t kind = MP_PARSE_NODE_LEAF_KIND(pn); + if (kind == MP_PARSE_NODE_STRING) { + return true; + } else if (kind == MP_PARSE_NODE_TOKEN) { + uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); + return arg == MP_TOKEN_KW_NONE + || arg == MP_TOKEN_KW_FALSE + || arg == MP_TOKEN_KW_TRUE + || arg == MP_TOKEN_ELLIPSIS; + } + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { + // Constant object. + return true; + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_atom_paren)) { + // Possible empty tuple. + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + return MP_PARSE_NODE_IS_NULL(pns->nodes[0]); + } + return false; +} + +STATIC mp_obj_t mp_parse_node_convert_to_obj(mp_parse_node_t pn) { + assert(mp_parse_node_is_const(pn)); + if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { + mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); + #if MICROPY_DYNAMIC_COMPILER + mp_uint_t sign_mask = -((mp_uint_t)1 << (mp_dynamic_compiler.small_int_bits - 1)); + if (!((arg & sign_mask) == 0 || (arg & sign_mask) == sign_mask)) { + // Integer doesn't fit in a small-int, so create a multi-precision int object. + return mp_obj_new_int_from_ll(arg); + } + #endif + return MP_OBJ_NEW_SMALL_INT(arg); + } else if (MP_PARSE_NODE_IS_LEAF(pn)) { + uintptr_t kind = MP_PARSE_NODE_LEAF_KIND(pn); + uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); + if (kind == MP_PARSE_NODE_STRING) { + return MP_OBJ_NEW_QSTR(arg); + } else { + assert(MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN); + switch (arg) { + case MP_TOKEN_KW_NONE: + return mp_const_none; + case MP_TOKEN_KW_FALSE: + return mp_const_false; + case MP_TOKEN_KW_TRUE: + return mp_const_true; + default: + assert(arg == MP_TOKEN_ELLIPSIS); + return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); + } + } + } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { + mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; + return mp_parse_node_extract_const_object(pns); + } else { + assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_atom_paren)); + assert(MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t *)pn)->nodes[0])); + return mp_const_empty_tuple; + } +} +#endif + size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes) { if (MP_PARSE_NODE_IS_NULL(*pn)) { *nodes = NULL; @@ -397,11 +479,14 @@ void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t ind // node must be a mp_parse_node_struct_t mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_const_object) { + mp_obj_t obj = mp_parse_node_extract_const_object(pns); #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D - mp_printf(print, "literal const(%016llx)\n", (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32)); + mp_printf(print, "literal const(%016llx)=", obj); #else - mp_printf(print, "literal const(%p)\n", (mp_obj_t)pns->nodes[0]); + mp_printf(print, "literal const(%p)=", obj); #endif + mp_obj_print_helper(print, obj, PRINT_REPR); + mp_printf(print, "\n"); } else { size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); #if MICROPY_DEBUG_PARSE_RULE_NAME @@ -793,6 +878,59 @@ STATIC bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { } #endif +#if MICROPY_COMP_CONST_TUPLE +STATIC bool build_tuple_from_stack(parser_t *parser, size_t src_line, size_t num_args) { + for (size_t i = num_args; i > 0;) { + mp_parse_node_t pn = peek_result(parser, --i); + if (!mp_parse_node_is_const(pn)) { + return false; + } + } + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_args, NULL)); + for (size_t i = num_args; i > 0;) { + mp_parse_node_t pn = pop_result(parser); + tuple->items[--i] = mp_parse_node_convert_to_obj(pn); + if (MP_PARSE_NODE_IS_STRUCT(pn)) { + parser_free_parse_node_struct(parser, (mp_parse_node_struct_t *)pn); + } + } + push_result_node(parser, make_node_const_object(parser, src_line, MP_OBJ_FROM_PTR(tuple))); + return true; +} + +STATIC bool build_tuple(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) { + if (rule_id == RULE_testlist_comp) { + if (peek_rule(parser, 0) == RULE_atom_paren) { + // Tuple of the form "(a,)". + return build_tuple_from_stack(parser, src_line, num_args); + } + } + if (rule_id == RULE_testlist_comp_3c) { + assert(peek_rule(parser, 0) == RULE_testlist_comp_3b); + assert(peek_rule(parser, 1) == RULE_testlist_comp); + if (peek_rule(parser, 2) == RULE_atom_paren) { + // Tuple of the form "(a, b)". + if (build_tuple_from_stack(parser, src_line, num_args)) { + parser->rule_stack_top -= 2; // discard 2 rules + return true; + } + } + } + if (rule_id == RULE_testlist_star_expr + || rule_id == RULE_testlist + || rule_id == RULE_subscriptlist) { + // Tuple of the form: + // - x = a, b + // - return a, b + // - for x in a, b: pass + // - x[a, b] + return build_tuple_from_stack(parser, src_line, num_args); + } + + return false; +} +#endif + STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, size_t num_args) { // Simplify and optimise certain rules, to reduce memory usage and simplify the compiler. if (rule_id == RULE_atom_paren) { @@ -849,6 +987,13 @@ STATIC void push_result_rule(parser_t *parser, size_t src_line, uint8_t rule_id, } #endif + #if MICROPY_COMP_CONST_TUPLE + if (build_tuple(parser, src_line, rule_id, num_args)) { + // we built a tuple from this rule so return straightaway + return; + } + #endif + mp_parse_node_struct_t *pn = parser_alloc(parser, sizeof(mp_parse_node_struct_t) + sizeof(mp_parse_node_t) * num_args); pn->source_line = src_line; pn->kind_num_nodes = (rule_id & 0xff) | (num_args << 8); diff --git a/py/parse.h b/py/parse.h index 9c327c28b6c77..5531e35cbb36c 100644 --- a/py/parse.h +++ b/py/parse.h @@ -77,9 +77,20 @@ typedef struct _mp_parse_node_struct_t { 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)); } + +static inline mp_obj_t mp_parse_node_extract_const_object(mp_parse_node_struct_t *pns) { + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D + // nodes are 32-bit pointers, but need to extract 64-bit object + return (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32); + #else + return (mp_obj_t)pns->nodes[0]; + #endif +} + 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); diff --git a/py/persistentcode.c b/py/persistentcode.c index f64e383a61690..f3d6b7205d6a2 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -198,29 +198,41 @@ STATIC qstr load_qstr(mp_reader_t *reader) { STATIC mp_obj_t load_obj(mp_reader_t *reader) { byte obj_type = read_byte(reader); #if MICROPY_EMIT_MACHINE_CODE - if (obj_type == 't') { + if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) { return MP_OBJ_FROM_PTR(&mp_fun_table); } else #endif - if (obj_type == 'e') { + if (obj_type == MP_PERSISTENT_OBJ_NONE) { + return mp_const_none; + } else if (obj_type == MP_PERSISTENT_OBJ_FALSE) { + return mp_const_false; + } else if (obj_type == MP_PERSISTENT_OBJ_TRUE) { + return mp_const_true; + } else if (obj_type == MP_PERSISTENT_OBJ_ELLIPSIS) { return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); } else { size_t len = read_uint(reader); - if (len == 0 && obj_type == 'b') { + if (len == 0 && obj_type == MP_PERSISTENT_OBJ_BYTES) { read_byte(reader); // skip null terminator return mp_const_empty_bytes; + } else if (obj_type == MP_PERSISTENT_OBJ_TUPLE) { + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL)); + for (size_t i = 0; i < len; ++i) { + tuple->items[i] = load_obj(reader); + } + return MP_OBJ_FROM_PTR(tuple); } vstr_t vstr; vstr_init_len(&vstr, len); read_bytes(reader, (byte *)vstr.buf, len); - if (obj_type == 's' || obj_type == 'b') { + if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) { read_byte(reader); // skip null terminator - return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); - } else if (obj_type == 'i') { + return mp_obj_new_str_from_vstr(obj_type == MP_PERSISTENT_OBJ_STR ? &mp_type_str : &mp_type_bytes, &vstr); + } else if (obj_type == MP_PERSISTENT_OBJ_INT) { 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); + assert(obj_type == MP_PERSISTENT_OBJ_FLOAT || obj_type == MP_PERSISTENT_OBJ_COMPLEX); + return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, false, NULL); } } } @@ -498,38 +510,57 @@ STATIC void save_qstr(mp_print_t *print, qstr qst) { STATIC void save_obj(mp_print_t *print, mp_obj_t o) { #if MICROPY_EMIT_MACHINE_CODE if (o == MP_OBJ_FROM_PTR(&mp_fun_table)) { - byte obj_type = 't'; + byte obj_type = MP_PERSISTENT_OBJ_FUN_TABLE; mp_print_bytes(print, &obj_type, 1); } else #endif if (mp_obj_is_str_or_bytes(o)) { byte obj_type; if (mp_obj_is_str(o)) { - obj_type = 's'; + obj_type = MP_PERSISTENT_OBJ_STR; } else { - obj_type = 'b'; + obj_type = MP_PERSISTENT_OBJ_BYTES; } size_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 + 1); // +1 to store null terminator + } else if (o == mp_const_none) { + byte obj_type = MP_PERSISTENT_OBJ_NONE; + mp_print_bytes(print, &obj_type, 1); + } else if (o == mp_const_false) { + byte obj_type = MP_PERSISTENT_OBJ_FALSE; + mp_print_bytes(print, &obj_type, 1); + } else if (o == mp_const_true) { + byte obj_type = MP_PERSISTENT_OBJ_TRUE; + mp_print_bytes(print, &obj_type, 1); } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { - byte obj_type = 'e'; + byte obj_type = MP_PERSISTENT_OBJ_ELLIPSIS; mp_print_bytes(print, &obj_type, 1); + } else if (mp_obj_is_type(o, &mp_type_tuple)) { + size_t len; + mp_obj_t *items; + mp_obj_tuple_get(o, &len, &items); + byte obj_type = MP_PERSISTENT_OBJ_TUPLE; + mp_print_bytes(print, &obj_type, 1); + mp_print_uint(print, len); + for (size_t i = 0; i < len; ++i) { + save_obj(print, items[i]); + } } else { // we save numbers using a simplistic text representation // TODO could be improved byte obj_type; if (mp_obj_is_int(o)) { - obj_type = 'i'; + obj_type = MP_PERSISTENT_OBJ_INT; #if MICROPY_PY_BUILTINS_COMPLEX } else if (mp_obj_is_type(o, &mp_type_complex)) { - obj_type = 'c'; + obj_type = MP_PERSISTENT_OBJ_COMPLEX; #endif } else { assert(mp_obj_is_float(o)); - obj_type = 'f'; + obj_type = MP_PERSISTENT_OBJ_FLOAT; } vstr_t vstr; mp_print_t pr; diff --git a/py/persistentcode.h b/py/persistentcode.h index 1991ba26fff43..37263f66b3c3a 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -102,6 +102,20 @@ enum { MP_NATIVE_ARCH_XTENSAWIN, }; +enum { + MP_PERSISTENT_OBJ_FUN_TABLE = 0, + MP_PERSISTENT_OBJ_NONE, + MP_PERSISTENT_OBJ_FALSE, + MP_PERSISTENT_OBJ_TRUE, + MP_PERSISTENT_OBJ_ELLIPSIS, + MP_PERSISTENT_OBJ_STR, + MP_PERSISTENT_OBJ_BYTES, + MP_PERSISTENT_OBJ_INT, + MP_PERSISTENT_OBJ_FLOAT, + MP_PERSISTENT_OBJ_COMPLEX, + MP_PERSISTENT_OBJ_TUPLE, +}; + mp_compiled_module_t mp_raw_code_load(mp_reader_t *reader, mp_module_context_t *ctx); mp_compiled_module_t mp_raw_code_load_mem(const byte *buf, size_t len, mp_module_context_t *ctx); mp_compiled_module_t mp_raw_code_load_file(const char *filename, mp_module_context_t *ctx); diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index 6ee96b7caf293..7f9aa047e4d56 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -31,7 +31,7 @@ [ 13] \(rule\|expr_stmt\)(5) (n=2) id(h) [ 13] \(rule\|atom_expr_normal\)(44) (n=2) -[ 13] literal const(\.\+) +[ 13] literal const(\.\+)="fstring: '{}'" [ 13] \(rule\|atom_expr_trailers\)(142) (n=2) [ 13] \(rule\|trailer_period\)(50) (n=1) id(format) diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 663bacda0159d..f3ca2eb634b95 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -47,9 +47,9 @@ arg names: 42 IMPORT_STAR 43 LOAD_CONST_NONE 44 RETURN_VALUE -File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 45\[68\] bytes) -Raw bytecode (code_info_size=8\[46\], bytecode_size=372): - a8 12 9\[bf\] 03 02 60 60 26 22 24 64 22 26 25 25 24 +File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 45\[46\] bytes) +Raw bytecode (code_info_size=8\[46\], bytecode_size=370): + a8 12 9\[bf\] 03 02 60 60 26 22 24 64 22 24 25 25 24 26 23 63 22 22 25 23 23 2f 6c 25 65 25 25 69 68 26 65 27 6a 62 20 23 62 2a 29 69 24 25 28 67 26 ######## @@ -68,77 +68,77 @@ arg names: bc=12 line=10 bc=16 line=13 bc=18 line=14 - bc=24 line=15 - bc=29 line=16 - bc=34 line=17 - bc=38 line=18 - bc=44 line=19 - bc=47 line=20 - bc=50 line=23 - bc=52 line=24 - bc=54 line=25 - bc=59 line=26 - bc=62 line=27 - bc=65 line=28 - bc=80 line=29 - bc=92 line=32 - bc=97 line=33 - bc=102 line=36 - bc=107 line=37 - bc=112 line=38 - bc=121 line=41 - bc=129 line=44 - bc=135 line=45 - bc=140 line=48 - bc=147 line=49 - bc=157 line=52 - bc=159 line=55 - bc=159 line=56 - bc=162 line=57 - bc=164 line=60 - bc=174 line=61 - bc=183 line=62 - bc=192 line=65 - bc=196 line=66 - bc=201 line=67 - bc=209 line=68 - bc=216 line=71 - bc=222 line=72 - bc=229 line=73 - bc=239 line=74 - bc=247 line=77 - bc=250 line=78 - bc=255 line=80 - bc=258 line=81 - bc=260 line=82 - bc=266 line=83 - bc=268 line=84 - bc=274 line=85 - bc=279 line=88 - bc=285 line=89 - bc=289 line=92 - bc=293 line=93 - bc=295 line=94 + bc=22 line=15 + bc=27 line=16 + bc=32 line=17 + bc=36 line=18 + bc=42 line=19 + bc=45 line=20 + bc=48 line=23 + bc=50 line=24 + bc=52 line=25 + bc=57 line=26 + bc=60 line=27 + bc=63 line=28 + bc=78 line=29 + bc=90 line=32 + bc=95 line=33 + bc=100 line=36 + bc=105 line=37 + bc=110 line=38 + bc=119 line=41 + bc=127 line=44 + bc=133 line=45 + bc=138 line=48 + bc=145 line=49 + bc=155 line=52 + bc=157 line=55 + bc=157 line=56 + bc=160 line=57 + bc=162 line=60 + bc=172 line=61 + bc=181 line=62 + bc=190 line=65 + bc=194 line=66 + bc=199 line=67 + bc=207 line=68 + bc=214 line=71 + bc=220 line=72 + bc=227 line=73 + bc=237 line=74 + bc=245 line=77 + bc=248 line=78 + bc=253 line=80 + bc=256 line=81 + bc=258 line=82 + bc=264 line=83 + bc=266 line=84 + bc=272 line=85 + bc=277 line=88 + bc=283 line=89 + bc=287 line=92 + bc=291 line=93 + bc=293 line=94 ######## - bc=303 line=96 - bc=310 line=98 - bc=313 line=99 - bc=315 line=100 - bc=317 line=101 + bc=301 line=96 + bc=308 line=98 + bc=311 line=99 + bc=313 line=100 + bc=315 line=101 ######## - bc=323 line=103 - bc=329 line=106 - bc=333 line=107 - bc=339 line=110 - bc=342 line=111 - bc=348 line=114 - bc=348 line=117 - bc=353 line=118 - bc=365 line=121 - bc=365 line=122 - bc=366 line=123 - bc=368 line=126 - bc=370 line=127 + bc=321 line=103 + bc=327 line=106 + bc=331 line=107 + bc=337 line=110 + bc=340 line=111 + bc=346 line=114 + bc=346 line=117 + bc=351 line=118 + bc=363 line=121 + bc=363 line=122 + bc=364 line=123 + bc=366 line=126 + bc=368 line=127 00 LOAD_CONST_NONE 01 LOAD_CONST_FALSE 02 BINARY_OP 27 __add__ @@ -153,258 +153,256 @@ arg names: 15 STORE_FAST 0 16 LOAD_CONST_SMALL_INT 1 17 STORE_FAST 0 -18 LOAD_CONST_SMALL_INT 1 -19 LOAD_CONST_SMALL_INT 2 -20 BUILD_TUPLE 2 -22 STORE_DEREF 14 -24 LOAD_CONST_SMALL_INT 1 -25 LOAD_CONST_SMALL_INT 2 -26 BUILD_LIST 2 -28 STORE_FAST 1 -29 LOAD_CONST_SMALL_INT 1 -30 LOAD_CONST_SMALL_INT 2 -31 BUILD_SET 2 -33 STORE_FAST 2 -34 BUILD_MAP 0 -36 STORE_DEREF 15 -38 BUILD_MAP 1 -40 LOAD_CONST_SMALL_INT 2 -41 LOAD_CONST_SMALL_INT 1 -42 STORE_MAP -43 STORE_FAST 3 -44 LOAD_CONST_STRING 'a' -46 STORE_FAST 4 -47 LOAD_CONST_OBJ \.\+=b'a' -49 STORE_FAST 5 -50 LOAD_CONST_SMALL_INT 1 -51 STORE_FAST 6 -52 LOAD_CONST_SMALL_INT 2 -53 STORE_FAST 7 -54 LOAD_FAST 0 -55 LOAD_DEREF 14 -57 BINARY_OP 27 __add__ -58 STORE_FAST 8 -59 LOAD_FAST 0 -60 UNARY_OP 1 __neg__ -61 STORE_FAST 9 -62 LOAD_FAST 0 -63 UNARY_OP 3 -64 STORE_FAST 10 -65 LOAD_FAST 0 -66 LOAD_DEREF 14 -68 DUP_TOP -69 ROT_THREE -70 BINARY_OP 2 __eq__ -71 JUMP_IF_FALSE_OR_POP 77 -73 LOAD_FAST 1 -74 BINARY_OP 2 __eq__ -75 JUMP 79 -77 ROT_TWO -78 POP_TOP -79 STORE_FAST 10 -80 LOAD_FAST 0 -81 LOAD_DEREF 14 -83 BINARY_OP 2 __eq__ -84 JUMP_IF_FALSE_OR_POP 90 -86 LOAD_DEREF 14 -88 LOAD_FAST 1 -89 BINARY_OP 2 __eq__ -90 UNARY_OP 3 -91 STORE_FAST 10 -92 LOAD_DEREF 14 -94 LOAD_ATTR c -96 STORE_FAST 11 -97 LOAD_FAST 11 -98 LOAD_DEREF 14 -100 STORE_ATTR c -102 LOAD_DEREF 14 -104 LOAD_CONST_SMALL_INT 0 -105 LOAD_SUBSCR -106 STORE_FAST 12 -107 LOAD_FAST 12 -108 LOAD_DEREF 14 -110 LOAD_CONST_SMALL_INT 0 -111 STORE_SUBSCR -112 LOAD_DEREF 14 -114 LOAD_CONST_SMALL_INT 0 -115 DUP_TOP_TWO -116 LOAD_SUBSCR -117 LOAD_FAST 12 -118 BINARY_OP 14 __iadd__ -119 ROT_THREE -120 STORE_SUBSCR -121 LOAD_DEREF 14 -123 LOAD_CONST_NONE -124 LOAD_CONST_NONE -125 BUILD_SLICE 2 -127 LOAD_SUBSCR -128 STORE_FAST 0 -129 LOAD_FAST 1 -130 UNPACK_SEQUENCE 2 -132 STORE_FAST 0 -133 STORE_DEREF 14 -135 LOAD_FAST 0 -136 UNPACK_EX 1 -138 STORE_FAST 0 -139 STORE_FAST 0 -140 LOAD_DEREF 14 -142 LOAD_FAST 0 -143 ROT_TWO -144 STORE_FAST 0 -145 STORE_DEREF 14 -147 LOAD_FAST 1 -148 LOAD_DEREF 14 -150 LOAD_FAST 0 -151 ROT_THREE -152 ROT_TWO -153 STORE_FAST 0 -154 STORE_DEREF 14 -156 STORE_FAST 1 -157 DELETE_FAST 0 -159 LOAD_FAST 0 -160 STORE_GLOBAL gl -162 DELETE_GLOBAL gl -164 LOAD_FAST 14 -165 LOAD_FAST 15 -166 MAKE_CLOSURE \.\+ 2 -169 LOAD_FAST 2 -170 GET_ITER -171 CALL_FUNCTION n=1 nkw=0 -173 STORE_FAST 0 -174 LOAD_FAST 14 -175 LOAD_FAST 15 -176 MAKE_CLOSURE \.\+ 2 -179 LOAD_FAST 2 -180 CALL_FUNCTION n=1 nkw=0 -182 STORE_FAST 0 -183 LOAD_FAST 14 -184 LOAD_FAST 15 -185 MAKE_CLOSURE \.\+ 2 -188 LOAD_FAST 2 -189 CALL_FUNCTION n=1 nkw=0 -191 STORE_FAST 0 -192 LOAD_FAST 0 -193 CALL_FUNCTION n=0 nkw=0 -195 POP_TOP -196 LOAD_FAST 0 -197 LOAD_CONST_SMALL_INT 1 -198 CALL_FUNCTION n=1 nkw=0 -200 POP_TOP -201 LOAD_FAST 0 -202 LOAD_CONST_STRING 'b' -204 LOAD_CONST_SMALL_INT 1 -205 CALL_FUNCTION n=0 nkw=1 -208 POP_TOP -209 LOAD_FAST 0 -210 LOAD_DEREF 14 -212 LOAD_CONST_SMALL_INT 1 -213 CALL_FUNCTION_VAR_KW n=1 nkw=0 -215 POP_TOP -216 LOAD_FAST 0 -217 LOAD_METHOD b -219 CALL_METHOD n=0 nkw=0 -221 POP_TOP -222 LOAD_FAST 0 -223 LOAD_METHOD b -225 LOAD_CONST_SMALL_INT 1 -226 CALL_METHOD n=1 nkw=0 -228 POP_TOP -229 LOAD_FAST 0 -230 LOAD_METHOD b -232 LOAD_CONST_STRING 'c' -234 LOAD_CONST_SMALL_INT 1 -235 CALL_METHOD n=0 nkw=1 -238 POP_TOP -239 LOAD_FAST 0 -240 LOAD_METHOD b -242 LOAD_FAST 1 -243 LOAD_CONST_SMALL_INT 1 -244 CALL_METHOD_VAR_KW n=1 nkw=0 -246 POP_TOP -247 LOAD_FAST 0 -248 POP_JUMP_IF_FALSE 255 -250 LOAD_DEREF 16 -252 POP_TOP -253 JUMP 258 -255 LOAD_GLOBAL y -257 POP_TOP -258 JUMP 263 -260 LOAD_DEREF 14 -262 POP_TOP -263 LOAD_FAST 0 -264 POP_JUMP_IF_TRUE 260 -266 JUMP 271 -268 LOAD_DEREF 14 -270 POP_TOP -271 LOAD_FAST 0 -272 POP_JUMP_IF_FALSE 268 -274 LOAD_FAST 0 -275 JUMP_IF_TRUE_OR_POP 278 -277 LOAD_FAST 0 -278 STORE_FAST 0 -279 LOAD_DEREF 14 -281 GET_ITER_STACK -282 FOR_ITER 289 -284 STORE_FAST 0 -285 LOAD_FAST 1 -286 POP_TOP -287 JUMP 282 -289 SETUP_FINALLY 310 -291 SETUP_EXCEPT 302 -293 JUMP 297 -295 JUMP 300 -297 LOAD_FAST 0 -298 POP_JUMP_IF_TRUE 295 -300 POP_EXCEPT_JUMP 309 -302 POP_TOP -303 LOAD_DEREF 14 -305 POP_TOP -306 POP_EXCEPT_JUMP 309 -308 END_FINALLY -309 LOAD_CONST_NONE -310 LOAD_FAST 1 -311 POP_TOP -312 END_FINALLY -313 JUMP 326 -315 SETUP_EXCEPT 322 -317 UNWIND_JUMP 329 1 -320 POP_EXCEPT_JUMP 326 -322 POP_TOP -323 POP_EXCEPT_JUMP 326 -325 END_FINALLY -326 LOAD_FAST 0 -327 POP_JUMP_IF_TRUE 315 -329 LOAD_FAST 0 -330 SETUP_WITH 337 -332 POP_TOP -333 LOAD_DEREF 14 -335 POP_TOP -336 LOAD_CONST_NONE -337 WITH_CLEANUP -338 END_FINALLY -339 LOAD_CONST_SMALL_INT 1 -340 STORE_DEREF 16 -342 LOAD_FAST_N 16 -344 MAKE_CLOSURE \.\+ 1 -347 STORE_FAST 13 -348 LOAD_CONST_SMALL_INT 0 -349 LOAD_CONST_NONE -350 IMPORT_NAME 'a' -352 STORE_FAST 0 -353 LOAD_CONST_SMALL_INT 0 -354 LOAD_CONST_STRING 'b' -356 BUILD_TUPLE 1 -358 IMPORT_NAME 'a' -360 IMPORT_FROM 'b' -362 STORE_DEREF 14 -364 POP_TOP -365 RAISE_LAST -366 LOAD_CONST_SMALL_INT 1 -367 RAISE_OBJ -368 LOAD_CONST_NONE +18 LOAD_CONST_OBJ \.\+=(1, 2) +20 STORE_DEREF 14 +22 LOAD_CONST_SMALL_INT 1 +23 LOAD_CONST_SMALL_INT 2 +24 BUILD_LIST 2 +26 STORE_FAST 1 +27 LOAD_CONST_SMALL_INT 1 +28 LOAD_CONST_SMALL_INT 2 +29 BUILD_SET 2 +31 STORE_FAST 2 +32 BUILD_MAP 0 +34 STORE_DEREF 15 +36 BUILD_MAP 1 +38 LOAD_CONST_SMALL_INT 2 +39 LOAD_CONST_SMALL_INT 1 +40 STORE_MAP +41 STORE_FAST 3 +42 LOAD_CONST_STRING 'a' +44 STORE_FAST 4 +45 LOAD_CONST_OBJ \.\+=b'a' +47 STORE_FAST 5 +48 LOAD_CONST_SMALL_INT 1 +49 STORE_FAST 6 +50 LOAD_CONST_SMALL_INT 2 +51 STORE_FAST 7 +52 LOAD_FAST 0 +53 LOAD_DEREF 14 +55 BINARY_OP 27 __add__ +56 STORE_FAST 8 +57 LOAD_FAST 0 +58 UNARY_OP 1 __neg__ +59 STORE_FAST 9 +60 LOAD_FAST 0 +61 UNARY_OP 3 +62 STORE_FAST 10 +63 LOAD_FAST 0 +64 LOAD_DEREF 14 +66 DUP_TOP +67 ROT_THREE +68 BINARY_OP 2 __eq__ +69 JUMP_IF_FALSE_OR_POP 75 +71 LOAD_FAST 1 +72 BINARY_OP 2 __eq__ +73 JUMP 77 +75 ROT_TWO +76 POP_TOP +77 STORE_FAST 10 +78 LOAD_FAST 0 +79 LOAD_DEREF 14 +81 BINARY_OP 2 __eq__ +82 JUMP_IF_FALSE_OR_POP 88 +84 LOAD_DEREF 14 +86 LOAD_FAST 1 +87 BINARY_OP 2 __eq__ +88 UNARY_OP 3 +89 STORE_FAST 10 +90 LOAD_DEREF 14 +92 LOAD_ATTR c +94 STORE_FAST 11 +95 LOAD_FAST 11 +96 LOAD_DEREF 14 +98 STORE_ATTR c +100 LOAD_DEREF 14 +102 LOAD_CONST_SMALL_INT 0 +103 LOAD_SUBSCR +104 STORE_FAST 12 +105 LOAD_FAST 12 +106 LOAD_DEREF 14 +108 LOAD_CONST_SMALL_INT 0 +109 STORE_SUBSCR +110 LOAD_DEREF 14 +112 LOAD_CONST_SMALL_INT 0 +113 DUP_TOP_TWO +114 LOAD_SUBSCR +115 LOAD_FAST 12 +116 BINARY_OP 14 __iadd__ +117 ROT_THREE +118 STORE_SUBSCR +119 LOAD_DEREF 14 +121 LOAD_CONST_NONE +122 LOAD_CONST_NONE +123 BUILD_SLICE 2 +125 LOAD_SUBSCR +126 STORE_FAST 0 +127 LOAD_FAST 1 +128 UNPACK_SEQUENCE 2 +130 STORE_FAST 0 +131 STORE_DEREF 14 +133 LOAD_FAST 0 +134 UNPACK_EX 1 +136 STORE_FAST 0 +137 STORE_FAST 0 +138 LOAD_DEREF 14 +140 LOAD_FAST 0 +141 ROT_TWO +142 STORE_FAST 0 +143 STORE_DEREF 14 +145 LOAD_FAST 1 +146 LOAD_DEREF 14 +148 LOAD_FAST 0 +149 ROT_THREE +150 ROT_TWO +151 STORE_FAST 0 +152 STORE_DEREF 14 +154 STORE_FAST 1 +155 DELETE_FAST 0 +157 LOAD_FAST 0 +158 STORE_GLOBAL gl +160 DELETE_GLOBAL gl +162 LOAD_FAST 14 +163 LOAD_FAST 15 +164 MAKE_CLOSURE \.\+ 2 +167 LOAD_FAST 2 +168 GET_ITER +169 CALL_FUNCTION n=1 nkw=0 +171 STORE_FAST 0 +172 LOAD_FAST 14 +173 LOAD_FAST 15 +174 MAKE_CLOSURE \.\+ 2 +177 LOAD_FAST 2 +178 CALL_FUNCTION n=1 nkw=0 +180 STORE_FAST 0 +181 LOAD_FAST 14 +182 LOAD_FAST 15 +183 MAKE_CLOSURE \.\+ 2 +186 LOAD_FAST 2 +187 CALL_FUNCTION n=1 nkw=0 +189 STORE_FAST 0 +190 LOAD_FAST 0 +191 CALL_FUNCTION n=0 nkw=0 +193 POP_TOP +194 LOAD_FAST 0 +195 LOAD_CONST_SMALL_INT 1 +196 CALL_FUNCTION n=1 nkw=0 +198 POP_TOP +199 LOAD_FAST 0 +200 LOAD_CONST_STRING 'b' +202 LOAD_CONST_SMALL_INT 1 +203 CALL_FUNCTION n=0 nkw=1 +206 POP_TOP +207 LOAD_FAST 0 +208 LOAD_DEREF 14 +210 LOAD_CONST_SMALL_INT 1 +211 CALL_FUNCTION_VAR_KW n=1 nkw=0 +213 POP_TOP +214 LOAD_FAST 0 +215 LOAD_METHOD b +217 CALL_METHOD n=0 nkw=0 +219 POP_TOP +220 LOAD_FAST 0 +221 LOAD_METHOD b +223 LOAD_CONST_SMALL_INT 1 +224 CALL_METHOD n=1 nkw=0 +226 POP_TOP +227 LOAD_FAST 0 +228 LOAD_METHOD b +230 LOAD_CONST_STRING 'c' +232 LOAD_CONST_SMALL_INT 1 +233 CALL_METHOD n=0 nkw=1 +236 POP_TOP +237 LOAD_FAST 0 +238 LOAD_METHOD b +240 LOAD_FAST 1 +241 LOAD_CONST_SMALL_INT 1 +242 CALL_METHOD_VAR_KW n=1 nkw=0 +244 POP_TOP +245 LOAD_FAST 0 +246 POP_JUMP_IF_FALSE 253 +248 LOAD_DEREF 16 +250 POP_TOP +251 JUMP 256 +253 LOAD_GLOBAL y +255 POP_TOP +256 JUMP 261 +258 LOAD_DEREF 14 +260 POP_TOP +261 LOAD_FAST 0 +262 POP_JUMP_IF_TRUE 258 +264 JUMP 269 +266 LOAD_DEREF 14 +268 POP_TOP +269 LOAD_FAST 0 +270 POP_JUMP_IF_FALSE 266 +272 LOAD_FAST 0 +273 JUMP_IF_TRUE_OR_POP 276 +275 LOAD_FAST 0 +276 STORE_FAST 0 +277 LOAD_DEREF 14 +279 GET_ITER_STACK +280 FOR_ITER 287 +282 STORE_FAST 0 +283 LOAD_FAST 1 +284 POP_TOP +285 JUMP 280 +287 SETUP_FINALLY 308 +289 SETUP_EXCEPT 300 +291 JUMP 295 +293 JUMP 298 +295 LOAD_FAST 0 +296 POP_JUMP_IF_TRUE 293 +298 POP_EXCEPT_JUMP 307 +300 POP_TOP +301 LOAD_DEREF 14 +303 POP_TOP +304 POP_EXCEPT_JUMP 307 +306 END_FINALLY +307 LOAD_CONST_NONE +308 LOAD_FAST 1 +309 POP_TOP +310 END_FINALLY +311 JUMP 324 +313 SETUP_EXCEPT 320 +315 UNWIND_JUMP 327 1 +318 POP_EXCEPT_JUMP 324 +320 POP_TOP +321 POP_EXCEPT_JUMP 324 +323 END_FINALLY +324 LOAD_FAST 0 +325 POP_JUMP_IF_TRUE 313 +327 LOAD_FAST 0 +328 SETUP_WITH 335 +330 POP_TOP +331 LOAD_DEREF 14 +333 POP_TOP +334 LOAD_CONST_NONE +335 WITH_CLEANUP +336 END_FINALLY +337 LOAD_CONST_SMALL_INT 1 +338 STORE_DEREF 16 +340 LOAD_FAST_N 16 +342 MAKE_CLOSURE \.\+ 1 +345 STORE_FAST 13 +346 LOAD_CONST_SMALL_INT 0 +347 LOAD_CONST_NONE +348 IMPORT_NAME 'a' +350 STORE_FAST 0 +351 LOAD_CONST_SMALL_INT 0 +352 LOAD_CONST_STRING 'b' +354 BUILD_TUPLE 1 +356 IMPORT_NAME 'a' +358 IMPORT_FROM 'b' +360 STORE_DEREF 14 +362 POP_TOP +363 RAISE_LAST +364 LOAD_CONST_SMALL_INT 1 +365 RAISE_OBJ +366 LOAD_CONST_NONE +367 RETURN_VALUE +368 LOAD_CONST_SMALL_INT 1 369 RETURN_VALUE -370 LOAD_CONST_SMALL_INT 1 -371 RETURN_VALUE File cmdline/cmd_showbc.py, code block 'f' (descriptor: \.\+, bytecode @\.\+ 59 bytes) Raw bytecode (code_info_size=8, bytecode_size=51): a8 10 0a 02 80 82 34 38 81 57 c0 57 c1 57 c2 57 diff --git a/tests/micropython/heapalloc.py b/tests/micropython/heapalloc.py index 99f157105daa9..e19f8d0255deb 100644 --- a/tests/micropython/heapalloc.py +++ b/tests/micropython/heapalloc.py @@ -33,6 +33,10 @@ def f3(a, b, c, d): print(x1, x3, x5, x7, x2 + x4 + x6 + x8) +def f4(): + return True, b"bytes", () + + global_var = 1 @@ -45,7 +49,11 @@ def test(): f1(a=i) # keyword arguments f2(i) # default arg (second one) f2(i, i) # 2 args + f1((1, "two", (b"three",))) # use a constant tuple f3(1, 2, 3, 4) # function with lots of local state + for i in 1, "two": # iterate over constant tuple + print(i) + print(f4()) # returns a constant tuple # call test() with heap allocation disabled diff --git a/tests/micropython/heapalloc.py.exp b/tests/micropython/heapalloc.py.exp index c8cffe183f074..b8580edc61c05 100644 --- a/tests/micropython/heapalloc.py.exp +++ b/tests/micropython/heapalloc.py.exp @@ -8,4 +8,8 @@ 1 1 2 1 1 +(1, 'two', (b'three',)) 1 2 3 4 10 +1 +two +(True, b'bytes', ()) diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 74caf20a38dca..a807aa6d7739c 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -23,7 +23,7 @@ def f(): x = ("const tuple", None, False, True, 1, 2, 3) result = 123 """ -file_data = b'M\x06\x02\x1f\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00s\x1cthis will be a string object\x00b\x1bthis will be a bytes object\x00s\x0bconst tuple\x00\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x83\x008\x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02QPR\x81\x82\x83*\x07\xc0Qc' +file_data = b'M\x06\x02\x1f\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00\x05\x1cthis will be a string object\x00\x06\x1bthis will be a bytes object\x00\n\x07\x05\x0bconst tuple\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x82@ \x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02\xc0Qc' class File(uio.IOBase): diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py index 1e8a4f531e3d3..87ad995aa6f42 100644 --- a/tests/perf_bench/core_import_mpy_single.py +++ b/tests/perf_bench/core_import_mpy_single.py @@ -78,7 +78,7 @@ def f1(): x = ("const tuple 9", None, False, True, 1, 2, 3) result = 123 """ -file_data = b"M\x06\x02\x1f\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00s\x1ethis will be a string object 0\x00s\x1ethis will be a string object 1\x00s\x1ethis will be a string object 2\x00s\x1ethis will be a string object 3\x00s\x1ethis will be a string object 4\x00s\x1ethis will be a string object 5\x00s\x1ethis will be a string object 6\x00s\x1ethis will be a string object 7\x00s\x1ethis will be a string object 8\x00s\x1ethis will be a string object 9\x00b\x1dthis will be a bytes object 0\x00b\x1dthis will be a bytes object 1\x00b\x1dthis will be a bytes object 2\x00b\x1dthis will be a bytes object 3\x00b\x1dthis will be a bytes object 4\x00b\x1dthis will be a bytes object 5\x00b\x1dthis will be a bytes object 6\x00b\x1dthis will be a bytes object 7\x00b\x1dthis will be a bytes object 8\x00b\x1dthis will be a bytes object 9\x00s\rconst tuple 0\x00s\rconst tuple 1\x00s\rconst tuple 2\x00s\rconst tuple 3\x00s\rconst tuple 4\x00s\rconst tuple 5\x00s\rconst tuple 6\x00s\rconst tuple 7\x00s\rconst tuple 8\x00s\rconst tuple 9\x00\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x8cp8@\x05\x80#####################+++++++++#\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14QPR\x81\x82\x83*\x07\xc0#\x15QPR\x81\x82\x83*\x07\xc0#\x16QPR\x81\x82\x83*\x07\xc0#\x17QPR\x81\x82\x83*\x07\xc0#\x18QPR\x81\x82\x83*\x07\xc0#\x19QPR\x81\x82\x83*\x07\xc0#\x1aQPR\x81\x82\x83*\x07\xc0#\x1bQPR\x81\x82\x83*\x07\xc0#\x1cQPR\x81\x82\x83*\x07\xc0#\x1dQPR\x81\x82\x83*\x07\xc0Qc" +file_data = b"M\x06\x02\x1f\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00\x05\x1ethis will be a string object 0\x00\x05\x1ethis will be a string object 1\x00\x05\x1ethis will be a string object 2\x00\x05\x1ethis will be a string object 3\x00\x05\x1ethis will be a string object 4\x00\x05\x1ethis will be a string object 5\x00\x05\x1ethis will be a string object 6\x00\x05\x1ethis will be a string object 7\x00\x05\x1ethis will be a string object 8\x00\x05\x1ethis will be a string object 9\x00\x06\x1dthis will be a bytes object 0\x00\x06\x1dthis will be a bytes object 1\x00\x06\x1dthis will be a bytes object 2\x00\x06\x1dthis will be a bytes object 3\x00\x06\x1dthis will be a bytes object 4\x00\x06\x1dthis will be a bytes object 5\x00\x06\x1dthis will be a bytes object 6\x00\x06\x1dthis will be a bytes object 7\x00\x06\x1dthis will be a bytes object 8\x00\x06\x1dthis will be a bytes object 9\x00\n\x07\x05\rconst tuple 0\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 1\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 2\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 3\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 4\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 5\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 6\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 7\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 8\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 9\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x87p\x08@\x05\x80###############################\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14\xc0#\x15\xc0#\x16\xc0#\x17\xc0#\x18\xc0#\x19\xc0#\x1a\xc0#\x1b\xc0#\x1c\xc0#\x1d\xc0Qc" class File(uio.IOBase): diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 84c09a0c688d2..3ebbdd11042ab 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -62,6 +62,11 @@ def hexlify_to_str(b): sys.path.append(sys.path[0] + "/../py") import makeqstrdata as qstrutil +# Threshold of str length below which it will be turned into a qstr when freezing. +# This helps to reduce frozen code size because qstrs are more efficient to encode +# as objects than full mp_obj_str_t instances. +PERSISTENT_STR_INTERN_THRESHOLD = 25 + class MPYReadError(Exception): def __init__(self, filename, msg): @@ -91,19 +96,6 @@ class Config: config = Config() -class QStrType: - def __init__(self, str): - self.str = str - self.qstr_esc = qstrutil.qstr_escape(self.str) - self.qstr_id = "MP_QSTR_" + self.qstr_esc - - -# Initialise global list of qstrs with static qstrs -global_qstrs = [None] # MP_QSTRnull should never be referenced -for n in qstrutil.static_qstr_list: - global_qstrs.append(QStrType(n)) - - MP_CODE_BYTECODE = 2 MP_CODE_NATIVE_PY = 3 MP_CODE_NATIVE_VIPER = 4 @@ -121,6 +113,18 @@ def __init__(self, str): MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 +MP_PERSISTENT_OBJ_FUN_TABLE = 0 +MP_PERSISTENT_OBJ_NONE = 1 +MP_PERSISTENT_OBJ_FALSE = 2 +MP_PERSISTENT_OBJ_TRUE = 3 +MP_PERSISTENT_OBJ_ELLIPSIS = 4 +MP_PERSISTENT_OBJ_STR = 5 +MP_PERSISTENT_OBJ_BYTES = 6 +MP_PERSISTENT_OBJ_INT = 7 +MP_PERSISTENT_OBJ_FLOAT = 8 +MP_PERSISTENT_OBJ_COMPLEX = 9 +MP_PERSISTENT_OBJ_TUPLE = 10 + MP_SCOPE_FLAG_VIPERRELOC = 0x10 MP_SCOPE_FLAG_VIPERRODATA = 0x20 MP_SCOPE_FLAG_VIPERBSS = 0x40 @@ -315,6 +319,13 @@ class Opcodes: mapping[MP_BC_BINARY_OP_MULTI + i] = "BINARY_OP %d %s" % (i, mp_binary_op_method_name[i]) +# This definition of a small int covers all possible targets, in the sense that every +# target can encode as a small int, an integer that passes this test. The minimum is set +# by MICROPY_OBJ_REPR_B on a 16-bit machine, where there are 14 bits for the small int. +def mp_small_int_fits(i): + return -0x2000 <= i <= 0x1FFF + + # this function mirrors that in py/bc.c def mp_opcode_format(bytecode, ip, count_var_uint): opcode = bytecode[ip] @@ -450,6 +461,35 @@ def local_read_byte(): ) +class QStrType: + def __init__(self, str): + self.str = str + self.qstr_esc = qstrutil.qstr_escape(self.str) + self.qstr_id = "MP_QSTR_" + self.qstr_esc + + +class GlobalQStrList: + def __init__(self): + # Initialise global list of qstrs with static qstrs + self.qstrs = [None] # MP_QSTRnull should never be referenced + for n in qstrutil.static_qstr_list: + self.qstrs.append(QStrType(n)) + + def add(self, s): + q = QStrType(s) + self.qstrs.append(q) + return q + + def get_by_index(self, i): + return self.qstrs[i] + + def find_by_str(self, s): + for q in self.qstrs: + if q is not None and q.str == s: + return q + return None + + class MPFunTable: def __repr__(self): return "mp_fun_table" @@ -477,10 +517,6 @@ def __init__( self.raw_code = raw_code self.escaped_name = escaped_name - def _unpack_qstr(self, ip): - qst = self.bytecode[ip] | self.bytecode[ip + 1] << 8 - return global_qstrs[qst] - def hexdump(self): with open(self.mpy_source_file, "rb") as f: WIDTH = 16 @@ -588,9 +624,123 @@ def freeze(self, compiled_module_index): print(" .rc = &raw_code_%s," % self.raw_code.escaped_name) print("};") - def freeze_constants(self): + def freeze_constant_obj(self, obj_name, obj): global const_str_content, const_int_content, const_obj_content + if isinstance(obj, MPFunTable): + return "&mp_fun_table" + elif obj is None: + return "MP_ROM_NONE" + elif obj is False: + return "MP_ROM_FALSE" + elif obj is True: + return "MP_ROM_TRUE" + elif obj is Ellipsis: + return "MP_ROM_PTR(&mp_const_ellipsis_obj)" + elif is_str_type(obj) or is_bytes_type(obj): + if len(obj) == 0: + if is_str_type(obj): + return "MP_ROM_QSTR(MP_QSTR_)" + else: + return "MP_ROM_PTR(&mp_const_empty_bytes_obj)" + if is_str_type(obj): + q = global_qstrs.find_by_str(obj) + if q: + return "MP_ROM_QSTR(%s)" % q.qstr_id + obj = bytes_cons(obj, "utf8") + obj_type = "mp_type_str" + else: + obj_type = "mp_type_bytes" + print( + 'static const mp_obj_str_t %s = {{&%s}, %u, %u, (const byte*)"%s"};' + % ( + obj_name, + obj_type, + qstrutil.compute_hash(obj, config.MICROPY_QSTR_BYTES_IN_HASH), + len(obj), + "".join(("\\x%02x" % b) for b in obj), + ) + ) + const_str_content += len(obj) + const_obj_content += 4 * 4 + return "MP_ROM_PTR(&%s)" % obj_name + elif is_int_type(obj): + if mp_small_int_fits(obj): + # Encode directly as a small integer object. + return "MP_ROM_INT(%d)" % obj + elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE: + raise FreezeError(self, "target does not support long int") + elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_LONGLONG: + # TODO + raise FreezeError(self, "freezing int to long-long is not implemented") + elif config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_MPZ: + neg = 0 + if obj < 0: + obj = -obj + neg = 1 + bits_per_dig = config.MPZ_DIG_SIZE + digs = [] + z = obj + while z: + digs.append(z & ((1 << bits_per_dig) - 1)) + z >>= bits_per_dig + ndigs = len(digs) + digs = ",".join(("%#x" % d) for d in digs) + print( + "static const mp_obj_int_t %s = {{&mp_type_int}, " + "{.neg=%u, .fixed_dig=1, .alloc=%u, .len=%u, .dig=(uint%u_t*)(const uint%u_t[]){%s}}};" + % (obj_name, neg, ndigs, ndigs, bits_per_dig, bits_per_dig, digs) + ) + const_int_content += (digs.count(",") + 1) * bits_per_dig // 8 + const_obj_content += 4 * 4 + return "MP_ROM_PTR(&%s)" % obj_name + elif type(obj) is float: + macro_name = "%s_macro" % obj_name + print( + "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B" + ) + print( + "static const mp_obj_float_t %s = {{&mp_type_float}, (mp_float_t)%.16g};" + % (obj_name, obj) + ) + print("#define %s MP_ROM_PTR(&%s)" % (macro_name, obj_name)) + print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C") + n = struct.unpack(">= bits_per_dig - ndigs = len(digs) - digs = ",".join(("%#x" % d) for d in digs) - print( - "static const mp_obj_int_t %s = {{&mp_type_int}, " - "{.neg=%u, .fixed_dig=1, .alloc=%u, .len=%u, .dig=(uint%u_t*)(const uint%u_t[]){%s}}};" - % (obj_name, neg, ndigs, ndigs, bits_per_dig, bits_per_dig, digs) - ) - const_int_content += (digs.count(",") + 1) * bits_per_dig // 8 - const_obj_content += 4 * 4 - elif type(obj) is float: - print( - "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B" - ) - print( - "static const mp_obj_float_t %s = {{&mp_type_float}, (mp_float_t)%.16g};" - % (obj_name, obj) - ) - print("#endif") - const_obj_content += 3 * 4 - elif type(obj) is complex: - print( - "static const mp_obj_complex_t %s = {{&mp_type_complex}, (mp_float_t)%.16g, (mp_float_t)%.16g};" - % (obj_name, obj.real, obj.imag) - ) - else: - raise FreezeError(self, "freezing of object %r is not implemented" % (obj,)) + obj_refs.append(self.freeze_constant_obj(obj_name, obj)) # generate constant table print() @@ -682,25 +768,8 @@ def freeze_constants(self): "static const mp_rom_obj_t const_obj_table_data_%s[%u] = {" % (self.escaped_name, len(self.obj_table)) ) - for i in range(len(self.obj_table)): - if isinstance(self.obj_table[i], MPFunTable): - print(" &mp_fun_table,") - elif type(self.obj_table[i]) is float: - print( - "#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B" - ) - print(" MP_ROM_PTR(&const_obj_%s_%u)," % (self.escaped_name, i)) - print("#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C") - n = struct.unpack("> 1].str, start_pos, start_pos) - ) - return ln >> 1 + q = global_qstrs.get_by_index(ln >> 1) + segments.append(MPYSegment(MPYSegment.META, q.str, start_pos, start_pos)) + return q ln >>= 1 start_pos = reader.tell() data = str_cons(reader.read_bytes(ln), "utf8") reader.read_byte() # read and discard null terminator segments.append(MPYSegment(MPYSegment.QSTR, data, start_pos, reader.tell())) - global_qstrs.append(QStrType(data)) - return len(global_qstrs) - 1 + return global_qstrs.add(data) def read_obj(reader, segments): - obj_type = reader.read_bytes(1) - if obj_type == b"t": + obj_type = reader.read_byte() + if obj_type == MP_PERSISTENT_OBJ_FUN_TABLE: return MPFunTable() - elif obj_type == b"e": + elif obj_type == MP_PERSISTENT_OBJ_NONE: + return None + elif obj_type == MP_PERSISTENT_OBJ_FALSE: + return False + elif obj_type == MP_PERSISTENT_OBJ_TRUE: + return True + elif obj_type == MP_PERSISTENT_OBJ_ELLIPSIS: return Ellipsis + elif obj_type == MP_PERSISTENT_OBJ_TUPLE: + ln = reader.read_uint() + return tuple(read_obj(reader, segments) for _ in range(ln)) else: ln = reader.read_uint() start_pos = reader.tell() buf = reader.read_bytes(ln) - if obj_type in (b"s", b"b"): + if obj_type in (MP_PERSISTENT_OBJ_STR, MP_PERSISTENT_OBJ_BYTES): reader.read_byte() # read and discard null terminator - if obj_type == b"s": + if obj_type == MP_PERSISTENT_OBJ_STR: obj = str_cons(buf, "utf8") - elif obj_type == b"b": + if len(obj) < PERSISTENT_STR_INTERN_THRESHOLD: + if not global_qstrs.find_by_str(obj): + global_qstrs.add(obj) + elif obj_type == MP_PERSISTENT_OBJ_BYTES: obj = buf - elif obj_type == b"i": + elif obj_type == MP_PERSISTENT_OBJ_INT: obj = int(str_cons(buf, "ascii"), 10) - elif obj_type == b"f": + elif obj_type == MP_PERSISTENT_OBJ_FLOAT: obj = float(str_cons(buf, "ascii")) - elif obj_type == b"c": + elif obj_type == MP_PERSISTENT_OBJ_COMPLEX: obj = complex(str_cons(buf, "ascii")) else: raise MPYReadError(reader.filename, "corrupt .mpy file") @@ -1246,8 +1324,7 @@ def read_mpy(filename): # Read qstrs and construct qstr table. qstr_table = [] for i in range(n_qstr): - q = read_qstr(reader, segments) - qstr_table.append(global_qstrs[q]) + qstr_table.append(read_qstr(reader, segments)) # Read objects and construct object table. obj_table = [] @@ -1287,7 +1364,7 @@ def disassemble_mpy(compiled_modules): def freeze_mpy(base_qstrs, compiled_modules): # add to qstrs new = {} - for q in global_qstrs: + for q in global_qstrs.qstrs: # don't add duplicates if q is None or q.qstr_esc in base_qstrs or q.qstr_esc in new: continue @@ -1529,6 +1606,8 @@ def merge_mpy(raw_codes, output_file): def main(): + global global_qstrs + import argparse cmd_parser = argparse.ArgumentParser(description="A tool to work with MicroPython .mpy files.") @@ -1579,6 +1658,9 @@ def main(): config.MICROPY_QSTR_BYTES_IN_HASH = 1 base_qstrs = list(qstrutil.static_qstr_list) + # Create initial list of global qstrs. + global_qstrs = GlobalQStrList() + # Load all .mpy files. try: compiled_modules = [read_mpy(file) for file in args.files]