summaryrefslogtreecommitdiff
path: root/py/compile.c
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2018-08-16 13:56:36 +1000
committerDamien George <damien.p.george@gmail.com>2018-08-16 13:56:36 +1000
commita3de776486396a4bf4f5233ce18143bd0fd81cac (patch)
tree55a02ba288968f5eb7eeaed843593eb4b0e85e90 /py/compile.c
parent2964b41c282917bfb3f6e3a1d6e3fd7a078abed6 (diff)
py/emitnative: Optimise and improve exception handling in native code.
Prior to this patch, native code would use a full nlr_buf_t for each exception handler (try-except, try-finally, with). For nested exception handlers this would use a lot of C stack and be rather inefficient. This patch changes how exceptions are handled in native code by setting up only a single nlr_buf_t context for the entire function, and then manages a state machine (using the PC) to work out which exception handler to run when an exception is raised by an nlr_jump. This keeps the C stack usage at a constant level regardless of the depth of Python exception blocks. The patch also fixes an existing bug when local variables are written to within an exception handler, then their value was incorrectly restored if an exception was raised (since the nlr_jump would restore register values, back to the point of the nlr_push). And it also gets nested try-finally+with working with the viper emitter. Broadly speaking, efficiency of executing native code that doesn't use any exception blocks is unchanged, and emitted code size is only slightly increased for such function. C stack usage of all native functions is either equal or less than before. Emitted code size for native functions that use exception blocks is increased by roughly 10% (due in part to fixing of above-mentioned bugs). But, most importantly, this patch allows to implement more Python features in native code, like unwind jumps and yielding from within nested exception blocks.
Diffstat (limited to 'py/compile.c')
-rw-r--r--py/compile.c19
1 files changed, 13 insertions, 6 deletions
diff --git a/py/compile.c b/py/compile.c
index df416b87f..8ef05d238 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -170,6 +170,16 @@ STATIC uint comp_next_label(compiler_t *comp) {
return comp->next_label++;
}
+#if MICROPY_EMIT_NATIVE
+STATIC void reserve_labels_for_native(compiler_t *comp, int n) {
+ if (comp->scope_cur->emit_options != MP_EMIT_OPT_BYTECODE) {
+ comp->next_label += n;
+ }
+}
+#else
+#define reserve_labels_for_native(comp, n)
+#endif
+
STATIC void compile_increase_except_level(compiler_t *comp) {
comp->cur_except_level += 1;
if (comp->cur_except_level > comp->scope_cur->exc_stack_size) {
@@ -1656,11 +1666,6 @@ STATIC void compile_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *n
compile_node(comp, body);
} else {
uint l_end = comp_next_label(comp);
- if (MICROPY_EMIT_NATIVE && comp->scope_cur->emit_options != MP_EMIT_OPT_BYTECODE) {
- // we need to allocate an extra label for the native emitter
- // it will use l_end+1 as an auxiliary label
- comp_next_label(comp);
- }
if (MP_PARSE_NODE_IS_STRUCT_KIND(nodes[0], PN_with_item)) {
// this pre-bit is of the form "a as b"
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)nodes[0];
@@ -1678,6 +1683,7 @@ STATIC void compile_with_stmt_helper(compiler_t *comp, int n, mp_parse_node_t *n
compile_with_stmt_helper(comp, n - 1, nodes + 1, body);
// finish this with block
EMIT_ARG(with_cleanup, l_end);
+ reserve_labels_for_native(comp, 2); // used by native's with_cleanup
compile_decrease_except_level(comp);
EMIT(end_finally);
}
@@ -2947,6 +2953,7 @@ STATIC void compile_scope(compiler_t *comp, scope_t *scope, pass_kind_t pass) {
comp->scope_cur = scope;
comp->next_label = 0;
EMIT_ARG(start_pass, pass, scope);
+ reserve_labels_for_native(comp, 4); // used by native's start_pass
if (comp->pass == MP_PASS_SCOPE) {
// reset maximum stack sizes in scope
@@ -3443,7 +3450,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, max_num_labels);
+ emit_native = NATIVE_EMITTER(new)(&comp->compile_error, &comp->next_label, max_num_labels);
}
comp->emit_method_table = &NATIVE_EMITTER(method_table);
comp->emit = emit_native;