如果查看诸如cmp
,test
,add
,sub
和and
之类的操作的文档,您会注意到涉及寄存器EAX
及其第16位和第8位变体的操作是第一个。操作数具有独特的操作码,与这些指令的“一般情况”版本不同。
是这种单独的操作码仅仅是节省代码空间的一种方式,还是比一般情况的操作码更高效,还是仅仅是出于兼容性原因而值得保留的过去的遗物?
最佳答案
这主要是过去的遗物,但也不是完全“过时”的。
在早期(即在Intel 8088上),x86寄存器集实际上像其他当代CISC处理器一样更加专业。 (8088的设计本身直接来自Intel 8080和Zilog Z80处理器。)也就是说,这8个寄存器并不像今天(功能上)一样都是通用的。有许多指令仅适用于硬编码寄存器。这意味着程序员经常发现自己在寄存器之间来回转换值,以便为下一条指令正确设置内容。
EAX是一个特别的寄存器。好吧,实际上,在那时候,它被称为AX,因为它只有16位,还没有扩展到32位。 AX是累加器,许多不同的指令将其用作硬编码的目标。 accumulator是用于存储中间结果的寄存器-它“累加”逻辑和算术运算的结果。几乎所有早期的微处理器都有一个累加器寄存器,其中许多迫使您以这种方式使用累加器。 x86架构在许多情况下更为灵活,但仍然受到该设计的启发。关于x86寄存器集背后逻辑的详细文章为here。
通用指令的这些特殊变体是该设计的结果。它们很短(只有1个字节),很快(主要是由于指令大小小,但是大概在早期的硅片级也有优化)与累加器寄存器中的值进行交互的方式。
因此,是的,这恰好是节省代码空间的一种方法,是的,它仍然比一般情况下的编码更有效,这恰恰是因为编码指令所需的字节数更少。当然,考虑到我们更大的指令高速缓存和更快的内存读取速度,今天小巧的代码已不像8088那样重要。但是,它仍然有所作为。任何优秀的x86汇编程序员都知道,只要有可能,他们都会更喜欢使用这些基于累加器的简短指令,许多编译器也是如此。这在内部循环中尤其重要,在内部循环中,减小代码大小对于确保所有内容都保留在缓存中至关重要。经常仔细地重新评估和重新安排寄存器使用量,甚至代码流,以尽可能精确地在累加器中保留尽可能多的内容(即使在今天),以便可以使用这些简短高效的操作码。