summaryrefslogtreecommitdiffstatshomepage
path: root/py/compile.c
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2021-10-22 22:22:47 +1100
committerDamien George <damien@micropython.org>2022-02-24 18:08:43 +1100
commitf2040bfc7ee033e48acef9f289790f3b4e6b74e5 (patch)
tree53402caf1b0e7321bf772278a94f5a87a9e7bf0d /py/compile.c
parent64bfaae7ab33e628f28ca3b53b10893fb047b48e (diff)
downloadmicropython-f2040bfc7ee033e48acef9f289790f3b4e6b74e5.tar.gz
micropython-f2040bfc7ee033e48acef9f289790f3b4e6b74e5.zip
py: Rework bytecode and .mpy file format to be mostly static data.
Background: .mpy files are precompiled .py files, built using mpy-cross, that contain compiled bytecode functions (and can also contain machine code). The benefit of using an .mpy file over a .py file is that they are faster to import and take less memory when importing. They are also smaller on disk. But the real benefit of .mpy files comes when they are frozen into the firmware. This is done by loading the .mpy file during compilation of the firmware and turning it into a set of big C data structures (the job of mpy-tool.py), which are then compiled and downloaded into the ROM of a device. These C data structures can be executed in-place, ie directly from ROM. This makes importing even faster because there is very little to do, and also means such frozen modules take up much less RAM (because their bytecode stays in ROM). The downside of frozen code is that it requires recompiling and reflashing the entire firmware. This can be a big barrier to entry, slows down development time, and makes it harder to do OTA updates of frozen code (because the whole firmware must be updated). This commit attempts to solve this problem by providing a solution that sits between loading .mpy files into RAM and freezing them into the firmware. The .mpy file format has been reworked so that it consists of data and bytecode which is mostly static and ready to run in-place. If these new .mpy files are located in flash/ROM which is memory addressable, the .mpy file can be executed (mostly) in-place. With this approach there is still a small amount of unpacking and linking of the .mpy file that needs to be done when it's imported, but it's still much better than loading an .mpy from disk into RAM (although not as good as freezing .mpy files into the firmware). The main trick to make static .mpy files is to adjust the bytecode so any qstrs that it references now go through a lookup table to convert from local qstr number in the module to global qstr number in the firmware. That means the bytecode does not need linking/rewriting of qstrs when it's loaded. Instead only a small qstr table needs to be built (and put in RAM) at import time. This means the bytecode itself is static/constant and can be used directly if it's in addressable memory. Also the qstr string data in the .mpy file, and some constant object data, can be used directly. Note that the qstr table is global to the module (ie not per function). In more detail, in the VM what used to be (schematically): qst = DECODE_QSTR_VALUE; is now (schematically): idx = DECODE_QSTR_INDEX; qst = qstr_table[idx]; That allows the bytecode to be fixed at compile time and not need relinking/rewriting of the qstr values. Only qstr_table needs to be linked when the .mpy is loaded. Incidentally, this helps to reduce the size of bytecode because what used to be 2-byte qstr values in the bytecode are now (mostly) 1-byte indices. If the module uses the same qstr more than two times then the bytecode is smaller than before. The following changes are measured for this commit compared to the previous (the baseline): - average 7%-9% reduction in size of .mpy files - frozen code size is reduced by about 5%-7% - importing .py files uses about 5% less RAM in total - importing .mpy files uses about 4% less RAM in total - importing .py and .mpy files takes about the same time as before The qstr indirection in the bytecode has only a small impact on VM performance. For stm32 on PYBv1.0 the performance change of this commit is: diff of scores (higher is better) N=100 M=100 baseline -> this-commit diff diff% (error%) bm_chaos.py 371.07 -> 357.39 : -13.68 = -3.687% (+/-0.02%) bm_fannkuch.py 78.72 -> 77.49 : -1.23 = -1.563% (+/-0.01%) bm_fft.py 2591.73 -> 2539.28 : -52.45 = -2.024% (+/-0.00%) bm_float.py 6034.93 -> 5908.30 : -126.63 = -2.098% (+/-0.01%) bm_hexiom.py 48.96 -> 47.93 : -1.03 = -2.104% (+/-0.00%) bm_nqueens.py 4510.63 -> 4459.94 : -50.69 = -1.124% (+/-0.00%) bm_pidigits.py 650.28 -> 644.96 : -5.32 = -0.818% (+/-0.23%) core_import_mpy_multi.py 564.77 -> 581.49 : +16.72 = +2.960% (+/-0.01%) core_import_mpy_single.py 68.67 -> 67.16 : -1.51 = -2.199% (+/-0.01%) core_qstr.py 64.16 -> 64.12 : -0.04 = -0.062% (+/-0.00%) core_yield_from.py 362.58 -> 354.50 : -8.08 = -2.228% (+/-0.00%) misc_aes.py 429.69 -> 405.59 : -24.10 = -5.609% (+/-0.01%) misc_mandel.py 3485.13 -> 3416.51 : -68.62 = -1.969% (+/-0.00%) misc_pystone.py 2496.53 -> 2405.56 : -90.97 = -3.644% (+/-0.01%) misc_raytrace.py 381.47 -> 374.01 : -7.46 = -1.956% (+/-0.01%) viper_call0.py 576.73 -> 572.49 : -4.24 = -0.735% (+/-0.04%) viper_call1a.py 550.37 -> 546.21 : -4.16 = -0.756% (+/-0.09%) viper_call1b.py 438.23 -> 435.68 : -2.55 = -0.582% (+/-0.06%) viper_call1c.py 442.84 -> 440.04 : -2.80 = -0.632% (+/-0.08%) viper_call2a.py 536.31 -> 532.35 : -3.96 = -0.738% (+/-0.06%) viper_call2b.py 382.34 -> 377.07 : -5.27 = -1.378% (+/-0.03%) And for unix on x64: diff of scores (higher is better) N=2000 M=2000 baseline -> this-commit diff diff% (error%) bm_chaos.py 13594.20 -> 13073.84 : -520.36 = -3.828% (+/-5.44%) bm_fannkuch.py 60.63 -> 59.58 : -1.05 = -1.732% (+/-3.01%) bm_fft.py 112009.15 -> 111603.32 : -405.83 = -0.362% (+/-4.03%) bm_float.py 246202.55 -> 247923.81 : +1721.26 = +0.699% (+/-2.79%) bm_hexiom.py 615.65 -> 617.21 : +1.56 = +0.253% (+/-1.64%) bm_nqueens.py 215807.95 -> 215600.96 : -206.99 = -0.096% (+/-3.52%) bm_pidigits.py 8246.74 -> 8422.82 : +176.08 = +2.135% (+/-3.64%) misc_aes.py 16133.00 -> 16452.74 : +319.74 = +1.982% (+/-1.50%) misc_mandel.py 128146.69 -> 130796.43 : +2649.74 = +2.068% (+/-3.18%) misc_pystone.py 83811.49 -> 83124.85 : -686.64 = -0.819% (+/-1.03%) misc_raytrace.py 21688.02 -> 21385.10 : -302.92 = -1.397% (+/-3.20%) The code size change is (firmware with a lot of frozen code benefits the most): bare-arm: +396 +0.697% minimal x86: +1595 +0.979% [incl +32(data)] unix x64: +2408 +0.470% [incl +800(data)] unix nanbox: +1396 +0.309% [incl -96(data)] stm32: -1256 -0.318% PYBV10 cc3200: +288 +0.157% esp8266: -260 -0.037% GENERIC esp32: -216 -0.014% GENERIC[incl -1072(data)] nrf: +116 +0.067% pca10040 rp2: -664 -0.135% PICO samd: +844 +0.607% ADAFRUIT_ITSYBITSY_M4_EXPRESS As part of this change the .mpy file format version is bumped to version 6. And mpy-tool.py has been improved to provide a good visualisation of the contents of .mpy files. In summary: this commit changes the bytecode to use qstr indirection, and reworks the .mpy file format to be simpler and allow .mpy files to be executed in-place. Performance is not impacted too much. Eventually it will be possible to store such .mpy files in a linear, read-only, memory- mappable filesystem so they can be executed from flash/ROM. This will essentially be able to replace frozen code for most applications. Signed-off-by: Damien George <damien@micropython.org>
Diffstat (limited to 'py/compile.c')
-rw-r--r--py/compile.c171
1 files changed, 146 insertions, 25 deletions
diff --git a/py/compile.c b/py/compile.c
index 8ebcc2289f..92736e22e5 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -35,6 +35,7 @@
#include "py/compile.h"
#include "py/runtime.h"
#include "py/asmbase.h"
+#include "py/nativeglue.h"
#include "py/persistentcode.h"
#if MICROPY_ENABLE_COMPILER
@@ -88,7 +89,7 @@ typedef enum {
#if MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER
#define NATIVE_EMITTER(f) emit_native_table[mp_dynamic_compiler.native_arch]->emit_##f
-#define NATIVE_EMITTER_TABLE emit_native_table[mp_dynamic_compiler.native_arch]
+#define NATIVE_EMITTER_TABLE (emit_native_table[mp_dynamic_compiler.native_arch])
STATIC const emit_method_table_t *emit_native_table[] = {
NULL,
@@ -121,7 +122,7 @@ STATIC const emit_method_table_t *emit_native_table[] = {
#else
#error "unknown native emitter"
#endif
-#define NATIVE_EMITTER_TABLE &NATIVE_EMITTER(method_table)
+#define NATIVE_EMITTER_TABLE (&NATIVE_EMITTER(method_table))
#endif
#if MICROPY_EMIT_INLINE_ASM && MICROPY_DYNAMIC_COMPILER
@@ -162,8 +163,6 @@ STATIC const emit_inline_asm_method_table_t *emit_asm_table[] = {
// elements in this struct are ordered to make it compact
typedef struct _compiler_t {
- qstr source_file;
-
uint8_t is_repl;
uint8_t pass; // holds enum type pass_kind_t
uint8_t have_star;
@@ -185,7 +184,10 @@ typedef struct _compiler_t {
scope_t *scope_head;
scope_t *scope_cur;
+ mp_emit_common_t emit_common;
+
emit_t *emit; // current emitter
+ emit_t *emit_bc;
#if NEED_METHOD_TABLE
const emit_method_table_t *emit_method_table; // current emit method table
#endif
@@ -196,6 +198,72 @@ typedef struct _compiler_t {
#endif
} compiler_t;
+/******************************************************************************/
+// mp_emit_common_t helper functions
+// These are defined here so they can be inlined, to reduce code size.
+
+STATIC void mp_emit_common_init(mp_emit_common_t *emit, qstr source_file) {
+ #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
+ mp_map_init(&emit->qstr_map, 1);
+
+ // add the source file as the first entry in the qstr table
+ mp_map_elem_t *elem = mp_map_lookup(&emit->qstr_map, MP_OBJ_NEW_QSTR(source_file), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
+ elem->value = MP_OBJ_NEW_SMALL_INT(0);
+ #endif
+}
+
+STATIC void mp_emit_common_start_pass(mp_emit_common_t *emit, pass_kind_t pass) {
+ emit->pass = pass;
+ if (pass == MP_PASS_STACK_SIZE) {
+ emit->ct_cur_obj_base = emit->ct_cur_obj;
+ } else if (pass > MP_PASS_STACK_SIZE) {
+ emit->ct_cur_obj = emit->ct_cur_obj_base;
+ }
+ if (pass == MP_PASS_EMIT) {
+ if (emit->ct_cur_child == 0) {
+ emit->children = NULL;
+ } else {
+ emit->children = m_new0(mp_raw_code_t *, emit->ct_cur_child);
+ }
+ }
+ emit->ct_cur_child = 0;
+}
+
+STATIC void mp_emit_common_finalise(mp_emit_common_t *emit, bool has_native_code) {
+ emit->ct_cur_obj += has_native_code; // allocate an additional slot for &mp_fun_table
+ emit->const_table = m_new0(mp_uint_t, emit->ct_cur_obj);
+ emit->ct_cur_obj = has_native_code; // reserve slot 0 for &mp_fun_table
+ #if MICROPY_EMIT_NATIVE
+ if (has_native_code) {
+ // store mp_fun_table pointer at the start of the constant table
+ emit->const_table[0] = (mp_uint_t)(uintptr_t)&mp_fun_table;
+ }
+ #endif
+}
+
+STATIC void mp_emit_common_populate_module_context(mp_emit_common_t *emit, qstr source_file, mp_module_context_t *context) {
+ #if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
+ size_t qstr_map_used = emit->qstr_map.used;
+ mp_module_context_alloc_tables(context, qstr_map_used, emit->ct_cur_obj);
+ for (size_t i = 0; i < emit->qstr_map.alloc; ++i) {
+ if (mp_map_slot_is_filled(&emit->qstr_map, i)) {
+ size_t idx = MP_OBJ_SMALL_INT_VALUE(emit->qstr_map.table[i].value);
+ qstr qst = MP_OBJ_QSTR_VALUE(emit->qstr_map.table[i].key);
+ context->constants.qstr_table[idx] = qst;
+ }
+ }
+ #else
+ mp_module_context_alloc_tables(context, 0, emit->ct_cur_obj);
+ context->constants.source_file = source_file;
+ #endif
+
+ if (emit->ct_cur_obj > 0) {
+ memcpy(context->constants.obj_table, emit->const_table, emit->ct_cur_obj * sizeof(mp_uint_t));
+ }
+}
+
+/******************************************************************************/
+
STATIC void compile_error_set_line(compiler_t *comp, mp_parse_node_t pn) {
// if the line of the error is unknown then try to update it from the pn
if (comp->compile_error_line == 0 && MP_PARSE_NODE_IS_STRUCT(pn)) {
@@ -246,7 +314,7 @@ STATIC void compile_decrease_except_level(compiler_t *comp) {
}
STATIC scope_t *scope_new_and_link(compiler_t *comp, scope_kind_t kind, mp_parse_node_t pn, uint emit_options) {
- scope_t *scope = scope_new(kind, pn, comp->source_file, emit_options);
+ scope_t *scope = scope_new(kind, pn, emit_options);
scope->parent = comp->scope_cur;
scope->next = NULL;
if (comp->scope_head == NULL) {
@@ -290,7 +358,8 @@ STATIC void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t *
STATIC void compile_load_id(compiler_t *comp, qstr qst) {
if (comp->pass == MP_PASS_SCOPE) {
mp_emit_common_get_id_for_load(comp->scope_cur, qst);
- } else {
+ }
+ {
#if NEED_METHOD_TABLE
mp_emit_common_id_op(comp->emit, &comp->emit_method_table->load_id, comp->scope_cur, qst);
#else
@@ -302,7 +371,8 @@ STATIC void compile_load_id(compiler_t *comp, qstr qst) {
STATIC void compile_store_id(compiler_t *comp, qstr qst) {
if (comp->pass == MP_PASS_SCOPE) {
mp_emit_common_get_id_for_modification(comp->scope_cur, qst);
- } else {
+ }
+ {
#if NEED_METHOD_TABLE
mp_emit_common_id_op(comp->emit, &comp->emit_method_table->store_id, comp->scope_cur, qst);
#else
@@ -314,7 +384,8 @@ STATIC void compile_store_id(compiler_t *comp, qstr qst) {
STATIC void compile_delete_id(compiler_t *comp, qstr qst) {
if (comp->pass == MP_PASS_SCOPE) {
mp_emit_common_get_id_for_modification(comp->scope_cur, qst);
- } else {
+ }
+ {
#if NEED_METHOD_TABLE
mp_emit_common_id_op(comp->emit, &comp->emit_method_table->delete_id, comp->scope_cur, qst);
#else
@@ -819,7 +890,7 @@ STATIC bool compile_built_in_decorator(compiler_t *comp, size_t name_len, mp_par
compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid micropython decorator"));
}
- #if MICROPY_DYNAMIC_COMPILER
+ #if MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER
if (*emit_options == MP_EMIT_OPT_NATIVE_PYTHON || *emit_options == MP_EMIT_OPT_VIPER) {
if (emit_native_table[mp_dynamic_compiler.native_arch] == NULL) {
compile_syntax_error(comp, name_nodes[1], MP_ERROR_TEXT("invalid arch"));
@@ -2082,6 +2153,7 @@ STATIC void compile_lambdef(compiler_t *comp, mp_parse_node_struct_t *pns) {
STATIC void compile_namedexpr_helper(compiler_t *comp, mp_parse_node_t pn_name, mp_parse_node_t pn_expr) {
if (!MP_PARSE_NODE_IS_ID(pn_name)) {
compile_syntax_error(comp, (mp_parse_node_t)pn_name, MP_ERROR_TEXT("can't assign to expression"));
+ return; // because pn_name is not a valid qstr (in compile_store_id below)
}
compile_node(comp, pn_expr);
EMIT(dup_top);
@@ -2814,6 +2886,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
// comes before a star, so counts as a positional parameter
comp->scope_cur->num_pos_args += 1;
}
+ mp_emit_common_use_qstr(&comp->emit_common, param_name);
} else {
assert(MP_PARSE_NODE_IS_STRUCT(pn));
pns = (mp_parse_node_struct_t *)pn;
@@ -2827,6 +2900,7 @@ STATIC void compile_scope_func_lambda_param(compiler_t *comp, mp_parse_node_t pn
// comes before a star, so counts as a positional parameter
comp->scope_cur->num_pos_args += 1;
}
+ mp_emit_common_use_qstr(&comp->emit_common, param_name);
} else if (MP_PARSE_NODE_STRUCT_KIND(pns) == pn_star) {
if (comp->have_star) {
// more than one star
@@ -2976,6 +3050,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
comp->pass = pass;
comp->scope_cur = scope;
comp->next_label = 0;
+ mp_emit_common_start_pass(&comp->emit_common, pass);
EMIT_ARG(start_pass, pass, scope);
reserve_labels_for_native(comp, 6); // used by native's start_pass
@@ -2984,6 +3059,14 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
// they will be computed in this first pass
scope->stack_size = 0;
scope->exc_stack_size = 0;
+
+ #if MICROPY_EMIT_NATIVE
+ if (scope->emit_options == MP_EMIT_OPT_NATIVE_PYTHON || scope->emit_options == MP_EMIT_OPT_VIPER) {
+ // allow native code to perfom basic tasks during the pass scope
+ // note: the first argument passed here is mp_emit_common_t, not the native emitter context
+ NATIVE_EMITTER_TABLE->start_pass((void *)&comp->emit_common, comp->pass, scope);
+ }
+ #endif
}
// compile
@@ -3064,6 +3147,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
if (comp->pass == MP_PASS_SCOPE) {
scope_find_or_add_id(comp->scope_cur, qstr_arg, ID_INFO_KIND_LOCAL);
scope->num_pos_args = 1;
+ mp_emit_common_use_qstr(&comp->emit_common, MP_QSTR__star_);
}
// Set the source line number for the start of the comprehension
@@ -3296,9 +3380,10 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind
f, mp_asm_base_get_code_size((mp_asm_base_t *)comp->emit_inline_asm),
NULL,
#if MICROPY_PERSISTENT_CODE_SAVE
- 0, 0, 0, 0, NULL,
+ 0,
+ 0, 0, NULL,
#endif
- comp->scope_cur->num_pos_args, 0, type_sig);
+ 0, comp->scope_cur->num_pos_args, type_sig);
}
}
@@ -3310,7 +3395,7 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind
}
#endif
-STATIC void scope_compute_things(scope_t *scope) {
+STATIC void scope_compute_things(scope_t *scope, mp_emit_common_t *emit_common) {
// in MicroPython we put the *x parameter after all other parameters (except **y)
if (scope->scope_flags & MP_SCOPE_FLAG_VARARGS) {
id_info_t *id_param = NULL;
@@ -3399,6 +3484,7 @@ STATIC void scope_compute_things(scope_t *scope) {
}
scope->num_pos_args += num_free; // free vars are counted as params for passing them into the function
scope->num_locals += num_free;
+ mp_emit_common_use_qstr(emit_common, MP_QSTR__star_);
}
}
}
@@ -3406,15 +3492,15 @@ STATIC void scope_compute_things(scope_t *scope) {
#if !MICROPY_PERSISTENT_CODE_SAVE
STATIC
#endif
-mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) {
+mp_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_module_context_t *context) {
// put compiler state on the stack, it's relatively small
compiler_t comp_state = {0};
compiler_t *comp = &comp_state;
- comp->source_file = source_file;
comp->is_repl = is_repl;
comp->break_label = INVALID_LABEL;
comp->continue_label = INVALID_LABEL;
+ mp_emit_common_init(&comp->emit_common, source_file);
// create the module scope
#if MICROPY_EMIT_NATIVE
@@ -3425,10 +3511,11 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f
scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt);
// create standard emitter; it's used at least for MP_PASS_SCOPE
- emit_t *emit_bc = emit_bc_new();
+ emit_t *emit_bc = emit_bc_new(&comp->emit_common);
- // compile pass 1
+ // compile MP_PASS_SCOPE
comp->emit = emit_bc;
+ comp->emit_bc = emit_bc;
#if MICROPY_EMIT_NATIVE
comp->emit_method_table = &emit_bc_method_table;
#endif
@@ -3458,14 +3545,24 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f
}
// compute some things related to scope and identifiers
+ bool has_native_code = false;
for (scope_t *s = comp->scope_head; s != NULL && comp->compile_error == MP_OBJ_NULL; s = s->next) {
- scope_compute_things(s);
+ #if MICROPY_EMIT_NATIVE
+ if (s->emit_options == MP_EMIT_OPT_NATIVE_PYTHON || s->emit_options == MP_EMIT_OPT_VIPER) {
+ has_native_code = true;
+ }
+ #endif
+
+ scope_compute_things(s, &comp->emit_common);
}
// set max number of labels now that it's calculated
emit_bc_set_max_num_labels(emit_bc, max_num_labels);
- // compile pass 2 and 3
+ // finalise and allocate the constant table
+ mp_emit_common_finalise(&comp->emit_common, has_native_code);
+
+ // compile MP_PASS_STACK_SIZE, MP_PASS_CODE_SIZE, MP_PASS_EMIT
#if MICROPY_EMIT_NATIVE
emit_t *emit_native = NULL;
#endif
@@ -3505,7 +3602,7 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f
case MP_EMIT_OPT_NATIVE_PYTHON:
case MP_EMIT_OPT_VIPER:
if (emit_native == NULL) {
- emit_native = NATIVE_EMITTER(new)(&comp->compile_error, &comp->next_label, max_num_labels);
+ emit_native = NATIVE_EMITTER(new)(&comp->emit_common, &comp->compile_error, &comp->next_label, max_num_labels);
}
comp->emit_method_table = NATIVE_EMITTER_TABLE;
comp->emit = emit_native;
@@ -3540,10 +3637,33 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f
// number for the start of this scope
compile_error_set_line(comp, comp->scope_cur->pn);
// add a traceback to the exception using relevant source info
- mp_obj_exception_add_traceback(comp->compile_error, comp->source_file,
+ mp_obj_exception_add_traceback(comp->compile_error, source_file,
comp->compile_error_line, comp->scope_cur->simple_name);
}
+ // construct the global qstr/const table for this module
+ mp_compiled_module_t cm;
+ cm.rc = module_scope->raw_code;
+ cm.context = context;
+ #if MICROPY_PERSISTENT_CODE_SAVE
+ cm.has_native = has_native_code;
+ cm.n_qstr = comp->emit_common.qstr_map.used;
+ cm.n_obj = comp->emit_common.ct_cur_obj;
+ #endif
+ if (comp->compile_error == MP_OBJ_NULL) {
+ mp_emit_common_populate_module_context(&comp->emit_common, source_file, context);
+
+ #if MICROPY_DEBUG_PRINTERS
+ // now that the module context is valid, the raw codes can be printed
+ if (mp_verbose_flag >= 2) {
+ for (scope_t *s = comp->scope_head; s != NULL; s = s->next) {
+ mp_raw_code_t *rc = s->raw_code;
+ mp_bytecode_print(&mp_plat_print, rc, rc->fun_data, rc->fun_data_len, &cm.context->constants);
+ }
+ }
+ #endif
+ }
+
// free the emitters
emit_bc_free(emit_bc);
@@ -3562,7 +3682,6 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f
mp_parse_tree_clear(parse_tree);
// free the scopes
- mp_raw_code_t *outer_raw_code = module_scope->raw_code;
for (scope_t *s = module_scope; s;) {
scope_t *next = s->next;
scope_free(s);
@@ -3571,15 +3690,17 @@ mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_f
if (comp->compile_error != MP_OBJ_NULL) {
nlr_raise(comp->compile_error);
- } else {
- return outer_raw_code;
}
+
+ return cm;
}
mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl) {
- mp_raw_code_t *rc = mp_compile_to_raw_code(parse_tree, source_file, is_repl);
+ mp_module_context_t *context = m_new_obj(mp_module_context_t);
+ context->module.globals = mp_globals_get();
+ mp_compiled_module_t cm = mp_compile_to_raw_code(parse_tree, source_file, is_repl, context);
// return function that executes the outer module
- return mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL);
+ return mp_make_function_from_raw_code(cm.rc, cm.context, NULL);
}
#endif // MICROPY_ENABLE_COMPILER