bpo-42615: Delete redundant jump instructions that only bypass empty blocks (GH-23733)
* Delete jump instructions that bypass empty blocks
* Add news entry
* Explicitly check for unconditional jump opcodes
Using the is_jump function results in the inclusion of instructions like
returns for which this optimization is not really valid. So, instead
explicitly check that the instruction is an unconditional jump.
* Handle conditional jumps, delete jumps gracefully
* Ensure b_nofallthrough and b_reachable are valid
* Add test for redundant jumps
* Regenerate importlib.h and edit Misc/ACKS
* Fix bad whitespace
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 3a37b6c..3e826b9 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -837,6 +837,34 @@ def test_big_dict_literal(self):
the_dict = "{" + ",".join(f"{x}:{x}" for x in range(dict_size)) + "}"
self.assertEqual(len(eval(the_dict)), dict_size)
+ def test_redundant_jump_in_if_else_break(self):
+ # Check if bytecode containing jumps that simply point to the next line
+ # is generated around if-else-break style structures. See bpo-42615.
+
+ def if_else_break():
+ val = 1
+ while True:
+ if val > 0:
+ val -= 1
+ else:
+ break
+ val = -1
+
+ INSTR_SIZE = 2
+ HANDLED_JUMPS = (
+ 'POP_JUMP_IF_FALSE',
+ 'POP_JUMP_IF_TRUE',
+ 'JUMP_ABSOLUTE',
+ 'JUMP_FORWARD',
+ )
+
+ for line, instr in enumerate(dis.Bytecode(if_else_break)):
+ if instr.opname == 'JUMP_FORWARD':
+ self.assertNotEqual(instr.arg, 0)
+ elif instr.opname in HANDLED_JUMPS:
+ self.assertNotEqual(instr.arg, (line + 1)*INSTR_SIZE)
+
+
class TestExpressionStackSize(unittest.TestCase):
# These tests check that the computed stack size for a code object
# stays within reasonable bounds (see issue #21523 for an example