Skip to content

Commit f42824e

Browse files
committed
avoid creating unnecessary tuple when looking for constant sequence
1 parent 5186de3 commit f42824e

File tree

1 file changed

+119
-74
lines changed

1 file changed

+119
-74
lines changed

Python/flowgraph.c

Lines changed: 119 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,63 +1345,42 @@ add_const(PyObject *newconst, PyObject *consts, PyObject *const_cache)
13451345

13461346
/*
13471347
Walk basic block backwards starting from "start" trying to collect "size" number of
1348-
subsequent constants from instructions loading constants into new tuple ignoring NOP's in between.
1348+
subsequent instructions that load constants into instruciton array "seq" ignoring NOP's in between.
1349+
Caller must make sure that length of "seq" is sufficient to fit in at least "size" instructions.
13491350
1350-
Returns ERROR on error and sets "seq" to NULL.
1351-
Returns SUCCESS on success and sets "seq" to NULL if failed to collect requested number of constants.
1352-
Returns SUCCESS on success and sets "seq" to resulting tuple if succeeded to collect requested number of constants.
1351+
Returns boolean indicating whether succeeded to collect requested number of instructions.
13531352
*/
1354-
static int
1355-
get_constant_sequence(basicblock *bb, int start, int size,
1356-
PyObject *consts, PyObject **seq)
1353+
static bool
1354+
get_const_sequence_instructions(basicblock *bb, int start, cfg_instr **seq, int size)
13571355
{
13581356
assert(start < bb->b_iused);
1359-
*seq = NULL;
1360-
PyObject *res = PyTuple_New((Py_ssize_t)size);
1361-
if (res == NULL) {
1362-
return ERROR;
1363-
}
1357+
assert(size >= 0);
1358+
assert(size <= STACK_USE_GUIDELINE);
1359+
13641360
for (; start >= 0 && size > 0; start--) {
13651361
cfg_instr *instr = &bb->b_instr[start];
13661362
if (instr->i_opcode == NOP) {
13671363
continue;
13681364
}
13691365
if (!loads_const(instr->i_opcode)) {
1370-
break;
1371-
}
1372-
PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts);
1373-
if (constant == NULL) {
1374-
Py_DECREF(res);
1375-
return ERROR;
1366+
return false;
13761367
}
1377-
PyTuple_SET_ITEM(res, --size, constant);
1368+
seq[--size] = instr;
13781369
}
1379-
if (size > 0) {
1380-
Py_DECREF(res);
1381-
}
1382-
else {
1383-
*seq = res;
1384-
}
1385-
return SUCCESS;
1370+
1371+
return size == 0;
13861372
}
13871373

13881374
/*
1389-
Walk basic block backwards starting from "start" and change "count" number of
1390-
non-NOP instructions to NOP's and set their location to NO_LOCATION.
1375+
Change every instruction in "instrs" NOP and set it's location to NO_LOCATION.
1376+
Caller must make sure "instrs" has at least "size" elements.
13911377
*/
1392-
static void
1393-
nop_out(basicblock *bb, int start, int count)
1394-
{
1395-
assert(start < bb->b_iused);
1396-
for (; count > 0; start--) {
1397-
assert(start >= 0);
1398-
cfg_instr *instr = &bb->b_instr[start];
1399-
if (instr->i_opcode == NOP) {
1400-
continue;
1401-
}
1378+
static void nop_out(basicblock *bb, cfg_instr **instrs, int size) {
1379+
for (int i = 0; i < size; i++) {
1380+
cfg_instr *instr = instrs[i];
1381+
assert(instr->i_opcode != NOP);
14021382
INSTR_SET_OP0(instr, NOP);
14031383
INSTR_SET_LOC(instr, NO_LOCATION);
1404-
count--;
14051384
}
14061385
}
14071386

@@ -1437,19 +1416,39 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const
14371416
/* Pre-conditions */
14381417
assert(PyDict_CheckExact(const_cache));
14391418
assert(PyList_CheckExact(consts));
1419+
14401420
cfg_instr *instr = &bb->b_instr[i];
14411421
assert(instr->i_opcode == BUILD_TUPLE);
1422+
14421423
int seq_size = instr->i_oparg;
1443-
PyObject *newconst;
1444-
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, seq_size, consts, &newconst));
1445-
if (newconst == NULL) {
1424+
if (seq_size > STACK_USE_GUIDELINE) {
1425+
return SUCCESS;
1426+
}
1427+
1428+
cfg_instr *seq[STACK_USE_GUIDELINE];
1429+
if (!get_const_sequence_instructions(bb, i-1, seq, seq_size)) {
14461430
/* not a const sequence */
14471431
return SUCCESS;
14481432
}
1449-
assert(PyTuple_Size(newconst) == seq_size);
1450-
RETURN_IF_ERROR(instr_make_load_const(instr, newconst, consts, const_cache));
1451-
nop_out(bb, i-1, seq_size);
1452-
return SUCCESS;
1433+
1434+
PyObject *newconst = PyTuple_New((Py_ssize_t)seq_size);
1435+
if (newconst == NULL) {
1436+
return ERROR;
1437+
}
1438+
1439+
for (int i = 0; i < seq_size; i++) {
1440+
cfg_instr *inst = seq[i];
1441+
assert(loads_const(inst->i_opcode));
1442+
PyObject *constant = get_const_value(inst->i_opcode, inst->i_oparg, consts);
1443+
if (constant == NULL) {
1444+
Py_DECREF(newconst);
1445+
return ERROR;
1446+
}
1447+
PyTuple_SET_ITEM(newconst, i, constant);
1448+
}
1449+
1450+
nop_out(bb, seq, seq_size);
1451+
return instr_make_load_const(instr, newconst, consts, const_cache);
14531452
}
14541453

