GH-125837: Split LOAD_CONST into three. (GH-125972)

* Add LOAD_CONST_IMMORTAL opcode

* Add LOAD_SMALL_INT opcode

* Remove RETURN_CONST opcode
This commit is contained in:
Mark Shannon 2024-10-29 11:15:42 +00:00 committed by GitHub
parent 67f5c5bd6f
commit faa3272fb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 706 additions and 538 deletions

View file

@ -775,7 +775,6 @@ class TestSpecifics(unittest.TestCase):
self.assertEqual(repr(f1()), repr(const))
check_same_constant(None)
check_same_constant(0)
check_same_constant(0.0)
check_same_constant(b'abc')
check_same_constant('abc')
@ -853,9 +852,9 @@ class TestSpecifics(unittest.TestCase):
eval(compile(code, "file.py", "exec"), g)
exec(code, g)
f = g['f']
expected = tuple([None, '', 1] + [f't{i}' for i in range(N)])
expected = tuple([None, ''] + [f't{i}' for i in range(N)])
self.assertEqual(f.__code__.co_consts, expected)
expected = "".join(expected[3:])
expected = "".join(expected[2:])
self.assertEqual(expected, f())
# Stripping unused constants is not a strict requirement for the
@ -867,7 +866,7 @@ class TestSpecifics(unittest.TestCase):
def f1():
"docstring"
return 42
self.assertEqual(f1.__code__.co_consts, (f1.__doc__, 42))
self.assertEqual(f1.__code__.co_consts, (f1.__doc__,))
# This is a regression test for a CPython specific peephole optimizer
# implementation bug present in a few releases. It's assertion verifies
@ -884,7 +883,7 @@ class TestSpecifics(unittest.TestCase):
# RETURN_VALUE opcode. This does not always crash an interpreter.
# When you build with the clang memory sanitizer it reliably aborts.
self.assertEqual(
'RETURN_CONST',
'RETURN_VALUE',
list(dis.get_instructions(unused_code_at_end))[-1].opname)
@support.cpython_only
@ -982,7 +981,6 @@ class TestSpecifics(unittest.TestCase):
self.assertEqual(repr(f1()), repr(const1))
self.assertEqual(repr(f2()), repr(const2))
check_different_constants(0, 0.0)
check_different_constants(+0.0, -0.0)
check_different_constants((0,), (0.0,))
check_different_constants('a', b'a')
@ -1045,8 +1043,8 @@ class TestSpecifics(unittest.TestCase):
for func in funcs:
opcodes = list(dis.get_instructions(func))
self.assertLessEqual(len(opcodes), 3)
self.assertEqual('RETURN_CONST', opcodes[-1].opname)
self.assertLessEqual(len(opcodes), 4)
self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
self.assertEqual(None, opcodes[-1].argval)
def test_false_while_loop(self):
@ -1063,8 +1061,8 @@ class TestSpecifics(unittest.TestCase):
# Check that we did not raise but we also don't generate bytecode
for func in funcs:
opcodes = list(dis.get_instructions(func))
self.assertEqual(2, len(opcodes))
self.assertEqual('RETURN_CONST', opcodes[1].opname)
self.assertEqual(3, len(opcodes))
self.assertEqual('RETURN_VALUE', opcodes[-1].opname)
self.assertEqual(None, opcodes[1].argval)
def test_consts_in_conditionals(self):
@ -1738,7 +1736,7 @@ class TestSourcePositions(unittest.TestCase):
line=1, end_line=3, column=0, end_column=36, occurrence=1)
# The "error msg":
self.assertOpcodeSourcePositionIs(compiled_code, 'LOAD_CONST',
line=3, end_line=3, column=25, end_column=36, occurrence=4)
line=3, end_line=3, column=25, end_column=36, occurrence=2)
self.assertOpcodeSourcePositionIs(compiled_code, 'CALL',
line=1, end_line=3, column=0, end_column=36, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RAISE_VARARGS',
@ -1760,7 +1758,7 @@ class TestSourcePositions(unittest.TestCase):
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=1, end_line=2, column=1, end_column=8, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=4, end_line=4, column=7, end_column=14, occurrence=1)
def test_multiline_async_generator_expression(self):
@ -1777,7 +1775,7 @@ class TestSourcePositions(unittest.TestCase):
self.assertIsInstance(compiled_code, types.CodeType)
self.assertOpcodeSourcePositionIs(compiled_code, 'YIELD_VALUE',
line=1, end_line=2, column=1, end_column=8, occurrence=2)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=1, end_line=6, column=0, end_column=32, occurrence=1)
def test_multiline_list_comprehension(self):
@ -1815,7 +1813,7 @@ class TestSourcePositions(unittest.TestCase):
line=2, end_line=3, column=5, end_column=12, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=2, end_line=3, column=5, end_column=12, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=2, end_line=7, column=4, end_column=36, occurrence=1)
def test_multiline_set_comprehension(self):
@ -1853,7 +1851,7 @@ class TestSourcePositions(unittest.TestCase):
line=2, end_line=3, column=5, end_column=12, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=2, end_line=3, column=5, end_column=12, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=2, end_line=7, column=4, end_column=36, occurrence=1)
def test_multiline_dict_comprehension(self):
@ -1891,7 +1889,7 @@ class TestSourcePositions(unittest.TestCase):
line=2, end_line=3, column=5, end_column=11, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'JUMP_BACKWARD',
line=2, end_line=3, column=5, end_column=11, occurrence=1)
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_CONST',
self.assertOpcodeSourcePositionIs(compiled_code, 'RETURN_VALUE',
line=2, end_line=7, column=4, end_column=36, occurrence=1)
def test_matchcase_sequence(self):
@ -2204,8 +2202,8 @@ class TestSourcePositions(unittest.TestCase):
start_line, end_line, _, _ = instr.positions
self.assertEqual(start_line, end_line)
# Expect three load None instructions for the no-exception __exit__ call,
# and one RETURN_VALUE.
# Expect four `LOAD_CONST None` instructions:
# three for the no-exception __exit__ call, and one for the return.
# They should all have the locations of the context manager ('xyz').
load_none = [instr for instr in dis.get_instructions(f) if
@ -2213,8 +2211,8 @@ class TestSourcePositions(unittest.TestCase):
return_value = [instr for instr in dis.get_instructions(f) if
instr.opname == 'RETURN_VALUE']
self.assertEqual(len(load_none), 3)
self.assertEqual(len(return_value), 1)
self.assertEqual(len(load_none), 4)
self.assertEqual(len(return_value), 2)
for instr in load_none + return_value:
start_line, end_line, start_col, end_col = instr.positions
self.assertEqual(start_line, f.__code__.co_firstlineno + 1)