My question is simple, Why do x86 architecture use two stack registers (esp ; ebp
)?
The length of stack frame has been determined during compile time, then I think we can just use one register (for example esp
) to access the stack, and maintain the base address which used to be in the ebp
register on the stack (or in other memory regions, but that would incur more performance penalty)
Is it possible?
This all depends on the calling convention of course but it generally goes like this.
The stack pointer is able to be moved arbitrarily based on whatever you need to push onto, or pop off from, the stack at any given point in time. This can happen anytime within a function, as you need to temporarily save some data on the stack.
The base pointer is generally set to the same value for any given stack depth, and is used to access passed parameters (on one side) and local variables (on the other side). It's also used to quickly move the stack pointer upon exiting a function.
The reason it's done this way is to simplify the code so that you're not having to reference stack content based on a possibly-changing stack pointer. Using the base pointer considerably eases the task of code generation (you don't have to know what the stack pointer is at any given time, just use the base pointer, which remains the same for the duration of the function).
Without that, code that wanted to push two copies of a local variable to call the next function, would look like this:
mov eax, [esp+16] ; get var1
push eax ; push it
mov eax, [esp+20] ; get var1 again
push eax
call _somethingElse
Put aside the fact that you wouldn't reload eax
in this case, the point I'm trying to make is that the relative location of items from a moving stack pointer can needlessly complicate matters.
For example, here's a function coded in assembly that follows a common calling convention:
_doSomething:
push ebp ; stack current base pointer
mov ebp, esp ; save previous stack pointer
sub esp, 48 ; incl 48 bytes local storage
; do whatever you want here, including changing
; esp, as long as it ends where it started.
mov esp, ebp ; restore previous stack pointer
pop ebp ; and previous base pointer
ret ; then return
_callIt:
mov eax, 7
push eax ; push parameter for function
call _doSomething
add esp, 4 ; get rid of pushed value
:
If you follow the code, you can see that ebp
inside the function body is a fixed reference point with [ebp]
(contents of ebp
) being the return address, [ebp+4]
being the pushed value of 7
, and [ebp-N]
being the local storage for _doSomething
, where N varies from 1
to 48
.
This is the case regardless of how many items are pushed or popped within the body of the function.
Why does x86 architecture use two stack registers (esp ; ebp)?
According to @gsg's answer to related question "Do any languages / compilers utilize the x86 ENTER instruction with a nonzero nesting level?" the x86
architecture was designed 30 years ago as "Pascal machine
".
See The 8086/8088 Primer, by Stephen P. Morse, Chapter 8: High-Level-Language Programming (Pascal) for rationale from one of the chip designers.
As such it incorporates hardware support for nested and recursive subroutines (procedures, functions) as in the Pascal programming language this was an important aspect of the structured programming paradigm supposed to produce code that was easier to read/write/maintain.
The hardware support in the form of special CPU instructions enabled to generate code using less instructions. Less instructions usually also meant faster code.
Is it possible to implement the stack frame variable access on another Turing complete machine without the use of ebp register?
Yes, but with different execution time
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With