据我所知,堆栈指针指向堆栈上的“空闲”内存,而“推送”堆栈上的数据写入堆栈指针所指向的位置,并递增/递减它。
但是,是否可以使用帧指针的偏移量来实现同样的目的,从而节省寄存器。向帧指针添加偏移量的开销与递增和递减堆栈指针的开销几乎相同。我看到的唯一优点是从“顶部”访问数据。(或底部)将较快,只要其不是推入或弹出操作,例如,仅阅读或写入到所述地址而不递增/递减。但话又说回来,此类操作将使用帧指针花费单个额外循环,并且将有一个附加寄存器用于通用用途。
看起来只有帧指针是真正需要的。而且它甚至比修改当前堆栈帧中的数据有更多的用途,比如在调试和堆栈展开中使用。我是否遗漏了什么?
2条答案
按热度按时间yb3bgrhw1#
是的,实际上对于64位代码生成器来说是很常见的。然而,有些复杂的情况并不能使它成为普遍的可能。一个硬性要求是在编译时知道堆栈指针的值,这样代码生成器就可以可靠地生成偏移量。在以下情况下,这是行不通的:
7rfyedvj2#
你的问题应该是:帧指针是否冗余?
在大多数情况下,可以在大多数CPU上只使用堆栈指针而不使用帧指针来编写代码(某些CPU,如16位模式下的x86,对访问堆栈指针有限制,因此需要帧指针)。
举个例子:
也可以写成:
然而,一些特殊情况--如alloca()函数--需要同时使用帧指针和堆栈指针。
然而,堆栈指针从不是冗余的:
您必须考虑到堆栈指针是由中断使用的。中断是操作系统函数,当满足某些条件时(例如,从USB端口接收到电信号),硬件会自动调用这些函数(而不是CALL指令)。
因为这样的中断假定堆栈指针下面的存储器是空闲的,所以使用堆栈指针下面的存储器将是一个非常坏的主意;如果发生中断,则堆栈指针下面的存储器将被破坏!
在MIPS CPU上(例如),哪一个寄存器是堆栈指针是纯粹的约定;你也可以说R9是堆栈指针,而堆栈不是位于地址R9,而是位于地址R9+1234。64位Sparc调用约定对堆栈指针使用了这样一种奇怪的约定。然而,这要求所有代码(包括操作系统和所有中断)都使用相同的约定。
在x86 CPU上,这是不可能的,因为CPU本身会假设堆栈指针下面的内存是空闲的:PUSH和CALL指令将写入堆栈指针下方的存储器,并且在中断的情况下,CPU本身将在堆栈指针指向的地址存储信息,而不可能改变这种行为!