From 281e84da03bba20e7f8e7923e6e1b685bcbe0936 Mon Sep 17 00:00:00 2001 From: ahgamut <41098605+ahgamut@users.noreply.github.com> Date: Fri, 19 Nov 2021 09:07:30 +0530 Subject: [PATCH] LOAD_METHOD support for C methods refer python/cpython@5566bbb8d56 refer python/cpython@93fac8dd358 refer python/cpython@7638eb892a5 refer python/cpython@25b87d35675 useful to keep the latest tag of 3.7 nearby when small things like function signatures change, helps to view the final form. --- third_party/python/Include/descrobject.h | 2 ++ third_party/python/Include/methodobject.h | 8 +++++ third_party/python/Objects/descrobject.c | 38 +++++++++++++++++++++++ third_party/python/Python/ceval.c | 32 +++++++++++++++++-- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/third_party/python/Include/descrobject.h b/third_party/python/Include/descrobject.h index bc917eba4..eaa6f989f 100644 --- a/third_party/python/Include/descrobject.h +++ b/third_party/python/Include/descrobject.h @@ -91,6 +91,8 @@ PyObject * PyDescr_NewMember(PyTypeObject *, PyObject * PyDescr_NewGetSet(PyTypeObject *, struct PyGetSetDef *); #ifndef Py_LIMITED_API +PyObject * _PyMethodDescr_FastCallKeywords( + PyObject *descrobj, PyObject *const *stack, Py_ssize_t nargs, PyObject *kwnames); PyObject * PyDescr_NewWrapper(PyTypeObject *, struct wrapperbase *, void *); #define PyDescr_IsData(d) (Py_TYPE(d)->tp_descr_set != NULL) diff --git a/third_party/python/Include/methodobject.h b/third_party/python/Include/methodobject.h index 25c0c2675..7ed2ff033 100644 --- a/third_party/python/Include/methodobject.h +++ b/third_party/python/Include/methodobject.h @@ -95,12 +95,20 @@ typedef struct { PyObject *m_module; /* The __module__ attribute, can be anything */ PyObject *m_weakreflist; /* List of weak references */ } PyCFunctionObject; + PyObject * _PyMethodDef_RawFastCallDict( PyMethodDef *method, PyObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwargs); + +PyObject * _PyMethodDef_RawFastCallKeywords( + PyMethodDef *method, + PyObject *self, + PyObject **args, + Py_ssize_t nargs, + PyObject *kwnames); #endif int PyCFunction_ClearFreeList(void); diff --git a/third_party/python/Objects/descrobject.c b/third_party/python/Objects/descrobject.c index be831695e..64c386d2f 100644 --- a/third_party/python/Objects/descrobject.c +++ b/third_party/python/Objects/descrobject.c @@ -264,6 +264,44 @@ methoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwargs) return result; } +// same to methoddescr_call(), but use FASTCALL convention. +PyObject * +_PyMethodDescr_FastCallKeywords(PyObject *descrobj, + PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames) +{ + assert(Py_TYPE(descrobj) == &PyMethodDescr_Type); + PyMethodDescrObject *descr = (PyMethodDescrObject *)descrobj; + PyObject *self, *result; + + /* Make sure that the first argument is acceptable as 'self' */ + if (nargs < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' of '%.100s' " + "object needs an argument", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name); + return NULL; + } + self = args[0]; + if (!_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), + (PyObject *)PyDescr_TYPE(descr))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' " + "requires a '%.100s' object " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name, + self->ob_type->tp_name); + return NULL; + } + + result = _PyMethodDef_RawFastCallKeywords(descr->d_method, self, + args+1, nargs-1, kwnames); + result = _Py_CheckFunctionResult((PyObject *)descr, result, NULL); + return result; +} + static PyObject * classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) diff --git a/third_party/python/Python/ceval.c b/third_party/python/Python/ceval.c index 5bace5cf9..5ee2d60a3 100644 --- a/third_party/python/Python/ceval.c +++ b/third_party/python/Python/ceval.c @@ -13,6 +13,7 @@ #include "third_party/python/Include/ceval.h" #include "third_party/python/Include/classobject.h" #include "third_party/python/Include/code.h" +#include "third_party/python/Include/descrobject.h" #include "third_party/python/Include/dictobject.h" #include "third_party/python/Include/eval.h" #include "third_party/python/Include/frameobject.h" @@ -4820,16 +4821,41 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) PyObject *x, *w; Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); Py_ssize_t nargs = oparg - nkwargs; - PyObject **stack; + PyObject **stack = (*pp_stack) - nargs - nkwargs; /* Always dispatch PyCFunction first, because these are presumed to be the most frequent callable object. */ if (PyCFunction_Check(func)) { PyThreadState *tstate = PyThreadState_GET(); PCALL(PCALL_CFUNCTION); - stack = (*pp_stack) - nargs - nkwargs; C_TRACE(x, _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames)); } + else if (Py_TYPE(func) == &PyMethodDescr_Type) { + PyThreadState *tstate = PyThreadState_GET(); + if (nargs > 0 && tstate->use_tracing) { + /* We need to create a temporary bound method as argument + for profiling. + + If nargs == 0, then this cannot work because we have no + "self". In any case, the call itself would raise + TypeError (foo needs an argument), so we just skip + profiling. */ + PyObject *self = stack[0]; + func = Py_TYPE(func)->tp_descr_get(func, self, (PyObject*)Py_TYPE(self)); + if (func != NULL) { + C_TRACE(x, _PyCFunction_FastCallKeywords(func, + stack+1, nargs-1, + kwnames)); + Py_DECREF(func); + } + else { + x = NULL; + } + } + else { + x = _PyMethodDescr_FastCallKeywords(func, stack, nargs, kwnames); + } + } else { if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { /* optimize access to bound methods */ @@ -4841,11 +4867,11 @@ call_function(PyObject ***pp_stack, Py_ssize_t oparg, PyObject *kwnames) Py_INCREF(func); Py_SETREF(*pfunc, self); nargs++; + stack--; } else { Py_INCREF(func); } - stack = (*pp_stack) - nargs - nkwargs; if (PyFunction_Check(func)) { x = _PyFunction_FastCallKeywords(func, stack, nargs, kwnames); }