LLVM Pass从添加到执行的过程

clang的过渡

要想搞清楚Pass添加和执行的过程首先要清楚如何从编译前段的clang过渡到LLVM。

当AST构建完成之后交给ASTConsumer的子类BackendConsumer处理。首先将会通过CodeGenerator来完成从AST到IR的转换。转换完成之后调用HandleTranslationUnit

EmbedBitcode(getModule(), CodeGenOpts, llvm::MemoryBufferRef());

EmitBackendOutput(Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts,
                  LangOpts, C.getTargetInfo().getDataLayout(),
                  getModule(), Action, std::move(AsmOutStream));

这里会调用两个函数,EmbedBitcode用于处理 -fembed-bitcode 参数,目的是用于在生成的obj文件中增加一个用于存放bitcode的section。

EmitBackendOutput定义位于BackendUtil.cpp中。函数中会定义一个AsmHelper,并且调用EmitAssembly或者EmitAssemblyWithNewPassManager

if (CGOpts.ExperimentalNewPassManager)
  AsmHelper.EmitAssemblyWithNewPassManager(Action, std::move(OS));
else
  AsmHelper.EmitAssembly(Action, std::move(OS));

BackendUtil.cpp文件仍然位于clang中,但是这里离LLVM已经相当接近了

重点看EmitAssembly

legacy::PassManager PerModulePasses;
PerModulePasses.add(
    createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));

legacy::FunctionPassManager PerFunctionPasses(TheModule);
PerFunctionPasses.add(
    createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));

CreatePasses(PerModulePasses, PerFunctionPasses);

这里生成两个PassManager,一个管理ModulePass,一个管理FunctionPass。CreatePasses中完成两个PassManager队与Pass的添加管理。

除了PerModulePasses和PerFunctionPasses之外还有一个CodeGenPasses,根据CodeGenOpts的编译要求来添加用于指令转换、生成的Pass

legacy::PassManager CodeGenPasses;
CodeGenPasses.add(
    createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));

std::unique_ptr<raw_fd_ostream> ThinLinkOS;

switch (Action) {
case Backend_EmitNothing:
  break;

case Backend_EmitBC:
  if (CodeGenOpts.EmitSummaryIndex) {
    if (!CodeGenOpts.ThinLinkBitcodeFile.empty()) {
      std::error_code EC;
      ThinLinkOS.reset(new llvm::raw_fd_ostream(
          CodeGenOpts.ThinLinkBitcodeFile, EC,
          llvm::sys::fs::F_None));
      if (EC) {
        Diags.Report(diag::err_fe_unable_to_open_output) << CodeGenOpts.ThinLinkBitcodeFile
                                                         << EC.message();
        return;
      }
    }
    PerModulePasses.add(
        createWriteThinLTOBitcodePass(*OS, ThinLinkOS.get()));
  }
  else
    PerModulePasses.add(
        createBitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists));
  break;

case Backend_EmitLL:
  PerModulePasses.add(
      createPrintModulePass(*OS, "", CodeGenOpts.EmitLLVMUseLists));
  break;

default:
  if (!AddEmitPasses(CodeGenPasses, Action, *OS))
    return;
}

当Pass添加完成就开始逐个启动

{
  PrettyStackTraceString CrashInfo("Per-function optimization");

  PerFunctionPasses.doInitialization();
  for (Function &F : *TheModule)
    if (!F.isDeclaration())
      PerFunctionPasses.run(F);
  PerFunctionPasses.doFinalization();
}

{
  PrettyStackTraceString CrashInfo("Per-module optimization passes");
  PerModulePasses.run(*TheModule);
}

{
  PrettyStackTraceString CrashInfo("Code generation");
  CodeGenPasses.run(*TheModule);
}

当Pass开始执行也就正式进入LLVM代码的范畴,执行完成后再返回到clang。

PassManagerBuilder与addExtension

回头再看CreatePasses中的内容,首先是有一个将PassManagerBuilder与LangOptions和CGOpts封装在一起的wrapper

// We need this wrapper to access LangOpts and CGOpts from extension functions
// that we add to the PassManagerBuilder.
class PassManagerBuilderWrapper : public PassManagerBuilder {
public:
  PassManagerBuilderWrapper(const Triple &TargetTriple,
                            const CodeGenOptions &CGOpts,
                            const LangOptions &LangOpts)
      : PassManagerBuilder(), TargetTriple(TargetTriple), CGOpts(CGOpts),
        LangOpts(LangOpts) {}
  const Triple &getTargetTriple() const { return TargetTriple; }
  const CodeGenOptions &getCGOpts() const { return CGOpts; }
  const LangOptions &getLangOpts() const { return LangOpts; }

private:
  const Triple &TargetTriple;
  const CodeGenOptions &CGOpts;
  const LangOptions &LangOpts;
};
}

CreatePasses函数中做的首要事情就是生成一个PassManagerBuilder并将CGOpts和LangOpts中的内容绑定到一起

PassManagerBuilderWrapper PMBuilder(TargetTriple, CodeGenOpts, LangOpts);

// At O0 and O1 we only run the always inliner which is more efficient. At
// higher optimization levels we run the normal inliner.
if (CodeGenOpts.OptimizationLevel <= 1) {
  bool InsertLifetimeIntrinsics = (CodeGenOpts.OptimizationLevel != 0 &&
                                   !CodeGenOpts.DisableLifetimeMarkers);
  PMBuilder.Inliner = createAlwaysInlinerLegacyPass(InsertLifetimeIntrinsics);
} else {
  // We do not want to inline hot callsites for SamplePGO module-summary build
  // because profile annotation will happen again in ThinLTO backend, and we
  // want the IR of the hot path to match the profile.
  PMBuilder.Inliner = createFunctionInliningPass(
      CodeGenOpts.OptimizationLevel, CodeGenOpts.OptimizeSize,
      (!CodeGenOpts.SampleProfileFile.empty() &&
       CodeGenOpts.EmitSummaryIndex));
}

