寄存器分配:图着色算法

背景

在编译器的中间表示中,一般会设定虚拟寄存器有无限多个(方便优化),而真实的物理寄存器是有限的,因而编译器后端在将中间表示翻译成目标指令集的时候会进行寄存器分配,也就是将无限的虚拟寄存器映射到有限的物理寄存器上。例如:

a := c + d
e := a + b
f := e - 1

a + ba的寄存器可以被复用,e - 1e的寄存器可以被复用,因而aef可以分配相同的寄存器。

r1 := r2 + r3
r1 := r1 + r4
r1 := r1 - 1

如果两个两个临时变量t1t2在不同的程序点只有一个是活跃的,则t1t2可以分配相同的物理寄存器。否则,如果t1t2如果同时活跃,则不能分配相同的物理寄存器。

接下来介绍图着色的寄存器分配算法。图着色算法首先要进行活跃分析,得到冲突图,然后通过对冲突图进行着色来解决寄存器分配问题。

活跃分析

变量的活跃分析采用数据流分析的方法,具体可以参阅南京大学的静态分析课程。这里直接给出活跃分析结果:

寄存器冲突图

活跃分析会得到每个程序点同时活跃的临时变量的集合。对于每一个集合(一个程序点)中每一对临时变量形成一条边,并将这些边加入到冲突图中。对程序中每个程序点的集合重复上述过程便形成寄存器冲突图(RIG, register interference graph)。

冲突中,顶点表示临时变量,边用于模拟两个临时变量在相同的程序点同时活跃。冲突图中每两个相连的变量不能分配相同的寄存器,例如ac

寄存器分配的问题就可以转换为冲突图图着色问题。图着色是给图的每个顶点进行着色,并且相连的顶点需要着不同的颜色。图的k可着色表示可以用k个颜色对图进行着色。假设物理寄存器个数为k个,如果RIG是可以k可着色的,则可以使用不多于k个寄存器完成寄存器分配。

例如,上述RIG是4可着色的,则可以使用4个寄存器完成寄存器分配。

图着色算法

图着色问题是一个NP-hard问题,但我们可以采用启发式的思想。假设图G有个节点m,它的邻节点个数少于m,令G‘G-{m},即G去掉节点m和相应的边形成G'。若G'能够k可着色的,那么G也可以。因为将m添加到已经着色的G'时,m的邻节点至多使用了k-1种颜色,那么总能找到一种颜色为m节点着色。因而可以使用这样的一种简化方法:

  • 选择一个邻节点个数小于k的节点t
  • 将节点t放入栈中,并从RIG中删除
  • 重复上述过程直到图中只剩下一个节点

然后给栈上的节点着色:

  • 从栈顶节点开始着色
  • 每次选择一个不同于已经着色的邻节点的颜色进行着色

例如上述的RIG,k=4,着色过程如下:

1)初始状态:

2)去除节点a

3)去除节点d

4)去除节点c

5)去除节点b

6)去除节点e:

7)去除节点f

8)给栈顶节点f着色:

9)给e着色,选择不同于f的颜色:

10)给b着色,选择不同于ef的颜色:

11)给c着色,选择不同于efb的颜色:

12)给d着色,选择不同于efc的颜色,可以选择和b相同的颜色:

13)给a着色,选择不同于fc的颜色,可以选择和e相同的颜色:
在这里插入图片描述

这就完成了RIG的着色,也即寄存器分配。但是,对于上述RIG,如果只有3个物理寄存器,再移除a后,则无法进行下去:
在这里插入图片描述
也就是说启发式的着色方法失败了。此时需要采用寄存器溢出的方法对上述方法进行修正。

溢出

寄存器溢出即在栈上申请一块内存来存放临时变量。例如,将f暂时存放在栈fa上,使用时候再从栈上读取。将f溢出到栈上后代码如下:

这里其实使用不同的名字 f1f2f3替代f更优,因为这可以减少RIG的边的数量,也即较少f活跃的区间:

此时,重新进行活跃分析,结果如下:

可以得到新的RIG图,并重新使用启发式的图着色方法即可完成寄存器分配,结果如下:
在这里插入图片描述

这里如何选择溢出的变量,有一些启发式的方法:

  • 选择有最多冲突的变量
  • 选择定值和使用比较少的变量
  • 避免溢出循环体内的变量

此外,寄存器分配还可以得到一个额外的优化收获,那就是给move指令的源和目的分配相同的物理寄存器,则可以删除该move指令。

本文介绍了比较经典的图着色的寄存器分配算法,此外目前使用比较广的还有线性扫描算法、整数线性规划算法等。LLVM中支持的寄存器分配算法有4种:Basic Register Allocator、Fast Register Allocator、PBQP Register Allocator、Greedy Register Allocator。大家可以去翻阅LLVM代码了解算法细节。

参考

  • https://tai-e.pascal-lab.net/lectures.html
  • https://web.stanford.edu/class/cs143/lectures/lecture16.pdf
  • 现代编译原理 C语言描述(修订版)
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
寄存器分配算法中,着色是一种常见的算法方法,用于为每个程序变量分配寄存器着色算法基于论,将程序中的变量表示为中的节点,寄存器表示为不同颜色的标记。该算法的目标是为每个变量分配一个寄存器,并且在满足寄存器数量限制的前提下,最大化寄存器的利用率。 具体来说,该算法首先将每个变量表示为中的一个节点,并将它们之间的数据依赖关系表示为边。然后,算法会尝试为每个节点着色,即为其分配一个寄存器。如果一个节点已经与其他节点共享了一个寄存器,那么它们必须拥有不同的颜色。 该算法的核心是通过迭代着色的方式,逐步增加已经分配寄存器数量。在每次迭代中,算法会尝试为所有未着色的节点找到一种颜色,使得它们与之前已经着色的节点不冲突。如果找不到这样的颜色,那么算法会将当前迭代中已经着色的节点拆分成多个独立的子集,然后再为每个子集进行颜色分配。这个过程会反复进行,直到所有节点都被着色为止。 着色算法的优点是可以在不需要进行复杂的数据流分析和变量重命名的情况下,为程序变量分配寄存器。但是,该算法的缺点是在某些情况下可能会导致寄存器的利用率不够高,从而影响程序的性能。因此,在实际应用中,通常需要结合其他寄存器分配算法,以达到更好的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值