]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-142276: Watch attribute loads when promoting JIT constants (GH-142303)
authorKen Jin <kenjin@python.org>
Mon, 8 Dec 2025 18:03:15 +0000 (02:03 +0800)
committerGitHub <noreply@github.com>
Mon, 8 Dec 2025 18:03:15 +0000 (18:03 +0000)
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Savannah Ostrowski <savannah@python.org>
Lib/test/test_capi/test_opt.py
Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-14-33-54.gh-issue-142276.H4j8hP.rst [new file with mode: 0644]
Python/optimizer_analysis.c
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h

index 51234a2e40f54fd28c8b9bcb18fde89039e6432f..2e7485e89fdac5f2a8b19204ba24f53c9bcc72cf 100644 (file)
@@ -2695,6 +2695,28 @@ class TestUopsOptimization(unittest.TestCase):
                     pass
         """))
 
+    def test_attribute_changes_are_watched(self):
+        # Just running to make sure it doesn't crash.
+        script_helper.assert_python_ok("-c", textwrap.dedent("""
+            from concurrent.futures import ThreadPoolExecutor
+            from unittest import TestCase
+            NTHREADS = 6
+            BOTTOM = 0
+            TOP = 1250000
+            class A:
+                attr = 10**1000
+            class TestType(TestCase):
+                def read(id0):
+                    for _ in range(BOTTOM, TOP):
+                        A.attr
+                def write(id0):
+                    x = A.attr
+                    x += 1
+                    A.attr = x
+                    with ThreadPoolExecutor(NTHREADS) as pool:
+                        pool.submit(read, (1,))
+                        pool.submit(write, (1,))
+        """))
 
 def global_identity(x):
     return x
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-14-33-54.gh-issue-142276.H4j8hP.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-14-33-54.gh-issue-142276.H4j8hP.rst
new file mode 100644 (file)
index 0000000..aa8e3da
--- /dev/null
@@ -0,0 +1 @@
+Fix missing type watcher when promoting attribute loads to constants in the JIT. Patch by Ken Jin. Reproducer by Yuancheng Jiang.
index 51722556554609bedf70cd8910dbd16f271e995c..c4afc6bd29086f30e18828565b4cf9dd2ca6bf98 100644 (file)
@@ -242,7 +242,7 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, bool exit)
 }
 
 static JitOptRef
-lookup_attr(JitOptContext *ctx, _PyUOpInstruction *this_instr,
+lookup_attr(JitOptContext *ctx, _PyBloomFilter *dependencies, _PyUOpInstruction *this_instr,
             PyTypeObject *type, PyObject *name, uint16_t immortal,
             uint16_t mortal)
 {
@@ -252,6 +252,8 @@ lookup_attr(JitOptContext *ctx, _PyUOpInstruction *this_instr,
         if (lookup) {
             int opcode = _Py_IsImmortal(lookup) ? immortal : mortal;
             REPLACE_OP(this_instr, opcode, 0, (uintptr_t)lookup);
+            PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
+            _Py_BloomFilter_Add(dependencies, type);
             return sym_new_const(ctx, lookup);
         }
     }
index 06fa8a4522a4994f60e98e50e39df29c8383a7d8..9eee3c69da7c866d275c3bc268bb00c3d8157986 100644 (file)
@@ -607,7 +607,7 @@ dummy_func(void) {
         (void)descr;
         PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner);
         PyObject *name = get_co_name(ctx, oparg >> 1);
-        attr = lookup_attr(ctx, this_instr, type, name,
+        attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _POP_TOP_LOAD_CONST_INLINE_BORROW,
                            _POP_TOP_LOAD_CONST_INLINE);
     }
@@ -616,7 +616,7 @@ dummy_func(void) {
         (void)descr;
         PyTypeObject *type = sym_get_type(owner);
         PyObject *name = get_co_name(ctx, oparg >> 1);
-        attr = lookup_attr(ctx, this_instr, type, name,
+        attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _POP_TOP_LOAD_CONST_INLINE_BORROW,
                            _POP_TOP_LOAD_CONST_INLINE);
     }
@@ -625,7 +625,7 @@ dummy_func(void) {
         (void)descr;
         PyTypeObject *type = sym_get_type(owner);
         PyObject *name = get_co_name(ctx, oparg >> 1);
-        attr = lookup_attr(ctx, this_instr, type, name,
+        attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _POP_TOP_LOAD_CONST_INLINE_BORROW,
                            _POP_TOP_LOAD_CONST_INLINE);
     }
@@ -634,7 +634,7 @@ dummy_func(void) {
         (void)descr;
         PyTypeObject *type = sym_get_type(owner);
         PyObject *name = get_co_name(ctx, oparg >> 1);
-        attr = lookup_attr(ctx, this_instr, type, name,
+        attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _LOAD_CONST_UNDER_INLINE_BORROW,
                            _LOAD_CONST_UNDER_INLINE);
         self = owner;
@@ -644,7 +644,7 @@ dummy_func(void) {
         (void)descr;
         PyTypeObject *type = sym_get_type(owner);
         PyObject *name = get_co_name(ctx, oparg >> 1);
-        attr = lookup_attr(ctx, this_instr, type, name,
+        attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _LOAD_CONST_UNDER_INLINE_BORROW,
                            _LOAD_CONST_UNDER_INLINE);
         self = owner;
@@ -654,7 +654,7 @@ dummy_func(void) {
         (void)descr;
         PyTypeObject *type = sym_get_type(owner);
         PyObject *name = get_co_name(ctx, oparg >> 1);
-        attr = lookup_attr(ctx, this_instr, type, name,
+        attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _LOAD_CONST_UNDER_INLINE_BORROW,
                            _LOAD_CONST_UNDER_INLINE);
         self = owner;
index 85bebed58677ed325dc1d1bd1b7059e417eca367..36130cdb893ab1bae288bbfcd97cb07145f9a999 100644 (file)
             (void)descr;
             PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner);
             PyObject *name = get_co_name(ctx, oparg >> 1);
-            attr = lookup_attr(ctx, this_instr, type, name,
+            attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _POP_TOP_LOAD_CONST_INLINE_BORROW,
                            _POP_TOP_LOAD_CONST_INLINE);
             stack_pointer[-1] = attr;
             (void)descr;
             PyTypeObject *type = sym_get_type(owner);
             PyObject *name = get_co_name(ctx, oparg >> 1);
-            attr = lookup_attr(ctx, this_instr, type, name,
+            attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _LOAD_CONST_UNDER_INLINE_BORROW,
                            _LOAD_CONST_UNDER_INLINE);
             self = owner;
             (void)descr;
             PyTypeObject *type = sym_get_type(owner);
             PyObject *name = get_co_name(ctx, oparg >> 1);
-            attr = lookup_attr(ctx, this_instr, type, name,
+            attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _LOAD_CONST_UNDER_INLINE_BORROW,
                            _LOAD_CONST_UNDER_INLINE);
             self = owner;
             (void)descr;
             PyTypeObject *type = sym_get_type(owner);
             PyObject *name = get_co_name(ctx, oparg >> 1);
-            attr = lookup_attr(ctx, this_instr, type, name,
+            attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _POP_TOP_LOAD_CONST_INLINE_BORROW,
                            _POP_TOP_LOAD_CONST_INLINE);
             stack_pointer[-1] = attr;
             (void)descr;
             PyTypeObject *type = sym_get_type(owner);
             PyObject *name = get_co_name(ctx, oparg >> 1);
-            attr = lookup_attr(ctx, this_instr, type, name,
+            attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _POP_TOP_LOAD_CONST_INLINE_BORROW,
                            _POP_TOP_LOAD_CONST_INLINE);
             stack_pointer[-1] = attr;
             (void)descr;
             PyTypeObject *type = sym_get_type(owner);
             PyObject *name = get_co_name(ctx, oparg >> 1);
-            attr = lookup_attr(ctx, this_instr, type, name,
+            attr = lookup_attr(ctx, dependencies, this_instr, type, name,
                            _LOAD_CONST_UNDER_INLINE_BORROW,
                            _LOAD_CONST_UNDER_INLINE);
             self = owner;