Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 12 additions & 93 deletions Include/cpython/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,72 +58,13 @@ PyVectorcall_NARGS(size_t n)
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
}

static inline vectorcallfunc
PyVectorcall_Function(PyObject *callable)
{
PyTypeObject *tp;
Py_ssize_t offset;
vectorcallfunc ptr;

assert(callable != NULL);
tp = Py_TYPE(callable);
if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) {
return NULL;
}
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.
PyAPI_FUNC(vectorcallfunc) PyVectorcall_Function(PyObject *callable);

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);
}
PyAPI_FUNC(PyObject *) PyObject_Vectorcall(
PyObject *callable,
PyObject *const *args,
size_t nargsf,
PyObject *kwnames);

// Backwards compatibility aliases for API that was provisional in Python 3.8
#define _PyObject_Vectorcall PyObject_Vectorcall
Expand All @@ -146,35 +87,13 @@ PyAPI_FUNC(PyObject *) PyObject_VectorcallDict(
"tuple" and keyword arguments "dict". "dict" may also be NULL */
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);

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);
}

/* Same as PyObject_Vectorcall except without keyword arguments */
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;
// Same as PyObject_Vectorcall(), except without keyword arguments
PyAPI_FUNC(PyObject *) _PyObject_FastCall(
PyObject *func,
PyObject *const *args,
Py_ssize_t nargs);

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_CallOneArg(PyObject *func, PyObject *arg);

PyAPI_FUNC(PyObject *) PyObject_VectorcallMethod(
PyObject *name, PyObject *const *args,
Expand Down
70 changes: 70 additions & 0 deletions Include/internal/pycore_call.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,88 @@ PyAPI_FUNC(PyObject *) _PyObject_Call(
PyObject *args,
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 *
_PyObject_CallNoArgsTstate(PyThreadState *tstate, PyObject *func) {
return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL);
}


// Private static inline function variant of public PyObject_CallNoArgs()
static inline PyObject *
_PyObject_CallNoArgs(PyObject *func) {
PyThreadState *tstate = _PyThreadState_GET();
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
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion Modules/_functoolsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
static void
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 */
pto->vectorcall = NULL;
}
Expand Down
47 changes: 42 additions & 5 deletions Objects/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ _Py_CheckSlotResult(PyObject *obj, const char *slot_name, int success)
PyObject *
PyObject_CallNoArgs(PyObject *func)
{
PyThreadState *tstate = _PyThreadState_GET();
return _PyObject_CallNoArgsTstate(tstate, func);
return _PyObject_CallNoArgs(func);
}


Expand All @@ -131,7 +130,7 @@ _PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable,
assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs));

vectorcallfunc func = PyVectorcall_Function(callable);
vectorcallfunc func = _PyVectorcall_Function(callable);
if (func == NULL) {
/* Use tp_call instead */
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs);
Expand Down Expand Up @@ -225,6 +224,13 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable,
}


vectorcallfunc
PyVectorcall_Function(PyObject *callable)
{
return _PyVectorcall_FunctionInline(callable);
}


static PyObject *
_PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func,
PyObject *callable, PyObject *tuple, PyObject *kwargs)
Expand Down Expand Up @@ -260,7 +266,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
{
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 */
Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset;
if (offset <= 0) {
Expand All @@ -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_Call(PyThreadState *tstate, PyObject *callable,
PyObject *args, PyObject *kwargs)
Expand All @@ -298,7 +322,7 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable,
assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs));

vectorcallfunc vector_func = PyVectorcall_Function(callable);
vectorcallfunc vector_func = _PyVectorcall_Function(callable);
if (vector_func != NULL) {
return _PyVectorcall_Call(tstate, vector_func, callable, args, kwargs);
}
Expand Down Expand Up @@ -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 ---------------------------------- */

PyObject *
Expand Down
1 change: 1 addition & 0 deletions Objects/classobject.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Class object implementation (dead now except for methods) */

#include "Python.h"
#include "pycore_call.h" // _PyObject_VectorcallTstate()
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h" // _PyThreadState_GET()
Expand Down
2 changes: 1 addition & 1 deletion Python/context.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "Python.h"

#include "pycore_call.h" // _PyObject_VectorcallTstate()
#include "pycore_context.h"
#include "pycore_gc.h" // _PyObject_GC_MAY_BE_TRACKED()
#include "pycore_hamt.h"
Expand Down