第7章 寄存器重命名和分发 摘录

7.1 概述

在一个程序的不同指令之间,存在很多的相关性dependency,即一条指令的执行依赖于另外一条指令的结果,可以将相关性分为下面几类:

(1)数据相关性data dependency;
        a. Output dependency,WAW,表示两条指令都将结果写到同一个目的寄存器中。
        b. Anti-dependency,WRR,表示一条指令的目的寄存器和前面的某条指令的源寄存器.
        c. True dependency,RAW,表示一条指令的源寄存器来自于前面某条指令计算的结果。

数据相关性是和寄存器直接相关的,只要在解码阶段得到了寄存器的名字,就可以找到这些相关性。

(2)控制相关性,由于分支指令而引起的相关性,使用分支预测可以解决;

(3)结构相关性, structure Dependency,指令必须等到处理器当中某些部件Structure可以使用的时候才可以继续执行,例如需要等到发射队列issue queue和重排序缓存ROB有空闲空间,或者功能单元是空闲的等。

在数据相关性中WAW、WAR和RAW,只有RAW是真的相关性,其他两种相关性都是和寄存器的名字有关的,可以通过使用不同的寄存器名字而解决。WAW和WAR之所以存在,是由于下述的原因;
        1. 有限个数的寄存器,导致必须在某些地方重复使用寄存器,现在的RISC处理器一般由32个通用寄存器,而32位x86只有8个通用寄存器,因此Intel的处理器很早就实现了寄存器重命名,使用更多的物理寄存器来弥补指令集上的短板。
        2. 程序中的循环体loop,如果一个循环体向寄存器R1写入了值,那么每次循环的时候都会向R1中写入值,这就产生了大量的WAW类型相关性。虽然可以使用拆分循环体的方法来解决这个问题,但是由于有限个数的寄存器,在循环体拆解到某一个时刻,总会有寄存器都用完的时刻,此时WAW不可避免,而且还会导致程序变得很大,需要占用更多的存储空间,导致了I-Cache缺失率升高。
        3. 代码重用code reuse,例如一些很小的function,在一段时间内被频繁调用的话,那么就和上述的循环体中的情况是一样的。在function中,向寄存器R1写入了值,那么频繁调用这个函数,处理器中就存在大量的指令都向R1寄存器中写入值,即存在WAW相关性。虽然可以通过将函数程序嵌入到调用程序中inling来解决这个问题,但是仍旧会存在寄存器在某个时刻被用光。

既然是由于有限个数的寄存器导致了上述假相关性的存在 ,这时候有一个很明显的解决办法,那就是增加指令集中寄存器的个数,但是这样做会导致处理器无法兼容以前的程序。如果增加了寄存器的数量,那么以前所有程序都需要重新编译,这显然是一个浩大工程;而且,即使增加指令集的寄存器的个数,也无法解决代码重用而产生的WAW相关性。

所以,最好的解决方法就是使用硬件管理的寄存器重命名register renaming,处理器中实际存在的寄存器个数要多于指令集中定义的通用寄存器的个数,这些在处理器内部实际存在的寄存器称为物理寄存器physical register,与之对应的,指令集中定义的寄存器称为逻辑寄存器logical register或者Architecture register。逻辑寄存器指的是在指令集中定义的通用寄存器。物理寄存器是指经过寄存器重命名之后使用的寄存器,它才是处理器中真正存在的寄存器。一般情况下,物理寄存器的个数要多于逻辑寄存器的个数,这样才可以使寄存器重命名发挥作用。

处理器在进行寄存器重命名的时候,会动态地将逻辑寄存器映射到物理寄存器,这样可以解决WAW和WAR的相关性。

重命名映射表Register Renaming Table,Intel也将其称为Register Alias Table和空闲寄存器列表Free Register List,用来完成这个过程,其中重命名映射表用来保存已经存在的映射关系,例如一个逻辑寄存器映射到了那个物理寄存器。对于程序中的指令来说,它的源寄存器通过读取重命名映射表,就可以得到它们对应的物理寄存器了。

重命名映射表是一个表格,它可以基于SRAM来实现,也可基于CAM来实现;空闲寄存器列表用来记录那些物理寄存器是空闲的,在进行寄存器重命名时,会通过这个表格来获得空闲的物理寄存器的编号。

