@@ -134,7 +134,85 @@ void FrameAsm::linkNormalGeneratorFrame(
134134 preserver.restore ();
135135}
136136
137- void FrameAsm::emitIncTotalRefCount (const arch::Gp& scratch_reg) {
137+ #ifdef Py_GIL_DISABLED
138+
139+ void emit_inc_total_ref_count_x86_nogil (
140+ [[maybe_unused]] asmjit::x86::Builder* as,
141+ [[maybe_unused]] const arch::Gp& tstate_reg) {
142+ #ifdef Py_REF_DEBUG
143+ #if defined(CINDER_X86_64)
144+ as->inc (
145+ x86::ptr (
146+ tstate_reg,
147+ offsetof (_PyThreadStateImpl, reftotal),
148+ sizeof (Py_ssize_t)));
149+ #else
150+ CINDER_UNSUPPORTED
151+ #endif
152+ #endif
153+ }
154+
155+ void inc_ref_x86_nogil (
156+ [[maybe_unused]] asmjit::x86::Builder* as,
157+ [[maybe_unused]] const arch::Gp& reg,
158+ [[maybe_unused]] const arch::Gp& scratch_reg,
159+ [[maybe_unused]] const arch::Gp& tstate_reg) {
160+ #if defined(CINDER_X86_64)
161+ // For free-threaded Python, check immortality via ob_ref_local.
162+ // Load ob_ref_local (32-bit). Note this load should be atomic with relaxed
163+ // memory semantics, which is default on x86.
164+ as->mov (
165+ scratch_reg.r32 (), x86::dword_ptr (reg, offsetof (PyObject, ob_ref_local)));
166+ // Add 1 - if result is zero, object was immortal (UINT32_MAX + 1 overflows
167+ // to 0)
168+ as->inc (scratch_reg.r32 ());
169+ Label immortal = as->newLabel ();
170+ as->jz (immortal);
171+
172+ // Check if object is owned by current thread by comparing ob_tid with
173+ // the current thread ID. This is equivalent to _Py_IsOwnedByCurrentThread.
174+ // TODO: I don't have the stomach to find another scratch register for the
175+ // ob_tid to current TID comparison, so I'm just reusing the one scratch
176+ // register we have for now plus the stack. Should still be pretty fast on
177+ // x86.
178+ Label not_owned = as->newLabel ();
179+ as->push (scratch_reg);
180+ as->mov (scratch_reg, x86::ptr (reg, offsetof (PyObject, ob_tid)));
181+ x86::Mem tid_mem;
182+ tid_mem.setOffset (0 );
183+ tid_mem.setSize (sizeof (uintptr_t ));
184+ tid_mem.setSegment (x86::fs);
185+ as->cmp (scratch_reg, tid_mem);
186+ as->pop (scratch_reg);
187+ as->jne (not_owned);
188+
189+ // Owned by current thread - store directly to ob_ref_local (fast path).
190+ // Note this store should be atomic with relaxed memory semantics, which is
191+ // default on x86.
192+ as->mov (
193+ x86::dword_ptr (reg, offsetof (PyObject, ob_ref_local)), scratch_reg.r32 ());
194+ Label done_incref = as->newLabel ();
195+ as->jmp (done_incref);
196+
197+ // Not owned - use atomic add to ob_ref_shared (slow path)
198+ as->bind (not_owned);
199+ as->lock ().add (
200+ x86::qword_ptr (reg, offsetof (PyObject, ob_ref_shared)),
201+ 1 << _Py_REF_SHARED_SHIFT);
202+ as->bind (done_incref);
203+ emit_inc_total_ref_count_x86_nogil (as, tstate_reg);
204+ as->bind (immortal);
205+ #else
206+ CINDER_UNSUPPORTED
207+ #endif
208+ }
209+
210+ #else
211+ // GILful inc-ref implementation
212+
213+ void emit_inc_total_ref_count_x86_gil (
214+ [[maybe_unused]] asmjit::x86::Builder* as,
215+ [[maybe_unused]] const arch::Gp& scratch_reg) {
138216#ifdef Py_REF_DEBUG
139217#if defined(CINDER_X86_64)
140218 PyInterpreterState* interp;
@@ -145,36 +223,54 @@ void FrameAsm::emitIncTotalRefCount(const arch::Gp& scratch_reg) {
145223 }
146224
147225 Py_ssize_t* ref_total = &interp->object_state .reftotal ;
148- as_ ->mov (scratch_reg, ref_total);
149- as_ ->inc (x86::ptr (scratch_reg. r64 () , 0 , sizeof (void *)));
226+ as ->mov (scratch_reg, ref_total);
227+ as ->inc (x86::ptr (scratch_reg, 0 , sizeof (void *)));
150228#else
151229 CINDER_UNSUPPORTED
152230#endif
153231#endif
154232}
155233
156- #ifdef ENABLE_LIGHTWEIGHT_FRAMES
157- // TODO(T251571746): need to correctly implement FT incref.
158- void FrameAsm::incRef (const arch::Gp& reg, const arch::Gp& scratch_reg) {
234+ void inc_ref_x86_gil (
235+ asmjit::x86::Builder* as,
236+ const arch::Gp& reg,
237+ const arch::Gp& scratch_reg) {
159238#if defined(CINDER_X86_64)
160- as_-> mov (scratch_reg, x86::ptr (reg, offsetof (PyObject, ob_refcnt)) );
161- as_-> inc (scratch_reg);
162- Label immortal = as_-> newLabel ( );
239+ Label immortal = as-> newLabel ( );
240+ as-> mov (scratch_reg. r32 (), x86::ptr (reg, offsetof (PyObject, ob_refcnt)) );
241+ as-> inc (scratch_reg. r32 () );
163242#if PY_VERSION_HEX >= 0x030E0000
164- as_ ->js (immortal);
243+ as ->js (immortal);
165244#else
166- as_ ->je (immortal);
245+ as ->je (immortal);
167246#endif
168247 // mortal
169- as_ ->mov (x86::ptr (reg, offsetof (PyObject, ob_refcnt)), scratch_reg);
170- emitIncTotalRefCount ( scratch_reg. r64 () );
171- as_ ->bind (immortal);
248+ as ->mov (x86::ptr (reg, offsetof (PyObject, ob_refcnt)), scratch_reg. r32 () );
249+ emit_inc_total_ref_count_x86_gil (as, scratch_reg);
250+ as ->bind (immortal);
172251#else
173252 CINDER_UNSUPPORTED
174253#endif
175254}
255+ #endif // Py_GIL_DISABLED
256+
257+ void FrameAsm::incRef (
258+ const arch::Gp& reg,
259+ const arch::Gp& scratch_reg,
260+ [[maybe_unused]] const arch::Gp& tstate_reg) {
261+ #if defined(CINDER_X86_64)
262+
263+ #if defined(Py_GIL_DISABLED)
264+ inc_ref_x86_nogil (as_, reg, scratch_reg, tstate_reg);
265+ #else
266+ inc_ref_x86_gil (as_, reg, scratch_reg);
176267#endif
177268
269+ #else
270+ CINDER_UNSUPPORTED
271+ #endif
272+ }
273+
178274bool FrameAsm::storeConst (
179275 const arch::Gp& reg,
180276 int32_t offset,
@@ -241,7 +337,7 @@ void FrameAsm::linkLightWeightFunctionFrame(
241337#else
242338 PyObject* frame_reifier = env_.code_rt ->reifier ();
243339#endif
244- const auto ref_cnt = x86::eax ;
340+ const auto ref_cnt = x86::rax ;
245341
246342#define FRAME_OFFSET (NAME ) \
247343 -frame_header_size + offsetof (_PyInterpreterFrame, NAME ) + sizeof (FrameHeader)
@@ -254,7 +350,7 @@ void FrameAsm::linkLightWeightFunctionFrame(
254350 // Initialize the fields minus previous.
255351 // Store func before the header
256352 as_->mov (x86::ptr (x86::rbp, -frame_header_size), func_reg);
257- incRef (func_reg, ref_cnt);
353+ incRef (func_reg, ref_cnt, tstate_reg );
258354 env_.addAnnotation (" Store func before frame header" , store_func_cursor);
259355#endif
260356
@@ -271,7 +367,7 @@ void FrameAsm::linkLightWeightFunctionFrame(
271367 // if this fit into a 32-bit value we didn't spill it into scratch
272368 as_->mov (scratch, reinterpret_cast <uint64_t >(executable));
273369 }
274- incRef (scratch, ref_cnt);
370+ incRef (scratch, ref_cnt, tstate_reg );
275371 }
276372 env_.addAnnotation (
277373 " Set _PyInterpreterFrame::f_executable/f_code" , store_f_code_cursor);
@@ -280,15 +376,15 @@ void FrameAsm::linkLightWeightFunctionFrame(
280376 asmjit::BaseNode* store_f_funcobj_cursor = as_->cursor ();
281377#if PY_VERSION_HEX >= 0x030E0000
282378 as_->mov (x86::ptr (x86::rbp, FRAME_OFFSET (f_funcobj)), func_reg);
283- incRef (func_reg, ref_cnt);
379+ incRef (func_reg, ref_cnt, tstate_reg );
284380#else
285381 storeConst (x86::rbp, FRAME_OFFSET (f_funcobj), frame_reifier, scratch);
286382 JIT_DCHECK (_Py_IsImmortal (frame_reifier), " frame helper must be immortal" );
287383#endif
288384 env_.addAnnotation (
289385 " Set _PyInterpreterFrame::f_funcobj" , store_f_funcobj_cursor);
290386
291- // Store prev_instr
387+ // Store prev_instr + tlbc_index
292388 asmjit::BaseNode* store_prev_instr_cursor = as_->cursor ();
293389#if PY_VERSION_HEX >= 0x030E0000
294390 _Py_CODEUNIT* code = _PyCode_CODE (GetFunction ()->code .get ());
@@ -298,6 +394,11 @@ void FrameAsm::linkLightWeightFunctionFrame(
298394 storeConst (x86::rbp, FRAME_OFFSET (FRAME_INSTR ), code, scratch);
299395 env_.addAnnotation (
300396 " Set _PyInterpreterFrame::prev_instr" , store_prev_instr_cursor);
397+ #ifdef Py_GIL_DISABLED
398+ asmjit::BaseNode* tlbc_index_cursor = as_->cursor ();
399+ as_->mov (x86::dword_ptr (x86::rbp, FRAME_OFFSET (tlbc_index)), 0 );
400+ env_.addAnnotation (" Set TLBC index to 0" , tlbc_index_cursor);
401+ #endif
301402
302403 // Store owner
303404 asmjit::BaseNode* store_owner_cursor = as_->cursor ();
@@ -361,8 +462,6 @@ void FrameAsm::linkLightWeightFunctionFrame(
361462 preserver.remap ();
362463 }
363464#else
364- // TODO(T251571746): For FTPython either need to set TLBC somewhere in here or
365- // in the reifier.
366465 throw std::runtime_error{
367466 " linkLightWeightFunctionFrame: Lightweight frames are not supported" };
368467#endif
0 commit comments