Skip to content

Commit ea23520

Browse files
committed
py: Add MICROPY_DYNAMIC_COMPILER option to config compiler at runtime.
This new compile-time option allows to make the bytecode compiler configurable at runtime by setting the fields in the mp_dynamic_compiler structure. By using this feature, the compiler can generate bytecode that targets any MicroPython runtime/VM, regardless of the host and target compile-time settings. Options so far that fall under this dynamic setting are: - maximum number of bits that a small int can hold; - whether caching of lookups is used in the bytecode; - whether to use unicode strings or not (lexer behaviour differs, and therefore generated string constants differ).
1 parent 57b96a7 commit ea23520

File tree

7 files changed

+82
-22
lines changed

7 files changed

+82
-22
lines changed

py/compile.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,7 +2486,23 @@ STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn) {
24862486
// pass
24872487
} else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) {
24882488
mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn);
2489+
#if MICROPY_DYNAMIC_COMPILER
2490+
mp_uint_t sign_mask = -(1 << (mp_dynamic_compiler.small_int_bits - 1));
2491+
if ((arg & sign_mask) == 0 || (arg & sign_mask) == sign_mask) {
2492+
// integer fits in target runtime's small-int
2493+
EMIT_ARG(load_const_small_int, arg);
2494+
} else {
2495+
// integer doesn't fit, so create a multi-precision int object
2496+
// (but only create the actual object on the last pass)
2497+
if (comp->pass != MP_PASS_EMIT) {
2498+
EMIT_ARG(load_const_obj, mp_const_none);
2499+
} else {
2500+
EMIT_ARG(load_const_obj, mp_obj_new_int_from_ll(arg));
2501+
}
2502+
}
2503+
#else
24892504
EMIT_ARG(load_const_small_int, arg);
2505+
#endif
24902506
} else if (MP_PARSE_NODE_IS_LEAF(pn)) {
24912507
uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn);
24922508
switch (MP_PARSE_NODE_LEAF_KIND(pn)) {

py/emitbc.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ void mp_emit_bc_load_name(emit_t *emit, qstr qst) {
579579
(void)qst;
580580
emit_bc_pre(emit, 1);
581581
emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_NAME, qst);
582-
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) {
582+
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
583583
emit_write_bytecode_byte(emit, 0);
584584
}
585585
}
@@ -588,15 +588,15 @@ void mp_emit_bc_load_global(emit_t *emit, qstr qst) {
588588
(void)qst;
589589
emit_bc_pre(emit, 1);
590590
emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_GLOBAL, qst);
591-
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) {
591+
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
592592
emit_write_bytecode_byte(emit, 0);
593593
}
594594
}
595595

596596
void mp_emit_bc_load_attr(emit_t *emit, qstr qst) {
597597
emit_bc_pre(emit, 0);
598598
emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_ATTR, qst);
599-
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) {
599+
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
600600
emit_write_bytecode_byte(emit, 0);
601601
}
602602
}
@@ -646,7 +646,7 @@ void mp_emit_bc_store_global(emit_t *emit, qstr qst) {
646646
void mp_emit_bc_store_attr(emit_t *emit, qstr qst) {
647647
emit_bc_pre(emit, -2);
648648
emit_write_bytecode_byte_qstr(emit, MP_BC_STORE_ATTR, qst);
649-
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) {
649+
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
650650
emit_write_bytecode_byte(emit, 0);
651651
}
652652
}

py/emitglue.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,13 @@ mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, mp_uint_t n_closed_ove
204204
((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) << 0) \
205205
| ((MICROPY_PY_BUILTINS_STR_UNICODE) << 1) \
206206
)
207+
// This is a version of the flags that can be configured at runtime.
208+
#define MPY_FEATURE_FLAGS_DYNAMIC ( \
209+
((MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) << 0) \
210+
| ((MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) << 1) \
211+
)
207212

213+
#if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER)
208214
// The bytecode will depend on the number of bits in a small-int, and
209215
// this function computes that (could make it a fixed constant, but it
210216
// would need to be defined in mpconfigport.h).
@@ -217,6 +223,7 @@ STATIC int mp_small_int_bits(void) {
217223
}
218224
return n;
219225
}
226+
#endif
220227

