Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-91049: Introduce set vectorcall field API for PyFunctionObject#92257

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from24 commits
Commits
Show all changes
26 commits
Select commitHold shift + click to select a range
9cf96ec
Enable setting vectorcall field on PyFunctionObjects
May 3, 2022
282e3dc
check if vectorcall is nondefault before inlining call
May 3, 2022
edcdcf1
📜🤖 Added by blurb_it.
blurb-it[bot]May 3, 2022
debf5a9
Apply suggestions from code review
adphrostMay 3, 2022
473d18d
addressed comments
May 3, 2022
76e8c9d
added to docs
May 3, 2022
3337459
Merge branch 'main' into pyfunctionobject-set-vectorcall-field
Jun 16, 2022
1543f44
updated doc with fix by itamaro
Jun 16, 2022
08082bd
formatting
Jun 16, 2022
ed93327
removed doc from 3.11
Jun 16, 2022
24ffd30
remove lines from 3.11
Jun 16, 2022
89cb4b2
Apply review feedback from vstinner and markshannon
itamaroJun 21, 2022
3387d4a
Merge branch 'main' into gh-91049-set-vectorcall
itamaroSep 2, 2022
b0cc28a
Merge branch 'main' into gh-91049-set-vectorcall
itamaroSep 5, 2022
99e4085
Add deopt on func version in LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
itamaroSep 6, 2022
24353c8
write func version to cache keys version when specializing LOAD_ATTR_…
itamaroSep 6, 2022
60f7769
remove redundant argcount check in LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN …
itamaroSep 6, 2022
376ee75
Add missing periods in docs
itamaroSep 6, 2022
56ffc70
move warning comment to the function C API docs
itamaroSep 6, 2022
5340e87
Merge branch 'main' into pyfunctionobject-set-vectorcall-field
itamaroSep 6, 2022
a5e9d13
fix race with GH-96519 (removed func_version in LOAD_ATTR_GETATTRIBUT…
itamaroSep 6, 2022
12faf52
PEP-7 formatting
itamaroSep 7, 2022
6623470
Address review feedback
itamaroSep 7, 2022
9149f14
Add test for LOAD_ATTR specialization when overriding vectorcall of _…
itamaroSep 8, 2022
e732d7e
Improve setvectorcall + specialization testing
itamaroSep 8, 2022
84874fb
Merge branch 'main' into pyfunctionobject-set-vectorcall-field
itamaroSep 15, 2022
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
9 changes: 9 additions & 0 deletionsDoc/c-api/function.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -83,6 +83,15 @@ There are a few functions specific to Python functions.
Raises:exc:`SystemError` and returns ``-1`` on failure.
..c:function::voidPyFunction_SetVectorcall(PyFunctionObject *func, vectorcallfunc vectorcall)
Set the vectorcall field of a given function object *func*.
Warning: extensions using this API must preserve the behavior
of the unaltered (default) vectorcall function!
.. versionadded:: 3.12
.. c:function:: PyObject* PyFunction_GetClosure(PyObject *op)
Return the closure associated with the function object *op*. This can be ``NULL``
Expand Down
4 changes: 4 additions & 0 deletionsDoc/whatsnew/3.12.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -470,6 +470,10 @@ New Features
``__dict__`` and weakrefs with less bookkeeping,
using less memory and with faster access.

* Added new function :c:func:`PyFunction_SetVectorcall` to the C API
which sets the vectorcall field of a given :c:type:`PyFunctionObject`.
(Contributed by Andrew Frost in :gh:`92257`.)

Porting to Python 3.12
----------------------

Expand Down
4 changes: 3 additions & 1 deletionInclude/cpython/funcobject.h
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -48,7 +48,8 @@ typedef struct {
* defaults
* kwdefaults (only if the object changes, not the contents of the dict)
* code
* annotations */
* annotations
* vectorcall function pointer */
uint32_tfunc_version;

/* Invariant:
Expand All@@ -69,6 +70,7 @@ PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *);
PyAPI_FUNC(PyObject*)PyFunction_GetModule(PyObject*);
PyAPI_FUNC(PyObject*)PyFunction_GetDefaults(PyObject*);
PyAPI_FUNC(int)PyFunction_SetDefaults(PyObject*,PyObject*);
PyAPI_FUNC(void)PyFunction_SetVectorcall(PyFunctionObject*,vectorcallfunc);
PyAPI_FUNC(PyObject*)PyFunction_GetKwDefaults(PyObject*);
PyAPI_FUNC(int)PyFunction_SetKwDefaults(PyObject*,PyObject*);
PyAPI_FUNC(PyObject*)PyFunction_GetClosure(PyObject*);
Expand Down
26 changes: 26 additions & 0 deletionsLib/test/test_call.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -760,6 +760,32 @@ def __call__(self, *args):
self.assertEqual(expected, meth(*args1, **kwargs))
self.assertEqual(expected, wrapped(*args, **kwargs))

def test_setvectorcall(self):
from _testcapi import function_setvectorcall
def f(num): return num + 1
num = 10
self.assertEqual(11, f(num))
function_setvectorcall(f)
# make sure specializer is triggered by running > 50 times
for _ in range(51):
self.assertEqual("overridden", f(num))

def test_setvectorcall_load_attr_specialization(self):
from _testcapi import function_setvectorcall

class X:
def __getattribute__(self, attr):
return attr

x = X()
# trigger LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN specialization
for _ in range(8):
self.assertEqual("a", x.a)
function_setvectorcall(X.__getattribute__)
# make sure specialization doesn't override the override
for _ in range(8):
self.assertEqual("overridden", x.a)

@requires_limited_api
def test_vectorcall_limited(self):
from _testcapi import pyobject_vectorcall
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
Add new function:c:func:`PyFunction_SetVectorcall` to the C API
which sets the vectorcall field of a given:c:type:`PyFunctionObject`.

Warning: extensions using this API must preserve the behavior
of the unaltered function!
19 changes: 19 additions & 0 deletionsModules/_testcapi/vectorcall.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,6 +102,24 @@ test_pyobject_vectorcall(PyObject *self, PyObject *args)
returnPyObject_Vectorcall(func,stack,nargs,kwnames);
}

staticPyObject*
override_vectorcall(PyObject*callable,PyObject*const*args,size_tnargsf,
PyObject*kwnames)
{
returnPyUnicode_FromString("overridden");
}

staticPyObject*
function_setvectorcall(PyObject*self,PyObject*func)
{
if (!PyFunction_Check(func)) {
PyErr_SetString(PyExc_TypeError,"'func' must be a function");
returnNULL;
}
PyFunction_SetVectorcall((PyFunctionObject*)func, (vectorcallfunc)override_vectorcall);
Py_RETURN_NONE;
}

staticPyObject*
test_pyvectorcall_call(PyObject*self,PyObject*args)
{
Expand DownExpand Up@@ -244,6 +262,7 @@ static PyMethodDef TestMethods[] = {
{"pyobject_fastcall",test_pyobject_fastcall,METH_VARARGS},
{"pyobject_fastcalldict",test_pyobject_fastcalldict,METH_VARARGS},
{"pyobject_vectorcall",test_pyobject_vectorcall,METH_VARARGS},
{"function_setvectorcall",function_setvectorcall,METH_O},
{"pyvectorcall_call",test_pyvectorcall_call,METH_VARARGS},
_TESTCAPI_MAKE_VECTORCALL_CLASS_METHODDEF
_TESTCAPI_HAS_VECTORCALL_FLAG_METHODDEF
Expand Down
11 changes: 11 additions & 0 deletionsObjects/funcobject.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -134,6 +134,9 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
if (func->func_version!=0) {
returnfunc->func_version;
}
if (func->vectorcall!=_PyFunction_Vectorcall) {
return0;
}
if (next_func_version==0) {
return0;
}
Expand DownExpand Up@@ -209,6 +212,14 @@ PyFunction_SetDefaults(PyObject *op, PyObject *defaults)
return0;
}

void
PyFunction_SetVectorcall(PyFunctionObject*func,vectorcallfuncvectorcall)
{
assert(func!=NULL);
func->func_version=0;
func->vectorcall=vectorcall;
}

PyObject*
PyFunction_GetKwDefaults(PyObject*op)
{
Expand Down
10 changes: 8 additions & 2 deletionsPython/ceval.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3135,8 +3135,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
PyObject *getattribute = read_obj(cache->descr);
assert(Py_IS_TYPE(getattribute, &PyFunction_Type));
PyFunctionObject *f = (PyFunctionObject *)getattribute;
uint32_t func_version = read_u32(cache->keys_version);
assert(func_version != 0);
DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
PyCodeObject *code = (PyCodeObject *)f->func_code;
DEOPT_IF(((PyCodeObject *)f->func_code)->co_argcount!= 2, LOAD_ATTR);
assert(code->co_argcount== 2);
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
STAT_INC(LOAD_ATTR, hit);

Expand DownExpand Up@@ -4138,7 +4141,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
function = PEEK(total_args + 1);
int positional_args = total_args - KWNAMES_LEN();
// Check if the call can be inlined or not
if (Py_TYPE(function) == &PyFunction_Type && tstate->interp->eval_frame == NULL) {
if (Py_TYPE(function) == &PyFunction_Type &&
tstate->interp->eval_frame == NULL &&
((PyFunctionObject *)function)->vectorcall == _PyFunction_Vectorcall)
{
int code_flags = ((PyCodeObject*)PyFunction_GET_CODE(function))->co_flags;
PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(function));
STACK_SHRINK(total_args);
Expand Down
5 changes: 5 additions & 0 deletionsPython/specialize.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -836,6 +836,11 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
if (!function_check_args(descr, 2, LOAD_ATTR)) {
goto fail;
}
uint32_t version = function_get_version(descr, LOAD_ATTR);
if (version == 0) {
goto fail;
}
write_u32(lm_cache->keys_version, version);
/* borrowed */
write_obj(lm_cache->descr, descr);
write_u32(lm_cache->type_version, type->tp_version_tag);
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp