mirror of
https://github.com/python/cpython.git
synced 2025-08-27 12:16:04 +00:00
gh-131738: optimize builtin any/all/tuple calls with a generator expression arg (#131737)
This commit is contained in:
parent
674dbf3b3a
commit
2c8f329dc6
16 changed files with 199 additions and 38 deletions
|
@ -1402,16 +1402,8 @@ dummy_func(
|
|||
|
||||
inst(LOAD_COMMON_CONSTANT, ( -- value)) {
|
||||
// Keep in sync with _common_constants in opcode.py
|
||||
// If we ever have more than two constants, use a lookup table
|
||||
PyObject *val;
|
||||
if (oparg == CONSTANT_ASSERTIONERROR) {
|
||||
val = PyExc_AssertionError;
|
||||
}
|
||||
else {
|
||||
assert(oparg == CONSTANT_NOTIMPLEMENTEDERROR);
|
||||
val = PyExc_NotImplementedError;
|
||||
}
|
||||
value = PyStackRef_FromPyObjectImmortal(val);
|
||||
assert(oparg < NUM_COMMON_CONSTANTS);
|
||||
value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
|
||||
}
|
||||
|
||||
inst(LOAD_BUILD_CLASS, ( -- bc)) {
|
||||
|
|
|
@ -3820,6 +3820,92 @@ update_start_location_to_match_attr(compiler *c, location loc,
|
|||
return loc;
|
||||
}
|
||||
|
||||
static int
|
||||
maybe_optimize_function_call(compiler *c, expr_ty e, jump_target_label end)
|
||||
{
|
||||
asdl_expr_seq *args = e->v.Call.args;
|
||||
asdl_keyword_seq *kwds = e->v.Call.keywords;
|
||||
expr_ty func = e->v.Call.func;
|
||||
|
||||
if (! (func->kind == Name_kind &&
|
||||
asdl_seq_LEN(args) == 1 &&
|
||||
asdl_seq_LEN(kwds) == 0 &&
|
||||
asdl_seq_GET(args, 0)->kind == GeneratorExp_kind))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
location loc = LOC(func);
|
||||
|
||||
int optimized = 0;
|
||||
NEW_JUMP_TARGET_LABEL(c, skip_optimization);
|
||||
|
||||
int const_oparg = -1;
|
||||
PyObject *initial_res = NULL;
|
||||
int continue_jump_opcode = -1;
|
||||
if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "all")) {
|
||||
const_oparg = CONSTANT_BUILTIN_ALL;
|
||||
initial_res = Py_True;
|
||||
continue_jump_opcode = POP_JUMP_IF_TRUE;
|
||||
}
|
||||
else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "any")) {
|
||||
const_oparg = CONSTANT_BUILTIN_ANY;
|
||||
initial_res = Py_False;
|
||||
continue_jump_opcode = POP_JUMP_IF_FALSE;
|
||||
}
|
||||
else if (_PyUnicode_EqualToASCIIString(func->v.Name.id, "tuple")) {
|
||||
const_oparg = CONSTANT_BUILTIN_TUPLE;
|
||||
}
|
||||
if (const_oparg != -1) {
|
||||
ADDOP_I(c, loc, COPY, 1); // the function
|
||||
ADDOP_I(c, loc, LOAD_COMMON_CONSTANT, const_oparg);
|
||||
ADDOP_COMPARE(c, loc, Is);
|
||||
ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, skip_optimization);
|
||||
ADDOP(c, loc, POP_TOP);
|
||||
|
||||
if (const_oparg == CONSTANT_BUILTIN_TUPLE) {
|
||||
ADDOP_I(c, loc, BUILD_LIST, 0);
|
||||
}
|
||||
expr_ty generator_exp = asdl_seq_GET(args, 0);
|
||||
VISIT(c, expr, generator_exp);
|
||||
|
||||
NEW_JUMP_TARGET_LABEL(c, loop);
|
||||
NEW_JUMP_TARGET_LABEL(c, cleanup);
|
||||
|
||||
USE_LABEL(c, loop);
|
||||
ADDOP_JUMP(c, loc, FOR_ITER, cleanup);
|
||||
if (const_oparg == CONSTANT_BUILTIN_TUPLE) {
|
||||
ADDOP_I(c, loc, LIST_APPEND, 2);
|
||||
ADDOP_JUMP(c, loc, JUMP, loop);
|
||||
}
|
||||
else {
|
||||
ADDOP(c, loc, TO_BOOL);
|
||||
ADDOP_JUMP(c, loc, continue_jump_opcode, loop);
|
||||
}
|
||||
|
||||
ADDOP(c, NO_LOCATION, POP_ITER);
|
||||
if (const_oparg != CONSTANT_BUILTIN_TUPLE) {
|
||||
ADDOP_LOAD_CONST(c, loc, initial_res == Py_True ? Py_False : Py_True);
|
||||
}
|
||||
ADDOP_JUMP(c, loc, JUMP, end);
|
||||
|
||||
USE_LABEL(c, cleanup);
|
||||
ADDOP(c, NO_LOCATION, END_FOR);
|
||||
ADDOP(c, NO_LOCATION, POP_ITER);
|
||||
if (const_oparg == CONSTANT_BUILTIN_TUPLE) {
|
||||
ADDOP_I(c, loc, CALL_INTRINSIC_1, INTRINSIC_LIST_TO_TUPLE);
|
||||
}
|
||||
else {
|
||||
ADDOP_LOAD_CONST(c, loc, initial_res);
|
||||
}
|
||||
|
||||
optimized = 1;
|
||||
ADDOP_JUMP(c, loc, JUMP, end);
|
||||
}
|
||||
USE_LABEL(c, skip_optimization);
|
||||
return optimized;
|
||||
}
|
||||
|
||||
// Return 1 if the method call was optimized, 0 if not, and -1 on error.
|
||||
static int
|
||||
maybe_optimize_method_call(compiler *c, expr_ty e)
|
||||
|
@ -3926,14 +4012,18 @@ codegen_call(compiler *c, expr_ty e)
|
|||
if (ret == 1) {
|
||||
return SUCCESS;
|
||||
}
|
||||
NEW_JUMP_TARGET_LABEL(c, skip_normal_call);
|
||||
RETURN_IF_ERROR(check_caller(c, e->v.Call.func));
|
||||
VISIT(c, expr, e->v.Call.func);
|
||||
RETURN_IF_ERROR(maybe_optimize_function_call(c, e, skip_normal_call));
|
||||
location loc = LOC(e->v.Call.func);
|
||||
ADDOP(c, loc, PUSH_NULL);
|
||||
loc = LOC(e);
|
||||
return codegen_call_helper(c, loc, 0,
|
||||
e->v.Call.args,
|
||||
e->v.Call.keywords);
|
||||
ret = codegen_call_helper(c, loc, 0,
|
||||
e->v.Call.args,
|
||||
e->v.Call.keywords);
|
||||
USE_LABEL(c, skip_normal_call);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
12
Python/executor_cases.c.h
generated
12
Python/executor_cases.c.h
generated
|
@ -1899,16 +1899,8 @@
|
|||
_PyStackRef value;
|
||||
oparg = CURRENT_OPARG();
|
||||
// Keep in sync with _common_constants in opcode.py
|
||||
// If we ever have more than two constants, use a lookup table
|
||||
PyObject *val;
|
||||
if (oparg == CONSTANT_ASSERTIONERROR) {
|
||||
val = PyExc_AssertionError;
|
||||
}
|
||||
else {
|
||||
assert(oparg == CONSTANT_NOTIMPLEMENTEDERROR);
|
||||
val = PyExc_NotImplementedError;
|
||||
}
|
||||
value = PyStackRef_FromPyObjectImmortal(val);
|
||||
assert(oparg < NUM_COMMON_CONSTANTS);
|
||||
value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
|
||||
stack_pointer[0] = value;
|
||||
stack_pointer += 1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
|
|
12
Python/generated_cases.c.h
generated
12
Python/generated_cases.c.h
generated
|
@ -8723,16 +8723,8 @@
|
|||
INSTRUCTION_STATS(LOAD_COMMON_CONSTANT);
|
||||
_PyStackRef value;
|
||||
// Keep in sync with _common_constants in opcode.py
|
||||
// If we ever have more than two constants, use a lookup table
|
||||
PyObject *val;
|
||||
if (oparg == CONSTANT_ASSERTIONERROR) {
|
||||
val = PyExc_AssertionError;
|
||||
}
|
||||
else {
|
||||
assert(oparg == CONSTANT_NOTIMPLEMENTEDERROR);
|
||||
val = PyExc_NotImplementedError;
|
||||
}
|
||||
value = PyStackRef_FromPyObjectImmortal(val);
|
||||
assert(oparg < NUM_COMMON_CONSTANTS);
|
||||
value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
|
||||
stack_pointer[0] = value;
|
||||
stack_pointer += 1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
|
|
|
@ -790,6 +790,26 @@ pycore_init_builtins(PyThreadState *tstate)
|
|||
}
|
||||
interp->callable_cache.len = len;
|
||||
|
||||
PyObject *all = PyDict_GetItemWithError(builtins_dict, &_Py_ID(all));
|
||||
if (!all) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
PyObject *any = PyDict_GetItemWithError(builtins_dict, &_Py_ID(any));
|
||||
if (!any) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
interp->common_consts[CONSTANT_ASSERTIONERROR] = PyExc_AssertionError;
|
||||
interp->common_consts[CONSTANT_NOTIMPLEMENTEDERROR] = PyExc_NotImplementedError;
|
||||
interp->common_consts[CONSTANT_BUILTIN_TUPLE] = (PyObject*)&PyTuple_Type;
|
||||
interp->common_consts[CONSTANT_BUILTIN_ALL] = all;
|
||||
interp->common_consts[CONSTANT_BUILTIN_ANY] = any;
|
||||
|
||||
for (int i=0; i < NUM_COMMON_CONSTANTS; i++) {
|
||||
assert(interp->common_consts[i] != NULL);
|
||||
}
|
||||
|
||||
PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append));
|
||||
if (list_append == NULL) {
|
||||
goto error;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue