Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit edfb267

Browse files
author
Anselm Kruis
committed
Merge branch 3.8 into 3.8-slp
The outcome of this merge does not compile
2 parents ed6fbca + bf8e82f commit edfb267

File tree

10 files changed

+372
-110
lines changed

10 files changed

+372
-110
lines changed

Include/descrobject.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,6 @@ PyAPI_FUNC(PyObject *) PyDescr_NewMember(PyTypeObject *,
9797
PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *,
9898
struct PyGetSetDef *);
9999
#ifndef Py_LIMITED_API
100-
101-
PyAPI_FUNC(PyObject *) _PyMethodDescr_Vectorcall(
102-
PyObject *descrobj, PyObject *const *args, size_t nargsf, PyObject *kwnames);
103100
PyAPI_FUNC(PyObject *) PyDescr_NewWrapper(PyTypeObject *,
104101
struct wrapperbase *, void *);
105102
#define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL)

Include/methodobject.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ PyAPI_FUNC(PyObject *) _PyCFunction_FastCallDict(PyObject *func,
4646
PyObject *const *args,
4747
Py_ssize_t nargs,
4848
PyObject *kwargs);
49-
50-
PyAPI_FUNC(PyObject *) _PyCFunction_Vectorcall(PyObject *func,
51-
PyObject *const *stack,
52-
size_t nargsf,
53-
PyObject *kwnames);
5449
#endif
5550

5651
struct PyMethodDef {

Lib/test/test_call.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,8 @@ def __call__(self, *args):
586586
return super().__call__(*args)
587587

588588
calls += [
589+
(dict.update, ({},), {"key":True}, None),
590+
({}.update, ({},), {"key":True}, None),
589591
(MethodDescriptorHeap(), (0,), {}, True),
590592
(MethodDescriptorOverridden(), (0,), {}, 'new'),
591593
(MethodDescriptorSuper(), (0,), {}, True),

Lib/test/test_gdb.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -850,10 +850,10 @@ def test_pycfunction(self):
850850
# called, so test a variety of calling conventions.
851851
for py_name, py_args, c_name, expected_frame_number in (
852852
('gmtime', '', 'time_gmtime', 1), # METH_VARARGS
853-
('len', '[]', 'builtin_len', 2), # METH_O
854-
('locals', '', 'builtin_locals', 2), # METH_NOARGS
855-
('iter', '[]', 'builtin_iter', 2), # METH_FASTCALL
856-
('sorted', '[]', 'builtin_sorted', 2), # METH_FASTCALL|METH_KEYWORDS
853+
('len', '[]', 'builtin_len', 1), # METH_O
854+
('locals', '', 'builtin_locals', 1), # METH_NOARGS
855+
('iter', '[]', 'builtin_iter', 1), # METH_FASTCALL
856+
('sorted', '[]', 'builtin_sorted', 1), # METH_FASTCALL|METH_KEYWORDS
857857
):
858858
with self.subTest(c_name):
859859
cmd = ('from time import gmtime\n' # (not always needed)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Implemented separate vectorcall functions for every calling convention of
2+
builtin functions and methods. This improves performance for calls.

Objects/call.c

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwargs)
218218
Py_DECREF(kwnames);
219219
}
220220

221-
return result;
221+
return _Py_CheckFunctionResult(callable, result, NULL);
222222
}
223223

224224

@@ -815,28 +815,6 @@ _PyMethodDef_RawFastCallKeywords(PyMethodDef *method, PyObject *self,
815815
}
816816

817817

