I have learned that if any of the caller saved registers (rax rdx rcx rsi rdi r8 r9 r10 r11)
is used by the callee, then it has to be saved before and restored after a call
instruction by the caller.
Through the following example,
int read();
void print(int i);
int main()
{
int a = read();
int b = read();
int c = read();
int d = read();
int e = read();
int f = read();
int g = read();
print(a);
print(b);
print(c);
print(d);
print(e);
print(f);
print(g);
}
Note
The variables a - g
should use all the callee saved registers (rbp rsp rbx r12 r13 r14 r15)
. And we cannot use both rbp
or rsp
, since either has to be used for addressing the stack memory.
The read
and print
are from some external compilation unit. Thus, we don't really know about their caller save registers usage when we compile the current compilation unit, specifically, during register allocation for the main
function.
In godbolt with -O3
it compiles to the following
main:
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbp
pushq %rbx
subq $24, %rsp # spill here
call read()
movl %eax, 12(%rsp) # spill here
call read()
movl %eax, %ebx
call read()
movl %eax, %r15d
call read()
movl %eax, %r14d
call read()
movl %eax, %r13d
call read()
movl %eax, %r12d
call read()
movl 12(%rsp), %edi
movl %eax, %ebp
call print(int)
movl %ebx, %edi
call print(int)
movl %r15d, %edi
call print(int)
movl %r14d, %edi
call print(int)
movl %r13d, %edi
call print(int)
movl %r12d, %edi
call print(int)
movl %ebp, %edi
call print(int)
addq $24, %rsp
xorl %eax, %eax
popq %rbx
popq %rbp
popq %r12
popq %r13
popq %r14
popq %r15
ret
Note
The variable a
is spilled into 12(%rsp)
.
We don't need to do spill any of the caller saved registers
since they are not used at all, which turns out to be more efficient here.
My questions
Look like we don't really need to deal with spilling the caller saved registers
, if we don't use them. Thus, when should we use the caller saved registers
?
For callees like read
and print
since we don't know about their register usage, how should we do the spilling for the caller saved registers
?
Thanks
It looks like the confusing and unintuitive "caller saved / callee saved" terminology has misled you into thinking every register should always be saved by someone somewhere. See What are callee and caller saved registers? - "call preserved" vs. "call clobbered" is more useful both in ease of remembering and as a mental model. It's normal to let values be destroyed, like a function arg.
Look like we don't really need to deal with spilling the caller saved registers, if we don't use them.
Note that your function does use a couple call-clobbered ("caller saved") registers: It uses RDI to pass the arg to print(int)
, and it zeros RAX as main's return value.
In cases where it has a value in a call-clobbered register that needs to survive across a function call, GCC chose to mov
that value to a call-preserved register. e.g. when read()
returns, its return value is in EAX, which would be destroyed by the next call. So it uses mov %eax, %ebp
or whatever to save it into a call-preserved register, or spills one to 12(%rsp)
.
(Note that GCC used push/pop to save/restore its caller's values of the call-preserved registers it uses.)
GCC's default code-gen strategy is to save/restore call-preserved registers to hold values across calls, instead of spilling to memory inside this function. That's generally a good thing for less trivial cases, especially for calls inside loops. See Why do compilers insist on using a callee-saved register here? for more about that.
And we cannot use both rbp or rsp, since either has to be used for addressing the stack memory.
Also false: with -fomit-frame-pointer
(on at most optimization levels), RBP is just another call-preserved register. Your function uses it to save a read
return value. (EBP is the low 32 bits of RBP).