超标量处理器-寄存器Rename详解

 1.相关性介绍

       在CPU中,一段程序会被编译成一连串的汇编指令,指令与指令之间可能会具有相关性(dependency)。所谓相关性,即一条指令的执行会依赖于另一条指令的结果,相关性可以分为:① 数据相关性;② 存储器数据相关性;③ 控制相关性;④ 结构相关性。本文主要介绍数据相关性。

        数据相关性(Data Dependence)可以分为以下几种:

        ① Output dependence,又称写后写相关(Write After Write,WAW),表示两条指令都将结果写到同一个目的寄存器中。

        ② Anti-dependence,又称读后写相关(Write After Read,WAR),表示一条指令的目的寄存器和它前面某条指令的源寄存器是一样的。

        ③ True dependence,又称写后读相关(Read After Write,RAW),是真相关,表示一条指令的源寄存器来自于它前面某条指令的计算结果。

        数据相关性的三种类型WAW、WAR和RAW中,只有RAW是真相关,其他两种相关性都和寄存器的名字有关,可以通过使用不同的寄存器名字来解决,如下图所示:

超标量处理器-寄存器Rename详解_第1张图片

         通过更换寄存器的名字,就可以解决WAW和WAR的相关性, 因此这两种相关性也称为假相关。现在RISC-V处理器一般都有32个通用寄存器(也叫逻辑寄存器或架构寄存器),如果在指令发生相关性时,就需要更多的物理寄存器来解决相关性问题。

        常见出现相关性的场景有:(1)循环loop,如果在一个循环体中向一个寄存器R1写入了值,并且每次循环的时候都会向R1写入值,那么就会产生大量的WAW相关,当寄存器用完时,WAW相关性就不可避免了,会导致程序变得很大,I-Cache缺失率升高。(2)代码复用,譬如有一个函数strcat()字符串拼接函数被频繁调用,处理器反复向一个寄存器R1写字符串,那么和第一种情况一样,也会发生WAW相关,导致同样的问题。

        解决办法就是用硬件来管理寄存器的重命名(register renaming)。处理器内部实际存在的寄存器称为物理寄存器(Physical Register)指令集中定义的寄存器称为逻辑寄存器(Logical Register)或者架构寄存器(Architecture Register)。物理寄存器的个数要多于逻辑寄存器的个数,这样才可以使寄存器rename起作用,譬如MIPS处理器定义了R0~R31这32个逻辑寄存器,处理器中真正存在的寄存器也就是物理寄存器有128个。处理器在进行寄存器重命名的时候,会动态地将逻辑寄存器映射到物理寄存器,这样可以解决WAW和WAR的相关性。

        如下图所示,有R0~R6共7个逻辑寄存器,有p0~p9共10个物理寄存器,编译器编译代码的时候会直接使用指令集中定义的逻辑寄存器,如图中左边的原始代码,箭头表示了指令之间存在的WAW/WAR相关性,通过寄存重命名的方法将它们消除掉,可以使原始程序获得并行性。

超标量处理器-寄存器Rename详解_第2张图片

         图中的重命名映射表保存着映射关系,对于程序中的指令来说,它的源寄存器通过读取重命名映射表,就可以得到它们对应的物理寄存器了。经过重命名之后的程序不存在WAW和WAR的相关性了,因此在超标量处理器中,这个程序可以获得最大的并行性,从而尽快执行完毕。        

        再举个简单点的例子,如下三行代码发生了WAW相关,

超标量处理器-寄存器Rename详解_第3张图片

        ① 在R2=R1+R0执行时,重命名映射表将R2映射到了物理寄存器p2上,将R1+R0的结果写到p2中。

        ② 在执行第二行指令,对R2逻辑寄存器进行WAW的时候,更新重命名映射表,将R2映射到物理寄存器p4上,然后将R1×R0的结果写到p4中。

        ③ 执行第三行指令,需要读取R2逻辑寄存器做加法,此时就会去查询重命名映射表,发现R2映射到了p4物理寄存器,所以其实就会去读取p4寄存器的值(R1×R0),然后再做加法。

         那么可能会产生一个疑问,p2物理寄存器怎么办?p2物理寄存器在WAW相关之后,映射的逻辑寄存器内的值被覆盖了,所以此时p2物理寄存器内存储的内容已经没用了,因此p2物理寄存器可以被释放,等待下一次有相关性冲突时,再被使用。

2.寄存器重命名设计方式

        针对超标量处理器设计中,实现寄存器重命名(Register Renaming)这个功能有很多种设计方式,可以分为:① 将逻辑寄存器(Architecture Register File,ARF)扩展来实现寄存器重命名;② 使用统一的物理寄存器(Physical Register File,PRF)来实现寄存器的重命名;③ 使用ROB来实现寄存器重命名。这三种方法本质都是讲指令集中定义的逻辑寄存器映射到物理寄存器上,从而可以增加寄存器的个数。

所以接下来,我们需要搞清楚几个问题:

(1)什么时候占用一个物理寄存器?

(2)什么时候释放一个物理寄存器?

(3)发生分支预测失败时,如何进行处理?

(4)发生异常(exception)时,如何进行处理?

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

        这种方法将重排序缓存(ROB)作为了物理寄存器,在超标量处理器中,每条指令都会将自身的信息按照程序中原始的顺序存储到ROB中,当一条指令将结果计算出来之后,会将其写到ROB中,但是由于分支预测失败(mis-prediction)和异常(exception)等原因,计算的结果可能是错误的,这种状态称为推测状态(speculative)。

        在一条指令真正退休之前,它会一直待在ROB中,只有当指令变成最旧的指令并且被验证为正确的时候,才会离开ROB,并将它的结果写到ARF中。这种方式相当于将物理寄存器(PRF)和ROB集成到了一起。

超标量处理器-寄存器Rename详解_第4张图片

        当一条指令被写到ROB中的一个表项(entry)时,这个表项在ROB中的编号也就成了该条指令目的寄存器对应的物理寄存器,这就将一个逻辑寄存器和ROB中表项的编号建立了映射关系,完成了对目的寄存器的rename过程,只要ROB中有空余的表项,那么rename就可以一直进行。

        ROB中存储着所有没有离开流水线的指令结果,而逻辑寄存器(ARF)中存储着所有“最新”离开流水线的指令结果。如果一个逻辑寄存器的值还在ROB中,那么重命名映射表会给出这个逻辑寄存器在ROB中所占用表项的编号。即当一条指令计算完毕,它的结果会被存储ROB的对应空间,当这条指令要离开流水线也就是退休时,它的结果会从ROB中更新到ARF中,这个位置的变动信息也要同步的更新到重命名映射表中。这样,一个寄存器在它的生命周期内,会有两个存放它信息的位置。

        虽然基于ROB的寄存器重命名方式便于管理,但存在一些缺点:① 很多指令没有目的寄存器,因此也就不需要对目的寄存器进行重命名,但每条指令都占用ROB中的一个表项。哪怕该条没有目的寄存器,它在ROB中对应的表项也无法将物理寄存器省掉,资源利用率不高。② 对于一条指令来说,它既可以从ROB中读取源操作数,也可以从ARF中读取源操作数。最坏情况下,4发射的超标量处理器,每条指令有2个源寄存器,那么ARF和ROB都要支持2×4=8个读端口,对芯片的面积和延时有较大负面影响。

2.2 使用ARF扩展进行寄存器重命名

        在指令的执行过程中,很多指令并没有目的寄存器,譬如store指令、分支指令和比较指令等,在一般的程序当中,这些指令大概占比25%左右。使用基于ROB的重命名方法会造成一定的资源浪费。因此,可以使用一个独立的存储部件,只有那些存在目的寄存器的指令才会占据这个存储部件当中的存储空间,这个部件称为PRF(Physical Register File),它可以看做ARF的扩展。

        每次指令被解码(decode)之后,那些存在目的寄存器的指令会占据PRF中的一个表项,这个表项的编号对应物理寄存器的编号,每当指令退休的时候,由于指令的结果会被写到ARF中,所以在PRF中,这条指令所占据的空间也就没用了,可以进行释放,因此,PRF本质上也可以使用FIFO实现。如果PRF中已经没有空间了,那么流水线中寄存器重命名阶段之前的所有流水线都需要暂停(stall前级流水),知道流水线中有指令离开而释放PRF的空间时,流水行才能继续执行。

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

2.3 使用同一PRF进行寄存器重命名

        将2.2节中的ARF和PRF合并在一起合并之后的部件称为统一的PRF,其中存储了所有推测的(speculative)和正确的寄存器(retire)值。在这个PRF中,没有和指令产生映射关系的寄存器都是处于空闲(free)状态,当一条指令被重命名,并且它存在目的寄存器的时候,它就会占据PRF当中的一个寄存器,这个寄存器就处于占用的状态,处于这个状态的寄存器会经历:值没有被计算出来、值被计算出来但没有退休和退休三个过程。这种寄存器重命名方法也需要一个重命名映射表,用来存储每个逻辑寄存器和物理寄存器的对应关系。

        同样的,如果此时没有空闲的物理寄存器,代表此时物理寄存器已经全部被占用,则流水线重命名阶段及其之前的流水线就要被暂停,直到有指令退休而释放掉物理寄存器为止。

3.寄存器释放

        当一个物理寄存器不再被后面的指令使用时,这个物理寄存器就可以变为空闲状态了,只要最后一条使用这个物理寄存器的指令由于退休而顺利地离开了流水线,这个物理寄存器就可以变为空闲状态。

        具体的,当一条指令和后面的某条指令都写到同一个目的寄存器时,则后面的指令退休的时候,前面指令对应的物理寄存器就已经没有用处了。

超标量处理器-寄存器Rename详解_第5张图片

         如上图所示,ADD和MUL两条指令都使用了r1作为目的寄存器。如果有一条指令使用了ADD的目的寄存器p1,作为源寄存器,那么这条指令必定位于ADD和MUL指令之间,当MUL指令退休时,则可以保证以后再也没有指令使用物理寄存器p1作为源寄存器了因此物理寄存器p1可以变为空闲状态。由于MUL指令,内容p1已经被p6覆盖了,指令MUL后面的指令如果还用到了逻辑寄存器r1作为源寄存器,肯定会使用物理寄存器p6。因此当指令MUL退休的时候,就可以释放物理寄存器p1。

        所以为了实现这个功能,在ROB中除了记录逻辑寄存器当前对应的物理寄存器之外,还需要存储它之前对应的物理寄存器,以便于在指令退休的事后,将它对应的旧映射关系的物理寄存器进行释放。

4.超标量处理器寄存器重命名步骤

        在介绍超标量处理器寄存器重命名步骤之前,需要先复习一下单条指令的重命名流程。

4.1 单条指令的重命名流程

        对于Dest = Sec1 op Src2这种指令来说,寄存器重命名步骤如下:

        ① 从重命名映射表(RAT)中找到Src1和Src2对应的物理寄存器Psrc1和Psrc2。

超标量处理器-寄存器Rename详解_第6张图片

         ② 需要用一个列表维护哪些物理寄存器被使用,哪些物理寄存器处于空闲状态,选一个空闲状态的物理寄存器Pdest,将其作为指令的目的寄存器Dest对应的物理寄存器。

超标量处理器-寄存器Rename详解_第7张图片

          ③ 将逻辑寄存器Dest和物理寄存器Pdest的映射关系写道重命名映射表(RAT)中,这样在之后使用Dest作为源寄存器的指令就可以在重命名映射表中查到这个映射关系了。

        经过这三步之后,就完成了对一条指令的寄存器重命名过程,由此可知,对一条指令进行寄存器重命名时,RAT需要支持两个读端口用来读取两个源寄存器对应的物理寄存器编号;还需要一个写端口,来将一条指令的目的寄存器对应的新映射关系写到RAT中。

        为了能够将物理寄存器释放为空闲状态,我们还需要将每条指令dest之前的映射关系也保存在ROB中,因此RAT还需要一个额外的读端口来读取之前的映射关系。故对一条指令来说,RAT需要3个读端口,1个写端口

        一条指令的rename过程是比较简单的,但是对于每周期执行多条指令的超标量处理器来说,rename过程就比较复杂。

4.2 多条指令的重命名流程

        以4-way超标量处理器为例,每周期需要对四条指令进行寄存器重命名。一条指令RAT需要3个读端口,1个写端口。那么四条指令RAT需要12个读端口和4个写端口。但是超标量处理器rename的复杂性不仅在RAT的多端口问题,还需要考虑每周期同时进行rename的多条指令之间的相关性问题。

        如下图所示,下图同时存在先写后写相关(WAW)、先写后读相关(RAW)和先读后写相关(WAR)三种下相关性。

超标量处理器-寄存器Rename详解_第8张图片

        ① 指令A和指令B之间存在写后读RAW相关性,指令B的源寄存器r0来自指令A的结果,因此进行寄存器rename的时候,指令B的r0对应的物理寄存器应该直接来自于指令A的目的寄存器对应的P30物理寄存器。

        ② 指令ABD存在先写后写WAW相关性。可以通过寄存器rename消除这种相关性,但是要注意一些问题。(a) 如果在一周期多条指令具有同一个目的寄存器(存在WAW相关性),那么只需要将最新的那条指令的映射关系写到RAW中即可。(b) 物理寄存器退休问题,对指令B来说,r0的旧映射关系对应的物理寄存器是P30,对指令D来说,r0的旧映射关系对应的物理寄存器是P31。

        ③ 指令B和D之间存在先读后写(WAR)相关性。指令B读取寄存器r0,指令D写入寄存器r0,可以通过寄存器rename来消除这种相关性。

        所以在超标量处理器中寄存器rename模块的设计,关键就在于解决RAW相关和WAW相关,需要解决多发射情况下的指令间的依赖关系,rename阶段的主要延时主要来源于两个方面,一个是多端口SRAM本身的延时,另一个是RAW和WAW相关性检查和处理电路引起的延时。综合起来看,寄存器重命名阶段对处理器周期时间的影响是很大的,所以有人提出使用两周期来完成这个过程

4.2.1 解决RAW相关性

        如果四条指令不存在RAW相关性,则四条指令的源寄存器会从重命名映射表(RAT)中找到对应的物理寄存器,目的寄存器也会找空闲的物理寄存器去重命名。

超标量处理器-寄存器Rename详解_第9张图片

         如果四条指令存在RAW相关性,如下图所示,指令A和指令C之间存在RAW相关性。指令C读取R1的物理寄存器应该是指令A rename之后的P1,而不是之前的旧映射关系P21。

超标量处理器-寄存器Rename详解_第10张图片

         所以针对这个RAW相关性问题,处理器中应该有一个检查机制,对同周期进行rename的所有指令进行RAW检查,也被称为组内相关性检查。在rename阶段,指令还保持着in-order的顺序,只需要将每条指令的源寄存器编号和它前面所有指令的目的寄存器编号进行比较,如果存在相等的项,那么这个源寄存器对应的物理寄存器就不是来自于RAT的旧映射关系,而是来自于当前周期从空闲列表输出的对应物理寄存器值。

        组内相关性检查,是检查每条指令的源寄存器编号和它前面所有指令的目的寄存器编号,这个过程可以和查RAT寄存器映射关系并行做,所以对处理器的时序没有很大的负面影响。

4.2.2 解决WAW相关性

超标量处理器-寄存器Rename详解_第11张图片

        在上面的指令中,有三条指令都对同一个目的寄存器r0进行了修改,但只有最新的那条指令(指令D)的映射关系才会被写到RAT中。所以需要对一周期内进行寄存器重命名的所有指令进行WAW相关性检查。假设当前周期进行rename的四条指令的目的寄存器分别是dst0、dst1、dst2和dst3。那么dst0只要和dst1、dst2、dst3中任意一个相同,则dst0的映射关系就不需要写进RAT,因为映射关系会被后面更新的指令覆盖;dst1只要和dst2、dst3中任意一个相同,则dst1的映射关系就不需要写进RAT;dst2只要和dst3相同,则dest2的映射关系就不需要写进RAT。

        rename阶段的四条指令,对应着RAT的四个写口,但不意味着所有指令都会写RAT,当存在两条指令的目的寄存器相同时,只需要比较新的指令写进RAT。

你可能感兴趣的:(CPU设计,体系结构)