818-
PyObject *
819-
_PyCFunction_Vectorcall(PyObject *func,
820-
PyObject *const *args, size_t nargsf,
821-
PyObject *kwnames)
822-
{
823-
STACKLESS_VECTORCALL_GETARG(_PyCFunction_Vectorcall);
824-
PyObject *result;
825-
826-
assert(func != NULL);
827-
assert(PyCFunction_Check(func));
828-
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
829-
830-
STACKLESS_PROMOTE_ALL();
831-
result = _PyMethodDef_RawFastCallKeywords(((PyCFunctionObject*)func)->m_ml,
832-
PyCFunction_GET_SELF(func),
833-
args, nargs, kwnames);
834-
STACKLESS_ASSERT();
835-
result = _Py_CheckFunctionResult(func, result, NULL);
836-
return result;
837-
}
838-
839-
840818
static PyObject *
841819
cfunction_call_varargs(PyObject *func, PyObject *args, PyObject *kwargs)
842820
{

Objects/descrobject.c

Lines changed: 196 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -227,86 +227,199 @@ getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
227227
return -1;
228228
}
229229

230-
static PyObject *
231-
methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs)
232-
{
233-
STACKLESS_GETARG();
234-
Py_ssize_t nargs;
235-
PyObject *self, *result;
236230

237-
/* Make sure that the first argument is acceptable as 'self' */
238-
assert(PyTuple_Check(args));
239-
nargs = PyTuple_GET_SIZE(args);
231+
/* Vectorcall functions for each of the PyMethodDescr calling conventions.
232+
*
233+
* First, common helpers
234+
*/
235+
static const char *
236+
get_name(PyObject *func) {
237+
assert(PyObject_TypeCheck(func, &PyMethodDescr_Type));
238+
return ((PyMethodDescrObject *)func)->d_method->ml_name;
239+
}
240+
241+
typedef void (*funcptr)(void);
242+
243+
static inline int
244+
method_check_args(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
245+
{
246+
assert(!PyErr_Occurred());
247+
assert(PyObject_TypeCheck(func, &PyMethodDescr_Type));
240248
if (nargs < 1) {
241249
PyErr_Format(PyExc_TypeError,
242-
"descriptor '%V' of '%.100s' "
250+
"descriptor '%.200s' of '%.100s' "
243251
"object needs an argument",
244-
descr_name((PyDescrObject *)descr), "?",
245-
PyDescr_TYPE(descr)->tp_name);
246-
return NULL;
252+
get_name(func), PyDescr_TYPE(func)->tp_name);
253+
return -1;
247254
}
248-
self = PyTuple_GET_ITEM(args, 0);
255+
PyObject *self = args[0];
249256
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
250-
(PyObject *)PyDescr_TYPE(descr))) {
257+
(PyObject *)PyDescr_TYPE(func)))
258+
{
251259
PyErr_Format(PyExc_TypeError,
252-
"descriptor '%V' for '%.100s' objects "
260+
"descriptor '%.200s' for '%.100s' objects "
253261
"doesn't apply to a '%.100s' object",
254-
descr_name((PyDescrObject *)descr), "?",
255-
PyDescr_TYPE(descr)->tp_name,
256-
self->ob_type->tp_name);
262+
get_name(func), PyDescr_TYPE(func)->tp_name,
263+
Py_TYPE(self)->tp_name);
264+
return -1;
265+
}
266+
if (kwnames && PyTuple_GET_SIZE(kwnames)) {
267+
PyErr_Format(PyExc_TypeError,
268+
"%.200s() takes no keyword arguments", get_name(func));
269+
return -1;
270+
}
271+
return 0;
272+
}
273+
274+
static inline funcptr
275+
method_enter_call(PyObject *func)
276+
{
277+
if (Py_EnterRecursiveCall(" while calling a Python object")) {
257278
return NULL;
258279
}
280+
return (funcptr)((PyMethodDescrObject *)func)->d_method->ml_meth;
281+
}
259282