7.2 寄存器重命名的方式

对于寄存器重命名来说,概括起来,有三种方式可以实现它,分别是:
(1)将逻辑寄存器Architecture Register File(ARF)扩展来实现寄存器重命名;
(2)使用统一的物理寄存器Pysical Register File(PRF)来实现寄存器重命名;
(3)使用ROB来实现寄存器重命名。

Intel多采用第3种,MIPS多采用第2种,这三种方法本质都是将指令集中定义的逻辑寄存器,在处理器的执行过程中,动态地映射处理器内部实际使用的物理寄存器上,使用这些物理寄存器来代替逻辑寄存器,从而可以增加寄存器的个数。要实现寄存器重命名,一般要考虑下面内容:
(1)什么时候占用一个物理寄存器?这些物理寄存器来自于哪里?
(2)什么时候释放一个物理寄存器?这些物理寄存器去往何处?
(3)发生分支预测失败时,如何进行处理?
(4)发生异常exception时,要如何处理?

7.2.1 使用ROB进行寄存器重命名

在ROB中存储着所有推测状态的结果,而使用逻辑寄存器ARF存储所有正确的结果。为什么想到使用ROB进行存储器重命名?这是一个很自然的事情,在超标量处理器中,每条指令都会将自身的信息按照程序中原始的顺序存储到ROB中,当一条指令将结果计算出来之后,会写到ROB中,但是由于分支预测失败和异常等原因,这些结果未必是正确的,因此它们的状态被称为推测的,在一条指令离开流水线之前,它都会一直待在ROB中,只有当指令变成流水线中最旧的指令,并且被验证是正确时,才会离开ROB,并对处理器的状态进行更新,例如将结果写到ARF。

当一条指令被写到ROB中一个表项的同时,这个表项在ROB中的编号也就成为了这条指令的目的寄存器对应的物理寄存器,这样就将一个逻辑寄存器和ROB中一个表项建立了映射关系,完成了对一条指令的目的寄存器进行重命名的过程,简化了寄存器重命名的过程,只要ROB中有空闲的空间,寄存器重命名就可以一直进行下去。

但是当一条指令离开流水线之后,将其结果更新到ARF中,又执行了另外一条指令,也使用了同样的目的寄存器,那么此时ARF中存储内容的就不是这个逻辑寄存器最新的值,这需要使用重命名映射表来指示每个逻辑寄存器的值是位于ROB中还是位于ARF中。

当这条指令离开流水线,也就是退休的时候,它的结果会从ROB中搬移到ARF中,这个位置变动的信息也需要随之更新到重命名映射表中,这样,一个寄存器在它的生命周期内,会有两个存放的位置,这会对指令的操作数读取产生负面影响,因此在实际的处理器中,都会配合数据捕捉data-capture的发射,并采用payload RAM来存储需要的操作数。

基于ROB进行寄存器重命名的方式,虽然便于管理,但是也有一些缺点,主要为;
        (1)很多指令并没有目的寄存器,因此也就不需要对目的寄存器进行重命名,但是每条指令都需要占用ROB中的一个表项,也就是说,一条指令即使没有目的寄存器,它在ROB中对应的表现也没有办法将物理寄存器省掉,浪费了硅片面积。
        (2)对于一条指令来说,它即可从ROB中读取源操作数,也可以从ARF中读取源操作数,所以ROB和ARF都要支持最坏的情况,也就是在一个周期内支线的所有指令都需要读取ROB或ARF,导致ROB和ARF的读端口都会非常多,对芯片的面积和延迟造成了很大的负面影响。

总体来看,基于ROB的寄存器重命名方式虽然有缺点,但是这种方式容易实现,相应的设计复杂度也不高,因此可以获得不错的性能。

7.2.2将ARF扩展进行寄存器重命名

这种方法是基于ROB进行重命名方法的延伸,由于在指令的执行过程中,很多指令没有目的寄存器,例如store、分支和比较指令。使用一个独立的存储部件,用来存储流水线中所有指令的结果,只有那些存在目的寄存器的指令才会占据这个存储部件,PRF,它可以看做是ARF的扩展。

指令在进行寄存器重命名的时候,如果它存在目的寄存器,则这条指令会占据PRF的一个空间,指令在得到结果之后,会首先将这个结果存储到PRF中,等到这条指令退休的时候,才会将结果搬移到ARF中。如果PRF中已经没有空间了,那么流水线中,寄存器重命名阶段之前的所有流水线都需要暂停,直到流水线中有指令离开而释放PRF空间,寄存器重命名各阶段之前的流水线才可以继续执行。

这种寄存器重命名的方式也需要一个重命名映射表,在其中记录每个逻辑寄存器的值是位于PRF还是ARF中,一条指令在PRF中对应的位置需要存储到这个表格中

 一个逻辑寄存器的值在其生命周期内,仍然可能存在于PRF和ARF两个地方,因此会对后面的指令使用这个寄存器作为源操作数的过程产生影响。

7.2.3 使用统一的PRF进行寄存器重命名

将上一种方法使用的ARF和PRF进行合并,合并之后的部件统称为统一的PRF,在其中存储了所有推测和正确的寄存器值,需要注意,这个统一的PRF和上一节的PRF是不同的,在这个PRF中,没有和指令产生映射关系的寄存器都是处于free状态,使用一个空闲列表free list来记录那些PRF中那些寄存器处于空闲状态;当一条指令被寄存器重命名,并且它存在目的寄存器时,它就会占据PRF当中的一个寄存器,这个寄存器就处于被占用状态。处于这个状态的寄存器会经历值没有被计算出来,值被计算出来但没有retire,退休三个过程,在此过程中,并不需要将寄存器的内容进行搬移,这样便于后续的指令读取操作数。也需要一个重命名映射表,用来存储每个逻辑寄存器和物理寄存器的对应关系。

用来记录空闲物理寄存器的空闲列表Free list可以使用FIFO来实现,所有空闲状态的物理寄存器的编号都存储在其中。

一条指令在寄存器重命名的时候,需要对源寄存器和目的寄存器都进行处理,对于源寄存器来说,它会取查找重命名的映射表,找出它对应的物理寄存器的编号。同时,如果这条指令存在目的寄存器的话,需要为这个目的寄存器指定一个空闲状态的物理寄存器,这需要从空闲列表中进行读取。如果此时空闲列表已经空了,那么就表明此时的物理寄存器已经全部被占用,则流水线的重命名阶段之前的流水线就要被暂停,直到有指令退休而释放物理寄存器为止

当需要从外部查看处理器的状态时,很多物理寄存器由于处于推测状态,是不能被外部看到的,一条指令只有在退休的时候,它的结果才会被外界看到。要实现这样功能,只使用RAT就不能满足要求了,还需要另外一个RAT,用来存储所有退休状态指令和物理寄存器的对应关系。每当指令退休的时候,它对应的映射关系就会写到这个RAT中,外界查询这个RAT,就可以找到逻辑寄存器此时对应的物理寄存器,这样避免了将内部一些错误状态暴露给程序员,因此使用这种基于统一的PRF进行寄存器重命名时,在处理器中需要使用两个RAT来配合进行工作。

一个物理寄存器被占用之后,何时才可以再次变为空闲状态呢?当一个物理寄存器不再被它后面的指令使用时,这个物理寄存器就可以变成空闲状态了,从理论上说,只要最后一条使用这个物理寄存器的指令由于退休而顺利地离开流水线,这个物理寄存器就可以变为空闲状态。尽管编译器可以很容易地获得这个信息,但是在当今所有指令集中,都没有地方能够装载这个信息来通知处理器的硬件。所以处理器采用了一种很简单也很保守的方法,当一条指令和后面的某条指令都写到同一个目的寄存器时,则后面的指令退休的时候,前面指令对应的物理寄存器就已经没有用处,可以释放。

因此在ROB中,除了记录逻辑寄存器对应的物理寄存器之外,还需要存储它之前对应的物理寄存器,以便在指令退休的时候,将它对应的旧的映射关系进行释放。

总结来说,基于ROB重命名方式很简单,不需要复杂控制逻辑,也就不会增加太多的硬件资源;对于ARF进行扩展的寄存器重命名方法,相对比较简单;使用统一PRF进行寄存器重命名需要更复杂的控制逻辑来管理,需要一个free list来存储所有空闲的物理寄存器编号,同时还需要两个重命名映射表配合才可以正常进行工作,这种方法设计难度是比较大的,但是还有处理器采用这种方式优点在于:
        1. 寄存器的值只需要被写入一次,而在另外两种方法,一个寄存器的值在它的生命周期内有两个地方要存放。第一次写到ROB或PRF,第二次写到PRF,很显然消耗更多的能量。
        2. 一条指令的源寄存器的值只能存储在一个地方,即PRF中。而另外两种方法,有两个地方可能存储源寄存器的值,即ARF和ROB(PRF)。

7.3 重命名映射表

RAT是一个表格,使用逻辑寄存器作为地址来进行寻址,对于指令的源寄存器来说,可以从这个表格得到对应的物理寄存器编号。物理实现层面,主要有两种方式,一是基于SRAM,二是基于CAM。

基于SRAM的重命名映射表使用SRAM(或者直接使用寄存器)作为表格的载体,考虑到每条指令的多个源寄存器和目的寄存器都可以在一个周期内寻址它,因此需要使用多端口的SRAM。这个表格使用逻辑寄存器的编号对其进行寻址,因此在这个表格中,entry的个数等于逻辑寄存器的个数,即32个,每个表项中存放着对应的物理寄存器的编号。

基于CAM的重命名映射表虽然也使用逻辑寄存器的编号进行寻址,但是这个表格的载体使用内容寻址的存储器,大小等于物理寄存器的个数,在每个entry中都存放了逻辑寄存器的编号。如果一个entry中存在有效的映射,并且比较结果也相等,则这个entry在cRAT中的地址就是对应的物理寄存器的编号了。

由于SRAM的访问速度要快于CAM,而且基于SRAM的重命名映射表更节省存储器资源,毕竟表格容量等于逻辑寄存器的个数。基于SRAM的重命名映射表由于占用面积小,速度快且低功耗,这种优势随着硬件复杂度的上升变得更加明显,且随着物理寄存器个数增加而明显。

现实中仍旧有一些处理器使用基于CAM的重命名映射表,因为对cRAT进行CehckPoint只需要保存状态位,而不需要将整个cRAT进行保存,这样大大减少了checkpoint电路的面积,所以cRAT并不会随着checkpoint个数的增加而引起复杂度和面积的增加。当checkpoint个数超过一定值,cRAT就优势了,而对于现代的处理器来说,更深的流水线和更高的执行并行度都会导致需要更多的checkpoint,所以cRAT在这种情况下是一个很好的选择。

7.3.1 基于SRAM的重命名映射表

在任意时刻,每个逻辑寄存器都只有一个物理寄存器与之对应,这个对应关系不需要任何的标志位。对分支指令的状态进行checkpoint保存时,需要将整个sRAT都保存起来。因此这种方法的每个checkpoint会占用大量的面积,正是由于这个限制,不可能将checkpoint的个数做的很多。

新写入到sRAT的值会覆盖原来的旧的对应关系,如果不将这个旧的对应关系记录下来,那么这个信息就永远地丢失了,这样当然是不可以的,原因有2:
        (1)在之前说过,一条指令在退休的时候,会将它之前对应的物理寄存器变为空闲状态,因此在1个物理寄存器编号写入到sRAT之前,需将被覆盖的信息保存起来,在ROB记录即可。
        (2)当一条指令之前存在异常或者分支预测失败的指令时,这条指令需要从流水线抹掉。

SRAM比CAM的映射表而言,读写速度回快一点,设计复杂度也不会随着物理寄存器个数的增加而变大,但是这种方式一个很大的缺点是,那就是无法对RAT使用个数较多的checkpoint,因为每个checkpoint都需要将RAT复制一份。

在超标量处理器中,大量地使用了预测算法,例如分支预测,load/store相关性预测、数值预测,只要预测的准确度很高,它对于提高处理器性能就可以起到很大的作用。因为分支指令一旦预测正确,那么它对应的checkpoint其实是没有作用的。

7.3.2 基于CAM的重命名映射表

虽然cRAT也是使用逻辑寄存器的编号进行寻址,但是它是一个内容寻址的存储器,它不会对地址直接进行解码,而是将这个地址和存储器中每个表项的内容进行比较,比较结果时相等的那个表项的地址,就是最终需要的结果,这种寻址方式和全相连结构的Cache是一样的。

为流水线中的每条指令都使用了一个checkpoint,这样任何指令发生异常时,都可以快速进行状态恢复,这也是使用cRAT最大的优势。

其实cRAT是由CAM和SRAM组成,SRAM部分用来存储每个物理寄存器对应的逻辑寄存器的编号,而CAM部分用来进行内容的比较,每个表项的比较结果都会送到一个编码器中,编码器会结合每个表项是否有效的信息,将最终匹配的那个物理寄存器的编号进行输出,这就是读取cRAT的过程了。

基于cRAT进行寄存器重命名的方式,仍需要使用空闲列表来记录那些物理寄存器是空闲的,对于一个物理寄存器何时可以变成空闲的状态,需要等到后面写入到同一个逻辑寄存器的指令退休的时候,才可以将这个逻辑寄存器对应的物理寄存器变为空闲状态。于是,在cRAT中,并不是一个物理寄存器对应的有效位为0,就表示这个物理寄存器是空闲状态,有可能这个映射关系刚刚被覆盖而已;但是一个物理寄存器变成空闲状态时,这个物理寄存器在cRAT中对应的有效位肯定是0.

这种重命名映射表最大的优势就是checkpoint占用资源少。一般情况下,流水线中最大允许存在的分支指令的个数,和处理器最大考验支持的checkpoint个数是一样的。

使用CAM方式实现的重命名映射表,一个最大的弊端就是其面积会随着处理器中物理寄存器个数的增加而增加。另一方面,cRAT进行状态保存的时候,只需要保存它的有效位即可,大大降低了对硬件的需求。

7.4 超标量处理器的寄存器重命名

对于Dest = Src1 op Src2
        (1)从重命名映射表RAT找到Src1和Src2对应的物理寄存器Psrc1和Psrc2。
        (2)从空闲列表free list找到一个物理寄存器Pdest,将其作为目的寄存器Dest对应的物理寄存器。
        (3)将逻辑寄存器Dest和物理寄存器Pdest的映射关系写到重命名映射表中,这样在之后使用Dest作为源寄存器的指令就可以查找到这个映射关系了。

对一条指令进行寄存器重命名时,RAT需要支持两个读端口,用来读取两个源寄存器对应物理寄存器的编号;同时RAT需要支持一个写端口,用来将一条指令的目的寄存器对应的映射关系写到RAT中。为了能够将一个物理寄存器释放为空闲状态,还需要将每条指令之前对应的映射关系保存到ROB中,因此RAT还需要一个额外的读端口,使用指令的目的寄存器作为地址,用来读出它之前对应的映射关系。

但是多端口RAT只是超标量处理器的寄存器重命名过程中复杂性的一方面,还需要考虑到每个周期同时进行重命名的多条指令之间还存在各种相关性。具体如下;

        1. 指令A和指令B之间存在RAW的相关性,指令B的源寄存器r0来自于指令A产生的结果,因此在进行寄存器重命名时,指令B的r0对应的物理寄存器应该直接来自于指令A所对应的P30,而不应该来自于RAT读取的值。
        2. 指令A,B,D之间存在WAW相关性,虽然从字面上看,通过寄存器重命名可以消除这种相关性,但是在实际中,仍然无法忽略它的存在,原因如下:在将每条指令的旧映射关系写到ROB时候,如果发现一个周期内多条指令都使用同一个目的寄存器,那么只需要将最新的那条指令的的映射关系写到RAT即可。另外写入到ROB中的旧映射关系不再是来自于RAT读取的值,而是直接来自于和它存在WAW相关性的指令。因此需要在超标量处理器在寄存器重命名阶段,对指令间存在的WAW相关性进行检查。
        3. 指令B和指令D存在WAR相关性,指令B读取寄存器r0,而指令D写入r0,通过寄存器可以消除这种关系,并且对于实际的重命名过程来说,没有影响。

需要在超标量处理器的寄存器重命名过程考虑RAW和WAW,这样就增加了重命名过程的复杂度。在超标量处理器中,重命名阶段的延迟主要来自于两方面,一个是多端口SRAM本身的延迟,另一个就是RAW和WAW相关性检查和处理电路引起的延迟

由于RAT需要多端口SRAM,而端口数量的增加直接影响了其面积和速度,此外需要对SRAM电路进行专门的设计,以降低对处理器周期时间的影响;而RAW和WAW相关性检查和电路处理也需要占据一定时间,寄存器重命名阶段对于处理器的周期时间影响是很大的,已经有人提出采用流水线方式,使用两个周期完成,虽然减少了对周期时间的影响,但是却增加了RAW和WAW相关检查电路的复杂度,因为此时不但需要对当前周期进行寄存器重命名的指令相关性检查,还需要对上个周期进行的寄存器重命名的指令进行相关性检查。

7.4.1 解决RAW相关性

四条指令之间不存在RAW相关性,这是一种最简单的情况,四条指令的源寄存器都会从重命名映射表中找到对应的物理寄存器,目的寄存器也会从RAT找到之前映射的物理寄存器的编号,同时本周期还会从空闲列表中找出4个空闲的物理寄存器的编号,将它们和4条指令的目的寄存器产生新的映射关系,并将这个关系写到RAT中。

处理器内有一种检查机制,对一个周期内进行重命名的所有指令进行RAW相关性检查,它被称为组内检查。在寄存器重命名阶段,指令之间还保持着程序中指定的顺序执行,因此还需要将每条指令的源寄存器编号和它前面所有指令的目的寄存器进行比较,如果存在一个相等的项,那么这个源寄存器对应的物理寄存器就不是来自于RAT的输出,而是来自于当前周期从空闲列表输出的对应值;如果存在多个相等的项,那么使用最新的那条指令所对应的物理寄存器。

在一个周期内进行寄存器重命名的4条指令中,第一条指令的源寄存器对应的物理寄存器只能来自RAT的输出;第二条指令的源寄存器对应的物理寄存器可能来自RAT的输出,也可能来自于第一条指令的目的寄存器对应的物理寄存器(空闲列表)。第二,第三类似。最后条指令的目的寄存器不可能作为其他指令的源寄存器,因此空闲列表输出的最后一个值并不会被其他指令使用。

组内相关性检查电路和访问RAT时并行工作的,因此不会对处理器的周期时间产生负面的影响。在组内相关性检查电路中,需要将每条电路的源寄存器和它之前的所有指令的目的寄存器进行比较,根据比较结果来控制这个源寄存器的来源。

7.4.2 解决WAW相关性

寄存器重命名虽然可以解决指令之间存在的WAW相关性,但是这种相关性仍然无法被忽视,它影响着对重命名映射表RAT和ROB的写入过程,因此也需要在重命名阶段对其进行检查。
        1. 对RAT进行检查:在进行寄存器重命名的这个周期内,如果存在多条指令的目的寄存器都相等的情况,那么只有最新的那条指令的映射关系才允许被写入到RAT中。对于每条指令来说,都要将它的目的寄存器和后面所有指令的目的寄存器进行比较,如果发现存在相等的情况,则说明本条指令不应该更新RAT,完成对WAW相关性检查。
        2. 对写ROB进行检查:为了能够释放那些不在使用的物理寄存器,同时又可以对处理器的状态进行恢复,每条指令都需要从RAT中读出它以前对应的物理寄存器,并将其写到ROB中,如果在一个周期内进行寄存器重命名的几条指令中,有两条指令的目的寄存器相等,那么比较新的这条指令对应的旧的物理寄存器直接来自于比较旧的那条指令,而不是来自于RAT。

为了实现这个功能,需要对当前周期进行寄存器重命名的所有指令进行WAW相关性的检查,每条指令都需要和比它旧的所有指令进行目的寄存器的比较。

上一节中,WAW相关性检查的结果用来控制对RAT的写入,而本节中WAW相关性的检查用来控制对ROB的写入。

应该保证使用目的寄存器读取RAT的过程先完成,然后才能够向RAT的这些地址写入新的内容。

在实际使用的多端口SRAM中,解决一个周期内写入和读取同一地址的方法是:在写优先的模式中,写入到SRAM中某个地址的内容在写入完成后,可以马上在读端口被获得,也就是在当前周期就可以读取到刚才写入的内容;而在读优先模式,写入到SRAM中某个地址的内容只有在下个周期才可以被读取,在当前周期读取这个地址的内容,只能读取到在本周期之前被写入之前的内容。对于RAT来说,必须选择读优先模式,因为目的寄存器读取RAT的真正原因,就是为了获得这个目的寄存器之前对应的物理寄存器。

对于特殊指令分支、存储等情况需要在解码之后加以标记,做法如下;
        (1)根据当前周期中需要重命名的目的寄存器的个数,决定当前周期需要从空闲列表中读取的数值的个数;
        (2)使用目的寄存器读取RAT时,根据标记的信息,目的寄存器不存在的那些指令将不会读取RAT,也不会写入RAT;
        (3)使用源寄存器读取RAT时,根据标记信息,源寄存器不存在(例如立即数)的那些指令不会读取RAT,或者忽略从RAT读取的结果;
        (4)在重命名阶段进行RAW和WAW相关性检查时,如果一条指令的源寄存器或者目的寄存器不存在,那么忽略和它有关的所有比较结果。

7.5 寄存器重命名过程的恢复

如果被抹掉的指令已经经过了寄存器重命名阶段,那么这条指令已经占据了重命名映射表RAT、重排序缓存和发射队列等资源,当从流水线中被抹掉时,被这条指令占用的资源进行恢复,这样才能够保证后续的指令可以在一个正确的流水线中开始执行,这个过程就是对寄存器重命名的恢复。

本小节介绍的三种典型方法,对于三种寄存器重命名方式都是适用的。

7.5.1 使用checkpoint

在处理器中,checkpoint是指在某个时间点,将处理器中某些部件的内容“原封不动”地保存起来

对于实现了checkpoint功能的RAT来说,在SRAM的每个最小存储单元Main Bit Cell周围都加入同样的存储单元,这些存储单元就实现了checkpoint功能。但需要对RAT进行状态保存时,将MBBC的内容复制到指定的CBC中;当对RAT进行状态恢复时,将对应CBC中的内容复制到MBC中。

7.5.2 使用WALK

另一种更廉价的方式进行RAT状态的保存和恢复,那就是使用ROB。对每一条指令来说,在ROB中都存储了这条指令之前对应的物理寄存器,利用这个信息,可以将RAT的状态逐步倒回去,使那些处在错误路径上的指令,例如分支预测失败或者异常路径上的指令,对RAT上的修改都进行恢复。举例来说,当发现一条分支指令预测失败时,使用这条分支指令的编号,将流水线中所有相关的指令都抹掉,同时将流水线暂停,从ROB的底端开始(ROB的最底端对应着最新放入的ROB指令),逐条地将每条指令之前对应的映射关系写到RAT中,这个过程被形象地称为WALK。

使用walk的方法对RAT进行恢复时比较慢的,它首先需要对流水线中的指令有选择地抹掉,同时还需要逐个指令地进行恢复,需要消耗很多时间。对于分支指令来说,这会增大分支预测失败时的惩罚,但是对于异常发生时处理的状态恢复,则不需要对流水线有选择地抹掉。

虽然walk的方法在速度会慢一些,但是它的优点是消耗的硬件资源比较少。

7.5.3 使用Architecture state

一般会在流水线的提交阶段用一个RAT,所有正确离开流水线的指令都会更新这个RAT,因此这个RAT记录的状态肯定是正确的,称它为aRAT。因为外界访问CPU时,只能看到指令集中定义的逻辑寄存器。而不能看到物理寄存器,因此只有从aRAT中才可以找到逻辑寄存器对应的正确状态的物理寄存器。如果直接查找寄存器重命名阶段的RAT,则得到只是出于推测状态的物理寄存器。

通过访问aRAT,在任何一个时刻,总能得到所有逻辑寄存器对应的正确状态,这个状态即Architecture state。

利用这个总是正确的aRAT,也可以对寄存器重命名阶段使用的RAT进行恢复。举例来说,当在流水线的执行阶段发现分支指令预测失败时,并不马上进行RAT状态恢复,而是让处理器继续执行,当这条分支预测失败的指令变成流水线中最旧的指令时,此时的aRAT即表示了分支指令所对应的正确状态RAT。因为此时,在分支指令之前进入到流水线中的所有指令都已经顺利地离开流水线,并且更新了处理器状态,而该分支指令后面所有的指令都没有在aRAT留下痕迹,此时可以将aRAT的内容复制到重命名阶段RAT中,这样就完成了分支预测失败时的RAT的恢复。因为重命名阶段的RAT和aRAT的结构是一模一样的。缺点是分支预测失败时的惩罚变大,一定程度上影响了处理器执行的效率。

如果一条分支指令之前存在异常或者另一条分支预测失败,那么即使这条分支指令发生分支预测失败,它也不会被处理,而是会从流水线中被抹掉,这样就避免做无用功。

7.6 分发

流水线分发Dispatch阶段就是顺序执行到乱序执行的分界点,指令经过寄存器重命名后,就会进入分发阶段。这个阶段之后,指令会被写到流水线的很多缓存中,主要有以下三类:
        (1)发射队列(乱序),大部分的功能单元都可以按照乱序方式执行,指令在送到FU中被执行之前,先被放到发射队列,每个FU都对应着一个发射队列(先假设如此),当指令被放到这个缓存中的时候,它的操作数可能还没有完全准备好,那么它就在这个缓存中等待,只要一条指令的所有源操作数都准备好了,就可以将其送到FU中执行,而不用理会这条指令在程序中原始的顺序。
        (2)发射队列(顺序),即使是在乱序执行的超标量处理器中,仍旧有部分指令是按照程序中指定的顺序来执行的,例如分支指令和store指令,这些指令如果按照乱序的方式执行,会带来不菲的硬件消耗,而且在性能上也不会带来本质的提高。容纳这些发射队列本质上就是FIFO。
        (3)重排序缓存,这个部件可以将乱序执行的指令拉回程序中指定的原始顺序,指令经过寄存器重命名之后,按照程序中指定的顺序写到重排序缓存中,同时在重排序缓存中还会记录指令在执行过程的一些状态。重排序缓存本质也是FIFO。

总结来说,流水线的分发Dispatch阶段就是将寄存器重命名之后的指令写到发射队列和重排序缓存的过程,指令到达发射队列之后,就可以按照乱序的方式执行了,通过重排序缓存将这些指令再变回程序中指定的顺序。分发阶段可以和寄存器重命名阶段放在一个周期内完成,但是当发射队列和重排序缓存的容量比较大时,向它写入东西会变得很慢,严重影响处理器的周期时间。因此很多处理器都会为分发阶段单独使用一个流水段,从功能上来看,分发阶段可以称得上是超标量处理器的流水线中最轻松的一级。

超标处理器设计》讲述超标(SuperScalar)处理器设计,现代的高性能处理器都采用了超标结构,大至服务器和高性能PC的处理器,小至平板电脑和智能手机的处理器,无一例外。《超标处理器设计》以超标处理器的流水线为主线展开内容介绍。《超标处理器设计》主要内容包括超标处理器的背景知识、流水线、顺序执行和乱序执行两种方式的特点;Cache的一般性原理、提高Cache性能的方法以及超标处理器中的Cache,尤其是多端口的Cache;虚拟存储器的基础知识、页表、TLB和Cache加入流水线后的工作流程;分支预测的一般性原理、在超标处理器中使用分支预测时遇到的问题和解决方法以及如何在分支预测失败时对处理器的状态进行恢复;一般的RISC指令集体系的简单介绍;指令解码的过程,尤其是超标处理器中的指令解码;寄存器重命名的一般性原理、重命名的方式、超标处理器中使用寄存器重命名时遇到的问题和解决方法以及如何对寄存器重命名的过程实现状态恢复;指令的分发(Dispatch)和发射(Issue)、发射过程中的流水线、选择电路和唤醒电路的实现过程;处理器中使用的基本运算单元、旁路网络、Cluster结构以及如何对Load/Store指令的执行过程进行加速;重排序缓存(ROB)、处理器状态的管理以及超标处理器中对异常的处理过程;经典的Alpha21264处理器的介绍。在本书中使用了一些现实世界的超标处理器作为例子,以便于读者加深对超标处理器的理解和认识。 《超标处理器设计》可用作高等院校电子及计算机专业研究生和高年级本科生教材,也可供自学者阅读。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值