为什么没有包含EAX的更高字节的寄存器?

%AX = (%AH + %AL)

那么为什么没有%EAX = (%SOME_RESTIER + %AX)的一些注册%SOME_REGISTER

只是为了一些澄清。 在20世纪70年代早期的微处理器时代,CPU只有less量寄存器和非常有限的指令集。 通常,算术单元只能在单个CPU寄存器上操作,通常称为“累加器”。 8位8080和Z80处理器上的累加器被称为“A”。 还有6个通用的8位寄存器:B,C,D,E,H和L.这6个寄存器可以配对成3个16位寄存器:BC,DE和HL。 在内部,累加器与标志寄存器组合形成AF 16位寄存器。

当英特尔开发16位8086系列时,他们希望能够移植8080代码,因此他们保持了相同的基本寄存器结构:

 8080/Z80 8086 A AX BC BX DE CX HL DX IX SI IY DI 

由于需要端口8位代码,他们需要能够引用AX,BX,CX和DX的各个8位部分。 对于BL / BH,CL / CH和DL / DH,AX的低字节和高字节称为AL,AH。 Z80上的IX&IY只用作16位指针寄存器,所以不需要访问SI&DI的两半。

当80386在20世纪80年代中期发布时,他们创build了所有寄存器的“扩展”版本。 所以,AX成为了EAX,BX成了EBX等等。没有必要访问这些新扩展寄存器的前16位,所以他们没有创buildEAXH伪寄存器。

AMD在生产第一款64位处理器时也采用了相同的技巧。 AX寄存器的64位版本被称为RAX。 所以,现在你有这样的东西:

 |63..32|31..16|15-8|7-0| |AH.|AL.| |AX.....| |EAX............| |RAX...................| 

在过去的8位时间里,有一个A寄存器。

在16位的日子里,有16位的AX寄存器被分成两个8位的部分,AH和AL,当时你还想用8位的值。

在32位的日子里,引入了32位的EAX寄存器,但AX,AH和AL寄存器都被保存了。 devise人员并不觉得有必要引入一个新的16位寄存器来寻址EAX的16到31位。

这里有很多答案,但没有真正回答给定的问题:为什么没有寄存器直接编码EAX的高16位或RAX的高32位? 答案归结为x86指令编码本身的限制。

16位历史课程

当英特尔devise8086时,他们对许多指令使用了可变长度编码scheme。 这意味着某些非常常见的指令,如POP AX ,可以表示为单个字节(58),而像MOV CX, [BX*4+BP+1023]这样罕见的(但仍然有用的)指令仍然可以表示,即使需要几个字节来存储它们(在这个例子中,8B 8C FF 03)。

这似乎是一个合理的解决scheme,但是当他们devise它时, 他们填补了大部分的可用空间 。 例如,对于八个单独的寄存器(AX,CX,DX,BX,SP,BP,SI,DI),有八个POP指令,并且它们填写操作码58至5F,而操作码60则完全是PUSHA ),与操作码57( PUSH DI )一样。 在那之前或之后没有任何余地。 即使推送和popup分段寄存器 – 这在概念上与推送和popup通用寄存器几乎相同 – 必须被编码在不同的位置(在06 / 0E / 16 / 1E左右),这是因为旁边没有空间其余的推/stream行指令。

同样,用于像MOV CX, [BX*4+BP+1023]这样的复杂指令的“mod r / m”字节只有三位用于编码寄存器,这意味着它只能代表八个寄存器总数。 如果你只有八个寄存器,那很好,但是如果你想要更多的寄存器则会出现真正的问题。

(这里有一个在x86架构中的所有这些字节分配的优秀映射: http : //i.imgur.com/xfeWv.png注意主映射中没有剩余空间,一些指令重叠字节,甚至是现在,由于MMX和SSE指令,现在使用了许多次级“0F”地图。)

往32位和64位

所以为了让CPU的devise从16位扩展到32位,他们已经有一个devise问题,他们用前缀字节解决了这个问题:通过在所有的标准16位之前加上一个特殊的“66”字节指令,CPU知道你需要相同的指令,而不是32位版本(EAX),而不是16位版本(AX)。 devise的其余部分保持不变:在整个CPU架构中,总共只有8个通用寄存器。

为了将体系结构扩展到64位(RAX和朋友),必须做类似的琐事。 在那里,通过增加另一组意味着“64位”的前缀代码( REX ,40-4F)(并且有效地向“mod r / m”字段添加了另外两个位)解决了问题,并且还丢弃了怪异的没有人曾经使用旧的指令,并重新使用他们的字节代码更新的东西。

除了8位寄存器

要问的一个更大的问题就是,如果在八个寄存器的devise中只有真正的空间,那么像AH和AL这样的东西是如何工作的。 答案的第一部分是没有“ PUSH AL ”这样的东西 – 有些指令根本就不能在字节大小的寄存器上运行! 唯一可能是一些特殊的奇怪(如AADXLAT )和特殊版本的“mod r / m”指令:通过在“mod r / m”字节中翻转一个特定位,那些“扩展指令“可以被翻转来操作8位寄存器而不是16位寄存器。 正好碰巧也有八个8位寄存器:AL,CL,DL,BL,AH,CH,DH和BH(按此顺序),并且与八个寄存器插槽可用在“mod r / m”字节中。

英特尔当时指出,8086devise应该与8080/8085“源兼容”:在8086中,每个8080/8085指令都有等效的指令,但是它没有使用相同的字节代码(他们甚至没有closures),你必须重新编译(重新组装)你的程序,以使它使用新的字节代码。 但是“源代码兼容”是老软件的一个前进方向,它允许8085的个人A,B,C等等,以及组合“BC”和“DE”寄存器仍旧在新处理器上工作,即使他们现在称为“AL”和“BL”和“BX”和“DX”(或任何映射)。

所以这真的是一个真正的答案:并不是Intel或AMD有意“排除”EAX的高16位寄存器,或RAX的32位高寄存器:高8位寄存器是一个奇怪的剩余历史exception,并且在较高比特大小的情况下复制它们的devise将是非常困难的,因为要求该架构是向后兼容的。

性能考虑

另外还有一个考虑因素,为什么还没有增加这些“高位寄存器”呢?在现代处理器体系结构内部,由于性能的原因,可变大小的寄存器实际上并不重叠:AH和AL不是实际的, AX是AX的一部分,而AX不是EAX的一部分,而EAX不是RAX的一部分:它们都是独立的寄存器,当处理其中一个时,处理器会在其他寄存器上设置一个无效标志他们知道它需要复制数据,当你读其他人。

(例如:如果设置AL = 5,处理器不更新AX,但是如果您从AX读取,处理器会迅速将AL从AL复制到AX的底部位。

通过保持寄存器的不同,CPU可以执行各种聪明的事情,比如无形的寄存器重命名,以使代码运行得更快,但这意味着如果您使用旧的模式将较小的寄存器视为较大的块寄存器,因为处理器将不得不停止并更新它们。 为了防止所有内部簿记失控,CPUdevise者明智地select在新的处理器上添加单独的寄存器,而不是添加更多重叠的寄存器。

(是的,这意味着在现代处理器上显式地执行“ MOVZX EAX, value ”的速度要快于“ MOV AX, value / use EAX ”的老式,

结论

尽pipe如此,如果英特尔和AMD真的想要增加更多的“重叠”寄存器吗? 当然。 如果有足够的需求,就有办法把它们搞砸。 但考虑到重要的历史包袱,当前的体系结构限制,显着的性能限制以及目前大多数代码是由针对非重叠寄存器优化的编译器生成的事实,他们不太可能在不久的将来添加这样的事情。