2017-10-17 134 views
3

我们正在研究MIPS汇编(我想这个问题可以适用于装配虽然在一般),老师向我们介绍了帧指针帧指针的优点是什么?

如果我有一个函数序言,我用来直接做堆栈指针

addiu $sp, $sp, -8 ; alloc 2 words in the stack 
sw $s0, 4($sp)  ; save caller function $s0 value in the stack 
sw $ra, ($sp)  ; save the return address for the callee function 

而在函数尾声:

move $v0, $0   ; set 0 as return value 
lw $s0, 4($sp)  ; pick up caller $s0 value from the stack 
lw $ra, ($sp)  ; pick up return address to return to the caller 
addiu $sp, $sp, 8 ; dealloc the stack words I used 
jr $ra    ; return back to caller 

老师说,使用帧指针当我们在装配中编写功能时对我们人类有用:

addiu $sp, $sp, -12 ; alloc 3 words in the stack 
sw $fp, 8($sp)  ; save caller frame pointer in the stack 
addiu $fp, $sp, 8 ; set $fp to the uppermost address of the activation frame 
sw $ra, -4($fp)  ; saving like the first example, but relative 
sw $s0, -8($fp)  ; to the frame pointer 

老师还说,有时候堆栈指针会继续分配其他空间,因为我们需要注意,所以在函数内部引用激活帧是很困难的。 使用帧指针我们将有一个指向激活帧的静态指针。

是的,但我会永远需要使用函数内的激活,因为它只包含调用者函数保存的数据?

我认为它只是使事情更难实现。有没有一个真实的例子,帧指针对程序员来说是一个很大的优势?

+0

对于许多指令集,你不需要使用帧指针,最多它使编译器作者更容易调试,这不是一个真正有效的借口,好像浪费了一个寄存器给我。 gcc等可能允许你选择不使用帧指针,不建立使用它的代码来节省开销。一些默认值,一些默认不带。它使该函数内的偏移量固定,对于该函数,变量X始终为fp-N。您可以为整个功能制作X sp + M,或者可以根据需要移动SP以保留空间。 –

+0

不管老师说什么,结束的时候,对于那个班级来说,通过班级,怀疑一切,然后(重新)发现它,看看你是否相信。是否只是让老师对作业进行评分更容易一些,还是这种编程规则在某种意义上被教导得“更好”?我同意它使人类更容易,但同时编译器或人类,如果你移动sp来覆盖函数的所有用法,没有帧指针也同样容易。 –

+1

试试这个:在你的脑海中定义一个C结构,包含5个或更多字段。在堆栈中为它分配空间。初始化它。将其成员作为堆栈中的参数传递给其他函数。做和没有框架指针。 –

回答

2

你只绝对需要帧指针时动态地分配不同数量的堆栈空间。 C中使用可变长度数组和/或alloca的函数是需要帧指针的函数的示例。由于函数使用的数量堆栈是变量,因此您无法使用堆栈指针中的常量偏移量访问变量,并且您需要一种方法在函数返回时撤消变量长度分配。使用帧指针解决了这两个问题。您可以使用它来使用常量偏移量来处理堆栈变量,并将堆栈指针恢复到函数开始时的值。

在MIPS,它也将使意义仅使用固定大小的堆栈分配,如果总的堆栈分配多于32K使用一个帧指针作为优化中的功能。 MIPS支持的有限寻址模式只允许相对于堆栈指针或任何其他寄存器的16位符号扩展偏移量。由于堆栈指针指向堆栈的底部,所以堆栈指针只能使用非负偏移量,因此堆栈中只有32k可以通过一条指令访问。通过使用帧指针并将它指向堆栈帧的中间(而不是帧的顶部),它可以用于在单个指令中寻址多达64k的堆栈。

以其它方式使用帧指针的东西,不仅有利于程序员,而不是程序。如果程序中的所有函数都使用带有帧指针的标准堆栈帧,则帧指针和存储在堆栈中的所有保存的帧指针值形成一个堆栈帧的链表。这个链接列表可以很容易地被横穿,以在调试时创建函数调用的回溯。但是,使用合适的现代调试器,也可以将元数据(展开信息)嵌入到可执行文件中,即使未使用帧指针,调试器也可以使用这些可执行文件来遍历堆栈帧。这是现代编译器可以自动完成的工作,但是在汇编语言中,包含所有必要的额外指令以使其可行是相当痛苦的。

如果堆栈指针在函数期间可以通过固定大小的分配和释放多次更改,那么跟踪程序中任何给定点的变量相对于堆栈指针的位置可能会很痛苦。虽然在任何给定的位置它总是处于固定的偏移量,但会根据位置而改变。确定偏移量可能会非常棘手且容易出错。由于帧指针值不会改变,所以使用帧指针将为每个变量赋予相对于从未改变的帧指针的偏移量。

请注意,如果您觉得您需要使用帧指针,因为最后两个原因之一您必须考虑为什么首先在程序集中进行编程。这些现代编译器不需要使用帧指针的情况下,所以会生成更好的代码。

1

帧指针省略是C和C++编译器实现的standard optimization option。好处是它可以腾出一个寄存器用于其他用途。然而,它经常被禁用,这使得调试程序崩溃过度困难。即使像生成堆栈跟踪这样的基本东西也非常困难,您不再知道父函数的框架位于何处,因此不知道在哪里查找返回地址。检查局部变量的状态同样会变得痛苦。这不仅在调试应用程序时很重要,而且当程序在生产中崩溃时它会回来。

调试元数据需要知道在哪里看,你必须知道帧的大小。一般来说,使代码可调试和可诊断的是更多比使其易于编写更为重要。典型的程序员花更多时间在调试和测试上,而不是写作。