From 5a2599d96299ad37cda954f1de345159f9acf11c Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Fri, 15 Feb 2019 12:18:59 +1100
Subject: [PATCH] py: Replace POP_BLOCK and POP_EXCEPT opcodes with
 POP_EXCEPT_JUMP.

POP_BLOCK and POP_EXCEPT are now the same, and are always followed by a
JUMP.  So this optimisation reduces code size, and RAM usage of bytecode by
two bytes for each try-except handler.
---
 py/bc.c                         |  2 +-
 py/bc0.h                        |  3 +--
 py/compile.c                    | 12 ++++--------
 py/emit.h                       |  6 ++----
 py/emitbc.c                     | 13 ++++---------
 py/emitnative.c                 | 22 +++++++++-------------
 py/showbc.c                     | 11 +++--------
 py/vm.c                         | 14 +++++---------
 py/vmentrytable.h               |  3 +--
 tests/cmdline/cmd_showbc.py.exp | 12 ++++--------
 tools/mpy-tool.py               |  2 +-
 11 files changed, 35 insertions(+), 65 deletions(-)

diff --git a/py/bc.c b/py/bc.c
index 1d6e4322b..4a29f439e 100644
--- a/py/bc.c
+++ b/py/bc.c
@@ -321,7 +321,7 @@ STATIC const byte opcode_format_table[64] = {
     OC4(O, O, U, U), // 0x38-0x3b
     OC4(U, O, B, O), // 0x3c-0x3f
     OC4(O, B, B, O), // 0x40-0x43
-    OC4(B, B, O, B), // 0x44-0x47
+    OC4(O, U, O, B), // 0x44-0x47
     OC4(U, U, U, U), // 0x48-0x4b
     OC4(U, U, U, U), // 0x4c-0x4f
     OC4(V, V, U, V), // 0x50-0x53
diff --git a/py/bc0.h b/py/bc0.h
index 70acfb0ca..175ee263a 100644
--- a/py/bc0.h
+++ b/py/bc0.h
@@ -77,8 +77,7 @@
 #define MP_BC_END_FINALLY        (0x41)
 #define MP_BC_GET_ITER           (0x42)
 #define MP_BC_FOR_ITER           (0x43) // rel byte code offset, 16-bit unsigned
-#define MP_BC_POP_BLOCK          (0x44)
-#define MP_BC_POP_EXCEPT         (0x45)
+#define MP_BC_POP_EXCEPT_JUMP    (0x44) // rel byte code offset, 16-bit unsigned
 #define MP_BC_UNWIND_JUMP        (0x46) // rel byte code offset, 16-bit signed, in excess; then a byte
 #define MP_BC_GET_ITER_STACK     (0x47)
 
diff --git a/py/compile.c b/py/compile.c
index ca01d7478..b7f1d7b0c 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -1535,8 +1535,7 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_
     compile_increase_except_level(comp, l1, MP_EMIT_SETUP_BLOCK_EXCEPT);
 
     compile_node(comp, pn_body); // body
-    EMIT(pop_block);
-    EMIT_ARG(jump, success_label); // jump over exception handler
+    EMIT_ARG(pop_except_jump, success_label, false); // jump over exception handler
 
     EMIT_ARG(label_assign, l1); // start of exception handler
     EMIT(start_except_handler);
@@ -1607,8 +1606,7 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_
             compile_decrease_except_level(comp);
         }
 
-        EMIT(pop_except);
-        EMIT_ARG(jump, l2);
+        EMIT_ARG(pop_except_jump, l2, true);
         EMIT_ARG(label_assign, end_finally_label);
         EMIT_ARG(adjust_stack_size, 1); // stack adjust for the exception instance
     }
@@ -1741,8 +1739,7 @@ STATIC void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns
     compile_load_id(comp, context);
     compile_await_object_method(comp, MP_QSTR___anext__);
     c_assign(comp, pns->nodes[0], ASSIGN_STORE); // variable
-    EMIT(pop_block);
-    EMIT_ARG(jump, try_else_label);
+    EMIT_ARG(pop_except_jump, try_else_label, false);
 
     EMIT_ARG(label_assign, try_exception_label);
     EMIT(start_except_handler);
