EarlyIfConversion

这是一个比较重要的 Pass,代码量 1300 多行。Early If-Conversion 旨在 在指令选择(SelectionDAG)后、寄存器分配(RegAlloc)前,把一些简单的条件分支转换成顺序执行==并用条件移动(cmov 或者其他选择指令)来完成,减少分支指令,从而降低 mispredict 带来的性能损失。==
概念好懂,其支持两种控制流形状:

    Head
    /  \
 TBB    FBB
    \  /
    Tail
# Head 块末尾有一个单分支跳转到 TBB/FBB,再汇合到 Tail。
# Pass 会把 TBB 或 FBB 中的指令按需合并到 Head 中,并在原来 Tail 中的 φ 节点处插入选择指令。
     Head
    /    \
  TBB    FBB
   \      /
    \    /
     Tail
# 类似三角形,但两条分支上都有指令需要合并。
# 将两边的指令都移动到 Head,并同样在 Tail 处用选择指令合并值。

下面可以看一个具体的例子:
ifconv 前

    ; Compare eax 和 ebx
    cmp   eax, ebx
    je    .Ltrue

    ; False path: 计算 sum = eax + ebx
    lea   ecx, [eax + ebx]
    jmp   .Lend

.Ltrue:
    ; True path: 计算 diff = eax - ebx
    lea   ecx, [eax - ebx]

.Lend:
    ; 此后使用 ecx,原本依赖分支结果

ifconv 后:

; 先顺序执行两条路径中的计算,推测性执行
lea   ecx, [eax + ebx]   ; false result (sum)
lea   edx, [eax - ebx]   ; true  result (diff)

cmp   eax, ebx
cmove ecx, edx           ; 如果 ZF=1 (eax==ebx),ecx ← edx (diff)

; 之后直接使用 ecx,无需分支
  1. Speculate:先执行两条路径的指令,分别把“假”和“真”结果放到不同寄存器(这里是 ecx/edx)。
  2. Select:根据条件标志用一条 cmov(conditional move)在 ecx 中挑选最终值。
  3. 消除分支:不再需要 je/jmp 跳转,避免 mispredict 惩罚。
    该 Pass 是可调参数和携带统计信息的 Pass:
// Absolute maximum number of instructions allowed per speculated block.  
// This bypasses all other heuristics, so it should be set fairly high.  
// 最大允许每个 speculated block(推测执行块)包含的指令数。如果遇到超过该阈值的 basic block,就跳过 if-conversion。
static cl::opt<unsigned>  
BlockInstrLimit("early-ifcvt-limit", cl::init(30), cl::Hidden,  
  cl::desc("Maximum number of instructions per speculated block."));  
  
// “压测模式”:关闭所有启发式判断,对遇到的每个可转换结构都强行尝试 if-conversion。主要用于回归测试或功能验证,不建议在常规编译中打开。
// Stress testing mode - disable heuristics.  
static cl::opt<bool> Stress("stress-early-ifcvt", cl::Hidden,  
  cl::desc("Turn all knobs to 11"));  
  
STATISTIC(NumDiamondsSeen,  "Number of diamonds");  
STATISTIC(NumDiamondsConv,  "Number of diamonds converted");  
STATISTIC(NumTrianglesSeen, "Number of triangles");  
STATISTIC(NumTrianglesConv, "Number of triangles converted");

再来看一下代码:
核心是:

bool EarlyIfConverter::tryConvertIf(MachineBasicBlock *MBB) {
  bool Changed = false;
  while (IfConv.canConvertIf(MBB) && shouldConvertIf()) {
    // 1. 清理之前的分析状态(方便多次尝试)
    invalidateTraces();

    // 2. 真正做转换,将被移除的分支块记录到 RemoveBlocks
    SmallVector<MachineBasicBlock *, 4> RemoveBlocks;
    IfConv.convertIf(RemoveBlocks);
    Changed = true;

    // 3. 更新支配树(Dominator Tree)
    updateDomTree(DomTree, IfConv, RemoveBlocks);

    // 4. 从函数中删除已被合并的块
    for (MachineBasicBlock *B : RemoveBlocks)
      B->eraseFromParent();

    // 5. 更新 LoopInfo,移除已经消失的块
    updateLoops(Loops, RemoveBlocks);
  }
  return Changed;
}

所以这个也是一个迭代的 Pass。