gh-115999: Enable free-threaded specialization of LOAD_CONST (#129365)
https://github.com/python/cpython/commit/5c930a26fb78c40929f1b894efee1b07c6d... commit: 5c930a26fb78c40929f1b894efee1b07c6d828fd branch: main author: T. Wouters <thomas@python.org> committer: Yhg1s <thomas@python.org> 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",
participants (1)
-
Yhg1s