Skip to content

gh-160147: Inline PyObject_IS_GC() in _PyObject_GC_MAY_BE_TRACKED() - #161189

Merged
vstinner merged 1 commit into
python:mainfrom
vstinner:gc_inline
Nov 3, 2026
Merged

vstinner merged 1 commit into
python:mainfrom
vstinner:gc_inline

Conversation

@vstinner

@vstinner vstinner commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Move _PyType_IS_GC() and _PyObject_IS_GC() from pycore_object.h to pycore_gc.h. Replace PyObject_IS_GC() call with _PyObject_IS_GC() in _PyObject_GC_MAY_BE_TRACKED().

Help the compiler to inline _PyObject_IS_GC() code in _PyObject_GC_MAY_BE_TRACKED(), especially when LTO is not used.

…ED()

Move _PyType_IS_GC() and _PyObject_IS_GC() from pycore_object.h to
pycore_gc.h. Replace PyObject_IS_GC() call with _PyObject_IS_GC() in
_PyObject_GC_MAY_BE_TRACKED().

Help the compiler to inline _PyObject_IS_GC() code in
_PyObject_GC_MAY_BE_TRACKED(), especially when LTO is not used.
@vstinner

vstinner commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

I built Python with gcc -O3 on Fedora 44 (gcc 16.2.1).

x86-64 assembly code, before: call 0x64b300 <PyObject_IS_GC>.

Details
(gdb) disassemble _PyTuple_MaybeUntrack
Dump of assembler code for function _PyTuple_MaybeUntrack:
   0x000000000052f860 <+0>:	cmp    QWORD PTR [rdi+0x8],0x9a4a80
   0x000000000052f868 <+8>:	je     0x52f870 <_PyTuple_MaybeUntrack+16>
   0x000000000052f86a <+10>:	ret
   0x000000000052f86b <+11>:	nop    DWORD PTR [rax+rax*1+0x0]
   0x000000000052f870 <+16>:	mov    rax,QWORD PTR [rdi-0x10]
   0x000000000052f874 <+20>:	test   rax,rax
   0x000000000052f877 <+23>:	je     0x52f86a <_PyTuple_MaybeUntrack+10>
   0x000000000052f879 <+25>:	push   rbp
   0x000000000052f87a <+26>:	mov    rdx,rdi
   0x000000000052f87d <+29>:	mov    rbp,rsp
   0x000000000052f880 <+32>:	push   rbx
   0x000000000052f881 <+33>:	sub    rsp,0x28
   0x000000000052f885 <+37>:	mov    rsi,QWORD PTR [rdi+0x10]
   0x000000000052f889 <+41>:	mov    rbx,rsi
   0x000000000052f88c <+44>:	test   rsi,rsi
   0x000000000052f88f <+47>:	jle    0x52f8ec <_PyTuple_MaybeUntrack+140>
   0x000000000052f891 <+49>:	xor    esi,esi
   0x000000000052f893 <+51>:	mov    rcx,QWORD PTR [rdx+rsi*8+0x20]
   0x000000000052f898 <+56>:	test   rcx,rcx
   0x000000000052f89b <+59>:	je     0x52f8cb <_PyTuple_MaybeUntrack+107>

   # _PyObject_GC_MAY_BE_TRACKED: PyObject_IS_GC()
   0x000000000052f89d <+61>:	mov    rdi,rcx
   0x000000000052f8a0 <+64>:	mov    QWORD PTR [rbp-0x18],rsi
   0x000000000052f8a4 <+68>:	mov    QWORD PTR [rbp-0x20],rdx
   0x000000000052f8a8 <+72>:	mov    QWORD PTR [rbp-0x28],rcx
   0x000000000052f8ac <+76>:	call   0x64b300 <PyObject_IS_GC>

   0x000000000052f8b1 <+81>:	mov    rsi,QWORD PTR [rbp-0x18]
   0x000000000052f8b5 <+85>:	mov    rdx,QWORD PTR [rbp-0x20]
   0x000000000052f8b9 <+89>:	test   eax,eax
   0x000000000052f8bb <+91>:	je     0x52f8df <_PyTuple_MaybeUntrack+127>
   0x000000000052f8bd <+93>:	mov    rcx,QWORD PTR [rbp-0x28]
   0x000000000052f8c1 <+97>:	cmp    QWORD PTR [rcx+0x8],0x9a4a80
   0x000000000052f8c9 <+105>:	je     0x52f8d8 <_PyTuple_MaybeUntrack+120>
   0x000000000052f8cb <+107>:	mov    rbx,QWORD PTR [rbp-0x8]
   0x000000000052f8cf <+111>:	leave
   0x000000000052f8d0 <+112>:	ret
   0x000000000052f8d1 <+113>:	nop    DWORD PTR [rax+0x0]
   0x000000000052f8d8 <+120>:	cmp    QWORD PTR [rcx-0x10],0x0
   0x000000000052f8dd <+125>:	jne    0x52f8cb <_PyTuple_MaybeUntrack+107>
   0x000000000052f8df <+127>:	add    rsi,0x1
   0x000000000052f8e3 <+131>:	cmp    rsi,rbx
   0x000000000052f8e6 <+134>:	jne    0x52f893 <_PyTuple_MaybeUntrack+51>
   0x000000000052f8e8 <+136>:	mov    rax,QWORD PTR [rdx-0x10]
   0x000000000052f8ec <+140>:	mov    rsi,QWORD PTR [rdx-0x8]
   0x000000000052f8f0 <+144>:	mov    rcx,QWORD PTR [rax+0x8]
   0x000000000052f8f4 <+148>:	and    rsi,0xfffffffffffffffc
   0x000000000052f8f8 <+152>:	and    ecx,0x3
   0x000000000052f8fb <+155>:	or     rcx,rsi
   0x000000000052f8fe <+158>:	mov    QWORD PTR [rsi],rax
   0x000000000052f901 <+161>:	mov    QWORD PTR [rax+0x8],rcx
   0x000000000052f905 <+165>:	mov    rax,0xfffffffffffffff0
   0x000000000052f90c <+172>:	and    QWORD PTR [rdx-0x8],0x1
   0x000000000052f911 <+177>:	mov    QWORD PTR [rdx-0x10],0x0
   0x000000000052f919 <+185>:	mov    rax,QWORD PTR fs:[rax]
   0x000000000052f91d <+189>:	sub    QWORD PTR [rax+0x1d78],0x1
   0x000000000052f925 <+197>:	jmp    0x52f8cb <_PyTuple_MaybeUntrack+107>
End of assembler dump.

x86-64 assembly code, after: _PyObject_IS_GC() is inlined.

Details
(gdb) disassemble _PyTuple_MaybeUntrack
Dump of assembler code for function _PyTuple_MaybeUntrack:
   0x000000000052f860 <+0>:	cmp    QWORD PTR [rdi+0x8],0x9a4a80
   0x000000000052f868 <+8>:	je     0x52f870 <_PyTuple_MaybeUntrack+16>
   0x000000000052f86a <+10>:	ret
   0x000000000052f86b <+11>:	nop    DWORD PTR [rax+rax*1+0x0]
   0x000000000052f870 <+16>:	mov    rax,QWORD PTR [rdi-0x10]
   0x000000000052f874 <+20>:	test   rax,rax
   0x000000000052f877 <+23>:	je     0x52f86a <_PyTuple_MaybeUntrack+10>
   0x000000000052f879 <+25>:	push   rbp
   0x000000000052f87a <+26>:	mov    rbp,rsp
   0x000000000052f87d <+29>:	sub    rsp,0x20
   0x000000000052f881 <+33>:	mov    QWORD PTR [rbp-0x8],r14
   0x000000000052f885 <+37>:	mov    r14,QWORD PTR [rdi+0x10]
   0x000000000052f889 <+41>:	mov    QWORD PTR [rbp-0x10],r13
   0x000000000052f88d <+45>:	mov    r13,rdi
   0x000000000052f890 <+48>:	test   r14,r14
   0x000000000052f893 <+51>:	jle    0x52f925 <_PyTuple_MaybeUntrack+197>
   0x000000000052f899 <+57>:	mov    QWORD PTR [rbp-0x18],r12
   0x000000000052f89d <+61>:	mov    QWORD PTR [rbp-0x20],rbx
   0x000000000052f8a1 <+65>:	xor    ebx,ebx
   0x000000000052f8a3 <+67>:	xchg   ax,ax
   0x000000000052f8a5 <+69>:	data16 cs nop WORD PTR [rax+rax*1+0x0]
   0x000000000052f8b0 <+80>:	mov    r12,QWORD PTR [r13+rbx*8+0x20]
   0x000000000052f8b5 <+85>:	test   r12,r12
   0x000000000052f8b8 <+88>:	je     0x52f8ea <_PyTuple_MaybeUntrack+138>

   # _PyObject_IS_GC: PyTypeObject *type = Py_TYPE(obj);
   0x000000000052f8ba <+90>:	mov    rax,QWORD PTR [r12+0x8]

   # _PyObject_IS_GC: _PyType_IS_GC(type)
   # _PyType_HasFeature(type, Py_TPFLAGS_HAVE_GC)
   0x000000000052f8bf <+95>:	test   BYTE PTR [rax+0xa9],0x40
   0x000000000052f8c6 <+102>:	je     0x52f910 <_PyTuple_MaybeUntrack+176>

   # _PyObject_IS_GC: type->tp_is_gc == NULL
   0x000000000052f8c8 <+104>:	mov    rdx,QWORD PTR [rax+0x148]
   0x000000000052f8cf <+111>:	test   rdx,rdx
   0x000000000052f8d2 <+114>:	je     0x52f8e2 <_PyTuple_MaybeUntrack+130>

   # _PyObject_IS_GC: type->tp_is_gc(obj)
   0x000000000052f8d4 <+116>:	mov    rdi,r12
   0x000000000052f8d7 <+119>:	call   rdx
   0x000000000052f8d9 <+121>:	test   eax,eax
   0x000000000052f8db <+123>:	je     0x52f910 <_PyTuple_MaybeUntrack+176>

   0x000000000052f8dd <+125>:	mov    rax,QWORD PTR [r12+0x8]
   0x000000000052f8e2 <+130>:	cmp    rax,0x9a4a80
   0x000000000052f8e8 <+136>:	je     0x52f900 <_PyTuple_MaybeUntrack+160>
   0x000000000052f8ea <+138>:	mov    rbx,QWORD PTR [rbp-0x20]
   0x000000000052f8ee <+142>:	mov    r12,QWORD PTR [rbp-0x18]
   0x000000000052f8f2 <+146>:	mov    r13,QWORD PTR [rbp-0x10]
   0x000000000052f8f6 <+150>:	mov    r14,QWORD PTR [rbp-0x8]
   0x000000000052f8fa <+154>:	leave
   0x000000000052f8fb <+155>:	ret
   0x000000000052f8fc <+156>:	nop    DWORD PTR [rax+0x0]
   0x000000000052f900 <+160>:	cmp    QWORD PTR [r12-0x10],0x0
   0x000000000052f906 <+166>:	jne    0x52f8ea <_PyTuple_MaybeUntrack+138>
   0x000000000052f908 <+168>:	nop    DWORD PTR [rax+rax*1+0x0]
   0x000000000052f910 <+176>:	add    rbx,0x1
   0x000000000052f914 <+180>:	cmp    rbx,r14
   0x000000000052f917 <+183>:	jne    0x52f8b0 <_PyTuple_MaybeUntrack+80>
   0x000000000052f919 <+185>:	mov    rax,QWORD PTR [r13-0x10]
   0x000000000052f91d <+189>:	mov    rbx,QWORD PTR [rbp-0x20]
   0x000000000052f921 <+193>:	mov    r12,QWORD PTR [rbp-0x18]
   0x000000000052f925 <+197>:	mov    rcx,QWORD PTR [r13-0x8]
   0x000000000052f929 <+201>:	mov    rdx,QWORD PTR [rax+0x8]
   0x000000000052f92d <+205>:	mov    r14,QWORD PTR [rbp-0x8]
   0x000000000052f931 <+209>:	and    rcx,0xfffffffffffffffc
   0x000000000052f935 <+213>:	and    edx,0x3
   0x000000000052f938 <+216>:	or     rdx,rcx
   0x000000000052f93b <+219>:	mov    QWORD PTR [rcx],rax
   0x000000000052f93e <+222>:	mov    QWORD PTR [rax+0x8],rdx
   0x000000000052f942 <+226>:	mov    rax,0xfffffffffffffff0
   0x000000000052f949 <+233>:	and    QWORD PTR [r13-0x8],0x1
   0x000000000052f94e <+238>:	mov    QWORD PTR [r13-0x10],0x0
   0x000000000052f956 <+246>:	mov    rax,QWORD PTR fs:[rax]
   0x000000000052f95a <+250>:	mov    r13,QWORD PTR [rbp-0x10]
   0x000000000052f95e <+254>:	sub    QWORD PTR [rax+0x1d78],0x1
   0x000000000052f966 <+262>:	leave
   0x000000000052f967 <+263>:	ret
End of assembler dump.

@vstinner

vstinner commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

This PR is interesting for #160154 which adds a _PyObject_GC_MAY_BE_TRACKED() call to Objects/setobject.c. But it's also interesting even if the frozenset change is not merged.

@vstinner vstinner removed the type-feature A feature request or enhancement label Nov 2, 2026
@vstinner
vstinner merged commit ce841a9 into python:main Nov 3, 2026
58 of 63 checks passed
@vstinner
vstinner deleted the gc_inline branch November 3, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance or resource usage skip news

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant