Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
17 changes: 16 additions & 1 deletion Include/internal/pycore_opcode_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 10 additions & 3 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]]
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_ast/test_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, '<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

Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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']

Expand Down
Loading
Loading