Hardware loops

这个 pass 有 608 行

这个 HardwareLoops Pass 的主要作用,是在 LLVM 编译流程中把“普通的”软件循环转换成底层目标机器所支持的 硬件循环
正好来区分一下软件循环和硬件循环:

  • 软件循环例子:
    MOV   R0, #0       ; i = 0
    MOV   R1, #n       ; R1 保存 n
loop_soft:
    CMP   R0, R1       ; 比较 i 和 n
    BGE   end_soft     ; 若 i >= n,跳出
    ; ——— 循环体 ———
    ADD   R0, R0, #1   ; i = i + 1
    B     loop_soft    ; 跳回比较
end_soft:
  • 硬件循环(hardware loop)
    • 原理:利用处理器提供的==专门指令内建硬件计数器来管理循环迭代次数==。编译器在循环外一次性设置好迭代次数,循环内部不用再做显式比较和计数器更新,硬件在每次迭代结束时自动递减并判断是否继续。
    • 迭代逻辑放到专门的微架构单元里做,大幅减少指令数量和分支开销,提高指令流的连续性和能效;尤其适合迭代次数已知或可推导的内层小环路。
  • 例子:LLVM IR 中的硬件循环 Intrinsics
    假设循环迭代次数存于 %n 中,LLVM 会插入两条 Intrinsic:
; 在循环外设置迭代次数(+1 用于测试退出条件)
%cnt = call i32 @llvm.set_loop_iterations.i32(i32 %n_plus_one)

; 无条件跳到 loop.header
br label %loop.header

loop.header:
  ; loop body…
  ; 在退出分支处,硬件自动递减并返回新计数
  %newcnt = call i32 @llvm.loop_decrement.i32(i32 %cnt)
  ; 当 newcnt != 0 时,跳回 header;否则退出
  %cond   = icmp ne i32 %newcnt, 0
  br i1 %cond, label %loop.header, label %loop.exit

loop.exit:
  ; 后续代码…

在真正支持硬件环路指令的处理器(如一些 DSP、Cortex-M 的增扩指令集、TI C6000 VLIW DSP 等),这两条 Intrinsic 会被下变换成诸如:

    LOOP   #N, loop_body   ; 一条指令同时设置计数并跳到 loop_body
loop_body:; 循环体
    ; 硬件自动在内部递减,决定是否回到 loop_body

该 Pass 是可以调参的,但是这些参数是供开发者使用的!测试

static cl::opt<bool>  
ForceHardwareLoops("force-hardware-loops", cl::Hidden, cl::init(false),  
                   cl::desc("Force hardware loops intrinsics to be inserted"));  
  
static cl::opt<bool>  
ForceHardwareLoopPHI(  
  "force-hardware-loop-phi", cl::Hidden, cl::init(false),  
  cl::desc("Force hardware loop counter to be updated through a phi"));  
  
static cl::opt<bool>  
ForceNestedLoop("force-nested-hardware-loop", cl::Hidden, cl::init(false),  
                cl::desc("Force allowance of nested hardware loops"));  
  
static cl::opt<unsigned>  
LoopDecrement("hardware-loop-decrement", cl::Hidden, cl::init(1),  
            cl::desc("Set the loop decrement value"));  
  
static cl::opt<unsigned>  
CounterBitWidth("hardware-loop-counter-bitwidth", cl::Hidden, cl::init(32),  
                cl::desc("Set the loop counter bitwidth"));  
  
static cl::opt<bool>  
ForceGuardLoopEntry(  
  "force-hardware-loop-guard", cl::Hidden, cl::init(false),  
  cl::desc("Force generation of loop guard intrinsic"));  
  
STATISTIC(NumHWLoops, "Number of loops converted to hardware loops");

该 Pass 依赖很多分析:

  • LoopAnalysis
  • ScalarEvolutionAnalysis
  • DominatorTreeAnalysis
  • TargetIRAnalysis
  • TargetLibraryAnalysis
  • AssumptionAnalysis
  • OptimizationRemarkEmitterAnalysis
  • DataLayout
    入口为:
bool HardwareLoopsImpl::run(Function &F) {  
  LLVMContext &Ctx = F.getContext();  
  for (Loop *L : LI)  
    if (L->isOutermost())  
      TryConvertLoop(L, Ctx);  
  return MadeChange;  
}

核心逻辑在:

bool HardwareLoopsImpl::TryConvertLoop(Loop *L, LLVMContext &Ctx) {
  // 1. 先处理所有嵌套子循环
  bool AnyChanged = false;
  for (Loop *SL : *L)
    AnyChanged |= TryConvertLoop(SL, Ctx);
  if (AnyChanged) {
    // 如果有任何一个内层循环被转换过(MadeChange = true),
    // 而目标又不允许嵌套硬件环路,就报错并直接返回 true,阻止对父循环的进一步尝试。
    reportHWLoopFailure("nested hardware-loops not supported", "HWLoopNested",
                        ORE, L);
    return true; // 停止在这一层继续查找
  }

  // 2. 调试打印
  LLVM_DEBUG(dbgs() << "HWLoops: Loop " << L->getHeader()->getName() << "\n");

  // 3. 构造针对这个 Loop 的分析信息对象
  HardwareLoopInfo HWLoopInfo(L);

  //    3.1 如果结构太复杂(不可约、CFG 奇怪等),就算了
  if (!HWLoopInfo.canAnalyze(LI)) {
    reportHWLoopFailure("cannot analyze loop, irreducible control flow",
                        "HWLoopCannotAnalyze", ORE, L);
    return false;
  }

  //    3.2 如果没强制插入,又被后端 TTI 评估为“不盈利”,就跳过
  if (!Opts.Force &&
      !TTI.isHardwareLoopProfitable(L, SE, AC, TLI, HWLoopInfo)) {
    reportHWLoopFailure("it's not profitable to create a hardware-loop",
                        "HWLoopNotProfitable", ORE, L);
    return false;
  }

  // 4. 根据命令行或测试选项,覆盖默认的计数器宽度和递减值
  if (Opts.Bitwidth.has_value()) {
    HWLoopInfo.CountType =
      IntegerType::get(Ctx, Opts.Bitwidth.value());
  }
  if (Opts.Decrement.has_value()) {
    HWLoopInfo.LoopDecrement =
      ConstantInt::get(HWLoopInfo.CountType,
                       Opts.Decrement.value());
  }

  // 5. 真正尝试把这个 Loop 转成硬件环路
  MadeChange |= TryConvertLoop(HWLoopInfo);

  // 6. 返回值——告诉调用者:
  //    • 如果已做过修改(MadeChange == true),并且此循环不允许嵌套(IsNestingLegal == false)且又没强制嵌套,
  //      则返回 true,表示“搜索应当停止”(上层不要再处理这个父循环)。
  //    • 其它情况返回 false,表示“本轮尝试结束但可以继续往上级或兄弟循环搜”。
  return MadeChange && (!HWLoopInfo.IsNestingLegal && !Opts.ForceNested);
}

同时也有代码函数: TTI.isHardwareLoopProfitable