260-
STACKLESS_PROMOTE_ALL();
261-
result = _PyMethodDef_RawFastCallDict(descr->d_method, self,
262-
&_PyTuple_ITEMS(args)[1], nargs - 1,
263-
kwargs);
264-
STACKLESS_ASSERT();
265-
result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL);
283+
/* Now the actual vectorcall functions */
284+
static PyObject *
285+
method_vectorcall_VARARGS(
286+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
287+
{
288+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
289+
if (method_check_args(func, args, nargs, kwnames)) {
290+
return NULL;
291+
}
292+
PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1);
293+
if (argstuple == NULL) {
294+
return NULL;
295+
}
296+
PyCFunction meth = (PyCFunction)method_enter_call(func);
297+
if (meth == NULL) {
298+
Py_DECREF(argstuple);
299+
return NULL;
300+
}
301+
PyObject *result = meth(args[0], argstuple);
302+
Py_DECREF(argstuple);
303+
Py_LeaveRecursiveCall();
266304
return result;
267305
}
268306

269-
// same to methoddescr_call(), but use FASTCALL convention.
270-
PyObject *
271-
_PyMethodDescr_Vectorcall(PyObject *descrobj,
272-
PyObject *const *args, size_t nargsf,
273-
PyObject *kwnames)
307+
static PyObject *
308+
method_vectorcall_VARARGS_KEYWORDS(
309+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
274310
{
275-
STACKLESS_VECTORCALL_GETARG(_PyMethodDescr_Vectorcall);
276-
assert(Py_TYPE(descrobj) == &PyMethodDescr_Type);
277-
PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj;
278-
PyObject *self, *result;
311+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
312+
if (method_check_args(func, args, nargs, NULL)) {
313+
return NULL;
314+
}
315+
PyObject *argstuple = _PyTuple_FromArray(args+1, nargs-1);
316+
if (argstuple == NULL) {
317+
return NULL;
318+
}
319+
PyObject *result = NULL;
320+
/* Create a temporary dict for keyword arguments */
321+
PyObject *kwdict = NULL;
322+
if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) > 0) {
323+
kwdict = _PyStack_AsDict(args + nargs, kwnames);
324+
if (kwdict == NULL) {
325+
goto exit;
326+
}
327+
}
328+
PyCFunctionWithKeywords meth = (PyCFunctionWithKeywords)
329+
method_enter_call(func);
330+
if (meth == NULL) {
331+
goto exit;
332+
}
333+
result = meth(args[0], argstuple, kwdict);
334+
Py_LeaveRecursiveCall();
335+
exit:
336+
Py_DECREF(argstuple);
337+
Py_XDECREF(kwdict);
338+
return result;
339+
}
279340

341+
static PyObject *
342+
method_vectorcall_FASTCALL(
343+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
344+
{
280345
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
281-
/* Make sure that the first argument is acceptable as 'self' */
282-
if (nargs < 1) {
283-
PyErr_Format(PyExc_TypeError,
284-
"descriptor '%V' of '%.100s' "
285-
"object needs an argument",
286-
descr_name((PyDescrObject *)descr), "?",
287-
PyDescr_TYPE(descr)->tp_name);
346+
if (method_check_args(func, args, nargs, kwnames)) {
288347
return NULL;
289348
}
290-
self = args[0];
291-
if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self),
292-
(PyObject *)PyDescr_TYPE(descr))) {
349+
_PyCFunctionFast meth = (_PyCFunctionFast)
350+
method_enter_call(func);
351+
if (meth == NULL) {
352+
return NULL;
353+
}
354+
PyObject *result = meth(args[0], args+1, nargs-1);
355+
Py_LeaveRecursiveCall();
356+
return result;
357+
}
358+
359+
static PyObject *
360+
method_vectorcall_FASTCALL_KEYWORDS(
361+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
362+
{
363+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
364+
if (method_check_args(func, args, nargs, NULL)) {
365+
return NULL;
366+
}
367+
_PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)
368+
method_enter_call(func);
369+
if (meth == NULL) {
370+
return NULL;
371+
}
372+
PyObject *result = meth(args[0], args+1, nargs-1, kwnames);
373+
Py_LeaveRecursiveCall();
374+
return result;
375+
}
376+
377+
static PyObject *
378+
method_vectorcall_NOARGS(
379+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
380+
{
381+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
382+
if (method_check_args(func, args, nargs, kwnames)) {
383+
return NULL;
384+
}
385+
if (nargs != 1) {
293386
PyErr_Format(PyExc_TypeError,
294-
"descriptor '%V' for '%.100s' objects "
295-
"doesn't apply to a '%.100s' object",
296-
descr_name((PyDescrObject *)descr), "?",
297-
PyDescr_TYPE(descr)->tp_name,
298-
self->ob_type->tp_name);
387+
"%.200s() takes no arguments (%zd given)", get_name(func), nargs-1);
299388
return NULL;
300389
}
390+
PyCFunction meth = (PyCFunction)method_enter_call(func);
391+
if (meth == NULL) {
392+
return NULL;
393+
}
394+
PyObject *result = meth(args[0], NULL);
395+
Py_LeaveRecursiveCall();
396+
return result;
397+
}
301398

