Skip to content

Commit 3cc56c8

Browse files
authored
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().
1 parent 39aa983 commit 3cc56c8

File tree

6 files changed

+127
-100
lines changed

6 files changed

+127
-100
lines changed

Include/cpython/abstract.h

+12-93
Original file line numberDiff line numberDiff line change
@@ -58,72 +58,13 @@ PyVectorcall_NARGS(size_t n)
5858
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
5959
}
6060

61-
static inline vectorcallfunc
62-
PyVectorcall_Function(PyObject *callable)
63-
{
64-
PyTypeObject *tp;
65-
Py_ssize_t offset;
66-
vectorcallfunc ptr;
67-
68-
assert(callable != NULL);
69-
tp = Py_TYPE(callable);
70-
if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) {
71-
return NULL;
72-
}
73-
assert(PyCallable_Check(callable));
74-
75-
offset = tp->tp_vectorcall_offset;
76-
assert(offset > 0);
77-
memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
78-
return ptr;
79-
}
80-
81-
/* Call the callable object 'callable' with the "vectorcall" calling
82-
convention.
83-
84-
args is a C array for positional arguments.
85-
86-
nargsf is the number of positional arguments plus optionally the flag
87-
PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
88-
modify args[-1].
89-
90-
kwnames is a tuple of keyword names. The values of the keyword arguments
91-
are stored in "args" after the positional arguments (note that the number
92-
of keyword arguments does not change nargsf). kwnames can also be NULL if
93-
there are no keyword arguments.
61+
PyAPI_FUNC(vectorcallfunc) PyVectorcall_Function(PyObject *callable);
9462

95-
keywords must only contain strings and all keys must be unique.
96-
97-
Return the result on success. Raise an exception and return NULL on
98-
error. */
99-
static inline PyObject *
100-
_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
101-
PyObject *const *args, size_t nargsf,
102-
PyObject *kwnames)
103-
{
104-
vectorcallfunc func;
105-
PyObject *res;
106-
107-
assert(kwnames == NULL || PyTuple_Check(kwnames));
108-
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
109-
110-
func = PyVectorcall_Function(callable);
111-
if (func == NULL) {
112-
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
113-
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
114-
}
115-
res = func(callable, args, nargsf, kwnames);
116-
return _Py_CheckFunctionResult(tstate, callable, res, NULL);
117-
}
118-
119-
static inline PyObject *
120-
PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
121-
size_t nargsf, PyObject *kwnames)
122-
{
123-
PyThreadState *tstate = PyThreadState_Get();
124-
return _PyObject_VectorcallTstate(tstate, callable,
125-
args, nargsf, kwnames);
126-
}
63+
PyAPI_FUNC(PyObject *) PyObject_Vectorcall(
64+
PyObject *callable,
65+
PyObject *const *args,
66+
size_t nargsf,
67+
PyObject *kwnames);
12768

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

149-
static inline PyObject *
150-
_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs)
151-
{
152-
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
153-
}
154-
155-
/* Same as PyObject_Vectorcall except without keyword arguments */
156-
static inline PyObject *
157-
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
158-
{
159-
PyThreadState *tstate = PyThreadState_Get();
160-
return _PyObject_FastCallTstate(tstate, func, args, nargs);
161-
}
162-
163-
static inline PyObject *
164-
PyObject_CallOneArg(PyObject *func, PyObject *arg)
165-
{
166-
PyObject *_args[2];
167-
PyObject **args;
168-
PyThreadState *tstate;
169-
size_t nargsf;
90+
// Same as PyObject_Vectorcall(), except without keyword arguments
91+
PyAPI_FUNC(PyObject *) _PyObject_FastCall(
92+
PyObject *func,
93+
PyObject *const *args,
94+
Py_ssize_t nargs);
17095

171-
assert(arg != NULL);
172-
args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET
173-
args[0] = arg;
174-
tstate = PyThreadState_Get();
175-
nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
176-
return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);
177-
}
96+
PyAPI_FUNC(PyObject *) PyObject_CallOneArg(PyObject *func, PyObject *arg);
17897

17998
PyAPI_FUNC(PyObject *) PyObject_VectorcallMethod(
18099
PyObject *name, PyObject *const *args,

Include/internal/pycore_call.h

+70
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,88 @@ PyAPI_FUNC(PyObject *) _PyObject_Call(
3030
PyObject *args,
3131
PyObject *kwargs);
3232

33+
34+
// Static inline variant of public PyVectorcall_Function().
35+
static inline vectorcallfunc
36+
_PyVectorcall_FunctionInline(PyObject *callable)
37+
{
38+
assert(callable != NULL);
39+
40+
PyTypeObject *tp = Py_TYPE(callable);
41+
if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) {
42+
return NULL;
43+
}
44+
assert(PyCallable_Check(callable));
45+
46+
Py_ssize_t offset = tp->tp_vectorcall_offset;
47+
assert(offset > 0);
48+
49+
vectorcallfunc ptr;
50+
memcpy(&ptr, (char *) callable + offset, sizeof(ptr));
51+
return ptr;
52+
}
53+
54+
55+
/* Call the callable object 'callable' with the "vectorcall" calling
56+
convention.
57+
58+
args is a C array for positional arguments.
59+
60+
nargsf is the number of positional arguments plus optionally the flag
61+
PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
62+
modify args[-1].
63+
64+
kwnames is a tuple of keyword names. The values of the keyword arguments
65+
are stored in "args" after the positional arguments (note that the number
66+
of keyword arguments does not change nargsf). kwnames can also be NULL if
67+
there are no keyword arguments.
68+
69+
keywords must only contain strings and all keys must be unique.
70+
71+
Return the result on success. Raise an exception and return NULL on
72+
error. */
73+
static inline PyObject *
74+
_PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
75+
PyObject *const *args, size_t nargsf,
76+
PyObject *kwnames)
77+
{
78+
vectorcallfunc func;
79+
PyObject *res;
80+
81+
assert(kwnames == NULL || PyTuple_Check(kwnames));
82+
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
83+
84+
func = _PyVectorcall_FunctionInline(callable);
85+
if (func == NULL) {
86+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
87+
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
88+
}
89+
res = func(callable, args, nargsf, kwnames);
90+
return _Py_CheckFunctionResult(tstate, callable, res, NULL);
91+
}
92+
93+
3394
static inline PyObject *
3495
_PyObject_CallNoArgsTstate(PyThreadState *tstate, PyObject *func) {
3596
return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL);
3697
}
3798

99+
38100
// Private static inline function variant of public PyObject_CallNoArgs()
39101
static inline PyObject *
40102
_PyObject_CallNoArgs(PyObject *func) {
41103
PyThreadState *tstate = _PyThreadState_GET();
42104
return _PyObject_VectorcallTstate(tstate, func, NULL, 0, NULL);
43105
}
44106

107+
108+
static inline PyObject *
109+
_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs)
110+
{
111+
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
112+
}
113+
114+
45115
#ifdef __cplusplus
46116
}
47117
#endif

