Skip to content

Commit 1200014

Browse files
committed
Add a small essay on function and code versions
1 parent 7e6ef78 commit 1200014

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

Objects/funcobject.c

+42
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,48 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
223223
return NULL;
224224
}
225225

226+
/*
227+
Function versions
228+
-----------------
229+
230+
Function versions are used to detect when a function object has been
231+
updated, invalidating inline cache data used by the `CALL` bytecode
232+
(notably `CALL_PY_EXACT_ARGS` and a few other `CALL` specializations).
233+
234+
They are also used by the Tier 2 superblock creation code to find
235+
the function being called (and from there the code object).
236+
237+
How does a function's `func_version` field get initialized?
238+
239+
- `PyFunction_New` and friends initialize it to 0.
240+
- The `MAKE_FUNCTION` instruction sets it from the code's `co_version`.
241+
- It is reset to 0 when various attributes like `__code__` are set.
242+
- A new version is allocated by `_PyFunction_GetVersionForCurrentState`
243+
when the specializer needs a version and the version is 0.
244+
245+
The latter allocates versions using a counter in the interpreter state;
246+
when the counter wraps around to 0, no more versions are allocated.
247+
There is one other special case: functions with a non-standard
248+
`vectorcall` field are not given a version.
249+
250+
When the function version is 0, the `CALL` bytecode is not specialized.
251+
252+
Code object versions
253+
--------------------
254+
255+
So where to code objects get their `co_version`? There is a single
256+
static global counter, `_Py_next_func_version`. This is initialized in
257+
the generated (!) file `Python/deepfreeze/deepfreeze.c`, to 1 plus the
258+
number of deep-frozen function objects in that file.
259+
(In `_bootstrap_python.c` and `freeze_module.c` it is initialized to 1.)
260+
261+
Code objects get a new `co_version` allocated from this counter upon
262+
creation. Since code objects are nominally immutable, `co_version` can
263+
not be invalidated. The only way it can be 0 is when 2**32 or more
264+
code objects have been created during the process's lifetime.
265+
(The counter isn't reset by `fork()`, extending the lifetime.)
266+
*/
267+
226268
void
227269
_PyFunction_SetVersion(PyFunctionObject *func, uint32_t version)
228270
{

0 commit comments

Comments
 (0)