@@ -1751,8 +1748,7 @@ STATIC void compile_async_for_stmt(compiler_t *comp, mp_parse_node_struct_t *pns
     EMIT_ARG(binary_op, MP_BINARY_OP_EXCEPTION_MATCH);
     EMIT_ARG(pop_jump_if, false, try_finally_label);
     EMIT(pop_top); // pop exception instance
-    EMIT(pop_except);
-    EMIT_ARG(jump, while_else_label);
+    EMIT_ARG(pop_except_jump, while_else_label, true);
 
     EMIT_ARG(label_assign, try_finally_label);
     EMIT_ARG(adjust_stack_size, 1); // if we jump here, the exc is on the stack
diff --git a/py/emit.h b/py/emit.h
index 3c42bdf14..34c6cfd84 100644
--- a/py/emit.h
+++ b/py/emit.h
@@ -134,8 +134,7 @@ typedef struct _emit_method_table_t {
     void (*get_iter)(emit_t *emit, bool use_stack);
     void (*for_iter)(emit_t *emit, mp_uint_t label);
     void (*for_iter_end)(emit_t *emit);
-    void (*pop_block)(emit_t *emit);
-    void (*pop_except)(emit_t *emit);
+    void (*pop_except_jump)(emit_t *emit, mp_uint_t label, bool within_exc_handler);
     void (*unary_op)(emit_t *emit, mp_unary_op_t op);
     void (*binary_op)(emit_t *emit, mp_binary_op_t op);
     void (*build)(emit_t *emit, mp_uint_t n_args, int kind);
@@ -232,8 +231,7 @@ void mp_emit_bc_end_finally(emit_t *emit);
 void mp_emit_bc_get_iter(emit_t *emit, bool use_stack);
 void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label);
 void mp_emit_bc_for_iter_end(emit_t *emit);
-void mp_emit_bc_pop_block(emit_t *emit);
-void mp_emit_bc_pop_except(emit_t *emit);
+void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler);
 void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op);
 void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op);
 void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind);
diff --git a/py/emitbc.c b/py/emitbc.c
index 65d650905..4142e892d 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -764,14 +764,10 @@ void mp_emit_bc_for_iter_end(emit_t *emit) {
     emit_bc_pre(emit, -MP_OBJ_ITER_BUF_NSLOTS);
 }
 
-void mp_emit_bc_pop_block(emit_t *emit) {
+void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) {
+    (void)within_exc_handler;
     emit_bc_pre(emit, 0);
-    emit_write_bytecode_byte(emit, MP_BC_POP_BLOCK);
-}
-
-void mp_emit_bc_pop_except(emit_t *emit) {
-    emit_bc_pre(emit, 0);
-    emit_write_bytecode_byte(emit, MP_BC_POP_EXCEPT);
+    emit_write_bytecode_byte_unsigned_label(emit, MP_BC_POP_EXCEPT_JUMP, label);
 }
 
 void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) {
@@ -958,8 +954,7 @@ const emit_method_table_t emit_bc_method_table = {
     mp_emit_bc_get_iter,
     mp_emit_bc_for_iter,
     mp_emit_bc_for_iter_end,
-    mp_emit_bc_pop_block,
-    mp_emit_bc_pop_except,
+    mp_emit_bc_pop_except_jump,
     mp_emit_bc_unary_op,
     mp_emit_bc_binary_op,
     mp_emit_bc_build,
diff --git a/py/emitnative.c b/py/emitnative.c
index 8b7ebe530..c8a1a33d6 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -1916,7 +1916,7 @@ STATIC void emit_native_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t exc
             prev_finally->unwind_label = UNWIND_LABEL_DO_FINAL_UNWIND;
             ASM_MOV_REG_PCREL(emit->as, REG_RET, label & ~MP_EMIT_BREAK_FROM_FOR);
             ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_HANDLER_UNWIND(emit), REG_RET);
-            // Cancel any active exception (see also emit_native_pop_except)
+            // Cancel any active exception (see also emit_native_pop_except_jump)
             emit_native_mov_reg_const(emit, REG_RET, MP_F_CONST_NONE_OBJ);
             ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_RET);
             // Jump to the innermost active finally
@@ -2111,18 +2111,15 @@ STATIC void emit_native_for_iter_end(emit_t *emit) {
     emit_post(emit);
 }
 
