https://github.com/python/cpython/commit/7c9ad27dd1fc9e05149b471b055f18ad64cd05f3
commit: 7c9ad27dd1fc9e05149b471b055f18ad64cd05f3
branch: main
author: Neko Asakura <[email protected]>
committer: markshannon <[email protected]>
date: 2026-05-02T19:59:51+01:00
summary:
gh-148871: extend and improve `LOAD_COMMON_CONSTANT` (GH-148971)
files:
M Include/internal/pycore_opcode_utils.h
M Lib/dis.py
M Lib/opcode.py
M Lib/test/test_ast/test_ast.py
M Lib/test/test_capi/test_opt.py
M Lib/test/test_code.py
M Lib/test/test_compile.py
M Lib/test/test_dis.py
M Lib/test/test_peepholer.py
M Objects/codeobject.c
M Programs/test_frozenmain.h
M Python/flowgraph.c
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Python/pylifecycle.c
diff --git a/Include/internal/pycore_opcode_utils.h
b/Include/internal/pycore_opcode_utils.h
index 1e71784c809d2d..3e2c4ae411c925 100644
--- a/Include/internal/pycore_opcode_utils.h
+++ b/Include/internal/pycore_opcode_utils.h
@@ -75,7 +75,12 @@ extern "C" {
#define CONSTANT_BUILTIN_ANY 4
#define CONSTANT_BUILTIN_LIST 5
#define CONSTANT_BUILTIN_SET 6
-#define NUM_COMMON_CONSTANTS 7
+#define CONSTANT_NONE 7
+#define CONSTANT_EMPTY_STR 8
+#define CONSTANT_TRUE 9
+#define CONSTANT_FALSE 10
+#define CONSTANT_MINUS_ONE 11
+#define NUM_COMMON_CONSTANTS 12
/* Values used in the oparg for RESUME */
#define RESUME_AT_FUNC_START 0
diff --git a/Lib/dis.py b/Lib/dis.py
index 58c7f6419032c6..64f3450da3071b 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -643,6 +643,7 @@ def get_argval_argrepr(self, op, arg, offset):
argrepr = _intrinsic_2_descs[arg]
elif deop == LOAD_COMMON_CONSTANT:
obj = _common_constants[arg]
+ argval = obj
if isinstance(obj, type):
argrepr = obj.__name__
else:
@@ -692,10 +693,15 @@ def _get_const_value(op, arg, co_consts):
Otherwise (if it is a LOAD_CONST and co_consts is not
provided) returns the dis.UNKNOWN sentinel.
"""
- assert op in hasconst or op == LOAD_SMALL_INT
+ assert op in hasconst or op == LOAD_SMALL_INT or op == LOAD_COMMON_CONSTANT
if op == LOAD_SMALL_INT:
return arg
+ if op == LOAD_COMMON_CONSTANT:
+ # Opargs 0-6 are callables; 7-11 are literal values.
+ if 7 <= arg <= 11:
+ return _common_constants[arg]
+ return UNKNOWN
argval = UNKNOWN
if co_consts is not None:
argval = co_consts[arg]
@@ -1015,8 +1021,9 @@ def _find_imports(co):
if op == IMPORT_NAME and i >= 2:
from_op = opargs[i-1]
level_op = opargs[i-2]
- if (from_op[0] in hasconst and
- (level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT)):
+ if ((from_op[0] in hasconst or from_op[0] == LOAD_COMMON_CONSTANT)
and
+ (level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT or
+ level_op[0] == LOAD_COMMON_CONSTANT)):
level = _get_const_value(level_op[0], level_op[1], consts)
fromlist = _get_const_value(from_op[0], from_op[1], consts)
# IMPORT_NAME encodes lazy/eager flags in bits 0-1,
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 8466814d2257c3..4e60fb5af34f22 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -41,7 +41,10 @@
_special_method_names = _opcode.get_special_method_names()
_common_constants = [builtins.AssertionError, builtins.NotImplementedError,
builtins.tuple, builtins.all, builtins.any, builtins.list,
- builtins.set]
+ builtins.set,
+ # Append-only — must match CONSTANT_* in
+ # Include/internal/pycore_opcode_utils.h.
+ None, "", True, False, -1]
_nb_ops = _opcode.get_nb_ops()
hascompare = [opmap["COMPARE_OP"]]
diff --git a/Lib/test/test_ast/test_ast.py b/Lib/test/test_ast/test_ast.py
index fcc6c93eb86981..af8b334da03e70 100644
--- a/Lib/test/test_ast/test_ast.py
+++ b/Lib/test/test_ast/test_ast.py
@@ -2685,11 +2685,12 @@ def test_get_docstring(self):
def get_load_const(self, tree):
# Compile to bytecode, disassemble and get parameter of LOAD_CONST
- # instructions
+ # and LOAD_COMMON_CONSTANT instructions
co = compile(tree, '<string>', 'exec')
consts = []
for instr in dis.get_instructions(co):
- if instr.opcode in dis.hasconst:
+ if instr.opcode in dis.hasconst or \
+ instr.opname == 'LOAD_COMMON_CONSTANT':
consts.append(instr.argval)
return consts
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 7118dfeed9faee..d4af910fb68c61 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -3446,6 +3446,27 @@ def testfunc(n):
self.assertIn("_BUILD_LIST", uops)
self.assertNotIn("_LOAD_COMMON_CONSTANT", uops)
+ def test_load_common_constant_new_literals(self):
+ def testfunc(n):
+ x = None
+ s = ""
+ t = True
+ f = False
+ m = -1
+ for _ in range(n):
+ x = None
+ s = ""
+ t = True
+ f = False
+ m = -1
+ return x, s, t, f, m
+ res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+ self.assertEqual(res, (None, "", True, False, -1))
+ self.assertIsNotNone(ex)
+ uops = get_opnames(ex)
+ self.assertNotIn("_LOAD_COMMON_CONSTANT", uops)
+ self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
+
def test_load_small_int(self):
def testfunc(n):
x = 0
diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py
index 5e802a929b14b8..3588872ed23ac4 100644
--- a/Lib/test/test_code.py
+++ b/Lib/test/test_code.py
@@ -87,7 +87,7 @@
freevars: ()
nlocals: 0
flags: 67108867
-consts: ("'doc string'", 'None')
+consts: ("'doc string'",)
>>> def keywordonly_args(a,b,*,k1):
... return a,b,k1
@@ -161,7 +161,7 @@
freevars: ()
nlocals: 3
flags: 67108995
-consts: ("'This is a docstring from async function'", 'None')
+consts: ("'This is a docstring from async function'",)
>>> def no_docstring(x, y, z):
... return x + "hello" + y + z + "world"
@@ -532,7 +532,7 @@ def test_co_positions_artificial_instructions(self):
],
[
("PUSH_EXC_INFO", None),
- ("LOAD_CONST", None), # artificial 'None'
+ ("LOAD_COMMON_CONSTANT", None), # artificial 'None'
("STORE_NAME", "e"), # XX: we know the location for this
("DELETE_NAME", "e"),
("RERAISE", 1),
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index ac8837359c1445..9edbca3c383b43 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -2485,12 +2485,13 @@ def f():
start_line, end_line, _, _ = instr.positions
self.assertEqual(start_line, end_line)
- # Expect four `LOAD_CONST None` instructions:
+ # Expect four `None`-loading 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
- instr.opname == 'LOAD_CONST' and instr.argval is None]
+ instr.opname in ('LOAD_CONST', 'LOAD_COMMON_CONSTANT')
+ and instr.argval is None]
return_value = [instr for instr in dis.get_instructions(f) if
instr.opname == 'RETURN_VALUE']
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index cc9bc918b616e2..8be104585814f4 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -56,7 +56,7 @@ def cm(cls, x):
COMPARE_OP 72 (==)
LOAD_FAST_BORROW 0 (self)
STORE_ATTR 0 (x)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
""" % (_C.__init__.__code__.co_firstlineno,
_C.__init__.__code__.co_firstlineno + 1,)
@@ -67,7 +67,7 @@ def cm(cls, x):
COMPARE_OP 72 (==)
LOAD_FAST_BORROW 0
STORE_ATTR 0
- LOAD_CONST 1
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -79,7 +79,7 @@ def cm(cls, x):
COMPARE_OP 72 (==)
LOAD_FAST_BORROW 0 (cls)
STORE_ATTR 0 (x)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
""" % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,)
@@ -90,7 +90,7 @@ def cm(cls, x):
LOAD_SMALL_INT 1
COMPARE_OP 72 (==)
STORE_FAST 0 (x)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
""" % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,)
@@ -182,7 +182,7 @@ def bug708901():
%3d L2: END_FOR
POP_ITER
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
""" % (bug708901.__code__.co_firstlineno,
bug708901.__code__.co_firstlineno + 1,
@@ -229,7 +229,7 @@ def bug42562():
dis_bug42562 = """\
RESUME 0
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -282,7 +282,7 @@ def wrap_func_w_kwargs():
LOAD_CONST 1 (('c',))
CALL_KW 3
POP_TOP
- LOAD_CONST 2 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
""" % (wrap_func_w_kwargs.__code__.co_firstlineno,
wrap_func_w_kwargs.__code__.co_firstlineno + 1)
@@ -295,7 +295,7 @@ def wrap_func_w_kwargs():
IMPORT_NAME 2 (math + eager)
CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR)
POP_TOP
- LOAD_CONST 2 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -322,7 +322,7 @@ def wrap_func_w_kwargs():
%3d LOAD_GLOBAL 0 (spam)
POP_TOP
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -331,19 +331,19 @@ def wrap_func_w_kwargs():
%4d LOAD_GLOBAL 0 (spam)
POP_TOP
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
dis_module_expected_results = """\
Disassembly of f:
4 RESUME 0
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
Disassembly of g:
5 RESUME 0
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -368,7 +368,7 @@ def wrap_func_w_kwargs():
LOAD_SMALL_INT 1
BINARY_OP 0 (+)
STORE_NAME 0 (x)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -409,7 +409,7 @@ def wrap_func_w_kwargs():
LOAD_SMALL_INT 0
CALL 1
STORE_SUBSCR
- LOAD_CONST 2 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -427,7 +427,7 @@ def foo(a: int, b: str) -> str:
MAKE_FUNCTION
SET_FUNCTION_ATTRIBUTE 16 (annotate)
STORE_NAME 0 (foo)
- LOAD_CONST 2 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""
@@ -477,14 +477,14 @@ def foo(a: int, b: str) -> str:
LOAD_ATTR 2 (__traceback__)
STORE_FAST 1 (tb)
L7: POP_EXCEPT
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
STORE_FAST 0 (e)
DELETE_FAST 0 (e)
%4d LOAD_FAST 1 (tb)
RETURN_VALUE
- -- L8: LOAD_CONST 1 (None)
+ -- L8: LOAD_COMMON_CONSTANT 7 (None)
STORE_FAST 0 (e)
DELETE_FAST 0 (e)
RERAISE 1
@@ -554,15 +554,15 @@ def _with(c):
%4d LOAD_SMALL_INT 1
STORE_FAST 1 (x)
-%4d L2: LOAD_CONST 1 (None)
- LOAD_CONST 1 (None)
- LOAD_CONST 1 (None)
+%4d L2: LOAD_COMMON_CONSTANT 7 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
CALL 3
POP_TOP
%4d LOAD_SMALL_INT 2
STORE_FAST 2 (y)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
%4d L3: PUSH_EXC_INFO
@@ -579,7 +579,7 @@ def _with(c):
%4d LOAD_SMALL_INT 2
STORE_FAST 2 (y)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
-- L8: COPY 3
@@ -617,7 +617,7 @@ async def _asyncwith(c):
CALL 0
GET_AWAITABLE 1
PUSH_NULL
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
L2: SEND 4 (to L5)
L3: YIELD_VALUE 1
L4: RESUME 3
@@ -628,13 +628,13 @@ async def _asyncwith(c):
%4d LOAD_SMALL_INT 1
STORE_FAST 1 (x)
-%4d L7: LOAD_CONST 0 (None)
- LOAD_CONST 0 (None)
- LOAD_CONST 0 (None)
+%4d L7: LOAD_COMMON_CONSTANT 7 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
CALL 3
GET_AWAITABLE 2
PUSH_NULL
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
L8: SEND 4 (to L11)
L9: YIELD_VALUE 1
L10: RESUME 3
@@ -644,7 +644,7 @@ async def _asyncwith(c):
%4d LOAD_SMALL_INT 2
STORE_FAST 2 (y)
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
%4d L12: CLEANUP_THROW
@@ -655,7 +655,7 @@ async def _asyncwith(c):
WITH_EXCEPT_START
GET_AWAITABLE 2
PUSH_NULL
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
L17: SEND 5 (to L21)
L18: YIELD_VALUE 1
L19: RESUME 3
@@ -674,7 +674,7 @@ async def _asyncwith(c):
%4d LOAD_SMALL_INT 2
STORE_FAST 2 (y)
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
-- L26: COPY 3
@@ -895,7 +895,7 @@ def foo(x):
JUMP_BACKWARD 17 (to L2)
L3: END_FOR
POP_ITER
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
-- L4: CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
@@ -933,7 +933,7 @@ def loop_test():
%3d RESUME_CHECK{: <6} 0
%3d BUILD_LIST 0
- LOAD_CONST 2 ((1, 2, 3))
+ LOAD_CONST 1 ((1, 2, 3))
LIST_EXTEND 1
LOAD_SMALL_INT 3
BINARY_OP 5 (*)
@@ -949,7 +949,7 @@ def loop_test():
%3d L2: END_FOR
POP_ITER
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
""" % (loop_test.__code__.co_firstlineno,
loop_test.__code__.co_firstlineno + 1,
@@ -967,7 +967,7 @@ def extended_arg_quick():
UNPACK_EX 256
POP_TOP
STORE_FAST 0 (_)
- LOAD_CONST 1 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
"""% (extended_arg_quick.__code__.co_firstlineno,
extended_arg_quick.__code__.co_firstlineno + 1,)
@@ -1084,7 +1084,7 @@ def test_dis_with_some_positions(self):
'',
'2:3-3:15 NOP',
'',
- '3:11-3:15 LOAD_CONST 0 (None)',
+ '3:11-3:15 LOAD_COMMON_CONSTANT 7 (None)',
'3:11-3:15 RETURN_VALUE',
'',
' -- L1: PUSH_EXC_INFO',
@@ -1115,7 +1115,7 @@ def test_dis_with_linenos_but_no_columns(self):
'',
'2:5-2:6 LOAD_SMALL_INT 1',
'2:?-2:? STORE_FAST 0 (x)',
- '2:?-2:? LOAD_CONST 1 (None)',
+ '2:?-2:? LOAD_COMMON_CONSTANT 7 (None)',
'2:?-2:? RETURN_VALUE',
'',
])
@@ -1128,7 +1128,7 @@ def f():
f.__code__ = f.__code__.replace(co_linetable=b'')
expect = '\n'.join([
' RESUME 0',
- ' LOAD_CONST 0 (None)',
+ ' LOAD_COMMON_CONSTANT 7 (None)',
' RETURN_VALUE',
'',
])
@@ -1533,7 +1533,6 @@ def f(c=c):
Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR
Constants:
0: <code object f at (.*), file "(.*)", line (.*)>
- 1: None
Variable names:
0: a
1: b
@@ -1605,7 +1604,6 @@ def f(c=c):
Flags: 0x0
Constants:
0: 1
- 1: None
Names:
0: x"""
@@ -1640,7 +1638,6 @@ async def async_def():
Flags: OPTIMIZED, NEWLOCALS, COROUTINE
Constants:
0: 1
- 1: None
Names:
0: b
1: c
@@ -1790,7 +1787,7 @@ def _prepare_test_cases():
make_inst(opname='MAKE_CELL', arg=0, argval='a', argrepr='a', offset=0,
start_offset=0, starts_line=True, line_number=None),
make_inst(opname='MAKE_CELL', arg=1, argval='b', argrepr='b', offset=2,
start_offset=2, starts_line=False, line_number=None),
make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=4,
start_offset=4, starts_line=True, line_number=1, cache_info=[('counter', 1,
b'\x00\x00')]),
- make_inst(opname='LOAD_CONST', arg=4, argval=(3, 4), argrepr='(3, 4)',
offset=8, start_offset=8, starts_line=True, line_number=2),
+ make_inst(opname='LOAD_CONST', arg=3, argval=(3, 4), argrepr='(3, 4)',
offset=8, start_offset=8, starts_line=True, line_number=2),
make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='a', argrepr='a',
offset=10, start_offset=10, starts_line=False, line_number=2),
make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='b', argrepr='b',
offset=12, start_offset=12, starts_line=False, line_number=2),
make_inst(opname='BUILD_TUPLE', arg=2, argval=2, argrepr='', offset=14,
start_offset=14, starts_line=False, line_number=2),
@@ -1802,11 +1799,11 @@ def _prepare_test_cases():
make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print +
NULL', offset=26, start_offset=26, starts_line=True, line_number=7,
cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'),
('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1,
b'\x00\x00')]),
make_inst(opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=36,
start_offset=36, starts_line=False, line_number=7),
make_inst(opname='LOAD_DEREF', arg=1, argval='b', argrepr='b', offset=38,
start_offset=38, starts_line=False, line_number=7),
- make_inst(opname='LOAD_CONST', arg=2, argval='', argrepr="''", offset=40,
start_offset=40, starts_line=False, line_number=7),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=8, argval='', argrepr="''",
offset=40, start_offset=40, starts_line=False, line_number=7),
make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=42,
start_offset=42, starts_line=False, line_number=7),
make_inst(opname='BUILD_LIST', arg=0, argval=0, argrepr='', offset=44,
start_offset=44, starts_line=False, line_number=7),
make_inst(opname='BUILD_MAP', arg=0, argval=0, argrepr='', offset=46,
start_offset=46, starts_line=False, line_number=7),
- make_inst(opname='LOAD_CONST', arg=3, argval='Hello world!', argrepr="'Hello
world!'", offset=48, start_offset=48, starts_line=False, line_number=7),
+ make_inst(opname='LOAD_CONST', arg=2, argval='Hello world!', argrepr="'Hello
world!'", offset=48, start_offset=48, starts_line=False, line_number=7),
make_inst(opname='CALL', arg=7, argval=7, argrepr='', offset=50,
start_offset=50, starts_line=False, line_number=7, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=58,
start_offset=58, starts_line=False, line_number=7),
make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='f', argrepr='f',
offset=60, start_offset=60, starts_line=True, line_number=8),
@@ -1851,7 +1848,7 @@ def _prepare_test_cases():
make_inst(opname='LOAD_FAST_BORROW_LOAD_FAST_BORROW', arg=1, argval=('e',
'f'), argrepr='e, f', offset=24, start_offset=24, starts_line=False,
line_number=4),
make_inst(opname='CALL', arg=6, argval=6, argrepr='', offset=26,
start_offset=26, starts_line=False, line_number=4, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=34,
start_offset=34, starts_line=False, line_number=4),
- make_inst(opname='LOAD_CONST', arg=0, argval=None, argrepr='None',
offset=36, start_offset=36, starts_line=False, line_number=4),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None',
offset=36, start_offset=36, starts_line=False, line_number=4),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='',
offset=38, start_offset=38, starts_line=False, line_number=4),
]
@@ -1934,16 +1931,16 @@ def _prepare_test_cases():
make_inst(opname='LOAD_CONST', arg=3, argval='Never reach this',
argrepr="'Never reach this'", offset=292, start_offset=292, starts_line=False,
line_number=26),
make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=294,
start_offset=294, starts_line=False, line_number=26, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=302,
start_offset=302, starts_line=False, line_number=26),
- make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None',
offset=304, start_offset=304, starts_line=True, line_number=25),
- make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None',
offset=306, start_offset=306, starts_line=False, line_number=25),
- make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None',
offset=308, start_offset=308, starts_line=False, line_number=25),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None',
offset=304, start_offset=304, starts_line=True, line_number=25),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None',
offset=306, start_offset=306, starts_line=False, line_number=25),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None',
offset=308, start_offset=308, starts_line=False, line_number=25),
make_inst(opname='CALL', arg=3, argval=3, argrepr='', offset=310,
start_offset=310, starts_line=False, line_number=25, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=318,
start_offset=318, starts_line=False, line_number=25),
make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print +
NULL', offset=320, start_offset=320, starts_line=True, line_number=28,
label=10, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'),
('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1,
b'\x00\x00')]),
- make_inst(opname='LOAD_CONST', arg=6, argval="OK, now we're done",
argrepr='"OK, now we\'re done"', offset=330, start_offset=330,
starts_line=False, line_number=28),
+ make_inst(opname='LOAD_CONST', arg=5, argval="OK, now we're done",
argrepr='"OK, now we\'re done"', offset=330, start_offset=330,
starts_line=False, line_number=28),
make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=332,
start_offset=332, starts_line=False, line_number=28, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=340,
start_offset=340, starts_line=False, line_number=28),
- make_inst(opname='LOAD_CONST', arg=4, argval=None, argrepr='None',
offset=342, start_offset=342, starts_line=False, line_number=28),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None',
offset=342, start_offset=342, starts_line=False, line_number=28),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='',
offset=344, start_offset=344, starts_line=False, line_number=28),
make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='',
offset=346, start_offset=346, starts_line=True, line_number=25),
make_inst(opname='WITH_EXCEPT_START', arg=None, argval=None, argrepr='',
offset=348, start_offset=348, starts_line=False, line_number=25),
@@ -1967,7 +1964,7 @@ def _prepare_test_cases():
make_inst(opname='NOT_TAKEN', arg=None, argval=None, argrepr='', offset=402,
start_offset=402, starts_line=False, line_number=22),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=404,
start_offset=404, starts_line=False, line_number=22),
make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print +
NULL', offset=406, start_offset=406, starts_line=True, line_number=23,
cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'),
('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1,
b'\x00\x00')]),
- make_inst(opname='LOAD_CONST', arg=5, argval='Here we go, here we go, here
we go...', argrepr="'Here we go, here we go, here we go...'", offset=416,
start_offset=416, starts_line=False, line_number=23),
+ make_inst(opname='LOAD_CONST', arg=4, argval='Here we go, here we go, here
we go...', argrepr="'Here we go, here we go, here we go...'", offset=416,
start_offset=416, starts_line=False, line_number=23),
make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=418,
start_offset=418, starts_line=False, line_number=23, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=426,
start_offset=426, starts_line=False, line_number=23),
make_inst(opname='POP_EXCEPT', arg=None, argval=None, argrepr='',
offset=428, start_offset=428, starts_line=False, line_number=23),
@@ -1978,7 +1975,7 @@ def _prepare_test_cases():
make_inst(opname='RERAISE', arg=1, argval=1, argrepr='', offset=438,
start_offset=438, starts_line=False, line_number=None),
make_inst(opname='PUSH_EXC_INFO', arg=None, argval=None, argrepr='',
offset=440, start_offset=440, starts_line=False, line_number=None),
make_inst(opname='LOAD_GLOBAL', arg=3, argval='print', argrepr='print +
NULL', offset=442, start_offset=442, starts_line=True, line_number=28,
cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'),
('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1,
b'\x00\x00')]),
- make_inst(opname='LOAD_CONST', arg=6, argval="OK, now we're done",
argrepr='"OK, now we\'re done"', offset=452, start_offset=452,
starts_line=False, line_number=28),
+ make_inst(opname='LOAD_CONST', arg=5, argval="OK, now we're done",
argrepr='"OK, now we\'re done"', offset=452, start_offset=452,
starts_line=False, line_number=28),
make_inst(opname='CALL', arg=1, argval=1, argrepr='', offset=454,
start_offset=454, starts_line=False, line_number=28, cache_info=[('counter', 1,
b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=462,
start_offset=462, starts_line=False, line_number=28),
make_inst(opname='RERAISE', arg=0, argval=0, argrepr='', offset=464,
start_offset=464, starts_line=False, line_number=28),
@@ -1991,7 +1988,7 @@ def _prepare_test_cases():
def simple(): pass
expected_opinfo_simple = [
make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=0,
start_offset=0, starts_line=True, line_number=simple.__code__.co_firstlineno),
- make_inst(opname='LOAD_CONST', arg=0, argval=None, argrepr='None', offset=4,
start_offset=4, starts_line=False, line_number=simple.__code__.co_firstlineno),
+ make_inst(opname='LOAD_COMMON_CONSTANT', arg=7, argval=None, argrepr='None',
offset=4, start_offset=4, starts_line=False,
line_number=simple.__code__.co_firstlineno),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='',
offset=6, start_offset=6, starts_line=False,
line_number=simple.__code__.co_firstlineno),
]
@@ -2600,7 +2597,7 @@ def test_show_cache(self):
CACHE 0 (func_version: 0)
CACHE 0
POP_TOP
- LOAD_CONST 0 (None)
+ LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
'''
for flag in ['-C', '--show-caches']:
@@ -2612,7 +2609,7 @@ def test_show_offsets(self):
expect = '''
0 0 RESUME 0
- 1 4 LOAD_CONST 0 (None)
+ 1 4 LOAD_COMMON_CONSTANT 7 (None)
6 RETURN_VALUE
'''
for flag in ['-O', '--show-offsets']:
@@ -2624,7 +2621,7 @@ def test_show_positions(self):
expect = '''
0:0-1:0 RESUME 0
- 1:0-1:4 LOAD_CONST 0 (None)
+ 1:0-1:4 LOAD_COMMON_CONSTANT 7 (None)
1:0-1:4 RETURN_VALUE
'''
for flag in ['-P', '--show-positions']:
@@ -2636,7 +2633,7 @@ def test_specialized_code(self):
expect = '''
0 RESUME 0
- 1 LOAD_CONST 0 (None)
+ 1 LOAD_COMMON_CONSTANT 7 (None)
RETURN_VALUE
'''
for flag in ['-S', '--specialized']:
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index abb071451d8b59..d44e61f25f7f55 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -106,7 +106,7 @@ def test_elim_inversion_of_is_or_in(self):
self.check_lnotab(code)
def test_global_as_constant(self):
- # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
+ # LOAD_GLOBAL None/True/False --> LOAD_COMMON_CONSTANT
None/True/False
def f():
x = None
x = None
@@ -121,7 +121,7 @@ def h():
for func, elem in ((f, None), (g, True), (h, False)):
with self.subTest(func=func):
self.assertNotInBytecode(func, 'LOAD_GLOBAL')
- self.assertInBytecode(func, 'LOAD_CONST', elem)
+ self.assertInBytecode(func, 'LOAD_COMMON_CONSTANT', elem)
self.check_lnotab(func)
def f():
@@ -129,7 +129,7 @@ def f():
return None
self.assertNotInBytecode(f, 'LOAD_GLOBAL')
- self.assertInBytecode(f, 'LOAD_CONST', None)
+ self.assertInBytecode(f, 'LOAD_COMMON_CONSTANT', None)
self.check_lnotab(f)
def test_while_one(self):
@@ -146,13 +146,14 @@ def f():
def test_pack_unpack(self):
for line, elem in (
- ('a, = a,', 'LOAD_CONST',),
+ ('a, = a,', None),
('a, b = a, b', 'SWAP',),
('a, b, c = a, b, c', 'SWAP',),
):
with self.subTest(line=line):
code = compile(line,'','single')
- self.assertInBytecode(code, elem)
+ if elem is not None:
+ self.assertInBytecode(code, elem)
self.assertNotInBytecode(code, 'BUILD_TUPLE')
self.assertNotInBytecode(code, 'UNPACK_SEQUENCE')
self.check_lnotab(code)
@@ -174,10 +175,10 @@ def test_constant_folding_tuples_of_constants(self):
# Long tuples should be folded too.
code = compile(repr(tuple(range(10000))),'','single')
self.assertNotInBytecode(code, 'BUILD_TUPLE')
- # One LOAD_CONST for the tuple, one for the None return value
+ # One LOAD_CONST for the tuple; None return value uses
LOAD_COMMON_CONSTANT
load_consts = [instr for instr in dis.get_instructions(code)
if instr.opname == 'LOAD_CONST']
- self.assertEqual(len(load_consts), 2)
+ self.assertEqual(len(load_consts), 1)
self.check_lnotab(code)
# Bug 1053819: Tuple of constants misidentified when presented with:
@@ -283,11 +284,11 @@ def test_constant_folding_unaryop(self):
('-0.0', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0),
('-(1.0-1.0)', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0),
('-0.5', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.5),
- ('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -1),
+ ('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_COMMON_CONSTANT', -1),
('---""', 'UNARY_NEGATIVE', None, False, None, None),
('~~~1', 'UNARY_INVERT', None, True, 'LOAD_CONST', -2),
('~~~""', 'UNARY_INVERT', None, False, None, None),
- ('not not True', 'UNARY_NOT', None, True, 'LOAD_CONST', True),
+ ('not not True', 'UNARY_NOT', None, True, 'LOAD_COMMON_CONSTANT',
True),
('not not x', 'UNARY_NOT', None, True, 'LOAD_NAME', 'x'), # this
should be optimized regardless of constant or not
('+++1', 'CALL_INTRINSIC_1', intrinsic_positive, True,
'LOAD_SMALL_INT', 1),
('---x', 'UNARY_NEGATIVE', None, False, None, None),
@@ -326,7 +327,7 @@ def test_constant_folding_binop(self):
('1 + 2', 'NB_ADD', True, 'LOAD_SMALL_INT', 3),
('1 + 2 + 3', 'NB_ADD', True, 'LOAD_SMALL_INT', 6),
('1 + ""', 'NB_ADD', False, None, None),
- ('1 - 2', 'NB_SUBTRACT', True, 'LOAD_CONST', -1),
+ ('1 - 2', 'NB_SUBTRACT', True, 'LOAD_COMMON_CONSTANT', -1),
('1 - 2 - 3', 'NB_SUBTRACT', True, 'LOAD_CONST', -4),
('1 - ""', 'NB_SUBTRACT', False, None, None),
('2 * 2', 'NB_MULTIPLY', True, 'LOAD_SMALL_INT', 4),
@@ -1539,7 +1540,7 @@ def test_optimize_literal_list_for_iter(self):
end,
('END_FOR', None, 0),
('POP_ITER', None, 0),
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[None],
expected_consts=[None, (1, 2)])
@@ -1572,7 +1573,7 @@ def test_optimize_literal_list_for_iter(self):
end,
('END_FOR', None, 0),
('POP_ITER', None, 0),
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[None],
expected_consts=[None])
@@ -1604,7 +1605,7 @@ def test_optimize_literal_set_for_iter(self):
end,
('END_FOR', None, 0),
('POP_ITER', None, 0),
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[None],
expected_consts=[None, frozenset({1, 2})])
@@ -1626,7 +1627,22 @@ def test_optimize_literal_set_for_iter(self):
('LOAD_CONST', 0, 0),
('RETURN_VALUE', None, 0),
]
- self.cfg_optimization_test(same, same, consts=[None],
expected_consts=[None])
+ expected = [
+ ('LOAD_SMALL_INT', 1, 0),
+ ('LOAD_NAME', 0, 0),
+ ('BUILD_SET', 2, 0),
+ ('GET_ITER', 0, 0),
+ start := self.Label(),
+ ('FOR_ITER', end := self.Label(), 0),
+ ('STORE_FAST', 0, 0),
+ ('JUMP', start, 0),
+ end,
+ ('END_FOR', None, 0),
+ ('POP_ITER', None, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
+ ('RETURN_VALUE', None, 0),
+ ]
+ self.cfg_optimization_test(same, expected, consts=[None],
expected_consts=[None])
def test_optimize_literal_list_contains(self):
# x in [1, 2] ==> x in (1, 2)
@@ -1645,7 +1661,7 @@ def test_optimize_literal_list_contains(self):
('LOAD_CONST', 1, 0),
('CONTAINS_OP', 0, 0),
('POP_TOP', None, 0),
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[None],
expected_consts=[None, (1, 2)])
@@ -1668,7 +1684,7 @@ def test_optimize_literal_list_contains(self):
('BUILD_TUPLE', 2, 0),
('CONTAINS_OP', 0, 0),
('POP_TOP', None, 0),
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[None],
expected_consts=[None])
@@ -1690,7 +1706,7 @@ def test_optimize_literal_set_contains(self):
('LOAD_CONST', 1, 0),
('CONTAINS_OP', 0, 0),
('POP_TOP', None, 0),
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[None],
expected_consts=[None, frozenset({1, 2})])
@@ -1707,7 +1723,17 @@ def test_optimize_literal_set_contains(self):
('LOAD_CONST', 0, 0),
('RETURN_VALUE', None, 0),
]
- self.cfg_optimization_test(same, same, consts=[None],
expected_consts=[None])
+ expected = [
+ ('LOAD_NAME', 0, 0),
+ ('LOAD_SMALL_INT', 1, 0),
+ ('LOAD_NAME', 1, 0),
+ ('BUILD_SET', 2, 0),
+ ('CONTAINS_OP', 0, 0),
+ ('POP_TOP', None, 0),
+ ('LOAD_COMMON_CONSTANT', 7, 0),
+ ('RETURN_VALUE', None, 0),
+ ]
+ self.cfg_optimization_test(same, expected, consts=[None],
expected_consts=[None])
def test_optimize_unary_not(self):
# test folding
@@ -1718,10 +1744,10 @@ def test_optimize_unary_not(self):
('RETURN_VALUE', None, 0),
]
after = [
- ('LOAD_CONST', 1, 0),
+ ('LOAD_COMMON_CONSTANT', 10, 0),
('RETURN_VALUE', None, 0),
]
- self.cfg_optimization_test(before, after, consts=[],
expected_consts=[True, False])
+ self.cfg_optimization_test(before, after, consts=[],
expected_consts=[True])
# test cancel out
before = [
@@ -1769,7 +1795,7 @@ def test_optimize_unary_not(self):
('RETURN_VALUE', None, 0),
]
after = [
- ('LOAD_CONST', 0, 0),
+ ('LOAD_COMMON_CONSTANT', 9, 0),
('RETURN_VALUE', None, 0),
]
self.cfg_optimization_test(before, after, consts=[],
expected_consts=[True])
@@ -1785,10 +1811,10 @@ def test_optimize_unary_not(self):
('RETURN_VALUE', None, 0),
]
after = [
- ('LOAD_CONST', 1, 0),
+ ('LOAD_COMMON_CONSTANT', 10, 0),
('RETURN_VALUE', None, 0),
]
- self.cfg_optimization_test(before, after, consts=[],
expected_consts=[True, False])
+ self.cfg_optimization_test(before, after, consts=[],
expected_consts=[True])
# test cancel out & eliminate to bool (to bool stays as we are not
iterating to a fixed point)
before = [
@@ -2430,7 +2456,7 @@ def test_list_to_tuple_get_iter(self):
end,
("END_FOR", None, 11),
("POP_TOP", None, 12),
- ("LOAD_CONST", 0, 13),
+ ("LOAD_COMMON_CONSTANT", 7, 13),
("RETURN_VALUE", None, 14),
]
self.cfg_optimization_test(insts, expected_insts, consts=[None])
@@ -2454,7 +2480,7 @@ def make_bb(self, insts):
maxconst = max(maxconst, arg)
consts = [None for _ in range(maxconst + 1)]
return insts + [
- ("LOAD_CONST", 0, last_loc + 1),
+ ("LOAD_COMMON_CONSTANT", 7, last_loc + 1),
("RETURN_VALUE", None, last_loc + 2),
], consts
@@ -2485,7 +2511,7 @@ def test_optimized(self):
]
expected = [
("LOAD_FAST_BORROW", 0, 1),
- ("LOAD_CONST", 1, 2),
+ ("LOAD_COMMON_CONSTANT", 7, 2),
("SWAP", 2, 3),
("POP_TOP", None, 4),
]
@@ -2523,7 +2549,13 @@ def test_unoptimized_if_support_killed(self):
("STORE_FAST", 0, 3),
("POP_TOP", None, 4),
]
- self.check(insts, insts)
+ expected = [
+ ("LOAD_FAST", 0, 1),
+ ("LOAD_COMMON_CONSTANT", 7, 2),
+ ("STORE_FAST", 0, 3),
+ ("POP_TOP", None, 4),
+ ]
+ self.check(insts, expected)
insts = [
("LOAD_FAST", 0, 1),
@@ -2532,7 +2564,14 @@ def test_unoptimized_if_support_killed(self):
("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
("POP_TOP", None, 5),
]
- self.check(insts, insts)
+ expected = [
+ ("LOAD_FAST", 0, 1),
+ ("LOAD_COMMON_CONSTANT", 7, 2),
+ ("LOAD_COMMON_CONSTANT", 7, 3),
+ ("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
+ ("POP_TOP", None, 5),
+ ]
+ self.check(insts, expected)
insts = [
("LOAD_FAST", 0, 1),
@@ -2553,7 +2592,12 @@ def test_unoptimized_if_aliased(self):
("LOAD_CONST", 0, 3),
("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
]
- self.check(insts, insts)
+ expected = [
+ ("LOAD_FAST", 0, 1),
+ ("LOAD_COMMON_CONSTANT", 7, 3),
+ ("STORE_FAST_STORE_FAST", ((0 << 4) | 1), 4),
+ ]
+ self.check(insts, expected)
def test_consume_no_inputs(self):
insts = [
@@ -2598,7 +2642,19 @@ def test_for_iter(self):
("LOAD_CONST", 0, 7),
("RETURN_VALUE", None, 8),
]
- self.cfg_optimization_test(insts, insts, consts=[None])
+ expected = [
+ ("LOAD_FAST", 0, 1),
+ top := self.Label(),
+ ("FOR_ITER", end := self.Label(), 2),
+ ("STORE_FAST", 2, 3),
+ ("JUMP", top, 4),
+ end,
+ ("END_FOR", None, 5),
+ ("POP_TOP", None, 6),
+ ("LOAD_COMMON_CONSTANT", 7, 7),
+ ("RETURN_VALUE", None, 8),
+ ]
+ self.cfg_optimization_test(insts, expected, consts=[None])
def test_load_attr(self):
insts = [
@@ -2667,10 +2723,10 @@ def test_send(self):
("LOAD_FAST", 0, 1),
("LOAD_FAST_BORROW", 1, 2),
("SEND", end := self.Label(), 3),
- ("LOAD_CONST", 0, 4),
+ ("LOAD_COMMON_CONSTANT", 7, 4),
("RETURN_VALUE", None, 5),
end,
- ("LOAD_CONST", 0, 6),
+ ("LOAD_COMMON_CONSTANT", 7, 6),
("RETURN_VALUE", None, 7)
]
self.cfg_optimization_test(insts, expected, consts=[None])
@@ -2708,7 +2764,15 @@ def test_set_function_attribute(self):
("LOAD_CONST", 0, 5),
("RETURN_VALUE", None, 6)
]
- self.cfg_optimization_test(insts, insts, consts=[None])
+ expected = [
+ ("LOAD_COMMON_CONSTANT", 7, 1),
+ ("LOAD_FAST", 0, 2),
+ ("SET_FUNCTION_ATTRIBUTE", 2, 3),
+ ("STORE_FAST", 1, 4),
+ ("LOAD_COMMON_CONSTANT", 7, 5),
+ ("RETURN_VALUE", None, 6)
+ ]
+ self.cfg_optimization_test(insts, expected, consts=[None])
insts = [
("LOAD_CONST", 0, 1),
@@ -2717,7 +2781,7 @@ def test_set_function_attribute(self):
("RETURN_VALUE", None, 4)
]
expected = [
- ("LOAD_CONST", 0, 1),
+ ("LOAD_COMMON_CONSTANT", 7, 1),
("LOAD_FAST_BORROW", 0, 2),
("SET_FUNCTION_ATTRIBUTE", 2, 3),
("RETURN_VALUE", None, 4)
@@ -2740,7 +2804,22 @@ def test_get_yield_from_iter(self):
("LOAD_CONST", 0, 11),
("RETURN_VALUE", None, 12),
]
- self.cfg_optimization_test(insts, insts, consts=[None])
+ expected = [
+ ("LOAD_FAST", 0, 1),
+ ("GET_ITER", 1, 2),
+ ("PUSH_NULL", None, 3),
+ ("LOAD_COMMON_CONSTANT", 7, 4),
+ send := self.Label(),
+ ("SEND", end := self.Label(), 6),
+ ("YIELD_VALUE", 1, 7),
+ ("RESUME", 2, 8),
+ ("JUMP", send, 9),
+ end,
+ ("END_SEND", None, 10),
+ ("LOAD_COMMON_CONSTANT", 7, 11),
+ ("RETURN_VALUE", None, 12),
+ ]
+ self.cfg_optimization_test(insts, expected, consts=[None])
def test_push_exc_info(self):
insts = [
diff --git a/Objects/codeobject.c b/Objects/codeobject.c
index 50ebe657a0eea6..8be85b1accbdca 100644
--- a/Objects/codeobject.c
+++ b/Objects/codeobject.c
@@ -2128,10 +2128,6 @@ code_returns_only_none(PyCodeObject *co)
int len = (int)Py_SIZE(co);
assert(len > 0);
- // The last instruction either returns or raises. We can take advantage
- // of that for a quick exit.
- _Py_CODEUNIT final = _Py_GetBaseCodeUnit(co, len-1);
-
// Look up None in co_consts.
Py_ssize_t nconsts = PyTuple_Size(co->co_consts);
int none_index = 0;
@@ -2140,45 +2136,25 @@ code_returns_only_none(PyCodeObject *co)
break;
}
}
- if (none_index == nconsts) {
- // None wasn't there, which means there was no implicit return,
- // "return", or "return None".
-
- // That means there must be
- // an explicit return (non-None), or it only raises.
- if (IS_RETURN_OPCODE(final.op.code)) {
- // It was an explicit return (non-None).
- return 0;
+ /* We don't worry about EXTENDED_ARG for now. */
+ for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) {
+ _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
+ if (!IS_RETURN_OPCODE(inst.op.code)) {
+ continue;
}
- // It must end with a raise then. We still have to walk the
- // bytecode to see if there's any explicit return (non-None).
- assert(IS_RAISE_OPCODE(final.op.code));
- for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) {
- _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
- if (IS_RETURN_OPCODE(inst.op.code)) {
- // We alraedy know it isn't returning None.
- return 0;
- }
+ assert(i != 0);
+ _Py_CODEUNIT prev = _Py_GetBaseCodeUnit(co, i-1);
+ if (prev.op.code == LOAD_COMMON_CONSTANT &&
+ prev.op.arg == CONSTANT_NONE)
+ {
+ continue;
}
- // It must only raise.
- }
- else {
- // Walk the bytecode, looking for RETURN_VALUE.
- for (int i = 0; i < len; i += _PyInstruction_GetLength(co, i)) {
- _Py_CODEUNIT inst = _Py_GetBaseCodeUnit(co, i);
- if (IS_RETURN_OPCODE(inst.op.code)) {
- assert(i != 0);
- // Ignore it if it returns None.
- _Py_CODEUNIT prev = _Py_GetBaseCodeUnit(co, i-1);
- if (prev.op.code == LOAD_CONST) {
- // We don't worry about EXTENDED_ARG for now.
- if (prev.op.arg == none_index) {
- continue;
- }
- }
- return 0;
- }
+ if (none_index < nconsts && prev.op.code == LOAD_CONST
+ && prev.op.arg == none_index)
+ {
+ continue;
}
+ return 0;
}
return 1;
}
diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h
index 28e08e42f5c88f..1bcf98a7600851 100644
--- a/Programs/test_frozenmain.h
+++ b/Programs/test_frozenmain.h
@@ -2,38 +2,38 @@
unsigned char M_test_frozenmain[] = {
227,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,
0,0,0,0,0,243,188,0,0,0,128,0,0,0,93,0,
- 81,1,72,0,115,0,93,0,81,1,72,4,115,1,92,2,
- 31,0,81,2,50,1,0,0,0,0,0,0,29,0,92,2,
- 31,0,81,3,92,0,79,6,0,0,0,0,0,0,0,0,
+ 80,7,72,0,115,0,93,0,80,7,72,4,115,1,92,2,
+ 31,0,81,1,50,1,0,0,0,0,0,0,29,0,92,2,
+ 31,0,81,2,92,0,79,6,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,50,2,0,0,0,0,
0,0,29,0,92,1,79,8,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,31,0,50,0,0,0,
- 0,0,0,0,81,4,42,26,0,0,0,0,0,0,0,0,
- 0,0,115,5,81,7,70,0,0,0,68,24,0,0,115,6,
- 92,2,31,0,81,5,92,6,12,0,81,6,92,5,92,6,
+ 0,0,0,0,81,3,42,26,0,0,0,0,0,0,0,0,
+ 0,0,115,5,81,6,70,0,0,0,68,24,0,0,115,6,
+ 92,2,31,0,81,4,92,6,12,0,81,5,92,5,92,6,
42,26,0,0,0,0,0,0,0,0,0,0,12,0,48,4,
50,1,0,0,0,0,0,0,29,0,74,26,0,0,9,0,
- 28,0,81,1,33,0,41,8,233,0,0,0,0,78,122,18,
- 70,114,111,122,101,110,32,72,101,108,108,111,32,87,111,114,
- 108,100,122,8,115,121,115,46,97,114,103,118,218,6,99,111,
- 110,102,105,103,122,7,99,111,110,102,105,103,32,122,2,58,
- 32,41,5,218,12,112,114,111,103,114,97,109,95,110,97,109,
- 101,218,10,101,120,101,99,117,116,97,98,108,101,218,15,117,
- 115,101,95,101,110,118,105,114,111,110,109,101,110,116,218,17,
- 99,111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,
- 111,218,14,98,117,102,102,101,114,101,100,95,115,116,100,105,
- 111,41,7,218,3,115,121,115,218,17,95,116,101,115,116,105,
- 110,116,101,114,110,97,108,99,97,112,105,218,5,112,114,105,
- 110,116,218,4,97,114,103,118,218,11,103,101,116,95,99,111,
- 110,102,105,103,115,114,3,0,0,0,218,3,107,101,121,169,
- 0,243,0,0,0,0,218,18,116,101,115,116,95,102,114,111,
- 122,101,110,109,97,105,110,46,112,121,218,8,60,109,111,100,
- 117,108,101,62,114,18,0,0,0,1,0,0,0,115,94,0,
- 0,0,241,3,1,1,1,243,8,0,1,11,219,0,24,225,
- 0,5,208,6,26,212,0,27,217,0,5,128,106,144,35,151,
- 40,145,40,212,0,27,216,9,26,215,9,38,210,9,38,211,
- 9,40,168,24,213,9,50,128,6,244,2,6,12,2,128,67,
- 241,14,0,5,10,136,71,144,67,144,53,152,2,152,54,160,
- 35,157,59,152,45,208,10,40,214,4,41,243,15,6,12,2,
- 114,16,0,0,0,
+ 28,0,80,7,33,0,41,7,233,0,0,0,0,122,18,70,
+ 114,111,122,101,110,32,72,101,108,108,111,32,87,111,114,108,
+ 100,122,8,115,121,115,46,97,114,103,118,218,6,99,111,110,
+ 102,105,103,122,7,99,111,110,102,105,103,32,122,2,58,32,
+ 41,5,218,12,112,114,111,103,114,97,109,95,110,97,109,101,
+ 218,10,101,120,101,99,117,116,97,98,108,101,218,15,117,115,
+ 101,95,101,110,118,105,114,111,110,109,101,110,116,218,17,99,
+ 111,110,102,105,103,117,114,101,95,99,95,115,116,100,105,111,
+ 218,14,98,117,102,102,101,114,101,100,95,115,116,100,105,111,
+ 41,7,218,3,115,121,115,218,17,95,116,101,115,116,105,110,
+ 116,101,114,110,97,108,99,97,112,105,218,5,112,114,105,110,
+ 116,218,4,97,114,103,118,218,11,103,101,116,95,99,111,110,
+ 102,105,103,115,114,3,0,0,0,218,3,107,101,121,169,0,
+ 243,0,0,0,0,218,18,116,101,115,116,95,102,114,111,122,
+ 101,110,109,97,105,110,46,112,121,218,8,60,109,111,100,117,
+ 108,101,62,114,18,0,0,0,1,0,0,0,115,94,0,0,
+ 0,241,3,1,1,1,243,8,0,1,11,219,0,24,225,0,
+ 5,208,6,26,212,0,27,217,0,5,128,106,144,35,151,40,
+ 145,40,212,0,27,216,9,26,215,9,38,210,9,38,211,9,
+ 40,168,24,213,9,50,128,6,244,2,6,12,2,128,67,241,
+ 14,0,5,10,136,71,144,67,144,53,152,2,152,54,160,35,
+ 157,59,152,45,208,10,40,214,4,41,243,15,6,12,2,114,
+ 16,0,0,0,
};
diff --git a/Python/flowgraph.c b/Python/flowgraph.c
index 2cb2d32a410613..224426b7aa44bc 100644
--- a/Python/flowgraph.c
+++ b/Python/flowgraph.c
@@ -10,6 +10,7 @@
#include "pycore_opcode_utils.h"
#include "pycore_opcode_metadata.h" // OPCODE_HAS_ARG, etc
+#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include <stdbool.h>
@@ -1125,6 +1126,8 @@ remove_redundant_nops(cfg_builder *g) {
return changes;
}
+static int loads_const(int opcode);
+
static int
remove_redundant_nops_and_pairs(basicblock *entryblock)
{
@@ -1148,7 +1151,7 @@ remove_redundant_nops_and_pairs(basicblock *entryblock)
int opcode = instr->i_opcode;
bool is_redundant_pair = false;
if (opcode == POP_TOP) {
- if (prev_opcode == LOAD_CONST || prev_opcode ==
LOAD_SMALL_INT) {
+ if (loads_const(prev_opcode)) {
is_redundant_pair = true;
}
else if (prev_opcode == COPY && prev_oparg == 1) {
@@ -1300,7 +1303,9 @@ jump_thread(basicblock *bb, cfg_instr *inst, cfg_instr
*target, int opcode)
static int
loads_const(int opcode)
{
- return OPCODE_HAS_CONST(opcode) || opcode == LOAD_SMALL_INT;
+ return OPCODE_HAS_CONST(opcode)
+ || opcode == LOAD_SMALL_INT
+ || opcode == LOAD_COMMON_CONSTANT;
}
/* Returns new reference */
@@ -1323,6 +1328,10 @@ get_const_value(int opcode, int oparg, PyObject
*co_consts)
if (opcode == LOAD_SMALL_INT) {
return PyLong_FromLong(oparg);
}
+ if (opcode == LOAD_COMMON_CONSTANT) {
+ assert(oparg < NUM_COMMON_CONSTANTS);
+ return Py_NewRef(_PyInterpreterState_GET()->common_consts[oparg]);
+ }
if (constant == NULL) {
PyErr_SetString(PyExc_SystemError,
@@ -1437,6 +1446,46 @@ maybe_instr_make_load_smallint(cfg_instr *instr,
PyObject *newconst,
return 0;
}
+/* Does not steal reference to "newconst".
+ Return 1 if changed instruction to LOAD_COMMON_CONSTANT.
+ Return 0 if could not change instruction to LOAD_COMMON_CONSTANT.
+ Return -1 on error.
+*/
+static int
+maybe_instr_make_load_common_const(cfg_instr *instr, PyObject *newconst)
+{
+ int oparg;
+ if (newconst == Py_None) {
+ oparg = CONSTANT_NONE;
+ }
+ else if (newconst == Py_True) {
+ oparg = CONSTANT_TRUE;
+ }
+ else if (newconst == Py_False) {
+ oparg = CONSTANT_FALSE;
+ }
+ else if (PyUnicode_CheckExact(newconst)
+ && PyUnicode_GET_LENGTH(newconst) == 0) {
+ oparg = CONSTANT_EMPTY_STR;
+ }
+ else if (PyLong_CheckExact(newconst)) {
+ int overflow;
+ long val = PyLong_AsLongAndOverflow(newconst, &overflow);
+ if (val == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ if (overflow || val != -1) {
+ return 0;
+ }
+ oparg = CONSTANT_MINUS_ONE;
+ }
+ else {
+ return 0;
+ }
+ assert(_Py_IsImmortal(newconst));
+ INSTR_SET_OP1(instr, LOAD_COMMON_CONSTANT, oparg);
+ return 1;
+}
/* Steals reference to "newconst" */
static int
@@ -1452,6 +1501,14 @@ instr_make_load_const(cfg_instr *instr, PyObject
*newconst,
if (res > 0) {
return SUCCESS;
}
+ res = maybe_instr_make_load_common_const(instr, newconst);
+ if (res < 0) {
+ Py_DECREF(newconst);
+ return ERROR;
+ }
+ if (res > 0) {
+ return SUCCESS;
+ }
int oparg = add_const(newconst, consts, const_cache, consts_index);
RETURN_IF_ERROR(oparg);
INSTR_SET_OP1(instr, LOAD_CONST, oparg);
@@ -2208,7 +2265,7 @@ basicblock_optimize_load_const(PyObject *const_cache,
basicblock *bb,
oparg = inst->i_oparg;
}
assert(!IS_ASSEMBLER_OPCODE(opcode));
- if (opcode != LOAD_CONST && opcode != LOAD_SMALL_INT) {
+ if (!loads_const(opcode)) {
continue;
}
int nextop = i+1 < bb->b_iused ? bb->b_instr[i+1].i_opcode : 0;
@@ -2308,6 +2365,17 @@ basicblock_optimize_load_const(PyObject *const_cache,
basicblock *bb,
break;
}
}
+ if (inst->i_opcode == LOAD_CONST) {
+ PyObject *constant = get_const_value(inst->i_opcode,
inst->i_oparg, consts);
+ if (constant == NULL) {
+ return ERROR;
+ }
+ int res = maybe_instr_make_load_common_const(inst, constant);
+ Py_DECREF(constant);
+ if (res < 0) {
+ return ERROR;
+ }
+ }
}
return SUCCESS;
}
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 83ecbf0ec12907..fa34fe4cbb3417 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -1,4 +1,6 @@
#include "Python.h"
+#include "pycore_long.h"
+#include "pycore_opcode_utils.h"
#include "pycore_optimizer.h"
#include "pycore_uops.h"
#include "pycore_uop_ids.h"
@@ -875,8 +877,14 @@ dummy_func(void) {
op(_LOAD_COMMON_CONSTANT, (-- value)) {
assert(oparg < NUM_COMMON_CONSTANTS);
PyObject *val = _PyInterpreterState_GET()->common_consts[oparg];
- ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val);
- value = PyJitRef_Borrow(sym_new_const(ctx, val));
+ if (_Py_IsImmortal(val)) {
+ ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val);
+ value = PyJitRef_Borrow(sym_new_const(ctx, val));
+ }
+ else {
+ ADD_OP(_LOAD_CONST_INLINE, 0, (uintptr_t)val);
+ value = sym_new_const(ctx, val);
+ }
}
op(_LOAD_SMALL_INT, (-- value)) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 39e98634a6e9c2..553bd7302026f0 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -1911,8 +1911,14 @@
JitOptRef value;
assert(oparg < NUM_COMMON_CONSTANTS);
PyObject *val = _PyInterpreterState_GET()->common_consts[oparg];
- ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val);
- value = PyJitRef_Borrow(sym_new_const(ctx, val));
+ if (_Py_IsImmortal(val)) {
+ ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val);
+ value = PyJitRef_Borrow(sym_new_const(ctx, val));
+ }
+ else {
+ ADD_OP(_LOAD_CONST_INLINE, 0, (uintptr_t)val);
+ value = sym_new_const(ctx, val);
+ }
CHECK_STACK_BOUNDS(1);
stack_pointer[0] = value;
stack_pointer += 1;
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 57ce519c3c10ef..728c0acdd4df67 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -879,13 +879,19 @@ pycore_init_builtins(PyThreadState *tstate)
interp->common_consts[CONSTANT_ASSERTIONERROR] = PyExc_AssertionError;
interp->common_consts[CONSTANT_NOTIMPLEMENTEDERROR] =
PyExc_NotImplementedError;
- interp->common_consts[CONSTANT_BUILTIN_TUPLE] = (PyObject*)&PyTuple_Type;
+ interp->common_consts[CONSTANT_BUILTIN_TUPLE] = (PyObject *)&PyTuple_Type;
interp->common_consts[CONSTANT_BUILTIN_ALL] = all;
interp->common_consts[CONSTANT_BUILTIN_ANY] = any;
- interp->common_consts[CONSTANT_BUILTIN_LIST] = (PyObject*)&PyList_Type;
- interp->common_consts[CONSTANT_BUILTIN_SET] = (PyObject*)&PySet_Type;
-
- for (int i=0; i < NUM_COMMON_CONSTANTS; i++) {
+ interp->common_consts[CONSTANT_BUILTIN_LIST] = (PyObject *)&PyList_Type;
+ interp->common_consts[CONSTANT_BUILTIN_SET] = (PyObject *)&PySet_Type;
+ interp->common_consts[CONSTANT_NONE] = Py_None;
+ interp->common_consts[CONSTANT_EMPTY_STR] =
+ Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_STR);
+ interp->common_consts[CONSTANT_TRUE] = Py_True;
+ interp->common_consts[CONSTANT_FALSE] = Py_False;
+ interp->common_consts[CONSTANT_MINUS_ONE] =
+ (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS - 1];
+ for (int i = 0; i < NUM_COMMON_CONSTANTS; i++) {
assert(interp->common_consts[i] != NULL);
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]