Skip to main content

Objects/codeobject.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/codeobject.c

This annotation covers the 3.11+ column-tracking (co_positions), the new location table encoding, and adaptive specialization plumbing. See objects_codeobject_detail for the basic PyCodeObject layout and creation.

Map

LinesSymbolRole
1-100Location table encodingco_linetable stores (line, col, end_col) per instruction
101-250_PyCode_InitAddressRangeIterator for the location table
251-400co_positions()Return iterator of (line, end_line, col, end_col) per instruction
401-550_PyCode_QuickenReplace instructions with adaptive variants for specialization
551-700_PyCode_GetVarnamesBuild co_varnames tuple from the localsplus array
701-1000_PyCode_NewInternal constructor used by the compiler

Reading

Location table encoding

// CPython: Objects/codeobject.c:48 co_linetable
/* co_linetable is a compact encoding of (line, end_line, col, end_col)
for every instruction in the code object.
Format (per entry):
- 1 or more bytes encoding the instruction span and position delta
- Uses a variable-length code with a 3-bit opcode in the high bits
This replaced the old lnotab in 3.11. */

The new format is more compact than lnotab and stores column information needed for SyntaxError caret highlighting and co_positions().

_PyCode_InitAddressRange

// CPython: Objects/codeobject.c:130 _PyCode_InitAddressRange
void
_PyCode_InitAddressRange(PyCodeObject *co, PyCodeAddressRange *bounds)
{
bounds->opaque.lo_next = (const char *)co->co_linetable->ob_val;
bounds->opaque.limit = bounds->opaque.lo_next + Py_SIZE(co->co_linetable);
bounds->ar_start = -1;
bounds->ar_end = 0;
bounds->opaque.computed_line = co->co_firstlineno;
bounds->ar_line = -1;
}

co_positions()

// CPython: Objects/codeobject.c:310 code_co_positions
static PyObject *
code_co_positions(PyObject *self, PyObject *noargs)
{
/* Returns an iterator yielding (line, end_line, col_offset, end_col_offset)
for each instruction. Used by dis.findlinestarts and traceback. */
return new_linesiterator((PyCodeObject *)self);
}

inspect.getsource() and traceback use co_positions() to pinpoint the exact expression that raised an error.

_PyCode_Quicken

// CPython: Objects/codeobject.c:480 _PyCode_Quicken
void
_PyCode_Quicken(PyCodeObject *code)
{
/* Replace each non-adaptive instruction with its ADAPTIVE_ variant.
E.g. LOAD_ATTR → LOAD_ATTR_ADAPTIVE
The adaptive variant collects type feedback on the first few calls
then specializes to LOAD_ATTR_MODULE, LOAD_ATTR_SLOT, etc. */
_Py_CODEUNIT *instructions = _PyCode_CODE(code);
Py_ssize_t n = Py_SIZE(code);
for (int i = 0; i < n; i++) {
uint8_t opcode = _Py_OPCODE(instructions[i]);
if (_PyOpcode_Caches[opcode]) {
instructions[i + 1].cache = adaptive_counter_warmup();
}
}
}

_PyCode_Quicken is called the first time a code object is executed. Each specializable opcode gets a warmup counter in its inline cache slot.

_PyCode_GetVarnames

// CPython: Objects/codeobject.c:620 _PyCode_GetVarnames
/* co_varnames: tuple of names for local variables
(first co_nlocals entries of localsplus, in slot order) */
static PyObject *
_PyCode_GetVarnames(PyCodeObject *co)
{
return _PyCode_GetLocalVariables(co, CO_FAST_LOCAL);
}

co_varnames only includes CO_FAST_LOCAL slots; free variables and cell variables are reported separately via co_freevars and co_cellvars.

gopy notes

The location table encoder is in compile/codegen_stmt_misc.go. _PyCode_Quicken has a gopy equivalent in vm/specialize.go:QuickenCode. co_positions() returns a Go iterator backed by the same encoded table. _PyCode_InitAddressRange is vm.NewAddressRange in vm/address_range.go.