Skip to content

Commit ed95e8c

Browse files
gh-98003: Inline call frames for CALL_FUNCTION_EX (GH-98004)
1 parent accb417 commit ed95e8c

File tree

6 files changed

+148
-55
lines changed

6 files changed

+148
-55
lines changed

Include/internal/pycore_call.h

+10
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ _PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const
116116
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
117117
}
118118

119+
PyObject *const *
120+
_PyStack_UnpackDict(PyThreadState *tstate,
121+
PyObject *const *args, Py_ssize_t nargs,
122+
PyObject *kwargs, PyObject **p_kwnames);
123+
124+
void
125+
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
126+
PyObject *kwnames);
127+
128+
void _PyStack_UnpackDict_FreeNoDecRef(PyObject *const *stack, PyObject *kwnames);
119129

120130
#ifdef __cplusplus
121131
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Complex function calls are now faster and consume no C stack
2+
space.
3+

Objects/call.c

+8-12
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,6 @@
88
#include "pycore_tuple.h" // _PyTuple_ITEMS()
99

1010

11-
static PyObject *const *
12-
_PyStack_UnpackDict(PyThreadState *tstate,
13-
PyObject *const *args, Py_ssize_t nargs,
14-
PyObject *kwargs, PyObject **p_kwnames);
15-
16-
static void
17-
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
18-
PyObject *kwnames);
19-
20-
2111
static PyObject *
2212
null_error(PyThreadState *tstate)
2313
{
@@ -965,7 +955,7 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames)
965955
The newly allocated argument vector supports PY_VECTORCALL_ARGUMENTS_OFFSET.
966956
967957
When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */
968-
static PyObject *const *
958+
PyObject *const *
969959
_PyStack_UnpackDict(PyThreadState *tstate,
970960
PyObject *const *args, Py_ssize_t nargs,
971961
PyObject *kwargs, PyObject **p_kwnames)
@@ -1034,14 +1024,20 @@ _PyStack_UnpackDict(PyThreadState *tstate,
10341024
return stack;
10351025
}
10361026

1037-
static void
1027+
void
10381028
_PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
10391029
PyObject *kwnames)
10401030
{
10411031
Py_ssize_t n = PyTuple_GET_SIZE(kwnames) + nargs;
10421032
for (Py_ssize_t i = 0; i < n; i++) {
10431033
Py_DECREF(stack[i]);
10441034
}
1035+
_PyStack_UnpackDict_FreeNoDecRef(stack, kwnames);
1036+
}
1037+
1038+
void
1039+
_PyStack_UnpackDict_FreeNoDecRef(PyObject *const *stack, PyObject *kwnames)
1040+
{
10451041
PyMem_Free((PyObject **)stack - 1);
10461042
Py_DECREF(kwnames);
10471043
}

Python/bytecodes.c

+19
Original file line numberDiff line numberDiff line change
@@ -3103,6 +3103,25 @@ dummy_func(
31033103
}
31043104
}
31053105
else {
3106+
if (Py_TYPE(func) == &PyFunction_Type &&
3107+
tstate->interp->eval_frame == NULL &&
3108+
((PyFunctionObject *)func)->vectorcall == _PyFunction_Vectorcall) {
3109+
assert(PyTuple_CheckExact(callargs));
3110+
Py_ssize_t nargs = PyTuple_GET_SIZE(callargs);
3111+
int code_flags = ((PyCodeObject *)PyFunction_GET_CODE(func))->co_flags;
3112+
PyObject *locals = code_flags & CO_OPTIMIZED ? NULL : Py_NewRef(PyFunction_GET_GLOBALS(func));
3113+
3114+
_PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit_Ex(tstate,
3115+
(PyFunctionObject *)func, locals,
3116+
nargs, callargs, kwargs);
3117+
// Need to manually shrink the stack since we exit with DISPATCH_INLINED.
3118+
STACK_SHRINK(oparg + 3);
3119+
if (new_frame == NULL) {
3120+
goto error;
3121+
}
3122+
frame->return_offset = 0;
3123+
DISPATCH_INLINED(new_frame);
3124+
}
31063125
result = PyObject_Call(func, callargs, kwargs);
31073126
}
31083127
DECREF_INPUTS();

Python/ceval.c

+46
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ static _PyInterpreterFrame *
212212
_PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
213213
PyObject *locals, PyObject* const* args,
214214
size_t argcount, PyObject *kwnames);
215+
static _PyInterpreterFrame *
216+
_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func,
217+
PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs);
215218
static void
216219
_PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
217220

@@ -1501,6 +1504,49 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
15011504
return NULL;
15021505
}
15031506

1507+
/* Same as _PyEvalFramePushAndInit but takes an args tuple and kwargs dict.
1508+
Steals references to func, callargs and kwargs.
1509+
*/
1510+
static _PyInterpreterFrame *
1511+
_PyEvalFramePushAndInit_Ex(PyThreadState *tstate, PyFunctionObject *func,
1512+
PyObject *locals, Py_ssize_t nargs, PyObject *callargs, PyObject *kwargs)
1513+
{
1514+
bool has_dict = (kwargs != NULL && PyDict_GET_SIZE(kwargs) > 0);
1515+
PyObject *kwnames = NULL;
1516+
PyObject *const *newargs;
1517+
if (has_dict) {
1518+
newargs = _PyStack_UnpackDict(tstate, _PyTuple_ITEMS(callargs), nargs, kwargs, &kwnames);
1519+
if (newargs == NULL) {
1520+
Py_DECREF(func);
1521+
goto error;
1522+
}
1523+
}
1524+
else {
1525+
newargs = &PyTuple_GET_ITEM(callargs, 0);
1526+
/* We need to incref all our args since the new frame steals the references. */
1527+
for (Py_ssize_t i = 0; i < nargs; ++i) {
1528+
Py_INCREF(PyTuple_GET_ITEM(callargs, i));
1529+
}
1530+
}
1531+
_PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
1532+
tstate, (PyFunctionObject *)func, locals,
1533+
newargs, nargs, kwnames
1534+
);
1535+
if (has_dict) {
1536+
_PyStack_UnpackDict_FreeNoDecRef(newargs, kwnames);
1537+
}
1538+
/* No need to decref func here because the reference has been stolen by
1539+
_PyEvalFramePushAndInit.
1540+
*/
1541+
Py_DECREF(callargs);
1542+
Py_XDECREF(kwargs);
1543+
return new_frame;
1544+
error:
1545+
Py_DECREF(callargs);
1546+
Py_XDECREF(kwargs);
1547+
return NULL;
1548+
}
1549+
15041550
PyObject *
15051551
_PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func,
15061552
PyObject *locals,

0 commit comments

Comments
 (0)