MachineCSE

MachineCSE 是 LLVM 后端用于在 SSA 机器指令级别进行 公共子表达式消除(CSE) 的优化 Pass,旨在通过识别重复的计算并替换为已有结果,从而减少指令数、降低寄存器/内存压力。
这个 Pass 的概念也非常简单,对应到中端,就是普通CSE。
该 Pass 将近 1000 行,可以使用 -aggressive-machine-cse 强制忽略收益判断,直接执行 CSE 操作。
该 Pass 依赖:

  • MachineDominatorTreeAnalysis
  • MachineBlockFrequencyAnalysis
    主处理函数为:
bool MachineCSEImpl::run(MachineFunction &MF) {  
  TII = MF.getSubtarget().getInstrInfo();  
  TRI = MF.getSubtarget().getRegisterInfo();  
  MRI = &MF.getRegInfo();  
  LookAheadLimit = TII->getMachineCSELookAheadLimit();  
  bool ChangedPRE, ChangedCSE;  
  ChangedPRE = PerformSimplePRE(DT); // 尝试将部分冗余表达式提前提升为全冗余,从而可以被后续的 CSE 消除。
  ChangedCSE = PerformCSE(DT->getRootNode()); // 真正公共子表达式消除
  releaseMemory();  
  return ChangedPRE || ChangedCSE;  
}

下面讲一下他的代价函数 isProfitableToCSE


寄存器压力判断(主要):

  • 如果 CSReg 的使用完全包含 Reg 的使用,不会增加寄存器活跃区间。
  • 但如果使用数量过多(大于 csuses-threshold),为保守起见,放弃优化。
    **启发式 #1 :对“便宜”指令(如 mov),如果其定义不在当前 BB 或直接前驱,不要做 CSE(担心跨块延长 live range):
if (TII->isAsCheapAsAMove(*MI)) {
  if (CSBB != BB && !CSBB->isSuccessor(BB))
    return false;
}

启发式 #2 :如果表达式无虚拟寄存器使用,且其使用只是复制(copy), 不要做 CSE:

if (!HasVRegUse && all uses are copies)
  return false;

启发式 #3:如果 CSReg 的使用出现在 PHI 节点中,且不在当前 BB,不做 CSE,防止值在不同路径被滥用。