From fb713b21833a17cba8022af0fa4c486512157d4b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Dec 2022 12:18:45 +0000 Subject: [PATCH] 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. --- Include/cpython/code.h | 1 + Include/internal/pycore_code.h | 2 ++ .../2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst | 3 +++ Objects/codeobject.c | 6 ++++-- Objects/funcobject.c | 3 +-- Programs/_bootstrap_python.c | 2 ++ Programs/_freeze_module.c | 3 +++ Python/bytecodes.c | 1 + Python/generated_cases.c.h | 1 + Tools/build/deepfreeze.py | 5 +++++ 10 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst diff --git a/Include/cpython/code.h b/Include/cpython/code.h index f11d099e037..fc7c5ed7024 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -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 \ diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 357fc85a95c..f22fd45f831 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -474,6 +474,8 @@ typedef struct _PyShimCodeDef { extern PyCodeObject * _Py_MakeShimCode(const _PyShimCodeDef *code); +extern uint32_t _Py_next_func_version; + #ifdef __cplusplus } diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst new file mode 100644 index 00000000000..d923af198f8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst @@ -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. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 0c197d767b0..c92c7deaf80 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -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; diff --git a/Objects/funcobject.c b/Objects/funcobject.c index bf97edc53ad..9df06520586 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -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) { diff --git a/Programs/_bootstrap_python.c b/Programs/_bootstrap_python.c index bbac0c4e1a8..6e1593a0b59 100644 --- a/Programs/_bootstrap_python.c +++ b/Programs/_bootstrap_python.c @@ -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) { diff --git a/Programs/_freeze_module.c b/Programs/_freeze_module.c index 9e2169f32e9..90fc2dc6e87 100644 --- a/Programs/_freeze_module.c +++ b/Programs/_freeze_module.c @@ -9,6 +9,7 @@ Keep this file in sync with Programs/_freeze_module.py. */ + #include #include #include "pycore_fileutils.h" // _Py_stat_struct @@ -22,6 +23,8 @@ #include #endif +uint32_t _Py_next_func_version = 1; + /* Empty initializer for deepfrozen modules */ int _Py_Deepfreeze_Init(void) { diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5807bd5dc2d..c56f1d3ef9f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3452,6 +3452,7 @@ dummy_func( func->func_defaults = POP(); } + func->func_version = ((PyCodeObject *)codeobj)->co_version; PUSH((PyObject *)func); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 59e70b72226..45382a466b1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3693,6 +3693,7 @@ func->func_defaults = POP(); } + func->func_version = ((PyCodeObject *)codeobj)->co_version; PUSH((PyObject *)func); DISPATCH(); } diff --git a/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index 2eef649437a..7f4e2428013 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -44,6 +44,7 @@ CO_FAST_LOCAL = 0x20 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 @@ class Printer: 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 @@ class Printer: 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}")