@@ -223,6 +223,48 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
223
223
return NULL ;
224
224
}
225
225
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
+
226
268
void
227
269
_PyFunction_SetVersion (PyFunctionObject * func , uint32_t version )
228
270
{
0 commit comments