From d1e02c48ffd9312350eeeaf0c584bc6bee329785 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 16:08:35 +0100 Subject: [PATCH 1/3] Add version number to code object for better versioning of functions. --- Include/cpython/code.h | 1 + Objects/codeobject.c | 6 +++++- Objects/funcobject.c | 6 +++--- Python/ceval.c | 1 + Tools/build/deepfreeze.py | 4 ++++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 893ff934d3d879..3268e9c9823b9b 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -88,6 +88,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/Objects/codeobject.c b/Objects/codeobject.c index 8920b1db2cadb7..6d57fee5fba14c 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -11,6 +11,7 @@ #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "clinic/codeobject.c.h" +extern uint32_t _Py_next_func_version; /****************** * generic helpers @@ -347,7 +348,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 ccc6d0b52eab68..c2e51e6dc0ae12 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -7,7 +7,7 @@ #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "structmember.h" // PyMemberDef -static uint32_t next_func_version = 1; +uint32_t _Py_next_func_version = 10000; /* Leave plenty of space for deep frozen code objects */ PyFunctionObject * _PyFunction_FromConstructor(PyFrameConstructor *constr) @@ -137,10 +137,10 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func) if (func->vectorcall != _PyFunction_Vectorcall) { return 0; } - if (next_func_version == 0) { + if (_Py_next_func_version == 0) { return 0; } - uint32_t v = next_func_version++; + uint32_t v = _Py_next_func_version++; func->func_version = v; return v; } diff --git a/Python/ceval.c b/Python/ceval.c index fb8dd4811628d1..d29716f900f1d3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4814,6 +4814,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int assert(PyTuple_CheckExact(TOP())); 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 28ac2b12f92fa7..a7b7f4e13958b9 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -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) @@ -228,6 +229,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) @@ -270,6 +272,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},") From 004b13250ff23166d35f58f5b60f3ff2be04727a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 16:10:46 +0100 Subject: [PATCH 2/3] Add news --- .../2022-10-21-16-10-39.gh-issue-98522.s_SixG.rst | 3 +++ 1 file changed, 3 insertions(+) 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/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 00000000000000..d923af198f8203 --- /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. From 46d77fd37867d2d9fec0bb983edd832b35d2f9ac Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 7 Nov 2022 12:34:13 +0000 Subject: [PATCH 3/3] Compute initial value of code version during deepfreeze. --- Include/internal/pycore_code.h | 2 ++ Objects/codeobject.c | 2 -- Objects/funcobject.c | 3 +-- Programs/_bootstrap_python.c | 2 ++ Programs/_freeze_module.c | 3 +++ Tools/build/deepfreeze.py | 1 + 6 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index bf5945435c1774..cb390e99e513c3 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -455,6 +455,8 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index) } } +extern uint32_t _Py_next_func_version; + #ifdef __cplusplus } diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 6d57fee5fba14c..7e0bd934db9ea5 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -11,8 +11,6 @@ #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "clinic/codeobject.c.h" -extern uint32_t _Py_next_func_version; - /****************** * generic helpers ******************/ diff --git a/Objects/funcobject.c b/Objects/funcobject.c index c2e51e6dc0ae12..4987297310036a 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -3,12 +3,11 @@ #include "Python.h" #include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals() +#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 -uint32_t _Py_next_func_version = 10000; /* Leave plenty of space for deep frozen code objects */ - PyFunctionObject * _PyFunction_FromConstructor(PyFrameConstructor *constr) { diff --git a/Programs/_bootstrap_python.c b/Programs/_bootstrap_python.c index bbac0c4e1a8a45..6e1593a0b5996a 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 d6d737d48d6070..e6cdac3a9876f1 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/Tools/build/deepfreeze.py b/Tools/build/deepfreeze.py index a7b7f4e13958b9..75ec90b0336ca5 100644 --- a/Tools/build/deepfreeze.py +++ b/Tools/build/deepfreeze.py @@ -472,6 +472,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}")