diff --git a/Include/internal/pycore_compile.h b/Include/internal/pycore_compile.h index 9f0ca33892a43b..b61ac8fccdca14 100644 --- a/Include/internal/pycore_compile.h +++ b/Include/internal/pycore_compile.h @@ -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 diff --git a/Python/codegen.c b/Python/codegen.c index 908711250d14bc..6ef3c63b33530e 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -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++) { diff --git a/Python/flowgraph.c b/Python/flowgraph.c index 286f8999902e32..fdafafd76617a8 100644 --- a/Python/flowgraph.c +++ b/Python/flowgraph.c @@ -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; - } - PyObject *constant = get_const_value(instr->i_opcode, instr->i_oparg, consts); - if (constant == NULL) { - Py_DECREF(res); - return ERROR; + return false; } - 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); + + 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; + } + PyTuple_SET_ITEM(const_result, i, element); + } + if (instr->i_opcode == BUILD_SET) { - PyObject *frozenset = PyFrozenSet_New(newconst); + PyObject *frozenset = PyFrozenSet_New(const_result); if (frozenset == NULL) { - Py_DECREF(newconst); + Py_DECREF(const_result); return ERROR; } - Py_SETREF(newconst, frozenset); + Py_SETREF(const_result, frozenset); } - int index = add_const(newconst, consts, const_cache); + + 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)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: