Yes exactly. If you have a function parameter or local variable, call it x, even though x dies, i.e. you may no longer access it, the compiler may not overwrite the pointer that may still be in the register or stack slot that holds x. Whether or not it does so, may depend on the register allocator and the degree of register pressure, i.e. whether it needs to reuse the register or stack slot. If it does not, the Boehm GC has no way of knowing that the value is dead. To allow x to be collected, you need to force the compiler to overwrite the register or stack slot. That is technically impossible. If you do: x = NULL; the compiler is free to ignore this, because x is dead. And gcc actually does ignore this. To get gcc not to ignore this, you can declare x to be volatile. But that foils certain optimizations. And even with this, there is no guarantee, that there isn't some other register or stack slot that was temporarily loaded with the value and there is no obligation for the compiler to overwrite that register or stack slot. There is no mechanism in the language spec to force the compiler to overwrite a dead value. But even if you could do this, it would defeat the purpose of using a garbage collector because if you knew where to free up variables then you wouldn't bother using a GC and manually do it. In ordinary usage, this is not much of a problem. People write small functions that have a small number of parameters and local variables and that return quickly. When a function returns, its stack slots disappear and its register slots usually get overwritten. Note that there is no guarantee that if you call a function that uses a register and that function returns, that that register gets overwritten by functions higher up in the call tree. It is possible that a value may linger in a register for a very long time. There is no mechanism in the language to control this. There is an even quirkier failure mode. A function may put stuff in stack slots. Then it may pop off the stack and return. And then the caller may call another function that allocates stack slots. But it may linger in initializing them until they are needed. In the interim, they have the dead values that were previously put there, which will prevent collection if the GC is called before those slots are overwritten. There is no mechanism in the language to control this. Usually this doesn't happen, with small short lived functions. Normally garbage collection is an issue in huge programs where there is generally a lot of register pressure. So we don't see people complaining about this. But this makes writing reliable code hard. There is no way to predict when a the program will reuse a register and finally let go of a pointer. As in my case, I have data residing on the GPU and the pointers to it are kept on the CPU and I rely on them being garbage collected when all references to it disappear. The GPU data is then freed up inside the finalization function. So even though my program works on a lot of data, its not on the CPU and the program overall doesn't have a lot of register pressure so absence of black-holing registers is hurting me. That's why I am asking if it is easy to put in a compiler option like -fgarbage. When it is enabled, gcc should put in an extra instruction to zero out a register because gcc knows when a variable dies (won't be used anymore in the program) because it uses this information to do a lot of optimizations. Hamad ________________________________ From: Andrew Haley <aph@xxxxxxxxxx> Sent: Tuesday, May 21, 2019 4:12:27 AM To: Florian Weimer Cc: Hamad Ahmed; Alexander Monakov; Xi Ruoyao; gcc-help@xxxxxxxxxxx Subject: Re: How to force gcc to blackhole registers so that things maybe garbage collected? On 5/20/19 3:13 PM, Florian Weimer wrote: > * Andrew Haley: > >> On 5/19/19 9:45 AM, Andrew Haley wrote: >>> On 5/18/19 8:45 PM, Hamad Ahmed wrote: >>>> How do I do that? >>> >>> Seriously? Go find the ABI for your processor and find out which >>> registers are call clobbered. Write a subroutine in assembly language >>> that zeroes those registers. >> >> And actually, you can go one better than that: write a bunch of inline asms >> that zero all of the registers, one at a time. >> >> #define CLOBBER(reg) \ >> asm volatile("sub %%" #reg ", %%" #reg ::: #reg, "memory") >> >> static inline void foo() { >> CLOBBER(rax); >> CLOBBER(rbx); >> CLOBBER(rcx); >> CLOBBER(rdx); >> CLOBBER(rbp); >> CLOBBER(rax); >> CLOBBER(rsi); >> CLOBBER(rdi); >> CLOBBER(r8); >> CLOBBER(r9); >> CLOBBER(r10); >> CLOBBER(r11); >> CLOBBER(r12); >> CLOBBER(r13); >> CLOBBER(r14); >> CLOBBER(r15); >> } >> >> This will mostly work, I think, but even then it's not 100% guaranteed. > > With most ABIs, there's also an issue with callee-saved registers, which > could leave dead pointers on the stack (because the callee will save the > register even if it is dead in the caller). True. > The above kludge does not clear those pointers, obviously. Yep. There are no guarantees. -- Andrew Haley Java Platform Lead Engineer Red Hat UK Ltd. <https://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671