From b996b8117d3aef5b736ff7249fa869af949d7dc4 Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Mon, 29 Jun 2026 19:41:19 -0400 Subject: [PATCH 1/3] gh-152600: Remove `LOAD_FAST/LOAD_FAST_BORROW; POP_TOP` pairs Changes - moved `add_checks_for_loads_of_uninitialized_variables` earlier so that a `LOAD_FAST` that should be a `LOAD_FAST_CHECK` are not removed. - refactored `remove_redundant_nops_and_pairs` to be more concise. Also extended it so that any `COPY` gets removed. I don't think there is any reason to only remove `COPY` with an `oparg` of 1. I don't think a NEWS entry is needed. --- Lib/test/test_peepholer.py | 45 ++++++++++++++++++++++++++------------ Python/flowgraph.c | 26 ++++++++++------------ 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 28748009f731bc1..40948be2fbcb577 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -2521,24 +2521,14 @@ def test_unoptimized_if_unconsumed(self): insts = [ ("LOAD_FAST", 0, 1), ("LOAD_FAST", 1, 2), - ("POP_TOP", None, 3), + ("LOAD_SMALL_INT", 1, 3), + ("BINARY_OP", 0, 3), ] expected = [ ("LOAD_FAST", 0, 1), ("LOAD_FAST_BORROW", 1, 2), - ("POP_TOP", None, 3), - ] - self.check(insts, expected) - - insts = [ - ("LOAD_FAST", 0, 1), - ("COPY", 1, 2), - ("POP_TOP", None, 3), - ] - expected = [ - ("LOAD_FAST", 0, 1), - ("NOP", None, 2), - ("NOP", None, 3), + ("LOAD_SMALL_INT", 1, 3), + ("BINARY_OP", 0, 3), ] self.check(insts, expected) @@ -2862,6 +2852,33 @@ def f(): return var self.assertEqual(f(), "1") + def test_load_fast_removed(self): + insts = [ + ("LOAD_FAST", 0, 1), + ("LOAD_FAST", 1, 2), + ("POP_TOP", None, 3), + ] + expected = [ + ("LOAD_FAST", 0, 1), + ("NOP", None, 2), + ("NOP", None, 3), + ] + self.check(insts, expected) + + def test_copy(self): + for i in range(1, 5): + insts = [ + ("LOAD_FAST", 0, 1), + ("COPY", i, 2), + ("POP_TOP", None, 3), + ] + expected = [ + ("LOAD_FAST", 0, 1), + ("NOP", None, 2), + ("NOP", None, 3), + ] + self.check(insts, expected) + if __name__ == "__main__": diff --git a/Python/flowgraph.c b/Python/flowgraph.c index f135f243c74e2f6..d3a5d95e8a6289c 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1147,21 +1147,16 @@ remove_redundant_nops_and_pairs(basicblock *entryblock) prev_instr = instr; instr = &b->b_instr[i]; int prev_opcode = prev_instr ? prev_instr->i_opcode : 0; - int prev_oparg = prev_instr ? prev_instr->i_oparg : 0; int opcode = instr->i_opcode; - bool is_redundant_pair = false; if (opcode == POP_TOP) { - if (loads_const(prev_opcode)) { - is_redundant_pair = true; + if (loads_const(prev_opcode) + || prev_opcode == COPY + || prev_opcode == LOAD_FAST) + { + INSTR_SET_OP0(prev_instr, NOP); + INSTR_SET_OP0(instr, NOP); + done = false; } - else if (prev_opcode == COPY && prev_oparg == 1) { - is_redundant_pair = true; - } - } - if (is_redundant_pair) { - INSTR_SET_OP0(prev_instr, NOP); - INSTR_SET_OP0(instr, NOP); - done = false; } } if ((instr && is_jump(instr)) || !BB_HAS_FALLTHROUGH(b)) { @@ -3790,6 +3785,10 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, } } + RETURN_IF_ERROR( + add_checks_for_loads_of_uninitialized_variables( + g->g_entryblock, nlocals, nparams)); + int ret = optimize_cfg(g, consts, const_cache, consts_index, firstlineno); _Py_hashtable_destroy(consts_index); @@ -3797,9 +3796,6 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, RETURN_IF_ERROR(ret); RETURN_IF_ERROR(remove_unused_consts(g->g_entryblock, consts)); - RETURN_IF_ERROR( - add_checks_for_loads_of_uninitialized_variables( - g->g_entryblock, nlocals, nparams)); RETURN_IF_ERROR(insert_superinstructions(g)); RETURN_IF_ERROR(push_cold_blocks_to_end(g)); From f15d1c052e6ff0c9ff7fac24a669bb3ac498d9ad Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:18:25 -0400 Subject: [PATCH 2/3] undo uninitialize change --- Python/flowgraph.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index d3a5d95e8a6289c..8e96369dbf1f9ca 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -3784,11 +3784,6 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, return ERROR; } } - - RETURN_IF_ERROR( - add_checks_for_loads_of_uninitialized_variables( - g->g_entryblock, nlocals, nparams)); - int ret = optimize_cfg(g, consts, const_cache, consts_index, firstlineno); _Py_hashtable_destroy(consts_index); @@ -3796,6 +3791,9 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, RETURN_IF_ERROR(ret); RETURN_IF_ERROR(remove_unused_consts(g->g_entryblock, consts)); + RETURN_IF_ERROR( + add_checks_for_loads_of_uninitialized_variables( + g->g_entryblock, nlocals, nparams)); RETURN_IF_ERROR(insert_superinstructions(g)); RETURN_IF_ERROR(push_cold_blocks_to_end(g)); From c05eaf1a85386914088ace79fedb0098112ac2a3 Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:39:15 -0400 Subject: [PATCH 3/3] update order --- Python/flowgraph.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 8e96369dbf1f9ca..c34cf432b77c779 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -2630,9 +2630,15 @@ remove_redundant_nops_and_jumps(cfg_builder *g) Code trasnformations that reduce code size initially fill the gaps with NOPs. Later those NOPs are removed. */ + +static int +add_checks_for_loads_of_uninitialized_variables(basicblock *entryblock, + int nlocals, + int nparams); + static int optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache, - _Py_hashtable_t *consts_index, int firstlineno) + _Py_hashtable_t *consts_index, int firstlineno, int nlocals, int nparams) { assert(PyDict_CheckExact(const_cache)); RETURN_IF_ERROR(check_cfg(g)); @@ -2643,6 +2649,9 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache, for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts, consts_index)); } + RETURN_IF_ERROR( + add_checks_for_loads_of_uninitialized_variables( + g->g_entryblock, nlocals, nparams)); RETURN_IF_ERROR(remove_redundant_nops_and_pairs(g->g_entryblock)); RETURN_IF_ERROR(remove_unreachable(g->g_entryblock)); RETURN_IF_ERROR(remove_redundant_nops_and_jumps(g)); @@ -3784,16 +3793,14 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache, return ERROR; } } - int ret = optimize_cfg(g, consts, const_cache, consts_index, firstlineno); + + int ret = optimize_cfg(g, consts, const_cache, consts_index, firstlineno, nlocals, nparams); _Py_hashtable_destroy(consts_index); RETURN_IF_ERROR(ret); RETURN_IF_ERROR(remove_unused_consts(g->g_entryblock, consts)); - RETURN_IF_ERROR( - add_checks_for_loads_of_uninitialized_variables( - g->g_entryblock, nlocals, nparams)); RETURN_IF_ERROR(insert_superinstructions(g)); RETURN_IF_ERROR(push_cold_blocks_to_end(g));