bpo-45439: Move _PyObject_VectorcallTstate() to pycore_call.h (GH-28893)

* Move _PyObject_VectorcallTstate() and _PyObject_FastCallTstate() to
  pycore_call.h (internal C API).
* Convert PyObject_CallOneArg(), PyObject_Vectorcall(),
  _PyObject_FastCall() and PyVectorcall_Function() static inline
  functions to regular functions.
* Add _PyVectorcall_FunctionInline() static inline function.
* PyObject_Vectorcall(), _PyObject_FastCall(), and
  PyObject_CallOneArg() now call _PyThreadState_GET() rather
  than PyThreadState_Get().
This commit is contained in:
Victor Stinner 2021-10-14 21:53:04 +02:00 committed by GitHub
parent 39aa98346d
commit 3cc56c828d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 127 additions and 100 deletions

View file

@ -58,72 +58,13 @@ PyVectorcall_NARGS(size_t n)
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
} }
static inline vectorcallfunc PyAPI_FUNC(vectorcallfunc) PyVectorcall_Function(PyObject *callable);
PyVectorcall_Function(PyObject *callable)
{
PyTypeObject *tp;
Py_ssize_t offset;
vectorcallfunc ptr;
assert(callable != NULL); PyAPI_FUNC(PyObject *) PyObject_Vectorcall(
tp = Py_TYPE(callable); PyObject *callable,
if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) { PyObject *const *args,
return NULL; size_t nargsf,
} PyObject *kwnames);
assert(PyCallable_Check(callable));
offset = tp->tp_vectorcall_offset;
assert(offset > 0);
memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
return ptr;
}
/* Call the callable object 'callable' with the "vectorcall" calling
convention.
args is a C array for positional arguments.
nargsf is the number of positional arguments plus optionally the flag
PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
modify args[-1].
kwnames is a tuple of keyword names. The values of the keyword arguments
are stored in "args" after the positional arguments (note that the number
of keyword arguments does not change nargsf). kwnames can also be NULL if
there are no keyword arguments.
keywords must only contain strings and all keys must be unique.
Return the result on success. Raise an exception and return NULL on
error. */
static inline PyObject *
_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
PyObject *const *args, size_t nargsf,
PyObject *kwnames)
{
vectorcallfunc func;
PyObject *res;
assert(kwnames == NULL || PyTuple_Check(kwnames));
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
func = PyVectorcall_Function(callable);
if (func == NULL) {
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
}
res = func(callable, args, nargsf, kwnames);
return _Py_CheckFunctionResult(tstate, callable, res, NULL);
}
static inline PyObject *
PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
PyThreadState *tstate = PyThreadState_Get();
return _PyObject_VectorcallTstate(tstate, callable,
args, nargsf, kwnames);
}
// Backwards compatibility aliases for API that was provisional in Python 3.8 // Backwards compatibility aliases for API that was provisional in Python 3.8
#define _PyObject_Vectorcall PyObject_Vectorcall #define _PyObject_Vectorcall PyObject_Vectorcall
@ -146,35 +87,13 @@ PyAPI_FUNC(PyObject *) PyObject_VectorcallDict(
"tuple" and keyword arguments "dict". "dict" may also be NULL */ "tuple" and keyword arguments "dict". "dict" may also be NULL */
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict); PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
static inline PyObject * // Same as PyObject_Vectorcall(), except without keyword arguments
_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs) PyAPI_FUNC(PyObject *) _PyObject_FastCall(
{ PyObject *func,
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL); PyObject *const *args,
} Py_ssize_t nargs);
/* Same as PyObject_Vectorcall except without keyword arguments */ PyAPI_FUNC(PyObject *) PyObject_CallOneArg(PyObject *func, PyObject *arg);
static inline PyObject *
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
{
PyThreadState *tstate = PyThreadState_Get();
return _PyObject_FastCallTstate(tstate, func, args, nargs);
}
static inline PyObject *
PyObject_CallOneArg(PyObject *func, PyObject *arg)
{
PyObject *_args[2];
PyObject **args;
PyThreadState *tstate;
size_t nargsf;
assert(arg != NULL);
args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET
args[0] = arg;
tstate = PyThreadState_Get();
nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);
}
PyAPI_FUNC(PyObject *) PyObject_VectorcallMethod( PyAPI_FUNC(PyObject *) PyObject_VectorcallMethod(
PyObject *name, PyObject *const *args, PyObject *name, PyObject *const *args,

View file

@ -30,11 +30,73 @@ PyAPI_FUNC(PyObject *) _PyObject_Call(
PyObject *args, PyObject *args,
PyObject *kwargs); PyObject *kwargs);
// Static inline variant of public PyVectorcall_Function().
static inline vectorcallfunc
_PyVectorcall_FunctionInline(PyObject *callable)
{
assert(callable != NULL);
PyTypeObject *tp = Py_TYPE(callable);
if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) {
return NULL;
}
assert(PyCallable_Check(callable));
Py_ssize_t offset = tp->tp_vectorcall_offset;
assert(offset > 0);
vectorcallfunc ptr;
memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
return ptr;
}
/* Call the callable object 'callable' with the "vectorcall" calling
convention.
args is a C array for positional arguments.
nargsf is the number of positional arguments plus optionally the flag
PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
modify args[-1].
kwnames is a tuple of keyword names. The values of the keyword arguments
are stored in "args" after the positional arguments (note that the number
of keyword arguments does not change nargsf). kwnames can also be NULL if
there are no keyword arguments.
keywords must only contain strings and all keys must be unique.
Return the result on success. Raise an exception and return NULL on
error. */
static inline PyObject *
_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
PyObject *const *args, size_t nargsf,
PyObject *kwnames)
{
vectorcallfunc func;
PyObject *res;
assert(kwnames == NULL || PyTuple_Check(kwnames));
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
func = _PyVectorcall_FunctionInline(callable);
if (func == NULL) {
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
}
res = func(callable, args, nargsf, kwnames);
return _Py_CheckFunctionResult(tstate, callable, res, NULL);
}
static inline PyObject * static inline PyObject *
_PyObject_CallNoArgsTstate(PyThreadState *tstate, PyObject *func) { _PyObject_CallNoArgsTstate(PyThreadState *tstate, PyObject *func) {
return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL); return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL);
} }
// Private static inline function variant of public PyObject_CallNoArgs() // Private static inline function variant of public PyObject_CallNoArgs()
static inline PyObject * static inline PyObject *
_PyObject_CallNoArgs(PyObject *func) { _PyObject_CallNoArgs(PyObject *func) {
@ -42,6 +104,14 @@ _PyObject_CallNoArgs(PyObject *func) {
return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL); return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL);
} }
static inline PyObject *
_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs)
{
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -269,7 +269,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
static void static void
partial_setvectorcall(partialobject *pto) partial_setvectorcall(partialobject *pto)
{ {
if (PyVectorcall_Function(pto->fn) == NULL) { if (_PyVectorcall_Function(pto->fn) == NULL) {
/* Don't use vectorcall if the underlying function doesn't support it */ /* Don't use vectorcall if the underlying function doesn't support it */
pto->vectorcall = NULL; pto->vectorcall = NULL;
} }

View file

@ -109,8 +109,7 @@ _Py_CheckSlotResult(PyObject *obj, const char *slot_name, int success)
PyObject * PyObject *
PyObject_CallNoArgs(PyObject *func) PyObject_CallNoArgs(PyObject *func)
{ {
PyThreadState *tstate = _PyThreadState_GET(); return _PyObject_CallNoArgs(func);
return _PyObject_CallNoArgsTstate(tstate, func);
} }
@ -131,7 +130,7 @@ _PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable,
assert(nargs == 0 || args != NULL); assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwargs == NULL || PyDict_Check(kwargs));
vectorcallfunc func = PyVectorcall_Function(callable); vectorcallfunc func = _PyVectorcall_Function(callable);
if (func == NULL) { if (func == NULL) {
/* Use tp_call instead */ /* Use tp_call instead */
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs); return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs);
@ -225,6 +224,13 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable,
} }
vectorcallfunc
PyVectorcall_Function(PyObject *callable)
{
return _PyVectorcall_FunctionInline(callable);
}
static PyObject * static PyObject *
_PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func, _PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func,
PyObject *callable, PyObject *tuple, PyObject *kwargs) PyObject *callable, PyObject *tuple, PyObject *kwargs)
@ -260,7 +266,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
{ {
PyThreadState *tstate = _PyThreadState_GET(); PyThreadState *tstate = _PyThreadState_GET();
/* get vectorcallfunc as in PyVectorcall_Function, but without /* get vectorcallfunc as in _PyVectorcall_Function, but without
* the Py_TPFLAGS_HAVE_VECTORCALL check */ * the Py_TPFLAGS_HAVE_VECTORCALL check */
Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset; Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset;
if (offset <= 0) { if (offset <= 0) {
@ -284,6 +290,24 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
} }
PyObject *
PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
PyThreadState *tstate = _PyThreadState_GET();
return _PyObject_VectorcallTstate(tstate, callable,
args, nargsf, kwnames);
}
PyObject *
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
{
PyThreadState *tstate = _PyThreadState_GET();
return _PyObject_FastCallTstate(tstate, func, args, nargs);
}
PyObject * PyObject *
_PyObject_Call(PyThreadState *tstate, PyObject *callable, _PyObject_Call(PyThreadState *tstate, PyObject *callable,
PyObject *args, PyObject *kwargs) PyObject *args, PyObject *kwargs)
@ -298,7 +322,7 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable,
assert(PyTuple_Check(args)); assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs)); assert(kwargs == NULL || PyDict_Check(kwargs));
vectorcallfunc vector_func = PyVectorcall_Function(callable); vectorcallfunc vector_func = _PyVectorcall_Function(callable);
if (vector_func != NULL) { if (vector_func != NULL) {
return _PyVectorcall_Call(tstate, vector_func, callable, args, kwargs); return _PyVectorcall_Call(tstate, vector_func, callable, args, kwargs);
} }
@ -339,6 +363,19 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
} }
PyObject *
PyObject_CallOneArg(PyObject *func, PyObject *arg)
{
assert(arg != NULL);
PyObject *_args[2];
PyObject **args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET
args[0] = arg;
PyThreadState *tstate = _PyThreadState_GET();
size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);
}
/* --- PyFunction call functions ---------------------------------- */ /* --- PyFunction call functions ---------------------------------- */
PyObject * PyObject *

View file

@ -1,6 +1,7 @@
/* Class object implementation (dead now except for methods) */ /* Class object implementation (dead now except for methods) */
#include "Python.h" #include "Python.h"
#include "pycore_call.h" // _PyObject_VectorcallTstate()
#include "pycore_object.h" #include "pycore_object.h"
#include "pycore_pyerrors.h" #include "pycore_pyerrors.h"
#include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystate.h" // _PyThreadState_GET()

View file

@ -1,5 +1,5 @@
#include "Python.h" #include "Python.h"
#include "pycore_call.h" // _PyObject_VectorcallTstate()
#include "pycore_context.h" #include "pycore_context.h"
#include "pycore_gc.h" // _PyObject_GC_MAY_BE_TRACKED() #include "pycore_gc.h" // _PyObject_GC_MAY_BE_TRACKED()
#include "pycore_hamt.h" #include "pycore_hamt.h"