Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Neccessity of push and pop operands on CPUs

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.

like image 399
Hawken Avatar asked Apr 14 '12 17:04

Hawken


3 Answers

But then, there's no reason for a CALL instruction, either. After all, you can simulate a call with:

sub esp,4
mov [esp-4], offset return_address
jmp myproc

And there's no need for a RET instruction, either, because you can simulate it with:

mov eax,[esp]
add esp,4
jmp [eax]

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 call the procedure. For example, this passes two dword values:

push eax
push ebx
call proc    ; pushes the return address and jumps to proc
...

proc:
  ; at this point, [esp] contains the return address

The ret instruction 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.

like image 181
Jim Mischel Avatar answered Nov 28 '22 03:11

Jim Mischel


Well. If you have two instructions, then they probably use more space. Which requires cpu to transfer more data. And that takes more time. Two separate instructions also typically require more cpu resources. Even if cpu parallelizes them, there is more work for fetcher, decoder... If you try to remove all but absolutely neccessary instructions you end up with turing machine. Ultimate risc, but not very effective in practice.

like image 29
dbrank0 Avatar answered Nov 28 '22 03:11

dbrank0


1 opcode > 2 opcodes, at least when you are trying to reduce CPU usage.

like image 31
tomByrer Avatar answered Nov 28 '22 02:11

tomByrer