[Python-checkins] gh-87092: bring compiler code closer to a preprocessing-opt-assembler organisation (GH-97644)

iritkatriel webhook-mailer at python.org
Wed Oct 5 03:52:57 EDT 2022


https://github.com/python/cpython/commit/c529b451226bc704ce648cbe7e0b8cb48bcf5e1c
commit: c529b451226bc704ce648cbe7e0b8cb48bcf5e1c
branch: main
author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com>
committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com>
date: 2022-10-05T08:52:35+01:00
summary:

gh-87092: bring compiler code closer to a preprocessing-opt-assembler organisation (GH-97644)

files:
M Lib/test/test_compile.py
M Python/compile.c

diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 21dcc1a719cc..8bf8470ff16f 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -670,10 +670,22 @@ def test_merge_code_attrs(self):
 
         self.assertIs(f1.__code__.co_linetable, f2.__code__.co_linetable)
 
+    @support.cpython_only
+    def test_strip_unused_consts(self):
+        def f():
+            "docstring"
+            if True:
+                return "used"
+            else:
+                return "unused"
+
+        self.assertEqual(f.__code__.co_consts,
+                         ("docstring", True, "used"))
+
     # Stripping unused constants is not a strict requirement for the
     # Python semantics, it's a more an implementation detail.
     @support.cpython_only
-    def test_strip_unused_consts(self):
+    def test_strip_unused_None(self):
         # Python 3.10rc1 appended None to co_consts when None is not used
         # at all. See bpo-45056.
         def f1():
diff --git a/Python/compile.c b/Python/compile.c
index 507fd040a89d..2da36d0f6316 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -912,6 +912,19 @@ cfg_builder_use_label(cfg_builder *g, jump_target_label lbl)
     return cfg_builder_maybe_start_new_block(g);
 }
 
+static inline int
+basicblock_append_instructions(basicblock *target, basicblock *source)
+{
+    for (int i = 0; i < source->b_iused; i++) {
+        int n = basicblock_next_instr(target);
+        if (n < 0) {
+            return -1;
+        }
+        target->b_instr[n] = source->b_instr[i];
+    }
+    return 0;
+}
+
 static basicblock *
 copy_basicblock(cfg_builder *g, basicblock *block)
 {
@@ -923,12 +936,8 @@ copy_basicblock(cfg_builder *g, basicblock *block)
     if (result == NULL) {
         return NULL;
     }
-    for (int i = 0; i < block->b_iused; i++) {
-        int n = basicblock_next_instr(result);
-        if (n < 0) {
-            return NULL;
-        }
-        result->b_instr[n] = block->b_instr[i];
+    if (basicblock_append_instructions(result, block) < 0) {
+        return NULL;
     }
     return result;
 }
@@ -7080,15 +7089,14 @@ stackdepth(basicblock *entryblock, int code_flags)
             if (new_depth > maxdepth) {
                 maxdepth = new_depth;
             }
-            assert(depth >= 0); /* invalid code or bug in stackdepth() */
             if (HAS_TARGET(instr->i_opcode)) {
                 effect = stack_effect(instr->i_opcode, instr->i_oparg, 1);
                 assert(effect != PY_INVALID_STACK_EFFECT);
                 int target_depth = depth + effect;
+                assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
                 if (target_depth > maxdepth) {
                     maxdepth = target_depth;
                 }
-                assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
                 stackdepth_push(&sp, instr->i_target, target_depth);
             }
             depth = new_depth;
@@ -7487,6 +7495,9 @@ convert_exception_handlers_to_nops(basicblock *entryblock) {
             }
         }
     }
+    for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
+        remove_redundant_nops(b);
+    }
 }
 
 static inline void
@@ -7964,8 +7975,8 @@ scan_block_for_local(int target, basicblock *b, bool unsafe_to_start,
 #undef MAYBE_PUSH
 
 static int
-add_checks_for_loads_of_unknown_variables(basicblock *entryblock,
-                                          struct compiler *c)
+add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock,
+                                                struct compiler *c)
 {
     basicblock **stack = make_cfg_traversal_stack(entryblock);
     if (stack == NULL) {
@@ -8291,7 +8302,7 @@ dump_basicblock(const basicblock *b)
 
 
 static int
-calculate_jump_targets(basicblock *entryblock);
+translate_jump_labels_to_targets(basicblock *entryblock);
 
 static int
 optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache);
@@ -8628,11 +8639,9 @@ assemble(struct compiler *c, int addNone)
     }
     nlocalsplus -= numdropped;
 
