bpo-26110: Add `CALL_METHOD_KW` opcode to speedup method calls with keywords (GH-26014)

* Add CALL_METHOD_KW

* Make CALL_METHOD branchless too since it shares the same code

* Place parentheses in STACK_SHRINK
This commit is contained in:
Ken Jin 2021-05-15 23:15:23 +08:00 committed by GitHub
parent e4e931a67e
commit f24afda591
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 247 additions and 180 deletions

View file

@ -1421,7 +1421,7 @@ eval_frame_handle_pending(PyThreadState *tstate)
#define STACK_SHRINK(n) do { \
assert(n >= 0); \
(void)(lltrace && prtrace(tstate, TOP(), "stackadj")); \
(void)(BASIC_STACKADJ(-n)); \
(void)(BASIC_STACKADJ(-(n))); \
assert(STACK_LEVEL() <= co->co_stacksize); \
} while (0)
#define EXT_POP(STACK_POINTER) ((void)(lltrace && \
@ -1431,7 +1431,7 @@ eval_frame_handle_pending(PyThreadState *tstate)
#define PUSH(v) BASIC_PUSH(v)
#define POP() BASIC_POP()
#define STACK_GROW(n) BASIC_STACKADJ(n)
#define STACK_SHRINK(n) BASIC_STACKADJ(-n)
#define STACK_SHRINK(n) BASIC_STACKADJ(-(n))
#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER))
#endif
@ -4164,16 +4164,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
case TARGET(CALL_METHOD): {
/* Designed to work in tamdem with LOAD_METHOD. */
PyObject **sp, *res, *meth;
PyObject **sp, *res;
int meth_found;
sp = stack_pointer;
/* `meth` is NULL when LOAD_METHOD thinks that it's not
a method call.
meth = PEEK(oparg + 2);
if (meth == NULL) {
/* `meth` is NULL when LOAD_METHOD thinks that it's not
a method call.
Stack layout:
Stack layout:
... | NULL | callable | arg1 | ... | argN
^- TOP()
@ -4181,15 +4179,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
^- (-oparg-1)
^- (-oparg-2)
`callable` will be POPed by call_function.
NULL will will be POPed manually later.
*/
res = call_function(tstate, &trace_info, &sp, oparg, NULL);
stack_pointer = sp;
(void)POP(); /* POP the NULL. */
}
else {
/* This is a method call. Stack layout:
`callable` will be POPed by call_function.
NULL will will be POPed manually later.
If `meth` isn't NULL, it's a method call. Stack layout:
... | method | self | arg1 | ... | argN
^- TOP()
@ -4197,21 +4189,45 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
^- (-oparg-1)
^- (-oparg-2)
`self` and `method` will be POPed by call_function.
We'll be passing `oparg + 1` to call_function, to
make it accept the `self` as a first argument.
*/
res = call_function(tstate, &trace_info, &sp, oparg + 1, NULL);
stack_pointer = sp;
}
`self` and `method` will be POPed by call_function.
We'll be passing `oparg + 1` to call_function, to
make it accept the `self` as a first argument.
*/
meth_found = (PEEK(oparg + 2) != NULL);
res = call_function(tstate, &trace_info, &sp, oparg + meth_found, NULL);
stack_pointer = sp;
STACK_SHRINK(1 - meth_found);
PUSH(res);
if (res == NULL)
if (res == NULL) {
goto error;
}
CHECK_EVAL_BREAKER();
DISPATCH();
}
case 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. */
PyObject **sp, *res;
PyObject *names = NULL;
int meth_found;
names = POP();
sp = stack_pointer;
meth_found = (PEEK(oparg + 2) != NULL);
res = call_function(tstate, &trace_info, &sp, oparg + meth_found, names);
stack_pointer = sp;
STACK_SHRINK(1 - meth_found);
PUSH(res);
Py_DECREF(names);
if (res == NULL) {
goto error;
}
CHECK_EVAL_BREAKER();
DISPATCH();
}
case TARGET(CALL_FUNCTION): {
PREDICTED(CALL_FUNCTION);
PyObject **sp, *res;