Функция может активно задействовать регистры для различных задач. Например, возьмем следующую программу на Linux:
global _start section .text _start: mov rdi, 125 mov rax, 15 call sum add rdi, rax ; RDI = 25 mov rax, 60 syscall ; определяем функцию sum sum: mov rdi, 5 mov rax, 10 add rax, rdi ret
Для примера здесь определена функция sum, которая устанавливает регистры rax и rdi и складывает их значения, помещая результат в регистр rax.
В основной части программы помещаем в регистр RDI чило 125, затем вызываем функцию sum. Далее результат функции sum - содержимое регистра RDI складываем с числом из регистра RAX. Однако, поскольку вызов процедуры sum изменил значение регистра RDI, то в после ее вызова в этом регистре будет не число 125, а число 5. Соответственно итоговый результат может отличаться от ожидаемого. В данном случае при завершении программы в регистре RDI будет число 20, а не 140.
Конечно, мы могли бы выбрать разные регистры, но данный пример - упрощение, в реальной функции одновременно может быть задействовано для сложных вычислений множество регистров. Кроме того, разрабытываемые процедуры могут использоваться во внешней программе, и при разработке процедуры мы можем не знать, какие именно регистры и как именно будет использовать внешняя программа. Поэтому хорошей, а иногда и необходимой практикой при вызове процедуры является сохранение значений используемых регистров в стек, а при завершении процедуры - их восстановление. Например:
global _start section .text _start: mov rdi, 125 call sum add rdi, rax ; RDI = 140 mov rax, 60 syscall ; определяем функцию sum sum: push rdi ; сохраняем в стек регистр rdi mov rdi, 5 mov rax, 10 add rax, rdi pop rdi ; восстанавливаем из стека регистр rdi ret
В данном случае в начале функции sum сохраняем значение rdi, а при завершении восстанавливаем его. Подобным образом стоит сохранять и восстанавливать все используемые регистры в стек. Регистр RAX в данном случае не сохраняется, так как мы ожидаем, что через него основная часть программы получить результат процедуры sum.
При этом в принципе необязательно сохранять регистры в вызываемой функции, мы могли бы это сделать и в вызывающем коде:
global _start section .text _start: mov rdi, 125 push rdi ; сохраняем в стек регистр rdi call sum pop rdi ; восстанавливаем из стека регистр rdi add rdi, rax ; RDI = 140 mov rax, 60 syscall ; определяем функцию sum sum: mov rdi, 5 mov rax, 10 add rax, rdi ret
Однако сохранение регистров в вызываемой функции может быть более предпочтительным, так как вызывающий код может вызывать кучу других функций и постоянно сохранять/восстанавливать регистры при каждом вызове значительно утяжелит код основной части программы.
Все, что касается регистров, также можно отнести и к глобальным переменным, которые используются в функциях. Например, возьмем следующую программу на Linux:
global _start section .data num dq 45 section .text _start: call sum mov rdi, rax ; RDI = RAX = 25 add rdi, [num] ; RDI = RDI + num = 25 + 15 = 40 mov rax, 60 syscall ; определяем функцию sum sum: mov qword [num], 15 ; изменяем переменную num mov rax, 10 add rax, [num] ; RAX = RAX + num = 10 + 15 = 25 ret
В данном случае в основной части программы мы ожидаем, что переменная num будет равна 45, но вызов функции sum меняет ее значение на 15. И подобным образом в вызываемой функции переменную можно сохранить в стек:
global _start section .data num dq 45 section .text _start: call sum mov rdi, rax ; RDI = RAX = 25 add rdi, [num] ; RDI = RDI + num = 25 + 45 = 70 mov rax, 60 syscall ; определяем функцию sum sum: push qword [num] ; сохраняем значение num в стек mov qword [num], 15 ; изменяем переменную num mov rax, 10 add rax, [num] ; RAX = RAX + num = 10 + 15 = 25 pop qword [num] ; восстанавливаем num из стека ret
Аналогичный пример на Windows:
global _start section .data num dq 45 section .text _start: call sum add rax, [rel num] ; RDI = RDI + num = 25 + 45 = 70 ret ; определяем функцию sum sum: push qword [rel num] ; сохраняем значение num в стек mov qword [rel num], 15 ; изменяем переменную num mov rax, 10 add rax, [rel num] ; RAX = RAX + num = 10 + 15 = 25 pop qword [rel num] ; восстанавливаем num из стека ret