LLVM Spiller
spill 函数
void InlineSpiller::spill(LiveRangeEdit &edit) {
++NumSpilledRanges;
Edit = &edit;
assert(!TargetRegisterInfo::isStackSlot(edit.getReg()) && "Trying to spill a stack slot.");
// Share a stack slot among all descendants of Original.
Original = VRM.getOriginal(edit.getReg());
StackSlot = VRM.getStackSlot(Original);
StackInt = nullptr;
DEBUG(dbgs() << "Inline spilling "
<< TRI.getRegClassName(MRI.getRegClass(edit.getReg()))
<< ':' << edit.getParent()
<< "\nFrom original " << PrintReg(Original) << '\n');
assert(edit.getParent().isSpillable() &&
"Attempting to spill already spilled value.");
assert(DeadDefs.empty() && "Previous spill didn't remove dead defs");
collectRegsToSpill();
analyzeSiblingValues();
reMaterializeAll();
// Remat may handle everything.
if (!RegsToSpill.empty())
spillAll();
Edit->calculateRegClassAndHint(MF, Loops, MBFI);
}
分析:
函数参数:LiveRangeEdit 类用于表示对虚拟寄存器进行 spill 或是 split 时发生的改变。LiveRangeEdit 类有两个成员对象要说明一下:1. LiveInterval *Parent,这个对象表示原始的父虚拟寄存器所在的活跃区间;2. SmallVectorImpl< unsigned > &NewRegs,当父虚拟寄存器的生命期发生 split 时会产生新的子生命期,也就是新的子虚拟寄存器。
第 2 行:NumSpilledRanges 表示溢出的生命期数目,不管后面是否实际插入了 spill 代码,还是通过别的手段避免插入 spill 代码,该生命期都已经溢出了,要加1。
第 4 行:edit.getReg() 获得的是此 LiveRangeEdit 对象的父虚拟寄存器,如果判断出它本身就是栈槽,显然不需要再进行spill,直接报错。
第 6 行:获得初始的虚拟寄存器。如果该父虚拟寄存器没有被 split 过,Original 就是它本身。如果该父虚拟寄存器被 split 过,那么 Original 则是它被 split 前的虚拟寄存器。
第 7 行:返回本 LiveRangeEdit 类对象的虚拟寄存器所在的栈槽。由于 Original 始终是最开始的虚拟寄存器,显然对于所有从 Original 分割出去的虚拟寄存器而言,都映射到了同一个栈槽里。
第 8 行:StackInt 表示当前栈槽对应的活跃区间(StackSlot Interval)。
第 18 行:本函数用于处理 snippet,snippet 是仅仅只有一次实际 use 的生命期。
第 19、20 行:处理可以通过 rematerialization 过程避免 spill 的情况。
第 23 行:RegsToSpill 在 collectRegsToSpill 中收集了数据(snippets + 本身),如果没有snippets,RegsToSpill 仅有一个数据,即父虚拟寄存器。
spillAll 函数
/// spillAll - Spill all registers remaining after rematerialization.
void InlineSpiller::spillAll() {
// Update LiveStacks now that we are committed to spilling.
if (StackSlot == VirtR