Register Allocation
Register allocation (Graph coloring) is an NP-complete problem
Coloring By Simplification
Build & Simplify
首先画图,每个结点代表一个变量,一条边代表他们有冲突(不能被分配同一个 reg)
然后对图进行化简,记我们有 k 个 reg,如果一个结点的邻居数量少于 k,就删掉这个结点
即,一个结点的邻居数量少于 K,那么我染色完它的邻居,一定能留一个寄存器给它
然后用 stack 按序储存删掉的结点,如此不断重复,直到没结点了,或剩下的都是 significant degree 的结点(邻居数 \(\ge\) k)
Spill
只剩下 significant degree 的结点时,我们选择一些结点储存到 mem 而非 reg 里,这些节点与其它结点不再冲突,然后我们会将其删除并 push 进 stack
spill 后继续 Simplify,如此循环,直到生成空图
下图中 spill 了 b
Select
从 stack pop 结点,一个一个染色,重建冲突图
乐观染色:这里的 b 虽然被设置为了 Potential Spill,此时已有两个邻居染色,但这里要染它时是由空闲颜色的,所以可以染色,其不是 actual spill
每次发现 actual spill 就得对原程序增加相应的内存访问指令:
- fetch them from memory just before each use
- and store them back after each def.
然后由于程序被重构,我们又可以构建新的冲突图,在重复上面的步骤
Coalescing
如果两个结点是虚线(合并边)连接的,即这其中一个变量复制于另一个变量,且二者没有冲突边,那么就可以将他俩合并
但是新节点的邻居可能变多,所以我们采取保守合并,保证合并后的图不会变成非 k-colorable graph
有两种具体的实现方式
- Briggs
- 新节点应当有少于 K 个邻居是 significant degree 的
- 确保 simplify 后能剩下少于 K 个邻居,于是新节点也可以被 simplify 掉
- George
- 对于合并前的 A 和 B,需满足其中一个条件:
- A 所有邻居都与 B 有冲突
- A 所有邻居都是 insignificant degree
- 符合第一个条件的边不会被重复增加至新节点
- 符合第二个条件的边能被优化
- 于是新节点的边被压制在 B 合并前的数量
- 对于合并前的 A 和 B,需满足其中一个条件:
注意:
- build 时需要注意 MOVE 并画出合并边
- simplify 先只处理非 MOVE 相关结点
- Coalescing 后再 simplify,二者循环直至去除所有合并边
- 如果没法去除所有合并边,进入 freeze 阶段,找一个 low degree 的 MOVE 相关结点,我们 freeze 这个 MOVE 操作,将虚线变为实线,放弃合并
Precolored Nodes
预着色结点与其它所有结点冲突,且无法被删除,无法被 spill
我们的目标就不是生成空图,而是生成只剩下 Precolored Nodes 的图
Chapter11.pdf 75有例子