14551454
#define MIN_CONST_SEQUENCE_SIZE 3
@@ -1470,23 +1469,43 @@ optimize_lists_and_sets(basicblock *bb, int i, int nextop,
14701469
{
14711470
assert(PyDict_CheckExact(const_cache));
14721471
assert(PyList_CheckExact(consts));
1472+
14731473
cfg_instr *instr = &bb->b_instr[i];
14741474
assert(instr->i_opcode == BUILD_LIST || instr->i_opcode == BUILD_SET);
1475+
14751476
bool contains_or_iter = nextop == GET_ITER || nextop == CONTAINS_OP;
14761477
int seq_size = instr->i_oparg;
1477-
if (seq_size < MIN_CONST_SEQUENCE_SIZE && !contains_or_iter) {
1478+
if (seq_size > STACK_USE_GUIDELINE ||
1479+
(seq_size < MIN_CONST_SEQUENCE_SIZE && !contains_or_iter))
1480+
{
14781481
return SUCCESS;
14791482
}
1480-
PyObject *newconst;
1481-
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, seq_size, consts, &newconst));
1482-
if (newconst == NULL) { /* not a const sequence */
1483+
1484+
cfg_instr *seq[STACK_USE_GUIDELINE];
1485+
if (!get_const_sequence_instructions(bb, i-1, seq, seq_size)) { /* not a const sequence */
14831486
if (contains_or_iter && instr->i_opcode == BUILD_LIST) {
14841487
/* iterate over a tuple instead of list */
14851488
INSTR_SET_OP1(instr, BUILD_TUPLE, instr->i_oparg);
14861489
}
14871490
return SUCCESS;
14881491
}
1489-
assert(PyTuple_Size(newconst) == seq_size);
1492+
1493+
PyObject *newconst = PyTuple_New((Py_ssize_t)seq_size);
1494+
if (newconst == NULL) {
1495+
return ERROR;
1496+
}
1497+
1498+
for (int i = 0; i < seq_size; i++) {
1499+
cfg_instr *inst = seq[i];
1500+
assert(loads_const(inst->i_opcode));
1501+
PyObject *constant = get_const_value(inst->i_opcode, inst->i_oparg, consts);
1502+
if (constant == NULL) {
1503+
Py_DECREF(newconst);
1504+
return ERROR;
1505+
}
1506+
PyTuple_SET_ITEM(newconst, i, constant);
1507+
}
1508+
14901509
if (instr->i_opcode == BUILD_SET) {
14911510
PyObject *frozenset = PyFrozenSet_New(newconst);
14921511
if (frozenset == NULL) {
@@ -1495,9 +1514,11 @@ optimize_lists_and_sets(basicblock *bb, int i, int nextop,
14951514
}
14961515
Py_SETREF(newconst, frozenset);
14971516
}
1517+
14981518
int index = add_const(newconst, consts, const_cache);
14991519
RETURN_IF_ERROR(index);
1500-
nop_out(bb, i-1, seq_size);
1520+
nop_out(bb, seq, seq_size);
1521+
15011522
if (contains_or_iter) {
15021523
INSTR_SET_OP1(instr, LOAD_CONST, index);
15031524
}
@@ -1696,29 +1717,44 @@ fold_const_binop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache)
16961717
#define BINOP_OPERAND_COUNT 2
16971718
assert(PyDict_CheckExact(const_cache));
16981719
assert(PyList_CheckExact(consts));
1720+
16991721
cfg_instr *binop = &bb->b_instr[i];
17001722
assert(binop->i_opcode == BINARY_OP);
1701-
PyObject *pair;
1702-
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, BINOP_OPERAND_COUNT, consts, &pair));
1703-
if (pair == NULL) {
1723+
1724+
cfg_instr *seq[BINOP_OPERAND_COUNT];
1725+
if (!get_const_sequence_instructions(bb, i-1, seq, BINOP_OPERAND_COUNT)) {
17041726
/* not a const sequence */
17051727
return SUCCESS;
17061728
}
1707-
assert(PyTuple_Size(pair) == BINOP_OPERAND_COUNT);
1708-
PyObject *left = PyTuple_GET_ITEM(pair, 0);
1709-
PyObject *right = PyTuple_GET_ITEM(pair, 1);
1729+
1730+
cfg_instr *first = seq[0];
1731+
assert(loads_const(first->i_opcode));
1732+
PyObject *left = get_const_value(first->i_opcode, first->i_oparg, consts);
1733+
if (left == NULL) {
1734+
return ERROR;
1735+
}
1736+
1737+
cfg_instr *second = seq[1];
1738+
assert(loads_const(second->i_opcode));
1739+
PyObject *right = get_const_value(second->i_opcode, second->i_oparg, consts);
1740+
if (right == NULL) {
1741+
Py_DECREF(left);
1742+
return ERROR;
1743+
}
1744+
17101745
PyObject *newconst = eval_const_binop(left, binop->i_oparg, right);
1711-
Py_DECREF(pair);
1746+
Py_DECREF(left);
1747+
Py_DECREF(right);
17121748
if (newconst == NULL) {
17131749
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
17141750
return ERROR;
17151751
}
17161752
PyErr_Clear();
17171753
return SUCCESS;
17181754
}
1719-
RETURN_IF_ERROR(instr_make_load_const(binop, newconst, consts, const_cache));
1720-
nop_out(bb, i-1, BINOP_OPERAND_COUNT);
1721-
return SUCCESS;
1755+
1756+
nop_out(bb, seq, BINOP_OPERAND_COUNT);
1757+
return instr_make_load_const(binop, newconst, consts, const_cache);
17221758
}
17231759

