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#define QSTR_LAST_STATIC MP_QSTR_zip
44
45#if MICROPY_DYNAMIC_COMPILER
46#define MPY_FEATURE_ARCH_DYNAMIC mp_dynamic_compiler.native_arch
47#else
48#define MPY_FEATURE_ARCH_DYNAMIC MPY_FEATURE_ARCH
49#endif
50
51#if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER)
52// The bytecode will depend on the number of bits in a small-int, and
53// this function computes that (could make it a fixed constant, but it
54// would need to be defined in mpconfigport.h).
55STATIC int mp_small_int_bits(void) {
56 mp_int_t i = MP_SMALL_INT_MAX;
57 int n = 1;
58 while (i != 0) {
59 i >>= 1;
60 ++n;
61 }
62 return n;
63}
64#endif
65
66#define QSTR_WINDOW_SIZE (32)
67
68typedef struct _qstr_window_t {
69 uint16_t idx; // indexes the head of the window
70 uint16_t window[QSTR_WINDOW_SIZE];
71} qstr_window_t;
72
73// Push a qstr to the head of the window, and the tail qstr is overwritten
74STATIC void qstr_window_push(qstr_window_t *qw, qstr qst) {
75 qw->idx = (qw->idx + 1) % QSTR_WINDOW_SIZE;
76 qw->window[qw->idx] = qst;
77}
78
79// Pull an existing qstr from within the window to the head of the window
80STATIC qstr qstr_window_pull(qstr_window_t *qw, size_t idx) {
81 qstr qst = qw->window[idx];
82 if (idx > qw->idx) {
83 memmove(&qw->window[idx], &qw->window[idx + 1], (QSTR_WINDOW_SIZE - idx - 1) * sizeof(uint16_t));
84 qw->window[QSTR_WINDOW_SIZE - 1] = qw->window[0];
85 idx = 0;
86 }
87 memmove(&qw->window[idx], &qw->window[idx + 1], (qw->idx - idx) * sizeof(uint16_t));
88 qw->window[qw->idx] = qst;
89 return qst;
90}
91
92#if MICROPY_PERSISTENT_CODE_LOAD
93
94// Access a qstr at the given index, relative to the head of the window (0=head)
95STATIC qstr qstr_window_access(qstr_window_t *qw, size_t idx) {
96 return qstr_window_pull(qw, (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE);
97}
98
99#endif
100
101#if MICROPY_PERSISTENT_CODE_SAVE
102
103// Insert a qstr at the head of the window, either by pulling an existing one or pushing a new one
104STATIC size_t qstr_window_insert(qstr_window_t *qw, qstr qst) {
105 for (size_t idx = 0; idx < QSTR_WINDOW_SIZE; ++idx) {
106 if (qw->window[idx] == qst) {
107 qstr_window_pull(qw, idx);
108 return (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE;
109 }
110 }
111 qstr_window_push(qw, qst);
112 return QSTR_WINDOW_SIZE;
113}
114
115#endif
116
117typedef struct _bytecode_prelude_t {
118 uint n_state;
119 uint n_exc_stack;
120 uint scope_flags;
121 uint n_pos_args;
122 uint n_kwonly_args;
123 uint n_def_pos_args;
124 uint code_info_size;
125} bytecode_prelude_t;
126
127// ip will point to start of opcodes
128// return value will point to simple_name, source_file qstrs
129STATIC byte *extract_prelude(const byte **ip, bytecode_prelude_t *prelude) {
130 MP_BC_PRELUDE_SIG_DECODE(*ip);
131 prelude->n_state = n_state;
132 prelude->n_exc_stack = n_exc_stack;
133 prelude->scope_flags = scope_flags;
134 prelude->n_pos_args = n_pos_args;
135 prelude->n_kwonly_args = n_kwonly_args;
136 prelude->n_def_pos_args = n_def_pos_args;
137 MP_BC_PRELUDE_SIZE_DECODE(*ip);
138 byte *ip_info = (byte *)*ip;
139 *ip += n_info;
140 *ip += n_cell;
141 return ip_info;
142}
143
144#endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
145
146#if MICROPY_PERSISTENT_CODE_LOAD
147
148#include "py/parsenum.h"
149
150STATIC int read_byte(mp_reader_t *reader);
151STATIC size_t read_uint(mp_reader_t *reader, byte **out);
152
153#if MICROPY_EMIT_MACHINE_CODE
154
155typedef struct _reloc_info_t {
156 mp_reader_t *reader;
157 mp_uint_t *const_table;
158} reloc_info_t;
159
160#if MICROPY_EMIT_THUMB
161STATIC void asm_thumb_rewrite_mov(uint8_t *pc, uint16_t val) {
162 // high part
163 *(uint16_t *)pc = (*(uint16_t *)pc & 0xfbf0) | (val >> 1 & 0x0400) | (val >> 12);
164 // low part
165 *(uint16_t *)(pc + 2) = (*(uint16_t *)(pc + 2) & 0x0f00) | (val << 4 & 0x7000) | (val & 0x00ff);
166
167}
168#endif
169
170STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) {
171 mp_uint_t val = qst;
172 if (is_obj) {
173 val = (mp_uint_t)MP_OBJ_NEW_QSTR(qst);
174 }
175 #if MICROPY_EMIT_X86 || MICROPY_EMIT_X64 || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN
176 pc[0] = val & 0xff;
177 pc[1] = (val >> 8) & 0xff;
178 pc[2] = (val >> 16) & 0xff;
179 pc[3] = (val >> 24) & 0xff;
180 #elif MICROPY_EMIT_THUMB
181 if (is_obj) {
182 // qstr object, movw and movt
183 asm_thumb_rewrite_mov(pc, val); // movw
184 asm_thumb_rewrite_mov(pc + 4, val >> 16); // movt
185 } else {
186 // qstr number, movw instruction
187 asm_thumb_rewrite_mov(pc, val); // movw
188 }
189 #endif
190}
191
192void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) {
193 // Relocate native code
194 reloc_info_t *ri = ri_in;
195 uint8_t op;
196 uintptr_t *addr_to_adjust = NULL;
197 while ((op = read_byte(ri->reader)) != 0xff) {
198 if (op & 1) {
199 // Point to new location to make adjustments
200 size_t addr = read_uint(ri->reader, NULL);
201 if ((addr & 1) == 0) {
202 // Point to somewhere in text
203 addr_to_adjust = &((uintptr_t *)text)[addr >> 1];
204 } else {
205 // Point to somewhere in rodata
206 addr_to_adjust = &((uintptr_t *)ri->const_table[1])[addr >> 1];
207 }
208 }
209 op >>= 1;
210 uintptr_t dest;
211 size_t n = 1;
212 if (op <= 5) {
213 if (op & 1) {
214 // Read in number of adjustments to make
215 n = read_uint(ri->reader, NULL);
216 }
217 op >>= 1;
218 if (op == 0) {
219 // Destination is text
220 dest = reloc_text;
221 } else {
222 // Destination is rodata (op=1) or bss (op=1 if no rodata, else op=2)
223 dest = ri->const_table[op];
224 }
225 } else if (op == 6) {
226 // Destination is mp_fun_table itself
227 dest = (uintptr_t)&mp_fun_table;
228 } else {
229 // Destination is an entry in mp_fun_table
230 dest = ((uintptr_t *)&mp_fun_table)[op - 7];
231 }
232 while (n--) {
233 *addr_to_adjust++ += dest;
234 }
235 }
236}
237
238#endif
239
240STATIC int read_byte(mp_reader_t *reader) {
241 return reader->readbyte(reader->data);
242}
243
244STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) {
245 while (len-- > 0) {
246 *buf++ = reader->readbyte(reader->data);
247 }
248}
249
250STATIC size_t read_uint(mp_reader_t *reader, byte **out) {
251 size_t unum = 0;
252 for (;;) {
253 byte b = reader->readbyte(reader->data);
254 if (out != NULL) {
255 **out = b;
256 ++*out;
257 }
258 unum = (unum << 7) | (b & 0x7f);
259 if ((b & 0x80) == 0) {
260 break;
261 }
262 }
263 return unum;
264}
265
266STATIC qstr load_qstr(mp_reader_t *reader, qstr_window_t *qw) {
267 size_t len = read_uint(reader, NULL);
268 if (len == 0) {
269 // static qstr
270 return read_byte(reader);
271 }
272 if (len & 1) {
273 // qstr in window
274 return qstr_window_access(qw, len >> 1);
275 }
276 len >>= 1;
277 char *str = m_new(char, len);
278 read_bytes(reader, (byte *)str, len);
279 qstr qst = qstr_from_strn(str, len);
280 m_del(char, str, len);
281 qstr_window_push(qw, qst);
282 return qst;
283}
284
285STATIC mp_obj_t load_obj(mp_reader_t *reader) {
286 byte obj_type = read_byte(reader);
287 if (obj_type == 'e') {
288 return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj);
289 } else {
290 size_t len = read_uint(reader, NULL);
291 vstr_t vstr;
292 vstr_init_len(&vstr, len);
293 read_bytes(reader, (byte *)vstr.buf, len);
294 if (obj_type == 's' || obj_type == 'b') {
295 return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr);
296 } else if (obj_type == 'i') {
297 return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
298 } else {
299 assert(obj_type == 'f' || obj_type == 'c');
300 return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL);
301 }
302 }
303}
304
305STATIC void load_prelude_qstrs(mp_reader_t *reader, qstr_window_t *qw, byte *ip) {
306 qstr simple_name = load_qstr(reader, qw);
307 ip[0] = simple_name;
308 ip[1] = simple_name >> 8;
309 qstr source_file = load_qstr(reader, qw);
310 ip[2] = source_file;
311 ip[3] = source_file >> 8;
312}
313
314STATIC void load_prelude(mp_reader_t *reader, qstr_window_t *qw, byte **ip, bytecode_prelude_t *prelude) {
315 // Read in the prelude header
316 byte *ip_read = *ip;
317 read_uint(reader, &ip_read); // read in n_state/etc (is effectively a var-uint)
318 read_uint(reader, &ip_read); // read in n_info/n_cell (is effectively a var-uint)
319
320 // Prelude header has been read into *ip, now decode and extract values from it
321 extract_prelude((const byte **)ip, prelude);
322
323 // Load qstrs in prelude
324 load_prelude_qstrs(reader, qw, ip_read);
325 ip_read += 4;
326
327 // Read remaining code info
328 read_bytes(reader, ip_read, *ip - ip_read);
329}
330
331STATIC void load_bytecode(mp_reader_t *reader, qstr_window_t *qw, byte *ip, byte *ip_top) {
332 while (ip < ip_top) {
333 *ip = read_byte(reader);
334 size_t sz;
335 uint f = mp_opcode_format(ip, &sz, false);
336 ++ip;
337 --sz;
338 if (f == MP_BC_FORMAT_QSTR) {
339 qstr qst = load_qstr(reader, qw);
340 *ip++ = qst;
341 *ip++ = qst >> 8;
342 sz -= 2;
343 } else if (f == MP_BC_FORMAT_VAR_UINT) {
344 while ((*ip++ = read_byte(reader)) & 0x80) {
345 }
346 }
347 read_bytes(reader, ip, sz);
348 ip += sz;
349 }
350}
351
352STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) {
353 // Load function kind and data length
354 size_t kind_len = read_uint(reader, NULL);
355 int kind = (kind_len & 3) + MP_CODE_BYTECODE;
356 size_t fun_data_len = kind_len >> 2;
357
358 #if !MICROPY_EMIT_MACHINE_CODE
359 if (kind != MP_CODE_BYTECODE) {
360 mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
361 }
362 #endif
363
364 uint8_t *fun_data = NULL;
365 bytecode_prelude_t prelude = {0};
366 #if MICROPY_EMIT_MACHINE_CODE
367 size_t prelude_offset = 0;
368 mp_uint_t type_sig = 0;
369 size_t n_qstr_link = 0;
370 #endif
371
372 if (kind == MP_CODE_BYTECODE) {
373 // Allocate memory for the bytecode
374 fun_data = m_new(uint8_t, fun_data_len);
375
376 // Load prelude
377 byte *ip = fun_data;
378 load_prelude(reader, qw, &ip, &prelude);
379
380 // Load bytecode
381 load_bytecode(reader, qw, ip, fun_data + fun_data_len);
382
383 #if MICROPY_EMIT_MACHINE_CODE
384 } else {
385 // Allocate memory for native data and load it
386 size_t fun_alloc;
387 MP_PLAT_ALLOC_EXEC(fun_data_len, (void **)&fun_data, &fun_alloc);
388 read_bytes(reader, fun_data, fun_data_len);
389
390 if (kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER) {
391 // Parse qstr link table and link native code
392 n_qstr_link = read_uint(reader, NULL);
393 for (size_t i = 0; i < n_qstr_link; ++i) {
394 size_t off = read_uint(reader, NULL);
395 qstr qst = load_qstr(reader, qw);
396 uint8_t *dest = fun_data + (off >> 2);
397 if ((off & 3) == 0) {
398 // Generic 16-bit link
399 dest[0] = qst & 0xff;
400 dest[1] = (qst >> 8) & 0xff;
401 } else if ((off & 3) == 3) {
402 // Generic, aligned qstr-object link
403 *(mp_obj_t *)dest = MP_OBJ_NEW_QSTR(qst);
404 } else {
405 // Architecture-specific link
406 arch_link_qstr(dest, (off & 3) == 2, qst);
407 }
408 }
409 }
410
411 if (kind == MP_CODE_NATIVE_PY) {
412 // Extract prelude for later use
413 prelude_offset = read_uint(reader, NULL);
414 const byte *ip = fun_data + prelude_offset;
415 byte *ip_info = extract_prelude(&ip, &prelude);
416 // Load qstrs in prelude
417 load_prelude_qstrs(reader, qw, ip_info);
418 } else {
419 // Load basic scope info for viper and asm
420 prelude.scope_flags = read_uint(reader, NULL);
421 prelude.n_pos_args = 0;
422 prelude.n_kwonly_args = 0;
423 if (kind == MP_CODE_NATIVE_ASM) {
424 prelude.n_pos_args = read_uint(reader, NULL);
425 type_sig = read_uint(reader, NULL);
426 }
427 }
428 #endif
429 }
430
431 size_t n_obj = 0;
432 size_t n_raw_code = 0;
433 mp_uint_t *const_table = NULL;
434
435 if (kind != MP_CODE_NATIVE_ASM) {
436 // Load constant table for bytecode, native and viper
437
438 // Number of entries in constant table
439 n_obj = read_uint(reader, NULL);
440 n_raw_code = read_uint(reader, NULL);
441
442 // Allocate constant table
443 size_t n_alloc = prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code;
444 #if MICROPY_EMIT_MACHINE_CODE
445 if (kind != MP_CODE_BYTECODE) {
446 ++n_alloc; // additional entry for mp_fun_table
447 if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
448 ++n_alloc; // additional entry for rodata
449 }
450 if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) {
451 ++n_alloc; // additional entry for BSS
452 }
453 }
454 #endif
455
456 const_table = m_new(mp_uint_t, n_alloc);
457 mp_uint_t *ct = const_table;
458
459 // Load function argument names (initial entries in const_table)
460 // (viper has n_pos_args=n_kwonly_args=0 so doesn't load any qstrs here)
461 for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) {
462 *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader, qw));
463 }
464
465 #if MICROPY_EMIT_MACHINE_CODE
466 if (kind != MP_CODE_BYTECODE) {
467 // Populate mp_fun_table entry
468 *ct++ = (mp_uint_t)(uintptr_t)&mp_fun_table;
469
470 // Allocate and load rodata if needed
471 if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
472 size_t size = read_uint(reader, NULL);
473 uint8_t *rodata = m_new(uint8_t, size);
474 read_bytes(reader, rodata, size);
475 *ct++ = (uintptr_t)rodata;
476 }
477
478 // Allocate BSS if needed
479 if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) {
480 size_t size = read_uint(reader, NULL);
481 uint8_t *bss = m_new0(uint8_t, size);
482 *ct++ = (uintptr_t)bss;
483 }
484 }
485 #endif
486
487 // Load constant objects and raw code children
488 for (size_t i = 0; i < n_obj; ++i) {
489 *ct++ = (mp_uint_t)load_obj(reader);
490 }
491 for (size_t i = 0; i < n_raw_code; ++i) {
492 *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader, qw);
493 }
494 }
495
496 // Create raw_code and return it
497 mp_raw_code_t *rc = mp_emit_glue_new_raw_code();
498 if (kind == MP_CODE_BYTECODE) {
499 // Assign bytecode to raw code object
500 mp_emit_glue_assign_bytecode(rc, fun_data,
501 #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
502 fun_data_len,
503 #endif
504 const_table,
505 #if MICROPY_PERSISTENT_CODE_SAVE
506 n_obj, n_raw_code,
507 #endif
508 prelude.scope_flags);
509
510 #if MICROPY_EMIT_MACHINE_CODE
511 } else {
512 // Relocate and commit code to executable address space
513 reloc_info_t ri = {reader, const_table};
514 #if defined(MP_PLAT_COMMIT_EXEC)
515 void *opt_ri = (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL;
516 fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri);
517 #else
518 if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) {
519 #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE
520 // If native code needs relocations then it's not guaranteed that a pointer to
521 // the head of `buf` (containing the machine code) will be retained for the GC
522 // to trace. This is because native functions can start inside `buf` and so
523 // it's possible that the only GC-reachable pointers are pointers inside `buf`.
524 // So put this `buf` on a list of reachable root pointers.
525 if (MP_STATE_PORT(track_reloc_code_list) == MP_OBJ_NULL) {
526 MP_STATE_PORT(track_reloc_code_list) = mp_obj_new_list(0, NULL);
527 }
528 mp_obj_list_append(MP_STATE_PORT(track_reloc_code_list), MP_OBJ_FROM_PTR(fun_data));
529 #endif
530 // Do the relocations.
531 mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data);
532 }
533 #endif
534
535 // Assign native code to raw code object
536 mp_emit_glue_assign_native(rc, kind,
537 fun_data, fun_data_len, const_table,
538 #if MICROPY_PERSISTENT_CODE_SAVE
539 prelude_offset,
540 n_obj, n_raw_code,
541 n_qstr_link, NULL,
542 #endif
543 prelude.n_pos_args, prelude.scope_flags, type_sig);
544 #endif
545 }
546 return rc;
547}
548
549mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) {
550 byte header[4];
551 read_bytes(reader, header, sizeof(header));
552 if (header[0] != 'M'
553 || header[1] != MPY_VERSION
554 || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS
555 || header[3] > mp_small_int_bits()
556 || read_uint(reader, NULL) > QSTR_WINDOW_SIZE) {
557 mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
558 }
559 if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) {
560 byte arch = MPY_FEATURE_DECODE_ARCH(header[2]);
561 if (!MPY_FEATURE_ARCH_TEST(arch)) {
562 mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch"));
563 }
564 }
565 qstr_window_t qw;
566 qw.idx = 0;
567 mp_raw_code_t *rc = load_raw_code(reader, &qw);
568 reader->close(reader->data);
569 return rc;
570}
571
572mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) {
573 mp_reader_t reader;
574 mp_reader_new_mem(&reader, buf, len, 0);
575 return mp_raw_code_load(&reader);
576}
577
578#if MICROPY_HAS_FILE_READER
579
580mp_raw_code_t *mp_raw_code_load_file(const char *filename) {
581 mp_reader_t reader;
582 mp_reader_new_file(&reader, filename);
583 return mp_raw_code_load(&reader);
584}
585
586#endif // MICROPY_HAS_FILE_READER
587
588#endif // MICROPY_PERSISTENT_CODE_LOAD
589
590#if MICROPY_PERSISTENT_CODE_SAVE
591
592#include "py/objstr.h"
593
594STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) {
595 print->print_strn(print->data, (const char *)data, len);
596}
597
598#define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7)
599STATIC void mp_print_uint(mp_print_t *print, size_t n) {
600 byte buf[BYTES_FOR_INT];
601 byte *p = buf + sizeof(buf);
602 *--p = n & 0x7f;
603 n >>= 7;
604 for (; n != 0; n >>= 7) {
605 *--p = 0x80 | (n & 0x7f);
606 }
607 print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p);
608}
609
610STATIC void save_qstr(mp_print_t *print, qstr_window_t *qw, qstr qst) {
611 if (qst <= QSTR_LAST_STATIC) {
612 // encode static qstr
613 byte buf[2] = {0, qst & 0xff};
614 mp_print_bytes(print, buf, 2);
615 return;
616 }
617 size_t idx = qstr_window_insert(qw, qst);
618 if (idx < QSTR_WINDOW_SIZE) {
619 // qstr found in window, encode index to it
620 mp_print_uint(print, idx << 1 | 1);
621 return;
622 }
623 size_t len;
624 const byte *str = qstr_data(qst, &len);
625 mp_print_uint(print, len << 1);
626 mp_print_bytes(print, str, len);
627}
628
629STATIC void save_obj(mp_print_t *print, mp_obj_t o) {
630 if (mp_obj_is_str_or_bytes(o)) {
631 byte obj_type;
632 if (mp_obj_is_str(o)) {
633 obj_type = 's';
634 } else {
635 obj_type = 'b';
636 }
637 size_t len;
638 const char *str = mp_obj_str_get_data(o, &len);
639 mp_print_bytes(print, &obj_type, 1);
640 mp_print_uint(print, len);
641 mp_print_bytes(print, (const byte *)str, len);
642 } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) {
643 byte obj_type = 'e';
644 mp_print_bytes(print, &obj_type, 1);
645 } else {
646 // we save numbers using a simplistic text representation
647 // TODO could be improved
648 byte obj_type;
649 if (mp_obj_is_type(o, &mp_type_int)) {
650 obj_type = 'i';
651 #if MICROPY_PY_BUILTINS_COMPLEX
652 } else if (mp_obj_is_type(o, &mp_type_complex)) {
653 obj_type = 'c';
654 #endif
655 } else {
656 assert(mp_obj_is_float(o));
657 obj_type = 'f';
658 }
659 vstr_t vstr;
660 mp_print_t pr;
661 vstr_init_print(&vstr, 10, &pr);
662 mp_obj_print_helper(&pr, o, PRINT_REPR);
663 mp_print_bytes(print, &obj_type, 1);
664 mp_print_uint(print, vstr.len);
665 mp_print_bytes(print, (const byte *)vstr.buf, vstr.len);
666 vstr_clear(&vstr);
667 }
668}
669
670STATIC void save_prelude_qstrs(mp_print_t *print, qstr_window_t *qw, const byte *ip) {
671 save_qstr(print, qw, ip[0] | (ip[1] << 8)); // simple_name
672 save_qstr(print, qw, ip[2] | (ip[3] << 8)); // source_file
673}
674
675STATIC void save_bytecode(mp_print_t *print, qstr_window_t *qw, const byte *ip, const byte *ip_top) {
676 while (ip < ip_top) {
677 size_t sz;
678 uint f = mp_opcode_format(ip, &sz, true);
679 if (f == MP_BC_FORMAT_QSTR) {
680 mp_print_bytes(print, ip, 1);
681 qstr qst = ip[1] | (ip[2] << 8);
682 save_qstr(print, qw, qst);
683 ip += 3;
684 sz -= 3;
685 }
686 mp_print_bytes(print, ip, sz);
687 ip += sz;
688 }
689}
690
691STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *qstr_window) {
692 // Save function kind and data length
693 mp_print_uint(print, (rc->fun_data_len << 2) | (rc->kind - MP_CODE_BYTECODE));
694
695 bytecode_prelude_t prelude;
696
697 if (rc->kind == MP_CODE_BYTECODE) {
698 // Extract prelude
699 const byte *ip = rc->fun_data;
700 const byte *ip_info = extract_prelude(&ip, &prelude);
701
702 // Save prelude
703 mp_print_bytes(print, rc->fun_data, ip_info - (const byte *)rc->fun_data);
704 save_prelude_qstrs(print, qstr_window, ip_info);
705 ip_info += 4;
706 mp_print_bytes(print, ip_info, ip - ip_info);
707
708 // Save bytecode
709 const byte *ip_top = (const byte *)rc->fun_data + rc->fun_data_len;
710 save_bytecode(print, qstr_window, ip, ip_top);
711 #if MICROPY_EMIT_MACHINE_CODE
712 } else {
713 // Save native code
714 mp_print_bytes(print, rc->fun_data, rc->fun_data_len);
715
716 if (rc->kind == MP_CODE_NATIVE_PY || rc->kind == MP_CODE_NATIVE_VIPER) {
717 // Save qstr link table for native code
718 mp_print_uint(print, rc->n_qstr);
719 for (size_t i = 0; i < rc->n_qstr; ++i) {
720 mp_print_uint(print, rc->qstr_link[i].off);
721 save_qstr(print, qstr_window, rc->qstr_link[i].qst);
722 }
723 }
724
725 if (rc->kind == MP_CODE_NATIVE_PY) {
726 // Save prelude size
727 mp_print_uint(print, rc->prelude_offset);
728
729 // Extract prelude and save qstrs in prelude
730 const byte *ip = (const byte *)rc->fun_data + rc->prelude_offset;
731 const byte *ip_info = extract_prelude(&ip, &prelude);
732 save_prelude_qstrs(print, qstr_window, ip_info);
733 } else {
734 // Save basic scope info for viper and asm
735 mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG);
736 prelude.n_pos_args = 0;
737 prelude.n_kwonly_args = 0;
738 if (rc->kind == MP_CODE_NATIVE_ASM) {
739 mp_print_uint(print, rc->n_pos_args);
740 mp_print_uint(print, rc->type_sig);
741 }
742 }
743 #endif
744 }
745
746 if (rc->kind != MP_CODE_NATIVE_ASM) {
747 // Save constant table for bytecode, native and viper
748
749 // Number of entries in constant table
750 mp_print_uint(print, rc->n_obj);
751 mp_print_uint(print, rc->n_raw_code);
752
753 const mp_uint_t *const_table = rc->const_table;
754
755 // Save function argument names (initial entries in const_table)
756 // (viper has n_pos_args=n_kwonly_args=0 so doesn't save any qstrs here)
757 for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) {
758 mp_obj_t o = (mp_obj_t)*const_table++;
759 save_qstr(print, qstr_window, MP_OBJ_QSTR_VALUE(o));
760 }
761
762 if (rc->kind != MP_CODE_BYTECODE) {
763 // Skip saving mp_fun_table entry
764 ++const_table;
765 }
766
767 // Save constant objects and raw code children
768 for (size_t i = 0; i < rc->n_obj; ++i) {
769 save_obj(print, (mp_obj_t)*const_table++);
770 }
771 for (size_t i = 0; i < rc->n_raw_code; ++i) {
772 save_raw_code(print, (mp_raw_code_t *)(uintptr_t)*const_table++, qstr_window);
773 }
774 }
775}
776
777STATIC bool mp_raw_code_has_native(mp_raw_code_t *rc) {
778 if (rc->kind != MP_CODE_BYTECODE) {
779 return true;
780 }
781
782 const byte *ip = rc->fun_data;
783 bytecode_prelude_t prelude;
784 extract_prelude(&ip, &prelude);
785
786 const mp_uint_t *const_table = rc->const_table
787 + prelude.n_pos_args + prelude.n_kwonly_args
788 + rc->n_obj;
789
790 for (size_t i = 0; i < rc->n_raw_code; ++i) {
791 if (mp_raw_code_has_native((mp_raw_code_t *)(uintptr_t)*const_table++)) {
792 return true;
793 }
794 }
795
796 return false;
797}
798
799void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) {
800 // header contains:
801 // byte 'M'
802 // byte version
803 // byte feature flags
804 // byte number of bits in a small int
805 // uint size of qstr window
806 byte header[4] = {
807 'M',
808 MPY_VERSION,
809 MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS_DYNAMIC),
810 #if MICROPY_DYNAMIC_COMPILER
811 mp_dynamic_compiler.small_int_bits,
812 #else
813 mp_small_int_bits(),
814 #endif
815 };
816 if (mp_raw_code_has_native(rc)) {
817 header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC);
818 }
819 mp_print_bytes(print, header, sizeof(header));
820 mp_print_uint(print, QSTR_WINDOW_SIZE);
821
822 qstr_window_t qw;
823 qw.idx = 0;
824 memset(qw.window, 0, sizeof(qw.window));
825 save_raw_code(print, rc, &qw);
826}
827
828#if MICROPY_PERSISTENT_CODE_SAVE_FILE
829
830#include <unistd.h>
831#include <sys/stat.h>
832#include <fcntl.h>
833
834STATIC void fd_print_strn(void *env, const char *str, size_t len) {
835 int fd = (intptr_t)env;
836 MP_THREAD_GIL_EXIT();
837 ssize_t ret = write(fd, str, len);
838 MP_THREAD_GIL_ENTER();
839 (void)ret;
840}
841
842void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) {
843 MP_THREAD_GIL_EXIT();
844 int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
845 MP_THREAD_GIL_ENTER();
846 mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn};
847 mp_raw_code_save(rc, &fd_print);
848 MP_THREAD_GIL_EXIT();
849 close(fd);
850 MP_THREAD_GIL_ENTER();
851}
852
853#endif // MICROPY_PERSISTENT_CODE_SAVE_FILE
854
855#endif // MICROPY_PERSISTENT_CODE_SAVE
856