MachineLateInstrsCleanup

该 Pass 用于在机器代码阶段删除完全相同的延迟(late)指令,特别是移除由重计算(rematerialization)或框架索引消除后产生的冗余立即数或地址加载指令,从而精简最终生成的机器指令。

  • “完全相同的延迟指令”“延迟指令” 指的是这些指令不是在最初阶段生成的,而是在寄存器分配之后FrameIndex 消解阶段才生成的“后期指令(late instructions)”
  • Rematerialization(重构/重建/重计算)是 LLVM 在 寄存器分配时的一种策略,“与其从栈中 reload 某个值,不如干脆重新 计算/加载 这个值”。目的是:
    • 避免使用太多物理寄存器;
    • 减少 spill & reload(存回 + 加载);
    • 利用立即数或简单可重构值的 cheap cost。

举个例子,假设在两个基本块中都有同一地址计算指令:

# 基本块 A
t0 = LEA addr(base, offset)
...

# 基本块 B(与 A 共享出口)
t1 = LEA addr(base, offset)
...

如果 B 的 t1 定义与 A 的 t0 完全相同,且在 B 的前驱块 A 中已有定义,那么 B 中的 t1 可以被移除,直接复用 A 的 t0,从而减少冗余加载。这就是该 Pass 的主要优化场景。所以如此简单的逻辑也是该 Pass 不需要调参,也只有两百行代码的原因。


  1. 初始化阶段
    在 run(MachineFunction &MF) 中,通过 ReversePostOrderTraversal 遍历所有基本块,并重置 RegDefs 与 RegKills 两个向量映射表,用于追踪每个基本块中每个寄存器的最新定义或清除标记。
  2. 识别候选指令
    isCandidate 判断一条 MachineInstr* MI,是否是“延迟加载”类型的简单指令:
    • 无副作用、无内存访问
    • 仅定义一个寄存器,无其他非 FrameReg 寄存器使用
    • 操作数全部是立即数、全局引用或常量
      若符合即返回 true 并标记定义的寄存器。
  3. 数据重用判断
    在 processBlock 内部,对每条指令:
  • 若为候选,检查前驱块中是否有相同定义(用 RegDefs.hasIdentical)
  • 若匹配,则执行 removeRedundantDef,在当前块移除此指令,记录删除数据量,同时调用 clearKillsForDef 清除前导块中任何对该寄存器的 kill 标记,确保可继续复用旧定义。
  1. 维护寄存器状态
    • 每遇其他指令修改该寄存器或其子寄存器时,都会清除 RegDefs/RegKills 中的缓存条目,确保不会误用过期定义。
    • clearKillsForDef 遍历前驱路径,处理多个 kill 标记,并为必要时添加 live-in 标记,从而维持跨块数据正确性与可达性
  2. 最终效果
    遍历完成后,该 Pass 返回是否发生了指令删除,供后续 Pass 调用优化或再次调度