diff --git a/Include/internal/pycore_interp_structs.h b/Include/internal/pycore_interp_structs.h index 01adadd1485189..af2ad383063d0e 100644 --- a/Include/internal/pycore_interp_structs.h +++ b/Include/internal/pycore_interp_structs.h @@ -1002,7 +1002,6 @@ struct _is { struct ast_state ast; struct types_state types; struct callable_cache callable_cache; - PyObject *common_consts[NUM_COMMON_CONSTANTS]; bool jit; bool compiling; diff --git a/Include/internal/pycore_opcode_utils.h b/Include/internal/pycore_opcode_utils.h index 1e71784c809d2d..04005ee83498b4 100644 --- a/Include/internal/pycore_opcode_utils.h +++ b/Include/internal/pycore_opcode_utils.h @@ -75,7 +75,22 @@ 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 + +PyAPI_DATA(PyCFunctionObject) _PyBuiltin_All; +PyAPI_DATA(PyCFunctionObject) _PyBuiltin_Any; + +/* Filled once by pycore_init_builtins; every entry is immortal. */ +PyAPI_DATA(PyObject *) _PyCommonConsts[NUM_COMMON_CONSTANTS]; + +/* Non-static: used by static _PyBuiltin_All/_Any in bltinmodule.c. */ +extern PyObject *_PyCFunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); /* 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 f29f98beb2d048..c4ae134c69e5d8 100644 --- a/Lib/test/test_ast/test_ast.py +++ b/Lib/test/test_ast/test_ast.py @@ -2642,11 +2642,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, '', '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 39075fc64cf02b..5bb3e123d78460 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 fac7e9148f1502..472bdac9f0f353 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" @@ -539,7 +539,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: - 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 e0cc010f15513b..8debecaab16012 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -105,7 +105,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 @@ -120,7 +120,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(): @@ -128,7 +128,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): @@ -145,13 +145,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) @@ -173,10 +174,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: @@ -282,11 +283,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), @@ -325,7 +326,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), @@ -1491,7 +1492,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)]) @@ -1524,7 +1525,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]) @@ -1556,7 +1557,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})]) @@ -1578,7 +1579,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) @@ -1597,7 +1613,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)]) @@ -1620,7 +1636,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]) @@ -1642,7 +1658,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})]) @@ -1659,7 +1675,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 @@ -1670,10 +1696,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 = [ @@ -1721,7 +1747,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]) @@ -1737,10 +1763,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 = [ @@ -2382,7 +2408,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]) @@ -2406,7 +2432,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 @@ -2437,7 +2463,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), ] @@ -2475,7 +2501,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), @@ -2484,7 +2516,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), @@ -2505,7 +2544,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 = [ @@ -2550,7 +2594,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 = [ @@ -2619,10 +2675,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]) @@ -2660,7 +2716,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), @@ -2669,7 +2733,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) @@ -2692,7 +2756,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/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 8897854078bd45..171ff6d0ca8567 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -9298,7 +9298,7 @@ INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); _PyStackRef value; assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = load_common_constant(oparg); stack_pointer[0] = value; stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 2c3d6dc4b0feed..7e869bd0950a81 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2199,10 +2199,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; @@ -2211,45 +2207,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/Objects/methodobject.c b/Objects/methodobject.c index e6e469ca270ac9..e26fe1bc9863f0 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -25,7 +25,8 @@ static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * cfunction_vectorcall_NOARGS( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * cfunction_vectorcall_O( +/* Non-static: used by static _PyBuiltin_All/_Any in bltinmodule.c. */ +PyObject *_PyCFunction_vectorcall_O( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); static PyObject * cfunction_call( PyObject *func, PyObject *args, PyObject *kwargs); @@ -67,7 +68,7 @@ PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *c vectorcall = cfunction_vectorcall_NOARGS; break; case METH_O: - vectorcall = cfunction_vectorcall_O; + vectorcall = _PyCFunction_vectorcall_O; break; case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD; @@ -352,6 +353,12 @@ meth_hash(PyObject *self) return x; } +static int +cfunction_is_gc(PyObject *op) +{ + return !_Py_IsStaticImmortal(op); +} + PyTypeObject PyCFunction_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) @@ -387,6 +394,15 @@ PyTypeObject PyCFunction_Type = { meth_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + /* Static immortal instances have no PyGC_Head prefix. */ + cfunction_is_gc, /* tp_is_gc */ }; PyTypeObject PyCMethod_Type = { @@ -511,8 +527,8 @@ cfunction_vectorcall_NOARGS( return result; } -static PyObject * -cfunction_vectorcall_O( +PyObject * +_PyCFunction_vectorcall_O( PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) { PyThreadState *tstate = _PyThreadState_GET(); 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/bltinmodule.c b/Python/bltinmodule.c index fec64e1ff9d25f..3803988cff5d59 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -13,6 +13,7 @@ #include "pycore_long.h" // _PyLong_CompactValue #include "pycore_modsupport.h" // _PyArg_NoKwnames() #include "pycore_object.h" // _Py_AddToAllObjects() +#include "pycore_opcode_utils.h" // _PyBuiltin_All, _PyBuiltin_Any, _PyCFunction_vectorcall_O #include "pycore_pyerrors.h" // _PyErr_NoMemory() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pythonrun.h" // _Py_SourceAsString() @@ -463,6 +464,34 @@ builtin_any(PyObject *module, PyObject *iterable) Py_RETURN_FALSE; } +static PyMethodDef _PyBuiltin_All_methoddef = { + "all", (PyCFunction)builtin_all, METH_O, builtin_all__doc__, +}; + +PyCFunctionObject _PyBuiltin_All = { + .ob_base = _PyObject_HEAD_INIT(&PyCFunction_Type), + .m_ml = &_PyBuiltin_All_methoddef, + .m_self = NULL, + .m_module = (PyObject *)&_Py_ID(builtins), + .m_weakreflist = NULL, + .vectorcall = _PyCFunction_vectorcall_O, +}; + +static PyMethodDef _PyBuiltin_Any_methoddef = { + "any", (PyCFunction)builtin_any, METH_O, builtin_any__doc__, +}; + +PyCFunctionObject _PyBuiltin_Any = { + .ob_base = _PyObject_HEAD_INIT(&PyCFunction_Type), + .m_ml = &_PyBuiltin_Any_methoddef, + .m_self = NULL, + .m_module = (PyObject *)&_Py_ID(builtins), + .m_weakreflist = NULL, + .vectorcall = _PyCFunction_vectorcall_O, +}; + +PyObject *_PyCommonConsts[NUM_COMMON_CONSTANTS]; + /*[clinic input] ascii as builtin_ascii @@ -3428,8 +3457,6 @@ static PyMethodDef builtin_methods[] = { BUILTIN___IMPORT___METHODDEF BUILTIN___LAZY_IMPORT___METHODDEF BUILTIN_ABS_METHODDEF - BUILTIN_ALL_METHODDEF - BUILTIN_ANY_METHODDEF BUILTIN_ASCII_METHODDEF BUILTIN_BIN_METHODDEF {"breakpoint", _PyCFunction_CAST(builtin_breakpoint), METH_FASTCALL | METH_KEYWORDS, breakpoint_doc}, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 59db0eb399b121..6365e9ef421f3d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1884,9 +1884,8 @@ dummy_func( } inst(LOAD_COMMON_CONSTANT, ( -- value)) { - // Keep in sync with _common_constants in opcode.py assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = load_common_constant(oparg); } inst(LOAD_BUILD_CLASS, ( -- bc)) { diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index a7d63fd3b82211..63eed6bfbec401 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -628,3 +628,19 @@ gen_try_set_executing(PyGenObject *gen) (PyLongObject *)PyStackRef_AsPyObjectBorrow(left), \ (PyLongObject *)PyStackRef_AsPyObjectBorrow(right)); \ } + +static inline _PyStackRef +load_common_constant(unsigned int oparg) +{ + switch (oparg) { + case CONSTANT_NONE: + return PyStackRef_None; + case CONSTANT_TRUE: + return PyStackRef_True; + case CONSTANT_FALSE: + return PyStackRef_False; + } + assert(oparg < NUM_COMMON_CONSTANTS); + assert(_Py_IsImmortal(_PyCommonConsts[oparg])); + return PyStackRef_FromPyObjectBorrow(_PyCommonConsts[oparg]); +} diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 93d39bee1b9ff6..cdfee136003a1a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -8845,7 +8845,7 @@ _PyStackRef value; oparg = CURRENT_OPARG(); assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = load_common_constant(oparg); _tos_cache0 = value; SET_CURRENT_CACHED_VALUES(1); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); @@ -8859,7 +8859,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; oparg = CURRENT_OPARG(); assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = load_common_constant(oparg); _tos_cache1 = value; _tos_cache0 = _stack_item_0; SET_CURRENT_CACHED_VALUES(2); @@ -8875,7 +8875,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; oparg = CURRENT_OPARG(); assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = load_common_constant(oparg); _tos_cache2 = value; _tos_cache1 = _stack_item_1; _tos_cache0 = _stack_item_0; diff --git a/Python/flowgraph.c b/Python/flowgraph.c index c234fa3d8c3876..6daa00395f2038 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -1124,6 +1124,8 @@ remove_redundant_nops(cfg_builder *g) { return changes; } +static int loads_const(int opcode); + static int remove_redundant_nops_and_pairs(basicblock *entryblock) { @@ -1147,7 +1149,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) { @@ -1299,7 +1301,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 */ @@ -1314,6 +1318,36 @@ get_const_value(int opcode, int oparg, PyObject *co_consts) if (opcode == LOAD_SMALL_INT) { return PyLong_FromLong(oparg); } + if (opcode == LOAD_COMMON_CONSTANT) { + switch (oparg) { + case CONSTANT_ASSERTIONERROR: + return Py_NewRef(PyExc_AssertionError); + case CONSTANT_NOTIMPLEMENTEDERROR: + return Py_NewRef(PyExc_NotImplementedError); + case CONSTANT_BUILTIN_TUPLE: + return Py_NewRef((PyObject *)&PyTuple_Type); + case CONSTANT_BUILTIN_ALL: + return Py_NewRef((PyObject *)&_PyBuiltin_All); + case CONSTANT_BUILTIN_ANY: + return Py_NewRef((PyObject *)&_PyBuiltin_Any); + case CONSTANT_BUILTIN_LIST: + return Py_NewRef((PyObject *)&PyList_Type); + case CONSTANT_BUILTIN_SET: + return Py_NewRef((PyObject *)&PySet_Type); + case CONSTANT_NONE: + return Py_NewRef(Py_None); + case CONSTANT_EMPTY_STR: + return Py_NewRef(Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_STR)); + case CONSTANT_TRUE: + return Py_NewRef(Py_True); + case CONSTANT_FALSE: + return Py_NewRef(Py_False); + case CONSTANT_MINUS_ONE: + return PyLong_FromLong(-1); + default: + Py_UNREACHABLE(); + } + } if (constant == NULL) { PyErr_SetString(PyExc_SystemError, @@ -1420,6 +1454,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 @@ -1434,6 +1508,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); RETURN_IF_ERROR(oparg); INSTR_SET_OP1(instr, LOAD_CONST, oparg); @@ -2167,6 +2249,9 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * cfg_instr *inst = &bb->b_instr[i]; 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_smallint(inst, constant, consts, const_cache); Py_DECREF(constant); if (res < 0) { @@ -2181,7 +2266,7 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * 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; @@ -2281,6 +2366,17 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject * 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/generated_cases.c.h b/Python/generated_cases.c.h index dccee0e4a3b110..d2dad6c1f2fd15 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -9297,7 +9297,7 @@ INSTRUCTION_STATS(LOAD_COMMON_CONSTANT); _PyStackRef value; assert(oparg < NUM_COMMON_CONSTANTS); - value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]); + value = load_common_constant(oparg); stack_pointer[0] = value; stack_pointer += 1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index daebef4a04320b..03671b0ce4d6de 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" @@ -872,7 +874,44 @@ dummy_func(void) { op(_LOAD_COMMON_CONSTANT, (-- value)) { assert(oparg < NUM_COMMON_CONSTANTS); - PyObject *val = _PyInterpreterState_GET()->common_consts[oparg]; + PyObject *val; + if (oparg == CONSTANT_ASSERTIONERROR) { + val = PyExc_AssertionError; + } + else if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { + val = PyExc_NotImplementedError; + } + else if (oparg == CONSTANT_BUILTIN_TUPLE) { + val = (PyObject *)&PyTuple_Type; + } + else if (oparg == CONSTANT_BUILTIN_ALL) { + val = (PyObject *)&_PyBuiltin_All; + } + else if (oparg == CONSTANT_BUILTIN_ANY) { + val = (PyObject *)&_PyBuiltin_Any; + } + else if (oparg == CONSTANT_BUILTIN_LIST) { + val = (PyObject *)&PyList_Type; + } + else if (oparg == CONSTANT_BUILTIN_SET) { + val = (PyObject *)&PySet_Type; + } + else if (oparg == CONSTANT_NONE) { + val = Py_None; + } + else if (oparg == CONSTANT_EMPTY_STR) { + val = Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_STR); + } + else if (oparg == CONSTANT_TRUE) { + val = Py_True; + } + else if (oparg == CONSTANT_FALSE) { + val = Py_False; + } + else { + assert(oparg == CONSTANT_MINUS_ONE); + val = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS - 1]; + } ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value = PyJitRef_Borrow(sym_new_const(ctx, val)); } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index c3c889e9de9a7e..49033049ef3f82 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1914,7 +1914,44 @@ case _LOAD_COMMON_CONSTANT: { JitOptRef value; assert(oparg < NUM_COMMON_CONSTANTS); - PyObject *val = _PyInterpreterState_GET()->common_consts[oparg]; + PyObject *val; + if (oparg == CONSTANT_ASSERTIONERROR) { + val = PyExc_AssertionError; + } + else if (oparg == CONSTANT_NOTIMPLEMENTEDERROR) { + val = PyExc_NotImplementedError; + } + else if (oparg == CONSTANT_BUILTIN_TUPLE) { + val = (PyObject *)&PyTuple_Type; + } + else if (oparg == CONSTANT_BUILTIN_ALL) { + val = (PyObject *)&_PyBuiltin_All; + } + else if (oparg == CONSTANT_BUILTIN_ANY) { + val = (PyObject *)&_PyBuiltin_Any; + } + else if (oparg == CONSTANT_BUILTIN_LIST) { + val = (PyObject *)&PyList_Type; + } + else if (oparg == CONSTANT_BUILTIN_SET) { + val = (PyObject *)&PySet_Type; + } + else if (oparg == CONSTANT_NONE) { + val = Py_None; + } + else if (oparg == CONSTANT_EMPTY_STR) { + val = Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_STR); + } + else if (oparg == CONSTANT_TRUE) { + val = Py_True; + } + else if (oparg == CONSTANT_FALSE) { + val = Py_False; + } + else { + assert(oparg == CONSTANT_MINUS_ONE); + val = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS - 1]; + } ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)val); value = PyJitRef_Borrow(sym_new_const(ctx, val)); CHECK_STACK_BOUNDS(1); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 0a88e32bb6b65e..d14211d8c2dd7e 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -18,6 +18,7 @@ #include "pycore_moduleobject.h" // _PyModule_InitModuleDictWatcher() #include "pycore_object.h" // _PyDebug_PrintTotalRefs() #include "pycore_obmalloc.h" // _PyMem_init_obmalloc() +#include "pycore_opcode_utils.h" // _PyBuiltin_All, _PyBuiltin_Any #include "pycore_optimizer.h" // _Py_Executors_InvalidateAll #include "pycore_pathconfig.h" // _PyPathConfig_UpdateGlobal() #include "pycore_pyerrors.h" // _PyErr_Occurred() @@ -867,26 +868,33 @@ pycore_init_builtins(PyThreadState *tstate) } interp->callable_cache.len = len; - PyObject *all = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all)); - if (!all) { + /* Install the static all/any singletons. */ + if (PyDict_SetItem(builtins_dict, &_Py_ID(all), + (PyObject *)&_PyBuiltin_All) < 0) { goto error; } - - PyObject *any = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any)); - if (!any) { + if (PyDict_SetItem(builtins_dict, &_Py_ID(any), + (PyObject *)&_PyBuiltin_Any) < 0) { goto error; } - 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_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++) { - assert(interp->common_consts[i] != NULL); + _PyCommonConsts[CONSTANT_ASSERTIONERROR] = PyExc_AssertionError; + _PyCommonConsts[CONSTANT_NOTIMPLEMENTEDERROR] = PyExc_NotImplementedError; + _PyCommonConsts[CONSTANT_BUILTIN_TUPLE] = (PyObject *)&PyTuple_Type; + _PyCommonConsts[CONSTANT_BUILTIN_ALL] = (PyObject *)&_PyBuiltin_All; + _PyCommonConsts[CONSTANT_BUILTIN_ANY] = (PyObject *)&_PyBuiltin_Any; + _PyCommonConsts[CONSTANT_BUILTIN_LIST] = (PyObject *)&PyList_Type; + _PyCommonConsts[CONSTANT_BUILTIN_SET] = (PyObject *)&PySet_Type; + _PyCommonConsts[CONSTANT_NONE] = Py_None; + _PyCommonConsts[CONSTANT_EMPTY_STR] = + Py_GetConstantBorrowed(Py_CONSTANT_EMPTY_STR); + _PyCommonConsts[CONSTANT_TRUE] = Py_True; + _PyCommonConsts[CONSTANT_FALSE] = Py_False; + _PyCommonConsts[CONSTANT_MINUS_ONE] = + (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS - 1]; + for (int i = 0; i < NUM_COMMON_CONSTANTS; i++) { + assert(_PyCommonConsts[i] != NULL); + assert(_Py_IsImmortal(_PyCommonConsts[i])); } PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append)); diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 74ca562824012b..307a8f8338ecac 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -106,6 +106,9 @@ Objects/weakrefobject.c - _PyWeakref_RefType - Python/bltinmodule.c - PyFilter_Type - Python/bltinmodule.c - PyMap_Type - Python/bltinmodule.c - PyZip_Type - +Python/bltinmodule.c - _PyBuiltin_All - +Python/bltinmodule.c - _PyBuiltin_Any - +Python/bltinmodule.c - _PyCommonConsts - Python/context.c - PyContextToken_Type - Python/context.c - PyContextVar_Type - Python/context.c - PyContext_Type - diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 414ca18be4654c..2654b67f5bcded 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -646,6 +646,7 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "_PyFunction_SetVersion", "_PyGen_GetGeneratorFromFrame", "gen_try_set_executing", + "load_common_constant", "_PyInterpreterState_GET", "_PyList_AppendTakeRef", "_PyList_ITEMS", diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index ba52ea2a30e0be..1c3fb33d982c2b 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1810,6 +1810,7 @@ def is_other_python_frame(self): return False if (caller.startswith('cfunction_vectorcall_') or + caller.startswith('_PyCFunction_vectorcall_') or caller == 'cfunction_call'): arg_name = 'func' # Within that frame: