]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-115709: Invalidate executors when a local variable is changed via frame.f_locals...
authorMark Shannon <mark@hotpy.org>
Mon, 6 May 2024 21:21:06 +0000 (22:21 +0100)
committerGitHub <noreply@github.com>
Mon, 6 May 2024 21:21:06 +0000 (21:21 +0000)
Also fix unrelated assert in debug Tier2/JIT builds.

Include/cpython/optimizer.h
Lib/test/test_capi/test_opt.py
Objects/frameobject.c
Python/bytecodes.c
Python/generated_cases.c.h

index 744a272251e75c12a5431962f10b1704c058e633..5f218d75b346a02b5d29d77fb9d3df590745e353 100644 (file)
@@ -141,9 +141,6 @@ void _Py_ExecutorDetach(_PyExecutorObject *);
 void _Py_BloomFilter_Init(_PyBloomFilter *);
 void _Py_BloomFilter_Add(_PyBloomFilter *bloom, void *obj);
 PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj);
-PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
-PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
-
 /* For testing */
 PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewCounter(void);
 PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewUOpOptimizer(void);
@@ -151,6 +148,15 @@ PyAPI_FUNC(PyObject *)PyUnstable_Optimizer_NewUOpOptimizer(void);
 #define _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS 3
 #define _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS 6
 
+#ifdef _Py_TIER2
+PyAPI_FUNC(void) _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is_invalidation);
+PyAPI_FUNC(void) _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation);
+#else
+#  define _Py_Executors_InvalidateDependency(A, B, C) ((void)0)
+#  define _Py_Executors_InvalidateAll(A, B) ((void)0)
+#endif
+
+
 #ifdef __cplusplus
 }
 #endif
index 6e5b626e93291ae7024898c31853aded042e76d2..0491ff9b84d486cac0d6f7c94cac698e3b155d08 100644 (file)
@@ -1321,5 +1321,18 @@ class TestUopsOptimization(unittest.TestCase):
         self.assertIsNotNone(ex)
         self.assertIn("_FOR_ITER_GEN_FRAME", get_opnames(ex))
 
+    def test_modified_local_is_seen_by_optimized_code(self):
+        l = sys._getframe().f_locals
+        a = 1
+        s = 0
+        for j in range(1 << 10):
+            a + a
+            l["xa"[j >> 9]] = 1.0
+            s += a
+        self.assertIs(type(a), float)
+        self.assertIs(type(s), float)
+        self.assertEqual(s, 1024.0)
+
+
 if __name__ == "__main__":
     unittest.main()
index 869cdec8aa70627af67555f27d4bbf670e0635f6..26a04cbeea90bf7e2b757b56a5a2fe6c2e12e13c 100644 (file)
@@ -148,8 +148,9 @@ framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value)
     if (PyUnicode_CheckExact(key)) {
         int i = framelocalsproxy_getkeyindex(frame, key, false);
         if (i >= 0) {
-            _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
+            _Py_Executors_InvalidateDependency(PyInterpreterState_Get(), co, 1);
 
+            _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
             PyObject *oldvalue = fast[i];
             PyObject *cell = NULL;
             if (kind == CO_FAST_FREE) {
index b2a0dc030e20cc7aecd68f8e25414d6d4cc8d53e..b2ddec98e682f273ea2b3afabc8f1d42d069daca 100644 (file)
@@ -2424,6 +2424,9 @@ dummy_func(
                 opcode = executor->vm_data.opcode;
                 oparg = (oparg & ~255) | executor->vm_data.oparg;
                 next_instr = this_instr;
+                if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]]) {
+                    PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter);
+                }
                 DISPATCH_GOTO();
             }
             tstate->previous_executor = Py_None;
index 87098b0506522f0528df56ffe513acc35a725801..d3126b0c9807238aa9d0d3607c4bc1216c1c4793 100644 (file)
                 opcode = executor->vm_data.opcode;
                 oparg = (oparg & ~255) | executor->vm_data.oparg;
                 next_instr = this_instr;
+                if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]]) {
+                    PAUSE_ADAPTIVE_COUNTER(this_instr[1].counter);
+                }
                 DISPATCH_GOTO();
             }
             tstate->previous_executor = Py_None;