Skip to content

Commit 9003760

Browse files
committed
map cells to arg slots at code creation time (closes #12399)
This removes nested loops in PyEval_EvalCodeEx.
1 parent 935fa01 commit 9003760

4 files changed

Lines changed: 88 additions & 78 deletions

File tree

Include/code.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ typedef struct {
2222
PyObject *co_freevars; /* tuple of strings (free variable names) */
2323
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
2424
/* The rest doesn't count for hash or comparisons */
25+
unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
2526
PyObject *co_filename; /* unicode (where it was loaded from) */
2627
PyObject *co_name; /* unicode (name, for reference) */
2728
int co_firstlineno; /* first source line number */
@@ -57,6 +58,11 @@ typedef struct {
5758

5859
#define CO_FUTURE_BARRY_AS_BDFL 0x40000
5960

61+
/* This value is found in the co_cell2arg array when the associated cell
62+
variable does not correspond to an argument. The maximum number of
63+
arguments is 255 (indexed up to 254), so 255 work as a special flag.*/
64+
#define CO_CELL_NOT_AN_ARG 255
65+
6066
/* This should be defined if a future statement modifies the syntax.
6167
For example, when a keyword is added.
6268
*/

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ def inner():
665665
return inner
666666
check(get_cell().__closure__[0], size(h + 'P'))
667667
# code
668-
check(get_cell().__code__, size(h + '5i8Pi3P'))
668+
check(get_cell().__code__, size(h + '5i9Pi3P'))
669669
# complex
670670
check(complex(0,1), size(h + '2d'))
671671
# method_descriptor (descriptor object)

Objects/codeobject.c

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ PyCode_New(int argcount, int kwonlyargcount,
5151
PyObject *lnotab)
5252
{
5353
PyCodeObject *co;
54-
Py_ssize_t i;
54+
unsigned char *cell2arg = NULL;
55+
Py_ssize_t i, n_cellvars;
5556

5657
/* Check argument types */
5758
if (argcount < 0 || kwonlyargcount < 0 || nlocals < 0 ||
@@ -68,48 +69,81 @@ PyCode_New(int argcount, int kwonlyargcount,
6869
PyErr_BadInternalCall();
6970
return NULL;
7071
}
72+
n_cellvars = PyTuple_GET_SIZE(cellvars);
7173
intern_strings(names);
7274
intern_strings(varnames);
7375
intern_strings(freevars);
7476
intern_strings(cellvars);
7577
/* Intern selected string constants */
76-
for (i = PyTuple_Size(consts); --i >= 0; ) {
78+
for (i = PyTuple_GET_SIZE(consts); --i >= 0; ) {
7779
PyObject *v = PyTuple_GetItem(consts, i);
7880
if (!PyUnicode_Check(v))
7981
continue;
8082
if (!all_name_chars(PyUnicode_AS_UNICODE(v)))
8183
continue;
8284
PyUnicode_InternInPlace(&PyTuple_GET_ITEM(consts, i));
8385
}
86+
/* Create mapping between cells and arguments if needed. */
87+
if (n_cellvars) {
88+
Py_ssize_t total_args = argcount + kwonlyargcount +
89+
((flags & CO_VARARGS) != 0) + ((flags & CO_VARKEYWORDS) != 0);
90+
Py_ssize_t alloc_size = sizeof(unsigned char) * n_cellvars;
91+
int used_cell2arg = 0;
92+
cell2arg = PyMem_MALLOC(alloc_size);
93+
if (cell2arg == NULL)
94+
return NULL;
95+
memset(cell2arg, CO_CELL_NOT_AN_ARG, alloc_size);
96+
/* Find cells which are also arguments. */
97+
for (i = 0; i < n_cellvars; i++) {
98+
Py_ssize_t j;
99+
PyObject *cell = PyTuple_GET_ITEM(cellvars, i);
100+
for (j = 0; j < total_args; j++) {
101+
PyObject *arg = PyTuple_GET_ITEM(varnames, j);
102+
if (!PyUnicode_Compare(cell, arg)) {
103+
cell2arg[i] = j;
104+
used_cell2arg = 1;
105+
break;
106+
}
107+
}
108+
}
109+
if (!used_cell2arg) {
110+
PyMem_FREE(cell2arg);
111+
cell2arg = NULL;
112+
}
113+
}
84114
co = PyObject_NEW(PyCodeObject, &PyCode_Type);
85-
if (co != NULL) {
86-
co->co_argcount = argcount;
87-
co->co_kwonlyargcount = kwonlyargcount;
88-
co->co_nlocals = nlocals;
89-
co->co_stacksize = stacksize;
90-
co->co_flags = flags;
91-
Py_INCREF(code);
92-
co->co_code = code;
93-
Py_INCREF(consts);
94-
co->co_consts = consts;
95-
Py_INCREF(names);
96-
co->co_names = names;
97-
Py_INCREF(varnames);
98-
co->co_varnames = varnames;
99-
Py_INCREF(freevars);
100-
co->co_freevars = freevars;
101-
Py_INCREF(cellvars);
102-
co->co_cellvars = cellvars;
103-
Py_INCREF(filename);
104-
co->co_filename = filename;
105-
Py_INCREF(name);
106-
co->co_name = name;
107-
co->co_firstlineno = firstlineno;
108-
Py_INCREF(lnotab);
109-
co->co_lnotab = lnotab;
110-
co->co_zombieframe = NULL;
111-
co->co_weakreflist = NULL;
115+
if (co == NULL) {
116+
if (cell2arg)
117+
PyMem_FREE(cell2arg);
118+
return NULL;
112119
}
120+
co->co_argcount = argcount;
121+
co->co_kwonlyargcount = kwonlyargcount;
122+
co->co_nlocals = nlocals;
123+
co->co_stacksize = stacksize;
124+
co->co_flags = flags;
125+
Py_INCREF(code);
126+
co->co_code = code;
127+
Py_INCREF(consts);
128+
co->co_consts = consts;
129+
Py_INCREF(names);
130+
co->co_names = names;
131+
Py_INCREF(varnames);
132+
co->co_varnames = varnames;
133+
Py_INCREF(freevars);
134+
co->co_freevars = freevars;
135+
Py_INCREF(cellvars);
136+
co->co_cellvars = cellvars;
137+
co->co_cell2arg = cell2arg;
138+
Py_INCREF(filename);
139+
co->co_filename = filename;
140+
Py_INCREF(name);
141+
co->co_name = name;
142+
co->co_firstlineno = firstlineno;
143+
Py_INCREF(lnotab);
144+
co->co_lnotab = lnotab;
145+
co->co_zombieframe = NULL;
146+
co->co_weakreflist = NULL;
113147
return co;
114148
}
115149

@@ -330,6 +364,8 @@ code_dealloc(PyCodeObject *co)
330364
Py_XDECREF(co->co_filename);
331365
Py_XDECREF(co->co_name);
332366
Py_XDECREF(co->co_lnotab);
367+
if (co->co_cell2arg != NULL)
368+
PyMem_FREE(co->co_cell2arg);
333369
if (co->co_zombieframe != NULL)
334370
PyObject_GC_Del(co->co_zombieframe);
335371
if (co->co_weakreflist != NULL)

Python/ceval.c

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,56 +3357,24 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
33573357
}
33583358

33593359
/* Allocate and initialize storage for cell vars, and copy free
3360-
vars into frame. This isn't too efficient right now. */
3361-
if (PyTuple_GET_SIZE(co->co_cellvars)) {
3362-
int i, j, nargs, found;
3363-
Py_UNICODE *cellname, *argname;
3360+
vars into frame. */
3361+
for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
33643362
PyObject *c;
3365-
3366-
nargs = total_args;
3367-
if (co->co_flags & CO_VARARGS)
3368-
nargs++;
3369-
if (co->co_flags & CO_VARKEYWORDS)
3370-
nargs++;
3371-
3372-
/* Initialize each cell var, taking into account
3373-
cell vars that are initialized from arguments.
3374-
3375-
Should arrange for the compiler to put cellvars
3376-
that are arguments at the beginning of the cellvars
3377-
list so that we can march over it more efficiently?
3378-
*/
3379-
for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
3380-
cellname = PyUnicode_AS_UNICODE(
3381-
PyTuple_GET_ITEM(co->co_cellvars, i));
3382-
found = 0;
3383-
for (j = 0; j < nargs; j++) {
3384-
argname = PyUnicode_AS_UNICODE(
3385-
PyTuple_GET_ITEM(co->co_varnames, j));
3386-
if (Py_UNICODE_strcmp(cellname, argname) == 0) {
3387-
c = PyCell_New(GETLOCAL(j));
3388-
if (c == NULL)
3389-
goto fail;
3390-
GETLOCAL(co->co_nlocals + i) = c;
3391-
found = 1;
3392-
break;
3393-
}
3394-
}
3395-
if (found == 0) {
3396-
c = PyCell_New(NULL);
3397-
if (c == NULL)
3398-
goto fail;
3399-
SETLOCAL(co->co_nlocals + i, c);
3400-
}
3401-
}
3363+
int arg;
3364+
/* Possibly account for the cell variable being an argument. */
3365+
if (co->co_cell2arg != NULL &&
3366+
(arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG)
3367+
c = PyCell_New(GETLOCAL(arg));
3368+
else
3369+
c = PyCell_New(NULL);
3370+
if (c == NULL)
3371+
goto fail;
3372+
SETLOCAL(co->co_nlocals + i, c);
34023373
}
3403-
if (PyTuple_GET_SIZE(co->co_freevars)) {
3404-
int i;
3405-
for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
3406-
PyObject *o = PyTuple_GET_ITEM(closure, i);
3407-
Py_INCREF(o);
3408-
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
3409-
}
3374+
for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
3375+
PyObject *o = PyTuple_GET_ITEM(closure, i);
3376+
Py_INCREF(o);
3377+
freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
34103378
}
34113379

34123380
if (co->co_flags & CO_GENERATOR) {

0 commit comments

Comments
 (0)