Skip to content

Commit fe39cfb

Browse files
committed
pythongh-148871: extend and improve LOAD_COMMON_CONSTANT
1 parent 618b726 commit fe39cfb

23 files changed

Lines changed: 500 additions & 177 deletions

Include/internal/pycore_interp_structs.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1002,7 +1002,6 @@ struct _is {
10021002
struct ast_state ast;
10031003
struct types_state types;
10041004
struct callable_cache callable_cache;
1005-
PyObject *common_consts[NUM_COMMON_CONSTANTS];
10061005
bool jit;
10071006
bool compiling;
10081007

Include/internal/pycore_opcode_utils.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,22 @@ extern "C" {
7575
#define CONSTANT_BUILTIN_ANY 4
7676
#define CONSTANT_BUILTIN_LIST 5
7777
#define CONSTANT_BUILTIN_SET 6
78-
#define NUM_COMMON_CONSTANTS 7
78+
#define CONSTANT_NONE 7
79+
#define CONSTANT_EMPTY_STR 8
80+
#define CONSTANT_TRUE 9
81+
#define CONSTANT_FALSE 10
82+
#define CONSTANT_MINUS_ONE 11
83+
#define NUM_COMMON_CONSTANTS 12
84+
85+
extern PyCFunctionObject _PyBuiltin_All;
86+
extern PyCFunctionObject _PyBuiltin_Any;
87+
88+
/* Filled once by pycore_init_builtins; every entry is immortal. */
89+
extern PyObject *_PyCommonConsts[NUM_COMMON_CONSTANTS];
90+
91+
/* Non-static: used by static _PyBuiltin_All/_Any in bltinmodule.c. */
92+
extern PyObject *_PyCFunction_vectorcall_O(
93+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames);
7994

8095
/* Values used in the oparg for RESUME */
8196
#define RESUME_AT_FUNC_START 0

Lib/dis.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ def get_argval_argrepr(self, op, arg, offset):
643643
argrepr = _intrinsic_2_descs[arg]
644644
elif deop == LOAD_COMMON_CONSTANT:
645645
obj = _common_constants[arg]
646+
argval = obj
646647
if isinstance(obj, type):
647648
argrepr = obj.__name__
648649
else:
@@ -692,10 +693,15 @@ def _get_const_value(op, arg, co_consts):
692693
Otherwise (if it is a LOAD_CONST and co_consts is not
693694
provided) returns the dis.UNKNOWN sentinel.
694695
"""
695-
assert op in hasconst or op == LOAD_SMALL_INT
696+
assert op in hasconst or op == LOAD_SMALL_INT or op == LOAD_COMMON_CONSTANT
696697

697698
if op == LOAD_SMALL_INT:
698699
return arg
700+
if op == LOAD_COMMON_CONSTANT:
701+
# Opargs 0-6 are callables; 7-11 are literal values.
702+
if 7 <= arg <= 11:
703+
return _common_constants[arg]
704+
return UNKNOWN
699705
argval = UNKNOWN
700706
if co_consts is not None:
701707
argval = co_consts[arg]
@@ -1015,8 +1021,9 @@ def _find_imports(co):
10151021
if op == IMPORT_NAME and i >= 2:
10161022
from_op = opargs[i-1]
10171023
level_op = opargs[i-2]
1018-
if (from_op[0] in hasconst and
1019-
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT)):
1024+
if ((from_op[0] in hasconst or from_op[0] == LOAD_COMMON_CONSTANT) and
1025+
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT or
1026+
level_op[0] == LOAD_COMMON_CONSTANT)):
10201027
level = _get_const_value(level_op[0], level_op[1], consts)
10211028
fromlist = _get_const_value(from_op[0], from_op[1], consts)
10221029
# IMPORT_NAME encodes lazy/eager flags in bits 0-1,

Lib/opcode.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
_special_method_names = _opcode.get_special_method_names()
4242
_common_constants = [builtins.AssertionError, builtins.NotImplementedError,
4343
builtins.tuple, builtins.all, builtins.any, builtins.list,
44-
builtins.set]
44+
builtins.set,
45+
# Append-only — must match CONSTANT_* in
46+
# Include/internal/pycore_opcode_utils.h.
47+
None, "", True, False, -1]
4548
_nb_ops = _opcode.get_nb_ops()
4649

4750
hascompare = [opmap["COMPARE_OP"]]

Lib/test/test_ast/test_ast.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2642,11 +2642,12 @@ def test_get_docstring(self):
26422642

26432643
def get_load_const(self, tree):
26442644
# Compile to bytecode, disassemble and get parameter of LOAD_CONST
2645-
# instructions
2645+
# and LOAD_COMMON_CONSTANT instructions
26462646
co = compile(tree, '<string>', 'exec')
26472647
consts = []
26482648
for instr in dis.get_instructions(co):
2649-
if instr.opcode in dis.hasconst:
2649+
if instr.opcode in dis.hasconst or \
2650+
instr.opname == 'LOAD_COMMON_CONSTANT':
26502651
consts.append(instr.argval)
26512652
return consts
26522653

Lib/test/test_capi/test_opt.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,6 +3446,27 @@ def testfunc(n):
34463446
self.assertIn("_BUILD_LIST", uops)
34473447
self.assertNotIn("_LOAD_COMMON_CONSTANT", uops)
34483448

3449+
def test_load_common_constant_new_literals(self):
3450+
def testfunc(n):
3451+
x = None
3452+
s = ""
3453+
t = True
3454+
f = False
3455+
m = -1
3456+
for _ in range(n):
3457+
x = None
3458+
s = ""
3459+
t = True
3460+
f = False
3461+
m = -1
3462+
return x, s, t, f, m
3463+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
3464+
self.assertEqual(res, (None, "", True, False, -1))
3465+
self.assertIsNotNone(ex)
3466+
uops = get_opnames(ex)
3467+
self.assertNotIn("_LOAD_COMMON_CONSTANT", uops)
3468+
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
3469+
34493470
def test_load_small_int(self):
34503471
def testfunc(n):
34513472
x = 0

Lib/test/test_code.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
freevars: ()
8888
nlocals: 0
8989
flags: 67108867
90-
consts: ("'doc string'", 'None')
90+
consts: ("'doc string'",)
9191
9292
>>> def keywordonly_args(a,b,*,k1):
9393
... return a,b,k1
@@ -161,7 +161,7 @@
161161
freevars: ()
162162
nlocals: 3
163163
flags: 67108995
164-
consts: ("'This is a docstring from async function'", 'None')
164+
consts: ("'This is a docstring from async function'",)
165165
166166
>>> def no_docstring(x, y, z):
167167
... return x + "hello" + y + z + "world"
@@ -539,7 +539,7 @@ def test_co_positions_artificial_instructions(self):
539539
],
540540
[
541541
("PUSH_EXC_INFO", None),
542-
("LOAD_CONST", None), # artificial 'None'
542+
("LOAD_COMMON_CONSTANT", None), # artificial 'None'
543543
("STORE_NAME", "e"), # XX: we know the location for this
544544
("DELETE_NAME", "e"),
545545
("RERAISE", 1),

Lib/test/test_compile.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2485,12 +2485,13 @@ def f():
24852485
start_line, end_line, _, _ = instr.positions
24862486
self.assertEqual(start_line, end_line)
24872487

2488-
# Expect four `LOAD_CONST None` instructions:
2488+
# Expect four `None`-loading instructions:
24892489
# three for the no-exception __exit__ call, and one for the return.
24902490
# They should all have the locations of the context manager ('xyz').
24912491

24922492
load_none = [instr for instr in dis.get_instructions(f) if
2493-
instr.opname == 'LOAD_CONST' and instr.argval is None]
2493+
instr.opname in ('LOAD_CONST', 'LOAD_COMMON_CONSTANT')
2494+
and instr.argval is None]
24942495
return_value = [instr for instr in dis.get_instructions(f) if
24952496
instr.opname == 'RETURN_VALUE']
24962497

0 commit comments

Comments
 (0)