Skip to main content

Python/ceval.c (part 50)

Source:

cpython 3.14 @ ab2d84fe1023/Python/ceval.c

This annotation covers FOR_ITER specializations for the most common iterator types. See python_ceval49_detail for SEND, YIELD_VALUE, and async iteration.

Map

LinesSymbolRole
1-80FOR_ITER_LISTSpecialization for list_iterator
81-160FOR_ITER_TUPLESpecialization for tuple_iterator
161-240FOR_ITER_RANGESpecialization for range_iterator
241-320FOR_ITER_GENSpecialization for generator __next__
321-500FOR_ITER (generic)General fallback via tp_iternext

Reading

FOR_ITER_LIST

// CPython: Python/ceval.c:4080 FOR_ITER_LIST
inst(FOR_ITER_LIST, (unused/2, iter -- iter, next)) {
DEOPT_IF(Py_TYPE(iter) != &PyListIter_Type);
_PyListIterObject *it = (_PyListIterObject *)iter;
DEOPT_IF(it->it_index >= PyList_GET_SIZE(it->it_seq));
next = Py_NewRef(PyList_GET_ITEM(it->it_seq, it->it_index++));
DISPATCH();
}

Direct index into ob_item. The guard checks the iterator type and bounds. No function call overhead: just a load and an increment.

FOR_ITER_RANGE

// CPython: Python/ceval.c:4140 FOR_ITER_RANGE
inst(FOR_ITER_RANGE, (unused/2, iter -- iter, next)) {
DEOPT_IF(Py_TYPE(iter) != &PyRangeIter_Type);
_PyRangeIterObject *r = (_PyRangeIterObject *)iter;
DEOPT_IF(r->len <= 0);
next = PyLong_FromLong(r->start);
r->start += r->step;
r->len--;
DISPATCH();
}

for i in range(n) is the most common loop in Python. The specialization reads start, advances it by step, decrements len. No allocation for small integers that hit the [−5, 256] free list.

FOR_ITER_GEN

// CPython: Python/ceval.c:4200 FOR_ITER_GEN
inst(FOR_ITER_GEN, (unused/2, iter -- iter, next)) {
DEOPT_IF(!PyGen_CheckExact(iter));
PyGenObject *gen = (PyGenObject *)iter;
DEOPT_IF(gen->gi_frame_state == FRAME_COMPLETED);
/* Inline generator advancement */
_PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe;
DISPATCH_INLINED(gen_frame);
}

Generator for loops inline the generator frame switch. Instead of calling gen.__next__(), the interpreter switches directly to the generator's frame, avoiding the overhead of a Python call.

gopy notes

FOR_ITER_LIST is in vm/eval_simple.go. It reads directly from objects.List.Items[iter.Index]. FOR_ITER_RANGE reads from rangeIter.Start and advances it. FOR_ITER_GEN calls vm.evalGen inline on the generator frame. All three deoptimize to the generic FOR_ITER which calls objects.IterNext.