https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126237
Bug ID: 126237
Summary: Local register variables have strange, poorly
documented behaviour
Product: gcc
Version: 14.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: srk31 at srcf dot ucam.org
Target Milestone: ---
Consider the following program, for x86_64.
```
#include <stdio.h>
void middle(long arg)
{
printf("Did we get argc? It's %ld\n", arg);
}
int main(int argc_, char **argv)
{
register long argc asm("rdi") = argc_;
/* Now do some stuff that will clobber %rdi */
printf("Hello, world!\n");
__asm__ volatile ("\
call middle \n\
\n\
" : /* no outputs */
: "r"(argc)
: "rax");
return 0;
}
```
I have tried this on GCC 14.0 (apologies, not tried .4) and a few earlier
versions, and it prints a garbage value rather than that of argc. As documented
here:
https://gcc.gnu.org/onlinedocs/gcc-14.4.0/gcc/Local-Register-Variables.html
... one might naively expect the "rdi" annotation to constrain the register
allocator at the site of an inline assembly block that names this variable as
an input or output, such that local variable argc is in that register at that
point. But then, the middle part of the documentation contains a curious
recommendation to remove any possibly-calling code from between a register
variable declaration and its use at an inline assembly block. That is obviously
violated in the example above, meaning no doubt that I deserve to see the
unwanted behaviour.
But this feels so wrong that I still struggle to believe it is the intended
behaviour. For example, in the code above I could instead simply use "D" as the
constraint (and drop the asm("rdi")) and get the expected behaviour. In that
case, the compiler does not blindly use whatever happened to be left in %rdi,
but instead explicitly reloads the local into %rdi from wherever else it is
stored.
I discovered this problem because I am interested in using local asm vars for
other registers, such as r8 and r9, which as far as I can see do not have their
own constraint.
If this behaviour really must stand, then rather than warning people to be
careful about call clobbers, surely the documentation needs a much clearer
positive statement that *unlike* the usual behaviour of constraints, the use of
local asm variables at an inline assembly block does not reload that local if
it has been displaced from its nominated register. This is also what happens if
I write "D" above but don't drop the asm("rdi"), so that needs documenting too.
Currently the documentation goes out of its way to emphasise that such
variables can be displaced from the nominated register. But this only makes the
reader think that at the inline assembly block they will surely be put back!
That is the usual behaviour of the register allocator, after all.
If I'm happy to author the relevant tweak to the documentation -- although an
implementation fix would be far superior.
This is related to 87978 and 116988 but it is not a duplicate.