LCOV - code coverage report
Current view: top level - py - persistentcode.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-305-g1034b1755.info Lines: 354 375 94.4 %
Date: 2025-02-19 18:31:36 Functions: 22 22 100.0 %
Branches: 179 234 76.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of the MicroPython project, http://micropython.org/
       3                 :            :  *
       4                 :            :  * The MIT License (MIT)
       5                 :            :  *
       6                 :            :  * Copyright (c) 2013-2020 Damien P. George
       7                 :            :  *
       8                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       9                 :            :  * of this software and associated documentation files (the "Software"), to deal
      10                 :            :  * in the Software without restriction, including without limitation the rights
      11                 :            :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12                 :            :  * copies of the Software, and to permit persons to whom the Software is
      13                 :            :  * furnished to do so, subject to the following conditions:
      14                 :            :  *
      15                 :            :  * The above copyright notice and this permission notice shall be included in
      16                 :            :  * all copies or substantial portions of the Software.
      17                 :            :  *
      18                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23                 :            :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      24                 :            :  * THE SOFTWARE.
      25                 :            :  */
      26                 :            : 
      27                 :            : #include <stdint.h>
      28                 :            : #include <stdio.h>
      29                 :            : #include <string.h>
      30                 :            : #include <assert.h>
      31                 :            : 
      32                 :            : #include "py/reader.h"
      33                 :            : #include "py/nativeglue.h"
      34                 :            : #include "py/persistentcode.h"
      35                 :            : #include "py/bc0.h"
      36                 :            : #include "py/objstr.h"
      37                 :            : #include "py/mpthread.h"
      38                 :            : 
      39                 :            : #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
      40                 :            : 
      41                 :            : #include "py/smallint.h"
      42                 :            : 
      43                 :            : // makeqstrdata.py has a fixed list of qstrs at the start that we can assume
      44                 :            : // are available with know indices on all MicroPython implementations, and
      45                 :            : // avoid needing to duplicate the string data in the .mpy file. This is the
      46                 :            : // last one in that list (anything with a qstr less than or equal to this is
      47                 :            : // assumed to be in the list).
      48                 :            : #define QSTR_LAST_STATIC MP_QSTR_zip
      49                 :            : 
      50                 :            : #if MICROPY_DYNAMIC_COMPILER
      51                 :            : #define MPY_FEATURE_ARCH_DYNAMIC mp_dynamic_compiler.native_arch
      52                 :            : #else
      53                 :            : #define MPY_FEATURE_ARCH_DYNAMIC MPY_FEATURE_ARCH
      54                 :            : #endif
      55                 :            : 
      56                 :            : typedef struct _bytecode_prelude_t {
      57                 :            :     uint n_state;
      58                 :            :     uint n_exc_stack;
      59                 :            :     uint scope_flags;
      60                 :            :     uint n_pos_args;
      61                 :            :     uint n_kwonly_args;
      62                 :            :     uint n_def_pos_args;
      63                 :            :     uint code_info_size;
      64                 :            : } bytecode_prelude_t;
      65                 :            : 
      66                 :            : #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
      67                 :            : 
      68                 :            : #if MICROPY_PERSISTENT_CODE_LOAD
      69                 :            : 
      70                 :            : #include "py/parsenum.h"
      71                 :            : 
      72                 :            : static int read_byte(mp_reader_t *reader);
      73                 :            : static size_t read_uint(mp_reader_t *reader);
      74                 :            : 
      75                 :            : #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA
      76                 :            : 
      77                 :            : // An mp_obj_list_t that tracks native text/BSS/rodata to prevent the GC from reclaiming them.
      78                 :            : MP_REGISTER_ROOT_POINTER(mp_obj_t persistent_code_root_pointers);
      79                 :            : 
      80                 :          8 : static void track_root_pointer(void *ptr) {
      81         [ +  - ]:          8 :     if (MP_STATE_PORT(persistent_code_root_pointers) == MP_OBJ_NULL) {
      82                 :          8 :         MP_STATE_PORT(persistent_code_root_pointers) = mp_obj_new_list(0, NULL);
      83                 :            :     }
      84                 :          8 :     mp_obj_list_append(MP_STATE_PORT(persistent_code_root_pointers), MP_OBJ_FROM_PTR(ptr));
      85                 :          8 : }
      86                 :            : 
      87                 :            : #endif
      88                 :            : 
      89                 :            : #if MICROPY_EMIT_MACHINE_CODE
      90                 :            : 
      91                 :            : typedef struct _reloc_info_t {
      92                 :            :     mp_reader_t *reader;
      93                 :            :     mp_module_context_t *context;
      94                 :            :     uint8_t *rodata;
      95                 :            :     uint8_t *bss;
      96                 :            : } reloc_info_t;
      97                 :            : 
      98                 :          8 : void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) {
      99                 :            :     // Relocate native code
     100                 :          8 :     reloc_info_t *ri = ri_in;
     101                 :          8 :     uint8_t op;
     102                 :          8 :     uintptr_t *addr_to_adjust = NULL;
     103         [ +  + ]:         48 :     while ((op = read_byte(ri->reader)) != 0xff) {
     104         [ +  - ]:         40 :         if (op & 1) {
     105                 :            :             // Point to new location to make adjustments
     106                 :         40 :             size_t addr = read_uint(ri->reader);
     107         [ +  + ]:         40 :             if ((addr & 1) == 0) {
     108                 :            :                 // Point to somewhere in text
     109                 :         36 :                 addr_to_adjust = &((uintptr_t *)text)[addr >> 1];
     110                 :            :             } else {
     111                 :            :                 // Point to somewhere in rodata
     112                 :          4 :                 addr_to_adjust = &((uintptr_t *)ri->rodata)[addr >> 1];
     113                 :            :             }
     114                 :            :         }
     115                 :         40 :         op >>= 1;
     116                 :         40 :         uintptr_t dest;
     117                 :         40 :         size_t n = 1;
     118         [ +  + ]:         40 :         if (op <= 5) {
     119         [ +  + ]:         20 :             if (op & 1) {
     120                 :            :                 // Read in number of adjustments to make
     121                 :          4 :                 n = read_uint(ri->reader);
     122                 :            :             }
     123                 :         20 :             op >>= 1;
     124         [ +  + ]:         20 :             if (op == 0) {
     125                 :            :                 // Destination is text
     126                 :         40 :                 dest = reloc_text;
     127         [ -  + ]:          4 :             } else if (op == 1) {
     128                 :            :                 // Destination is rodata
     129                 :          0 :                 dest = (uintptr_t)ri->rodata;
     130                 :            :             } else {
     131                 :            :                 // Destination is bss
     132                 :          4 :                 dest = (uintptr_t)ri->bss;
     133                 :            :             }
     134         [ +  + ]:         20 :         } else if (op == 6) {
     135                 :            :             // Destination is qstr_table
     136                 :          4 :             dest = (uintptr_t)ri->context->constants.qstr_table;
     137         [ -  + ]:         16 :         } else if (op == 7) {
     138                 :            :             // Destination is obj_table
     139                 :          0 :             dest = (uintptr_t)ri->context->constants.obj_table;
     140         [ +  + ]:         16 :         } else if (op == 8) {
     141                 :            :             // Destination is mp_fun_table itself
     142                 :          4 :             dest = (uintptr_t)&mp_fun_table;
     143                 :            :         } else {
     144                 :            :             // Destination is an entry in mp_fun_table
     145                 :         12 :             dest = ((uintptr_t *)&mp_fun_table)[op - 9];
     146                 :            :         }
     147         [ +  + ]:         76 :         while (n--) {
     148                 :         36 :             *addr_to_adjust++ += dest;
     149                 :            :         }
     150                 :            :     }
     151                 :          8 : }
     152                 :            : 
     153                 :            : #endif
     154                 :            : 
     155                 :      22660 : static int read_byte(mp_reader_t *reader) {
     156                 :      22660 :     return reader->readbyte(reader->data);
     157                 :            : }
     158                 :            : 
     159                 :      22223 : static void read_bytes(mp_reader_t *reader, byte *buf, size_t len) {
     160         [ +  + ]:    2034171 :     while (len-- > 0) {
     161                 :    2011948 :         *buf++ = reader->readbyte(reader->data);
     162                 :            :     }
     163                 :      22223 : }
     164                 :            : 
     165                 :      35648 : static size_t read_uint(mp_reader_t *reader) {
     166                 :      35648 :     size_t unum = 0;
     167                 :      44828 :     for (;;) {
     168                 :      44828 :         byte b = reader->readbyte(reader->data);
     169                 :      44828 :         unum = (unum << 7) | (b & 0x7f);
     170         [ +  + ]:      44828 :         if ((b & 0x80) == 0) {
     171                 :            :             break;
     172                 :            :         }
     173                 :            :     }
     174                 :      35648 :     return unum;
     175                 :            : }
     176                 :            : 
     177                 :      18531 : static qstr load_qstr(mp_reader_t *reader) {
     178                 :      18531 :     size_t len = read_uint(reader);
     179         [ +  + ]:      18531 :     if (len & 1) {
     180                 :            :         // static qstr
     181                 :       8121 :         return len >> 1;
     182                 :            :     }
     183                 :      10410 :     len >>= 1;
     184                 :            : 
     185                 :            :     #if MICROPY_VFS_ROM
     186                 :            :     // If possible, create the qstr from the memory-mapped string data.
     187                 :      10410 :     const uint8_t *memmap = mp_reader_try_read_rom(reader, len + 1);
     188         [ +  + ]:      10410 :     if (memmap != NULL) {
     189                 :         10 :         return qstr_from_strn_static((const char *)memmap, len);
     190                 :            :     }
     191                 :            :     #endif
     192                 :            : 
     193                 :      10400 :     char *str = m_new(char, len);
     194                 :      10400 :     read_bytes(reader, (byte *)str, len);
     195                 :      10400 :     read_byte(reader); // read and discard null terminator
     196                 :      10400 :     qstr qst = qstr_from_strn(str, len);
     197                 :      10400 :     m_del(char, str, len);
     198                 :      10400 :     return qst;
     199                 :            : }
     200                 :            : 
     201                 :            : #if MICROPY_VFS_ROM
     202                 :            : // Create a str/bytes object that can forever reference the given data.
     203                 :          6 : static mp_obj_t mp_obj_new_str_static(const mp_obj_type_t *type, const byte *data, size_t len) {
     204         [ +  + ]:          6 :     if (type == &mp_type_str) {
     205                 :          4 :         qstr q = qstr_find_strn((const char *)data, len);
     206         [ +  + ]:          4 :         if (q != MP_QSTRnull) {
     207                 :          2 :             return MP_OBJ_NEW_QSTR(q);
     208                 :            :         }
     209                 :            :     }
     210         [ -  + ]:          4 :     assert(data[len] == '\0');
     211                 :          4 :     mp_obj_str_t *o = mp_obj_malloc(mp_obj_str_t, type);
     212                 :          4 :     o->len = len;
     213                 :          4 :     o->hash = qstr_compute_hash(data, len);
     214                 :          4 :     o->data = data;
     215                 :          4 :     return MP_OBJ_FROM_PTR(o);
     216                 :            : }
     217                 :            : #endif
     218                 :            : 
     219                 :       7686 : static mp_obj_t load_obj(mp_reader_t *reader) {
     220                 :       7686 :     byte obj_type = read_byte(reader);
     221                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     222   [ +  +  +  +  :       7686 :     if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) {
                   +  + ]
     223                 :            :         return MP_OBJ_FROM_PTR(&mp_fun_table);
     224                 :            :     } else
     225                 :            :     #endif
     226                 :            :     if (obj_type == MP_PERSISTENT_OBJ_NONE) {
     227                 :         10 :         return mp_const_none;
     228                 :            :     } else if (obj_type == MP_PERSISTENT_OBJ_FALSE) {
     229                 :          8 :         return mp_const_false;
     230                 :            :     } else if (obj_type == MP_PERSISTENT_OBJ_TRUE) {
     231                 :         10 :         return mp_const_true;
     232                 :            :     } else if (obj_type == MP_PERSISTENT_OBJ_ELLIPSIS) {
     233                 :          7 :         return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj);
     234                 :            :     } else {
     235                 :       6932 :         size_t len = read_uint(reader);
     236                 :            : 
     237                 :            :         // Handle empty bytes object, and tuple objects.
     238         [ +  + ]:       6932 :         if (len == 0 && obj_type == MP_PERSISTENT_OBJ_BYTES) {
     239                 :         40 :             read_byte(reader); // skip null terminator
     240                 :         40 :             return mp_const_empty_bytes;
     241         [ +  + ]:       6892 :         } else if (obj_type == MP_PERSISTENT_OBJ_TUPLE) {
     242                 :        519 :             mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL));
     243         [ +  + ]:       1840 :             for (size_t i = 0; i < len; ++i) {
     244                 :       1321 :                 tuple->items[i] = load_obj(reader);
     245                 :            :             }
     246                 :            :             return MP_OBJ_FROM_PTR(tuple);
     247                 :            :         }
     248                 :            : 
     249                 :            :         // Read in the object's data, either from ROM or into RAM.
     250                 :       6373 :         const uint8_t *memmap = NULL;
     251                 :       6373 :         vstr_t vstr;
     252                 :            :         #if MICROPY_VFS_ROM
     253                 :       6373 :         memmap = mp_reader_try_read_rom(reader, len);
     254                 :       6373 :         vstr.buf = (void *)memmap;
     255                 :       6373 :         vstr.len = len;
     256                 :            :         #endif
     257         [ +  + ]:       6373 :         if (memmap == NULL) {
     258                 :            :             // Data could not be memory-mapped, so allocate it in RAM and read it in.
     259                 :       6363 :             vstr_init_len(&vstr, len);
     260                 :       6363 :             read_bytes(reader, (byte *)vstr.buf, len);
     261                 :            :         }
     262                 :            : 
     263                 :            :         // Create and return the object.
     264         [ +  + ]:       6373 :         if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) {
     265                 :       4486 :             read_byte(reader); // skip null terminator (it needs to be there for ROM str objects)
     266                 :            :             #if MICROPY_VFS_ROM
     267         [ +  + ]:       4486 :             if (memmap != NULL) {
     268                 :            :                 // Create a str/bytes that references the memory-mapped data.
     269         [ +  + ]:          6 :                 const mp_obj_type_t *t = obj_type == MP_PERSISTENT_OBJ_STR ? &mp_type_str : &mp_type_bytes;
     270                 :          6 :                 return mp_obj_new_str_static(t, memmap, len);
     271                 :            :             }
     272                 :            :             #endif
     273         [ +  + ]:       4480 :             if (obj_type == MP_PERSISTENT_OBJ_STR) {
     274                 :       3862 :                 return mp_obj_new_str_from_utf8_vstr(&vstr);
     275                 :            :             } else {
     276                 :        618 :                 return mp_obj_new_bytes_from_vstr(&vstr);
     277                 :            :             }
     278         [ +  + ]:       1887 :         } else if (obj_type == MP_PERSISTENT_OBJ_INT) {
     279                 :       1309 :             return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
     280                 :            :         } else {
     281         [ -  + ]:        578 :             assert(obj_type == MP_PERSISTENT_OBJ_FLOAT || obj_type == MP_PERSISTENT_OBJ_COMPLEX);
     282                 :        578 :             return mp_parse_num_float(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, NULL);
     283                 :            :         }
     284                 :            :     }
     285                 :            : }
     286                 :            : 
     287                 :       4052 : static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *context) {
     288                 :            :     // Load function kind and data length
     289                 :       4052 :     size_t kind_len = read_uint(reader);
     290                 :       4052 :     int kind = (kind_len & 3) + MP_CODE_BYTECODE;
     291                 :       4052 :     bool has_children = !!(kind_len & 4);
     292                 :       4052 :     size_t fun_data_len = kind_len >> 3;
     293                 :            : 
     294                 :            :     #if !MICROPY_EMIT_MACHINE_CODE
     295                 :            :     if (kind != MP_CODE_BYTECODE) {
     296                 :            :         mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
     297                 :            :     }
     298                 :            :     #endif
     299                 :            : 
     300                 :       4052 :     uint8_t *fun_data = NULL;
     301                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     302                 :       4052 :     size_t prelude_offset = 0;
     303                 :       4052 :     mp_uint_t native_scope_flags = 0;
     304                 :       4052 :     mp_uint_t native_n_pos_args = 0;
     305                 :       4052 :     mp_uint_t native_type_sig = 0;
     306                 :            :     #endif
     307                 :            : 
     308         [ +  + ]:       4052 :     if (kind == MP_CODE_BYTECODE) {
     309                 :            :         #if MICROPY_VFS_ROM
     310                 :            :         // Try to reference memory-mapped data for the bytecode.
     311                 :       1929 :         fun_data = (uint8_t *)mp_reader_try_read_rom(reader, fun_data_len);
     312                 :            :         #endif
     313                 :            : 
     314         [ +  + ]:       1929 :         if (fun_data == NULL) {
     315                 :            :             // Allocate memory for the bytecode.
     316                 :       1927 :             fun_data = m_new(uint8_t, fun_data_len);
     317                 :            :             // Load bytecode.
     318                 :       1927 :             read_bytes(reader, fun_data, fun_data_len);
     319                 :            :         }
     320                 :            : 
     321                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     322                 :            :     } else {
     323                 :            :         // Allocate memory for native data and load it
     324                 :       2123 :         size_t fun_alloc;
     325                 :       2123 :         MP_PLAT_ALLOC_EXEC(fun_data_len, (void **)&fun_data, &fun_alloc);
     326                 :       2123 :         read_bytes(reader, fun_data, fun_data_len);
     327                 :            : 
     328         [ +  + ]:       2123 :         if (kind == MP_CODE_NATIVE_PY) {
     329                 :            :             // Read prelude offset within fun_data, and extract scope flags.
     330                 :       1901 :             prelude_offset = read_uint(reader);
     331                 :       1901 :             const byte *ip = fun_data + prelude_offset;
     332         [ +  + ]:       2229 :             MP_BC_PRELUDE_SIG_DECODE(ip);
     333                 :       2123 :             native_scope_flags = scope_flags;
     334                 :            :         } else {
     335                 :            :             // Load basic scope info for viper and asm.
     336                 :        222 :             native_scope_flags = read_uint(reader);
     337         [ +  + ]:        222 :             if (kind == MP_CODE_NATIVE_ASM) {
     338                 :          4 :                 native_n_pos_args = read_uint(reader);
     339                 :          4 :                 native_type_sig = read_uint(reader);
     340                 :            :             }
     341                 :            :         }
     342                 :            :     #endif
     343                 :            :     }
     344                 :            : 
     345                 :       4052 :     size_t n_children = 0;
     346                 :       4052 :     mp_raw_code_t **children = NULL;
     347                 :            : 
     348                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     349                 :            :     // Load optional BSS/rodata for viper.
     350                 :       4052 :     uint8_t *rodata = NULL;
     351                 :       4052 :     uint8_t *bss = NULL;
     352         [ +  + ]:       4052 :     if (kind == MP_CODE_NATIVE_VIPER) {
     353                 :        218 :         size_t rodata_size = 0;
     354         [ +  + ]:        218 :         if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
     355                 :          4 :             rodata_size = read_uint(reader);
     356                 :            :         }
     357                 :            : 
     358                 :        218 :         size_t bss_size = 0;
     359         [ +  + ]:        218 :         if (native_scope_flags & MP_SCOPE_FLAG_VIPERBSS) {
     360                 :          8 :             bss_size = read_uint(reader);
     361                 :            :         }
     362                 :            : 
     363         [ +  + ]:        218 :         if (rodata_size + bss_size != 0) {
     364                 :          8 :             bss_size = (uintptr_t)MP_ALIGN(bss_size, sizeof(uintptr_t));
     365                 :          8 :             uint8_t *data = m_new0(uint8_t, bss_size + rodata_size);
     366                 :          8 :             bss = data;
     367                 :          8 :             rodata = bss + bss_size;
     368         [ +  + ]:          8 :             if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
     369                 :          4 :                 read_bytes(reader, rodata, rodata_size);
     370                 :            :             }
     371                 :            : 
     372                 :            :             #if MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA
     373                 :            :             // Track the BSS/rodata memory so it's not reclaimed by the GC.
     374                 :          8 :             track_root_pointer(data);
     375                 :            :             #endif
     376                 :            :         }
     377                 :            :     }
     378                 :            :     #endif
     379                 :            : 
     380                 :            :     // Load children if any.
     381         [ +  + ]:       4052 :     if (has_children) {
     382                 :       1170 :         n_children = read_uint(reader);
     383                 :       1170 :         children = m_new(mp_raw_code_t *, n_children + (kind == MP_CODE_NATIVE_PY));
     384         [ +  + ]:       3834 :         for (size_t i = 0; i < n_children; ++i) {
     385                 :       2664 :             children[i] = load_raw_code(reader, context);
     386                 :            :         }
     387                 :            :     }
     388                 :            : 
     389                 :            :     // Create raw_code and return it
     390                 :       4052 :     mp_raw_code_t *rc = mp_emit_glue_new_raw_code();
     391         [ +  + ]:       4052 :     if (kind == MP_CODE_BYTECODE) {
     392                 :       1929 :         const byte *ip = fun_data;
     393         [ +  + ]:       2334 :         MP_BC_PRELUDE_SIG_DECODE(ip);
     394                 :            :         // Assign bytecode to raw code object
     395                 :       1929 :         mp_emit_glue_assign_bytecode(rc, fun_data,
     396                 :            :             children,
     397                 :            :             #if MICROPY_PERSISTENT_CODE_SAVE
     398                 :            :             fun_data_len,
     399                 :            :             n_children,
     400                 :            :             #endif
     401                 :            :             scope_flags);
     402                 :            : 
     403                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     404                 :            :     } else {
     405                 :       2123 :         const uint8_t *prelude_ptr = NULL;
     406                 :            :         #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE
     407                 :            :         if (kind == MP_CODE_NATIVE_PY) {
     408                 :            :             // Executable code cannot be accessed byte-wise on this architecture, so copy
     409                 :            :             // the prelude to a separate memory region that is byte-wise readable.
     410                 :            :             void *buf = fun_data + prelude_offset;
     411                 :            :             size_t n = fun_data_len - prelude_offset;
     412                 :            :             prelude_ptr = memcpy(m_new(uint8_t, n), buf, n);
     413                 :            :         }
     414                 :            :         #endif
     415                 :            : 
     416                 :            :         // Relocate and commit code to executable address space
     417                 :       2123 :         reloc_info_t ri = {reader, context, rodata, bss};
     418                 :            :         #if defined(MP_PLAT_COMMIT_EXEC)
     419                 :            :         void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL;
     420                 :            :         fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri);
     421                 :            :         #else
     422         [ +  + ]:       2123 :         if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) {
     423                 :            :             #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA
     424                 :            :             // Track the function data memory so it's not reclaimed by the GC.
     425                 :            :             track_root_pointer(fun_data);
     426                 :            :             #endif
     427                 :            :             // Do the relocations.
     428                 :          8 :             mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data);
     429                 :            :         }
     430                 :            :         #endif
     431                 :            : 
     432         [ +  + ]:       2123 :         if (kind == MP_CODE_NATIVE_PY) {
     433                 :            :             #if !MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE
     434                 :       1901 :             prelude_ptr = fun_data + prelude_offset;
     435                 :            :             #endif
     436         [ +  + ]:       1901 :             if (n_children == 0) {
     437                 :            :                 children = (void *)prelude_ptr;
     438                 :            :             } else {
     439                 :        574 :                 children[n_children] = (void *)prelude_ptr;
     440                 :            :             }
     441                 :            :         }
     442                 :            : 
     443                 :            :         // Assign native code to raw code object
     444                 :       2123 :         mp_emit_glue_assign_native(rc, kind,
     445                 :            :             fun_data, fun_data_len,
     446                 :            :             children,
     447                 :            :             #if MICROPY_PERSISTENT_CODE_SAVE
     448                 :            :             n_children,
     449                 :            :             prelude_offset,
     450                 :            :             #endif
     451                 :            :             native_scope_flags, native_n_pos_args, native_type_sig
     452                 :            :             );
     453                 :            :     #endif
     454                 :            :     }
     455                 :       4052 :     return rc;
     456                 :            : }
     457                 :            : 
     458                 :       1406 : void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) {
     459                 :            :     // Set exception handler to close the reader if an exception is raised.
     460                 :       1406 :     MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, reader->close, reader->data);
     461                 :       1406 :     nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback);
     462                 :            : 
     463                 :       1406 :     byte header[4];
     464                 :       1406 :     read_bytes(reader, header, sizeof(header));
     465                 :       1406 :     byte arch = MPY_FEATURE_DECODE_ARCH(header[2]);
     466         [ +  + ]:       1406 :     if (header[0] != 'M'
     467         [ +  + ]:       1400 :         || header[1] != MPY_VERSION
     468   [ +  +  +  - ]:       1392 :         || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION)
     469         [ -  + ]:       1392 :         || header[3] > MP_SMALL_INT_BITS) {
     470                 :         14 :         mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
     471                 :            :     }
     472         [ +  + ]:       1392 :     if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) {
     473         [ +  + ]:        735 :         if (!MPY_FEATURE_ARCH_TEST(arch)) {
     474                 :          4 :             if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) {
     475                 :            :                 // On supported ports this can be resolved by enabling feature, eg
     476                 :            :                 // mpconfigboard.h: MICROPY_EMIT_THUMB (1)
     477                 :            :                 mp_raise_ValueError(MP_ERROR_TEXT("native code in .mpy unsupported"));
     478                 :            :             } else {
     479                 :          4 :                 mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch"));
     480                 :            :             }
     481                 :            :         }
     482                 :            :     }
     483                 :            : 
     484                 :       1388 :     size_t n_qstr = read_uint(reader);
     485                 :       1388 :     size_t n_obj = read_uint(reader);
     486                 :       1388 :     mp_module_context_alloc_tables(cm->context, n_qstr, n_obj);
     487                 :            : 
     488                 :            :     // Load qstrs.
     489         [ +  + ]:      19919 :     for (size_t i = 0; i < n_qstr; ++i) {
     490                 :      18531 :         cm->context->constants.qstr_table[i] = load_qstr(reader);
     491                 :            :     }
     492                 :            : 
     493                 :            :     // Load constant objects.
     494         [ +  + ]:       7753 :     for (size_t i = 0; i < n_obj; ++i) {
     495                 :       6365 :         cm->context->constants.obj_table[i] = load_obj(reader);
     496                 :            :     }
     497                 :            : 
     498                 :            :     // Load top-level module.
     499                 :       1388 :     cm->rc = load_raw_code(reader, cm->context);
     500                 :            : 
     501                 :            :     #if MICROPY_PERSISTENT_CODE_SAVE
     502                 :            :     cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE;
     503                 :            :     cm->n_qstr = n_qstr;
     504                 :            :     cm->n_obj = n_obj;
     505                 :            :     #endif
     506                 :            : 
     507                 :            :     // Deregister exception handler and close the reader.
     508                 :       1388 :     nlr_pop_jump_callback(true);
     509                 :       1388 : }
     510                 :            : 
     511                 :          7 : void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *context) {
     512                 :          7 :     mp_reader_t reader;
     513                 :          7 :     mp_reader_new_mem(&reader, buf, len, 0);
     514                 :          7 :     mp_raw_code_load(&reader, context);
     515                 :          7 : }
     516                 :            : 
     517                 :            : #if MICROPY_HAS_FILE_READER
     518                 :            : 
     519                 :       1399 : void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *context) {
     520                 :       1399 :     mp_reader_t reader;
     521                 :       1399 :     mp_reader_new_file(&reader, filename);
     522                 :       1399 :     mp_raw_code_load(&reader, context);
     523                 :       1381 : }
     524                 :            : 
     525                 :            : #endif // MICROPY_HAS_FILE_READER
     526                 :            : 
     527                 :            : #endif // MICROPY_PERSISTENT_CODE_LOAD
     528                 :            : 
     529                 :            : #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PERSISTENT_CODE_SAVE_FUN
     530                 :            : 
     531                 :            : #include "py/objstr.h"
     532                 :            : 
     533                 :        234 : static void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) {
     534                 :        234 :     print->print_strn(print->data, (const char *)data, len);
     535                 :        234 : }
     536                 :            : 
     537                 :            : #define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7)
     538                 :        274 : static void mp_print_uint(mp_print_t *print, size_t n) {
     539                 :        274 :     byte buf[BYTES_FOR_INT];
     540                 :        274 :     byte *p = buf + sizeof(buf);
     541                 :        274 :     *--p = n & 0x7f;
     542                 :        274 :     n >>= 7;
     543         [ +  + ]:        277 :     for (; n != 0; n >>= 7) {
     544                 :          3 :         *--p = 0x80 | (n & 0x7f);
     545                 :            :     }
     546                 :        274 :     print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p);
     547                 :        274 : }
     548                 :            : 
     549                 :        122 : static void save_qstr(mp_print_t *print, qstr qst) {
     550         [ +  + ]:        122 :     if (qst <= QSTR_LAST_STATIC) {
     551                 :            :         // encode static qstr
     552                 :        105 :         mp_print_uint(print, qst << 1 | 1);
     553                 :        105 :         return;
     554                 :            :     }
     555                 :         17 :     size_t len;
     556                 :         17 :     const byte *str = qstr_data(qst, &len);
     557                 :         17 :     mp_print_uint(print, len << 1);
     558                 :         17 :     mp_print_bytes(print, str, len + 1); // +1 to store null terminator
     559                 :            : }
     560                 :            : 
     561                 :        138 : static void save_obj(mp_print_t *print, mp_obj_t o) {
     562                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     563         [ -  + ]:        138 :     if (o == MP_OBJ_FROM_PTR(&mp_fun_table)) {
     564                 :          0 :         byte obj_type = MP_PERSISTENT_OBJ_FUN_TABLE;
     565                 :          0 :         mp_print_bytes(print, &obj_type, 1);
     566                 :            :     } else
     567                 :            :     #endif
     568   [ +  -  +  +  :        138 :     if (mp_obj_is_str_or_bytes(o)) {
             +  +  -  + ]
     569                 :          0 :         byte obj_type;
     570   [ #  #  #  #  :          0 :         if (mp_obj_is_str(o)) {
                   #  # ]
     571                 :          0 :             obj_type = MP_PERSISTENT_OBJ_STR;
     572                 :            :         } else {
     573                 :          0 :             obj_type = MP_PERSISTENT_OBJ_BYTES;
     574                 :            :         }
     575                 :          0 :         size_t len;
     576                 :          0 :         const char *str = mp_obj_str_get_data(o, &len);
     577                 :          0 :         mp_print_bytes(print, &obj_type, 1);
     578                 :          0 :         mp_print_uint(print, len);
     579                 :          0 :         mp_print_bytes(print, (const byte *)str, len + 1); // +1 to store null terminator
     580         [ +  + ]:        138 :     } else if (o == mp_const_none) {
     581                 :          6 :         byte obj_type = MP_PERSISTENT_OBJ_NONE;
     582                 :          6 :         mp_print_bytes(print, &obj_type, 1);
     583         [ -  + ]:        132 :     } else if (o == mp_const_false) {
     584                 :          0 :         byte obj_type = MP_PERSISTENT_OBJ_FALSE;
     585                 :          0 :         mp_print_bytes(print, &obj_type, 1);
     586         [ -  + ]:        132 :     } else if (o == mp_const_true) {
     587                 :          0 :         byte obj_type = MP_PERSISTENT_OBJ_TRUE;
     588                 :          0 :         mp_print_bytes(print, &obj_type, 1);
     589         [ +  + ]:        132 :     } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) {
     590                 :          1 :         byte obj_type = MP_PERSISTENT_OBJ_ELLIPSIS;
     591                 :          1 :         mp_print_bytes(print, &obj_type, 1);
     592   [ -  +  -  +  :        197 :     } else if (mp_obj_is_type(o, &mp_type_tuple)) {
          -  +  -  +  +  
                +  +  - ]
     593                 :         66 :         size_t len;
     594                 :         66 :         mp_obj_t *items;
     595                 :         66 :         mp_obj_tuple_get(o, &len, &items);
     596                 :         66 :         byte obj_type = MP_PERSISTENT_OBJ_TUPLE;
     597                 :         66 :         mp_print_bytes(print, &obj_type, 1);
     598                 :         66 :         mp_print_uint(print, len);
     599         [ +  + ]:        133 :         for (size_t i = 0; i < len; ++i) {
     600                 :         67 :             save_obj(print, items[i]);
     601                 :            :         }
     602                 :            :     } else {
     603                 :            :         // we save numbers using a simplistic text representation
     604                 :            :         // TODO could be improved
     605                 :         65 :         byte obj_type;
     606   [ -  +  -  -  :         65 :         if (mp_obj_is_int(o)) {
                   -  - ]
     607                 :         65 :             obj_type = MP_PERSISTENT_OBJ_INT;
     608                 :            :         #if MICROPY_PY_BUILTINS_COMPLEX
     609   [ #  #  #  #  :          0 :         } else if (mp_obj_is_type(o, &mp_type_complex)) {
          #  #  #  #  #  
                #  #  # ]
     610                 :          0 :             obj_type = MP_PERSISTENT_OBJ_COMPLEX;
     611                 :            :         #endif
     612                 :            :         } else {
     613   [ #  #  #  #  :          0 :             assert(mp_obj_is_float(o));
          #  #  #  #  #  
                #  #  # ]
     614                 :          0 :             obj_type = MP_PERSISTENT_OBJ_FLOAT;
     615                 :            :         }
     616                 :         65 :         vstr_t vstr;
     617                 :         65 :         mp_print_t pr;
     618                 :         65 :         vstr_init_print(&vstr, 10, &pr);
     619                 :         65 :         mp_obj_print_helper(&pr, o, PRINT_REPR);
     620                 :         65 :         mp_print_bytes(print, &obj_type, 1);
     621                 :         65 :         mp_print_uint(print, vstr.len);
     622                 :         65 :         mp_print_bytes(print, (const byte *)vstr.buf, vstr.len);
     623                 :         65 :         vstr_clear(&vstr);
     624                 :            :     }
     625                 :        138 : }
     626                 :            : 
     627                 :            : #endif // MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PERSISTENT_CODE_SAVE_FUN
     628                 :            : 
     629                 :            : #if MICROPY_PERSISTENT_CODE_SAVE
     630                 :            : 
     631                 :            : static void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) {
     632                 :            :     // Save function kind and data length
     633                 :            :     mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE));
     634                 :            : 
     635                 :            :     // Save function code.
     636                 :            :     mp_print_bytes(print, rc->fun_data, rc->fun_data_len);
     637                 :            : 
     638                 :            :     #if MICROPY_EMIT_MACHINE_CODE
     639                 :            :     if (rc->kind == MP_CODE_NATIVE_PY) {
     640                 :            :         // Save prelude size
     641                 :            :         mp_print_uint(print, rc->prelude_offset);
     642                 :            :     } else if (rc->kind == MP_CODE_NATIVE_VIPER || rc->kind == MP_CODE_NATIVE_ASM) {
     643                 :            :         // Save basic scope info for viper and asm
     644                 :            :         // Viper/asm functions don't support generator, variable args, or default keyword args
     645                 :            :         // so (scope_flags & MP_SCOPE_FLAG_ALL_SIG) for these functions is always 0.
     646                 :            :         mp_print_uint(print, 0);
     647                 :            :         #if MICROPY_EMIT_INLINE_ASM
     648                 :            :         if (rc->kind == MP_CODE_NATIVE_ASM) {
     649                 :            :             mp_print_uint(print, rc->asm_n_pos_args);
     650                 :            :             mp_print_uint(print, rc->asm_type_sig);
     651                 :            :         }
     652                 :            :         #endif
     653                 :            :     }
     654                 :            :     #endif
     655                 :            : 
     656                 :            :     if (rc->n_children) {
     657                 :            :         mp_print_uint(print, rc->n_children);
     658                 :            :         for (size_t i = 0; i < rc->n_children; ++i) {
     659                 :            :             save_raw_code(print, rc->children[i]);
     660                 :            :         }
     661                 :            :     }
     662                 :            : }
     663                 :            : 
     664                 :            : void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) {
     665                 :            :     // header contains:
     666                 :            :     //  byte  'M'
     667                 :            :     //  byte  version
     668                 :            :     //  byte  native arch (and sub-version if native)
     669                 :            :     //  byte  number of bits in a small int
     670                 :            :     byte header[4] = {
     671                 :            :         'M',
     672                 :            :         MPY_VERSION,
     673                 :            :         cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0,
     674                 :            :         #if MICROPY_DYNAMIC_COMPILER
     675                 :            :         mp_dynamic_compiler.small_int_bits,
     676                 :            :         #else
     677                 :            :         MP_SMALL_INT_BITS,
     678                 :            :         #endif
     679                 :            :     };
     680                 :            :     mp_print_bytes(print, header, sizeof(header));
     681                 :            : 
     682                 :            :     // Number of entries in constant table.
     683                 :            :     mp_print_uint(print, cm->n_qstr);
     684                 :            :     mp_print_uint(print, cm->n_obj);
     685                 :            : 
     686                 :            :     // Save qstrs.
     687                 :            :     for (size_t i = 0; i < cm->n_qstr; ++i) {
     688                 :            :         save_qstr(print, cm->context->constants.qstr_table[i]);
     689                 :            :     }
     690                 :            : 
     691                 :            :     // Save constant objects.
     692                 :            :     for (size_t i = 0; i < cm->n_obj; ++i) {
     693                 :            :         save_obj(print, (mp_obj_t)cm->context->constants.obj_table[i]);
     694                 :            :     }
     695                 :            : 
     696                 :            :     // Save outer raw code, which will save all its child raw codes.
     697                 :            :     save_raw_code(print, cm->rc);
     698                 :            : }
     699                 :            : 
     700                 :            : #endif // MICROPY_PERSISTENT_CODE_SAVE
     701                 :            : 
     702                 :            : #if MICROPY_PERSISTENT_CODE_SAVE_FILE
     703                 :            : 
     704                 :            : #include <unistd.h>
     705                 :            : #include <sys/stat.h>
     706                 :            : #include <fcntl.h>
     707                 :            : 
     708                 :            : static void fd_print_strn(void *env, const char *str, size_t len) {
     709                 :            :     int fd = (intptr_t)env;
     710                 :            :     MP_THREAD_GIL_EXIT();
     711                 :            :     ssize_t ret = write(fd, str, len);
     712                 :            :     MP_THREAD_GIL_ENTER();
     713                 :            :     (void)ret;
     714                 :            : }
     715                 :            : 
     716                 :            : void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename) {
     717                 :            :     MP_THREAD_GIL_EXIT();
     718                 :            :     int fd = open(qstr_str(filename), O_WRONLY | O_CREAT | O_TRUNC, 0644);
     719                 :            :     MP_THREAD_GIL_ENTER();
     720                 :            :     if (fd < 0) {
     721                 :            :         mp_raise_OSError_with_filename(errno, qstr_str(filename));
     722                 :            :     }
     723                 :            :     mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn};
     724                 :            :     mp_raw_code_save(cm, &fd_print);
     725                 :            :     MP_THREAD_GIL_EXIT();
     726                 :            :     close(fd);
     727                 :            :     MP_THREAD_GIL_ENTER();
     728                 :            : }
     729                 :            : 
     730                 :            : #endif // MICROPY_PERSISTENT_CODE_SAVE_FILE
     731                 :            : 
     732                 :            : #if MICROPY_PERSISTENT_CODE_SAVE_FUN
     733                 :            : 
     734                 :            : #include "py/bc0.h"
     735                 :            : #include "py/objfun.h"
     736                 :            : #include "py/smallint.h"
     737                 :            : #include "py/gc.h"
     738                 :            : 
     739                 :            : #define MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode) (MP_BC_UNWIND_JUMP <= (opcode) && (opcode) <= MP_BC_POP_JUMP_IF_FALSE)
     740                 :            : 
     741                 :            : typedef struct _bit_vector_t {
     742                 :            :     size_t max_bit_set;
     743                 :            :     size_t alloc;
     744                 :            :     uintptr_t *bits;
     745                 :            : } bit_vector_t;
     746                 :            : 
     747                 :         14 : static void bit_vector_init(bit_vector_t *self) {
     748                 :         14 :     self->max_bit_set = 0;
     749                 :         14 :     self->alloc = 1;
     750                 :         14 :     self->bits = m_new(uintptr_t, self->alloc);
     751                 :         14 : }
     752                 :            : 
     753                 :         14 : static void bit_vector_clear(bit_vector_t *self) {
     754                 :         14 :     m_del(uintptr_t, self->bits, self->alloc);
     755                 :         14 : }
     756                 :            : 
     757                 :        193 : static bool bit_vector_is_set(bit_vector_t *self, size_t index) {
     758                 :        193 :     const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE;
     759                 :        193 :     return index / bits_size < self->alloc
     760   [ +  -  +  + ]:        193 :            && (self->bits[index / bits_size] & (1 << (index % bits_size))) != 0;
     761                 :            : }
     762                 :            : 
     763                 :        100 : static void bit_vector_set(bit_vector_t *self, size_t index) {
     764                 :        100 :     const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE;
     765                 :        100 :     self->max_bit_set = MAX(self->max_bit_set, index);
     766         [ +  + ]:        100 :     if (index / bits_size >= self->alloc) {
     767                 :          1 :         size_t new_alloc = self->alloc * 2;
     768                 :          1 :         self->bits = m_renew(uintptr_t, self->bits, self->alloc, new_alloc);
     769                 :          1 :         self->alloc = new_alloc;
     770                 :            :     }
     771                 :        100 :     self->bits[index / bits_size] |= 1 << (index % bits_size);
     772                 :        100 : }
     773                 :            : 
     774                 :            : typedef struct _mp_opcode_t {
     775                 :            :     uint8_t opcode;
     776                 :            :     uint8_t format;
     777                 :            :     uint8_t size;
     778                 :            :     mp_int_t arg;
     779                 :            :     uint8_t extra_arg;
     780                 :            : } mp_opcode_t;
     781                 :            : 
     782                 :        270 : static mp_opcode_t mp_opcode_decode(const uint8_t *ip) {
     783                 :        270 :     const uint8_t *ip_start = ip;
     784                 :        270 :     uint8_t opcode = *ip++;
     785                 :        270 :     uint8_t opcode_format = MP_BC_FORMAT(opcode);
     786                 :        270 :     mp_uint_t arg = 0;
     787                 :        270 :     uint8_t extra_arg = 0;
     788         [ +  + ]:        270 :     if (opcode_format == MP_BC_FORMAT_QSTR || opcode_format == MP_BC_FORMAT_VAR_UINT) {
     789                 :         97 :         arg = *ip & 0x7f;
     790   [ +  +  +  + ]:         97 :         if (opcode == MP_BC_LOAD_CONST_SMALL_INT && (arg & 0x40) != 0) {
     791                 :          1 :             arg |= (mp_uint_t)(-1) << 7;
     792                 :            :         }
     793         [ +  + ]:         98 :         while ((*ip & 0x80) != 0) {
     794                 :          1 :             arg = (arg << 7) | (*++ip & 0x7f);
     795                 :            :         }
     796                 :         97 :         ++ip;
     797         [ +  + ]:        173 :     } else if (opcode_format == MP_BC_FORMAT_OFFSET) {
     798         [ +  + ]:         22 :         if ((*ip & 0x80) == 0) {
     799                 :         20 :             arg = *ip++;
     800         [ +  + ]:         20 :             if (MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode)) {
     801                 :          5 :                 arg -= 0x40;
     802                 :            :             }
     803                 :            :         } else {
     804                 :          2 :             arg = (ip[0] & 0x7f) | (ip[1] << 7);
     805                 :          2 :             ip += 2;
     806         [ +  - ]:          2 :             if (MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode)) {
     807                 :          2 :                 arg -= 0x4000;
     808                 :            :             }
     809                 :            :         }
     810                 :            :     }
     811         [ +  + ]:        270 :     if ((opcode & MP_BC_MASK_EXTRA_BYTE) == 0) {
     812                 :          7 :         extra_arg = *ip++;
     813                 :            :     }
     814                 :            : 
     815                 :        270 :     mp_opcode_t op = { opcode, opcode_format, ip - ip_start, arg, extra_arg };
     816                 :        270 :     return op;
     817                 :            : }
     818                 :            : 
     819                 :          7 : mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, const uint8_t *bytecode) {
     820                 :          7 :     const uint8_t *fun_data = bytecode;
     821                 :          7 :     const uint8_t *fun_data_top = fun_data + gc_nbytes(fun_data);
     822                 :            : 
     823                 :            :     // Extract function information.
     824                 :          7 :     const byte *ip = fun_data;
     825         [ +  + ]:         10 :     MP_BC_PRELUDE_SIG_DECODE(ip);
     826         [ +  + ]:          8 :     MP_BC_PRELUDE_SIZE_DECODE(ip);
     827                 :            : 
     828                 :            :     // Track the qstrs used by the function.
     829                 :          7 :     bit_vector_t qstr_table_used;
     830                 :          7 :     bit_vector_init(&qstr_table_used);
     831                 :            : 
     832                 :            :     // Track the objects used by the function.
     833                 :          7 :     bit_vector_t obj_table_used;
     834                 :          7 :     bit_vector_init(&obj_table_used);
     835                 :            : 
     836                 :          7 :     const byte *ip_names = ip;
     837                 :          7 :     mp_uint_t simple_name = mp_decode_uint(&ip_names);
     838                 :          7 :     bit_vector_set(&qstr_table_used, simple_name);
     839         [ +  + ]:         14 :     for (size_t i = 0; i < n_pos_args + n_kwonly_args; ++i) {
     840                 :          7 :         mp_uint_t arg_name = mp_decode_uint(&ip_names);
     841                 :          7 :         bit_vector_set(&qstr_table_used, arg_name);
     842                 :            :     }
     843                 :            : 
     844                 :            :     // Skip pass source code info and cell info.
     845                 :            :     // Then ip points to the start of the opcodes.
     846                 :          7 :     ip += n_info + n_cell;
     847                 :            : 
     848                 :            :     // Decode bytecode.
     849         [ +  + ]:        277 :     while (ip < fun_data_top) {
     850                 :        270 :         mp_opcode_t op = mp_opcode_decode(ip);
     851         [ +  + ]:        270 :         if (op.opcode == MP_BC_BASE_RESERVED) {
     852                 :            :             // End of opcodes.
     853                 :            :             fun_data_top = ip;
     854         [ +  + ]:        263 :         } else if (op.opcode == MP_BC_LOAD_CONST_OBJ) {
     855                 :         66 :             bit_vector_set(&obj_table_used, op.arg);
     856         [ +  + ]:        197 :         } else if (op.format == MP_BC_FORMAT_QSTR) {
     857                 :         20 :             bit_vector_set(&qstr_table_used, op.arg);
     858                 :            :         }
     859                 :        270 :         ip += op.size;
     860                 :            :     }
     861                 :            : 
     862                 :          7 :     mp_uint_t fun_data_len = fun_data_top - fun_data;
     863                 :            : 
     864                 :          7 :     mp_print_t print;
     865                 :          7 :     vstr_t vstr;
     866                 :          7 :     vstr_init_print(&vstr, 64, &print);
     867                 :            : 
     868                 :            :     // Start with .mpy header.
     869                 :          7 :     const uint8_t header[4] = { 'M', MPY_VERSION, 0, MP_SMALL_INT_BITS };
     870                 :          7 :     mp_print_bytes(&print, header, sizeof(header));
     871                 :            : 
     872                 :            :     // Number of entries in constant table.
     873                 :          7 :     mp_print_uint(&print, qstr_table_used.max_bit_set + 1);
     874                 :          7 :     mp_print_uint(&print, obj_table_used.max_bit_set + 1);
     875                 :            : 
     876                 :            :     // Save qstrs.
     877         [ +  + ]:        129 :     for (size_t i = 0; i <= qstr_table_used.max_bit_set; ++i) {
     878         [ +  + ]:        122 :         if (bit_vector_is_set(&qstr_table_used, i)) {
     879                 :         21 :             save_qstr(&print, consts->qstr_table[i]);
     880                 :            :         } else {
     881                 :        101 :             save_qstr(&print, MP_QSTR_);
     882                 :            :         }
     883                 :            :     }
     884                 :            : 
     885                 :            :     // Save constant objects.
     886         [ +  + ]:         78 :     for (size_t i = 0; i <= obj_table_used.max_bit_set; ++i) {
     887         [ +  + ]:         71 :         if (bit_vector_is_set(&obj_table_used, i)) {
     888                 :         66 :             save_obj(&print, consts->obj_table[i]);
     889                 :            :         } else {
     890                 :          5 :             save_obj(&print, mp_const_none);
     891                 :            :         }
     892                 :            :     }
     893                 :            : 
     894                 :          7 :     bit_vector_clear(&qstr_table_used);
     895                 :          7 :     bit_vector_clear(&obj_table_used);
     896                 :            : 
     897                 :            :     // Save function kind and data length.
     898                 :          7 :     mp_print_uint(&print, fun_data_len << 3);
     899                 :            : 
     900                 :            :     // Save function code.
     901                 :          7 :     mp_print_bytes(&print, fun_data, fun_data_len);
     902                 :            : 
     903                 :            :     // Create and return bytes representing the .mpy data.
     904                 :          7 :     return mp_obj_new_bytes_from_vstr(&vstr);
     905                 :            : }
     906                 :            : 
     907                 :            : #endif // MICROPY_PERSISTENT_CODE_SAVE_FUN
     908                 :            : 
     909                 :            : #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE
     910                 :            : // An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them.
     911                 :            : MP_REGISTER_ROOT_POINTER(mp_obj_t track_reloc_code_list);
     912                 :            : #endif

Generated by: LCOV version 1.15-5-g462f71d