Skip to content

Commit a11ba77

Browse files
committed
py/persistentcode: Add mp_raw_code_save_fun_to_bytes.
Serialises a bytecode function/generator to a valid .mpy as bytes. Signed-off-by: Damien George <damien@micropython.org>
1 parent ceb8ba6 commit a11ba77

File tree

3 files changed

+194
-2
lines changed

3 files changed

+194
-2
lines changed

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@
342342
#define MICROPY_PERSISTENT_CODE_SAVE_FILE (0)
343343
#endif
344344

345+
// Whether to support converting functions to persistent code (bytes)
346+
#ifndef MICROPY_PERSISTENT_CODE_SAVE_FUN
347+
#define MICROPY_PERSISTENT_CODE_SAVE_FUN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING)
348+
#endif
349+
345350
// Whether generated code can persist independently of the VM/runtime instance
346351
// This is enabled automatically when needed by other features
347352
#ifndef MICROPY_PERSISTENT_CODE

py/persistentcode.c

Lines changed: 188 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *context) {
526526

527527
#endif // MICROPY_PERSISTENT_CODE_LOAD
528528

529-
#if MICROPY_PERSISTENT_CODE_SAVE
529+
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PERSISTENT_CODE_SAVE_FUN
530530

531531
#include "py/objstr.h"
532532

@@ -624,6 +624,10 @@ static void save_obj(mp_print_t *print, mp_obj_t o) {
624624
}
625625
}
626626

627+
#endif // MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PERSISTENT_CODE_SAVE_FUN
628+
629+
#if MICROPY_PERSISTENT_CODE_SAVE
630+
627631
static void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) {
628632
// Save function kind and data length
629633
mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE));
@@ -693,6 +697,8 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) {
693697
save_raw_code(print, cm->rc);
694698
}
695699

700+
#endif // MICROPY_PERSISTENT_CODE_SAVE
701+
696702
#if MICROPY_PERSISTENT_CODE_SAVE_FILE
697703

