summaryrefslogtreecommitdiff
path: root/Python
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-12-14 18:22:44 +0000
committerGitHub <noreply@github.com>2021-12-14 18:22:44 +0000
commit9f8f45144b6f0ad481e80570538cce89b414f7f9 (patch)
treebde67ec27794633c1e58d7349be4bf16687a1949 /Python
parentd60457a6673cf0263213c2f2be02c633ec2e2038 (diff)
downloadcpython-git-9f8f45144b6f0ad481e80570538cce89b414f7f9.tar.gz
bpo-44525: Split calls into PRECALL and CALL (GH-30011)
* Add 3 new opcodes for calls: PRECALL_METHOD, CALL_NO_KW, CALL_KW. * Update specialization to handle new CALL opcodes. * Specialize call to method descriptors. * Remove old CALL opcodes: CALL_FUNCTION, CALL_METHOD, CALL_METHOD_KW, CALL_FUNCTION_KW.
Diffstat (limited to 'Python')
-rw-r--r--Python/ceval.c216
-rw-r--r--Python/compile.c31
-rw-r--r--Python/opcode_targets.h54
-rw-r--r--Python/specialize.c99
4 files changed, 280 insertions, 120 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index fb19f78ba1..6d2784894f 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1673,6 +1673,22 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
CFrame cframe;
+ /* Variables used for making calls */
+ PyObject *kwnames;
+ int nargs;
+ /*
+ * It is only between a PRECALL_METHOD instruction and the following instruction,
+ * that these two values can be anything other than their defaults. */
+ int postcall_shrink = 1;
+ int extra_args = 0;
+#define RESET_STACK_ADJUST_FOR_CALLS \
+ do { \
+ postcall_shrink = 1; \
+ extra_args = 0; \
+ } while (0)
+#define STACK_ADJUST_IS_RESET \
+ (postcall_shrink == 1 && extra_args == 0)
+
/* WARNING: Because the CFrame lives on the C stack,
* but can be accessed from a heap allocated object (tstate)
* strict stack discipline must be maintained.
@@ -1815,11 +1831,6 @@ check_eval_breaker:
switch (opcode) {
#endif
- /* Variables used for making calls */
- PyObject *kwnames;
- int nargs;
- int postcall_shrink;
-
/* BEWARE!
It is essential that any operation that fails must goto error
and that all operation that succeed call DISPATCH() ! */
@@ -4319,7 +4330,7 @@ check_eval_breaker:
if (iter == NULL)
goto error;
PREDICT(FOR_ITER);
- PREDICT(CALL_FUNCTION);
+ PREDICT(CALL_NO_KW);
DISPATCH();
}
@@ -4661,7 +4672,7 @@ check_eval_breaker:
DISPATCH();
}
- TARGET(CALL_METHOD) {
+ TARGET(PRECALL_METHOD) {
/* Designed to work in tamdem with LOAD_METHOD. */
/* `meth` is NULL when LOAD_METHOD thinks that it's not
a method call.
@@ -4689,38 +4700,26 @@ check_eval_breaker:
make it accept the `self` as a first argument.
*/
int is_method = (PEEK(oparg + 2) != NULL);
- oparg += is_method;
- nargs = oparg;
- kwnames = NULL;
+ extra_args = is_method;
postcall_shrink = 2-is_method;
- goto call_function;
- }
-
- TARGET(CALL_METHOD_KW) {
- /* Designed to work in tandem with LOAD_METHOD. Same as CALL_METHOD
- but pops TOS to get a tuple of keyword names. */
- kwnames = POP();
- int is_method = (PEEK(oparg + 2) != NULL);
- oparg += is_method;
- nargs = oparg - (int)PyTuple_GET_SIZE(kwnames);
- postcall_shrink = 2-is_method;
- goto call_function;
+ DISPATCH();
}
- TARGET(CALL_FUNCTION_KW) {
+ TARGET(CALL_KW) {
kwnames = POP();
+ oparg += extra_args;
+ extra_args = 0;
nargs = oparg - (int)PyTuple_GET_SIZE(kwnames);
- postcall_shrink = 1;
goto call_function;
}
- TARGET(CALL_FUNCTION) {
- PREDICTED(CALL_FUNCTION);
- STAT_INC(CALL_FUNCTION, unquickened);
+ TARGET(CALL_NO_KW) {
PyObject *function;
- nargs = oparg;
+ PREDICTED(CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, unquickened);
kwnames = NULL;
- postcall_shrink = 1;
+ oparg += extra_args;
+ nargs = oparg;
call_function:
function = PEEK(oparg + 1);
if (Py_TYPE(function) == &PyMethod_Type) {
@@ -4748,6 +4747,7 @@ check_eval_breaker:
stack_pointer, nargs, kwnames
);
STACK_SHRINK(postcall_shrink);
+ RESET_STACK_ADJUST_FOR_CALLS;
// The frame has stolen all the arguments from the stack,
// so there is no need to clean them up.
Py_XDECREF(kwnames);
@@ -4780,6 +4780,7 @@ check_eval_breaker:
Py_DECREF(stack_pointer[i]);
}
STACK_SHRINK(postcall_shrink);
+ RESET_STACK_ADJUST_FOR_CALLS;
PUSH(res);
if (res == NULL) {
goto error;
@@ -4788,43 +4789,46 @@ check_eval_breaker:
DISPATCH();
}
- TARGET(CALL_FUNCTION_ADAPTIVE) {
+ TARGET(CALL_NO_KW_ADAPTIVE) {
SpecializedCacheEntry *cache = GET_CACHE();
- nargs = cache->adaptive.original_oparg;
+ oparg = cache->adaptive.original_oparg;
if (cache->adaptive.counter == 0) {
next_instr--;
- if (_Py_Specialize_CallFunction(
+ int nargs = oparg+extra_args;
+ if (_Py_Specialize_CallNoKw(
PEEK(nargs + 1), next_instr, nargs, cache, BUILTINS()) < 0) {
goto error;
}
DISPATCH();
}
else {
- STAT_INC(CALL_FUNCTION, deferred);
+ STAT_INC(CALL_NO_KW, deferred);
cache->adaptive.counter--;
- oparg = nargs;
kwnames = NULL;
- postcall_shrink = 1;
+ oparg += extra_args;
+ nargs = oparg;
goto call_function;
}
}
- TARGET(CALL_FUNCTION_PY_SIMPLE) {
+ TARGET(CALL_NO_KW_PY_SIMPLE) {
SpecializedCacheEntry *caches = GET_CACHE();
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
- int argcount = cache0->original_oparg;
+ int argcount = cache0->original_oparg + extra_args;
+ DEOPT_IF(argcount != cache0->index, CALL_NO_KW);
_PyCallCache *cache1 = &caches[-1].call;
PyObject *callable = PEEK(argcount+1);
- DEOPT_IF(!PyFunction_Check(callable), CALL_FUNCTION);
+ DEOPT_IF(!PyFunction_Check(callable), CALL_NO_KW);
PyFunctionObject *func = (PyFunctionObject *)callable;
- DEOPT_IF(func->func_version != cache1->func_version, CALL_FUNCTION);
+ DEOPT_IF(func->func_version != cache1->func_version, CALL_NO_KW);
/* PEP 523 */
- DEOPT_IF(tstate->interp->eval_frame != NULL, CALL_FUNCTION);
- STAT_INC(CALL_FUNCTION, hit);
+ DEOPT_IF(tstate->interp->eval_frame != NULL, CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
PyCodeObject *code = (PyCodeObject *)func->func_code;
size_t size = code->co_nlocalsplus + code->co_stacksize + FRAME_SPECIALS_SIZE;
InterpreterFrame *new_frame = _PyThreadState_BumpFramePointer(tstate, size);
if (new_frame == NULL) {
+ RESET_STACK_ADJUST_FOR_CALLS;
goto error;
}
_PyFrame_InitializeSpecials(new_frame, func,
@@ -4842,7 +4846,8 @@ check_eval_breaker:
for (int i = argcount+deflen; i < code->co_nlocalsplus; i++) {
new_frame->localsplus[i] = NULL;
}
- STACK_SHRINK(1);
+ STACK_SHRINK(postcall_shrink);
+ RESET_STACK_ADJUST_FOR_CALLS;
Py_DECREF(func);
_PyFrame_SetStackPointer(frame, stack_pointer);
new_frame->previous = frame;
@@ -4851,14 +4856,15 @@ check_eval_breaker:
goto start_frame;
}
- TARGET(CALL_FUNCTION_BUILTIN_O) {
+ TARGET(CALL_NO_KW_BUILTIN_O) {
assert(cframe.use_tracing == 0);
+ assert(STACK_ADJUST_IS_RESET);
/* Builtin METH_O functions */
PyObject *callable = SECOND();
- DEOPT_IF(!PyCFunction_CheckExact(callable), CALL_FUNCTION);
- DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL_FUNCTION);
- STAT_INC(CALL_FUNCTION, hit);
+ DEOPT_IF(!PyCFunction_CheckExact(callable), CALL_NO_KW);
+ DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_O, CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
// This is slower but CPython promises to check all non-vectorcall
@@ -4881,18 +4887,19 @@ check_eval_breaker:
DISPATCH();
}
- TARGET(CALL_FUNCTION_BUILTIN_FAST) {
+ TARGET(CALL_NO_KW_BUILTIN_FAST) {
assert(cframe.use_tracing == 0);
+ assert(STACK_ADJUST_IS_RESET);
/* Builtin METH_FASTCALL functions, without keywords */
SpecializedCacheEntry *caches = GET_CACHE();
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
int nargs = cache0->original_oparg;
PyObject **pfunc = &PEEK(nargs + 1);
PyObject *callable = *pfunc;
- DEOPT_IF(!PyCFunction_CheckExact(callable), CALL_FUNCTION);
+ DEOPT_IF(!PyCFunction_CheckExact(callable), CALL_NO_KW);
DEOPT_IF(PyCFunction_GET_FLAGS(callable) != METH_FASTCALL,
- CALL_FUNCTION);
- STAT_INC(CALL_FUNCTION, hit);
+ CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable);
/* res = func(self, args, nargs) */
@@ -4919,16 +4926,17 @@ check_eval_breaker:
DISPATCH();
}
- TARGET(CALL_FUNCTION_LEN) {
+ TARGET(CALL_NO_KW_LEN) {
assert(cframe.use_tracing == 0);
+ assert(STACK_ADJUST_IS_RESET);
/* len(o) */
SpecializedCacheEntry *caches = GET_CACHE();
assert(caches[0].adaptive.original_oparg == 1);
_PyObjectCache *cache1 = &caches[-1].obj;
PyObject *callable = SECOND();
- DEOPT_IF(callable != cache1->obj, CALL_FUNCTION);
- STAT_INC(CALL_FUNCTION, hit);
+ DEOPT_IF(callable != cache1->obj, CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
Py_ssize_t len_i = PyObject_Length(TOP());
if (len_i < 0) {
@@ -4947,16 +4955,17 @@ check_eval_breaker:
DISPATCH();
}
- TARGET(CALL_FUNCTION_ISINSTANCE) {
+ TARGET(CALL_NO_KW_ISINSTANCE) {
assert(cframe.use_tracing == 0);
+ assert(STACK_ADJUST_IS_RESET);
/* isinstance(o, o2) */
SpecializedCacheEntry *caches = GET_CACHE();
assert(caches[0].adaptive.original_oparg == 2);
_PyObjectCache *cache1 = &caches[-1].obj;
PyObject *callable = THIRD();
- DEOPT_IF(callable != cache1->obj, CALL_FUNCTION);
- STAT_INC(CALL_FUNCTION, hit);
+ DEOPT_IF(callable != cache1->obj, CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
int retval = PyObject_IsInstance(SECOND(), TOP());
if (retval < 0) {
@@ -4976,6 +4985,97 @@ check_eval_breaker:
DISPATCH();
}
+ TARGET(CALL_NO_KW_LIST_APPEND) {
+ assert(_Py_OPCODE(next_instr[-2]) == PRECALL_METHOD);
+ assert(GET_CACHE()->adaptive.original_oparg == 1);
+ DEOPT_IF(extra_args == 0, CALL_NO_KW);
+ PyObject *list = SECOND();
+ DEOPT_IF(!PyList_CheckExact(list), CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
+ assert(extra_args == 1);
+ extra_args = 0;
+ assert(STACK_ADJUST_IS_RESET);
+ PyObject *arg = TOP();
+ int err = PyList_Append(list, arg);
+ if (err) {
+ goto error;
+ }
+ PyObject *callable = THIRD();
+ Py_DECREF(arg);
+ Py_DECREF(list);
+ Py_INCREF(Py_None);
+ STACK_SHRINK(2);
+ SET_TOP(Py_None);
+ Py_DECREF(callable);
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
+ assert(_Py_OPCODE(next_instr[-2]) == PRECALL_METHOD);
+ assert(GET_CACHE()->adaptive.original_oparg == 1);
+ DEOPT_IF(extra_args == 0, CALL_NO_KW);
+ assert(extra_args == 1);
+ PyObject *callable = THIRD();
+ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL_NO_KW);
+ DEOPT_IF(((PyMethodDescrObject *)callable)->d_method->ml_flags != METH_O, CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
+ assert(extra_args == 1);
+ extra_args = 0;
+ assert(STACK_ADJUST_IS_RESET);
+ PyCFunction cfunc = ((PyMethodDescrObject *)callable)->d_method->ml_meth;
+ // This is slower but CPython promises to check all non-vectorcall
+ // function calls.
+ if (_Py_EnterRecursiveCall(tstate, " while calling a Python object")) {
+ goto error;
+ }
+ PyObject *arg = POP();
+ PyObject *self = POP();
+ PyObject *res = cfunc(self, arg);
+ _Py_LeaveRecursiveCall(tstate);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ Py_DECREF(self);
+ Py_DECREF(arg);
+ SET_TOP(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ DISPATCH();
+ }
+
+ TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
+ assert(_Py_OPCODE(next_instr[-2]) == PRECALL_METHOD);
+ /* Builtin METH_FASTCALL methods, without keywords */
+ SpecializedCacheEntry *caches = GET_CACHE();
+ _PyAdaptiveEntry *cache0 = &caches[0].adaptive;
+ DEOPT_IF(extra_args == 0, CALL_NO_KW);
+ assert(extra_args == 1);
+ int nargs = cache0->original_oparg;
+ PyObject *callable = PEEK(nargs + 2);
+ DEOPT_IF(!Py_IS_TYPE(callable, &PyMethodDescr_Type), CALL_NO_KW);
+ PyMethodDef *meth = ((PyMethodDescrObject *)callable)->d_method;
+ DEOPT_IF(meth->ml_flags != METH_FASTCALL, CALL_NO_KW);
+ STAT_INC(CALL_NO_KW, hit);
+ assert(extra_args == 1);
+ extra_args = 0;
+ assert(STACK_ADJUST_IS_RESET);
+ _PyCFunctionFast cfunc = (_PyCFunctionFast)(void(*)(void))meth->ml_meth;
+ PyObject *self = PEEK(nargs+1);
+ PyObject *res = cfunc(self, &PEEK(nargs), nargs);
+ assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
+ /* Clear the stack of the arguments. */
+ STACK_SHRINK(nargs+1);
+ for (int i = 0; i <= nargs; i++) {
+ Py_DECREF(stack_pointer[i]);
+ }
+ SET_TOP(res);
+ Py_DECREF(callable);
+ if (res == NULL) {
+ goto error;
+ }
+ DISPATCH();
+ }
+
TARGET(CALL_FUNCTION_EX) {
PREDICTED(CALL_FUNCTION_EX);
PyObject *func, *callargs, *kwargs = NULL, *result;
@@ -5289,7 +5389,7 @@ MISS_WITH_CACHE(LOAD_ATTR)
MISS_WITH_CACHE(STORE_ATTR)
MISS_WITH_CACHE(LOAD_GLOBAL)
MISS_WITH_CACHE(LOAD_METHOD)
-MISS_WITH_CACHE(CALL_FUNCTION)
+MISS_WITH_CACHE(CALL_NO_KW)
MISS_WITH_CACHE(BINARY_OP)
MISS_WITH_CACHE(COMPARE_OP)
MISS_WITH_CACHE(BINARY_SUBSCR)
@@ -7509,7 +7609,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevprevop
"that does not implement __await__: %.100s",
type->tp_name);
}
- else if (prevopcode == WITH_EXCEPT_START || (prevopcode == CALL_FUNCTION && prevprevopcode == DUP_TOP)) {
+ else if (prevopcode == WITH_EXCEPT_START || (prevopcode == CALL_NO_KW && prevprevopcode == DUP_TOP)) {
_PyErr_Format(tstate, PyExc_TypeError,
"'async with' received an object from __aexit__ "
"that does not implement __await__: %.100s",
diff --git a/Python/compile.c b/Python/compile.c
index 00e1e01696..afd9a629c0 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1155,13 +1155,11 @@ stack_effect(int opcode, int oparg, int jump)
return -oparg;
/* Functions and calls */
- case CALL_FUNCTION:
+ case PRECALL_METHOD:
+ return -1;
+ case CALL_NO_KW:
return -oparg;
- case CALL_METHOD:
- return -oparg-1;
- case CALL_METHOD_KW:
- return -oparg-2;
- case CALL_FUNCTION_KW:
+ case CALL_KW:
return -oparg-1;
case CALL_FUNCTION_EX:
return -1 - ((oparg & 0x01) != 0);
@@ -1817,7 +1815,7 @@ compiler_call_exit_with_nones(struct compiler *c) {
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, DUP_TOP);
ADDOP(c, DUP_TOP);
- ADDOP_I(c, CALL_FUNCTION, 3);
+ ADDOP_I(c, CALL_NO_KW, 3);
return 1;
}
@@ -2166,7 +2164,7 @@ compiler_apply_decorators(struct compiler *c, asdl_expr_seq* decos)
int old_end_col_offset = c->u->u_end_col_offset;
for (Py_ssize_t i = asdl_seq_LEN(decos) - 1; i > -1; i--) {
SET_LOC(c, (expr_ty)asdl_seq_GET(decos, i));
- ADDOP_I(c, CALL_FUNCTION, 1);
+ ADDOP_I(c, CALL_NO_KW, 1);
}
c->u->u_lineno = old_lineno;
c->u->u_end_lineno = old_end_lineno;
@@ -3885,7 +3883,7 @@ compiler_assert(struct compiler *c, stmt_ty s)
ADDOP(c, LOAD_ASSERTION_ERROR);
if (s->v.Assert.msg) {
VISIT(c, expr, s->v.Assert.msg);
- ADDOP_I(c, CALL_FUNCTION, 1);
+ ADDOP_I(c, CALL_NO_KW, 1);
}
ADDOP_I(c, RAISE_VARARGS, 1);
compiler_use_next_block(c, end);
@@ -4693,10 +4691,12 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
if (!compiler_call_simple_kw_helper(c, kwds, kwdsl)) {
return 0;
};
- ADDOP_I(c, CALL_METHOD_KW, argsl + kwdsl);
+ ADDOP_I(c, PRECALL_METHOD, argsl + kwdsl+1);
+ ADDOP_I(c, CALL_KW, argsl + kwdsl);
}
else {
- ADDOP_I(c, CALL_METHOD, argsl);
+ ADDOP_I(c, PRECALL_METHOD, argsl);
+ ADDOP_I(c, CALL_NO_KW, argsl);
}
c->u->u_lineno = old_lineno;
return 1;
@@ -4763,7 +4763,8 @@ compiler_joined_str(struct compiler *c, expr_ty e)
VISIT(c, expr, asdl_seq_GET(e->v.JoinedStr.values, i));
ADDOP_I(c, LIST_APPEND, 1);
}
- ADDOP_I(c, CALL_METHOD, 1);
+ ADDOP_I(c, PRECALL_METHOD, 1);
+ ADDOP_I(c, CALL_NO_KW, 1);
}
else {
VISIT_SEQ(c, expr, e->v.JoinedStr.values);
@@ -4935,11 +4936,11 @@ compiler_call_helper(struct compiler *c,
if (!compiler_call_simple_kw_helper(c, keywords, nkwelts)) {
return 0;
};
- ADDOP_I(c, CALL_FUNCTION_KW, n + nelts + nkwelts);
+ ADDOP_I(c, CALL_KW, n + nelts + nkwelts);
return 1;
}
else {
- ADDOP_I(c, CALL_FUNCTION, n + nelts);
+ ADDOP_I(c, CALL_NO_KW, n + nelts);
return 1;
}
@@ -5336,7 +5337,7 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
ADDOP(c, GET_ITER);
}
- ADDOP_I(c, CALL_FUNCTION, 1);
+ ADDOP_I(c, CALL_NO_KW, 1);
if (is_async_generator && type != COMP_GENEXP) {
ADDOP(c, GET_AWAITABLE);
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index aeb9830ccc..05bd9392d8 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -39,34 +39,34 @@ static void *opcode_targets[256] = {
&&TARGET_POP_EXCEPT_AND_RERAISE,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_STORE_SUBSCR_DICT,
- &&TARGET_CALL_FUNCTION_ADAPTIVE,
- &&TARGET_CALL_FUNCTION_BUILTIN_O,
- &&TARGET_CALL_FUNCTION_BUILTIN_FAST,
- &&TARGET_CALL_FUNCTION_LEN,
- &&TARGET_CALL_FUNCTION_ISINSTANCE,
- &&TARGET_CALL_FUNCTION_PY_SIMPLE,
- &&TARGET_JUMP_ABSOLUTE_QUICK,
- &&TARGET_LOAD_ATTR_ADAPTIVE,
- &&TARGET_LOAD_ATTR_INSTANCE_VALUE,
+ &&TARGET_CALL_NO_KW_ADAPTIVE,
+ &&TARGET_CALL_NO_KW_BUILTIN_O,
+ &&TARGET_CALL_NO_KW_BUILTIN_FAST,
+ &&TARGET_CALL_NO_KW_LEN,
+ &&TARGET_CALL_NO_KW_ISINSTANCE,
+ &&TARGET_CALL_NO_KW_PY_SIMPLE,
+ &&TARGET_CALL_NO_KW_LIST_APPEND,
+ &&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_O,
+ &&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_FAST,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
&&TARGET_BEFORE_ASYNC_WITH,
&&TARGET_BEFORE_WITH,
&&TARGET_END_ASYNC_FOR,
+ &&TARGET_JUMP_ABSOLUTE_QUICK,
+ &&TARGET_LOAD_ATTR_ADAPTIVE,
+ &&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_SLOT,
+ &&TARGET_STORE_SUBSCR,
+ &&TARGET_DELETE_SUBSCR,
&&TARGET_LOAD_ATTR_MODULE,
&&TARGET_LOAD_GLOBAL_ADAPTIVE,
&&TARGET_LOAD_GLOBAL_MODULE,
- &&TARGET_STORE_SUBSCR,
- &&TARGET_DELETE_SUBSCR,
&&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_LOAD_METHOD_ADAPTIVE,
&&TARGET_LOAD_METHOD_CACHED,
- &&TARGET_LOAD_METHOD_CLASS,
- &&TARGET_LOAD_METHOD_MODULE,
- &&TARGET_LOAD_METHOD_NO_DICT,
&&TARGET_GET_ITER,
&&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_PRINT_EXPR,
@@ -74,19 +74,19 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_FROM,
&&TARGET_GET_AWAITABLE,
&&TARGET_LOAD_ASSERTION_ERROR,
+ &&TARGET_LOAD_METHOD_CLASS,
+ &&TARGET_LOAD_METHOD_MODULE,
+ &&TARGET_LOAD_METHOD_NO_DICT,
&&TARGET_STORE_ATTR_ADAPTIVE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT,
- &&TARGET_LOAD_FAST__LOAD_FAST,
- &&TARGET_STORE_FAST__LOAD_FAST,
- &&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_LIST_TO_TUPLE,
&&TARGET_RETURN_VALUE,
&&TARGET_IMPORT_STAR,
&&TARGET_SETUP_ANNOTATIONS,
&&TARGET_YIELD_VALUE,
- &&TARGET_LOAD_CONST__LOAD_FAST,
+ &&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_PREP_RERAISE_STAR,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
@@ -122,25 +122,25 @@ static void *opcode_targets[256] = {
&&TARGET_COPY,
&&TARGET_JUMP_IF_NOT_EXC_MATCH,
&&TARGET_BINARY_OP,
- &&TARGET_STORE_FAST__STORE_FAST,
+ &&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_LOAD_FAST,
&&TARGET_STORE_FAST,
&&TARGET_DELETE_FAST,
&&TARGET_JUMP_IF_NOT_EG_MATCH,
- &&_unknown_opcode,
+ &&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_GEN_START,
&&TARGET_RAISE_VARARGS,
- &&TARGET_CALL_FUNCTION,
+ &&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_MAKE_FUNCTION,
&&TARGET_BUILD_SLICE,
- &&_unknown_opcode,
+ &&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_MAKE_CELL,
&&TARGET_LOAD_CLOSURE,
&&TARGET_LOAD_DEREF,
&&TARGET_STORE_DEREF,
&&TARGET_DELETE_DEREF,
&&_unknown_opcode,
- &&TARGET_CALL_FUNCTION_KW,
+ &&_unknown_opcode,
&&TARGET_CALL_FUNCTION_EX,
&&_unknown_opcode,
&&TARGET_EXTENDED_ARG,
@@ -160,16 +160,16 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_LOAD_METHOD,
- &&TARGET_CALL_METHOD,
+ &&_unknown_opcode,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
- &&TARGET_CALL_METHOD_KW,
- &&_unknown_opcode,
- &&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
+ &&TARGET_PRECALL_METHOD,
+ &&TARGET_CALL_NO_KW,
+ &&TARGET_CALL_KW,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
diff --git a/Python/specialize.c b/Python/specialize.c
index 5121845008..bdcba46ed4 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -127,7 +127,7 @@ _Py_GetSpecializationStats(void) {
err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr");
err += add_stat_dict(stats, STORE_SUBSCR, "store_subscr");
err += add_stat_dict(stats, STORE_ATTR, "store_attr");
- err += add_stat_dict(stats, CALL_FUNCTION, "call_function");
+ err += add_stat_dict(stats, CALL_NO_KW, "call_no_kw");
err += add_stat_dict(stats, BINARY_OP, "binary_op");
err += add_stat_dict(stats, COMPARE_OP, "compare_op");
if (err < 0) {
@@ -186,7 +186,7 @@ _Py_PrintSpecializationStats(void)
print_stats(out, &_specialization_stats[BINARY_SUBSCR], "binary_subscr");
print_stats(out, &_specialization_stats[STORE_SUBSCR], "store_subscr");
print_stats(out, &_specialization_stats[STORE_ATTR], "store_attr");
- print_stats(out, &_specialization_stats[CALL_FUNCTION], "call_function");
+ print_stats(out, &_specialization_stats[CALL_NO_KW], "call_no_kw");
print_stats(out, &_specialization_stats[BINARY_OP], "binary_op");
print_stats(out, &_specialization_stats[COMPARE_OP], "compare_op");
if (out != stderr) {
@@ -238,7 +238,7 @@ static uint8_t adaptive_opcodes[256] = {
[LOAD_METHOD] = LOAD_METHOD_ADAPTIVE,
[BINARY_SUBSCR] = BINARY_SUBSCR_ADAPTIVE,
[STORE_SUBSCR] = STORE_SUBSCR_ADAPTIVE,
- [CALL_FUNCTION] = CALL_FUNCTION_ADAPTIVE,
+ [CALL_NO_KW] = CALL_NO_KW_ADAPTIVE,
[STORE_ATTR] = STORE_ATTR_ADAPTIVE,
[BINARY_OP] = BINARY_OP_ADAPTIVE,
[COMPARE_OP] = COMPARE_OP_ADAPTIVE,
@@ -251,7 +251,7 @@ static uint8_t cache_requirements[256] = {
[LOAD_METHOD] = 3, /* _PyAdaptiveEntry, _PyAttrCache and _PyObjectCache */
[BINARY_SUBSCR] = 2, /* _PyAdaptiveEntry, _PyObjectCache */
[STORE_SUBSCR] = 0,
- [CALL_FUNCTION] = 2, /* _PyAdaptiveEntry and _PyObjectCache/_PyCallCache */
+ [CALL_NO_KW] = 2, /* _PyAdaptiveEntry and _PyObjectCache/_PyCallCache */
[STORE_ATTR] = 2, /* _PyAdaptiveEntry and _PyAttrCache */
[BINARY_OP] = 1, // _PyAdaptiveEntry
[COMPARE_OP] = 1, /* _PyAdaptiveEntry */
@@ -491,6 +491,8 @@ initial_counter_value(void) {
#define SPEC_FAIL_PYCFUNCTION_NOARGS 16
#define SPEC_FAIL_BAD_CALL_FLAGS 17
#define SPEC_FAIL_CLASS 18
+#define SPEC_FAIL_C_METHOD_CALL 19
+#define SPEC_FAIL_METHDESCR_NON_METHOD 20
/* COMPARE_OP */
#define SPEC_FAIL_STRING_COMPARE 13
@@ -1261,7 +1263,51 @@ specialize_class_call(
PyObject *callable, _Py_CODEUNIT *instr,
int nargs, SpecializedCacheEntry *cache)
{
- SPECIALIZATION_FAIL(CALL_FUNCTION, SPEC_FAIL_CLASS);
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_CLASS);
+ return -1;
+}
+
+static PyMethodDescrObject *_list_append = NULL;
+_Py_IDENTIFIER(append);
+
+static int
+specialize_method_descriptor(
+ PyMethodDescrObject *descr, _Py_CODEUNIT *instr,
+ int nargs, SpecializedCacheEntry *cache)
+{
+ int oparg = cache->adaptive.original_oparg;
+ if (nargs - oparg != 1) {
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_METHDESCR_NON_METHOD);
+ return -1;
+ }
+ if (_list_append == NULL) {
+ _list_append = (PyMethodDescrObject *)_PyType_LookupId(&PyList_Type, &PyId_append);
+ }
+ if (oparg == 1 && descr == _list_append) {
+ assert(_Py_OPCODE(instr[-1]) == PRECALL_METHOD);
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_LIST_APPEND, _Py_OPARG(*instr));
+ return 0;
+ }
+
+ switch (descr->d_method->ml_flags &
+ (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O |
+ METH_KEYWORDS | METH_METHOD)) {
+ case METH_O: {
+ if (oparg != 1) {
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_OUT_OF_RANGE);
+ return 1;
+ }
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_METHOD_DESCRIPTOR_O,
+ _Py_OPARG(*instr));
+ return 0;
+ }
+ case METH_FASTCALL: {
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_METHOD_DESCRIPTOR_FAST,
+ _Py_OPARG(*instr));
+ return 0;
+ }
+ }
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_OTHER);
return -1;
}
@@ -1274,15 +1320,19 @@ specialize_py_call(
PyCodeObject *code = (PyCodeObject *)func->func_code;
int kind = function_kind(code);
if (kind != SIMPLE_FUNCTION) {
- SPECIALIZATION_FAIL(CALL_FUNCTION, kind);
+ SPECIALIZATION_FAIL(CALL_NO_KW, kind);
return -1;
}
int argcount = code->co_argcount;
+ if (argcount > 0xffff) {
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_OUT_OF_RANGE);
+ return -1;
+ }
int defcount = func->func_defaults == NULL ? 0 : (int)PyTuple_GET_SIZE(func->func_defaults);
assert(defcount <= argcount);
int min_args = argcount-defcount;
if (nargs > argcount || nargs < min_args) {
- SPECIALIZATION_FAIL(CALL_FUNCTION, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS);
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS);
return -1;
}
assert(nargs <= argcount && nargs >= min_args);
@@ -1291,18 +1341,19 @@ specialize_py_call(
assert(defstart >= 0 && deflen >= 0);
assert(deflen == 0 || func->func_defaults != NULL);
if (defstart > 0xffff || deflen > 0xffff) {
- SPECIALIZATION_FAIL(CALL_FUNCTION, SPEC_FAIL_OUT_OF_RANGE);
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_OUT_OF_RANGE);
return -1;
}
int version = _PyFunction_GetVersionForCurrentState(func);
if (version == 0) {
- SPECIALIZATION_FAIL(CALL_FUNCTION, SPEC_FAIL_OUT_OF_VERSIONS);
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_OUT_OF_VERSIONS);
return -1;
}
+ cache[0].adaptive.index = nargs;
cache1->func_version = version;
cache1->defaults_start = defstart;
cache1->defaults_len = deflen;
- *instr = _Py_MAKECODEUNIT(CALL_FUNCTION_PY_SIMPLE, _Py_OPARG(*instr));
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_PY_SIMPLE, _Py_OPARG(*instr));
return 0;
}
@@ -1335,6 +1386,10 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
SpecializedCacheEntry *cache, PyObject *builtins)
{
_PyObjectCache *cache1 = &cache[-1].obj;
+ if (_Py_OPCODE(instr[-1]) == PRECALL_METHOD) {
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_C_METHOD_CALL);
+ return -1;
+ }
if (PyCFunction_GET_FUNCTION(callable) == NULL) {
return 1;
}
@@ -1343,18 +1398,18 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
METH_KEYWORDS | METH_METHOD)) {
case METH_O: {
if (nargs != 1) {
- SPECIALIZATION_FAIL(CALL_FUNCTION, SPEC_FAIL_OUT_OF_RANGE);
+ SPECIALIZATION_FAIL(CALL_NO_KW, SPEC_FAIL_OUT_OF_RANGE);
return 1;
}
/* len(o) */
PyObject *builtin_len = PyDict_GetItemString(builtins, "len");
if (callable == builtin_len) {
cache1->obj = builtin_len; // borrowed
- *instr = _Py_MAKECODEUNIT(CALL_FUNCTION_LEN,
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_LEN,
_Py_OPARG(*instr));
return 0;
}
- *instr = _Py_MAKECODEUNIT(CALL_FUNCTION_BUILTIN_O,
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_BUILTIN_O,
_Py_OPARG(*instr));
return 0;
}
@@ -1365,17 +1420,17 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs,
builtins, "isinstance");
if (callable == builtin_isinstance) {
cache1->obj = builtin_isinstance; // borrowed
- *instr = _Py_MAKECODEUNIT(CALL_FUNCTION_ISINSTANCE,
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_ISINSTANCE,
_Py_OPARG(*instr));
return 0;
}
}
- *instr = _Py_MAKECODEUNIT(CALL_FUNCTION_BUILTIN_FAST,
+ *instr = _Py_MAKECODEUNIT(CALL_NO_KW_BUILTIN_FAST,
_Py_OPARG(*instr));
return 0;
}
default:
- SPECIALIZATION_FAIL(CALL_FUNCTION,
+ SPECIALIZATION_FAIL(CALL_NO_KW,
builtin_call_fail_kind(PyCFunction_GET_FLAGS(callable)));
return 1;
}
@@ -1406,7 +1461,7 @@ call_fail_kind(PyObject *callable)
- Specialize calling classes.
*/
int
-_Py_Specialize_CallFunction(
+_Py_Specialize_CallNoKw(
PyObject *callable, _Py_CODEUNIT *instr,
int nargs, SpecializedCacheEntry *cache,
PyObject *builtins)
@@ -1421,18 +1476,22 @@ _Py_Specialize_CallFunction(
else if (PyType_Check(callable)) {
fail = specialize_class_call(callable, instr, nargs, cache);
}
+ else if (Py_IS_TYPE(callable, &PyMethodDescr_Type)) {
+ fail = specialize_method_descriptor(
+ (PyMethodDescrObject *)callable, instr, nargs, cache);
+ }
else {
- SPECIALIZATION_FAIL(CALL_FUNCTION, call_fail_kind(callable));
+ SPECIALIZATION_FAIL(CALL_NO_KW, call_fail_kind(callable));
fail = -1;
}
_PyAdaptiveEntry *cache0 = &cache->adaptive;
if (fail) {
- STAT_INC(CALL_FUNCTION, specialization_failure);
+ STAT_INC(CALL_NO_KW, specialization_failure);
assert(!PyErr_Occurred());
cache_backoff(cache0);
}
else {
- STAT_INC(CALL_FUNCTION, specialization_success);
+ STAT_INC(CALL_NO_KW, specialization_success);
assert(!PyErr_Occurred());
cache0->counter = initial_counter_value();
}