221228
typedef struct _bytecode_prelude_t {
222229
uint n_state;
@@ -366,7 +373,7 @@ mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) {
366373
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
367374
"invalid .mpy file"));
368375
}
369-
if (header[2] != MPY_FEATURE_FLAGS || header[3] != mp_small_int_bits()) {
376+
if (header[2] != MPY_FEATURE_FLAGS || header[3] > mp_small_int_bits()) {
370377
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError,
371378
"incompatible .mpy file"));
372379
}
@@ -615,7 +622,13 @@ void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) {
615622
// byte version
616623
// byte feature flags
617624
// byte number of bits in a small int
618-
byte header[4] = {'M', 0, MPY_FEATURE_FLAGS, mp_small_int_bits()};
625+
byte header[4] = {'M', 0, MPY_FEATURE_FLAGS_DYNAMIC,
626+
#if MICROPY_DYNAMIC_COMPILER
627+
mp_dynamic_compiler.small_int_bits,
628+
#else
629+
mp_small_int_bits(),
630+
#endif
631+
};
619632
mp_print_bytes(print, header, sizeof(header));
620633

621634
save_raw_code(print, rc);

py/lexer.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -490,22 +490,25 @@ STATIC void mp_lexer_next_token_into(mp_lexer_t *lex, bool first_token) {
490490
}
491491
}
492492
if (c != MP_LEXER_EOF) {
493-
#if MICROPY_PY_BUILTINS_STR_UNICODE
494-
if (c < 0x110000 && !is_bytes) {
495-
vstr_add_char(&lex->vstr, c);
496-
} else if (c < 0x100 && is_bytes) {
497-
vstr_add_byte(&lex->vstr, c);
498-
}
499-
#else
500-
// without unicode everything is just added as an 8-bit byte
501-
if (c < 0x100) {
502-
vstr_add_byte(&lex->vstr, c);
503-
}
504-
#endif
505-
else {
506-
// unicode character out of range
507-
// this raises a generic SyntaxError; could provide more info
508-
lex->tok_kind = MP_TOKEN_INVALID;
493+
if (MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) {
494+
if (c < 0x110000 && !is_bytes) {
495+
vstr_add_char(&lex->vstr, c);
496+
} else if (c < 0x100 && is_bytes) {
497+
vstr_add_byte(&lex->vstr, c);
498+
} else {
499+
// unicode character out of range
500+
// this raises a generic SyntaxError; could provide more info
501+
lex->tok_kind = MP_TOKEN_INVALID;
502+
}
503+
} else {
504+
// without unicode everything is just added as an 8-bit byte
505+
if (c < 0x100) {
506+
vstr_add_byte(&lex->vstr, c);
507+
} else {
508+
// 8-bit character out of range
509+
// this raises a generic SyntaxError; could provide more info
510+
lex->tok_kind = MP_TOKEN_INVALID;
511+
}
509512
}
510513
}
511514
} else {

py/mpconfig.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,20 @@
283283
#define MICROPY_ENABLE_COMPILER (1)
284284
#endif
285285

286+
// Whether the compiler is dynamically configurable (ie at runtime)
287+
#ifndef MICROPY_DYNAMIC_COMPILER
288+
#define MICROPY_DYNAMIC_COMPILER (0)
289+
#endif
290+
291+
// Configure dynamic compiler macros
292+
#if MICROPY_DYNAMIC_COMPILER
293+
#define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC (mp_dynamic_compiler.opt_cache_map_lookup_in_bytecode)
294+
#define MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC (mp_dynamic_compiler.py_builtins_str_unicode)
295+
#else
296+
#define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE
297+
#define MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC MICROPY_PY_BUILTINS_STR_UNICODE
298+
#endif
299+
286300
// Whether to enable constant folding; eg 1+2 rewritten as 3
287301
#ifndef MICROPY_COMP_CONST_FOLDING
288302
#define MICROPY_COMP_CONST_FOLDING (1)

py/mpstate.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@
2626

2727
#include "py/mpstate.h"
2828

29+
#if MICROPY_DYNAMIC_COMPILER
30+
mp_dynamic_compiler_t mp_dynamic_compiler = {0};
31+
#endif
32+
2933
mp_state_ctx_t mp_state_ctx;

py/mpstate.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@
3939
// memory system, runtime and virtual machine. The state is a global
4040
// variable, but in the future it is hoped that the state can become local.
4141

42+
// This structure contains dynamic configuration for the compiler.
43+
#if MICROPY_DYNAMIC_COMPILER
44+
typedef struct mp_dynamic_compiler_t {
45+
uint8_t small_int_bits; // must be <= host small_int_bits
46+
bool opt_cache_map_lookup_in_bytecode;
47+
bool py_builtins_str_unicode;
48+
} mp_dynamic_compiler_t;
49+
extern mp_dynamic_compiler_t mp_dynamic_compiler;
50+
#endif
51+
4252
// This structure hold information about the memory allocation system.
4353
typedef struct _mp_state_mem_t {
4454
#if MICROPY_MEM_STATS

0 commit comments

Comments
 (0)