Skip to main content

Python/ceval.c (part 69)

Source:

cpython 3.14 @ ab2d84fe1023/Python/ceval.c

This annotation covers LOAD_GLOBAL specializations. See python_ceval68_detail for BINARY_SUBSCR specializations.

Map

LinesSymbolRole
1-80LOAD_GLOBAL baselineLook up in f_globals then f_builtins
81-160LOAD_GLOBAL_MODULEFast path: module dict by version + index
161-240LOAD_GLOBAL_BUILTINFast path: builtins dict by version + index
241-340_Py_Specialize_LoadGlobalSpecialization logic
341-500Globals / builtins version tagsCache invalidation

Reading

LOAD_GLOBAL_MODULE

// CPython: Python/ceval.c:2840 LOAD_GLOBAL_MODULE
inst(LOAD_GLOBAL_MODULE, (unused/1 -- res, null if (oparg & 1))) {
uint32_t globals_version = read_u32(&next_instr[-3].cache);
uint16_t index = read_u16(&next_instr[-1].cache);
PyDictObject *dict = (PyDictObject *)GLOBALS();
DEOPT_IF(!PyDict_CheckExact(dict), LOAD_GLOBAL);
DEOPT_IF(dict->ma_version_tag != globals_version, LOAD_GLOBAL);
res = _PyDict_GetItemByIndex(dict, index);
DEOPT_IF(res == NULL, LOAD_GLOBAL);
Py_INCREF(res);
}

LOAD_GLOBAL_MODULE caches the index of the variable in the module dict and a version tag. When the module dict is modified (new import, assignment), the version tag changes, causing a deopt on the next access. O(1) lookup by index.

LOAD_GLOBAL_BUILTIN

// CPython: Python/ceval.c:2880 LOAD_GLOBAL_BUILTIN
inst(LOAD_GLOBAL_BUILTIN, (unused/1 -- res, null if (oparg & 1))) {
uint32_t globals_version = read_u32(&next_instr[-3].cache);
uint32_t builtins_version = read_u32(&next_instr[-2].cache);
uint16_t index = read_u16(&next_instr[-1].cache);
PyDictObject *globals = (PyDictObject *)GLOBALS();
DEOPT_IF(globals->ma_version_tag != globals_version, LOAD_GLOBAL);
PyDictObject *builtins = (PyDictObject *)BUILTINS();
DEOPT_IF(builtins->ma_version_tag != builtins_version, LOAD_GLOBAL);
res = _PyDict_GetItemByIndex(builtins, index);
DEOPT_IF(res == NULL, LOAD_GLOBAL);
Py_INCREF(res);
}

LOAD_GLOBAL_BUILTIN checks both the globals version (to ensure the name isn't shadowed by a local global) and the builtins version. Both must match for the fast path to be safe.

_Py_Specialize_LoadGlobal

// CPython: Python/specialize.c:640 _Py_Specialize_LoadGlobal
void
_Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins,
_Py_CODEUNIT *instr, PyObject *name)
{
PyDictObject *globals_dict = (PyDictObject *)globals;
PyObject *value;
/* Try globals first */
Py_ssize_t index = _PyDict_GetItemIndexByHash(globals_dict, name, hash);
if (index >= 0) {
write_u32(instr[-3].cache, globals_dict->ma_version_tag);
write_u16(instr[-1].cache, (uint16_t)index);
_Py_SET_OPCODE(*instr, LOAD_GLOBAL_MODULE);
return;
}
/* Try builtins */
...
_Py_SET_OPCODE(*instr, LOAD_GLOBAL_BUILTIN);
}

On first execution, LOAD_GLOBAL calls _Py_Specialize_LoadGlobal, which looks up the name's current location (globals or builtins) and rewrites the instruction to the appropriate specialized form with cached version tags and index.

gopy notes

LOAD_GLOBAL_MODULE is in vm/eval_simple.go. It checks objects.Module.DictVersion and reads by index. LOAD_GLOBAL_BUILTIN checks both module and builtins versions. Specialization is in vm/specialize.go which rewrites the opcode in the code object's instruction array.