[This question and it's answers do a pretty thorough general treatment of the pros and cons of passing structs vs struct-pointers. This answer intends to treat the specific case mentioned in this question i.e., 8 byte struct vs 8 byte pointer and an ABI that passes arguments in registers.]
On a 64-bit Intel CPU running Linux, the ABI requires that 8 byte arguments
be passed via registers until there are no more left. e.g. the first one is
passed via the %rdi
register. This isn't about optimization. It is an ABI
requirement.
In this particular case (8 byte struct vs 8 byte pointer), both the pointer
and the struct will be passed via one single register. i.e. neither case
uses the stack at all. In fact, if you have a simple enough function like:
int
add (struct Point p)
{
return p.x + p.y;
}
.. and compile with gcc -O1
, the function won't even have a stack frame.
You can see this in generated code (x86_64 Linux gcc
5.1, with -O1
):
# Passing the struct:
movq %rdi, %rax
sarq $32, %rax
addl %edi, %eax
ret
# Passing a pointer to the struct:
# [each (%rdi) is a memory access]
movl 4(%rdi), %eax
addl (%rdi), %eax
ret
But as you can see, the pointer version accesses memory twice. So, passing the value is faster. Passing the pointer will generate memory accesses in order to fetch the struct's members. And then there's the additional risk that the struct might be on a memory block that isn't cached by the CPU cache, and the access will lead to a cache miss. This shouldn't happen because typically, the caller would just have accessed the same struct and so it is on the cache.
On 32-bit Linux, int
s continue to be 4 bytes, but pointers get smaller (8
down to 4). And since arguments are passed on the stack, this means passing
the pointer saves 4 bytes on the stack (8 byte struct, vs 4 byte pointer).
But I still like passing by value because of improved spatial locality.