BranchRelaxation

LLVM 后端的 BranchRelaxationPass 是一个专门用于处理跳转指令距离限制的 MachineFunctionPass。它的主要作用是确保生成的汇编代码中,所有的跳转指令(如 b、bl、beq 等)都能正确跳转到目标位置,避免因跳转距离超出指令编码范围而导致的错误。
原因:在某些目标架构(如 AArch64、RISC-V 等)中,跳转指令的目标地址是通过指令中的偏移量编码的,而这个偏移量有一定的范围限制。例如:

  • AArch64 的 B 指令支持 ±128MB 的跳转范围。
  • RISC-V 的 JAL 指令支持 ±1MB 的跳转范围。
    当超出限制时,就需要进行 BranchRelaxtion 了。
    其 Pass 入口在文件尾部:
PreservedAnalyses  
BranchRelaxationPass::run(MachineFunction &MF,  
                          MachineFunctionAnalysisManager &MFAM) {  
  if (!BranchRelaxation().run(MF))  
    return PreservedAnalyses::all();  
  
  return getMachineFunctionPassPreservedAnalyses();  
}  
  
bool BranchRelaxation::run(MachineFunction &mf) {  
  MF = &mf;  
  
  LLVM_DEBUG(dbgs() << "***** BranchRelaxation *****\n");  
  
  const TargetSubtargetInfo &ST = MF->getSubtarget();  
  TII = ST.getInstrInfo();  
  TM = &MF->getTarget();  
  
  TRI = ST.getRegisterInfo();  
  if (TRI->trackLivenessAfterRegAlloc(*MF))  
    RS.reset(new RegScavenger());  
  
  // Renumber all of the machine basic blocks in the function, guaranteeing that  
  // the numbers agree with the position of the block in the function.  MF->RenumberBlocks();  
  MF->RenumberBlocks();
  
  // Do the initial scan of the function, building up information about the  
  // sizes of each block.  scanFunction();  
  scanFunction();
  
  LLVM_DEBUG(dbgs() << "  Basic blocks before relaxation\n"; dumpBBs(););  
  
  bool MadeChange = false;  
  while (relaxBranchInstructions())  
    MadeChange = true;  
  
  // After a while, this might be made debug-only, but it is not expensive.  
  verify();  
  
  LLVM_DEBUG(dbgs() << "  Basic blocks after relaxation\n\n"; dumpBBs());  
  
  BlockInfo.clear();  
  RelaxedUnconditionals.clear();  
  
  return MadeChange;  
}

不依赖任何启发式分析因素,然后第二入口为 relaxBranchInstructions,并且理论复杂度是O(n)\mathcal O (n) 的。
我们可以看到,唯一前置工作就是需要为基本块编号,然后扫描记录他们的大小。

然后其主要处理逻辑 relaxBranchInstructions 的原理也较为简单,就是遍历所有的 MBB,然后

  • 处理无条件跳转,如果跳转距离太远,且之前没处理过,则插入跳板或者间接跳转方式放宽"跳转"。
  • 处理条件跳转,同上,对部分分支处理。

然后需要注意的就是每次跳转结构修改后需要重新计算偏移。