问题
As far as I understand it, the stack pointer points to the "free" memory on the stack, and "pushing" data on the stack writes to the location pointed by the stack pointer and increments/decrements it.
But isn't it possible to use offsets from the frame pointer to achieve the same thing, thus saving a register. The overhead from adding offsets to the frame pointer is pretty much the same as the overhead of incrementing and decrementing the stack pointer. The only advantage I see is accessing data from the "top" (or bottom) will be faster, as long as it is not a push or pop operation, e.g. just reading or writing to that address without incrementing/decrementing. But then again, such operations would take a single extra cycle using the frame pointer, and there will be one additional register for general purpose use.
It seems like only the frame pointer is really needed. And it even serves a lot more purpose than just modifying data in the current stack frame, such as to be used in debugging and for stack unwinding. Am I missing something?
回答1:
Well, yes, and in fact common for 64-bit code generators. There are complications however that do not make it universally possible. A hard requirement is that the value of the stack pointer is known at compile time so the code generator can generate the offset reliably. This does not work when:
the language runtime provides non-trivial alignment guarantees. Particularly a problem in 32-bit code when the stack frame contains 8-byte variables, like double. Accessing a mis-aligned variable is very expensive (x2 if misaligned by 4, x3 if it straddles an L1 cache-line) and might invalidate a memory model guarantee. The code generator cannot normally assume that the function is entered with an aligned stack so needs to generate code in the function prologue, this can cause the stack pointer to decrement by an extra 4 bytes.
the language runtime provides a way for a program to dynamically allocate stack space. Very common and desirable, it is very cheap and fast memory. Examples are alloca() in the CRT, variable length arrays in C99+, the stackalloc keyword in the C# language.
the language runtime needs to provide a reliable way to walk the stack. Common in exception handling, implementation of a sandbox that need to be able to discover the caller's rights, garbage collected languages that need to be able to discover pointers to objects. Many possible ways to do this of course, but using the base pointer and storing the caller's base pointer in a known location in the stack frame makes it simple.
回答2:
Your question should be: Is the frame pointer redundant?
In most cases it is possible to write code only using the stack pointer and not using the frame pointer on most CPUs (some CPUs, like x86 in 16-bit mode, have restrictions on accessing the stack pointer so the frame pointer is required).
One example:
mov ebp, esp
push esi
mov eax, [ebp+4]
push edi
mov eax, [ebp+8]
could also be written as:
push esi
mov eax, [esp+8]
push edi
mov eax, [esp+16]
Some special cases - like the alloca() function - however require using both frame and stack pointers.
The stack pointer however is never redundant:
You must consider that the stack pointer is used by interrupts. Interrupts are operating system functions that are automatically called by the hardware (instead of a CALL instruction) when certain conditions are met (e.g. an electrical signal from the USB port has been received).
Because such interrupts assume that the memory below the stack pointer is free it would be a very bad idea to use memory below the stack pointer; if an interrupt occurs than the memory below the stack pointer will be destroyed!
On MIPS CPUs (for example) it is pure convention which of the registers is the stack pointer; you may also say that R9 is the stack pointer and the stack is not located at address R9 but at address R9+1234. The 64-bit Sparc calling convention uses such a strange convention for the stack pointer. This however requires that all code (including the operating system and all interrupts) uses the same convention.
On x86 CPUs this is not possible because the CPU itself will assume that the memory below the stack pointer is free: PUSH and CALL instructions will write to the memory below the stack pointer and in the case of an interrupt the CPU itself will store information at the address the stack pointer points to without the possibility to change this behaviour!
来源:https://stackoverflow.com/questions/26278160/doesnt-the-frame-pointer-make-the-stack-pointer-redundant