Skip to main content

Python/ceval.c (part 34)

Source:

cpython 3.14 @ ab2d84fe1023/Python/ceval.c

This annotation covers call specializations. See python_ceval33_detail for STORE_ATTR specializations.

Map

LinesSymbolRole
1-80CALL_PY_EXACT_ARGSCall a Python function with exact positional args
81-180CALL_BOUND_METHOD_EXACT_ARGSCall a bound method (unwrap self + call inner function)
181-280CALL_BUILTIN_FASTCall a C builtin with vectorcall
281-380CALL_BUILTIN_CLASSConstruct a built-in type (int, str, list, etc.)
381-500CALL_METHOD_DESCRIPTOR_FASTCall a method descriptor (e.g. str.upper(s))

Reading

CALL_PY_EXACT_ARGS

// CPython: Python/ceval.c:3120 CALL_PY_EXACT_ARGS
inst(CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- res)) {
/* Cache: func_version tag */
DEOPT_IF(!PyFunction_Check(callable), CALL);
PyFunctionObject *func = (PyFunctionObject *)callable;
DEOPT_IF(func->func_version != cache->func_version, CALL);
PyCodeObject *code = (PyCodeObject *)func->func_code;
DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL), CALL);
/* No defaults, no *args, no **kwargs: push a new frame directly */
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, func, oparg);
for (int i = 0; i < oparg; i++) {
new_frame->localsplus[i] = args[i];
}
DISPATCH_INLINED(new_frame);
}

CALL_PY_EXACT_ARGS is the hot path for f(a, b, c) where f is a Python function with exactly the right number of positional arguments. It bypasses _PyObject_Vectorcall entirely and pushes a new frame directly, saving a substantial amount of work.

CALL_BOUND_METHOD_EXACT_ARGS

// CPython: Python/ceval.c:3180 CALL_BOUND_METHOD_EXACT_ARGS
inst(CALL_BOUND_METHOD_EXACT_ARGS, (callable, unused, args[oparg] -- res)) {
/* Unwrap bound method: callable is a PyMethodObject */
DEOPT_IF(!PyMethod_Check(callable), CALL);
PyObject *self = PyMethod_GET_SELF(callable);
PyObject *func = PyMethod_GET_FUNCTION(callable);
DEOPT_IF(!PyFunction_Check(func), CALL);
DEOPT_IF(((PyFunctionObject *)func)->func_version != cache->func_version, CALL);
/* Push self as the first argument */
PEEK(oparg + 1) = self; /* self replaces NULL slot */
Py_INCREF(self);
/* Continue as CALL_PY_EXACT_ARGS with self prepended */
...
}

obj.method(a, b) compiles to LOAD_METHOD + CALL. CALL_BOUND_METHOD_EXACT_ARGS handles the case where LOAD_METHOD found a bound method: it unwraps self and redirects to the function's frame.

CALL_BUILTIN_FAST

// CPython: Python/ceval.c:3260 CALL_BUILTIN_FAST
inst(CALL_BUILTIN_FAST, (callable, self_or_null, args[oparg] -- res)) {
/* For C functions with METH_FASTCALL | METH_KEYWORDS */
DEOPT_IF(!PyCFunction_CheckExact(callable), CALL);
int flags = ((PyCFunctionObject *)callable)->m_ml->ml_flags;
DEOPT_IF(!(flags & METH_FASTCALL), CALL);
PyCFunction cfunc = ((PyCFunctionObject *)callable)->m_ml->ml_meth;
res = cfunc(((PyCFunctionObject *)callable)->m_self,
(PyObject * const *)(args - (self_or_null != NULL)),
oparg + (self_or_null != NULL));
...
}

METH_FASTCALL C functions receive a PyObject ** array and count directly, without building a tuple. len([1,2,3]), isinstance(x, int), hasattr(obj, 'x') all use this path.

CALL_BUILTIN_CLASS

// CPython: Python/ceval.c:3340 CALL_BUILTIN_CLASS
inst(CALL_BUILTIN_CLASS, (callable, self_or_null, args[oparg] -- res)) {
/* For type objects (int, str, list, dict, etc.) */
DEOPT_IF(!PyType_Check(callable), CALL);
DEOPT_IF(((PyTypeObject *)callable)->tp_new == NULL, CALL);
res = _PyObject_Vectorcall(callable,
(PyObject * const *)(args - (self_or_null != NULL)),
oparg + (self_or_null != NULL) | PY_VECTORCALL_ARGUMENTS_OFFSET,
NULL);
...
}

int(x), str(y), list(it) all go through CALL_BUILTIN_CLASS. The PY_VECTORCALL_ARGUMENTS_OFFSET flag signals that the caller has reserved a slot before args[0] that the callee can use as self.

gopy notes

CALL_PY_EXACT_ARGS is in vm/eval_call.go and calls vm.pushFrame. CALL_BOUND_METHOD_EXACT_ARGS unwraps objects.BoundMethod. CALL_BUILTIN_FAST calls the Go function stored in objects.CFunction.FastCall. CALL_BUILTIN_CLASS calls objects.TypeCall which dispatches to tp_new then tp_init.