[英]How does a return address register work in a processor architecture that doesn't store the return address on the stack?
我試圖弄清楚將調用的返回地址存儲在寄存器(RR)中的架構如何工作(而不是在堆棧上推送和彈出返回地址)。
每次進行嵌套調用時,返回地址寄存器不會被覆蓋(因此在一次返回之后不可能返回)? 閱讀我的作業問題,我應該修改一個匯編程序以使用一個 RR 寄存器來存儲調用的返回地址,而不是在堆棧上推送和彈出它。 我已經搜索過它是如何工作的,但要么那里什么都沒有,信息被很好地隱藏了,要么我的谷歌搜索技能不是那么好。
我不是要求解決問題,但我想知道如何通過程序中的多次調用將返回地址存儲在一個寄存器中是可行的,而無需隨后將寄存器值存儲在堆棧中(這會破壞點練習)。
謝謝你的幫助。
是的,在使用“鏈接寄存器”傳遞返回地址的 ISA 上,非葉 function 必須保存/恢復其返回地址,這與他們在 function 中保存他們想要使用的調用保留寄存器的方式非常相似. 即通常在調用堆棧上。
許多 RISC ISA 沒有推送/彈出指令,但可以使用多條指令完成相同的操作。 例如,從堆棧指針中減去以騰出空間,然后在 function 條目上保存一些寄存器,包括 LR。 然后在返回之前,重新加載寄存器並添加到堆棧指針以恢復調用者的 SP 值和任何其他寄存器。
葉函數(不進行任何 function 調用)可以只保留該寄存器,因此當它們ret
時返回地址仍然存在(或調用任何返回指令,例如 MIPS jr $ra
- 跳轉寄存器到返回 -地址寄存器)。
以編譯器 output 為例:
void external();
void foo(int *p) {
external();
*p = 0; // defeat tail-call optimization
}
由Godbolt 編譯器資源管理器上的GCC 5.4 -O2 -fno-delayed-branch
為 MIPS 編譯
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
function 通常不需要返回與之前相同的寄存器中的返回地址,具體取決於 ISA 使用哪種返回指令。 不過,這是典型的,並且可能有助於對某些微架構進行分支預測。
32 位 ARM 很有趣,並且具有微編碼的push
/ pop
指令,這些指令采用寄存器的位域來推送和彈出。 所以通常在 function 條目上push {r4, lr}
並pop {r4, pc}
作為返回指令。 (ARM 將程序計數器作為 16 個體系結構通用寄存器之一。寫入它是一個跳轉。)將r4
與鏈接寄存器lr
一起推送可保持堆棧對齊,並為您提供一個保留調用的寄存器以供使用.
假設不需要遞歸,您可以發明一個約定,將鏈接(返回寄存器)存儲在不同的寄存器中,具體取決於嵌套級別。
請注意,經典模式下的 IBM 大型機沒有堆棧。 相反,調用者提供了一個由 R13 指向的保存區域,然后在進行調用時,R14 包含返回地址,R15 是被調用的 function 的基地址。 對於遞歸,每個調用者在進行調用之前都會從堆中分配一個新的保存區域。 約定是被調用者將 R13 存儲在保存區域的適當位置,創建保存區域的鏈接鏈,稱為“鏈接堆棧”。 返回時,被調用者需要在返回之前釋放其分配的保存區域。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.