698704
#include <unistd.h>
@@ -723,4 +729,184 @@ void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename) {
723729

724730
#endif // MICROPY_PERSISTENT_CODE_SAVE_FILE
725731

726-
#endif // MICROPY_PERSISTENT_CODE_SAVE
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+
static void bit_vector_init(bit_vector_t *self) {
748+
self->max_bit_set = 0;
749+
self->alloc = 1;
750+
self->bits = m_new(uintptr_t, self->alloc);
751+
}
752+
753+
static void bit_vector_clear(bit_vector_t *self) {
754+
m_del(uintptr_t, self->bits, self->alloc);
755+
}
756+
757+
static bool bit_vector_is_set(bit_vector_t *self, size_t index) {
758+
const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE;
759+
return index / bits_size < self->alloc
760+
&& (self->bits[index / bits_size] & (1 << (index % bits_size))) != 0;
761+
}
762+
763+
static void bit_vector_set(bit_vector_t *self, size_t index) {
764+
const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE;
765+
self->max_bit_set = MAX(self->max_bit_set, index);
766+
if (index / bits_size >= self->alloc) {
767+
size_t new_alloc = self->alloc * 2;
768+
self->bits = m_renew(uintptr_t, self->bits, self->alloc, new_alloc);
769+
self->alloc = new_alloc;
770+
}
771+
self->bits[index / bits_size] |= 1 << (index % bits_size);
772+
}
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+
static mp_opcode_t mp_opcode_decode(const uint8_t *ip) {
783+
const uint8_t *ip_start = ip;
784+
uint8_t opcode = *ip++;
785+
uint8_t opcode_format = MP_BC_FORMAT(opcode);
786+
mp_uint_t arg = 0;
787+
uint8_t extra_arg = 0;
788+
if (opcode_format == MP_BC_FORMAT_QSTR || opcode_format == MP_BC_FORMAT_VAR_UINT) {
789+
arg = *ip & 0x7f;
790+
if (opcode == MP_BC_LOAD_CONST_SMALL_INT && (arg & 0x40) != 0) {
791+
arg |= (mp_uint_t)(-1) << 7;
792+
}
793+
while ((*ip & 0x80) != 0) {
794+
arg = (arg << 7) | (*++ip & 0x7f);
795+
}
796+
++ip;
797+
} else if (opcode_format == MP_BC_FORMAT_OFFSET) {
798+
if ((*ip & 0x80) == 0) {
799+
arg = *ip++;
800+
if (MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode)) {
801+
arg -= 0x40;
802+
}
803+
} else {
804+
arg = (ip[0] & 0x7f) | (ip[1] << 7);
805+
ip += 2;
806+
if (MP_BC_OPCODE_HAS_SIGNED_OFFSET(opcode)) {
807+
arg -= 0x4000;
808+
}
809+
}
810+
}
811+
if ((opcode & MP_BC_MASK_EXTRA_BYTE) == 0) {
812+
extra_arg = *ip++;
813+
}
814+
815+
mp_opcode_t op = { opcode, opcode_format, ip - ip_start, arg, extra_arg };
816+
return op;
817+
}
818+
819+
mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, const uint8_t *bytecode) {
820+
const uint8_t *fun_data = bytecode;
821+
const uint8_t *fun_data_top = fun_data + gc_nbytes(fun_data);
822+
823+
// Extract function information.
824+
const byte *ip = fun_data;
825+
MP_BC_PRELUDE_SIG_DECODE(ip);
826+
MP_BC_PRELUDE_SIZE_DECODE(ip);
827+
828+
// Track the qstrs used by the function.
829+
bit_vector_t qstr_table_used;
830+
bit_vector_init(&qstr_table_used);
831+
832+
// Track the objects used by the function.
833+
bit_vector_t obj_table_used;
834+
bit_vector_init(&obj_table_used);
835+
836+
const byte *ip_names = ip;
837+
mp_uint_t simple_name = mp_decode_uint(&ip_names);
838+
bit_vector_set(&qstr_table_used, simple_name);
839+
for (size_t i = 0; i < n_pos_args + n_kwonly_args; ++i) {
840+
mp_uint_t arg_name = mp_decode_uint(&ip_names);
841+
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+
ip += n_info + n_cell;
847+
848+
// Decode bytecode.
849+
while (ip < fun_data_top) {
850+
mp_opcode_t op = mp_opcode_decode(ip);
851+
if (op.opcode == MP_BC_BASE_RESERVED) {
852+
// End of opcodes.
853+
fun_data_top = ip;
854+
} else if (op.opcode == MP_BC_LOAD_CONST_OBJ) {
855+
bit_vector_set(&obj_table_used, op.arg);
856+
} else if (op.format == MP_BC_FORMAT_QSTR) {
857+
bit_vector_set(&qstr_table_used, op.arg);
858+
}
859+
ip += op.size;
860+
}
861+
862+
mp_uint_t fun_data_len = fun_data_top - fun_data;
863+
864+
mp_print_t print;
865+
vstr_t vstr;
866+
vstr_init_print(&vstr, 64, &print);
867+
868+
// Start with .mpy header.
869+
const uint8_t header[4] = { 'M', MPY_VERSION, 0, MP_SMALL_INT_BITS };
870+
mp_print_bytes(&print, header, sizeof(header));
871+
872+
// Number of entries in constant table.
873+
mp_print_uint(&print, qstr_table_used.max_bit_set + 1);
874+
mp_print_uint(&print, obj_table_used.max_bit_set + 1);
875+
876+
// Save qstrs.
877+
for (size_t i = 0; i <= qstr_table_used.max_bit_set; ++i) {
878+
if (bit_vector_is_set(&qstr_table_used, i)) {
879+
save_qstr(&print, consts->qstr_table[i]);
880+
} else {
881+
save_qstr(&print, MP_QSTR_);
882+
}
883+
}
884+
885+
// Save constant objects.
886+
for (size_t i = 0; i <= obj_table_used.max_bit_set; ++i) {
887+
if (bit_vector_is_set(&obj_table_used, i)) {
888+
save_obj(&print, consts->obj_table[i]);
889+
} else {
890+
save_obj(&print, mp_const_none);
891+
}
892+
}
893+
894+
bit_vector_clear(&qstr_table_used);
895+
bit_vector_clear(&obj_table_used);
896+
897+
// Save function kind and data length.
898+
mp_print_uint(&print, fun_data_len << 3);
899+
900+
// Save function code.
901+
mp_print_bytes(&print, fun_data, fun_data_len);
902+
903+
// Create and return bytes representing the .mpy data.
904+
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

py/persistentcode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ void mp_raw_code_load_file(qstr filename, mp_compiled_module_t *ctx);
121121

122122
void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print);
123123
void mp_raw_code_save_file(mp_compiled_module_t *cm, qstr filename);
124+
mp_obj_t mp_raw_code_save_fun_to_bytes(const mp_module_constants_t *consts, const uint8_t *bytecode);
124125

125126
void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text);
126127

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy