Commit 90037609 authored by Benjamin Peterson's avatar Benjamin Peterson

map cells to arg slots at code creation time (closes #12399)

This removes nested loops in PyEval_EvalCodeEx.
parent 935fa016
...@@ -22,6 +22,7 @@ typedef struct { ...@@ -22,6 +22,7 @@ typedef struct {
PyObject *co_freevars; /* tuple of strings (free variable names) */ PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */ PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest doesn't count for hash or comparisons */ /* The rest doesn't count for hash or comparisons */
unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
PyObject *co_filename; /* unicode (where it was loaded from) */ PyObject *co_filename; /* unicode (where it was loaded from) */
PyObject *co_name; /* unicode (name, for reference) */ PyObject *co_name; /* unicode (name, for reference) */
int co_firstlineno; /* first source line number */ int co_firstlineno; /* first source line number */
...@@ -57,6 +58,11 @@ typedef struct { ...@@ -57,6 +58,11 @@ typedef struct {
#define CO_FUTURE_BARRY_AS_BDFL 0x40000 #define CO_FUTURE_BARRY_AS_BDFL 0x40000
/* This value is found in the co_cell2arg array when the associated cell
variable does not correspond to an argument. The maximum number of
arguments is 255 (indexed up to 254), so 255 work as a special flag.*/
#define CO_CELL_NOT_AN_ARG 255
/* 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.
*/ */
......
...@@ -665,7 +665,7 @@ class SizeofTest(unittest.TestCase): ...@@ -665,7 +665,7 @@ class SizeofTest(unittest.TestCase):
return inner return inner
check(get_cell().__closure__[0], size(h + 'P')) check(get_cell().__closure__[0], size(h + 'P'))
# code # code
check(get_cell().__code__, size(h + '5i8Pi3P')) check(get_cell().__code__, size(h + '5i9Pi3P'))
# complex # complex
check(complex(0,1), size(h + '2d')) check(complex(0,1), size(h + '2d'))
# method_descriptor (descriptor object) # method_descriptor (descriptor object)
......
...@@ -51,7 +51,8 @@ PyCode_New(int argcount, int kwonlyargcount, ...@@ -51,7 +51,8 @@ PyCode_New(int argcount, int kwonlyargcount,
PyObject *lnotab) PyObject *lnotab)
{ {
PyCodeObject *co; PyCodeObject *co;
Py_ssize_t i; unsigned char *cell2arg = NULL;
Py_ssize_t i, n_cellvars;
/* Check argument types */ /* Check argument types */
if (argcount < 0 || kwonlyargcount < 0 || nlocals < 0 || if (argcount < 0 || kwonlyargcount < 0 || nlocals < 0 ||
...@@ -68,12 +69,13 @@ PyCode_New(int argcount, int kwonlyargcount, ...@@ -68,12 +69,13 @@ PyCode_New(int argcount, int kwonlyargcount,
PyErr_BadInternalCall(); PyErr_BadInternalCall();
return NULL; return NULL;
} }
n_cellvars = PyTuple_GET_SIZE(cellvars);
intern_strings(names); intern_strings(names);
intern_strings(varnames); intern_strings(varnames);
intern_strings(freevars); intern_strings(freevars);
intern_strings(cellvars); intern_strings(cellvars);
/* Intern selected string constants */ /* Intern selected string constants */
for (i = PyTuple_Size(consts); --i >= 0; ) { for (i = PyTuple_GET_SIZE(consts); --i >= 0; ) {
PyObject *v = PyTuple_GetItem(consts, i); PyObject *v = PyTuple_GetItem(consts, i);
if (!PyUnicode_Check(v)) if (!PyUnicode_Check(v))
continue; continue;
...@@ -81,35 +83,67 @@ PyCode_New(int argcount, int kwonlyargcount, ...@@ -81,35 +83,67 @@ PyCode_New(int argcount, int kwonlyargcount,
continue; continue;
PyUnicode_InternInPlace(&PyTuple_GET_ITEM(consts, i)); PyUnicode_InternInPlace(&PyTuple_GET_ITEM(consts, i));
} }
/* Create mapping between cells and arguments if needed. */
if (n_cellvars) {
Py_ssize_t total_args = argcount + kwonlyargcount +
((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
Py_ssize_t alloc_size = sizeof(unsigned char) * n_cellvars;
int used_cell2arg = 0;
cell2arg = PyMem_MALLOC(alloc_size);
if (cell2arg == NULL)
return NULL;
memset(cell2arg, CO_CELL_NOT_AN_ARG, alloc_size);
/* Find cells which are also arguments. */
for (i = 0; i < n_cellvars; i++) {
Py_ssize_t j;
PyObject *cell = PyTuple_GET_ITEM(cellvars, i);
for (j = 0; j < total_args; j++) {
PyObject *arg = PyTuple_GET_ITEM(varnames, j);
if (!PyUnicode_Compare(cell, arg)) {
cell2arg[i] = j;
used_cell2arg = 1;
break;
}
}
}
if (!used_cell2arg) {
PyMem_FREE(cell2arg);
cell2arg = NULL;
}
}
co = PyObject_NEW(PyCodeObject, &PyCode_Type); co = PyObject_NEW(PyCodeObject, &PyCode_Type);
if (co != NULL) { if (co == NULL) {
co->co_argcount = argcount; if (cell2arg)
co->co_kwonlyargcount = kwonlyargcount; PyMem_FREE(cell2arg);
co->co_nlocals = nlocals; return NULL;
co->co_stacksize = stacksize;
co->co_flags = flags;
Py_INCREF(code);
co->co_code = code;
Py_INCREF(consts);
co->co_consts = consts;
Py_INCREF(names);
co->co_names = names;
Py_INCREF(varnames);
co->co_varnames = varnames;
Py_INCREF(freevars);
co->co_freevars = freevars;
Py_INCREF(cellvars);
co->co_cellvars = cellvars;
Py_INCREF(filename);
co->co_filename = filename;
Py_INCREF(name);
co->co_name = name;
co->co_firstlineno = firstlineno;
Py_INCREF(lnotab);
co->co_lnotab = lnotab;
co->co_zombieframe = NULL;
co->co_weakreflist = NULL;
} }
co->co_argcount = argcount;
co->co_kwonlyargcount = kwonlyargcount;
co->co_nlocals = nlocals;
co->co_stacksize = stacksize;
co->co_flags = flags;
Py_INCREF(code);
co->co_code = code;
Py_INCREF(consts);
co->co_consts = consts;
Py_INCREF(names);
co->co_names = names;
Py_INCREF(varnames);
co->co_varnames = varnames;
Py_INCREF(freevars);
co->co_freevars = freevars;
Py_INCREF(cellvars);
co->co_cellvars = cellvars;
co->co_cell2arg = cell2arg;
Py_INCREF(filename);
co->co_filename = filename;
Py_INCREF(name);
co->co_name = name;
co->co_firstlineno = firstlineno;
Py_INCREF(lnotab);
co->co_lnotab = lnotab;
co->co_zombieframe = NULL;
co->co_weakreflist = NULL;
return co; return co;
} }
...@@ -330,6 +364,8 @@ code_dealloc(PyCodeObject *co) ...@@ -330,6 +364,8 @@ code_dealloc(PyCodeObject *co)
Py_XDECREF(co->co_filename); Py_XDECREF(co->co_filename);
Py_XDECREF(co->co_name); Py_XDECREF(co->co_name);
Py_XDECREF(co->co_lnotab); Py_XDECREF(co->co_lnotab);
if (co->co_cell2arg != NULL)
PyMem_FREE(co->co_cell2arg);
if (co->co_zombieframe != NULL) if (co->co_zombieframe != NULL)
PyObject_GC_Del(co->co_zombieframe); PyObject_GC_Del(co->co_zombieframe);
if (co->co_weakreflist != NULL) if (co->co_weakreflist != NULL)
......
...@@ -3357,56 +3357,24 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, ...@@ -3357,56 +3357,24 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
} }
/* Allocate and initialize storage for cell vars, and copy free /* Allocate and initialize storage for cell vars, and copy free
vars into frame. This isn't too efficient right now. */ vars into frame. */
if (PyTuple_GET_SIZE(co->co_cellvars)) { for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
int i, j, nargs, found;
Py_UNICODE *cellname, *argname;
PyObject *c; PyObject *c;
int arg;
nargs = total_args; /* Possibly account for the cell variable being an argument. */
if (co->co_flags & CO_VARARGS) if (co->co_cell2arg != NULL &&
nargs++; (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG)
if (co->co_flags & CO_VARKEYWORDS) c = PyCell_New(GETLOCAL(arg));
nargs++; else
c = PyCell_New(NULL);
/* Initialize each cell var, taking into account if (c == NULL)
cell vars that are initialized from arguments. goto fail;
SETLOCAL(co->co_nlocals + i, c);
Should arrange for the compiler to put cellvars
that are arguments at the beginning of the cellvars
list so that we can march over it more efficiently?
*/
for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
cellname = PyUnicode_AS_UNICODE(
PyTuple_GET_ITEM(co->co_cellvars, i));
found = 0;
for (j = 0; j < nargs; j++) {
argname = PyUnicode_AS_UNICODE(
PyTuple_GET_ITEM(co->co_varnames, j));
if (Py_UNICODE_strcmp(cellname, argname) == 0) {
c = PyCell_New(GETLOCAL(j));
if (c == NULL)
goto fail;
GETLOCAL(co->co_nlocals + i) = c;
found = 1;
break;
}
}
if (found == 0) {
c = PyCell_New(NULL);
if (c == NULL)
goto fail;
SETLOCAL(co->co_nlocals + i, c);
}
}
} }
if (PyTuple_GET_SIZE(co->co_freevars)) { for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
int i; PyObject *o = PyTuple_GET_ITEM(closure, i);
for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) { Py_INCREF(o);
PyObject *o = PyTuple_GET_ITEM(closure, i); freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
Py_INCREF(o);
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
}
} }
if (co->co_flags & CO_GENERATOR) { if (co->co_flags & CO_GENERATOR) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment