Функция может активно задействовать регистры для различных задач. Например:
.globl _start .text _start: movq $125, %rdi call sum addq %rax, %rdi # RDI = 20 movq $60, %rax syscall sum: movq $5, %rdi movq $10, %rax addq %rdi, %rax ret
Для примера здесь определена функция sum, которая устанавливает регистры rax и rdi и складывает их значения, помещая результат в регистр rax.
В основной части программы помещаем в регистр RDI чило 125, затем вызываем функцию sum. Далее результат функции sum - содержимое регистра RDI складываем с числом из регистра RAX. Однако, поскольку вызов процедуры sum изменил значение регистра RDI, то в после ее вызова в этом регистре будет не число 125, а число 5. Соответственно итоговый результат может отличаться от ожидаемого. В данном случае при завершении программы в регистре RDI будет число 20, а не 140.
Конечно, мы могли бы выбрать разные регистры, но данный пример - упрощение, в реальной функции одновременно может быть задействовано для сложных вычислений множество регистров. Кроме того, разрабытываемые процедуры могут использоваться во внешней программе, и при разработке процедуры мы можем не знать, какие именно регистры и как именно будет использовать внешняя программа. Поэтому хорошей, а иногда и необходимой практикой при вызове процедуры является сохранение значений используемых регистров в стек, а при завершении процедуры - их восстановление. Например:
.globl _start .text _start: movq $125, %rdi call sum addq %rax, %rdi # RDI = 140 movq $60, %rax syscall sum: pushq %rdi # сохраняем в стек регистр rdi movq $5, %rdi movq $10, %rax addq %rdi, %rax popq %rdi # восстанавливаем из стека регистр rdi ret
В данном случае в начале функции sum сохраняем значение rdi, а при завершении восстанавливаем его. Подобным образом стоит сохранять и восстанавливать все используемые регистры в стек. Регистр RAX в данном случае не сохраняется, так как мы ожидаем, что через него основная часть программы получить результат процедуры sum.
При этом в принципе необязательно сохранять регистры в вызываемой функции, мы могли бы это сделать и в вызывающем коде:
.globl _start .text _start: movq $125, %rdi pushq %rdi # сохраняем в стек регистр rdi call sum popq %rdi # восстанавливаем из стека регистр rdi addq %rax, %rdi # RDI = 140 movq $60, %rax syscall sum: movq $5, %rdi movq $10, %rax addq %rdi, %rax ret
Однако сохранение регистров в вызываемой функции может быть более предпочтительным, так как вызывающий код может вызывать кучу других функций и постоянно сохранять/восстанавливать регистры при каждом вызове значительно утяжелит код основной части программы.
Все, что касается регистров, также можно отнести и к глобальным переменным, которые используются в процедурах:
.globl _start .data num: .quad 45 .text _start: call sum movq %rax, %rdi # RDI = RAX = 25 addq num, %rdi # RDI = RDI + num = 25 + 15 = 40 movq $60, %rax syscall sum: movq $15, num movq $10, %rax addq num, %rax # RAX = RAX + num = 10 + 15 = 25 ret
В данном случае в основной части программы мы ожидаем, что переменная num будет равна 45, но вызов функции sum меняет ее значение на 15. И подобным образом в вызываемой функции переменную можно сохранить в стек:
.globl _start .data num: .quad 45 .text _start: call sum movq %rax, %rdi # RDI = RAX = 25 addq num, %rdi # RDI = RDI + num = 25 + 45 = 70 movq $60, %rax syscall sum: pushq num # сохраняем значение num в стек movq $15, num movq $10, %rax addq num, %rax # RAX = RAX + num = 10 + 15 = 25 popq num # восстанавливаем num из стека ret