Hi,

On Wed, 8 Aug 2007, Maurizio Vitale wrote:

> with reference to the following:
> 
>   struct data {
>     data (long v) : m_data (v) {}
>     data (const data&) {}
>     long  m_data;
>   };
> 
>   data foo (data v) {
>     return v;
>   }
> 
> my reading of the x86_64 ABI (v .98, sept 2006) on page 17 is that
> data should have class MEMORY when passed as argument to function foo.
> This because it has a non-trivial copy constructor 
> (it is not implicitely declared).

That is correct.

> But GCC 4.1.1 and a more recent version from svn give (for foo):
> 
>   .globl _Z3foo4data
>           .type   _Z3foo4data, @function
>   _Z3foo4data:
>   .LFB8:
>           movq    %rdi, %rax
>           ret
>   .LFE8:

That is also correct.

> [so v is passed in a register]

But this conclusion isn't.  In short: that's the address of the return 
slot.

Longr version:

Your confusion stems from the fact, that your 
copy-ctor is empty (so it's code can't be seen in the asm dump), and that 
also your return type of 'foo' is data, which also needs to be returned by 
memory.  Returning in memory is done by the caller allocating the place 
for the return value on its stack, and giving the address of that 
to-be-written-to place as implicit first argument to the function.  That 
address is then also returned in %rax.

So what's placed in %rdi here is not 'v' itself, but some place on it's 
callers stack, which is also to be returned in %rax.  As you have an empty 
copy-ctor you also don't see any other interesting access to 'v' itself, 
so the asm is confusing because nothing hints at the fact that %rdi really 
only holds the address of the return slot.  Look at some arbitrary caller 
of your foo function.  For clarity I've also removed the empty body of of 
cctor, so that calls to external functions remain:

data foo (data v) { return v; }
void callfoo()
{
  data d(32);
  foo (d);
}

callfoo now looks like so:

Z7callfoov:
        pushq   %rbx
        subq    $48, %rsp
        leaq    16(%rsp), %rbx
        leaq    32(%rsp), %rsi
        movq    $32, 32(%rsp)
        movq    %rbx, %rdi
        call    _ZN4dataC1ERKS_
        movq    %rsp, %rdi
        movq    %rbx, %rsi
        call    _Z3foo4data
        addq    $48, %rsp
        popq    %rbx
        ret

You can see three objects overall allocated on the stack 
(%rsp,%rsp+16,%rsp+32).  In particular the one at %rsp is used for the 
return slot for the call of 'foo'.  foo itself is given the address of 
that return slot, and it's input argument 'd', placed in memory at %rsp+16 
(initialised by the cctor from the object at %rsp+32).  So it's passed and 
return in memory, as defined by the ABI.

The passing by memory can be seen easier by actually accessing something 
in the object, like here:

long access (data v) { return v.m_data; }

That's assembled into 

_Z6access4data:
        movq    (%rdi), %rax
        ret

I.e. accessing the m_data member of 'v', which is given only as its 
address in %rdi, i.e. passed by memory.  All as intended.


Ciao,
Michael.
  • x86_64 ABI Maurizio Vitale
    • Re: x86_64 ABI Michael Matz

Reply via email to