mirror of https://github.com/python/cpython
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:
parent
1d10bf0bb9
commit
ac38a9f2df
|
@ -83,11 +83,11 @@ struct PyCodeObject {
|
|||
|
||||
/* 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)
|
||||
int co_nlocalsplus; /* number of local + cell + free 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 */
|
||||
// lazily-computed values
|
||||
PyObject *co_varnames; /* tuple of strings (local variable names) */
|
||||
|
@ -142,10 +142,6 @@ struct PyCodeObject {
|
|||
#define CO_FUTURE_GENERATOR_STOP 0x800000
|
||||
#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.
|
||||
For example, when a keyword is added.
|
||||
*/
|
||||
|
|
|
@ -32,8 +32,6 @@ _PyFrame_GetBuiltins(PyFrameObject *f)
|
|||
|
||||
int _PyFrame_TakeLocals(PyFrameObject *f);
|
||||
|
||||
PyAPI_FUNC(int) _PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -358,6 +358,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# 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 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
|
||||
|
@ -367,7 +368,7 @@ _code_type = type(_write_atomic.__code__)
|
|||
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
|
||||
# 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
|
||||
|
||||
_PYCACHE = '__pycache__'
|
||||
|
|
|
@ -427,9 +427,9 @@ def _h(y):
|
|||
return foo
|
||||
|
||||
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
|
||||
6 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
|
||||
8 LOAD_CONST 2 ('_h.<locals>.foo')
|
||||
|
@ -446,14 +446,14 @@ dis_nested_0 = """\
|
|||
|
||||
dis_nested_1 = """%s
|
||||
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
|
||||
6 LOAD_CONST 1 (<code object <listcomp> at 0x..., file "%s", line %d>)
|
||||
8 LOAD_CONST 2 ('_h.<locals>.foo.<locals>.<listcomp>')
|
||||
10 MAKE_FUNCTION 8 (closure)
|
||||
12 LOAD_DEREF 2 (y)
|
||||
12 LOAD_DEREF 1 (y)
|
||||
14 GET_ITER
|
||||
16 CALL_FUNCTION 1
|
||||
18 RETURN_VALUE
|
||||
|
@ -966,19 +966,19 @@ expected_jumpy_line = 1
|
|||
|
||||
Instruction = dis.Instruction
|
||||
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=4, argval='b', argrepr='b', offset=2, 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=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_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=4, argval='b', argrepr='b', offset=8, 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=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='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='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='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=4, argval='b', argrepr='b', offset=24, 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=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=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),
|
||||
|
@ -991,23 +991,23 @@ expected_opinfo_outer = [
|
|||
]
|
||||
|
||||
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=4, argval='d', argrepr='d', offset=2, 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=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_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=6, 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=4, argval='d', argrepr='d', offset=12, 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=4, argval='b', argrepr='b', offset=8, 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=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='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='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='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=6, 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=4, argval='d', argrepr='d', offset=32, 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=4, argval='b', argrepr='b', offset=28, 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=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='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),
|
||||
|
|
|
@ -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.
|
|
@ -162,43 +162,28 @@ _Py_set_localsplus_info(int offset, PyObject *name, _PyLocalsPlusKind kind,
|
|||
Py_INCREF(name);
|
||||
PyTuple_SET_ITEM(names, offset, name);
|
||||
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
|
||||
get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
|
||||
int *pnlocals, int *pncellvars,
|
||||
int *pnlocals, int *pnplaincellvars, int *pncellvars,
|
||||
int *pnfreevars)
|
||||
{
|
||||
int nlocals = 0;
|
||||
int nplaincellvars = 0;
|
||||
int ncellvars = 0;
|
||||
int nfreevars = 0;
|
||||
int nlocalsplus = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(names),
|
||||
Py_ssize_t, int);
|
||||
Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(names);
|
||||
for (int i = 0; i < nlocalsplus; i++) {
|
||||
if (kinds[i] & CO_FAST_LOCAL) {
|
||||
nlocals += 1;
|
||||
if (kinds[i] & CO_FAST_CELL) {
|
||||
ncellvars += 1;
|
||||
}
|
||||
}
|
||||
else if (kinds[i] & CO_FAST_CELL) {
|
||||
ncellvars += 1;
|
||||
nplaincellvars += 1;
|
||||
}
|
||||
else if (kinds[i] & CO_FAST_FREE) {
|
||||
nfreevars += 1;
|
||||
|
@ -207,6 +192,9 @@ get_localsplus_counts(PyObject *names, _PyLocalsPlusKinds kinds,
|
|||
if (pnlocals != NULL) {
|
||||
*pnlocals = nlocals;
|
||||
}
|
||||
if (pnplaincellvars != NULL) {
|
||||
*pnplaincellvars = nplaincellvars;
|
||||
}
|
||||
if (pncellvars != NULL) {
|
||||
*pncellvars = ncellvars;
|
||||
}
|
||||
|
@ -227,10 +215,6 @@ get_localsplus_names(PyCodeObject *co, _PyLocalsPlusKind kind, int num)
|
|||
if ((co->co_localspluskinds[offset] & kind) == 0) {
|
||||
continue;
|
||||
}
|
||||
// For now there may be duplicates, which we ignore.
|
||||
if (kind == CO_FAST_CELL && co->co_localspluskinds[offset] != kind) {
|
||||
continue;
|
||||
}
|
||||
assert(index < num);
|
||||
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, offset);
|
||||
Py_INCREF(name);
|
||||
|
@ -283,7 +267,7 @@ _PyCode_Validate(struct _PyCodeConstructor *con)
|
|||
* here to avoid the possibility of overflow (however remote). */
|
||||
int nlocals;
|
||||
get_localsplus_counts(con->localsplusnames, con->localspluskinds,
|
||||
&nlocals, NULL, NULL);
|
||||
&nlocals, NULL, NULL, NULL);
|
||||
int nplainlocals = nlocals -
|
||||
con->argcount -
|
||||
con->kwonlyargcount -
|
||||
|
@ -301,9 +285,9 @@ static void
|
|||
init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
|
||||
{
|
||||
int nlocalsplus = (int)PyTuple_GET_SIZE(con->localsplusnames);
|
||||
int nlocals, ncellvars, nfreevars;
|
||||
int nlocals, nplaincellvars, ncellvars, nfreevars;
|
||||
get_localsplus_counts(con->localsplusnames, con->localspluskinds,
|
||||
&nlocals, &ncellvars, &nfreevars);
|
||||
&nlocals, &nplaincellvars, &ncellvars, &nfreevars);
|
||||
|
||||
Py_INCREF(con->filename);
|
||||
co->co_filename = con->filename;
|
||||
|
@ -338,9 +322,9 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
|
|||
co->co_exceptiontable = con->exceptiontable;
|
||||
|
||||
/* derived values */
|
||||
co->co_cell2arg = NULL; // This will be set soon.
|
||||
co->co_nlocalsplus = nlocalsplus;
|
||||
co->co_nlocals = nlocals;
|
||||
co->co_nplaincellvars = nplaincellvars;
|
||||
co->co_ncellvars = ncellvars;
|
||||
co->co_nfreevars = nfreevars;
|
||||
co->co_varnames = NULL;
|
||||
|
@ -392,44 +376,6 @@ _PyCode_New(struct _PyCodeConstructor *con)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -478,6 +424,23 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
|
|||
}
|
||||
for (int i = 0; i < ncellvars; i++, offset++) {
|
||||
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,
|
||||
localsplusnames, localspluskinds);
|
||||
}
|
||||
|
@ -486,6 +449,11 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
|
|||
_Py_set_localsplus_info(offset, name, CO_FAST_FREE,
|
||||
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 = {
|
||||
.filename = filename,
|
||||
|
@ -1182,8 +1150,6 @@ code_dealloc(PyCodeObject *co)
|
|||
Py_XDECREF(co->co_name);
|
||||
Py_XDECREF(co->co_linetable);
|
||||
Py_XDECREF(co->co_exceptiontable);
|
||||
if (co->co_cell2arg != NULL)
|
||||
PyMem_Free(co->co_cell2arg);
|
||||
if (co->co_weakreflist != NULL)
|
||||
PyObject_ClearWeakRefs((PyObject*)co);
|
||||
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]);
|
||||
}
|
||||
|
||||
if (co->co_cell2arg != NULL && co->co_cellvars != NULL) {
|
||||
res += co->co_ncellvars * sizeof(Py_ssize_t);
|
||||
}
|
||||
|
||||
if (co->co_quickened != NULL) {
|
||||
Py_ssize_t count = co->co_quickened[0].entry.zero.cache_count;
|
||||
count += (PyBytes_GET_SIZE(co->co_code)+sizeof(SpecializedCacheEntry)-1)/
|
||||
|
|
|
@ -918,7 +918,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
|
|||
return f;
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
_PyFrame_OpAlreadyRan(PyFrameObject *f, int opcode, int oparg)
|
||||
{
|
||||
const _Py_CODEUNIT *code =
|
||||
|
@ -966,26 +966,9 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
|
|||
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 *value = fast[i];
|
||||
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) {
|
||||
// The cell was set by _PyEval_MakeFrameVector() from
|
||||
// the function's closure.
|
||||
|
@ -1003,20 +986,10 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f)
|
|||
// (likely) MAKE_CELL must have executed already.
|
||||
value = PyCell_GET(value);
|
||||
}
|
||||
// (unlikely) Otherwise it must be an initial value set
|
||||
// by an earlier call to PyFrame_FastToLocals().
|
||||
}
|
||||
else {
|
||||
// (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];
|
||||
}
|
||||
// (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL),
|
||||
// with the initial value set by _PyEval_MakeFrameVector()...
|
||||
// (unlikely) ...or it was set to some initial value by
|
||||
// an earlier call to PyFrame_LocalsToFast().
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1079,10 +1052,6 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
|||
if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {
|
||||
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 *value = PyObject_GetItem(locals, name);
|
||||
/* We only care about NULLs if clear is true. */
|
||||
|
@ -1093,12 +1062,6 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
|||
}
|
||||
}
|
||||
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;
|
||||
if (kind == CO_FAST_FREE) {
|
||||
// The cell was set by _PyEval_MakeFrameVector() from
|
||||
|
@ -1107,13 +1070,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
|||
cell = oldvalue;
|
||||
}
|
||||
else if (kind & CO_FAST_CELL && oldvalue != NULL) {
|
||||
if (cellargoffset != CO_CELL_NOT_AN_ARG) {
|
||||
// (likely) MAKE_CELL must have executed already.
|
||||
// It's the cell for an arg.
|
||||
assert(PyCell_Check(oldvalue));
|
||||
cell = oldvalue;
|
||||
}
|
||||
else {
|
||||
/* Same test as in PyFrame_FastToLocals() above. */
|
||||
if (PyCell_Check(oldvalue) &&
|
||||
_PyFrame_OpAlreadyRan(f, MAKE_CELL, i)) {
|
||||
// (likely) MAKE_CELL must have executed already.
|
||||
|
@ -1122,7 +1079,6 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
|||
// (unlikely) Otherwise, it must have been set to some
|
||||
// initial value by an earlier call to PyFrame_LocalsToFast().
|
||||
}
|
||||
}
|
||||
if (cell != NULL) {
|
||||
oldvalue = PyCell_GET(cell);
|
||||
if (value != oldvalue) {
|
||||
|
@ -1131,30 +1087,9 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
|||
PyCell_SET(cell, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int offset = i;
|
||||
if (kind & CO_FAST_CELL) {
|
||||
// (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) {
|
||||
else if (value != oldvalue) {
|
||||
Py_XINCREF(value);
|
||||
Py_XSETREF(fast[offset], value);
|
||||
}
|
||||
Py_XSETREF(fast[i], value);
|
||||
}
|
||||
Py_XDECREF(value);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||
#include "pycore_unionobject.h" // _Py_Union(), _Py_union_type_or
|
||||
#include "frameobject.h"
|
||||
#include "pycore_frame.h" // _PyFrame_OpAlreadyRan
|
||||
#include "opcode.h" // MAKE_CELL
|
||||
#include "structmember.h" // PyMemberDef
|
||||
|
||||
|
@ -8878,23 +8877,18 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
|
|||
return -1;
|
||||
}
|
||||
|
||||
PyObject *obj = f->f_localsptr[0];
|
||||
int i;
|
||||
if (obj == NULL && co->co_cell2arg) {
|
||||
/* The first argument might be a cell. */
|
||||
for (i = 0; i < co->co_ncellvars; i++) {
|
||||
if (co->co_cell2arg[i] == 0) {
|
||||
int celloffset = co->co_nlocals + i;
|
||||
PyObject *cell = f->f_localsptr[celloffset];
|
||||
if (PyCell_Check(cell) &&
|
||||
_PyFrame_OpAlreadyRan(f, MAKE_CELL, celloffset)) {
|
||||
obj = PyCell_GET(cell);
|
||||
}
|
||||
break;
|
||||
PyObject *firstarg = f->f_localsptr[0];
|
||||
// The first argument might be a cell.
|
||||
if (firstarg != NULL && (co->co_localspluskinds[0] & CO_FAST_CELL)) {
|
||||
// "firstarg" is a cell here unless (very unlikely) super()
|
||||
// was called from the C-API before the first MAKE_CELL op.
|
||||
if (f->f_lasti >= 0) {
|
||||
assert(_Py_OPCODE(*co->co_firstinstr) == MAKE_CELL);
|
||||
assert(PyCell_Check(firstarg));
|
||||
firstarg = PyCell_GET(firstarg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj == NULL) {
|
||||
if (firstarg == NULL) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"super(): arg[0] deleted");
|
||||
return -1;
|
||||
|
@ -8902,9 +8896,9 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
|
|||
|
||||
// Look for __class__ in the free vars.
|
||||
PyTypeObject *type = NULL;
|
||||
i = co->co_nlocals + co->co_ncellvars;
|
||||
int i = co->co_nlocals + co->co_nplaincellvars;
|
||||
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);
|
||||
assert(PyUnicode_Check(name));
|
||||
if (_PyUnicode_EqualToASCIIId(name, &PyId___class__)) {
|
||||
|
@ -8936,7 +8930,7 @@ super_init_without_args(PyFrameObject *f, PyCodeObject *co,
|
|||
}
|
||||
|
||||
*type_p = type;
|
||||
*obj_p = obj;
|
||||
*obj_p = firstarg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2922,29 +2922,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
|
|||
}
|
||||
|
||||
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);
|
||||
// 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);
|
||||
if (cell == NULL) {
|
||||
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);
|
||||
DISPATCH();
|
||||
}
|
||||
|
@ -4915,7 +4899,7 @@ initialize_locals(PyThreadState *tstate, PyFrameConstructor *con,
|
|||
for (i = 0; i < co->co_nfreevars; ++i) {
|
||||
PyObject *o = PyTuple_GET_ITEM(con->fc_closure, i);
|
||||
Py_INCREF(o);
|
||||
localsplus[co->co_nlocals + co->co_ncellvars + i] = o;
|
||||
localsplus[co->co_nlocals + co->co_nplaincellvars + i] = o;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -6244,7 +6228,7 @@ format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg)
|
|||
if (_PyErr_Occurred(tstate))
|
||||
return;
|
||||
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,
|
||||
UNBOUNDLOCAL_ERROR_MSG, name);
|
||||
} else {
|
||||
|
|
173
Python/compile.c
173
Python/compile.c
|
@ -21,6 +21,8 @@
|
|||
* objects.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_ast.h" // _PyAST_GetDocString()
|
||||
#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;
|
||||
|
||||
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) {
|
||||
/* Bypass com_addop_varname because it will generate
|
||||
LOAD_DEREF but LOAD_CLOSURE is needed.
|
||||
|
@ -7188,11 +7190,10 @@ extern void _Py_set_localsplus_info(int, PyObject *, _PyLocalsPlusKind,
|
|||
PyObject *, _PyLocalsPlusKinds);
|
||||
|
||||
static void
|
||||
compute_localsplus_info(struct compiler *c,
|
||||
compute_localsplus_info(struct compiler *c, int nlocalsplus,
|
||||
PyObject *names, _PyLocalsPlusKinds kinds)
|
||||
{
|
||||
int nlocalsplus = (int)PyTuple_GET_SIZE(names);
|
||||
(void)nlocalsplus; // Avoid compiler errors for unused variable
|
||||
assert(PyTuple_GET_SIZE(names) == nlocalsplus);
|
||||
|
||||
PyObject *k, *v;
|
||||
Py_ssize_t pos = 0;
|
||||
|
@ -7201,15 +7202,26 @@ compute_localsplus_info(struct compiler *c,
|
|||
assert(offset >= 0);
|
||||
assert(offset < nlocalsplus);
|
||||
// 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);
|
||||
|
||||
// This counter mirrors the fix done in fix_cell_offsets().
|
||||
int numdropped = 0;
|
||||
pos = 0;
|
||||
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);
|
||||
assert(offset >= 0);
|
||||
offset += nlocals;
|
||||
offset += nlocals - numdropped;
|
||||
assert(offset < nlocalsplus);
|
||||
_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)) {
|
||||
int offset = (int)PyLong_AS_LONG(v);
|
||||
assert(offset >= 0);
|
||||
offset += nlocals;
|
||||
offset += nlocals - numdropped;
|
||||
assert(offset < nlocalsplus);
|
||||
_Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds);
|
||||
}
|
||||
|
@ -7226,7 +7238,7 @@ compute_localsplus_info(struct compiler *c,
|
|||
|
||||
static PyCodeObject *
|
||||
makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
|
||||
int maxdepth)
|
||||
int maxdepth, int nlocalsplus)
|
||||
{
|
||||
PyCodeObject *co = NULL;
|
||||
PyObject *names = NULL;
|
||||
|
@ -7264,15 +7276,6 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
|
|||
assert(INT_MAX - posonlyargcount - posorkwargcount > 0);
|
||||
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);
|
||||
if (localsplusnames == NULL) {
|
||||
goto error;
|
||||
|
@ -7280,7 +7283,7 @@ makecode(struct compiler *c, struct assembler *a, PyObject *constslist,
|
|||
if (_PyCode_InitLocalsPlusKinds(nlocalsplus, &localspluskinds) < 0) {
|
||||
goto error;
|
||||
}
|
||||
compute_localsplus_info(c, localsplusnames, localspluskinds);
|
||||
compute_localsplus_info(c, nlocalsplus, localsplusnames, localspluskinds);
|
||||
|
||||
struct _PyCodeConstructor con = {
|
||||
.filename = c->c_filename,
|
||||
|
@ -7376,6 +7379,39 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
|
|||
static int
|
||||
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
|
||||
insert_instruction(basicblock *block, int pos, struct instr *instr) {
|
||||
if (compiler_next_instr(block) < 0) {
|
||||
|
@ -7389,7 +7425,9 @@ insert_instruction(basicblock *block, int pos, struct instr *instr) {
|
|||
}
|
||||
|
||||
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);
|
||||
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. */
|
||||
PyObject *k, *v;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(c->u->u_cellvars, &pos, &k, &v)) {
|
||||
assert(PyLong_AS_LONG(v) < INT_MAX);
|
||||
int cellindex = (int)PyLong_AS_LONG(v);
|
||||
const int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
|
||||
if (ncellvars) {
|
||||
// c->u->u_cellvars has the cells out of order so we sort them
|
||||
// before adding the MAKE_CELL instructions. Note that we
|
||||
// adjust for arg cells, which come first.
|
||||
const int nvars = ncellvars + (int)PyDict_GET_SIZE(c->u->u_varnames);
|
||||
int *sorted = PyMem_RawCalloc(nvars, sizeof(int));
|
||||
if (sorted == NULL) {
|
||||
PyErr_NoMemory();
|
||||
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 = cellindex,
|
||||
.i_oparg = oldindex,
|
||||
.i_lineno = -1,
|
||||
.i_target = NULL,
|
||||
};
|
||||
if (insert_instruction(entryblock, (int)(pos - 1), &make_cell) < 0) {
|
||||
if (insert_instruction(entryblock, ncellsused, &make_cell) < 0) {
|
||||
return -1;
|
||||
}
|
||||
ncellsused += 1;
|
||||
}
|
||||
PyMem_RawFree(sorted);
|
||||
}
|
||||
|
||||
/* Add the generator prefix instructions. */
|
||||
|
@ -7469,14 +7524,33 @@ guarantee_lineno_for_exits(struct assembler *a, int firstlineno) {
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fix_cell_offsets(struct compiler *c, basicblock *entryblock)
|
||||
static int
|
||||
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 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 (int i = 0; i < b->b_iused; 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) {
|
||||
case MAKE_CELL:
|
||||
case LOAD_CLOSURE:
|
||||
|
@ -7484,10 +7558,15 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock)
|
|||
case STORE_DEREF:
|
||||
case DELETE_DEREF:
|
||||
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 *
|
||||
|
@ -7528,7 +7607,22 @@ assemble(struct compiler *c, int addNone)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -7545,7 +7639,13 @@ assemble(struct compiler *c, int addNone)
|
|||
a.a_entry = entryblock;
|
||||
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);
|
||||
if (consts == NULL) {
|
||||
|
@ -7586,10 +7686,10 @@ assemble(struct compiler *c, int addNone)
|
|||
}
|
||||
|
||||
if (!assemble_exception_table(&a)) {
|
||||
return 0;
|
||||
goto error;
|
||||
}
|
||||
if (!assemble_line_range(&a)) {
|
||||
return 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) {
|
||||
|
@ -7610,10 +7710,13 @@ assemble(struct compiler *c, int addNone)
|
|||
goto error;
|
||||
}
|
||||
|
||||
co = makecode(c, &a, consts, maxdepth);
|
||||
co = makecode(c, &a, consts, maxdepth, nlocalsplus);
|
||||
error:
|
||||
Py_XDECREF(consts);
|
||||
assemble_free(&a);
|
||||
if (cellfixedoffsets != NULL) {
|
||||
PyMem_Free(cellfixedoffsets);
|
||||
}
|
||||
return co;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue