From 42d0bd2c17fd76860c3dc0ef5c62faa48efeb121 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 7 Apr 2022 22:18:37 +1000 Subject: [PATCH 01/17] py/persistentcode: Define enum values for obj types instead of letters. To keep the separate parts of the code that use these values in sync. And make it easier to add new object types. Signed-off-by: Damien George --- py/persistentcode.c | 30 +++++++++++++++--------------- py/persistentcode.h | 10 ++++++++++ tools/mpy-tool.py | 26 +++++++++++++++++--------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/py/persistentcode.c b/py/persistentcode.c index f64e383a61690..c9cfc3b5c285c 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -198,29 +198,29 @@ 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_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; } 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,16 +498,16 @@ 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); @@ -515,21 +515,21 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) { mp_print_uint(print, len); mp_print_bytes(print, (const byte *)str, len + 1); // +1 to store null terminator } 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 { // 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..7426029addc2e 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -102,6 +102,16 @@ enum { MP_NATIVE_ARCH_XTENSAWIN, }; +enum { + MP_PERSISTENT_OBJ_FUN_TABLE = 0, + 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_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/tools/mpy-tool.py b/tools/mpy-tool.py index 84c09a0c688d2..fc0b7c09cac1e 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -121,6 +121,14 @@ def __init__(self, str): MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 +MP_PERSISTENT_OBJ_FUN_TABLE = 0 +MP_PERSISTENT_OBJ_ELLIPSIS = 1 +MP_PERSISTENT_OBJ_STR = 2 +MP_PERSISTENT_OBJ_BYTES = 3 +MP_PERSISTENT_OBJ_INT = 4 +MP_PERSISTENT_OBJ_FLOAT = 5 +MP_PERSISTENT_OBJ_COMPLEX = 6 + MP_SCOPE_FLAG_VIPERRELOC = 0x10 MP_SCOPE_FLAG_VIPERRODATA = 0x20 MP_SCOPE_FLAG_VIPERBSS = 0x40 @@ -1104,26 +1112,26 @@ def read_qstr(reader, segments): 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_ELLIPSIS: return Ellipsis 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": + 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") From e52f14d05772b670847c0692a635a5ec223c5e57 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 14:26:07 +1100 Subject: [PATCH 02/17] py/parse: Factor obj extract code to mp_parse_node_extract_const_object. Signed-off-by: Damien George --- py/compile.c | 7 +------ py/parse.c | 12 ++++-------- py/parse.h | 11 +++++++++++ 3 files changed, 16 insertions(+), 14 deletions(-) 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/parse.c b/py/parse.c index 233cba11b46ca..f0e0a165c3458 100644 --- a/py/parse.c +++ b/py/parse.c @@ -333,12 +333,7 @@ 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; @@ -397,10 +392,11 @@ 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)\n", obj); #else - mp_printf(print, "literal const(%p)\n", (mp_obj_t)pns->nodes[0]); + mp_printf(print, "literal const(%p)\n", obj); #endif } else { size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); 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); From 24bc1f61f9c73e922cda2cb429d9ace0a85cff94 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 14:26:36 +1100 Subject: [PATCH 03/17] py/parse: Print const object value in mp_parse_node_print. To give more information when printing the parse tree. Signed-off-by: Damien George --- py/parse.c | 6 ++++-- tests/cmdline/cmd_parsetree.py.exp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/py/parse.c b/py/parse.c index f0e0a165c3458..8f0a25cf2c731 100644 --- a/py/parse.c +++ b/py/parse.c @@ -394,10 +394,12 @@ void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t ind 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", obj); + mp_printf(print, "literal const(%016llx)=", obj); #else - mp_printf(print, "literal const(%p)\n", obj); + 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 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) From 35c0cff92bea230f8e16a8a7cdd50ea5f6a9df03 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 14:27:47 +1100 Subject: [PATCH 04/17] py/parse: Add MICROPY_COMP_CONST_TUPLE option to build const tuples. This commit adds support to the parser so that tuples which contain only constant elements (bool, int, str, bytes, etc) are immediately converted to a tuple object. This makes it more efficient to use tuples containing constant data because they no longer need to be created at runtime by the bytecode (or native code). Furthermore, with this improvement constant tuples that are part of frozen code are now able to be stored fully in ROM (this will be implemented in later commits). Code size is increased by about 400 bytes on Cortex-M4 platforms. See related issue #722. Signed-off-by: Damien George --- py/mpconfig.h | 6 + py/parse.c | 147 ++++++++ tests/cmdline/cmd_showbc.py.exp | 644 ++++++++++++++++---------------- 3 files changed, 474 insertions(+), 323 deletions(-) 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 8f0a25cf2c731..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); @@ -340,6 +357,76 @@ bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { } } +#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; @@ -791,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) { @@ -847,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/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 From 4ca96983ff808d914256fb85376fc14b871d235e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 14:28:19 +1100 Subject: [PATCH 05/17] py/persistentcode: Support loading and saving tuples in .mpy files. Signed-off-by: Damien George --- py/persistentcode.c | 33 ++++++++++++++++++++++++++++++++- py/persistentcode.h | 4 ++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/py/persistentcode.c b/py/persistentcode.c index c9cfc3b5c285c..f3d6b7205d6a2 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -202,13 +202,25 @@ STATIC mp_obj_t load_obj(mp_reader_t *reader) { return MP_OBJ_FROM_PTR(&mp_fun_table); } else #endif - if (obj_type == MP_PERSISTENT_OBJ_ELLIPSIS) { + 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 == 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); @@ -514,9 +526,28 @@ STATIC void save_obj(mp_print_t *print, mp_obj_t o) { 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 = 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 diff --git a/py/persistentcode.h b/py/persistentcode.h index 7426029addc2e..37263f66b3c3a 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -104,12 +104,16 @@ enum { 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); From 2a075cc8a9596d42a71906be4b3c91858e249a3c Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 15:26:14 +1100 Subject: [PATCH 06/17] tools/mpy-tool.py: Support loading tuples from .mpy files. Signed-off-by: Damien George --- tools/mpy-tool.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index fc0b7c09cac1e..e5470861138de 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -122,12 +122,16 @@ def __init__(self, str): MP_NATIVE_ARCH_XTENSAWIN = 10 MP_PERSISTENT_OBJ_FUN_TABLE = 0 -MP_PERSISTENT_OBJ_ELLIPSIS = 1 -MP_PERSISTENT_OBJ_STR = 2 -MP_PERSISTENT_OBJ_BYTES = 3 -MP_PERSISTENT_OBJ_INT = 4 -MP_PERSISTENT_OBJ_FLOAT = 5 -MP_PERSISTENT_OBJ_COMPLEX = 6 +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 @@ -846,7 +850,7 @@ def disassemble(self): while ip < len(bc): fmt, sz, arg = mp_opcode_decode(bc, ip) if bc[ip] == Opcodes.MP_BC_LOAD_CONST_OBJ: - arg = "%r" % self.obj_table[arg] + arg = repr(self.obj_table[arg]) if fmt == MP_BC_FORMAT_QSTR: arg = self.qstr_table[arg].str elif fmt in (MP_BC_FORMAT_VAR_UINT, MP_BC_FORMAT_OFFSET): @@ -1115,8 +1119,17 @@ def read_obj(reader, segments): obj_type = reader.read_byte() if obj_type == MP_PERSISTENT_OBJ_FUN_TABLE: return MPFunTable() + 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() From 68b3aeeb577c92328dc96a197d3625dd4379d969 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 31 Mar 2022 16:20:23 +1100 Subject: [PATCH 07/17] tools/mpy-tool.py: Support freezing tuples and other consts. This also simplifies how constants are frozen. Signed-off-by: Damien George --- tools/mpy-tool.py | 195 ++++++++++++++++++++++++++-------------------- 1 file changed, 109 insertions(+), 86 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index e5470861138de..000b22578881a 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -600,9 +600,113 @@ 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 is_str_type(obj): + 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 config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE: + # TODO check if we can actually fit this long-int into a small-int + 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() @@ -694,25 +734,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(" Date: Thu, 7 Apr 2022 23:59:59 +1000 Subject: [PATCH 08/17] ports: Recompile bytecode tests now that .mpy format changed. Signed-off-by: Damien George --- ports/minimal/frozentest.mpy | Bin 198 -> 196 bytes ports/powerpc/frozentest.mpy | Bin 198 -> 196 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/ports/minimal/frozentest.mpy b/ports/minimal/frozentest.mpy index cd75d65a8bad195029d131cc42f18437ffeb7523..99581617ac3d13e416722a63852da14088ff8db7 100644 GIT binary patch delta 67 zcmX@cc!Y6+0we20MHL|7rXk73om5$pTC8AbWNgC7&|)CK$EXejT51#9HAH>F#aINH U1X)ZNBO9NFw_JDDj|@x(02fRUcmMzZ delta 69 zcmX@Yc#Ls^0%P$+MHL|7rXiWcom5$pTC8AbWNgC7(BdG#$EXejTIv(qHN<%u!o^qw WnFLu(7$X~>hPPaIepeqEm<#}f(i1@d diff --git a/ports/powerpc/frozentest.mpy b/ports/powerpc/frozentest.mpy index cd75d65a8bad195029d131cc42f18437ffeb7523..99581617ac3d13e416722a63852da14088ff8db7 100644 GIT binary patch delta 67 zcmX@cc!Y6+0we20MHL|7rXk73om5$pTC8AbWNgC7&|)CK$EXejT51#9HAH>F#aINH U1X)ZNBO9NFw_JDDj|@x(02fRUcmMzZ delta 69 zcmX@Yc#Ls^0%P$+MHL|7rXiWcom5$pTC8AbWNgC7(BdG#$EXejTIv(qHN<%u!o^qw WnFLu(7$X~>hPPaIepeqEm<#}f(i1@d From 999abbb8b5a0ed9ee17e9295cc758e44a5511b80 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Apr 2022 14:50:47 +1000 Subject: [PATCH 09/17] tests/perf_bench: Update import tests for changes to .mpy consts. Signed-off-by: Damien George --- tests/perf_bench/core_import_mpy_multi.py | 2 +- tests/perf_bench/core_import_mpy_single.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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): From abdc4ec08d742642598555ba8e1854fe1445645e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 7 Apr 2022 23:54:24 +1000 Subject: [PATCH 10/17] qemu-arm/test-frzmpy: Add test for freezing constant tuples. Signed-off-by: Damien George --- ports/qemu-arm/test-frzmpy/frozen_const.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ports/qemu-arm/test-frzmpy/frozen_const.py 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, ...))) From 9c8a56343fea4e3603dbeeabd1b3a2194f9dca91 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 7 Apr 2022 23:53:37 +1000 Subject: [PATCH 11/17] tools/mpy-tool.py: Optimise freezing of ints that can fit a small int. Signed-off-by: Damien George --- tools/mpy-tool.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 000b22578881a..17dd8704aebcb 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -327,6 +327,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] @@ -633,8 +640,10 @@ def freeze_constant_obj(self, obj_name, obj): const_obj_content += 4 * 4 return "MP_ROM_PTR(&%s)" % obj_name elif is_int_type(obj): - if config.MICROPY_LONGINT_IMPL == config.MICROPY_LONGINT_IMPL_NONE: - # TODO check if we can actually fit this long-int into a small-int + 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 From dfc6c6299c3ce3788412e054b8381d11283eb4a9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Apr 2022 13:07:25 +1000 Subject: [PATCH 12/17] tools/mpy-tool.py: Optimise freezing of empty str and bytes objects. Signed-off-by: Damien George --- tools/mpy-tool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 17dd8704aebcb..6a769f7b4070a 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -621,6 +621,11 @@ def freeze_constant_obj(self, obj_name, obj): 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): obj = bytes_cons(obj, "utf8") obj_type = "mp_type_str" From e647966fc919cc4519b84a36960e983f70b4cdca Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Apr 2022 14:04:21 +1000 Subject: [PATCH 13/17] tools/mpy-tool.py: Make global qstr list a dedicated class. Signed-off-by: Damien George --- tools/mpy-tool.py | 63 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 6a769f7b4070a..e3a812ebe0596 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -91,19 +91,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 @@ -469,6 +456,29 @@ 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] + + class MPFunTable: def __repr__(self): return "mp_fun_table" @@ -496,10 +506,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 @@ -1077,8 +1083,7 @@ def freeze(self): if qi < len(self.qstr_links) and i == self.qstr_links[qi][0]: # link qstr qi_off, qi_kind, qi_val = self.qstr_links[qi] - qst = global_qstrs[qi_val].qstr_id - i += self._link_qstr(i, qi_kind, qst) + i += self._link_qstr(i, qi_kind, qi_val.qstr_id) qi += 1 else: # copy machine code (max 16 bytes) @@ -1139,17 +1144,15 @@ def read_qstr(reader, segments): ln = reader.read_uint() if ln & 1: # static qstr - segments.append( - MPYSegment(MPYSegment.META, global_qstrs[ln >> 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): @@ -1304,8 +1307,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 = [] @@ -1345,7 +1347,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 @@ -1587,6 +1589,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.") @@ -1637,6 +1641,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] From 40d431d1bbb7928ab399bf892e90cf232a83e2b9 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Apr 2022 14:05:23 +1000 Subject: [PATCH 14/17] tools/mpy-tool.py: Optimise freezing of str when str data is a qstr. Signed-off-by: Damien George --- tools/mpy-tool.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index e3a812ebe0596..f4a8ef6420f20 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -478,6 +478,12 @@ def add(self, s): 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): @@ -633,6 +639,9 @@ def freeze_constant_obj(self, obj_name, obj): 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: From 07f526067e207f23529b4f2234baffd74ce1f35c Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 8 Apr 2022 14:09:08 +1000 Subject: [PATCH 15/17] tools/mpy-tool.py: Intern more strings when freezing. Signed-off-by: Damien George --- tools/mpy-tool.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index f4a8ef6420f20..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): @@ -1187,6 +1192,9 @@ def read_obj(reader, segments): reader.read_byte() # read and discard null terminator if obj_type == MP_PERSISTENT_OBJ_STR: obj = str_cons(buf, "utf8") + 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 == MP_PERSISTENT_OBJ_INT: From 865b61dac205fe10150e8c4a38411470b4eb82f4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Apr 2022 23:34:37 +1000 Subject: [PATCH 16/17] tests/micropython: Add tests that const tuples don't use the heap. Signed-off-by: Damien George --- tests/micropython/heapalloc.py | 8 ++++++++ tests/micropython/heapalloc.py.exp | 4 ++++ 2 files changed, 12 insertions(+) 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', ()) From 9ab66b50cb0f15dab9c935324751edb638144099 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 11 Apr 2022 23:40:42 +1000 Subject: [PATCH 17/17] docs/reference: Update constrained docs now that tuples can be const. Signed-off-by: Damien George --- docs/reference/constrained.rst | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) 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**