Я узнал, что если какой-либо из caller saved registers (rax rdx rcx rsi rdi r8 r9 r10 r11)
используется вызываемым пользователем, он должен быть сохранен до и восстановлен после инструкции call
вызывающим.
В следующем примере
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);
}
Примечание
Переменные
a - g
должны использовать всеcallee saved registers (rbp rsp rbx r12 r13 r14 r15)
. И мы не можем использовать одновременноrbp
илиrsp
, поскольку для адресации памяти стека необходимо использовать любой из них.read
иprint
взяты из некоторого внешнего модуля компиляции. Таким образом, мы действительно не знаем об использовании регистров сохранения вызывающей стороны, когда мы компилируем текущий модуль компиляции, в частности, во время выделения регистров для функцииmain
.
В godbolt с -O3
он компилируется в следующее
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
Примечание
Переменная
a
переносится в12(%rsp)
.Нам не нужно проливать какие-либо
caller saved registers
, поскольку они вообще не используются, что здесь оказывается более эффективным.
Мои вопросы
Похоже, нам действительно не нужно заниматься проливанием
caller saved registers
, если мы их не используем. Таким образом, когда мы должны использоватьcaller saved registers
?Для таких вызываемых абонентов, как
read
иprint
, поскольку мы не знаем об их использовании регистров, как мы должны сделать разлив дляcaller saved registers
?
Спасибо