GlobalMergeFunctions

600 行

GlobalMergeFunc 是 LLVM 中的一个 ModulePass,旨在识别并合并在模块或跨模块范围内功能等价或高度相似的函数,从而去重并减小生成代码体积。它通过结构化哈希算法对函数进行比较,忽略可参数化的常量操作数,然后为差异化的常量生成额外参数,最终创建单一的合并函数并用 thunk 保持原始接口不变。该过程既支持本地合并,也能借助 CGData 在 LTO 阶段执行跨模块的乐观合并。

  • thunk:Thunk(有时也直译为“馅饼”或“跳板”)是一个轻量级的包装函数,它本身不做实际业务逻辑,而是把调用“转发”(forward)给另一个目标函数。
  • 结构化 hash:结构化哈希是一种对函数(或指令序列)结构进行“指纹”提取的算法,它考虑==指令的种类、控制流、操作数==类型等结构信息,而不是简单地对字节码或源代码做哈希。

STATISTIC(NumMergedFunctions,  
          "Number of functions that are actually merged using function hash");  
STATISTIC(NumAnalyzedModules, "Number of modules that are analyzed");  
STATISTIC(NumAnalyzedFunctions, "Number of functions that are analyzed");  
STATISTIC(NumEligibleFunctions, "Number of functions that are eligible");

核心函数为:

bool GlobalMergeFunc::run(Module &M) {  
  initializeMergerMode(M);  
  
  const StableFunctionMap *FuncMap;  
  if (MergerMode == HashFunctionMode::UsingHashFunction) {  
    // Use the prior CG data to optimistically create global merge candidates.  
    FuncMap = cgdata::getStableFunctionMap();  
  } else {  
    analyze(M);  
    // Emit the local function map to the custom section, __llvm_merge before  
    // finalizing it.    if (MergerMode == HashFunctionMode::BuildingHashFuncion)  
      emitFunctionMap(M);  
    LocalFunctionMap->finalize();  
    FuncMap = LocalFunctionMap.get();  
  }  
  
  return merge(M, FuncMap);  
}

核心处理函数 merge 主要承担了以下职能:

  1. 收集同哈希的函数
    根据前面 analyze 阶段或 CGData 提供的哈希映射,把模块中所有结构哈希相同的函数归到同一组(HashToFuncs)。
  2. 过滤与匹配
    对每组函数,先用指令数和可忽略常量位置做快速过滤,然后再用 checkConstHashCompatible 和 checkConstLocationCompatible 做更严格的校验,确保这些函数在逻辑上可合并。
  3. 计算常量参数化信息
    一旦确认了一组可合并的函数,就调用 computeParamInfo,根据各函数在相同指令位置上不同的常量,生成一组“参数化位置”。
  4. 创建合并函数
    对每个待合并函数,调用 createMergedFunction,生成那个带 .Tgm 后缀的新函数(把原函数体整体搬过去,并在最后追加那些常量作为新参数)。
  5. 生成 Thunk
    再调用 createThunk,把原函数体改成一个简单的“包装”——把原来的参数和常量参数一并传给新函数,然后转发返回。
  6. 统计与返回
    每合并一次就更新 NumMergedFunctions 统计,最终返回是否有任何改动。
    因此,merge 函数就是将“结构哈希分桶 + 常量参数化 + 合并 + Thunk 转发”串联起来的主流程。其余几个函数(analyze、createMergedFunction、createThunk、各种检查函数)都是为它服务的辅助模块。