Modules/_functoolsmodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
269269
static void
270270
partial_setvectorcall(partialobject *pto)
271271
{
272-
if (PyVectorcall_Function(pto->fn) == NULL) {
272+
if (_PyVectorcall_Function(pto->fn) == NULL) {
273273
/* Don't use vectorcall if the underlying function doesn't support it */
274274
pto->vectorcall = NULL;
275275
}

Objects/call.c

+42-5
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,7 @@ _Py_CheckSlotResult(PyObject *obj, const char *slot_name, int success)
109109
PyObject *
110110
PyObject_CallNoArgs(PyObject *func)
111111
{
112-
PyThreadState *tstate = _PyThreadState_GET();
113-
return _PyObject_CallNoArgsTstate(tstate, func);
112+
return _PyObject_CallNoArgs(func);
114113
}
115114

116115

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

134-
vectorcallfunc func = PyVectorcall_Function(callable);
133+
vectorcallfunc func = _PyVectorcall_Function(callable);
135134
if (func == NULL) {
136135
/* Use tp_call instead */
137136
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs);
@@ -225,6 +224,13 @@ _PyObject_MakeTpCall(PyThreadState *tstate, PyObject *callable,
225224
}
226225

227226

227+
vectorcallfunc
228+
PyVectorcall_Function(PyObject *callable)
229+
{
230+
return _PyVectorcall_FunctionInline(callable);
231+
}
232+
233+
228234
static PyObject *
229235
_PyVectorcall_Call(PyThreadState *tstate, vectorcallfunc func,
230236
PyObject *callable, PyObject *tuple, PyObject *kwargs)
@@ -260,7 +266,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
260266
{
261267
PyThreadState *tstate = _PyThreadState_GET();
262268

263-
/* get vectorcallfunc as in PyVectorcall_Function, but without
269+
/* get vectorcallfunc as in _PyVectorcall_Function, but without
264270
* the Py_TPFLAGS_HAVE_VECTORCALL check */
265271
Py_ssize_t offset = Py_TYPE(callable)->tp_vectorcall_offset;
266272
if (offset <= 0) {
@@ -284,6 +290,24 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
284290
}
285291

286292

293+
PyObject *
294+
PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
295+
size_t nargsf, PyObject *kwnames)
296+
{
297+
PyThreadState *tstate = _PyThreadState_GET();
298+
return _PyObject_VectorcallTstate(tstate, callable,
299+
args, nargsf, kwnames);
300+
}
301+
302+
303+
PyObject *
304+
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
305+
{
306+
PyThreadState *tstate = _PyThreadState_GET();
307+
return _PyObject_FastCallTstate(tstate, func, args, nargs);
308+
}
309+
310+
287311
PyObject *
288312
_PyObject_Call(PyThreadState *tstate, PyObject *callable,
289313
PyObject *args, PyObject *kwargs)
@@ -298,7 +322,7 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable,
298322
assert(PyTuple_Check(args));
299323
assert(kwargs == NULL || PyDict_Check(kwargs));
300324

301-
vectorcallfunc vector_func = PyVectorcall_Function(callable);
325+
vectorcallfunc vector_func = _PyVectorcall_Function(callable);
302326
if (vector_func != NULL) {
303327
return _PyVectorcall_Call(tstate, vector_func, callable, args, kwargs);
304328
}
@@ -339,6 +363,19 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
339363
}
340364

341365

366+
PyObject *
367+
PyObject_CallOneArg(PyObject *func, PyObject *arg)
368+
{
369+
assert(arg != NULL);
370+
PyObject *_args[2];
371+
PyObject **args = _args + 1; // For PY_VECTORCALL_ARGUMENTS_OFFSET
372+
args[0] = arg;
373+
PyThreadState *tstate = _PyThreadState_GET();
374+
size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
375+
return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);
376+
}
377+
378+
342379
/* --- PyFunction call functions ---------------------------------- */
343380

344381
PyObject *

Objects/classobject.c

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Class object implementation (dead now except for methods) */
22

33
#include "Python.h"
4+
#include "pycore_call.h" // _PyObject_VectorcallTstate()
45
#include "pycore_object.h"
56
#include "pycore_pyerrors.h"
67
#include "pycore_pystate.h" // _PyThreadState_GET()

Python/context.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include "Python.h"
2-
2+
#include "pycore_call.h" // _PyObject_VectorcallTstate()
33
#include "pycore_context.h"
44
#include "pycore_gc.h" // _PyObject_GC_MAY_BE_TRACKED()
55
#include "pycore_hamt.h"

0 commit comments

Comments
 (0)