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 == VirtRegMap::NO_STACK_SLOT) { StackSlot = VRM.
assignVirt2StackSlot(Original); StackInt = &LSS.getOrCreateInterval(StackSlot,
MRI.getRegClass(Original)); StackInt->getNextValue(SlotIndex(), LSS.
getVNInfoAllocator()); } else StackInt = &LSS.getInterval(StackSlot); if (
Original!= Edit->getReg()) VRM.assignVirt2StackSlot(Edit->getReg(), StackSlot);
assert(StackInt->getNumValNums() == 1 && "Bad stack interval values"); for (
unsigned Reg : RegsToSpill) StackInt->MergeSegmentsInAsValue(LIS.getInterval(Reg
), StackInt->getValNumInfo(0)); DEBUG(dbgs() << "Merged spilled regs: " << *
StackInt<< '\n'); // Spill around uses of all RegsToSpill. for (unsigned Reg :
RegsToSpill) spillAroundUses(Reg); // Hoisted spills may cause dead code. if (!
DeadDefs.empty()) { DEBUG(dbgs() << "Eliminating " << DeadDefs.size() << " dead
defs\n"); Edit->eliminateDeadDefs(DeadDefs, RegsToSpill); } // Finally delete
the SnippetCopies. for (unsigned Reg : RegsToSpill) { for (MachineRegisterInfo::
reg_instr_iterator RI= MRI.reg_instr_begin(Reg), E = MRI.reg_instr_end(); RI !=
E; ) { MachineInstr *MI = &*(RI++); assert(SnippetCopies.count(MI) &&
"Remaining use wasn't a snippet copy"); // FIXME: Do this with a LiveRangeEdit
callback. LIS.RemoveMachineInstrFromMaps(MI); MI->eraseFromParent(); } } //
Delete all spilled registers. for (unsigned Reg : RegsToSpill) Edit->
eraseVirtReg(Reg); }
分析:
第 4-9 行:这是个条件语句,条件为真代表当前虚拟寄存器对应的栈槽还未分配,这种情况出现在当前虚拟寄存器没有被 split
时。这时就要给该虚拟寄存器分配一个栈槽(第4行),同时创造一个 StackInt(第5、6行)。否则,条件为假时直接从对应的栈槽里取出对应的 StackInt。
第 11、12 行:这也是个条件语句,条件为真代表当前虚拟寄存器是 split 后形成的新的虚拟寄存器,因此要把他们和 StackSlot 对应起来。
第 14 行:StackInt->getNumValNums() 获取的是定义点的个数。由于本 LiveRange 是 spill
的,因此只能有一个定义点(0@x)。
第 21、22 行:给代码中该虚拟寄存器的所有 use 插入 spill 代码。
第 25-28 行:用于删除死定义。
第 31-41 行:与 snippet 有关,未知。
第 42、45 行:原有的虚拟寄存器 spill 到栈槽中,以后的 MIR 中就使用栈槽来代替该虚拟寄存器。因此,可以把原来的虚拟寄存器都删掉。
spillAroundUses 函数
/// spillAroundUses - insert spill code around each use of Reg. void
InlineSpiller::spillAroundUses(unsigned Reg) { DEBUG(dbgs() << "spillAroundUses
" << PrintReg(Reg) << '\n'); LiveInterval &OldLI = LIS.getInterval(Reg); //
Iterate over instructions using Reg. for (MachineRegisterInfo::
reg_bundle_iterator RegI= MRI.reg_bundle_begin(Reg), E = MRI.reg_bundle_end();
RegI!= E; ) { MachineInstr *MI = &*(RegI++); // Debug values are not allowed to
affect codegen. if (MI->isDebugValue()) { // Modify DBG_VALUE now that the
value is in a spill slot. bool IsIndirect = MI->isIndirectDebugValue(); uint64_t
Offset= IsIndirect ? MI->getOperand(1).getImm() : 0; const MDNode *Var = MI->
getDebugVariable(); const MDNode *Expr = MI->getDebugExpression(); DebugLoc DL =
MI->getDebugLoc(); DEBUG(dbgs() << "Modifying debug info due to spill:" << "\t"
<< *MI); MachineBasicBlock *MBB = MI->getParent(); assert(cast<DILocalVariable>(
Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree");
BuildMI(*MBB, MBB->erase(MI), DL, TII.get(TargetOpcode::DBG_VALUE)) .
addFrameIndex(StackSlot) .addImm(Offset) .addMetadata(Var) .addMetadata(Expr);
continue; } // Ignore copies to/from snippets. We'll delete them. if (
SnippetCopies.count(MI)) continue; // Stack slot accesses may coalesce away. if
(coalesceStackAccess(MI, Reg)) continue; // Analyze instruction. SmallVector<std
::pair<MachineInstr*, unsigned>, 8> Ops; MIBundleOperands::VirtRegInfo RI =
MIBundleOperands(MI).analyzeVirtReg(Reg, &Ops); // Find the slot index where
this instruction reads and writes OldLI. // This is usually the def slot,
except for tied early clobbers. SlotIndex Idx = LIS.getInstructionIndex(MI).
getRegSlot(); if (VNInfo *VNI = OldLI.getVNInfoAt(Idx.getRegSlot(true))) if (
SlotIndex::isSameInstr(Idx, VNI->def)) Idx = VNI->def; // Check for a sibling
copy. unsigned SibReg = isFullCopyOf(MI, Reg); if (SibReg && isSibling(SibReg))
{ // This may actually be a copy between snippets. if (isRegToSpill(SibReg)) {
DEBUG(dbgs() << "Found new snippet copy: " << *MI); SnippetCopies.insert(MI);
continue; } if (RI.Writes) { // Hoist the spill of a sib-reg copy. if (
hoistSpill(OldLI, MI)) { // This COPY is now dead, the value is already in the
stack slot. MI->getOperand(0).setIsDead(); DeadDefs.push_back(MI); continue; } }
else { // This is a reload for a sib-reg copy. Drop spills downstream.
LiveInterval&SibLI = LIS.getInterval(SibReg); eliminateRedundantSpills(SibLI,
SibLI.getVNInfoAt(Idx)); // The COPY will fold to a reload below. } } //
Attempt to fold memory ops. if (foldMemoryOperand(Ops)) continue; // Create a
new virtual register for spill/fill. // FIXME: Infer regclass from instruction
alone. unsigned NewVReg = Edit->createFrom(Reg); if (RI.Reads) insertReload(
NewVReg, Idx, MI); // Rewrite instruction operands. bool hasLiveDef = false; for
(const auto &OpPair : Ops) { MachineOperand &MO = OpPair.first->getOperand(
OpPair.second); MO.setReg(NewVReg); if (MO.isUse()) { if (!OpPair.first->
isRegTiedToDefOperand(OpPair.second)) MO.setIsKill(); } else { if (!MO.isDead())
hasLiveDef= true; } } DEBUG(dbgs() << "\trewrite: " << Idx << '\t' << *MI <<
'\n'); // FIXME: Use a second vreg if instruction has no tied ops. if (RI.Writes
) if (hasLiveDef) insertSpill(NewVReg, true, MI); } }
第 4 行:获取要 spill 的寄存器(Reg)的活跃区间,后面会用上。
第 7-102 行:遍历所有该虚拟寄存器的 use 和 def ,分析每一条指令。要注意,这个循环里的 RegI 代表虚拟寄存器的每一次 use 或者
def。如果一条机器指令里某个虚拟寄存器同时进行了 use 和 def,那么该机器指令会遍历两次。
第 10 行:获取 use/def 所在的 MachineInstr。注意,这里先获取了 use/def 所在的机器指令,再迭代加 1 到下一次
use/def。
第 13- 30 行:处理机器指令是 DEBUG_VALUE 的情形,也就是调试信息。
第 33、34 行:SnippetCopies 记录了 snippet 相关的 copy 指令,这种 copy 指令操作数 spill
后均来自于同一个栈槽,因此可以直接把这些指令删除。

技术
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:766591547
关注微信