为什么我们有 push 和 pop 之类的命令?
据我了解pop
,push
基本上与分别执行 ( mov
then add
) 和 ( sub
then mov
)esp
相同。
例如不会:
pushl %eax
相当于:
subl $4, %esp
movl %eax, (%esp-4)
如果堆栈访问不是(%esp-4)
,请纠正我,我还在学习组装
我能看到的唯一真正的好处是,如果同时进行这两项操作会提供一些优势;但是我不明白它怎么可能。
为什么我们有 push 和 pop 之类的命令?
据我了解pop
,push
基本上与分别执行 ( mov
then add
) 和 ( sub
then mov
)esp
相同。
例如不会:
pushl %eax
相当于:
subl $4, %esp
movl %eax, (%esp-4)
如果堆栈访问不是(%esp-4)
,请纠正我,我还在学习组装
我能看到的唯一真正的好处是,如果同时进行这两项操作会提供一些优势;但是我不明白它怎么可能。
但是,也没有理由发出CALL
指令。毕竟,您可以通过以下方式模拟通话:
sub esp,4
mov [esp-4], offset return_address
jmp myproc
而且也不需要RET
指令,因为您可以通过以下方式模拟它:
mov eax,[esp]
add esp,4
jmp [eax]
如果你仔细看,你会发现很多指令可以通过组合其他指令来模拟。重点是什么?
这类问题的答案植根于 x86 处理器系列的悠久历史以及之前的处理器。设计人员研究了程序员如何使用处理器,并创建了一个在执行速度和内存使用方面高效的指令集。
在 70 年代后期,64 KB 是很多 RAM,而 RAM 则要慢得多。每个指令字节都是宝贵的,仅仅从内存中获取一条指令就有大量的开销。取指令花费的时间比执行时间长的情况并不少见。因此,通过尽可能少的指令字节对事物进行编码,可以获得巨大的性能提升。
与 CPU 时钟速度相比,RAM 仍然非常慢,因此通过尽可能少的指令字节编码仍然可以获得收益。确实,我们拥有的大型 CPU 缓存非常有用,分支预测和预取逻辑也有很大帮助,但是从 RAM 传输到 CPU 缓存的每个字节仍然很昂贵。节省指令编码是值得的。
关于调用程序:
汇编语言调用过程的标准方法是先传入参数,然后再call
传入过程。例如,这会传递两个 dword 值:
push eax
push ebx
call proc ; pushes the return address and jumps to proc
...
proc:
; at this point, [esp] contains the return address
该ret
指令将返回地址弹出到指令指针中。
当然,必须有人清理堆栈。调用者可以通过递增堆栈指针来清理堆栈。或者被调用的过程可以使用 清理堆栈ret 8
,这将弹出返回地址并增加堆栈指针。
有关调用约定的更多信息,请参阅http://www.delorie.com/djgpp/doc/ug/asm/calling.html。
好。如果您有两条指令,那么它们可能会占用更多空间。这需要cpu传输更多数据。这需要更多时间。两条单独的指令通常也需要更多的 CPU 资源。即使 cpu 将它们并行化,提取器、解码器还有更多工作......如果您尝试删除所有但绝对必要的指令,您最终会得到图灵机。终极风险,但在实践中不是很有效。
1 个操作码 > 2 个操作码,至少在您尝试降低 CPU 使用率时。