%rbp可以转换为呼叫者保存吗?



我是一名装配领域的新生。我想知道为什么%rbp是用来救病人的。
因为我认为帧指针描述了当前帧的开始,也就是调用者,所以保存这个值的任务应该属于调用者,为什么要把进程延迟给调用者呢?
这里面有什么我不明白的地方吗?或者这只是一种惯例?如果我说错了,请纠正我。

是的,自定义调用约定是可能的(只要您不想调用或被编译器生成的代码调用),但通常这种更改会变得更糟。


帧指针的主要作用是在函数的生命周期内设置一次。如果你必须在每次函数调用时保存/恢复它,那就太糟糕了。

另一点是,您将它保存在堆栈帧中的标准位置,因此回溯可以遵循遍历堆栈帧的保存的帧指针值的链表。如果它没有被保存到堆栈帧内的标准位置(相对于返回地址),这是不可能发生的。


你只需选择一个不同的寄存器作为你的帧指针(在你发明的任何调用约定中都是调用保留的,比如RBP在标准约定中)而不是做mov %rbp, %r13/call foo/mov %r13, %rbp或每个函数调用周围的东西。(或者把它留在R13。)你不会想要push %rbp/pop %rbp围绕每个调用,因为这甚至更低效率,并使它更麻烦,以获得堆栈对齐正确。

这很好,RBP没有什么特别的,它只是意味着如果你使用RBP以外的reg作为帧指针,你不能用leave保存代码大小。

实际上,RBP(和R13)在效率POV中有些特殊:以RBP为基础的模式,如(%rbp),只能用disp8disp32编码,而不是没有位移。(这种编码实际上意味着"无基")。因此,RBP显然是一个很好的帧指针的选择,不太适合其他用途,你可能持有一个指针,你想要执行没有偏移。

参见为什么rbp和rsp被称为通用寄存器?更多信息。

R13也有同样的缺点,但不是一个很好的选择,因为它意味着每次访问堆栈总是需要一个REX前缀,就像4字节的mov -4(%r13), %eax和3字节的RBP。

所以,是的,你可以这样做,但是有很多理由不这样做。


大多数函数都不需要帧指针,但gcc -O1和更高版本意味着-fomit-frame-pointer,所以它只在做alloca或C99变长数组之类的函数中使用帧指针。(或者过度对齐堆栈,例如32或64字节)

在这种情况下(-fomit-frame-pointer),在少数使用寄存器的函数中使用其他寄存器作为帧指针会减少代码大小的浪费。或者,如果你使用R13,让RBP在一个几乎从不引用自己的堆栈帧的函数中自由运行,只需要为几个指令对齐,这是一个实际的改进。

最新更新