RA, 寄存器分配
2016-05-26 17:21
316 查看
寄存器分配
1. 如何计算D-U链
(1) 首先遍历整个routine,保存所有def值;问题:
你如何去保存这些def值呢?要保证能快速的得到每个def值,因为每一 个 def 值不仅仅只包含dst寄存器号,还包括这条指令,以及其它信息。
解答之一:
最快速的查找算法,就是hash算法。在我们这里通过构建一个含有256个元素的hash表,hash[regNo & (256-1)] = defIndex;对于hash冲突的处理方法是,在def值的结构中存放一个变量,用来保存冲突的defIndex.
比如:
r1 = r2 + r3;//defIndex = x;
r1 = r1 * r4;//defIndex = x + 1;
很显然,此时两次def的r1的hash值是一样的,但两次def的defIndex是不一样的, 假定第一次的defIndex = x, 那么(pDefs + defIndex) ->hashNextDefIndex = x + 1;其实可以把这理解为链表的形式处理hash冲突,但冲突不是在hash表上处理的而已。
(2) 自顶向下计算每个Basic Block的LiveOut def值,这里的LiveOut def值表示在离开当前block后还可能活跃的def值。然后通过数据流分析,得到每个Basic Block的LiveIn def值。
问题:
如何计算?
解答之一 :
根据routine中的def值的数目,来定义一个长度为32bit倍数的变量(假定变量名字为FlowFunction),每一位用来表示对应的defIndex, 为1,表示活跃, 为0, 表示不活跃。
1) 分block扫描所有指令,在每一个block里,对有dst的指令,将FlowFunction变量的对应位置为1;如果出现多次定义一个变量的情况,则只会把最后一次的defIndex置为1,也就是在每次扫描的过程中,在最后一次def的时候,会把前一次的def clear 掉;这样每个Basic Block的FlowFunction变量的值就得到了;
2) 上一步只是得到每一个Block中的LiveOut def变量,因为在整个routine中block是有数据流关系的,如果当前Block有predecessor Block,则当前Block的LiveIn def变量 = 所有predecessor Block的LiveOut的或运算。
比如:
LiveIn(Block 5) = LiveOut(Block 3) | LiveOut(Block 4);
(3) 计算完每个Basic Block的LiveIn def值后,自顶向下扫描每个Block。首先检查每条指令的src, 然后根据src可以得到其所有的defIndex, 如果这个defIndex在LiveIn中,那么就将这个Usage添加到这个def中。然后检查这条指令的dst,这个dst是否已经在LiveIn中,如果是,则将 旧的defIndex从LiveIn中删掉,否则将这个dst的defIndex添加到LiveIn中。(此时的LiveIn已经不是原来的LiveIn,只能说初始值是原来的LiveIn吧)。
问题:为什么要计算LiveIn的def变量?如下图所示,如果不计算第1个block的LiveOut def值,那么在其successor block中use变量i就不知道来自于哪个def了
2. 如何构建Web?
构建web很简单,遍历所有的def变量,如果两个def变量对应的register相同,且有相同的use,则这两个def属于相同的web.3. 构建冲突图
在构建寄存器的冲突图,也就是构建web之间的冲突图。在构建冲突图的过程中,主要分两步,首先计算出CFG中每个Basic Block的LiveOut变量,注意这里的LiveOut跟上面的的LiveOut是有区别的,上面的LiveOut可以理解为计算变量的可达性,而这里的LiveOut指变量的活跃性。然后根据LiveOut变量,以及当前Basic Block的指令得到冲突图。(1)如何计算每个Basic Block的LiveOut?
我们采用自底向上的遍历方式来计算,
1) 首先假定每一个Block的LiveOut为NULL,然后根据算法LiveIn = LiveOut + use - def,要计算LiveIn,必须知道use和def,此时我们计算出每一个Block的use和def
2) 然后根据迭代算法, 当前Block的LiveOut = 所有后继结点的LiveIn的并集。
注意,在整个过程中,我们只计算了LiveOut,并没有计算出LiveIn.
(2) 构建冲突图,从最后一条指令开始扫描Block,首先检查当前指令的dst,然后将此dst跟{LiveOut}中的所有变量构建冲突边;最后重新检查dst,如果这个dst的所有def都已经kill了(注意,必须是所有的def都已经被kill掉了),那么就将这个def对应的node从{LiveOut}中删除,然后检查src,并将src的加入到集合{ LiveOut, src}中;
4. 修剪冲突图
我们尝试着用R种颜色给图着色,其中R是可用寄存器的个数。当R>=3时,这是一个NP完全问题。一种方法保证能使得图的一部分是可R色着色的,只要这个图的剩余部分是可R色着色的。另一种方法则在第一种方法没有穷尽的情况下继续乐观地进行处理。后一种方法不会直接产生一种R色着色,但与只使用前一种方法相比,它常常能使图的更多部分成为可着色的,因此非常有助于它的启发式值。前一种方法基于一种简单但非常有效的观察,我们称之为度<R规则:在图中选定一个度数小于R的结点,如果删除这个结点后,这个图还是可R作色的,则原图是R可作色的。但这种方法可能会漏掉某些可以R作色的图。比如下图是一个可2色作色的,用之前的方法就没法得到这个结论。
后一种方法即乐观启发式方法,通过删除度>=R的结点来推广度<R规则。这种方法的理由是因为观察到一个结点虽有R个以上的相邻结点,但它们不必都有不同的颜色,因而它们可能不会使用到所有的R种颜色。上面右边的图就是3色可作色的。
4.1 PRF的分配策略
1.首先按顺序遍历所有node, 根据冲突图,如果这个node的冲突边<=R的node,将其压入栈(用来存放寄存器分配顺序的栈colorStackDepth),并删除与这个结点相关的冲突边,同时标记这个node已经被spruned;直到遍历完所有node;2.第1步做完之后,如果发现还有node没有压入栈colorStackDepth, 很显然这些没有压入栈的node可能是需要spill的node, 因此找出这些没有压入栈中spill代价最小的node,并将其压入栈;并删除与这个结点有关的冲突边,同时标记这个node已经被spruned,然后再执行第1步;
3.直到第2步中没有其它的node结束;
4.然后从栈顶拿出一个node,根据冲突图,为其按序分配寄存器,如果没有可用的寄存器用来分配,则将这个node标记为spill;
5.如果存在某些node需要spill,则执行相应的spill操作;
下面描述下简单的spill过程,由于在执行PRF spill操作时,是通过将PRF spill 到CRF的,下面举个简单的例子:
P4 = P2 & P3; //def P4;
…
P5 = P4 | P6; //use P4;
现在假定要将P4 spill到CRF中,经过处理后的代码如下:
Rx = P2 & P3;
…
MOV Py, Rx;
P5 = Py | P6;
这里有2点需要注意:
1)这里的Rx是临时的CRF,因为首先是做PRF的分配,然后才是CRF的分配,因此,这里可以用一个临时的CRF处理;
2)在PRF spill的时候,首先找到需要需要spill的PRF,然后寻找其use,找到use后,在use的指令前添加一条MOV Py, Rx指令,并将这个use的PRF替换为Py;在这里有一个技巧,我们没有必要碰到一个use就为其添加一个MOV指令,这就太浪费了,但我们也不能只添加一条MOV指令,这样Py的生存期又太长了;折中的方法是适当添加MOV指令,比如碰到如下的情况:
P4 = P2 & P3; //def P4;
…
(P4) R1 = R2 + R3;
P5 = P4 | P6; //use P4;
那么此时通过spill操作的代码为:
Rx = P2 & P3;
…
MOV Py, Rx;
(Py) R1 = R2 + R3;
P5 = Py | P6;
4.2 CRF的分配策略
在这里我们只考虑两种类型的CRF的分配,也就是range CRF和single CRF.具体步骤如下:1.首先统计range CRF的node数目和single CRF的node数目,比如standalone CRF的node数目为SingleCRFNodeColorStackDepth;
2.遍历所有的node,如果发现其为range CRF的Node,则将其保存到pColorStack中,但此时的偏移为SingleCRFNodeColorStackDepth,也就是range CRF从地址
PcolorStack + SingleCRFNodeColorStackDepth开始进行保存;如果发现是single CRF,则先通过位域变量先保存在pLiveNodeMask中;
3.然后通过扫描pLiveNodeMask,来确定single CRF的node,找出其中冲突边最少的node,如果这个node的冲突边< R,则将其压入栈pColorStack,并将pLiveNodeMask对应位置为0,并删除与这个结点有关的冲突边,如果这个node的冲突边>=R,则从剩下的node中选择一个SpillCost最小的node 压入栈;并将pLiveNodeMask对应位置为0,并删除与这个结点有关的冲突边;然后继续执行第3步,直到pLiveNodeMask为0时结束;
4.然后从栈顶(pColorStack)拿出一个node(很显然,从前面的过程来看,先是给range CRF分配寄存器的),根据冲突图,为其按序分配寄存器,如果没有可用的寄存器用来分配,则将这个node标记为spill;这里需要注意的是,对于range CRF,必须是为其分配连续的CRF;对于single CRF来说,选择一个最小的没有分配的CRF即可;
相关文章推荐
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- IPv6无状态地址自动检测
- 经典的基于图着色模型的寄存器分配
- A Top-down Approach to Melody Match in Pitch Contour for Query by Humming
- ORACLE RAC11G 更改IP地址
- RA layer request failed--svn密码造成的另一问题
- 7.3寄存器分配
- RabbitMQ详细文档
- 编译原理之代码生成
- Ukey证书校验流程和使用注意事项
- ukey证书简介
- EJBCA简介
- PKI及Blockchain-Base PKI概述
- 菜鸟学Linux 第025篇笔记 RAID watch
- raid1 raid2 raid5 raid6 raid10的优缺点和做各自raid需要几块硬盘
- Mybaits之逆向工程
- debug.keystore文件不存在解决办法
- JS学习的博客收藏
- java 接口练习题5
- Aquarium Cycling