-STATIC void emit_native_pop_block(emit_t *emit) {
-    emit_native_pre(emit);
-    if (!emit->exc_stack[emit->exc_stack_size - 1].is_finally) {
+STATIC void emit_native_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) {
+    if (within_exc_handler) {
+        // Cancel any active exception so subsequent handlers don't see it
+        emit_native_mov_reg_const(emit, REG_TEMP0, MP_F_CONST_NONE_OBJ);
+        ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0);
+    } else {
         emit_native_leave_exc_stack(emit, false);
     }
-    emit_post(emit);
-}
-
-STATIC void emit_native_pop_except(emit_t *emit) {
-    // Cancel any active exception so subsequent handlers don't see it
-    emit_native_mov_reg_const(emit, REG_TEMP0, MP_F_CONST_NONE_OBJ);
-    ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0);
+    emit_native_jump(emit, label);
 }
 
 STATIC void emit_native_unary_op(emit_t *emit, mp_unary_op_t op) {
@@ -2726,8 +2723,7 @@ const emit_method_table_t EXPORT_FUN(method_table) = {
     emit_native_get_iter,
     emit_native_for_iter,
     emit_native_for_iter_end,
-    emit_native_pop_block,
-    emit_native_pop_except,
+    emit_native_pop_except_jump,
     emit_native_unary_op,
     emit_native_binary_op,
     emit_native_build,
diff --git a/py/showbc.c b/py/showbc.c
index 3deb18cd3..b9024b716 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -401,14 +401,9 @@ const byte *mp_bytecode_print_str(const byte *ip) {
             printf("FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start));
             break;
 
-        case MP_BC_POP_BLOCK:
-            // pops block and restores the stack
-            printf("POP_BLOCK");
-            break;
-
-        case MP_BC_POP_EXCEPT:
-            // pops block, checks it's an exception block, and restores the stack, saving the 3 exception values to local threadstate
-            printf("POP_EXCEPT");
+        case MP_BC_POP_EXCEPT_JUMP:
+            DECODE_ULABEL; // these labels are always forward
+            printf("POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start));
             break;
 
         case MP_BC_BUILD_TUPLE:
diff --git a/py/vm.c b/py/vm.c
index a53b4a083..901a23f22 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -759,17 +759,13 @@ unwind_jump:;
                 }
 
                 // matched against: SETUP_EXCEPT, SETUP_FINALLY, SETUP_WITH
-                ENTRY(MP_BC_POP_BLOCK):
-                    // we are exiting an exception handler, so pop the last one of the exception-stack
+                ENTRY(MP_BC_POP_EXCEPT_JUMP): {
                     assert(exc_sp >= exc_stack);
                     POP_EXC_BLOCK();
-                    DISPATCH();
-
-                // matched against: SETUP_EXCEPT
-                ENTRY(MP_BC_POP_EXCEPT):
-                    assert(exc_sp >= exc_stack);
-                    POP_EXC_BLOCK();
-                    DISPATCH();
+                    DECODE_ULABEL;
+                    ip += ulab;
+                    DISPATCH_WITH_PEND_EXC_CHECK();
+                }
 
                 ENTRY(MP_BC_BUILD_TUPLE): {
                     MARK_EXC_IP_SELECTIVE();
diff --git a/py/vmentrytable.h b/py/vmentrytable.h
index 615f4e2ce..641c8ee42 100644
--- a/py/vmentrytable.h
+++ b/py/vmentrytable.h
@@ -76,8 +76,7 @@ static const void *const entry_table[256] = {
     [MP_BC_GET_ITER] = &&entry_MP_BC_GET_ITER,
     [MP_BC_GET_ITER_STACK] = &&entry_MP_BC_GET_ITER_STACK,
     [MP_BC_FOR_ITER] = &&entry_MP_BC_FOR_ITER,
-    [MP_BC_POP_BLOCK] = &&entry_MP_BC_POP_BLOCK,
-    [MP_BC_POP_EXCEPT] = &&entry_MP_BC_POP_EXCEPT,
+    [MP_BC_POP_EXCEPT_JUMP] = &&entry_MP_BC_POP_EXCEPT_JUMP,
     [MP_BC_BUILD_TUPLE] = &&entry_MP_BC_BUILD_TUPLE,
     [MP_BC_BUILD_LIST] = &&entry_MP_BC_BUILD_LIST,
     [MP_BC_BUILD_MAP] = &&entry_MP_BC_BUILD_MAP,
diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp
index d119a6198..8d36b89df 100644
--- a/tests/cmdline/cmd_showbc.py.exp
+++ b/tests/cmdline/cmd_showbc.py.exp
@@ -257,13 +257,11 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+):
 \\d\+ JUMP \\d\+
 \\d\+ LOAD_FAST 0
 \\d\+ POP_JUMP_IF_TRUE \\d\+
-\\d\+ POP_BLOCK
-\\d\+ JUMP \\d\+
+\\d\+ POP_EXCEPT_JUMP \\d\+
 \\d\+ POP_TOP
 \\d\+ LOAD_DEREF 14
 \\d\+ POP_TOP
-\\d\+ POP_EXCEPT
-\\d\+ JUMP \\d\+
+\\d\+ POP_EXCEPT_JUMP \\d\+
 \\d\+ END_FINALLY
 \\d\+ LOAD_CONST_NONE
 \\d\+ LOAD_FAST 1
@@ -272,11 +270,9 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+):
 \\d\+ JUMP \\d\+
 \\d\+ SETUP_EXCEPT \\d\+
 \\d\+ UNWIND_JUMP \\d\+ 1
-\\d\+ POP_BLOCK
-\\d\+ JUMP \\d\+
+\\d\+ POP_EXCEPT_JUMP \\d\+
 \\d\+ POP_TOP
-\\d\+ POP_EXCEPT
-\\d\+ JUMP \\d\+
+\\d\+ POP_EXCEPT_JUMP \\d\+
 \\d\+ END_FINALLY
 \\d\+ LOAD_FAST 0
 \\d\+ POP_JUMP_IF_TRUE \\d\+
diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py
index 6fbb10a39..62f2ac481 100755
--- a/tools/mpy-tool.py
+++ b/tools/mpy-tool.py
@@ -105,7 +105,7 @@ def make_opcode_format():
     OC4(O, O, U, U), # 0x38-0x3b
     OC4(U, O, B, O), # 0x3c-0x3f
     OC4(O, B, B, O), # 0x40-0x43
-    OC4(B, B, O, B), # 0x44-0x47
+    OC4(O, U, O, B), # 0x44-0x47
     OC4(U, U, U, U), # 0x48-0x4b
     OC4(U, U, U, U), # 0x4c-0x4f
     OC4(V, V, U, V), # 0x50-0x53
-- 
GitLab