%AX = (%AH + %AL)
那么为什么不%EAX = (%SOME_REGISTER + %AX)
注册%SOME_REGISTER
呢?
只是为了澄清一下.在1970年代早期的微处理器时代,CPU只有少量的寄存器和非常有限的指令集.通常,算术单元只能在单个CPU寄存器上操作,通常称为"累加器".8位8080和Z80处理器上的累加器称为"A".还有6个其他通用8位寄存器:B,C,D,E,H和L.这六个寄存器可以配对形成3个16位寄存器:BC,DE和HL.在内部,累加器与Flags寄存器组合以形成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位部分.这些被称为AL,AH用于AX的低和高字节,依此类推,用于BL/BH,CL/CH和DL/DH.Z80上的IX和IY仅用作16位指针寄存器,因此无需访问SI和DI的两半.
当80386在20世纪80年代中期发布时,他们创建了所有寄存器的"扩展"版本.因此,AX成为EAX,BX成为EBX等.不需要访问这些新扩展寄存器的前16位,因此它们不会创建EAXH伪寄存器.
AMD在生产第一批64位处理器时应用了相同的技巧.AX寄存器的64位版本称为RAX.所以,现在你有了这样的东西:
|63..32|31..16|15-8|7-0| |AH.|AL.| |AX.....| |EAX............| |RAX...................|
这里发布了很多答案,但没有一个真正回答给定的问题:为什么没有一个寄存器直接编码高16位的EAX或高32位的RAX?答案归结为x86指令编码本身的局限性.
16位历史课程
当英特尔设计8086时,他们对许多指令使用了可变长度编码方案.这意味着某些非常常见的指令,例如POP AX
,可以表示为单个字节(58),而罕见(但仍然可能有用)的指令MOV CX, [BX*4+BP+1023]
仍然可以表示,即使它需要几个字节来存储它们(在这个例子中) ,8B 8C FF 03).
这似乎是一个合理的解决方案,但是当他们设计它时,他们填写了大部分可用空间.因此,例如,POP
八个独立寄存器(AX,CX,DX,BX,SP,BP,SI,DI)有八条指令,它们填写了操作码58到5F,而操作码60完全是其他东西(PUSHA
) ,如操作码57(PUSH DI
).在那之后或之前没有任何余地.甚至推送和弹出段寄存器 - 这在概念上几乎与推送和弹出通用寄存器完全相同 - 必须在不同的位置编码(低于06/0E/16/1E),因为旁边没有空间其余的推/弹指令.
同样,用于复杂指令的"mod r/m"字节MOV CX, [BX*4+BP+1023]
只有3位用于编码寄存器,这意味着它只能代表8个寄存器.如果你只有8个寄存器,这很好,但如果你想拥有更多寄存器,那就是一个真正的问题.
(这里有x86架构中所有这些字节分配的优秀地图:http: //i.imgur.com/xfeWv.png.注意主映射中没有剩余空间,一些指令重叠字节,甚至如何由于MMX和SSE指令,现在使用了大部分辅助"0F"映射.)
走向32位和64位
因此,为了使CPU设计从16位扩展到32位,它们已经存在设计问题,并且它们通过前缀字节解决了这个问题:通过在所有标准16位前面添加一个特殊的"66"字节说明,CPU知道你想要相同的指令,但32位版本(EAX)而不是16位版本(AX).设计的其余部分保持不变:整体CPU架构中仍然只有8个通用寄存器.
类似的hackery必须将架构扩展到64位(RAX和朋友); 在那里,问题通过添加另一组前缀代码(REX
,40-4F)来解决,这些前缀代码意味着"64位"(并且有效地将另外两位添加到"mod r/m"字段),并且还丢弃了奇怪的旧没有人使用过的指令,并将其字节代码重新用于更新的东西.
除了8位寄存器之外
那么,要问的一个更大的问题是,如果设计中只有8个寄存器的空间,那么像AH和AL这样的东西是如何起作用的.答案的第一部分是没有" PUSH AL
" - 有些指令根本无法对字节大小的寄存器进行操作!唯一可能是一些特殊的奇怪(像AAD
和XLAT
)和"mod r/m"指令的特殊版本:通过在"mod r/m"字节中翻转一个非常特定的位,可以翻转这些"扩展指令"以在8位寄存器上操作而不是16位的.恰好恰好有8个8位寄存器:AL,CL,DL,BL,AH,CH,DH和BH(按此顺序),并且与8个可用的寄存器插槽非常吻合在"mod r/m"字节中.
英特尔当时指出,8086设计应该与8080/8085"源兼容":8086中每条8080/8085指令都有一条等效指令,但它没有使用相同的字节代码(它们甚至不是很接近),你必须重新编译(重新组装)你的程序才能使它使用新的字节代码.但"源兼容"是旧软件的一种前进方式,它允许8085的个人A,B,C等以及组合"BC"和"DE"寄存器仍在新处理器上运行,即使它们现在也是如此称为"AL"和"BL","BX"和"DX"(或任何映射).
所以这才是真正的答案:并非英特尔或AMD故意"遗漏"高速16位寄存器用于EAX,或高速32位寄存器用于RAX:这就是高8位寄存器是一个奇怪的剩余历史异常,并且考虑到体系结构是向后兼容的要求,在更高的比特尺寸下复制它们的设计将是非常困难的.
绩效考虑
还有另外一个考虑因为,为什么那些"高寄存器"还没有被添加,因为:在现代处理器架构中,出于性能原因,可变大小的寄存器实际上并不真正重叠:AH和AL不是'在AX的一部分,而AX不是EAX的一部分,而EAX不是RAX的一部分:它们都是引擎盖下的独立寄存器,当你操作其中一个时,处理器会在其他寄存器上设置一个无效标志.它们使得它知道当你从其他人那里读取时需要复制数据.
(例如:如果设置AL = 5,则处理器不会更新AX.但是如果您从AX读取,处理器会快速将AL中的5个复制到AX的底部位.)
通过保持寄存器分离,CPU可以执行各种聪明的操作,例如不可见的寄存器重命名,以使代码运行得更快,但这意味着如果使用旧的模式将小寄存器视为较大的寄存器,则代码运行较慢寄存器,因为处理器将不得不停顿并更新它们.为了防止所有内部簿记失控,CPU设计人员明智地选择在较新的处理器上添加单独的寄存器,而不是添加更多重叠的寄存器.
(是的,这意味着它在现代处理器上明确地"更快MOVZX EAX, value
",而不是用旧的,更加笨拙的方式" MOV AX, value / use EAX
".)
结论
尽管如此,英特尔和AMD如果真的想要增加更多"重叠"寄存器吗?当然.如果有足够的需求,有办法让他们陷入困境.但鉴于重要的历史包袱,当前的架构限制,显着的性能限制以及目前大多数代码都是由针对非重叠寄存器优化的编译器生成的事实,它们很可能很快就会添加这些内容.
在旧的8位日,有一个A寄存器.
在16位时间内,有16位AX寄存器,它被分成两个8位部分AH和AL,用于那些你还想使用8位值的时候.
在32位时间内,引入了32位EAX寄存器,但是AX,AH和AL寄存器都被保留了.设计人员认为没有必要引入一个新的16位寄存器来寻址EAX的16到31位.