Да, на ISA, которые используют «регистр связи» для передачи обратного адреса, неконечная функция должна сохранять / восстанавливать свой обратный адрес, очень похоже на то, как они сохраняют сохраняемый вызовом регистр, который они хотели использовать внутри функции. то есть обычно в стеке вызовов.
Многие ISA RIS C не имеют инструкций push / pop, но одну и ту же операцию можно выполнить с несколькими инструкциями. например, вычесть из указателя стека, чтобы освободить место, затем сохранить некоторые регистры, включая LR, при входе в функцию. Затем, перед возвратом, перезагрузите регистры и добавьте в указатель стека, чтобы восстановить значение SP вызывающей стороны и любые другие регистры.
Листовые функции (которые не выполняют никаких вызовов функций) могут просто оставить этот регистр в покое, поэтому адрес возврата все еще там, когда они ret
(или как бы ни была вызвана инструкция возврата, например, MIPS jr $ra
- переход в регистр адреса возврата).
Посмотрите на вывод компилятора, например:
void external();
void foo(int *p) {
external();
*p = 0; // defeat tail-call optimization
}
скомпилировано для MIPS G CC 5.4 -O2 -fno-delayed-branch
в проводнике компилятора Godbolt
foo(int*):
addiu $sp,$sp,-32 # reserve 32 bytes of stack space (MIPS calling convention I think guarantees some "shadow space" for callees)
sw $31,28($sp) # $31 is MIPS's $ra return address reg
sw $16,24($sp) # $16 is a call-preserved register
move $16,$4 # save p for later use
jal external
nop # branch-delay slot
lw $31,28($sp) # reload return address
sw $0,0($16) # *p = 0
lw $16,24($sp) # restore caller's $16
addiu $sp,$sp,32 # restore stack
j $31 # jump to return address
nop # branch delay slot
Это не , как правило, необходимо для возврата функции с адресом возврата в том же регистре, в котором она была раньше, в зависимости от того, какую инструкцию возврата использует ISA. Это типично и, возможно, помогает прогнозировать переходы на некоторых микроархитектурах.
32-битный ARM забавен и содержит микрокодированные инструкции push
/ pop
, которые переводят битовое поле регистров в pu sh и поп-музыка. Так что обычно push {r4, lr}
при вводе функции и pop {r4, pc}
как инструкция возврата. (ARM имеет счетчик программ в качестве одного из 16 архитектурных регистров общего назначения. Запись в него - прыжок.) Нажатие r4
вместе с регистром ссылок lr
сохраняет стек в выравнивании и обеспечивает сохранение вызова. Зарегистрируйтесь, чтобы играть с.