在32位计算机中,我们有8个“通用”寄存器。使用64位系统,寄存器数量翻倍了,但这似乎与64位本身没有关系。
现在,由于寄存器非常快(无内存访问),为什么不自然地增加更多的寄存器呢?难道CPU制造商不应该尽可能多地将寄存器集成到CPU中吗?为什么我们只有现在的数量,有什么逻辑限制呢?
在32位计算机中,我们有8个“通用”寄存器。使用64位系统,寄存器数量翻倍了,但这似乎与64位本身没有关系。
现在,由于寄存器非常快(无内存访问),为什么不自然地增加更多的寄存器呢?难道CPU制造商不应该尽可能多地将寄存器集成到CPU中吗?为什么我们只有现在的数量,有什么逻辑限制呢?
为什么不能拥有大量寄存器呢?原因有很多:
如今我们确实拥有很多寄存器 - 只是它们没有被显式编程。我们有“寄存器重命名”。虽然您只能访问少量(8-32个)寄存器,但它们实际上由一个更大的集合支持(例如64-256个)。 CPU然后跟踪每个寄存器的可见性,并将它们分配给重命名集。例如,在ARM中,您可以多次加载、修改和存储一个寄存器,并且每个操作都会独立执行,具体取决于缓存未命中等因素。
ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]
Cortex A9内核会进行寄存器重命名,因此对“r0”的第一次加载实际上是加载到一个重命名的虚拟寄存器 - 让我们称其为“v0”。加载、增量和存储都在“v0”上完成。同时,我们还对r0执行了一次加载/修改/存储操作,但由于这是完全独立的序列,所以它将被重命名为“v1”。假设由于缓存未命中而导致了从“r4”中指向的指针的加载延迟。没关系 - 我们不需要等待“r0”准备就绪。因为它已被重命名,我们可以使用“v1”(也映射到r0)运行下一个序列 - 也许这是一个缓存命中,我们刚刚获得了巨大的性能提升。
ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]
我认为现在的x86处理器有很多重命名寄存器(大约256个),这意味着每个指令都需要8位乘以2来表示源和目的地。这将大量增加内核所需的电线数量和大小。因此,大多数设计师已经选择了16-32个寄存器的甜点,对于乱序CPU设计,寄存器重命名是缓解问题的方法。由于几乎每个指令都必须选择 1、2 或 3 个体系结构可见寄存器,增加它们的数量将使每个指令的代码大小增加几位,从而降低代码密度。它还增加了必须在线程状态中保存的上下文量以及部分保存在函数的激活记录 中。这些操作频繁发生。流水线互锁必须为每个寄存器检查记分牌,并且这具有二次时间和空间复杂度。也许最重要的原因是与已定义的指令集兼容。
但事实证明,感谢寄存器重命名,我们确实有很多可用的寄存器,而且我们甚至不需要保存它们。CPU 实际上具有许多寄存器集,并且在您的代码执行时自动在它们之间切换。它这样做纯粹是为了让您获得更多的寄存器。
示例:
load r1, a # x = a
store r1, x
load r1, b # y = b
store r1, y
load r1, a
store r1, x
load r10, b
store r10, y
他们一直在添加寄存器,但它们通常与特殊用途指令(例如 SIMD、SSE2 等)相关联,或者需要编译到特定的 CPU 架构中,这降低了可移植性。现有的指令通常使用特定的寄存器,并且如果其他寄存器可用,则无法利用它们。遗留指令集等。
push ax
在x86上的操作码为0x50,最后一个寄存器di的操作码为0x57,而指令pop ax
从0x58开始,一直到0x5F的pop di
来完成第一个16进制数。每个大小有8个寄存器,十六进制的一致性得以维持。