Nav*_*een 1 assembly cpu-architecture cpu-registers
我开始学习x86_64汇编,我注意到的一件事是寄存器的使用,例如rdi,rbp,rax,rbx。它们是否存在于 CPU 中,或者这是汇编器使用的某种抽象机制?
例如,如果我这样做
mov rax, 60
Run Code Online (Sandbox Code Playgroud)
这是否在硬件中找到具有该指定名称的寄存器?
CPU 硬件无法按名称查找寄存器,而是由汇编器将名称(如机器代码中的rax3 位或 4 位寄存器编号)转换为名称。 (寄存器名称隐含的操作数大小也通过操作码和(缺少)前缀进行编码)。
例如add ecx, edx组装到
01 d1. 操作码01是add r/m32, r. 第二个字节 ModRM0xd1 = 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 是为与 8080 的 asm 源兼容性而设计的- 即在每条指令的基础上进行“移植”,足够简单,机器可以自动执行。)
这就是 CPU 实际解码的内容,以及它用来“寻址”其内部寄存器的内容。无需寄存器重命名的简单有序 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 对向量寄存器所做的那样),不值得付出代码大小的代价。
x86-64 是从 1979 年设计的 16 位 8086 发展而来的。如果现在以现代晶体管预算重新开始,那么当时做出的许多设计选择都不是你会做出的选择。(并不是为了与 8 位 8080 实现 asm 源代码级兼容性)。
更多的架构寄存器在每个操作数的机器代码中花费更多的位。更多的物理寄存器意味着更多的无序执行能力来处理更多的寄存器重命名。(物理寄存器编号是一个内部细节。) 本文测量了用于隐藏缓存未命中延迟的实际无序窗口大小,并将其与已知的 ROB 和 PRF 大小进行比较 - 在某些情况下,CPU 耗尽了可以重命名的物理寄存器,在填充 ROB 之前,针对所选的填充指令组合。
,更多的寄存器不是意味着更高的性能吗?
更多的架构寄存器通常确实有助于提高性能,但回报会递减。与 8 相比,16 避免了大量存储/重新加载工作,但增加到 32 仅节省了更多存储/重新加载工作;16 通常足以让编译器将他们想要的所有内容保存在寄存器中。
事实上,AMD 设法将其扩展到 16 个寄存器(从 8 个增加到了),这已经是一个重大改进。是的,32 个整数寄存器有时会更好一些,但如果不重新设计机器代码格式或使用更长的前缀(例如 AVX-512 的 4 字节 EVEX 前缀,允许 32 个 SIMD 寄存器,x/y),就无法完成/zmm0..31 用于 AVX-512 指令。)
也可以看看:
https://www.realworldtech.com/sandy-bridge/5/ - 英特尔 Sandybridge 是英特尔开始使用 PRF(物理寄存器文件)而不是在 ROB(重新排序缓冲区)中保留临时值的时候。
相关问答: