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
| Lines | Symbol | Role |
|---|---|---|
| 1-80 | LOAD_GLOBAL baseline | Look up in f_globals then f_builtins |
| 81-160 | LOAD_GLOBAL_MODULE | Fast path: module dict by version + index |
| 161-240 | LOAD_GLOBAL_BUILTIN | Fast path: builtins dict by version + index |
| 241-340 | _Py_Specialize_LoadGlobal | Specialization logic |
| 341-500 | Globals / builtins version tags | Cache 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.