GH-98522: Add version number to code objects. (GH-98525)

* Add version number to code object for better versioning of functions.

* Improves specialization for closures and list comprehensions.
This commit is contained in:
Mark Shannon 2022-12-09 12:18:45 +00:00 committed by GitHub
parent 3c5355496b
commit fb713b2183
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 23 additions and 4 deletions

View file

@ -87,6 +87,7 @@ typedef struct {
int co_nplaincellvars; /* number of non-arg cell variables */ \
int co_ncellvars; /* total number of cell variables */ \
int co_nfreevars; /* number of free variables */ \
uint32_t co_version; /* version number */ \
\
PyObject *co_localsplusnames; /* tuple mapping offsets to names */ \
PyObject *co_localspluskinds; /* Bytes mapping to local kinds (one byte \

View file

@ -474,6 +474,8 @@ typedef struct _PyShimCodeDef {
extern PyCodeObject *
_Py_MakeShimCode(const _PyShimCodeDef *code);
extern uint32_t _Py_next_func_version;
#ifdef __cplusplus
}

View file

@ -0,0 +1,3 @@
Add an internal version number to code objects, to give better versioning of
inner functions and comprehensions, and thus better specialization of those
functions. This change is invisible to both Python and C extensions.

View file

@ -11,7 +11,6 @@
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "clinic/codeobject.c.h"
static void
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
{
@ -398,7 +397,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->co_nplaincellvars = nplaincellvars;
co->co_ncellvars = ncellvars;
co->co_nfreevars = nfreevars;
co->co_version = _Py_next_func_version;
if (_Py_next_func_version != 0) {
_Py_next_func_version++;
}
/* not set */
co->co_weakreflist = NULL;
co->co_extra = NULL;

View file

@ -3,7 +3,7 @@
#include "Python.h"
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
#include "pycore_function.h" // FUNC_MAX_WATCHERS
#include "pycore_code.h" // _Py_next_func_version
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "structmember.h" // PyMemberDef
@ -64,7 +64,6 @@ PyFunction_ClearWatcher(int watcher_id)
interp->active_func_watchers &= ~(1 << watcher_id);
return 0;
}
PyFunctionObject *
_PyFunction_FromConstructor(PyFrameConstructor *constr)
{

View file

@ -14,6 +14,8 @@
#include "Python/frozen_modules/importlib._bootstrap_external.h"
/* End includes */
uint32_t _Py_next_func_version = 1;
/* Empty initializer for deepfrozen modules */
int _Py_Deepfreeze_Init(void)
{

View file

@ -9,6 +9,7 @@
Keep this file in sync with Programs/_freeze_module.py.
*/
#include <Python.h>
#include <marshal.h>
#include "pycore_fileutils.h" // _Py_stat_struct
@ -22,6 +23,8 @@
#include <unistd.h>
#endif
uint32_t _Py_next_func_version = 1;
/* Empty initializer for deepfrozen modules */
int _Py_Deepfreeze_Init(void)
{

View file

@ -3452,6 +3452,7 @@ dummy_func(
func->func_defaults = POP();
}
func->func_version = ((PyCodeObject *)codeobj)->co_version;
PUSH((PyObject *)func);
}

View file

@ -3693,6 +3693,7 @@
func->func_defaults = POP();
}
func->func_version = ((PyCodeObject *)codeobj)->co_version;
PUSH((PyObject *)func);
DISPATCH();
}

View file

@ -44,6 +44,7 @@ def make_string_literal(b: bytes) -> str:
CO_FAST_CELL = 0x40
CO_FAST_FREE = 0x80
next_code_version = 1
def get_localsplus(code: types.CodeType):
a = collections.defaultdict(int)
@ -227,6 +228,7 @@ def generate_unicode(self, name: str, s: str) -> str:
def generate_code(self, name: str, code: types.CodeType) -> str:
global next_code_version
# The ordering here matches PyCode_NewWithPosOnlyArgs()
# (but see below).
co_consts = self.generate(name + "_consts", code.co_consts)
@ -268,6 +270,8 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
self.write(f".co_nplaincellvars = {nplaincellvars},")
self.write(f".co_ncellvars = {ncellvars},")
self.write(f".co_nfreevars = {nfreevars},")
self.write(f".co_version = {next_code_version},")
next_code_version += 1
self.write(f".co_localsplusnames = {co_localsplusnames},")
self.write(f".co_localspluskinds = {co_localspluskinds},")
self.write(f".co_filename = {co_filename},")
@ -461,6 +465,7 @@ def generate(args: list[str], output: TextIO) -> None:
with printer.block(f"if ({p} < 0)"):
printer.write("return -1;")
printer.write("return 0;")
printer.write(f"\nuint32_t _Py_next_func_version = {next_code_version};\n")
if verbose:
print(f"Cache hits: {printer.hits}, misses: {printer.misses}")