@@ -223,7 +223,73 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
223223 return NULL ;
224224}
225225
226- uint32_t _PyFunction_GetVersionForCurrentState (PyFunctionObject * func )
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+
268+ void
269+ _PyFunction_SetVersion (PyFunctionObject * func , uint32_t version )
270+ {
271+ func -> func_version = version ;
272+ if (version != 0 ) {
273+ PyInterpreterState * interp = _PyInterpreterState_GET ();
274+ interp -> func_state .func_version_cache [
275+ version % FUNC_VERSION_CACHE_SIZE ] = func ;
276+ }
277+ }
278+
279+ PyFunctionObject *
280+ _PyFunction_LookupByVersion (uint32_t version )
281+ {
282+ PyInterpreterState * interp = _PyInterpreterState_GET ();
283+ PyFunctionObject * func = interp -> func_state .func_version_cache [
284+ version % FUNC_VERSION_CACHE_SIZE ];
285+ if (func != NULL && func -> func_version == version ) {
286+ return (PyFunctionObject * )Py_NewRef (func );
287+ }
288+ return NULL ;
289+ }
290+
291+ uint32_t
292+ _PyFunction_GetVersionForCurrentState (PyFunctionObject * func )
227293{
228294 if (func -> func_version != 0 ) {
229295 return func -> func_version ;
@@ -236,7 +302,7 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
236302 return 0 ;
237303 }
238304 uint32_t v = interp -> func_state .next_version ++ ;
239- func -> func_version = v ;
305+ _PyFunction_SetVersion ( func , v ) ;
240306 return v ;
241307}
242308
@@ -851,6 +917,15 @@ func_dealloc(PyFunctionObject *op)
851917 if (op -> func_weakreflist != NULL ) {
852918 PyObject_ClearWeakRefs ((PyObject * ) op );
853919 }
920+ if (op -> func_version != 0 ) {
921+ PyInterpreterState * interp = _PyInterpreterState_GET ();
922+ PyFunctionObject * * slot =
923+ interp -> func_state .func_version_cache
924+ + (op -> func_version % FUNC_VERSION_CACHE_SIZE );
925+ if (* slot == op ) {
926+ * slot = NULL ;
927+ }
928+ }
854929 (void )func_clear (op );
855930 // These aren't cleared by func_clear().
856931 Py_DECREF (op -> func_code );
0 commit comments