summaryrefslogtreecommitdiff
path: root/Lib/test/test_peepholer.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_peepholer.py')
-rw-r--r--Lib/test/test_peepholer.py81
1 files changed, 80 insertions, 1 deletions
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index 794d104d59..860ceeb003 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -1,5 +1,7 @@
import dis
import unittest
+import types
+import textwrap
from test.bytecode_helper import BytecodeTestCase
@@ -18,6 +20,27 @@ def count_instr_recursively(f, opname):
class TestTranforms(BytecodeTestCase):
+ def check_jump_targets(self, code):
+ instructions = list(dis.get_instructions(code))
+ targets = {instr.offset: instr for instr in instructions}
+ for instr in instructions:
+ if 'JUMP_' not in instr.opname:
+ continue
+ tgt = targets[instr.argval]
+ # jump to unconditional jump
+ if tgt.opname in ('JUMP_ABSOLUTE', 'JUMP_FORWARD'):
+ self.fail(f'{instr.opname} at {instr.offset} '
+ f'jumps to {tgt.opname} at {tgt.offset}')
+ # unconditional jump to RETURN_VALUE
+ if (instr.opname in ('JUMP_ABSOLUTE', 'JUMP_FORWARD') and
+ tgt.opname == 'RETURN_VALUE'):
+ self.fail(f'{instr.opname} at {instr.offset} '
+ f'jumps to {tgt.opname} at {tgt.offset}')
+ # JUMP_IF_*_OR_POP jump to conditional jump
+ if '_OR_POP' in instr.opname and 'JUMP_IF_' in tgt.opname:
+ self.fail(f'{instr.opname} at {instr.offset} '
+ f'jumps to {tgt.opname} at {tgt.offset}')
+
def test_unot(self):
# UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
def unot(x):
@@ -259,13 +282,69 @@ class TestTranforms(BytecodeTestCase):
def test_elim_jump_to_return(self):
# JUMP_FORWARD to RETURN --> RETURN
def f(cond, true_value, false_value):
- return true_value if cond else false_value
+ # Intentionally use two-line expression to test issue37213.
+ return (true_value if cond
+ else false_value)
+ self.check_jump_targets(f)
self.assertNotInBytecode(f, 'JUMP_FORWARD')
self.assertNotInBytecode(f, 'JUMP_ABSOLUTE')
returns = [instr for instr in dis.get_instructions(f)
if instr.opname == 'RETURN_VALUE']
self.assertEqual(len(returns), 2)
+ def test_elim_jump_to_uncond_jump(self):
+ # POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump
+ def f():
+ if a:
+ # Intentionally use two-line expression to test issue37213.
+ if (c
+ or d):
+ foo()
+ else:
+ baz()
+ self.check_jump_targets(f)
+
+ def test_elim_jump_to_uncond_jump2(self):
+ # POP_JUMP_IF_FALSE to JUMP_ABSOLUTE --> POP_JUMP_IF_FALSE to non-jump
+ def f():
+ while a:
+ # Intentionally use two-line expression to test issue37213.
+ if (c
+ or d):
+ a = foo()
+ self.check_jump_targets(f)
+
+ def test_elim_jump_to_uncond_jump3(self):
+ # Intentionally use two-line expressions to test issue37213.
+ # JUMP_IF_FALSE_OR_POP to JUMP_IF_FALSE_OR_POP --> JUMP_IF_FALSE_OR_POP to non-jump
+ def f(a, b, c):
+ return ((a and b)
+ and c)
+ self.check_jump_targets(f)
+ self.assertEqual(count_instr_recursively(f, 'JUMP_IF_FALSE_OR_POP'), 2)
+ # JUMP_IF_TRUE_OR_POP to JUMP_IF_TRUE_OR_POP --> JUMP_IF_TRUE_OR_POP to non-jump
+ def f(a, b, c):
+ return ((a or b)
+ or c)
+ self.check_jump_targets(f)
+ self.assertEqual(count_instr_recursively(f, 'JUMP_IF_TRUE_OR_POP'), 2)
+ # JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump
+ def f(a, b, c):
+ return ((a and b)
+ or c)
+ self.check_jump_targets(f)
+ self.assertNotInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
+ self.assertInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
+ self.assertInBytecode(f, 'POP_JUMP_IF_FALSE')
+ # JUMP_IF_TRUE_OR_POP to JUMP_IF_FALSE_OR_POP --> POP_JUMP_IF_TRUE to non-jump
+ def f(a, b, c):
+ return ((a or b)
+ and c)
+ self.check_jump_targets(f)
+ self.assertNotInBytecode(f, 'JUMP_IF_TRUE_OR_POP')
+ self.assertInBytecode(f, 'JUMP_IF_FALSE_OR_POP')
+ self.assertInBytecode(f, 'POP_JUMP_IF_TRUE')
+
def test_elim_jump_after_return1(self):
# Eliminate dead code: jumps immediately after returns can't be reached
def f(cond1, cond2):