> компилятор просто передаст регистр(ы) в качестве аргумента вызываемой функции
> и постарается использовать еще не задействованные регистры. Тут их количество и
> сыграет решающую роль.Возможно, вы меня не так поняли (или я вас). Я говорю не о передаче через регистры параметров. Давайте лучше на примере.
Пусть у нас есть функции void f(void) и void g(void):
void f (void) {
for (int i = 0; i != 10; ++i)
g();
}
Допустим компилятор решает хранить переменную i в регистре %rdi. Однако функция g тоже может использовать этот регистр, и его состояние надо сохранить на время её вызова. Тут есть два варианта того, какое соглашение принято (не знаю, какой из них применяется на практике, возможно даже в разных ОС применяются разные).
1 вариант. За сохранение регистров отвечает вызывающая функция (та, чьё значение должно быть сохранено). То есть функция заботится о сохранении своих локальных переменных. В таком случае f будет выглядеть как-то так (грубо, просто чтоб смысл объяснить):
f:
movl $0, %rdi
f_loop:
cmpl %rdi, $10
je f_return
pushl %rdi ; сохранить регистр, так как g может его поменять
call g
popl %rdi ; восстановить значение
incl %rdi
jmp f_loop
f_return
ret
2 вариант. За сохранение регистров отвечает вызываемая функция (та, которая использует регистр). То есть функция заботится о сохранении чужих локальных переменных, которые она может затереть.
f:
pushl %rdi; сохранить регистр, так как он может использоваться внешней функцией
movl $0, %rdi
f_loop:
cmpl %rdi, $10
je f_return
call g
incl %rdi
jmp f_loop
f_return
popl %rdi ; восстановить значение, потенциально используемое внешней функцией
ret
Во-первых, в любом случае есть как минимум одно сохранение-восстановление из стека. Во-вторых, если вместо стека мы попытаемся использовать другой, "неиспользуемый", регистр (например, %rsi), то тогда в стек придётся сохранять его, %rsi, значение.
Можно ли как-то оптимизировать этот случай (кроме inline)?