-    consts = consts_dict_keys_inorder(c->u->u_consts);
-    if (consts == NULL) {
-        goto error;
-    }
-    if (calculate_jump_targets(g->g_entryblock)) {
+    /** Preprocessing **/
+    /* Map labels to targets and mark exception handlers */
+    if (translate_jump_labels_to_targets(g->g_entryblock)) {
         goto error;
     }
     if (mark_except_handlers(g->g_entryblock) < 0) {
@@ -8641,18 +8650,31 @@ assemble(struct compiler *c, int addNone)
     if (label_exception_targets(g->g_entryblock)) {
         goto error;
     }
+
+    /** Optimization **/
+    consts = consts_dict_keys_inorder(c->u->u_consts);
+    if (consts == NULL) {
+        goto error;
+    }
     if (optimize_cfg(g, consts, c->c_const_cache)) {
         goto error;
     }
-    if (trim_unused_consts(g->g_entryblock, consts)) {
+    if (add_checks_for_loads_of_uninitialized_variables(g->g_entryblock, c) < 0) {
         goto error;
     }
+
+    /** line numbers (TODO: move this before optimization stage) */
     if (duplicate_exits_without_lineno(g) < 0) {
         goto error;
     }
     propagate_line_numbers(g->g_entryblock);
     guarantee_lineno_for_exits(g->g_entryblock, c->u->u_firstlineno);
 
+    if (push_cold_blocks_to_end(g, code_flags) < 0) {
+        goto error;
+    }
+
+    /** Assembly **/
     int maxdepth = stackdepth(g->g_entryblock, code_flags);
     if (maxdepth < 0) {
         goto error;
@@ -8661,27 +8683,19 @@ assemble(struct compiler *c, int addNone)
 
     convert_exception_handlers_to_nops(g->g_entryblock);
 
-    if (push_cold_blocks_to_end(g, code_flags) < 0) {
-        goto error;
-    }
-    for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
-        remove_redundant_nops(b);
-    }
-
     /* Order of basic blocks must have been determined by now */
     if (normalize_jumps(g) < 0) {
         goto error;
     }
 
-    if (add_checks_for_loads_of_unknown_variables(g->g_entryblock, c) < 0) {
-        goto error;
-    }
-
     assert(no_redundant_jumps(g));
 
     /* Can't modify the bytecode after computing jump offsets. */
     assemble_jump_offsets(g->g_entryblock);
 
+    if (trim_unused_consts(g->g_entryblock, consts)) {
+        goto error;
+    }
 
     /* Create assembler */
     if (!assemble_init(&a, c->u->u_firstlineno))
@@ -9265,12 +9279,8 @@ inline_small_exit_blocks(basicblock *bb) {
     basicblock *target = last->i_target;
     if (basicblock_exits_scope(target) && target->b_iused <= MAX_COPY_SIZE) {
         last->i_opcode = NOP;
-        for (int i = 0; i < target->b_iused; i++) {
-            int index = basicblock_next_instr(bb);
-            if (index < 0) {
-                return -1;
-            }
-            bb->b_instr[index] = target->b_instr[i];
+        if (basicblock_append_instructions(bb, target) < 0) {
+            return -1;
         }
         return 1;
     }
@@ -9456,7 +9466,7 @@ propagate_line_numbers(basicblock *entryblock) {
 
 /* Calculate the actual jump target from the target_label */
 static int
-calculate_jump_targets(basicblock *entryblock)
+translate_jump_labels_to_targets(basicblock *entryblock)
 {
     int max_label = -1;
     for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
@@ -9599,12 +9609,14 @@ is_exit_without_lineno(basicblock *b) {
 static int
 duplicate_exits_without_lineno(cfg_builder *g)
 {
+    assert(no_empty_basic_blocks(g));
     /* Copy all exit blocks without line number that are targets of a jump.
      */
     basicblock *entryblock = g->g_entryblock;
     for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
         struct instr *last = basicblock_last_instr(b);
-        if (last != NULL && is_jump(last)) {
+        assert(last != NULL);
+        if (is_jump(last)) {
             basicblock *target = last->i_target;
             if (is_exit_without_lineno(target) && target->b_predecessors > 1) {
                 basicblock *new_target = copy_basicblock(g, target);
@@ -9621,8 +9633,6 @@ duplicate_exits_without_lineno(cfg_builder *g)
         }
     }
 
-    assert(no_empty_basic_blocks(g));
-
     /* Any remaining reachable exit blocks without line number can only be reached by
      * fall through, and thus can only have a single predecessor */
     for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
@@ -9775,7 +9785,7 @@ _PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts)
     if (const_cache == NULL) {
         goto error;
     }
-    if (calculate_jump_targets(g.g_entryblock)) {
+    if (translate_jump_labels_to_targets(g.g_entryblock)) {
         goto error;
     }
     if (optimize_cfg(&g, consts, const_cache) < 0) {



More information about the Python-checkins mailing list