在2018年编写LLVM遍——部分III

缺失的故事——树内遍整合

原文地址:https://medium.com/@mshockwave/writing-llvm-pass-in-2018-part-iii-d44cd0c2c354

作者:Bekket McClane

正如副标题所示,本文更像LLVM(源代码树内)开发的一个“缺失的指引”,而不是引入新东西。

官方的《Writing an LLVM Pass》教你编写一个确实简单的(旧式的)LLVM遍,它可以被opt动态载入。但现在你是个懒人,你希望在opt,甚至在clang里运行你酷酷的遍。本文提供几个方法将你的遍整合进旧式PassManager流水线。我下一篇文章将讨论如何添加定制的clang命令行选项,以启用定制特性。

要在遍流水线里默认运行一个遍,我们需要先弄清楚流水线是如何构造的。存在一个构建类,PassManagerBuilder,用于构建旧式PassManager以及缺省的遍流水线。不过,构建类所在的路径有点奇怪——它放在include/llvm/Transforms/IPO/PassManagerBuilder.h以及lib/Transform /IPO/PassManagerBuilder.cpp,而不是我们熟悉的IR或PassManager目录。

在类PassManagerBuilder中,我们看到许多方法的名字是自解释的,例如addInstructionCombiningPass以及addFunctionSimplificationPasses。这些方法可用于将特定类别的遍添加到流水线。除了显式调用它们来更新候选遍列表,在代码里有许多地方使用OptLevel属性,即我们熟悉的-O1、-O2命令行选项,通过它们的优先级级别添加遍。

让我们看一下其中一个通用入口。首先:populateFuntionPassManager

void PassManagerBuilder::populateFunctionPassManager(

    legacy::FunctionPassManager &FPM) {

  //...Some code skipped...

 

  if (OptLevel == 0) return;

 

  addInitialAliasAnalysisPasses(FPM);

 

  FPM.add(createCFGSimplificationPass());

  FPM.add(createSROAPass());

  FPM.add(createEarlyCSEPass());

  FPM.add(createLowerExpectIntrinsicPass());

}

之前,我们只知道一个方式运行我们的遍:使用

static RegisterPass<MyPass> X("my-pass", ...);

注册它。通过命令行选项-load=MyPass.so -my-pass运行opt。不过,如果它已经在源代码树里了,每次动态地载入与运行一个遍是奇怪且不必要的。

因此,从上面的代码,我们看到如果一个遍已经在LLVM源代码树里,我们所需做的一切是创建一个带有几个工厂方法的遍,例如createSROAPass,显式调用legacy::FunctionPassManager:: add(…)将期望的遍加入流水线。

不幸,这还没完。事实证明,在你的树内遍都设置好之前,还有几个简单的设置(这是为什么我写这篇文章)。下面是检查清单:

  1. createXXXPass函数
  2. initializeXXXPassPass函数/InitializedPasses.h文件
  3. INITIALIZE_PASS_BEGIN/END/DEPENDENCY代码
  4. 将你的initializeXXXPassPass放在合适的地方
  5. LinkAllPasses.h文件
  6. 将你的createXXXPass放在合适的地方

上面的列表是完成这些任务通常的次序,当然它们间没有特定的优先级。让我们从上到下仔细检查它们。

第一项,早先提到的,是相当简单明了的,如常它仅要求3行代码来实现:

FunctionPass* llvm::createMyAwesomePass() {

  return new MyAwesomePass();

}

只是new一个你遍的实例并返回它。注意它是在llvm名字空间里的一个全局静态函数,因此不要忘记前缀llvm::或者只是使用namespace llvm {…}围绕它。

下两个检查项,序号2和3,实际上是相同的——initializeXXXPassPass函数。这个函数将为一个遍创建一个内部遍信息项,并把它注册到PassManager,连同它要求的依赖性。这类似于用于动态载入遍的RegisterPass<MyPass> X(…)。为了实现这个函数,首先我们把函数声明放在include/llvm/initializePasses.h里:

// ...Previous lines...
void initializeModuleSummaryIndexWrapperPassPass(PassRegistry&);
void initializeMustExecutePrinterPass(PassRegistry&);
// -----------------------------
void initializeMyAwesomePassPass(PassRegistry&);
// -----------------------------
void initializeNameAnonGlobalLegacyPassPass(PassRegistry&);
// ...

在我们自己遍的源代码文件里,添加以下行:

INITIALIZE_PASS_BEGIN(MyAwesomePass, "my-awesome-pass",

                      "Some description for the Pass",

                      false, false)

INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) // Or whatever your Pass dependencies

INITIALIZE_PASS_END(MyAwesomePass, "my-awesome-pass",

                    "Some description for the Pass",

                    false, false)

这实际上构造initializeXXXPassPass函数的函数体。

现在我们有initializeXXXPassPass与createXXXPass以及函数。接下来我们所需要做的是在正确的地方放置/调用它们。让我们先处理前者。有两个地方我们需要调用initializeXXXPassPass函数。第一个在你的遍的类构造函数里:

MyAwesomePass() : FunctionPass(ID) {
  initializeMyAwesomePassPass(*PassRegistry::getPassRegistry());
}

第二个地方是在上级初始化函数中。例如,如果你的遍是一个在lib/Analysis目录下的分析遍,那么在lib/Analysis/Analysis.cpp里添加以下行:

void llvm::initializeAnalysis(PassRegistry &Registry) {
  //...Other initialization function calls
  initializeMyAwesomePassPass(Registry);
  //...
}

如果你的遍是放在lib/Transforms/Scalar下的一个转换遍,你要查找的文件是lib/Transforms/ Scalar/Scalar.cpp。

现在让我们转到createXXXPass函数。为了防止对这些createXXXPass符号进行某些激进的链接时优化,我们需要在include/llvm/LinkAllPasses.h里添加一个假的函数调用:

struct ForcePassLinking {
    ForcePassLinking() {
      //...
      (void) llvm::createMyAwesomePass();
      //...
    }
}

最后,在本文前面提到的PassManagerBuilder里的createXXXPass函数上,我们准备添加一个真正的函数调用。你可以增加更多复杂的逻辑,在特定的优化层级或条件下,将你的遍插入流水线,但通常我把这个函数调用放在populateXXXPassManager中的某处,这也在前面提到了:

void PassManagerBuilder::populateFunctionPassManager(

    legacy::FunctionPassManager &FPM) {

  //...Some code skipped...

 

  if (OptLevel == 0) return;

 

  addInitialAliasAnalysisPasses(FPM);

 

  FPM.add(createCFGSimplificationPass());

  FPM.add(createSROAPass());

  // Here you are!

  FPM.add(createMyAwesomePass());

  FPM.add(createEarlyCSEPass());

  FPM.add(createLowerExpectIntrinsicPass());

}

不管是旧式还是新式,遍流水线构造一直是PassManager里一个有趣的话题。仍然有许多其他因素影响遍流水线的信息。前面章节仅提供最简单的方式在旧式PassManager里缺省运行你的遍。

我认为这是将你的遍放入LLVM源代码树所需的所有额外要求。希望这使得你的LLVM开发更容易?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值