-
-
Notifications
You must be signed in to change notification settings - Fork 31k
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
gh-98003: Inline call frames for CALL_FUNCTION_EX #98004
Changes from 15 commits
830084c
d9fbb11
e724666
01cbef6
92fead8
84d0f8b
1955f19
eb25c1b
3f409da
09b2528
f0d77dc
898c75d
b933f6e
06ddc00
bfab4dc
80194fb
dfd6b01
1afff6d
f1a9465
94782c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Complex function calls are now faster and consume no C stack | ||
space. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -217,6 +217,9 @@ static _PyInterpreterFrame * | |
_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, | ||
PyObject *locals, PyObject* const* args, | ||
size_t argcount, PyObject *kwnames); | ||
static _PyInterpreterFrame * | ||
_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func, | ||
PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs); | ||
static void | ||
_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame); | ||
|
||
|
@@ -2005,6 +2008,48 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, | |
return NULL; | ||
} | ||
|
||
/* Same as _PyEvalFramePushAndInit but takes an args tuple and kwargs dict. | ||
Steals references to func, callargs and kwargs. | ||
*/ | ||
static _PyInterpreterFrame * | ||
_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func, | ||
PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs) | ||
{ | ||
bool has_dict = (kwargs != NULL && PyDict_GET_SIZE(kwargs) > 0); | ||
PyObject *kwnames = NULL; | ||
PyObject *const *newargs = has_dict | ||
? _PyStack_UnpackDict(tstate, _PyTuple_ITEMS(callargs), | ||
nargs, kwargs, &kwnames) | ||
: &PyTuple_GET_ITEM(callargs, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this safe if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be. When |
||
if (newargs == NULL) { | ||
Py_DECREF(func); | ||
goto error; | ||
} | ||
if (!has_dict) { | ||
/* We need to incref all our args since the new frame steals the references. */ | ||
for (Py_ssize_t i = 0; i < nargs; ++i) { | ||
Py_INCREF(PyTuple_GET_ITEM(callargs, i)); | ||
} | ||
} | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than transforming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can work on this in another PR. |
||
tstate, (PyFunctionObject *)func, locals, | ||
newargs, nargs, kwnames | ||
); | ||
if (has_dict) { | ||
_PyStack_UnpackDict_FreeNoDecRef(newargs, kwnames); | ||
} | ||
/* No need to decref func here because the reference has been stolen by | ||
_PyEvalFramePushAndInit. | ||
*/ | ||
Py_DECREF(callargs); | ||
Py_XDECREF(kwargs); | ||
return new_frame; | ||
error: | ||
Py_DECREF(callargs); | ||
Py_XDECREF(kwargs); | ||
return NULL; | ||
} | ||
|
||
static void | ||
clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) | ||
{ | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense to move the unpacking of the tuple into
_PyEvalFramePushAndInit_Ex
.CALL_FUNCTION_EX
is quite a large instruction. Pushing more code into_PyEvalFramePushAndInit_Ex
would keep it smaller.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unpacking which tuple?
If you mean move the code above in, that would break convention with the rest of the code base. The other inline calls do these
before creating the new frame.