图着色基本原理
为了将实际寄存器分配给虚拟寄存器,需要得知哪两个虚拟寄存器不能使用同一个实际寄存器。由虚拟寄存器构成图的节点,"不能使用同一个实际寄存器"的关系构成了图的边(无向图),于是寄存器分配就转化成了在这张图上使用平台提供的寄存器数来进行着色(假定图着色的定义大家都已经理解了)。
两个虚拟寄存器不能使用同一个实际寄存器的条件是存在一条语句,如果一个虚拟寄存器被定值,而另一个虚拟寄存器活跃。
下面以这个函数为例讲解:
经过指令选择我们会得到(有一定修改,且省略了prologue和epilogue的部分,后面会讲解这两个部分):
注:
以_R开头的都是虚拟寄存器,它们的计数从32开始,这样就可以用数值范围来区分虚拟寄存器和机器寄存器了(也许这在rust里算一个anti-pattern,但是至少在这里我觉得还是比较方便的)。
这里有机器寄存器$zero和$v0出现,$zero是指令选择中的一个小优化,可以不用管它;$v0在mips调用约定中用来存放返回值,这在"预着色节点"会详细说明
第2,3行看起来有点多余,这是因为指令选择中的优化还是做的太少了,这种冗余可以用窥孔优化来消除,只是我们并没有实现窥孔优化
从tac的流图,生成带有虚拟寄存器的汇编代码后,流图信息得到保留,无需再次划分基本块,但是活跃变量分析需要以汇编代码为对象进行,这与之前以tac为对象进行的活跃变量分析是类似的,下面直接给出最终结果,注释里的是本条语句的集合:
注:其实$zero也会被划到集合,但它与寄存器分配无关,就不考虑了;$v0虽然作为传递函数返回值使用,但是也可以作为通用寄存器参与一般的计算,最后$v0 活跃的原因是特殊处理了的函数的返回,在返回语句处将容纳返回值的$v0标记为活跃。
构建干涉图,一个合法的图着色如下:
粉色对应$v0,灰色对应$a0,蓝色对应$a1,这样即得到了分配完成的程序:
显然粉色对应于$v0是强制的,不过灰色和蓝色对应的寄存器也不是任意选择的,而是利用了后面提到的干涉图节点合并的技术,将函数传参用的$a0和$a1与虚拟寄存器进行了节点合并。
Last updated