Why do we have commands like push and pop?
From what I understand pop and push are basically the same as doing a (mov then add) and (sub then mov) on esp respectively.
For example wouldn’t:
pushl %eax
be equivalent to:
subl $4, %esp
movl %eax, (%esp-4)
please correct me if stack access is not (%esp-4), I’m still learning assembly
The only true benefit I can see is if doing both operation simultaneously offers some advantage; however I don’t see how it could.
But then, there’s no reason for a
CALLinstruction, either. After all, you can simulate a call with:And there’s no need for a
RETinstruction, either, because you can simulate it with:If you look hard enough, you’ll find lots of instructions that can be simulated by combining other instructions. What’s the point?
The answer to these types of questions is rooted in the long history of the x86 processor family, and in processors that came before it. The designers studied how programmers use processors and created an instruction set that was efficient in terms of execution speed and memory use.
In the late ’70s, 64 kilobytes was a lot of RAM, and RAM was much slower. Every instruction byte was precious, and there was a huge amount of overhead just fetching an instruction from memory. It was not uncommon for instruction fetch to take longer than execution. So there was a huge performance gain to be had by encoding things in as few instruction bytes as possible.
RAM is still incredibly slow when compared to CPU clock speeds, so there’s still a gain to be had by encoding in as few instruction bytes as possible. It’s true that the large CPU caches we have help a great deal, as does branch prediction and prefetch logic, but every byte transferred from RAM to the CPU cache is still expensive. It pays to be frugal with instruction encodings.
About calling procedures:
The standard way of calling procedures in assembly language is to push the parameters and then
callthe procedure. For example, this passes two dword values:The
retinstruction pops the return address into the instruction pointer.Somebody has to clean up the stack, of course. The caller can clean up the stack by incrementing the stack pointer. Or the called procedure can clean up the stack by using
ret 8, which will pop the return address and increment the stack pointer.See http://www.delorie.com/djgpp/doc/ug/asm/calling.html for more info on calling conventions.