推送和弹出操作数在CPU上的必要性

Haw*_*ken 5 cpu assembly stack cpu-architecture att

为什么我们有像push和pop这样的命令?

从我的理解poppush基本上分别做(mov然后add)和(sub然后mov)esp.


例如,不会:

pushl %eax
Run Code Online (Sandbox Code Playgroud)

相当于:

subl $4, %esp
movl %eax, (%esp-4)
Run Code Online (Sandbox Code Playgroud)

请纠正我,如果没有堆栈访问(%esp-4),我还在学习汇编


我能看到的唯一真正的好处是,如果同时进行两种操作都会带来一些好处; 但我不知道它怎么可能.

Jim*_*hel 8

但是,也没有理由进行CALL指导.毕竟,您可以使用以下方式模拟通话:

sub esp,4
mov [esp-4], offset return_address
jmp myproc
Run Code Online (Sandbox Code Playgroud)

而且也不需要RET指令,因为你可以用以下方法模拟它:

mov eax,[esp]
add esp,4
jmp [eax]
Run Code Online (Sandbox Code Playgroud)

如果你看起来足够努力,你会发现许多可以通过组合其他指令来模拟的指令.重点是什么?

这些问题的答案源于x86处理器系列的悠久历史,以及之前的处理器.设计人员研究了程序员如何使用处理器并创建了一个在执行速度和内存使用方面高效的指令集.

在70年代后期,64千字节的内存很多,RAM也慢得多.每个指令字节都很珍贵,并且从内存中获取指令会产生大量开销.指令获取比执行花费更长时间并不罕见.因此,通过尽可能少的指令字节对事物进行编码,可以获得巨大的性能提升.

与CPU时钟速度相比,RAM仍然非常慢,因此通过尽可能少的指令字节进行编码仍然可以获得增益.确实,大型CPU缓存我们有很多帮助,分支预测和预取逻辑也是如此,但从RAM传输到CPU缓存的每个字节仍然很昂贵.用指令编码节俭是值得的.

关于调用程序:

在汇编语言中调用过程的标准方法是推送参数然后推送call过程.例如,这会传递两个双字值:

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

proc:
  ; at this point, [esp] contains the return address
Run Code Online (Sandbox Code Playgroud)

ret指令将返回地址弹出到指令指针中.

当然,有人必须清理堆栈.调用者可以通过递增堆栈指针来清理堆栈.或者被调用的过程可以通过使用来清理堆栈ret 8,这将弹出返回地址并递增堆栈指针.

有关调用约定的详细信息,请参见http://www.delorie.com/djgpp/doc/ug/asm/calling.html.