Skip to content

Commit 8693744

Browse files
Sort the MAKE_CELL ops by oparg.
1 parent 09c3cdd commit 8693744

File tree

1 file changed

+102
-52
lines changed

1 file changed

+102
-52
lines changed

Python/compile.c

Lines changed: 102 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
* objects.
2222
*/
2323

24+
#include <stdbool.h>
25+
2426
#include "Python.h"
2527
#include "pycore_ast.h" // _PyAST_GetDocString()
2628
#include "pycore_compile.h" // _PyFuture_FromAST()
@@ -7377,6 +7379,39 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts);
73777379
static int
73787380
ensure_exits_have_lineno(struct compiler *c);
73797381

7382+
static int *
7383+
build_cellfixedoffsets(struct compiler *c)
7384+
{
7385+
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
7386+
int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
7387+
int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
7388+
7389+
int noffsets = ncellvars + nfreevars;
7390+
int *fixed = PyMem_New(int, noffsets);
7391+
if (fixed == NULL) {
7392+
PyErr_NoMemory();
7393+
return NULL;
7394+
}
7395+
for (int i = 0; i < noffsets; i++) {
7396+
fixed[i] = nlocals + i;
7397+
}
7398+
7399+
PyObject *varname, *cellindex;
7400+
Py_ssize_t pos = 0;
7401+
while (PyDict_Next(c->u->u_cellvars, &pos, &varname, &cellindex)) {
7402+
PyObject *varindex = PyDict_GetItem(c->u->u_varnames, varname);
7403+
if (varindex != NULL) {
7404+
assert(PyLong_AS_LONG(cellindex) < INT_MAX);
7405+
assert(PyLong_AS_LONG(varindex) < INT_MAX);
7406+
int oldindex = (int)PyLong_AS_LONG(cellindex);
7407+
int argoffset = (int)PyLong_AS_LONG(varindex);
7408+
fixed[oldindex] = argoffset;
7409+
}
7410+
}
7411+
7412+
return fixed;
7413+
}
7414+
73807415
static inline int
73817416
insert_instruction(basicblock *block, int pos, struct instr *instr) {
73827417
if (compiler_next_instr(block) < 0) {
@@ -7390,29 +7425,48 @@ insert_instruction(basicblock *block, int pos, struct instr *instr) {
73907425
}
73917426

73927427
static int
7393-
insert_prefix_instructions(struct compiler *c, basicblock *entryblock) {
7428+
insert_prefix_instructions(struct compiler *c, basicblock *entryblock,
7429+
int *fixed)
7430+
{
73947431

73957432
int flags = compute_code_flags(c);
73967433
if (flags < 0) {
73977434
return -1;
73987435
}
73997436

74007437
/* Set up cells for any variable that escapes, to be put in a closure. */
7401-
PyObject *k, *v;
7402-
Py_ssize_t pos = 0;
7403-
while (PyDict_Next(c->u->u_cellvars, &pos, &k, &v)) {
7404-
assert(PyLong_AS_LONG(v) < INT_MAX);
7405-
int cellindex = (int)PyLong_AS_LONG(v);
7406-
struct instr make_cell = {
7407-
.i_opcode = MAKE_CELL,
7408-
// This will get fixed in offset_derefs().
7409-
.i_oparg = cellindex,
7410-
.i_lineno = -1,
7411-
.i_target = NULL,
7412-
};
7413-
if (insert_instruction(entryblock, (int)(pos - 1), &make_cell) < 0) {
7438+
const int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
7439+
if (ncellvars) {
7440+
// c->u->u_cellvars has the cells out of order so we sort them
7441+
// before adding the MAKE_CELL instructions. Note that we
7442+
// adjust for arg cells, which come first.
7443+
const int nvars = ncellvars + (int)PyDict_GET_SIZE(c->u->u_varnames);
7444+
int *sorted = PyMem_RawCalloc(nvars, sizeof(int));
7445+
if (sorted == NULL) {
7446+
PyErr_NoMemory();
74147447
return -1;
74157448
}
7449+
for (int i = 0; i < ncellvars; i++) {
7450+
sorted[fixed[i]] = i + 1;
7451+
}
7452+
for (int i = 0, ncellsused = 0; ncellsused < ncellvars; i++) {
7453+
int oldindex = sorted[i] - 1;
7454+
if (oldindex == -1) {
7455+
continue;
7456+
}
7457+
struct instr make_cell = {
7458+
.i_opcode = MAKE_CELL,
7459+
// This will get fixed in offset_derefs().
7460+
.i_oparg = oldindex,
7461+
.i_lineno = -1,
7462+
.i_target = NULL,
7463+
};
7464+
if (insert_instruction(entryblock, ncellsused, &make_cell) < 0) {
7465+
return -1;
7466+
}
7467+
ncellsused += 1;
7468+
}
7469+
PyMem_RawFree(sorted);
74167470
}
74177471

74187472
/* Add the generator prefix instructions. */
@@ -7471,42 +7525,16 @@ guarantee_lineno_for_exits(struct assembler *a, int firstlineno) {
74717525
}
74727526

74737527
static int
7474-
fix_cell_offsets(struct compiler *c, basicblock *entryblock)
7528+
fix_cell_offsets(struct compiler *c, basicblock *entryblock, int *fixedmap)
74757529
{
7476-
assert(PyDict_GET_SIZE(c->u->u_varnames) < INT_MAX);
7477-
assert(PyDict_GET_SIZE(c->u->u_cellvars) < INT_MAX);
7478-
assert(PyDict_GET_SIZE(c->u->u_freevars) < INT_MAX);
74797530
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
74807531
int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
74817532
int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
7482-
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
7483-
int nlocalsplus = nlocals + ncellvars + nfreevars;
7533+
int noffsets = ncellvars + nfreevars;
74847534

7485-
int maxoldoffset = ncellvars + nfreevars;
7486-
int *fixedmap = PyMem_New(int, maxoldoffset + 1);
7487-
if (fixedmap == NULL) {
7488-
PyErr_NoMemory();
7489-
return -1;
7490-
}
7491-
for (int i = 0; i < maxoldoffset; i++) {
7492-
fixedmap[i] = nlocals + i;
7493-
}
7494-
7495-
// First map cell vars to args.
7496-
PyObject *varname, *cellindex;
7497-
Py_ssize_t pos = 0;
7498-
while (PyDict_Next(c->u->u_cellvars, &pos, &varname, &cellindex)) {
7499-
PyObject *varindex = PyDict_GetItem(c->u->u_varnames, varname);
7500-
if (varindex != NULL) {
7501-
assert(PyLong_AS_LONG(cellindex) < INT_MAX);
7502-
assert(PyLong_AS_LONG(varindex) < INT_MAX);
7503-
int oldindex = (int)PyLong_AS_LONG(cellindex);
7504-
int argoffset = (int)PyLong_AS_LONG(varindex);
7505-
fixedmap[oldindex] = argoffset;
7506-
}
7507-
}
7535+
// First deal with duplicates (arg cells).
75087536
int numdropped = 0;
7509-
for (int i = 0; i < maxoldoffset; i++) {
7537+
for (int i = 0; i < noffsets ; i++) {
75107538
if (fixedmap[i] == i + nlocals) {
75117539
fixedmap[i] -= numdropped;
75127540
}
@@ -7516,7 +7544,7 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock)
75167544
}
75177545
}
75187546

7519-
// Then update offsets, either relative to locals or by cell2var.
7547+
// Then update offsets, either relative to locals or by cell2arg.
75207548
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
75217549
for (int i = 0; i < b->b_iused; i++) {
75227550
struct instr *inst = &b->b_instr[i];
@@ -7530,13 +7558,15 @@ fix_cell_offsets(struct compiler *c, basicblock *entryblock)
75307558
case STORE_DEREF:
75317559
case DELETE_DEREF:
75327560
case LOAD_CLASSDEREF:
7561+
assert(oldoffset >= 0);
7562+
assert(oldoffset < noffsets);
7563+
assert(fixedmap[oldoffset] >= 0);
75337564
inst->i_oparg = fixedmap[oldoffset];
75347565
}
75357566
}
75367567
}
75377568

7538-
PyMem_Free(fixedmap);
7539-
return nlocalsplus - numdropped;
7569+
return numdropped;
75407570
}
75417571

75427572
static PyCodeObject *
@@ -7577,8 +7607,22 @@ assemble(struct compiler *c, int addNone)
75777607
}
75787608
assert(entryblock != NULL);
75797609

7610+
assert(PyDict_GET_SIZE(c->u->u_varnames) < INT_MAX);
7611+
assert(PyDict_GET_SIZE(c->u->u_cellvars) < INT_MAX);
7612+
assert(PyDict_GET_SIZE(c->u->u_freevars) < INT_MAX);
7613+
int nlocals = (int)PyDict_GET_SIZE(c->u->u_varnames);
7614+
int ncellvars = (int)PyDict_GET_SIZE(c->u->u_cellvars);
7615+
int nfreevars = (int)PyDict_GET_SIZE(c->u->u_freevars);
7616+
assert(INT_MAX - nlocals - ncellvars > 0);
7617+
assert(INT_MAX - nlocals - ncellvars - nfreevars > 0);
7618+
int nlocalsplus = nlocals + ncellvars + nfreevars;
7619+
int *cellfixedoffsets = build_cellfixedoffsets(c);
7620+
if (cellfixedoffsets == NULL) {
7621+
goto error;
7622+
}
7623+
75807624
// This must be called before fix_cell_offsets().
7581-
if (insert_prefix_instructions(c, entryblock)) {
7625+
if (insert_prefix_instructions(c, entryblock, cellfixedoffsets)) {
75827626
goto error;
75837627
}
75847628

@@ -7595,10 +7639,13 @@ assemble(struct compiler *c, int addNone)
75957639
a.a_entry = entryblock;
75967640
a.a_nblocks = nblocks;
75977641

7598-
int nlocalsplus = fix_cell_offsets(c, entryblock);
7599-
if (nlocalsplus < 0) {
7642+
int numdropped = fix_cell_offsets(c, entryblock, cellfixedoffsets);
7643+
PyMem_Free(cellfixedoffsets); // At this point we're done with it.
7644+
cellfixedoffsets = NULL;
7645+
if (numdropped < 0) {
76007646
goto error;
76017647
}
7648+
nlocalsplus -= numdropped;
76027649

76037650
consts = consts_dict_keys_inorder(c->u->u_consts);
76047651
if (consts == NULL) {
@@ -7639,10 +7686,10 @@ assemble(struct compiler *c, int addNone)
76397686
}
76407687

76417688
if (!assemble_exception_table(&a)) {
7642-
return 0;
7689+
goto error;
76437690
}
76447691
if (!assemble_line_range(&a)) {
7645-
return 0;
7692+
goto error;
76467693
}
76477694

76487695
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0) {
@@ -7667,6 +7714,9 @@ assemble(struct compiler *c, int addNone)
76677714
error:
76687715
Py_XDECREF(consts);
76697716
assemble_free(&a);
7717+
if (cellfixedoffsets != NULL) {
7718+
PyMem_Free(cellfixedoffsets);
7719+
}
76707720
return co;
76717721
}
76727722

0 commit comments

Comments
 (0)