bpo-43693: Eliminate unused "fast locals". (gh-26587)

Currently, if an arg value escapes (into the closure for an inner function) we end up allocating two indices in the fast locals even though only one gets used.  Additionally, using the lower index would be better in some cases, such as with no-arg `super()`.  To address this, we update the compiler to fix the offsets so each variable only gets one "fast local".  As a consequence, now some cell offsets are interspersed with the locals (only when an arg escapes to an inner function).

https://bugs.python.org/issue43693
This commit is contained in:
Eric Snow 2021-06-15 16:35:25 -06:00 committed by GitHub
parent 1d10bf0bb9
commit ac38a9f2df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 4189 additions and 4214 deletions

View File

@ -83,11 +83,11 @@ struct PyCodeObject {
/* These fields are set with computed values on new code objects. */ /* These fields are set with computed values on new code objects. */
int *co_cell2arg; /* Maps cell vars which are arguments. */
// redundant values (derived from co_localsplusnames and co_localspluskinds) // redundant values (derived from co_localsplusnames and co_localspluskinds)
int co_nlocalsplus; /* number of local + cell + free variables */ int co_nlocalsplus; /* number of local + cell + free variables */
int co_nlocals; /* number of local variables */ int co_nlocals; /* number of local variables */
int co_ncellvars; /* number of cell variables */ int co_nplaincellvars; /* number of non-arg cell variables */
int co_ncellvars; /* total number of cell variables */
int co_nfreevars; /* number of free variables */ int co_nfreevars; /* number of free variables */
// lazily-computed values // lazily-computed values
PyObject *co_varnames; /* tuple of strings (local variable names) */ PyObject *co_varnames; /* tuple of strings (local variable names) */
@ -142,10 +142,6 @@ struct PyCodeObject {
#define CO_FUTURE_GENERATOR_STOP 0x800000 #define CO_FUTURE_GENERATOR_STOP 0x800000
#define CO_FUTURE_ANNOTATIONS 0x1000000 #define CO_FUTURE_ANNOTATIONS 0x1000000
/* This value is found in the co_cell2arg array when the associated cell
variable does not correspond to an argument. */
#define CO_CELL_NOT_AN_ARG (-1)
/* This should be defined if a future statement modifies the syntax. /* This should be defined if a future statement modifies the syntax.
For example, when a keyword is added. For example, when a keyword is added.
*/ */

View File

@ -32,8 +32,6 @@ _PyFrame_GetBuiltins(PyFrameObject *f)
int _PyFrame_TakeLocals(PyFrameObject *f); int _PyFrame_TakeLocals(PyFrameObject *f);
PyAPI_FUNC(int) _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -358,6 +358,7 @@ _code_type = type(_write_atomic.__code__)
# Python 3.11a1 3453 (add co_fastlocalnames and co_fastlocalkinds) # Python 3.11a1 3453 (add co_fastlocalnames and co_fastlocalkinds)
# Python 3.11a1 3454 (compute cell offsets relative to locals bpo-43693) # Python 3.11a1 3454 (compute cell offsets relative to locals bpo-43693)
# Python 3.11a1 3455 (add MAKE_CELL bpo-43693) # Python 3.11a1 3455 (add MAKE_CELL bpo-43693)
# Python 3.11a1 3456 (interleave cell args bpo-43693)
# #
# MAGIC must change whenever the bytecode emitted by the compiler may no # MAGIC must change whenever the bytecode emitted by the compiler may no
@ -367,7 +368,7 @@ _code_type = type(_write_atomic.__code__)
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated. # in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3455).to_bytes(2, 'little') + b'\r\n' MAGIC_NUMBER = (3456).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
_PYCACHE = '__pycache__' _PYCACHE = '__pycache__'

View File

@ -427,9 +427,9 @@ def _h(y):
return foo return foo
dis_nested_0 = """\ dis_nested_0 = """\
0 MAKE_CELL 2 (y) 0 MAKE_CELL 0 (y)
%3d 2 LOAD_CLOSURE 2 (y) %3d 2 LOAD_CLOSURE 0 (y)
4 BUILD_TUPLE 1 4 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>) 6 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
8 LOAD_CONST 2 ('_h.<locals>.foo') 8 LOAD_CONST 2 ('_h.<locals>.foo')
@ -446,14 +446,14 @@ dis_nested_0 = """\
dis_nested_1 = """%s dis_nested_1 = """%s
Disassembly of <code object foo at 0x..., file "%s", line %d>: Disassembly of <code object foo at 0x..., file "%s", line %d>:
0 MAKE_CELL 1 (x) 0 MAKE_CELL 0 (x)
%3d 2 LOAD_CLOSURE 1 (x) %3d 2 LOAD_CLOSURE 0 (x)
4 BUILD_TUPLE 1 4 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object <listcomp> at 0x..., file "%s", line %d>) 6 LOAD_CONST 1 (<code object <listcomp> at 0x..., file "%s", line %d>)
8 LOAD_CONST 2 ('_h.<locals>.foo.<locals>.<listcomp>') 8 LOAD_CONST 2 ('_h.<locals>.foo.<locals>.<listcomp>')
10 MAKE_FUNCTION 8 (closure) 10 MAKE_FUNCTION 8 (closure)
12 LOAD_DEREF 2 (y) 12 LOAD_DEREF 1 (y)
14 GET_ITER 14 GET_ITER
16 CALL_FUNCTION 1 16 CALL_FUNCTION 1
18 RETURN_VALUE 18 RETURN_VALUE
@ -966,19 +966,19 @@ expected_jumpy_line = 1
Instruction = dis.Instruction Instruction = dis.Instruction
expected_opinfo_outer = [ expected_opinfo_outer = [
Instruction(opname='MAKE_CELL', opcode=135, arg=3, argval='a', argrepr='a', offset=0, starts_line=None, is_jump_target=False), Instruction(opname='MAKE_CELL', opcode=135, arg=0, argval='a', argrepr='a', offset=0, starts_line=None, is_jump_target=False),
Instruction(opname='MAKE_CELL', opcode=135, arg=4, argval='b', argrepr='b', offset=2, starts_line=None, is_jump_target=False), Instruction(opname='MAKE_CELL', opcode=135, arg=1, argval='b', argrepr='b', offset=2, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=4, starts_line=2, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=(3, 4), argrepr='(3, 4)', offset=4, starts_line=2, is_jump_target=False),
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CLOSURE', opcode=136, arg=0, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CLOSURE', opcode=136, arg=1, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False),
Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=10, starts_line=None, is_jump_target=False), Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=10, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=12, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_f, argrepr=repr(code_object_f), offset=12, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=14, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f', argrepr="'outer.<locals>.f'", offset=14, starts_line=None, is_jump_target=False),
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=16, starts_line=None, is_jump_target=False), Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=16, starts_line=None, is_jump_target=False),
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=18, starts_line=None, is_jump_target=False), Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=18, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=7, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=20, starts_line=7, is_jump_target=False),
Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=137, arg=0, argval='a', argrepr='a', offset=22, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=137, arg=1, argval='b', argrepr='b', offset=24, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=26, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval='', argrepr="''", offset=26, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=28, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval=1, argrepr='1', offset=28, starts_line=None, is_jump_target=False),
Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=30, starts_line=None, is_jump_target=False), Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=30, starts_line=None, is_jump_target=False),
@ -991,23 +991,23 @@ expected_opinfo_outer = [
] ]
expected_opinfo_f = [ expected_opinfo_f = [
Instruction(opname='MAKE_CELL', opcode=135, arg=3, argval='c', argrepr='c', offset=0, starts_line=None, is_jump_target=False), Instruction(opname='MAKE_CELL', opcode=135, arg=0, argval='c', argrepr='c', offset=0, starts_line=None, is_jump_target=False),
Instruction(opname='MAKE_CELL', opcode=135, arg=4, argval='d', argrepr='d', offset=2, starts_line=None, is_jump_target=False), Instruction(opname='MAKE_CELL', opcode=135, arg=1, argval='d', argrepr='d', offset=2, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=4, starts_line=3, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(5, 6), argrepr='(5, 6)', offset=4, starts_line=3, is_jump_target=False),
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=5, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='a', argrepr='a', offset=6, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=6, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='b', argrepr='b', offset=8, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='c', argrepr='c', offset=10, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CLOSURE', opcode=136, arg=0, argval='c', argrepr='c', offset=10, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='d', argrepr='d', offset=12, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CLOSURE', opcode=136, arg=1, argval='d', argrepr='d', offset=12, starts_line=None, is_jump_target=False),
Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=14, starts_line=None, is_jump_target=False), Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=14, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=16, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=code_object_inner, argrepr=repr(code_object_inner), offset=16, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=18, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='outer.<locals>.f.<locals>.inner', argrepr="'outer.<locals>.f.<locals>.inner'", offset=18, starts_line=None, is_jump_target=False),
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=20, starts_line=None, is_jump_target=False), Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=20, starts_line=None, is_jump_target=False),
Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=22, starts_line=None, is_jump_target=False), Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=22, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=24, starts_line=5, is_jump_target=False), Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=24, starts_line=5, is_jump_target=False),
Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='a', argrepr='a', offset=26, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='a', argrepr='a', offset=26, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_DEREF', opcode=137, arg=6, argval='b', argrepr='b', offset=28, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='b', argrepr='b', offset=28, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='c', argrepr='c', offset=30, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=137, arg=0, argval='c', argrepr='c', offset=30, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='d', argrepr='d', offset=32, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_DEREF', opcode=137, arg=1, argval='d', argrepr='d', offset=32, starts_line=None, is_jump_target=False),
Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=34, starts_line=None, is_jump_target=False), Instruction(opname='CALL_FUNCTION', opcode=131, arg=4, argval=4, argrepr='', offset=34, starts_line=None, is_jump_target=False),
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=36, starts_line=None, is_jump_target=False),
Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=38, starts_line=6, is_jump_target=False), Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=38, starts_line=6, is_jump_target=False),

View File

@ -0,0 +1,4 @@
Computation of the offsets of cell variables is done in the compiler instead
of at runtime. This reduces the overhead of handling cell and free
variables, especially in the case where a variable is both an argument and
cell variable.

View File

@ -162,43 +162,28 @@ _Py_set_localsplus_info(int offset, PyObject *name, _PyLocalsPlusKind kind,
Py_INCREF(name); Py_INCREF(name);
PyTuple_SET_ITEM(names, offset, name); PyTuple_SET_ITEM(names, offset, name);
kinds[offset] = kind; kinds[offset] = kind;
if (kind == CO_FAST_CELL) {
// Cells can overlap with args, so mark those cases.
int nlocalsplus = (int)PyTuple_GET_SIZE(names);
for (int i = 0; i < nlocalsplus; i++) {
_PyLocalsPlusKind kind = kinds[i];
if (kind && !(kind & CO_FAST_LOCAL)) {
// We've moved past the locals.
break;
}
PyObject *varname = PyTuple_GET_ITEM(names, i);
int cmp = PyUnicode_Compare(name, varname);
if (cmp == 0) {
kinds[i] |= CO_FAST_CELL;
break;
}
assert(cmp > 0 || !PyErr_Occurred());
}
}
} }
static void static void
get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds, get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
int *pnlocals, int *pncellvars, int *pnlocals, int *pnplaincellvars, int *pncellvars,
int *pnfreevars) int *pnfreevars)
{ {
int nlocals = 0; int nlocals = 0;
int nplaincellvars = 0;
int ncellvars = 0; int ncellvars = 0;
int nfreevars = 0; int nfreevars = 0;
int nlocalsplus = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(names), Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(names);
Py_ssize_t, int);
for (int i = 0; i < nlocalsplus; i++) { for (int i = 0; i < nlocalsplus; i++) {
if (kinds[i] & CO_FAST_LOCAL) { if (kinds[i] & CO_FAST_LOCAL) {
nlocals += 1; nlocals += 1;
if (kinds[i] & CO_FAST_CELL) {
ncellvars += 1;
}
} }
else if (kinds[i] & CO_FAST_CELL) { else if (kinds[i] & CO_FAST_CELL) {
ncellvars += 1; ncellvars += 1;
nplaincellvars += 1;
} }
else if (kinds[i] & CO_FAST_FREE) { else if (kinds[i] & CO_FAST_FREE) {
nfreevars += 1; nfreevars += 1;
@ -207,6 +192,9 @@ get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
if (pnlocals != NULL) { if (pnlocals != NULL) {
*pnlocals = nlocals; *pnlocals = nlocals;
} }
if (pnplaincellvars != NULL) {
*pnplaincellvars = nplaincellvars;
}
if (pncellvars != NULL) { if (pncellvars != NULL) {
*pncellvars = ncellvars; *pncellvars = ncellvars;
} }
@ -227,10 +215,6 @@ get_localsplus_names(PyCodeObject *co, _PyLocalsPlusKind kind, int num)
if ((co->co_localspluskinds[offset] & kind) == 0) { if ((co->co_localspluskinds[offset] & kind) == 0) {
continue; continue;
} }
// For now there may be duplicates, which we ignore.
if (kind == CO_FAST_CELL && co->co_localspluskinds[offset] != kind) {
continue;
}
assert(index < num); assert(index < num);
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, offset); PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, offset);
Py_INCREF(name); Py_INCREF(name);
@ -283,7 +267,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con)
* here to avoid the possibility of overflow (however remote). */ * here to avoid the possibility of overflow (however remote). */
int nlocals; int nlocals;
get_localsplus_counts(con->localsplusnames, con->localspluskinds, get_localsplus_counts(con->localsplusnames, con->localspluskinds,
&nlocals, NULL, NULL); &nlocals, NULL, NULL, NULL);
int nplainlocals = nlocals - int nplainlocals = nlocals -
con->argcount - con->argcount -
con->kwonlyargcount - con->kwonlyargcount -
@ -301,9 +285,9 @@ static void
init_code(PyCodeObject *co, struct _PyCodeConstructor *con) init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
{ {
int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames); int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames);
int nlocals, ncellvars, nfreevars; int nlocals, nplaincellvars, ncellvars, nfreevars;
get_localsplus_counts(con->localsplusnames, con->localspluskinds, get_localsplus_counts(con->localsplusnames, con->localspluskinds,
&nlocals, &ncellvars, &nfreevars); &nlocals, &nplaincellvars, &ncellvars, &nfreevars);
Py_INCREF(con->filename); Py_INCREF(con->filename);
co->co_filename = con->filename; co->co_filename = con->filename;
@ -338,9 +322,9 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->co_exceptiontable = con->exceptiontable; co->co_exceptiontable = con->exceptiontable;
/* derived values */ /* derived values */
co->co_cell2arg = NULL; // This will be set soon.
co->co_nlocalsplus = nlocalsplus; co->co_nlocalsplus = nlocalsplus;
co->co_nlocals = nlocals; co->co_nlocals = nlocals;
co->co_nplaincellvars = nplaincellvars;
co->co_ncellvars = ncellvars; co->co_ncellvars = ncellvars;
co->co_nfreevars = nfreevars; co->co_nfreevars = nfreevars;
co->co_varnames = NULL; co->co_varnames = NULL;
@ -392,44 +376,6 @@ _PyCode_New(struct _PyCodeConstructor *con)
co->co_flags &= ~CO_NOFREE; co->co_flags &= ~CO_NOFREE;
} }
/* Create mapping between cells and arguments if needed. */
if (co->co_ncellvars) {
int totalargs = co->co_argcount +
co->co_kwonlyargcount +
((co->co_flags & CO_VARARGS) != 0) +
((co->co_flags & CO_VARKEYWORDS) != 0);
assert(totalargs <= co->co_nlocals);
/* Find cells which are also arguments. */
for (int i = 0; i < co->co_ncellvars; i++) {
PyObject *cellname = PyTuple_GET_ITEM(co->co_localsplusnames,
i + co->co_nlocals);
for (int j = 0; j < totalargs; j++) {
PyObject *argname = PyTuple_GET_ITEM(co->co_localsplusnames, j);
int cmp = PyUnicode_Compare(cellname, argname);
if (cmp == -1 && PyErr_Occurred()) {
Py_DECREF(co);
return NULL;
}
if (cmp == 0) {
if (co->co_cell2arg == NULL) {
co->co_cell2arg = PyMem_NEW(int, co->co_ncellvars);
if (co->co_cell2arg == NULL) {
Py_DECREF(co);
PyErr_NoMemory();
return NULL;
}
for (int k = 0; k < co->co_ncellvars; k++) {
co->co_cell2arg[k] = CO_CELL_NOT_AN_ARG;
}
}
co->co_cell2arg[i] = j;
// Go to the next cell name.
break;
}
}
}
}
return co; return co;
} }
@ -478,6 +424,23 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
} }
for (int i = 0; i < ncellvars; i++, offset++) { for (int i = 0; i < ncellvars; i++, offset++) {
PyObject *name = PyTuple_GET_ITEM(cellvars, i); PyObject *name = PyTuple_GET_ITEM(cellvars, i);
int argoffset = -1;
for (int j = 0; j < nvarnames; j++) {
int cmp = PyUnicode_Compare(PyTuple_GET_ITEM(varnames, j),
name);
assert(!PyErr_Occurred());
if (cmp == 0) {
argoffset = j;
break;
}
}
if (argoffset >= 0) {
// Merge the localsplus indices.
nlocalsplus -= 1;
offset -= 1;
localspluskinds[argoffset] |= CO_FAST_CELL;
continue;
}
_Py_set_localsplus_info(offset, name, CO_FAST_CELL, _Py_set_localsplus_info(offset, name, CO_FAST_CELL,
localsplusnames, localspluskinds); localsplusnames, localspluskinds);
} }
@ -486,6 +449,11 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
_Py_set_localsplus_info(offset, name, CO_FAST_FREE, _Py_set_localsplus_info(offset, name, CO_FAST_FREE,
localsplusnames, localspluskinds); localsplusnames, localspluskinds);
} }
// If any cells were args then nlocalsplus will have shrunk.
// We don't bother resizing localspluskinds.
if (_PyTuple_Resize(&localsplusnames, nlocalsplus) < 0) {
goto error;
}
struct _PyCodeConstructor con = { struct _PyCodeConstructor con = {
.filename = filename, .filename = filename,
@ -1182,8 +1150,6 @@ code_dealloc(PyCodeObject *co)
Py_XDECREF(co->co_name); Py_XDECREF(co->co_name);
Py_XDECREF(co->co_linetable); Py_XDECREF(co->co_linetable);
Py_XDECREF(co->co_exceptiontable); Py_XDECREF(co->co_exceptiontable);
if (co->co_cell2arg != NULL)
PyMem_Free(co->co_cell2arg);
if (co->co_weakreflist != NULL) if (co->co_weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject*)co); PyObject_ClearWeakRefs((PyObject*)co);
if (co->co_quickened) { if (co->co_quickened) {
@ -1377,10 +1343,6 @@ code_sizeof(PyCodeObject *co, PyObject *Py_UNUSED(args))
(co_extra->ce_size-1) * sizeof(co_extra->ce_extras[0]); (co_extra->ce_size-1) * sizeof(co_extra->ce_extras[0]);
} }
if (co->co_cell2arg != NULL && co->co_cellvars != NULL) {
res += co->co_ncellvars * sizeof(Py_ssize_t);
}
if (co->co_quickened != NULL) { if (co->co_quickened != NULL) {
Py_ssize_t count = co->co_quickened[0].entry.zero.cache_count; Py_ssize_t count = co->co_quickened[0].entry.zero.cache_count;
count += (PyBytes_GET_SIZE(co->co_code)+sizeof(SpecializedCacheEntry)-1)/ count += (PyBytes_GET_SIZE(co->co_code)+sizeof(SpecializedCacheEntry)-1)/

View File

@ -918,7 +918,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
return f; return f;
} }
int static int
_PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg) _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg)
{ {
const _Py_CODEUNIT *code = const _Py_CODEUNIT *code =
@ -966,26 +966,9 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
continue; continue;
} }
/* Some args are also cells. For now each of those variables
has two indices in the fast array, with both marked as cells
but only one marked as an arg. That one is always set
to NULL in _PyEval_MakeFrameVector() and the other index
gets the cell holding the arg value. So we ignore the
former here and will later use the cell for the variable.
*/
if (kind & CO_FAST_LOCAL && kind & CO_FAST_CELL) {
continue;
}
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
PyObject *value = fast[i]; PyObject *value = fast[i];
if (f->f_state != FRAME_CLEARED) { if (f->f_state != FRAME_CLEARED) {
int cellargoffset = CO_CELL_NOT_AN_ARG;
if (kind & CO_FAST_CELL && co->co_cell2arg != NULL) {
assert(i - co->co_nlocals >= 0);
assert(i - co->co_nlocals < co->co_ncellvars);
cellargoffset = co->co_cell2arg[i - co->co_nlocals];
}
if (kind & CO_FAST_FREE) { if (kind & CO_FAST_FREE) {
// The cell was set by _PyEval_MakeFrameVector() from // The cell was set by _PyEval_MakeFrameVector() from
// the function's closure. // the function's closure.
@ -1003,20 +986,10 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
// (likely) MAKE_CELL must have executed already. // (likely) MAKE_CELL must have executed already.
value = PyCell_GET(value); value = PyCell_GET(value);
} }
// (unlikely) Otherwise it must be an initial value set // (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL),
// by an earlier call to PyFrame_FastToLocals(). // with the initial value set by _PyEval_MakeFrameVector()...
} // (unlikely) ...or it was set to some initial value by
else { // an earlier call to PyFrame_LocalsToFast().
// (unlikely) MAKE_CELL hasn't executed yet.
if (cellargoffset != CO_CELL_NOT_AN_ARG) {
// It is an arg that escapes into an inner
// function so we use the initial value that
// was already set by _PyEval_MakeFrameVector().
// Normally the arg value would always be set.
// However, it can be NULL if it was deleted via
// PyFrame_LocalsToFast().
value = fast[cellargoffset];
}
} }
} }
} }
@ -1079,10 +1052,6 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) { if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {
continue; continue;
} }
/* Same test as in PyFrame_FastToLocals() above. */
if (kind & CO_FAST_LOCAL && kind & CO_FAST_CELL) {
continue;
}
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
PyObject *value = PyObject_GetItem(locals, name); PyObject *value = PyObject_GetItem(locals, name);
/* We only care about NULLs if clear is true. */ /* We only care about NULLs if clear is true. */
@ -1093,12 +1062,6 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
} }
} }
PyObject *oldvalue = fast[i]; PyObject *oldvalue = fast[i];
int cellargoffset = CO_CELL_NOT_AN_ARG;
if (kind & CO_FAST_CELL && co->co_cell2arg != NULL) {
assert(i - co->co_nlocals >= 0);
assert(i - co->co_nlocals < co->co_ncellvars);
cellargoffset = co->co_cell2arg[i - co->co_nlocals];
}
PyObject *cell = NULL; PyObject *cell = NULL;
if (kind == CO_FAST_FREE) { if (kind == CO_FAST_FREE) {
// The cell was set by _PyEval_MakeFrameVector() from // The cell was set by _PyEval_MakeFrameVector() from
@ -1107,21 +1070,14 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
cell = oldvalue; cell = oldvalue;
} }
else if (kind & CO_FAST_CELL && oldvalue != NULL) { else if (kind & CO_FAST_CELL && oldvalue != NULL) {
if (cellargoffset != CO_CELL_NOT_AN_ARG) { /* Same test as in PyFrame_FastToLocals() above. */
if (PyCell_Check(oldvalue) &&
_PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) {
// (likely) MAKE_CELL must have executed already. // (likely) MAKE_CELL must have executed already.
// It's the cell for an arg.
assert(PyCell_Check(oldvalue));
cell = oldvalue; cell = oldvalue;
} }
else { // (unlikely) Otherwise, it must have been set to some
if (PyCell_Check(oldvalue) && // initial value by an earlier call to PyFrame_LocalsToFast().
_PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) {
// (likely) MAKE_CELL must have executed already.
cell = oldvalue;
}
// (unlikely) Otherwise, it must have been set to some
// initial value by an earlier call to PyFrame_LocalsToFast().
}
} }
if (cell != NULL) { if (cell != NULL) {
oldvalue = PyCell_GET(cell); oldvalue = PyCell_GET(cell);
@ -1131,30 +1087,9 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
PyCell_SET(cell, value); PyCell_SET(cell, value);
} }
} }
else { else if (value != oldvalue) {
int offset = i; Py_XINCREF(value);
if (kind & CO_FAST_CELL) { Py_XSETREF(fast[i], value);
// (unlikely) MAKE_CELL hasn't executed yet.
// Note that there is no need to create the cell that
// MAKE_CELL would otherwise create later, since no
// *_DEREF ops can happen before MAKE_CELL has run.
if (cellargoffset != CO_CELL_NOT_AN_ARG) {
// It's the cell for an arg.
// Replace the initial value that was set by
// _PyEval_MakeFrameVector().
// Normally the arg value would always be set.
// However, it can be NULL if it was deleted
// via an earlier PyFrame_LocalsToFast() call.
offset = cellargoffset;
oldvalue = fast[offset];
}
// Otherwise set an initial value for MAKE_CELL to use
// when it runs later.
}
if (value != oldvalue) {
Py_XINCREF(value);
Py_XSETREF(fast[offset], value);
}
} }
Py_XDECREF(value); Py_XDECREF(value);
} }

View File

@ -11,7 +11,6 @@
#include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_unionobject.h" // _Py_Union(), _Py_union_type_or #include "pycore_unionobject.h" // _Py_Union(), _Py_union_type_or
#include "frameobject.h" #include "frameobject.h"
#include "pycore_frame.h" // _PyFrame_OpAlreadyRan
#include "opcode.h" // MAKE_CELL #include "opcode.h" // MAKE_CELL
#include "structmember.h" // PyMemberDef #include "structmember.h" // PyMemberDef
@ -8878,23 +8877,18 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
return -1; return -1;
} }
PyObject *obj = f->f_localsptr[0]; PyObject *firstarg = f->f_localsptr[0];
int i; // The first argument might be a cell.
if (obj == NULL && co->co_cell2arg) { if (firstarg != NULL && (co->co_localspluskinds[0] & CO_FAST_CELL)) {
/* The first argument might be a cell. */ // "firstarg" is a cell here unless (very unlikely) super()
for (i = 0; i < co->co_ncellvars; i++) { // was called from the C-API before the first MAKE_CELL op.
if (co->co_cell2arg[i] == 0) { if (f->f_lasti >= 0) {
int celloffset = co->co_nlocals + i; assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL);
PyObject *cell = f->f_localsptr[celloffset]; assert(PyCell_Check(firstarg));
if (PyCell_Check(cell) && firstarg = PyCell_GET(firstarg);
_PyFrame_OpAlreadyRan(f, MAKE_CELL, celloffset)) {
obj = PyCell_GET(cell);
}
break;
}
} }
} }
if (obj == NULL) { if (firstarg == NULL) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"super(): arg[0] deleted"); "super(): arg[0] deleted");
return -1; return -1;
@ -8902,9 +8896,9 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
// Look for __class__ in the free vars. // Look for __class__ in the free vars.
PyTypeObject *type = NULL; PyTypeObject *type = NULL;
i = co->co_nlocals + co->co_ncellvars; int i = co->co_nlocals + co->co_nplaincellvars;
for (; i < co->co_nlocalsplus; i++) { for (; i < co->co_nlocalsplus; i++) {
assert(co->co_localspluskinds[i] & CO_FAST_FREE); assert((co->co_localspluskinds[i] & CO_FAST_FREE) != 0);
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
assert(PyUnicode_Check(name)); assert(PyUnicode_Check(name));
if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) { if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) {
@ -8936,7 +8930,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
} }
*type_p = type; *type_p = type;
*obj_p = obj; *obj_p = firstarg;
return 0; return 0;
} }

View File

@ -2922,29 +2922,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
} }
case TARGET(MAKE_CELL): { case TARGET(MAKE_CELL): {
// "initial" is probably NULL but not if it's an arg (or set
// via PyFrame_LocalsToFast() before MAKE_CELL has run).
PyObject *initial = GETLOCAL(oparg); PyObject *initial = GETLOCAL(oparg);
// Normally initial would be NULL. However, it
// might have been set to an initial value during
// a call to PyFrame_LocalsToFast().
PyObject *cell = PyCell_New(initial); PyObject *cell = PyCell_New(initial);
if (cell == NULL) { if (cell == NULL) {
goto error; goto error;
} }
/* If it is an arg then copy the arg into the cell. */
if (initial == NULL && co->co_cell2arg != NULL) {
int argoffset = co->co_cell2arg[oparg - co->co_nlocals];
if (argoffset != CO_CELL_NOT_AN_ARG) {
PyObject *arg = GETLOCAL(argoffset);
// It will have been set in initialize_locals() but
// may have been deleted PyFrame_LocalsToFast().
if (arg != NULL) {;
Py_INCREF(arg);
PyCell_SET(cell, arg);
/* Clear the local copy. */
SETLOCAL(argoffset, NULL);
}
}
}
SETLOCAL(oparg, cell); SETLOCAL(oparg, cell);
DISPATCH(); DISPATCH();
} }
@ -4915,7 +4899,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con,
for (i = 0; i < co->co_nfreevars; ++i) { for (i = 0; i < co->co_nfreevars; ++i) {
PyObject *o = PyTuple_GET_ITEM(con->fc_closure, i); PyObject *o = PyTuple_GET_ITEM(con->fc_closure, i);
Py_INCREF(o); Py_INCREF(o);
localsplus[co->co_nlocals + co->co_ncellvars + i] = o; localsplus[co->co_nlocals + co->co_nplaincellvars + i] = o;
} }
return 0; return 0;
@ -6244,7 +6228,7 @@ format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg)
if (_PyErr_Occurred(tstate)) if (_PyErr_Occurred(tstate))
return; return;
name = PyTuple_GET_ITEM(co->co_localsplusnames, oparg); name = PyTuple_GET_ITEM(co->co_localsplusnames, oparg);
if (oparg < co->co_ncellvars + co->co_nlocals) { if (oparg < co->co_nplaincellvars + co->co_nlocals) {
format_exc_check_arg(tstate, PyExc_UnboundLocalError, format_exc_check_arg(tstate, PyExc_UnboundLocalError,
UNBOUNDLOCAL_ERROR_MSG, name); UNBOUNDLOCAL_ERROR_MSG, name);
} else { } else {

View File

@ -21,6 +21,8 @@
* objects. * objects.
*/ */
#include <stdbool.h>
#include "Python.h" #include "Python.h"
#include "pycore_ast.h" // _PyAST_GetDocString() #include "pycore_ast.h" // _PyAST_GetDocString()
#include "pycore_compile.h" // _PyFuture_FromAST() #include "pycore_compile.h" // _PyFuture_FromAST()
@ -2053,7 +2055,7 @@ compiler_make_closure(struct compiler *c, PyCodeObject *co, Py_ssize_t flags,
qualname = co->co_name; qualname = co->co_name;
if (co->co_nfreevars) { if (co->co_nfreevars) {
int i = co->co_nlocals + co->co_ncellvars; int i = co->co_nlocals + co->co_nplaincellvars;
for (; i < co->co_nlocalsplus; ++i) { for (; i < co->co_nlocalsplus; ++i) {
/* Bypass com_addop_varname because it will generate /* Bypass com_addop_varname because it will generate
LOAD_DEREF but LOAD_CLOSURE is needed. LOAD_DEREF but LOAD_CLOSURE is needed.
@ -7188,11 +7190,10 @@ extern void _Py_set_localsplus_info(int, PyObject *, _PyLocalsPlusKind,
PyObject *, _PyLocalsPlusKinds); PyObject *, _PyLocalsPlusKinds);
static void static void
compute_localsplus_info(struct compiler *c, compute_localsplus_info(struct compiler *c, int nlocalsplus,
PyObject *names, _PyLocalsPlusKinds kinds) PyObject *names, _PyLocalsPlusKinds kinds)
{ {
int nlocalsplus = (int)PyTuple_GET_SIZE(names); assert(PyTuple_GET_SIZE(names) == nlocalsplus);
(void)nlocalsplus; // Avoid compiler errors for unused variable
PyObject *k, *v; PyObject *k, *v;
Py_ssize_t pos = 0; Py_ssize_t pos = 0;
@ -7201,15 +7202,26 @@ compute_localsplus_info(struct compiler *c,
assert(offset >= 0); assert(offset >= 0);
assert(offset < nlocalsplus); assert(offset < nlocalsplus);
// For now we do not distinguish arg kinds. // For now we do not distinguish arg kinds.
_Py_set_localsplus_info(offset, k, CO_FAST_LOCAL, names, kinds); _PyLocalsPlusKind kind = CO_FAST_LOCAL;
if (PyDict_GetItem(c->u->u_cellvars, k) != NULL) {
kind |= CO_FAST_CELL;
}
_Py_set_localsplus_info(offset, k, kind, names, kinds);
} }
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames); int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
// This counter mirrors the fix done in fix_cell_offsets().
int numdropped = 0;
pos = 0; pos = 0;
while (PyDict_Next(c->u->u_cellvars, &pos, &k, &v)) { while (PyDict_Next(c->u->u_cellvars, &pos, &k, &v)) {
if (PyDict_GetItem(c->u->u_varnames, k) != NULL) {
// Skip cells that are already covered by locals.
numdropped += 1;
continue;
}
int offset = (int)PyLong_AS_LONG(v); int offset = (int)PyLong_AS_LONG(v);
assert(offset >= 0); assert(offset >= 0);
offset += nlocals; offset += nlocals - numdropped;
assert(offset < nlocalsplus); assert(offset < nlocalsplus);
_Py_set_localsplus_info(offset, k, CO_FAST_CELL, names, kinds); _Py_set_localsplus_info(offset, k, CO_FAST_CELL, names, kinds);
} }
@ -7218,7 +7230,7 @@ compute_localsplus_info(struct compiler *c,
while (PyDict_Next(c->u->u_freevars, &pos, &k, &v)) { while (PyDict_Next(c->u->u_freevars, &pos, &k, &v)) {
int offset = (int)PyLong_AS_LONG(v); int offset = (int)PyLong_AS_LONG(v);
assert(offset >= 0); assert(offset >= 0);
offset += nlocals; offset += nlocals - numdropped;
assert(offset < nlocalsplus); assert(offset < nlocalsplus);
_Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds); _Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds);
} }
@ -7226,7 +7238,7 @@ compute_localsplus_info(struct compiler *c,
static PyCodeObject * static PyCodeObject *
makecode(struct compiler *c, struct assembler *a, PyObject *constslist, makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
int maxdepth) int maxdepth, int nlocalsplus)
{ {
PyCodeObject *co = NULL; PyCodeObject *co = NULL;
PyObject *names = NULL; PyObject *names = NULL;
@ -7264,15 +7276,6 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
assert(INT_MAX - posonlyargcount - posorkwargcount > 0); assert(INT_MAX - posonlyargcount - posorkwargcount > 0);
int kwonlyargcount = (int)c->u->u_kwonlyargcount; int kwonlyargcount = (int)c->u->u_kwonlyargcount;
Py_ssize_t nlocals = PyDict_GET_SIZE(c->u->u_varnames);
Py_ssize_t ncellvars = PyDict_GET_SIZE(c->u->u_cellvars);
Py_ssize_t nfreevars = PyDict_GET_SIZE(c->u->u_freevars);
assert(nlocals < INT_MAX);
assert(ncellvars < INT_MAX);
assert(nfreevars < INT_MAX);
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
int nlocalsplus = (int)nlocals + (int)ncellvars + (int)nfreevars;
localsplusnames = PyTuple_New(nlocalsplus); localsplusnames = PyTuple_New(nlocalsplus);
if (localsplusnames == NULL) { if (localsplusnames == NULL) {
goto error; goto error;
@ -7280,7 +7283,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
if (_PyCode_InitLocalsPlusKinds(nlocalsplus, &localspluskinds) < 0) { if (_PyCode_InitLocalsPlusKinds(nlocalsplus, &localspluskinds) < 0) {
goto error; goto error;
} }
compute_localsplus_info(c, localsplusnames, localspluskinds); compute_localsplus_info(c, nlocalsplus, localsplusnames, localspluskinds);
struct _PyCodeConstructor con = { struct _PyCodeConstructor con = {
.filename = c->c_filename, .filename = c->c_filename,
@ -7376,6 +7379,39 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
static int static int
ensure_exits_have_lineno(struct compiler *c); ensure_exits_have_lineno(struct compiler *c);
static int *
build_cellfixedoffsets(struct compiler *c)
{
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
int noffsets = ncellvars + nfreevars;
int *fixed = PyMem_New(int, noffsets);
if (fixed == NULL) {
PyErr_NoMemory();
return NULL;
}
for (int i = 0; i < noffsets; i++) {
fixed[i] = nlocals + i;
}
PyObject *varname, *cellindex;
Py_ssize_t pos = 0;
while (PyDict_Next(c->u->u_cellvars, &pos, &varname, &cellindex)) {
PyObject *varindex = PyDict_GetItem(c->u->u_varnames, varname);
if (varindex != NULL) {
assert(PyLong_AS_LONG(cellindex) < INT_MAX);
assert(PyLong_AS_LONG(varindex) < INT_MAX);
int oldindex = (int)PyLong_AS_LONG(cellindex);
int argoffset = (int)PyLong_AS_LONG(varindex);
fixed[oldindex] = argoffset;
}
}
return fixed;
}
static inline int static inline int
insert_instruction(basicblock *block, int pos, struct instr *instr) { insert_instruction(basicblock *block, int pos, struct instr *instr) {
if (compiler_next_instr(block) < 0) { if (compiler_next_instr(block) < 0) {
@ -7389,7 +7425,9 @@ insert_instruction(basicblock *block, int pos, struct instr *instr) {
} }
static int static int
insert_prefix_instructions(struct compiler *c, basicblock *entryblock) { insert_prefix_instructions(struct compiler *c, basicblock *entryblock,
int *fixed)
{
int flags = compute_code_flags(c); int flags = compute_code_flags(c);
if (flags < 0) { if (flags < 0) {
@ -7397,21 +7435,38 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock) {
} }
/* Set up cells for any variable that escapes, to be put in a closure. */ /* Set up cells for any variable that escapes, to be put in a closure. */
PyObject *k, *v; const int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
Py_ssize_t pos = 0; if (ncellvars) {
while (PyDict_Next(c->u->u_cellvars, &pos, &k, &v)) { // c->u->u_cellvars has the cells out of order so we sort them
assert(PyLong_AS_LONG(v) < INT_MAX); // before adding the MAKE_CELL instructions. Note that we
int cellindex = (int)PyLong_AS_LONG(v); // adjust for arg cells, which come first.
struct instr make_cell = { const int nvars = ncellvars + (int)PyDict_GET_SIZE(c->u->u_varnames);
.i_opcode = MAKE_CELL, int *sorted = PyMem_RawCalloc(nvars, sizeof(int));
// This will get fixed in offset_derefs(). if (sorted == NULL) {
.i_oparg = cellindex, PyErr_NoMemory();
.i_lineno = -1,
.i_target = NULL,
};
if (insert_instruction(entryblock, (int)(pos - 1), &make_cell) < 0) {
return -1; return -1;
} }
for (int i = 0; i < ncellvars; i++) {
sorted[fixed[i]] = i + 1;
}
for (int i = 0, ncellsused = 0; ncellsused < ncellvars; i++) {
int oldindex = sorted[i] - 1;
if (oldindex == -1) {
continue;
}
struct instr make_cell = {
.i_opcode = MAKE_CELL,
// This will get fixed in offset_derefs().
.i_oparg = oldindex,
.i_lineno = -1,
.i_target = NULL,
};
if (insert_instruction(entryblock, ncellsused, &make_cell) < 0) {
return -1;
}
ncellsused += 1;
}
PyMem_RawFree(sorted);
} }
/* Add the generator prefix instructions. */ /* Add the generator prefix instructions. */
@ -7469,14 +7524,33 @@ guarantee_lineno_for_exits(struct assembler *a, int firstlineno) {
} }
} }
static void static int
fix_cell_offsets(struct compiler *c, basicblock *entryblock) fix_cell_offsets(struct compiler *c, basicblock *entryblock, int *fixedmap)
{ {
assert(PyDict_GET_SIZE(c->u->u_varnames) < INT_MAX);
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames); int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
int noffsets = ncellvars + nfreevars;
// First deal with duplicates (arg cells).
int numdropped = 0;
for (int i = 0; i < noffsets ; i++) {
if (fixedmap[i] == i + nlocals) {
fixedmap[i] -= numdropped;
}
else {
// It was a duplicate (cell/arg).
numdropped += 1;
}
}
// Then update offsets, either relative to locals or by cell2arg.
for (basicblock *b = entryblock; b != NULL; b = b->b_next) { for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
for (int i = 0; i < b->b_iused; i++) { for (int i = 0; i < b->b_iused; i++) {
struct instr *inst = &b->b_instr[i]; struct instr *inst = &b->b_instr[i];
// This is called before extended args are generated.
assert(inst->i_opcode != EXTENDED_ARG);
int oldoffset = inst->i_oparg;
switch(inst->i_opcode) { switch(inst->i_opcode) {
case MAKE_CELL: case MAKE_CELL:
case LOAD_CLOSURE: case LOAD_CLOSURE:
@ -7484,10 +7558,15 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock)
case STORE_DEREF: case STORE_DEREF:
case DELETE_DEREF: case DELETE_DEREF:
case LOAD_CLASSDEREF: case LOAD_CLASSDEREF:
inst->i_oparg += nlocals; assert(oldoffset >= 0);
assert(oldoffset < noffsets);
assert(fixedmap[oldoffset] >= 0);
inst->i_oparg = fixedmap[oldoffset];
} }
} }
} }
return numdropped;
} }
static PyCodeObject * static PyCodeObject *
@ -7528,7 +7607,22 @@ assemble(struct compiler *c, int addNone)
} }
assert(entryblock != NULL); assert(entryblock != NULL);
if (insert_prefix_instructions(c, entryblock)) { assert(PyDict_GET_SIZE(c->u->u_varnames) < INT_MAX);
assert(PyDict_GET_SIZE(c->u->u_cellvars) < INT_MAX);
assert(PyDict_GET_SIZE(c->u->u_freevars) < INT_MAX);
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
assert(INT_MAX - nlocals - ncellvars > 0);
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
int nlocalsplus = nlocals + ncellvars + nfreevars;
int *cellfixedoffsets = build_cellfixedoffsets(c);
if (cellfixedoffsets == NULL) {
goto error;
}
// This must be called before fix_cell_offsets().
if (insert_prefix_instructions(c, entryblock, cellfixedoffsets)) {
goto error; goto error;
} }
@ -7545,7 +7639,13 @@ assemble(struct compiler *c, int addNone)
a.a_entry = entryblock; a.a_entry = entryblock;
a.a_nblocks = nblocks; a.a_nblocks = nblocks;
fix_cell_offsets(c, entryblock); int numdropped = fix_cell_offsets(c, entryblock, cellfixedoffsets);
PyMem_Free(cellfixedoffsets); // At this point we're done with it.
cellfixedoffsets = NULL;
if (numdropped < 0) {
goto error;
}
nlocalsplus -= numdropped;
consts = consts_dict_keys_inorder(c->u->u_consts); consts = consts_dict_keys_inorder(c->u->u_consts);
if (consts == NULL) { if (consts == NULL) {
@ -7586,10 +7686,10 @@ assemble(struct compiler *c, int addNone)
} }
if (!assemble_exception_table(&a)) { if (!assemble_exception_table(&a)) {
return 0; goto error;
} }
if (!assemble_line_range(&a)) { if (!assemble_line_range(&a)) {
return 0; goto error;
} }
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) { if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) {
@ -7610,10 +7710,13 @@ assemble(struct compiler *c, int addNone)
goto error; goto error;
} }
co = makecode(c, &a, consts, maxdepth); co = makecode(c, &a, consts, maxdepth, nlocalsplus);
error: error:
Py_XDECREF(consts); Py_XDECREF(consts);
assemble_free(&a); assemble_free(&a);
if (cellfixedoffsets != NULL) {
PyMem_Free(cellfixedoffsets);
}
return co; return co;
} }

2891
Python/importlib.h generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff