https://github.com/python/cpython/commit/5c930a26fb78c40929f1b894efee1b07c6d828fd
commit: 5c930a26fb78c40929f1b894efee1b07c6d828fd
branch: main
author: T. Wouters <[email protected]>
committer: Yhg1s <[email protected]>
date: 2025-01-29T01:07:56+01:00
summary:

gh-115999: Enable free-threaded specialization of LOAD_CONST (#129365)

Enable free-threaded specialization of LOAD_CONST.

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

diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py
index 4d7304b1c9abb6..e8ea21f8179978 100644
--- a/Lib/test/test_opcache.py
+++ b/Lib/test/test_opcache.py
@@ -1773,6 +1773,20 @@ def compare_op_str():
         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()
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 5f0be8d3feefd4..7d463511aee41d 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -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)) {
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index ad044e62a38b1c..5dd2f37d811109 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -3318,7 +3318,7 @@
                 }
                 OPCODE_DEFERRED_INC(COMPARE_OP);
                 ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
-                #endif  /* ENABLE_SPECIALIZATION */
+                #endif  /* ENABLE_SPECIALIZATION_FT */
             }
             // _COMPARE_OP
             {
@@ -6035,11 +6035,21 @@
              * 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());
diff --git a/Tools/cases_generator/analyzer.py 
b/Tools/cases_generator/analyzer.py
index bc9c42e045a610..b9293ff4b19951 100644
--- a/Tools/cases_generator/analyzer.py
+++ b/Tools/cases_generator/analyzer.py
@@ -634,6 +634,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool:
     "_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",

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to