How to force gcc to blackhole registers so that things maybe garbage collected?

Hamad Ahmed ahmed90@purdue.edu
Tue May 21 16:30:00 GMT 2019


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@redhat.com>
Sent: Tuesday, May 21, 2019 4:12:27 AM
To: Florian Weimer
Cc: Hamad Ahmed; Alexander Monakov; Xi Ruoyao; gcc-help@gcc.gnu.org
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



More information about the Gcc-help mailing list