]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-115999: Enable free-threaded specialization of LOAD_CONST (#129365)
authorT. Wouters <thomas@python.org>
Wed, 29 Jan 2025 00:07:56 +0000 (01:07 +0100)
committerGitHub <noreply@github.com>
Wed, 29 Jan 2025 00:07:56 +0000 (01:07 +0100)
Enable free-threaded specialization of LOAD_CONST.

Lib/test/test_opcache.py
Python/bytecodes.c
Python/generated_cases.c.h
Tools/cases_generator/analyzer.py

index 4d7304b1c9abb67fd5814eaba890b0e5013bf2a9..e8ea21f8179978adc27964de9d4312c29dac17b5 100644 (file)
@@ -1773,6 +1773,20 @@ class TestSpecializer(TestBase):
         self.assert_specialized(compare_op_str, "COMPARE_OP_STR")
         self.assert_no_opcode(compare_op_str, "COMPARE_OP")
 
+    @cpython_only
+    @requires_specialization_ft
+    def test_load_const(self):
+        def load_const():
+            def unused(): pass
+            # Currently, the empty tuple is immortal, and the otherwise
+            # unused nested function's code object is mortal. This test will
+            # have to use different values if either of that changes.
+            return ()
+
+        load_const()
+        self.assert_specialized(load_const, "LOAD_CONST_IMMORTAL")
+        self.assert_specialized(load_const, "LOAD_CONST_MORTAL")
+        self.assert_no_opcode(load_const, "LOAD_CONST")
 
 if __name__ == "__main__":
     unittest.main()
index 5f0be8d3feefd49d2edccea9a9693a019678733c..7d463511aee41db1ea9769a5cbe7c1830db5f7ec 100644 (file)
@@ -294,10 +294,20 @@ dummy_func(
              * marshalling can intern strings and make them immortal. */
             PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg);
             value = PyStackRef_FromPyObjectNew(obj);
-#if ENABLE_SPECIALIZATION
+#if ENABLE_SPECIALIZATION_FT
+#ifdef Py_GIL_DISABLED
+            uint8_t expected = LOAD_CONST;
+            if (!_Py_atomic_compare_exchange_uint8(
+                    &this_instr->op.code, &expected,
+                    _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL)) {
+                // We might lose a race with instrumentation, which we don't care about.
+                assert(expected >= MIN_INSTRUMENTED_OPCODE);
+            }
+#else
             if (this_instr->op.code == LOAD_CONST) {
                 this_instr->op.code = _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL;
             }
+#endif
 #endif
         }
 
@@ -2558,7 +2568,7 @@ dummy_func(
             }
             OPCODE_DEFERRED_INC(COMPARE_OP);
             ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-            #endif  /* ENABLE_SPECIALIZATION */
+            #endif  /* ENABLE_SPECIALIZATION_FT */
         }
 
         op(_COMPARE_OP, (left, right -- res)) {
index ad044e62a38b1cb4fa3843bd7068c4e40ad84442..5dd2f37d811109c666631b30baf08680ac4ca8ce 100644 (file)
                 }
                 OPCODE_DEFERRED_INC(COMPARE_OP);
                 ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-                #endif  /* ENABLE_SPECIALIZATION */
+                #endif  /* ENABLE_SPECIALIZATION_FT */
             }
             // _COMPARE_OP
             {
              * marshalling can intern strings and make them immortal. */
             PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg);
             value = PyStackRef_FromPyObjectNew(obj);
-            #if ENABLE_SPECIALIZATION
+            #if ENABLE_SPECIALIZATION_FT
+            #ifdef Py_GIL_DISABLED
+            uint8_t expected = LOAD_CONST;
+            if (!_Py_atomic_compare_exchange_uint8(
+                    &this_instr->op.code, &expected,
+                    _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL)) {
+                // We might lose a race with instrumentation, which we don't care about.
+                assert(expected >= MIN_INSTRUMENTED_OPCODE);
+            }
+            #else
             if (this_instr->op.code == LOAD_CONST) {
                 this_instr->op.code = _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL;
             }
             #endif
+            #endif
             stack_pointer[0] = value;
             stack_pointer += 1;
             assert(WITHIN_STACK_BOUNDS());
index bc9c42e045a6100ae676b2e66fd97334c9bbf70a..b9293ff4b19951c841b3f0e9fa1df4b6c6df9ff9 100644 (file)
@@ -634,6 +634,7 @@ NON_ESCAPING_FUNCTIONS = (
     "_Py_STR",
     "_Py_TryIncrefCompare",
     "_Py_TryIncrefCompareStackRef",
+    "_Py_atomic_compare_exchange_uint8",
     "_Py_atomic_load_ptr_acquire",
     "_Py_atomic_load_uintptr_relaxed",
     "_Py_set_eval_breaker_bit",