17241760
static PyObject *
@@ -1765,30 +1801,39 @@ fold_const_unaryop(basicblock *bb, int i, PyObject *consts, PyObject *const_cach
17651801
#define UNARYOP_OPERAND_COUNT 1
17661802
assert(PyDict_CheckExact(const_cache));
17671803
assert(PyList_CheckExact(consts));
1768-
cfg_instr *instr = &bb->b_instr[i];
1769-
PyObject *seq;
1770-
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, UNARYOP_OPERAND_COUNT, consts, &seq));
1771-
if (seq == NULL) {
1804+
cfg_instr *unaryop = &bb->b_instr[i];
1805+
1806+
cfg_instr *instr;
1807+
if (!get_const_sequence_instructions(bb, i - 1, &instr, UNARYOP_OPERAND_COUNT)) {
17721808
/* not a const */
17731809
return SUCCESS;
17741810
}
1775-
assert(PyTuple_Size(seq) == UNARYOP_OPERAND_COUNT);
1776-
PyObject *operand = PyTuple_GET_ITEM(seq, 0);
1777-
PyObject *newconst = eval_const_unaryop(operand, instr->i_opcode, instr->i_oparg);
1778-
Py_DECREF(seq);
1811+
1812+
assert(loads_const(instr->i_opcode));
1813+
PyObject *operand = get_const_value(
1814+
instr->i_opcode,
1815+
instr->i_oparg,
1816+
consts
1817+
);
1818+
if (operand == NULL) {
1819+
return ERROR;
1820+
}
1821+
1822+
PyObject *newconst = eval_const_unaryop(operand, unaryop->i_opcode, unaryop->i_oparg);
1823+
Py_DECREF(operand);
17791824
if (newconst == NULL) {
17801825
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
17811826
return ERROR;
17821827
}
17831828
PyErr_Clear();
17841829
return SUCCESS;
17851830
}
1786-
if (instr->i_opcode == UNARY_NOT) {
1831+
1832+
if (unaryop->i_opcode == UNARY_NOT) {
17871833
assert(PyBool_Check(newconst));
17881834
}
1789-
RETURN_IF_ERROR(instr_make_load_const(instr, newconst, consts, const_cache));
1790-
nop_out(bb, i-1, UNARYOP_OPERAND_COUNT);
1791-
return SUCCESS;
1835+
nop_out(bb, &instr, UNARYOP_OPERAND_COUNT);
1836+
return instr_make_load_const(unaryop, newconst, consts, const_cache);
17921837
}
17931838

17941839
#define VISITED (-1)

0 commit comments

Comments
 (0)