302-
STACKLESS_PROMOTE_ALL();
303-
result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self,
304-
args+1, nargs-1, kwnames);
305-
STACKLESS_ASSERT();
306-
result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL);
399+
static PyObject *
400+
method_vectorcall_O(
401+
PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames)
402+
{
403+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
404+
if (method_check_args(func, args, nargs, kwnames)) {
405+
return NULL;
406+
}
407+
if (nargs != 2) {
408+
PyErr_Format(PyExc_TypeError,
409+
"%.200s() takes exactly one argument (%zd given)",
410+
get_name(func), nargs-1);
411+
return NULL;
412+
}
413+
PyCFunction meth = (PyCFunction)method_enter_call(func);
414+
if (meth == NULL) {
415+
return NULL;
416+
}
417+
PyObject *result = meth(args[0], args[1]);
418+
Py_LeaveRecursiveCall();
307419
return result;
308420
}
309421

422+
310423
static PyObject *
311424
classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args,
312425
PyObject *kwds)
@@ -579,7 +692,7 @@ PyTypeObject PyMethodDescr_Type = {
579692
0, /* tp_as_sequence */
580693
SLP_TP_AS_MAPPING(descr_as_mapping), /* tp_as_mapping */
581694
0, /* tp_hash */
582-
(ternaryfunc)methoddescr_call, /* tp_call */
695+
PyVectorcall_Call, /* tp_call */
583696
0, /* tp_str */
584697
PyObject_GenericGetAttr, /* tp_getattro */
585698
0, /* tp_setattro */
@@ -783,13 +896,40 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)
783896
PyObject *
784897
PyDescr_NewMethod(PyTypeObject *type, PyMethodDef *method)
785898
{
899+
/* Figure out correct vectorcall function to use */
900+
vectorcallfunc vectorcall;
901+
switch (method->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS))
902+
{
903+
case METH_VARARGS:
904+
vectorcall = method_vectorcall_VARARGS;
905+
break;
906+
case METH_VARARGS | METH_KEYWORDS:
907+
vectorcall = method_vectorcall_VARARGS_KEYWORDS;
908+
break;
909+
case METH_FASTCALL:
910+
vectorcall = method_vectorcall_FASTCALL;
911+
break;
912+
case METH_FASTCALL | METH_KEYWORDS:
913+
vectorcall = method_vectorcall_FASTCALL_KEYWORDS;
914+
break;
915+
case METH_NOARGS:
916+
vectorcall = method_vectorcall_NOARGS;
917+
break;
918+
case METH_O:
919+
vectorcall = method_vectorcall_O;
920+
break;
921+
default:
922+
PyErr_SetString(PyExc_SystemError, "bad call flags");
923+
return NULL;
924+
}
925+
786926
PyMethodDescrObject *descr;
787927

788928
descr = (PyMethodDescrObject *)descr_new(&PyMethodDescr_Type,
789929
type, method->ml_name);
790930
if (descr != NULL) {
791931
descr->d_method = method;
792-
descr->vectorcall = _PyMethodDescr_Vectorcall;
932+
descr->vectorcall = vectorcall;
793933
}
794934
return (PyObject *)descr;
795935
}

0 commit comments

Comments
 (0)