物理寄存器是CPU内部实际存在的寄存器,它们通常由快速的静态随机读写存储器 (SRAM)实现,可以多路并发访问不同的寄存器。架构寄存器是指令集架构(ISA)提供给软件可见的寄存器,比如x86中的ax, bx, cx, dx等。它们是程序员或编译器可以使用的有限的寄存器名字空间。

在简单的CPU中,物理寄存器和架构寄存器是一一对应的,也就是说每个架构寄存器都有一个固定的物理寄存器与之对应。但在复杂的CPU中,为了提高指令层面的并行性,使用了一种寄存器重命名的技术(详情请参见本人其他博客)。这种技术会把架构寄存器动态地映射到物理寄存器上,从而解决了一些依赖问题(比如WAW,WAR等)。这样,一个架构寄存器可以对应多个物理寄存器,而一个物理寄存器只能对应一个架构寄存器。这种映射关系由一个叫做重命名映射表的部件记录和维护。

下面是一个简单的例子来说明物理寄存器和架构寄存器的区别:

假设我们有8个架构寄存器(R0-R7),21个物理寄存器(P0-P20),以及以下四条指令:

“`
ADD R0 <- R1, R2
IMUL R0 <- R0, R1
SUB R1 <- R2, R3
IMUL R0 <- R1, R2
“`

在没有重命名的情况下,第一二条指令存在WAW和RAW依赖,第二三条指令存在WAR依赖,第三四条指令存在RAW依赖。这些依赖会限制指令的并行执行。

在有重命名的情况下,重命名映射表会将架构寄存器映射到不同的物理寄存器上,从而消除除了RAW之外的所有依赖。例如,重命名后的指令可能如下:

“`
ADD P10 <- P11, P12
IMUL P13 <- P10, P11
SUB P14 <- P12, P15
IMUL P16 <- P14, P12
“`

这样,第一三条指令可以同时执行,因为它们没有共享任何物理寄存器。第二四条指令还是需要等待前面指令完成才能执行,因为它们有RAW依赖。

有三种主要的方法来实现寄存器重命名:

– 使用重排序缓冲(Reorder Buffer,ROB)来实现寄存器重命名。这种方法是把ROB中的每个表项都当作一个物理寄存器,当一条指令被发射到ROB中,它就占据了一个表项,并用它的编号和逻辑寄存器建立映射关系。这样,后续指令就可以通过映射表找到源数据或目的数据所在的物理寄存器。
– 使用逻辑寄存器堆(Architectural Register File,ARF)扩展来实现寄存器重命名。这种方法是在ARF之外增加一些额外的物理寄存器,称为远期寄存器堆(Future File),用于保存投机执行的结果。当一条指令被发射时,它会从空闲列表中找到一个空闲的物理寄存器,并用它和逻辑寄存器建立映射关系。当指令提交时,它会把结果从远期寄存器堆写回到ARF中。
– 使用统一的物理寄存器堆(Physical Register File,PRF)来实现寄存器重命名。这种方法是把所有的物理寄存器都放在一个大的寄存器堆中,用若干位表示的标签(tag)来索引。当一条指令被发射时,它会从空闲列表中找到一个空闲的物理寄存器,并用它的标签和逻辑寄存器建立映射关系。当指令执行时,它会根据标签从物理寄存器堆中读取或写入数据。