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_nplaincellvars; /* number of non-arg cell variables */ \
int co_ncellvars; /* total number of cell variables */ \ int co_ncellvars; /* total number of cell variables */ \
int co_nfreevars; /* number of free variables */ \ int co_nfreevars; /* number of free variables */ \
uint32_t co_version; /* version number */ \
\ \
PyObject *co_localsplusnames; /* tuple mapping offsets to names */ \ PyObject *co_localsplusnames; /* tuple mapping offsets to names */ \
PyObject *co_localspluskinds; /* Bytes mapping to local kinds (one byte \ PyObject *co_localspluskinds; /* Bytes mapping to local kinds (one byte \

View File

@ -474,6 +474,8 @@ typedef struct _PyShimCodeDef {
extern PyCodeObject * extern PyCodeObject *
_Py_MakeShimCode(const _PyShimCodeDef *code); _Py_MakeShimCode(const _PyShimCodeDef *code);
extern uint32_t _Py_next_func_version;
#ifdef __cplusplus #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 "pycore_tuple.h" // _PyTuple_ITEMS()
#include "clinic/codeobject.c.h" #include "clinic/codeobject.c.h"
static void static void
notify_code_watchers(PyCodeEvent event, PyCodeObject *co) notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
{ {
@ -398,7 +397,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->co_nplaincellvars = nplaincellvars; co->co_nplaincellvars = nplaincellvars;
co->co_ncellvars = ncellvars; co->co_ncellvars = ncellvars;
co->co_nfreevars = nfreevars; co->co_nfreevars = nfreevars;
co->co_version = _Py_next_func_version;
if (_Py_next_func_version != 0) {
_Py_next_func_version++;
}
/* not set */ /* not set */
co->co_weakreflist = NULL; co->co_weakreflist = NULL;
co->co_extra = NULL; co->co_extra = NULL;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,6 +44,7 @@ CO_FAST_LOCAL = 0x20
CO_FAST_CELL = 0x40 CO_FAST_CELL = 0x40
CO_FAST_FREE = 0x80 CO_FAST_FREE = 0x80
next_code_version = 1
def get_localsplus(code: types.CodeType): def get_localsplus(code: types.CodeType):
a = collections.defaultdict(int) a = collections.defaultdict(int)
@ -227,6 +228,7 @@ class Printer:
def generate_code(self, name: str, code: types.CodeType) -> str: def generate_code(self, name: str, code: types.CodeType) -> str:
global next_code_version
# The ordering here matches PyCode_NewWithPosOnlyArgs() # The ordering here matches PyCode_NewWithPosOnlyArgs()
# (but see below). # (but see below).
co_consts = self.generate(name + "_consts", code.co_consts) co_consts = self.generate(name + "_consts", code.co_consts)
@ -268,6 +270,8 @@ class Printer:
self.write(f".co_nplaincellvars = {nplaincellvars},") self.write(f".co_nplaincellvars = {nplaincellvars},")
self.write(f".co_ncellvars = {ncellvars},") self.write(f".co_ncellvars = {ncellvars},")
self.write(f".co_nfreevars = {nfreevars},") 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_localsplusnames = {co_localsplusnames},")
self.write(f".co_localspluskinds = {co_localspluskinds},") self.write(f".co_localspluskinds = {co_localspluskinds},")
self.write(f".co_filename = {co_filename},") 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)"): with printer.block(f"if ({p} < 0)"):
printer.write("return -1;") printer.write("return -1;")
printer.write("return 0;") printer.write("return 0;")
printer.write(f"\nuint32_t _Py_next_func_version = {next_code_version};\n")
if verbose: if verbose:
print(f"Cache hits: {printer.hits}, misses: {printer.misses}") print(f"Cache hits: {printer.hits}, misses: {printer.misses}")