返回地址寄存器如何在不将返回地址存储在堆栈中的处理器体系结构中工作?

问题描述 投票:4回答:2

我正在尝试弄清楚将调用的返回地址存储在寄存器(RR)中的体系结构将如何工作(与将返回地址压入并弹出堆栈)相对。

每次进行嵌套调用时,返回地址寄存器是否都将被覆盖(因此,一次返回就不可能返回)?阅读我的作业问题,我应该修改一个汇编程序以使用一个RR寄存器来存储调用的返回地址,而不是将其压入并弹出堆栈。我已经搜索了它的工作方式,但是要么没有东西了,信息被很好地隐藏了,要么我的谷歌搜索技能不是那么好。

我不是要解决的问题,但是我想知道在程序中多次调用如何将返回地址存储在一个寄存器中而不在随后将寄存器值存储在堆栈中是如何可行的(这会破坏点的练习)。

感谢您的帮助。

assembly cpu-architecture
2个回答
5
投票

是的,在使用“链接寄存器”传递返回地址的ISA上,非叶子函数必须保存/恢复其返回地址,这与他们保存要在内部使用的保留呼叫的寄存器的方式非常相似功能。即通常在调用堆栈上。

许多RISC ISA没有推送/弹出指令,但是可以使用多个指令完成相同的操作。例如从堆栈指针中减去以腾出空间,然后在函数入口保存一些寄存器,包括LR。然后在返回之前,重新加载寄存器并添加到堆栈指针以恢复调用者的SP值以及其他任何寄存器。

叶子函数(不进行任何函数调用)可以只保留该寄存器,因此当它们ret(或任何返回指令被调用,例如MIPS jr $ra-跳转寄存器)时,返回地址仍然存在到返回地址寄存器)。

例如,查看编译器输出:

void external();

void foo(int *p) {
    external();
    *p = 0;    // defeat tail-call optimization
}

由GCC 5.4-O2 -fno-delayed-branchon the Godbolt compiler explorer编译为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

通常,不是

函数必须返回的地址与之前所在的寄存器相同,这取决于ISA使用哪种类型的返回指令。它是典型的,但可能有助于某些微体系结构的分支预测。

32位ARM很有趣,并且具有微码的push / pop指令,这些指令采用寄存器的位域进行压入和弹出操作。因此,通常在函数输入时输入push {r4, lr},在返回指令中输入pop {r4, pc} as

。 (ARM将程序计数器作为16个体系结构通用寄存器之一。向其中写入是一个跳转。)将r4与链接寄存器lr一起按下可保持堆栈对齐,并为您保留调用注册玩。

5
投票

假设不需要递归,您可以发明一个约定,即链接(返回寄存器)存储在不同的寄存器中,具体取决于嵌套的级别。

© www.soinside.com 2019 - 2024. All rights reserved.