mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-126835: Avoid creating unnecessary tuple when looking for constant sequence during constant folding (#131054)
This commit is contained in:
parent
b52866953a
commit
3618240624
3 changed files with 143 additions and 96 deletions
|
@ -14,6 +14,15 @@ extern "C" {
|
|||
#include "pycore_symtable.h" // _Py_SourceLocation
|
||||
#include "pycore_instruction_sequence.h"
|
||||
|
||||
/* A soft limit for stack use, to avoid excessive
|
||||
* memory use for large constants, etc.
|
||||
*
|
||||
* The value 30 is plucked out of thin air.
|
||||
* Code that could use more stack than this is
|
||||
* rare, so the exact value is unimportant.
|
||||
*/
|
||||
#define _PY_STACK_USE_GUIDELINE 30
|
||||
|
||||
struct _arena; // Type defined in pycore_pyarena.h
|
||||
struct _mod; // Type defined in pycore_ast.h
|
||||
|
||||
|
|
|
@ -37,15 +37,6 @@
|
|||
#define COMP_SETCOMP 2
|
||||
#define COMP_DICTCOMP 3
|
||||
|
||||
/* A soft limit for stack use, to avoid excessive
|
||||
* memory use for large constants, etc.
|
||||
*
|
||||
* The value 30 is plucked out of thin air.
|
||||
* Code that could use more stack than this is
|
||||
* rare, so the exact value is unimportant.
|
||||
*/
|
||||
#define STACK_USE_GUIDELINE 30
|
||||
|
||||
#undef SUCCESS
|
||||
#undef ERROR
|
||||
#define SUCCESS 0
|
||||
|
@ -3209,7 +3200,7 @@ starunpack_helper_impl(compiler *c, location loc,
|
|||
int build, int add, int extend, int tuple)
|
||||
{
|
||||
Py_ssize_t n = asdl_seq_LEN(elts);
|
||||
int big = n + pushed + (injected_arg ? 1 : 0) > STACK_USE_GUIDELINE;
|
||||
int big = n + pushed + (injected_arg ? 1 : 0) > _PY_STACK_USE_GUIDELINE;
|
||||
int seen_star = 0;
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
expr_ty elt = asdl_seq_GET(elts, i);
|
||||
|
@ -3364,7 +3355,7 @@ static int
|
|||
codegen_subdict(compiler *c, expr_ty e, Py_ssize_t begin, Py_ssize_t end)
|
||||
{
|
||||
Py_ssize_t i, n = end - begin;
|
||||
int big = n*2 > STACK_USE_GUIDELINE;
|
||||
int big = n*2 > _PY_STACK_USE_GUIDELINE;
|
||||
location loc = LOC(e);
|
||||
if (big) {
|
||||
ADDOP_I(c, loc, BUILD_MAP, 0);
|
||||
|
@ -3411,7 +3402,7 @@ codegen_dict(compiler *c, expr_ty e)
|
|||
ADDOP_I(c, loc, DICT_UPDATE, 1);
|
||||
}
|
||||
else {
|
||||
if (elements*2 > STACK_USE_GUIDELINE) {
|
||||
if (elements*2 > _PY_STACK_USE_GUIDELINE) {
|
||||
RETURN_IF_ERROR(codegen_subdict(c, e, i - elements, i + 1));
|
||||
if (have_dict) {
|
||||
ADDOP_I(c, loc, DICT_UPDATE, 1);
|
||||
|
@ -3752,7 +3743,7 @@ maybe_optimize_method_call(compiler *c, expr_ty e)
|
|||
/* Check that there aren't too many arguments */
|
||||
argsl = asdl_seq_LEN(args);
|
||||
kwdsl = asdl_seq_LEN(kwds);
|
||||
if (argsl + kwdsl + (kwdsl != 0) >= STACK_USE_GUIDELINE) {
|
||||
if (argsl + kwdsl + (kwdsl != 0) >= _PY_STACK_USE_GUIDELINE) {
|
||||
return 0;
|
||||
}
|
||||
/* Check that there are no *varargs types of arguments. */
|
||||
|
@ -3849,7 +3840,7 @@ codegen_joined_str(compiler *c, expr_ty e)
|
|||
{
|
||||
location loc = LOC(e);
|
||||
Py_ssize_t value_count = asdl_seq_LEN(e->v.JoinedStr.values);
|
||||
if (value_count > STACK_USE_GUIDELINE) {
|
||||
if (value_count > _PY_STACK_USE_GUIDELINE) {
|
||||
_Py_DECLARE_STR(empty, "");
|
||||
ADDOP_LOAD_CONST_NEW(c, loc, Py_NewRef(&_Py_STR(empty)));
|
||||
ADDOP_NAME(c, loc, LOAD_METHOD, &_Py_ID(join), names);
|
||||
|
@ -3928,7 +3919,7 @@ codegen_subkwargs(compiler *c, location loc,
|
|||
Py_ssize_t i, n = end - begin;
|
||||
keyword_ty kw;
|
||||
assert(n > 0);
|
||||
int big = n*2 > STACK_USE_GUIDELINE;
|
||||
int big = n*2 > _PY_STACK_USE_GUIDELINE;
|
||||
if (big) {
|
||||
ADDOP_I(c, NO_LOCATION, BUILD_MAP, 0);
|
||||
}
|
||||
|
@ -3981,7 +3972,7 @@ codegen_call_helper_impl(compiler *c, location loc,
|
|||
nelts = asdl_seq_LEN(args);
|
||||
nkwelts = asdl_seq_LEN(keywords);
|
||||
|
||||
if (nelts + nkwelts*2 > STACK_USE_GUIDELINE) {
|
||||
if (nelts + nkwelts*2 > _PY_STACK_USE_GUIDELINE) {
|
||||
goto ex_call;
|
||||
}
|
||||
for (i = 0; i < nelts; i++) {
|
||||
|
|
|
@ -1344,64 +1344,45 @@ add_const(PyObject *newconst, PyObject *consts, PyObject *const_cache)
|
|||
}
|
||||
|
||||
/*
|
||||
Walk basic block backwards starting from "start" trying to collect "size" number of
|
||||
subsequent constants from instructions loading constants into new tuple ignoring NOP's in between.
|
||||
Traverse the instructions of the basic block backwards from index "start", skipping over NOPs.
|
||||
Try to collect "size" number of consecutive instructions that load constants into the array "instrs".
|
||||
Caller must make sure that length of "instrs" is sufficient to fit in at least "size" instructions.
|
||||
|
||||
Returns ERROR on error and sets "seq" to NULL.
|
||||
Returns SUCCESS on success and sets "seq" to NULL if failed to collect requested number of constants.
|
||||
Returns SUCCESS on success and sets "seq" to resulting tuple if succeeded to collect requested number of constants.
|
||||
Return boolean indicating whether "size" such instructions were found.
|
||||
*/
|
||||
static int
|
||||
get_constant_sequence(basicblock *bb, int start, int size,
|
||||
PyObject *consts, PyObject **seq)
|
||||
static bool
|
||||
get_const_loading_instrs(basicblock *bb, int start, cfg_instr **instrs, int size)
|
||||
{
|
||||
assert(start < bb->b_iused);
|
||||
*seq = NULL;
|
||||
PyObject *res = PyTuple_New((Py_ssize_t)size);
|
||||
if (res == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
assert(size >= 0);
|
||||
assert(size <= _PY_STACK_USE_GUIDELINE);
|
||||
|
||||
for (; start >= 0 && size > 0; start--) {
|
||||
cfg_instr *instr = &bb->b_instr[start];
|
||||
if (instr->i_opcode == NOP) {
|
||||
continue;
|
||||
}
|
||||
if (!loads_const(instr->i_opcode)) {
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts);
|
||||
if (constant == NULL) {
|
||||
Py_DECREF(res);
|
||||
return ERROR;
|
||||
}
|
||||
PyTuple_SET_ITEM(res, --size, constant);
|
||||
instrs[--size] = instr;
|
||||
}
|
||||
if (size > 0) {
|
||||
Py_DECREF(res);
|
||||
}
|
||||
else {
|
||||
*seq = res;
|
||||
}
|
||||
return SUCCESS;
|
||||
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Walk basic block backwards starting from "start" and change "count" number of
|
||||
non-NOP instructions to NOP's and set their location to NO_LOCATION.
|
||||
Change every instruction in "instrs" NOP and set its location to NO_LOCATION.
|
||||
Caller must make sure "instrs" has at least "size" elements.
|
||||
*/
|
||||
static void
|
||||
nop_out(basicblock *bb, int start, int count)
|
||||
nop_out(cfg_instr **instrs, int size)
|
||||
{
|
||||
assert(start < bb->b_iused);
|
||||
for (; count > 0; start--) {
|
||||
assert(start >= 0);
|
||||
cfg_instr *instr = &bb->b_instr[start];
|
||||
if (instr->i_opcode == NOP) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
cfg_instr *instr = instrs[i];
|
||||
assert(instr->i_opcode != NOP);
|
||||
INSTR_SET_OP0(instr, NOP);
|
||||
INSTR_SET_LOC(instr, NO_LOCATION);
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1437,19 +1418,39 @@ fold_tuple_of_constants(basicblock *bb, int i, PyObject *consts, PyObject *const
|
|||
/* Pre-conditions */
|
||||
assert(PyDict_CheckExact(const_cache));
|
||||
assert(PyList_CheckExact(consts));
|
||||
|
||||
cfg_instr *instr = &bb->b_instr[i];
|
||||
assert(instr->i_opcode == BUILD_TUPLE);
|
||||
|
||||
int seq_size = instr->i_oparg;
|
||||
PyObject *newconst;
|
||||
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, seq_size, consts, &newconst));
|
||||
if (newconst == NULL) {
|
||||
if (seq_size > _PY_STACK_USE_GUIDELINE) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
cfg_instr *const_instrs[_PY_STACK_USE_GUIDELINE];
|
||||
if (!get_const_loading_instrs(bb, i-1, const_instrs, seq_size)) {
|
||||
/* not a const sequence */
|
||||
return SUCCESS;
|
||||
}
|
||||
assert(PyTuple_Size(newconst) == seq_size);
|
||||
RETURN_IF_ERROR(instr_make_load_const(instr, newconst, consts, const_cache));
|
||||
nop_out(bb, i-1, seq_size);
|
||||
return SUCCESS;
|
||||
|
||||
PyObject *const_tuple = PyTuple_New((Py_ssize_t)seq_size);
|
||||
if (const_tuple == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
for (int i = 0; i < seq_size; i++) {
|
||||
cfg_instr *inst = const_instrs[i];
|
||||
assert(loads_const(inst->i_opcode));
|
||||
PyObject *element = get_const_value(inst->i_opcode, inst->i_oparg, consts);
|
||||
if (element == NULL) {
|
||||
Py_DECREF(const_tuple);
|
||||
return ERROR;
|
||||
}
|
||||
PyTuple_SET_ITEM(const_tuple, i, element);
|
||||
}
|
||||
|
||||
nop_out(const_instrs, seq_size);
|
||||
return instr_make_load_const(instr, const_tuple, consts, const_cache);
|
||||
}
|
||||
|
||||
#define MIN_CONST_SEQUENCE_SIZE 3
|
||||
|
@ -1470,34 +1471,56 @@ optimize_lists_and_sets(basicblock *bb, int i, int nextop,
|
|||
{
|
||||
assert(PyDict_CheckExact(const_cache));
|
||||
assert(PyList_CheckExact(consts));
|
||||
|
||||
cfg_instr *instr = &bb->b_instr[i];
|
||||
assert(instr->i_opcode == BUILD_LIST || instr->i_opcode == BUILD_SET);
|
||||
|
||||
bool contains_or_iter = nextop == GET_ITER || nextop == CONTAINS_OP;
|
||||
int seq_size = instr->i_oparg;
|
||||
if (seq_size < MIN_CONST_SEQUENCE_SIZE && !contains_or_iter) {
|
||||
if (seq_size > _PY_STACK_USE_GUIDELINE ||
|
||||
(seq_size < MIN_CONST_SEQUENCE_SIZE && !contains_or_iter))
|
||||
{
|
||||
return SUCCESS;
|
||||
}
|
||||
PyObject *newconst;
|
||||
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, seq_size, consts, &newconst));
|
||||
if (newconst == NULL) { /* not a const sequence */
|
||||
|
||||
cfg_instr *const_instrs[_PY_STACK_USE_GUIDELINE];
|
||||
if (!get_const_loading_instrs(bb, i-1, const_instrs, seq_size)) { /* not a const sequence */
|
||||
if (contains_or_iter && instr->i_opcode == BUILD_LIST) {
|
||||
/* iterate over a tuple instead of list */
|
||||
INSTR_SET_OP1(instr, BUILD_TUPLE, instr->i_oparg);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
assert(PyTuple_Size(newconst) == seq_size);
|
||||
if (instr->i_opcode == BUILD_SET) {
|
||||
PyObject *frozenset = PyFrozenSet_New(newconst);
|
||||
if (frozenset == NULL) {
|
||||
Py_DECREF(newconst);
|
||||
|
||||
PyObject *const_result = PyTuple_New((Py_ssize_t)seq_size);
|
||||
if (const_result == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
for (int i = 0; i < seq_size; i++) {
|
||||
cfg_instr *inst = const_instrs[i];
|
||||
assert(loads_const(inst->i_opcode));
|
||||
PyObject *element = get_const_value(inst->i_opcode, inst->i_oparg, consts);
|
||||
if (element == NULL) {
|
||||
Py_DECREF(const_result);
|
||||
return ERROR;
|
||||
}
|
||||
Py_SETREF(newconst, frozenset);
|
||||
PyTuple_SET_ITEM(const_result, i, element);
|
||||
}
|
||||
int index = add_const(newconst, consts, const_cache);
|
||||
|
||||
if (instr->i_opcode == BUILD_SET) {
|
||||
PyObject *frozenset = PyFrozenSet_New(const_result);
|
||||
if (frozenset == NULL) {
|
||||
Py_DECREF(const_result);
|
||||
return ERROR;
|
||||
}
|
||||
Py_SETREF(const_result, frozenset);
|
||||
}
|
||||
|
||||
int index = add_const(const_result, consts, const_cache);
|
||||
RETURN_IF_ERROR(index);
|
||||
nop_out(bb, i-1, seq_size);
|
||||
nop_out(const_instrs, seq_size);
|
||||
|
||||
if (contains_or_iter) {
|
||||
INSTR_SET_OP1(instr, LOAD_CONST, index);
|
||||
}
|
||||
|
@ -1696,19 +1719,34 @@ fold_const_binop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache)
|
|||
#define BINOP_OPERAND_COUNT 2
|
||||
assert(PyDict_CheckExact(const_cache));
|
||||
assert(PyList_CheckExact(consts));
|
||||
|
||||
cfg_instr *binop = &bb->b_instr[i];
|
||||
assert(binop->i_opcode == BINARY_OP);
|
||||
PyObject *pair;
|
||||
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, BINOP_OPERAND_COUNT, consts, &pair));
|
||||
if (pair == NULL) {
|
||||
|
||||
cfg_instr *operands_instrs[BINOP_OPERAND_COUNT];
|
||||
if (!get_const_loading_instrs(bb, i-1, operands_instrs, BINOP_OPERAND_COUNT)) {
|
||||
/* not a const sequence */
|
||||
return SUCCESS;
|
||||
}
|
||||
assert(PyTuple_Size(pair) == BINOP_OPERAND_COUNT);
|
||||
PyObject *left = PyTuple_GET_ITEM(pair, 0);
|
||||
PyObject *right = PyTuple_GET_ITEM(pair, 1);
|
||||
PyObject *newconst = eval_const_binop(left, binop->i_oparg, right);
|
||||
Py_DECREF(pair);
|
||||
|
||||
cfg_instr *lhs_instr = operands_instrs[0];
|
||||
assert(loads_const(lhs_instr->i_opcode));
|
||||
PyObject *lhs = get_const_value(lhs_instr->i_opcode, lhs_instr->i_oparg, consts);
|
||||
if (lhs == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
cfg_instr *rhs_instr = operands_instrs[1];
|
||||
assert(loads_const(rhs_instr->i_opcode));
|
||||
PyObject *rhs = get_const_value(rhs_instr->i_opcode, rhs_instr->i_oparg, consts);
|
||||
if (rhs == NULL) {
|
||||
Py_DECREF(lhs);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
PyObject *newconst = eval_const_binop(lhs, binop->i_oparg, rhs);
|
||||
Py_DECREF(lhs);
|
||||
Py_DECREF(rhs);
|
||||
if (newconst == NULL) {
|
||||
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
||||
return ERROR;
|
||||
|
@ -1716,9 +1754,9 @@ fold_const_binop(basicblock *bb, int i, PyObject *consts, PyObject *const_cache)
|
|||
PyErr_Clear();
|
||||
return SUCCESS;
|
||||
}
|
||||
RETURN_IF_ERROR(instr_make_load_const(binop, newconst, consts, const_cache));
|
||||
nop_out(bb, i-1, BINOP_OPERAND_COUNT);
|
||||
return SUCCESS;
|
||||
|
||||
nop_out(operands_instrs, BINOP_OPERAND_COUNT);
|
||||
return instr_make_load_const(binop, newconst, consts, const_cache);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
@ -1765,17 +1803,26 @@ fold_const_unaryop(basicblock *bb, int i, PyObject *consts, PyObject *const_cach
|
|||
#define UNARYOP_OPERAND_COUNT 1
|
||||
assert(PyDict_CheckExact(const_cache));
|
||||
assert(PyList_CheckExact(consts));
|
||||
cfg_instr *instr = &bb->b_instr[i];
|
||||
PyObject *seq;
|
||||
RETURN_IF_ERROR(get_constant_sequence(bb, i-1, UNARYOP_OPERAND_COUNT, consts, &seq));
|
||||
if (seq == NULL) {
|
||||
cfg_instr *unaryop = &bb->b_instr[i];
|
||||
|
||||
cfg_instr *operand_instr;
|
||||
if (!get_const_loading_instrs(bb, i-1, &operand_instr, UNARYOP_OPERAND_COUNT)) {
|
||||
/* not a const */
|
||||
return SUCCESS;
|
||||
}
|
||||
assert(PyTuple_Size(seq) == UNARYOP_OPERAND_COUNT);
|
||||
PyObject *operand = PyTuple_GET_ITEM(seq, 0);
|
||||
PyObject *newconst = eval_const_unaryop(operand, instr->i_opcode, instr->i_oparg);
|
||||
Py_DECREF(seq);
|
||||
|
||||
assert(loads_const(operand_instr->i_opcode));
|
||||
PyObject *operand = get_const_value(
|
||||
operand_instr->i_opcode,
|
||||
operand_instr->i_oparg,
|
||||
consts
|
||||
);
|
||||
if (operand == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
PyObject *newconst = eval_const_unaryop(operand, unaryop->i_opcode, unaryop->i_oparg);
|
||||
Py_DECREF(operand);
|
||||
if (newconst == NULL) {
|
||||
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
||||
return ERROR;
|
||||
|
@ -1783,12 +1830,12 @@ fold_const_unaryop(basicblock *bb, int i, PyObject *consts, PyObject *const_cach
|
|||
PyErr_Clear();
|
||||
return SUCCESS;
|
||||
}
|
||||
if (instr->i_opcode == UNARY_NOT) {
|
||||
|
||||
if (unaryop->i_opcode == UNARY_NOT) {
|
||||
assert(PyBool_Check(newconst));
|
||||
}
|
||||
RETURN_IF_ERROR(instr_make_load_const(instr, newconst, consts, const_cache));
|
||||
nop_out(bb, i-1, UNARYOP_OPERAND_COUNT);
|
||||
return SUCCESS;
|
||||
nop_out(&operand_instr, UNARYOP_OPERAND_COUNT);
|
||||
return instr_make_load_const(unaryop, newconst, consts, const_cache);
|
||||
}
|
||||
|
||||
#define VISITED (-1)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue