Skip to content

Commit fb713b2

Browse files
authored
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.
1 parent 3c53554 commit fb713b2

File tree

10 files changed

+23
-4
lines changed

10 files changed

+23
-4
lines changed

Include/cpython/code.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ typedef struct {
8787
int co_nplaincellvars; /* number of non-arg cell variables */ \
8888
int co_ncellvars; /* total number of cell variables */ \
8989
int co_nfreevars; /* number of free variables */ \
90+
uint32_t co_version; /* version number */ \
9091
\
9192
PyObject *co_localsplusnames; /* tuple mapping offsets to names */ \
9293
PyObject *co_localspluskinds; /* Bytes mapping to local kinds (one byte \

Include/internal/pycore_code.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ typedef struct _PyShimCodeDef {
474474
extern PyCodeObject *
475475
_Py_MakeShimCode(const _PyShimCodeDef *code);
476476

477+
extern uint32_t _Py_next_func_version;
478+
477479

478480
#ifdef __cplusplus
479481
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add an internal version number to code objects, to give better versioning of
2+
inner functions and comprehensions, and thus better specialization of those
3+
functions. This change is invisible to both Python and C extensions.

Objects/codeobject.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "pycore_tuple.h" // _PyTuple_ITEMS()
1212
#include "clinic/codeobject.c.h"
1313

14-
1514
static void
1615
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
1716
{
@@ -398,7 +397,10 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
398397
co->co_nplaincellvars = nplaincellvars;
399398
co->co_ncellvars = ncellvars;
400399
co->co_nfreevars = nfreevars;
401-
400+
co->co_version = _Py_next_func_version;
401+
if (_Py_next_func_version != 0) {
402+
_Py_next_func_version++;
403+
}
402404
/* not set */
403405
co->co_weakreflist = NULL;
404406
co->co_extra = NULL;

Objects/funcobject.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include "Python.h"
55
#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
6-
#include "pycore_function.h" // FUNC_MAX_WATCHERS
6+
#include "pycore_code.h" // _Py_next_func_version
77
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
88
#include "pycore_pyerrors.h" // _PyErr_Occurred()
99
#include "structmember.h" // PyMemberDef
@@ -64,7 +64,6 @@ PyFunction_ClearWatcher(int watcher_id)
6464
interp->active_func_watchers &= ~(1 << watcher_id);
6565
return 0;
6666
}
67-
6867
PyFunctionObject *
6968
_PyFunction_FromConstructor(PyFrameConstructor *constr)
7069
{

Programs/_bootstrap_python.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "Python/frozen_modules/importlib._bootstrap_external.h"
1515
/* End includes */
1616

17+
uint32_t _Py_next_func_version = 1;
18+
1719
/* Empty initializer for deepfrozen modules */
1820
int _Py_Deepfreeze_Init(void)
1921
{

Programs/_freeze_module.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Keep this file in sync with Programs/_freeze_module.py.
1010
*/
1111

12+
1213
#include <Python.h>
1314
#include <marshal.h>
1415
#include "pycore_fileutils.h" // _Py_stat_struct
@@ -22,6 +23,8 @@
2223
#include <unistd.h>
2324
#endif
2425

26+
uint32_t _Py_next_func_version = 1;
27+
2528
/* Empty initializer for deepfrozen modules */
2629
int _Py_Deepfreeze_Init(void)
2730
{

Python/bytecodes.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3452,6 +3452,7 @@ dummy_func(
34523452
func->func_defaults = POP();
34533453
}
34543454

3455+
func->func_version = ((PyCodeObject *)codeobj)->co_version;
34553456
PUSH((PyObject *)func);
34563457
}
34573458

Python/generated_cases.c.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/build/deepfreeze.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def make_string_literal(b: bytes) -> str:
4444
CO_FAST_CELL = 0x40
4545
CO_FAST_FREE = 0x80
4646

47+
next_code_version = 1
4748

4849
def get_localsplus(code: types.CodeType):
4950
a = collections.defaultdict(int)
@@ -227,6 +228,7 @@ def generate_unicode(self, name: str, s: str) -> str:
227228

228229

229230
def generate_code(self, name: str, code: types.CodeType) -> str:
231+
global next_code_version
230232
# The ordering here matches PyCode_NewWithPosOnlyArgs()
231233
# (but see below).
232234
co_consts = self.generate(name + "_consts", code.co_consts)
@@ -268,6 +270,8 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
268270
self.write(f".co_nplaincellvars = {nplaincellvars},")
269271
self.write(f".co_ncellvars = {ncellvars},")
270272
self.write(f".co_nfreevars = {nfreevars},")
273+
self.write(f".co_version = {next_code_version},")
274+
next_code_version += 1
271275
self.write(f".co_localsplusnames = {co_localsplusnames},")
272276
self.write(f".co_localspluskinds = {co_localspluskinds},")
273277
self.write(f".co_filename = {co_filename},")
@@ -461,6 +465,7 @@ def generate(args: list[str], output: TextIO) -> None:
461465
with printer.block(f"if ({p} < 0)"):
462466
printer.write("return -1;")
463467
printer.write("return 0;")
468+
printer.write(f"\nuint32_t _Py_next_func_version = {next_code_version};\n")
464469
if verbose:
465470
print(f"Cache hits: {printer.hits}, misses: {printer.misses}")
466471

0 commit comments

Comments
 (0)