diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 8597b8f14ac058..8c29523874bde8 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1029,28 +1029,31 @@ def expected(count, w): s = ['''\ 1 %*d RESUME 0 +''' % (w, 0)] + s += ['''\ 2 %*d LOAD_FAST 0 (x) %*d LOAD_CONST 1 (1) %*d BINARY_OP 0 (+) -''' % (w, 0, w, 2, w, 4, w, 6)] +''' % (w, 2, w, 4, w, 6)] s += ['''\ - %*d STORE_FAST_LOAD_FAST 0 (x, x) + %*d POP_TOP + %*d LOAD_FAST 0 (x) %*d LOAD_CONST 1 (1) %*d BINARY_OP 0 (+) -''' % (w, 8*i + 10, w, 8*i + 12, w, 8*i + 14) - for i in range(count-1)] +''' % (w, 10*i + 10, w, 10*i + 12, w, 10*i + 14, w, 10*i + 16) + for i in range(count-1)] s += ['''\ %*d STORE_FAST 0 (x) 3 %*d LOAD_FAST 0 (x) %*d RETURN_VALUE -''' % (w, 8*count + 2, w, 8*count + 4, w, 8*count + 6)] +''' % (w, 10*count, w, 10*count + 2, w, 10*count + 4)] return ''.join(s) for i in range(1, 5): self.do_disassembly_test(func(i), expected(i, 4), True) - self.do_disassembly_test(func(1200), expected(1200, 4), True) - self.do_disassembly_test(func(1300), expected(1300, 5), True) + self.do_disassembly_test(func(999), expected(999, 4), True) + self.do_disassembly_test(func(1000), expected(1000, 5), True) def test_disassemble_str(self): self.do_disassembly_test(expr_str, dis_expr_str) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 82b0b50d0ea437..3a6af0250e690c 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -1104,6 +1104,25 @@ def test_dead_store_elimination_in_same_lineno(self): ] self.cfg_optimization_test(insts, expected_insts, consts=list(range(3)), nlocals=1) + def test_dead_store_elimination_in_same_lineno_wider(self): + insts = [ + ('LOAD_CONST', 0, 1), + ('LOAD_CONST', 1, 2), + ('LOAD_CONST', 2, 3), + ('STORE_FAST', 1, 4), + ('STORE_FAST', 0, 4), + ('STORE_FAST', 1, 4), + ('RETURN_VALUE', 5) + ] + expected_insts = [ + ('LOAD_CONST', 0, 1), + ('LOAD_CONST', 1, 2), + ('NOP', 0, 3), + ('STORE_FAST_STORE_FAST', 1, 4), + ('RETURN_VALUE', 5) + ] + self.cfg_optimization_test(insts, expected_insts, consts=list(range(3)), nlocals=1) + def test_no_dead_store_elimination_in_different_lineno(self): insts = [ ('LOAD_CONST', 0, 1), diff --git a/Python/flowgraph.c b/Python/flowgraph.c index e159a4356dfe46..d2c32988f3ca29 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1303,6 +1303,30 @@ apply_static_swaps(basicblock *block, int i) } } +static void +remove_redundant_deadstore(basicblock *bb) +{ + for (int i = bb->b_iused - 1; i >= 0; i--) { + cfg_instr *inst = &bb->b_instr[i]; + // Optimize only if the opcode is STORE_FAST and the lineno is same. + // The number of STORE_FAST will be decreased if the optimization is success. + // So most of case could be skipped. + if (inst->i_opcode == STORE_FAST) { + for (int j = i - 1; j >= 0; j--) { + cfg_instr *prev = &bb->b_instr[j]; + if (prev->i_loc.lineno != inst->i_loc.lineno) { + // Invariant condition: lineno is always monotonically increasing. + break; + } + if (prev->i_opcode == STORE_FAST && prev->i_oparg == inst->i_oparg) { + prev->i_opcode = POP_TOP; + prev->i_oparg = 0; + } + } + } + } +} + static int optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) { @@ -1314,6 +1338,9 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) int opcode = 0; int oparg = 0; int nextop = 0; + + remove_redundant_deadstore(bb); + for (int i = 0; i < bb->b_iused; i++) { cfg_instr *inst = &bb->b_instr[i]; bool is_copy_of_load_const = (opcode == LOAD_CONST && @@ -1491,14 +1518,6 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts) */ } break; - case STORE_FAST: - if (opcode == nextop && - oparg == bb->b_instr[i+1].i_oparg && - bb->b_instr[i].i_loc.lineno == bb->b_instr[i+1].i_loc.lineno) { - bb->b_instr[i].i_opcode = POP_TOP; - bb->b_instr[i].i_oparg = 0; - } - break; case SWAP: if (oparg == 1) { INSTR_SET_OP0(inst, NOP);