1 背景介绍
本文档是基于 LLVM 的寄存器分配系列科研笔记第一篇,以一个 C 语言程序为主干介绍 LLVM 在寄存器分配前做的一些主要工作,分析在寄存器分配前期可能的写操作来源,并记录了我在研究 LLVM 后端中 SSA 形式的中间表示时的一些想法(私货)。
2 进入寄存器分配
我们以下述 C 语言源码为例开始本文的说明:
int foo(int a, int b, int e) {
b = a + e;
a = b * e;
e = a * b;
b = a * e;
b = b * e;
return b;
}
根据我的上一篇博文 LLVM SSA 介绍 可知,经过 mem2reg pass 后,最初 Clang 默认生成的 Alloca/Load/Store 形式的 IR 被转化成了完全 SSA 形式的 IR。这时,源变量在 SSA IR 中已经有了许多重命名,如下述代码所示,%add、%mul2、%mul3 都是源程序变量(以下简称源变量)b 的重命名:
define i32 @foo(i32 %a, i32 %b, i32 %e) #0 {
entry:
%add = add nsw i32 %a, %e
%mul = mul nsw i32 %add, %e
%mul1 = mul nsw i32 %mul, %add
%mul2 = mul nsw i32 %mul, %mul1
%mul3 = mul nsw i32 %mul2, %mul1
ret i32 %mul3
}
这个结果是很直观的,SSA IR 中的每一条指令都与源程序中每一条语句一一对应。在此 SSA IR 里,所有重命名都是虚拟寄存器,这些重命名在后面的 MIR 中是以 <%vreg + idx> (%vreg0、%vreg1…) 的虚拟寄存器形式表示的。但是,并不是 SSA IR 中所有的重命名都会在 MIR 中生成相应的<%vreg + idx>。我们可以分析刚刚进入寄存器分配时此程序的 MIR,通过给 llc 添加 -print-after=livevars 选项(给 llc 加上 -help-hidden 选项可以查看 -print-after 支持的所有 pass 名称)可以观察做了活变量分析后的 MIR,此阶段的 MIR 尚未做 PHI 指令消除和二地址转换:
Frame Objects:
fi#-3: size=4, align=4, fixed, at location [SP+12]
fi#-2: size=4, align=4, fixed, at location [SP+8]
fi#-1: size=4, align=4, fixed, at location [SP+4]
BB#0: derived from LLVM BB %entry
%vreg0<def> = MOV32rm <fi#-3>, 1, %noreg, 0, %noreg; mem:LD4[FixedStack-3] GR32:%vreg0
%vreg1&l