This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: x86_64 ABI
- From: Michael Matz <matz at suse dot de>
- To: Maurizio Vitale <maurizio dot vitale at polymath-solutions dot com>
- Cc: GCC Mailing List <gcc at gcc dot gnu dot org>
- Date: Fri, 14 Jul 2006 19:10:07 +0200 (CEST)
- Subject: Re: x86_64 ABI
- References: <D5FEC07E-EF24-4228-82C4-87D7E0FE9220@polymath-solutions.com>
Hi,
On Thu, 13 Jul 2006, Maurizio Vitale wrote:
> my understanding of the x86_64 ABI is that the following structure should be
> passed in registers:
Right.
> struct data {
> unsigned int x;
> unsigned int y;
> unsigned long z;
> };
>
> but when I compile:
>
> #include <stdio.h>
>
> struct data {
> unsigned int x : 32;
> unsigned int y : 32;
> unsigned long dummy : 64;
> } ;
Note that this contains bitfields, which sometimes is handled specially
in the ABI. It doesn't matter in this case, but I though I point it out.
> I get different results, depending on whether I compile it as C or C++ code.
> I'm using gcc 4.1.1 (but the same happens with 3.4.5).
>
> In C I get:
> main:
> .LFB12:
> subq $8, %rsp
> .LCFI0:
> movl $1, d+8(%rip)
> movl $2, d+12(%rip)
> movq d(%rip), %rdi
> movq d+8(%rip), %rsi
> call foo
That doesn't match the layout in the code presented. It should set d+0
and d+4 (members x and y are at the start of d). But let's ignore that.
The value is passed just fine in registers here, as expected and as you
said already.
> But in C++ the result is different and the data structure is passed on the
> stack.
> main:
> .LFB13:
> subq $24, %rsp
> .LCFI0:
> movl $1, d+8(%rip)
> movq d(%rip), %rdi
> movl $2, d+12(%rip)
> movq d+8(%rip), %rsi
> movq %rdi, (%rsp)
> movq %rsi, 8(%rsp)
> call foo
No, nothing is passed on the stack. To see that you need to look from
where 'foo' actually reads the arguments. For a trivial foo I get this
code:
int foo (data t)
{
return t.x + t.y;
}
foo:
.LFB13:
movq %rdi, -16(%rsp)
movl -16(%rsp), %eax
addl -12(%rsp), %eax
ret
Reading from registers, exactly right. What confused you were the writes
to some stack memory, which _looks_ as if they are there to pass something
on the stack. In reality it's simply a dead store to a dead temporary
which happened to be placed on the stack and gcc wasn't able to optimize
away. To see that look at the GIMPLE code generated by the gcc and g++
frontends. (from t03.gimple). gcc:
main (argc, argv)
{
int D.2232;
int D.2233;
d.x = 1;
d.y = 2;
D.2232 = foo (d);
g++:
int main(int, char**) (argc, argv)
{
struct data D.2838;
int D.2839;
int D.2840;
d.x = 1;
d.y = 2;
D.2838 = d;
D.2839 = foo (D.2838);
Note the extra temporary D.2838. The stack write you see correspond to
"D.2838 = d". GCC then later was able to optimize the use of D.2838 (as
call argument) away to directly use d (i.e. %rsi/%rdi which happen to
contain the value of d after the writes to x and y), but not the store to
the stack place allocated for D.2838.
That's an artifact how calls are generated in the c++ frontend (it's too
eagerly using temporaries), but not error.
Ciao,
Michael.