CPU 硬件无法通过 name 找到寄存器,这取决于汇编器将诸如 rax 之类的名称转换为 3 位或 4 位寄存器数字在机器代码中。(并且寄存器名称隐含的操作数大小也通过操作码和(缺少)前缀进行编码)。
例如add ecx, edx 集合到
01 d1。 Opcode 01 is add r/m32, r。第 2 个字节,ModRM 0xd1 = 0b0b11010001,对操作数进行编码:高 2 位 (11) 是寻址模式,普通寄存器,而不是内存(在这种情况下是 dest,因为它是 01 add r/m32, r 而不是 03 add r32, r/m32)。
中间 3 位是 /r 字段,010 = 2 是 edx 的寄存器号。
低3位为r/m字段,001为ECX的寄存器号。
(编号为 EAX、ECX、EDX、EBX ......,可能是因为 8086 是 designed for asm source compatibility with 8080 - 即在每条指令的基础上“移植”足够简单,机器可以自动完成。)
这是 CPU 实际解码的内容,以及它用来“寻址”其内部寄存器的内容。没有register renaming 的简单有序CPU 可以直接将这些数字用作实现寄存器文件的SRAM 中的地址。 (特别是如果它是像 MIPS 或 ARM 这样的 RISC。x86 很复杂,因为您可以使用具有不同宽度的相同寄存器编号,并且您可以将 AH 和 AL 之类的部分寄存器映射到 AX 的一半上。但这仍然只是一个问题将寄存器编号映射到 SRAM 中的位置,如果您没有进行寄存器重命名。)
对于 x86-64,寄存器编号始终为 4 位,但有时前导零是隐含的,例如在没有 REX 前缀的指令中,例如 mov eax, 60。寄存器编号位于该特殊编码的操作码的低 3 位中。
在物理上,现代 CPU 使用物理寄存器文件和寄存器重命名表 (RAT) 来实现架构寄存器。因此他们可以在多个时间点跟踪 RAX 的价值。例如mov eax, 60 / push rax / mov eax, 12345 / push rax 可以同时运行两个 mov 指令,写入单独的物理寄存器。但仍然要整理出push 应该从哪一个读取。
如果是这样,我想知道为什么 x86_64 架构中只有 16 个寄存器...
为 x86 竞争的高性能用例而设计的新 ISA将很可能有 32 个整数寄存器。但是将其硬塞到 x86 机器代码中(就像 AVX-512 对矢量 reg 所做的那样),不值得代码大小的成本。
x86-64 是从 1979 年设计的 16 位 8086 演变而来的。如果现在以现代晶体管预算重新开始,当时做出的许多设计选择都不是您会做出的选择。 (而不是针对 8 位 8080 的 asm 源代码级兼容性)。
更多的体系结构寄存器在每个操作数的机器代码中花费更多位。更多的物理寄存器只是意味着更多的乱序执行能力来处理更多的寄存器重命名。 (物理寄存器编号是内部细节。)This article 测量实际的无序窗口大小以隐藏缓存未命中延迟,并将其与已知的 ROB 和 PRF 大小进行比较——在某些情况下,CPU 会用完物理寄存器来重命名到,在它填充 ROB 之前,针对所选的填充指令组合。
,更多的寄存器不是意味着更高的性能吗?
更多的架构寄存器通常确实有助于提高性能,但收益会递减。 16 与 8 相比避免了大量的存储/重新加载工作,但增加到 32 只会节省更多的存储/重新加载工作; 16 通常足以让编译器将他们想要的所有内容保存在寄存器中。
AMD 设法将其扩展到 16 个寄存器(从 8 个增加)这一事实已经是一项重大改进。是的,32 个整数 reg 有时会好一些,但如果不重新设计机器代码格式或使用更长的前缀(如 AVX-512 的 4 字节 EVEX 前缀,它允许 32 个 SIMD 寄存器,x/y /zmm0..31 用于 AVX-512 指令。)
另见:
相关问答: