Pointers 不';帧指针是否使堆栈指针冗余?

Pointers 不';帧指针是否使堆栈指针冗余?,pointers,assembly,stack,frame,Pointers,Assembly,Stack,Frame,据我所知,堆栈指针指向堆栈上的“空闲”内存,“推送”堆栈上的数据写入堆栈指针指向的位置,并递增/递减 但是,是否可以使用帧指针的偏移量来实现相同的目标,从而保存一个寄存器。向帧指针添加偏移的开销与递增和递减堆栈指针的开销几乎相同。我看到的唯一优势是从“顶部”(或底部)访问数据会更快,只要不是推送或弹出操作,例如,只读取或写入该地址而不增加/减少。但是,同样地,这样的操作将使用帧指针花费一个额外的周期,并且将有一个额外的寄存器用于一般用途 似乎只需要帧指针。它甚至比修改当前堆栈帧中的数据更有用,例

据我所知,堆栈指针指向堆栈上的“空闲”内存,“推送”堆栈上的数据写入堆栈指针指向的位置,并递增/递减

但是,是否可以使用帧指针的偏移量来实现相同的目标,从而保存一个寄存器。向帧指针添加偏移的开销与递增和递减堆栈指针的开销几乎相同。我看到的唯一优势是从“顶部”(或底部)访问数据会更快,只要不是推送或弹出操作,例如,只读取或写入该地址而不增加/减少。但是,同样地,这样的操作将使用帧指针花费一个额外的周期,并且将有一个额外的寄存器用于一般用途


似乎只需要帧指针。它甚至比修改当前堆栈帧中的数据更有用,例如用于调试和堆栈展开。我遗漏了什么吗?

您的问题应该是:帧指针是多余的吗

在大多数情况下,可以在大多数CPU上仅使用堆栈指针而不使用帧指针来编写代码(某些CPU,如16位模式下的x86,对访问堆栈指针有限制,因此需要帧指针)

一个例子:

mov ebp, esp
push esi
mov eax, [ebp+4]
push edi
mov eax, [ebp+8]
也可以写成:

push esi
mov eax, [esp+8]
push edi
mov eax, [esp+16]
但是,有些特殊情况(如alloca()函数)需要同时使用帧指针和堆栈指针

但是,堆栈指针从不冗余:

你必须考虑栈指针被中断使用。中断是指当满足某些条件(例如,已接收到来自USB端口的电信号)时,硬件自动调用的操作系统功能(而不是调用指令)

因为这样的中断假设堆栈指针下面的内存是空闲的,所以使用堆栈指针下面的内存是一个非常糟糕的主意;如果发生中断,堆栈指针下面的内存将被破坏

例如,在MIPS CPU上,哪一个寄存器是堆栈指针是纯粹的约定;您也可以说R9是堆栈指针,堆栈不位于地址R9,而是位于地址R9+1234。64位Sparc调用约定对堆栈指针使用了这样一种奇怪的约定。但是,这要求所有代码(包括操作系统和所有中断)使用相同的约定


在x86 CPU上,这是不可能的,因为CPU本身将假定堆栈指针下的内存是空闲的:推送和调用指令将写入堆栈指针下的内存,在中断的情况下,CPU本身将在堆栈指针指向的地址处存储信息,而不可能更改此地址行为

您的问题应该是:帧指针是否冗余

在大多数情况下,可以在大多数CPU上仅使用堆栈指针而不使用帧指针来编写代码(某些CPU,如16位模式下的x86,对访问堆栈指针有限制,因此需要帧指针)

一个例子:

mov ebp, esp
push esi
mov eax, [ebp+4]
push edi
mov eax, [ebp+8]
也可以写成:

push esi
mov eax, [esp+8]
push edi
mov eax, [esp+16]
但是,有些特殊情况(如alloca()函数)需要同时使用帧指针和堆栈指针

但是,堆栈指针从不冗余:

你必须考虑栈指针被中断使用。中断是指当满足某些条件(例如,已接收到来自USB端口的电信号)时,硬件自动调用的操作系统功能(而不是调用指令)

因为这样的中断假设堆栈指针下面的内存是空闲的,所以使用堆栈指针下面的内存是一个非常糟糕的主意;如果发生中断,堆栈指针下面的内存将被破坏

例如,在MIPS CPU上,哪一个寄存器是堆栈指针是纯粹的约定;您也可以说R9是堆栈指针,堆栈不位于地址R9,而是位于地址R9+1234。64位Sparc调用约定对堆栈指针使用了这样一种奇怪的约定。但是,这要求所有代码(包括操作系统和所有中断)使用相同的约定


在x86 CPU上,这是不可能的,因为CPU本身将假定堆栈指针下的内存是空闲的:推送和调用指令将写入堆栈指针下的内存,在中断的情况下,CPU本身将在堆栈指针指向的地址处存储信息,而不可能更改此地址行为

是的,事实上,64位代码生成器很常见。然而,也有一些复杂的情况并不能使其成为普遍可能。硬要求是堆栈指针的值在编译时已知,这样代码生成器就可以可靠地生成偏移量。在以下情况下,此选项不起作用:

  • 语言运行库提供了非平凡的对齐保证。特别是当堆栈帧包含8字节变量(如double)时,32位代码中存在一个问题。访问错误对齐的变量非常昂贵(如果未对齐4,则为x2;如果跨一级缓存线,则为x3),并且可能会使内存模型保证失效。代码生成器通常无法假定函数是使用对齐堆栈输入的,因此需要在函数序言中生成代码,这可能会导致堆栈指针额外减少4个字节

  • 语言运行时为程序提供了一种动态分配堆栈空间的方法。非常常见和可取,它是非常便宜和快速的内存。例如CRT中的alloca()、C99+中的可变长度数组、C#语言中的stackalloc关键字

  • 语言运行库需要提供可靠的行走方式