PMBuilder.OptLevel = CodeGenOpts.OptimizationLevel;
PMBuilder.SizeLevel = CodeGenOpts.OptimizeSize;
PMBuilder.SLPVectorize = CodeGenOpts.VectorizeSLP;
PMBuilder.LoopVectorize = CodeGenOpts.VectorizeLoop;

PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops;
PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions;
PMBuilder.PrepareForThinLTO = CodeGenOpts.EmitSummaryIndex;
PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO;
PMBuilder.RerollLoops = CodeGenOpts.RerollLoops;

然后根据各种LangOpts和CodeGenOpts使用PassManagerBuilder的addExtension接口。

if (CodeGenOpts.DebugInfoForProfiling ||
    !CodeGenOpts.SampleProfileFile.empty())
  PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
                         addAddDiscriminatorsPass);

...

if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds)) {
  PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate,
                         addBoundsCheckingPass);
  PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
                         addBoundsCheckingPass);
}

...

if (LangOpts.Sanitize.hasOneOf(SanitizerKind::Efficiency)) {
  PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
                         addEfficiencySanitizerPass);
  PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
                         addEfficiencySanitizerPass);
}
...

addExtension接受的第一个参数是ExtensionPointTy类型,指定Pass插入的位置。ExtensionPointTy是一个enum,内容如下

enum ExtensionPointTy {
//插入时机尽可能的早,允许在代码从前端出来后就被处理
EP_EarlyAsPossible,

//在模块级优化前
EP_ModuleOptimizerEarly,

//在循环优化后
EP_LoopOptimizerEnd,

//允许插入优化pass在大多数主优化后,在clean-ish优化前
EP_ScalarOptimizerLate,

//在最后
EP_OptimizerLast,

//在vectorizer和其他更高级的平台指定优化之前
EP_VectorizerStart,

//O0下位于inlining pass之后
EP_EnabledOnOptLevel0,

//执行类似于与指令组合的窥孔优化(peephole optimizations),在指令组合pass的每个实例之后
EP_Peephole,

//后期循环的规范化和简化这是最后一个循环优化管道,在循环删除前。
//必须是LoopPass的实例,加入到可以删除循环的地方,例如指定平台的循环方言识别
EP_LateLoopOptimizations,

//通过CGPassManager增加CallGraphSCC pass在主CallGraphSCC pass之后,function simplification passe之前
EP_CGSCCOptimizerLate,
};

后一个参数为ExtensionFn

typedef std::function<void(const PassManagerBuilder &Builder,
                             legacy::PassManagerBase &PM)>
      ExtensionFn;

这个函数里主要调用了PassManager的add接口,将指定的Pass加入队列

PassManagerBuilder中有一个存放Pass添加函数的vector

std::vector<std::pair<ExtensionPointTy, ExtensionFn>> Extensions;

addExtension就是将由ExtensionPointTy和ExtensionFn组成的pair添加到vector中

Pass添加的最后一步

在CreatePasses的最后,调用populate,完成从Extensions到PassManager的添加过程。

PMBuilder.populateFunctionPassManager(FPM);
PMBuilder.populateModulePassManager(MPM);

以populateModulePassManager为例

void PassManagerBuilder::populateModulePassManager(
    legacy::PassManagerBase &MPM) {
    
  ...

  // If all optimizations are disabled, just run the always-inline pass and,
  // if enabled, the function merging pass.
  if (OptLevel == 0) {
    addPGOInstrPasses(MPM);
    if (Inliner) {
      MPM.add(Inliner);
      Inliner = nullptr;
    }

    // FIXME: The BarrierNoopPass is a HACK! The inliner pass above implicitly
    // creates a CGSCC pass manager, but we don't want to add extensions into
    // that pass manager. To prevent this we insert a no-op module pass to reset
    // the pass manager to get the same behavior as EP_OptimizerLast in non-O0
    // builds. The function merging pass is
    if (MergeFunctions)
      MPM.add(createMergeFunctionsPass());
    else if (GlobalExtensionsNotEmpty() || !Extensions.empty())
      MPM.add(createBarrierNoopPass());

    addExtensionsToPM(EP_EnabledOnOptLevel0, MPM);

    // Rename anon globals to be able to export them in the summary.
    // This has to be done after we add the extensions to the pass manager
    // as there could be passes (e.g. Adddress sanitizer) which introduce
    // new unnamed globals.
    if (PrepareForThinLTO)
      MPM.add(createNameAnonGlobalPass());

    return;
  }
  ...
  ...

从Extension这个命令可以看出来,CreatePass更多的是为了将由CodeGenOpts和LangOpts参数指定的内容分类,而populate才是Pass最终进入执行队列的位置。接下来的代码也大致相同,根据OptLevel、LibraryInfo等信息直接调用add函数添加。

addExtensionsToPM就是将Extensions中的内容添加到PassManager的接口

void PassManagerBuilder::addExtensionsToPM(ExtensionPointTy ETy,
                                           legacy::PassManagerBase &PM) const {
  if (GlobalExtensionsNotEmpty()) {
    for (auto &Ext : *GlobalExtensions) {
      if (Ext.first == ETy)
        Ext.second(*this, PM);
    }
  }
  for (unsigned i = 0, e = Extensions.size(); i != e; ++i)
    if (Extensions[i].first == ETy)
      Extensions[i].second(*this, PM);
}

Pass的添加大致如此。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!