PassManager系统是LLVM中最重要的基础架构之一。
经过大约10多年的努力,开发人员决定给她一个新的面目。
新的PassManager Pass背后的基本概念仍然与旧版本相同:通过LLVM IR单元运行Pass,例如,功能来检查内部的IR或甚至修改它,然后将结果IR传递给下一个Pass管道。最大的区别是我们写Pass的方式。
新Pass和新PassManager背后有几种设计理念。我强烈建议读者查看过去LLVM开发者大会的相关讲座。但我不打算详细介绍。本文仅通过简单的HelloNewPM Pass 作为新PassManager系统的预告。让我们亲自动手吧!
通常,编写(遗留)LLVM Pass的教程将首先告诉您编写一个继承其中一个llvm::Pass
族的类, 例如llvm::FunctionPass
, 然后实现一些必要的方法,比如bool FunctionPass::runOnFunction(Function &F)
在这里你第一步将要做的也是类似的事情:
struct HelloNewPMPass : public PassInfoMixin<HelloNewPMPass> { |
我们任然需要继承父类,但是这次PassInfoMixin
类中不包含任何供我们覆盖的虚函数。提供默认实现HelloNewPMPass::name()
是它在这里的唯一原因。我们只使用参数类型的run
方法,即Function
和FunctionAnalysisManager
,告诉我们应用的是哪个IR单元。
Run
方法,正如它的名字表现的,担任在过去的的FunctionPass::runOnFunction
,ModulePass::runOnModule
等方法相同的功能,但它不再是虚拟方法,所以这里没有关键字 override
。此外,返回类型不同,还有一个额外的FunctionAnalysisManager
参数。事实证明,它们都与分析框架相关,前一个用于分析数据失效,后一个用于检索分析结果,这与getAnalysis<…>()
传统通过中的方法类似。我们将分析主题留给第二部分。目前,由于我们不打算修改任何IR,我们只需返回PreservedAnalyses::all()
告诉框架,运行此Pass后所有分析结果都是一致的。
然后我们添加一些丢失的片段并将一些代码填充到run
函数体中….
#include "llvm/IR/PassManager.h" |
你猜怎么着?这就是构建新Pass所需要做的一切!我们只需要注册它。这是骨架代码:
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK |
这与传统Passes中的Pass注册有很大不同的是,我们只是从 llvmGetPassPluginInfo
函数返回Pass实体,而不是使用 RegisterPass<...>
实例和一些静态Pass注册魔术,这里是return
语句之后的花括号将构造一个llvm::PassPluginInfo
对象,携带一些传递信息。像HelloNewPMPass
是Pass名称,v0.1
是Pass版本。最后一个字段是lambda函数,它提供了一个PassBuilder
实例。PassBuilder
正如其名称所示,用于构建PassManager管道。因此我们将使用它将我们的Pass“插入”管道内的适当位置。
在我们继续之前,让我们看看如何使用该opt
工具运行新的PassManager Passes。新的PassManager使用普通字符串来描述Pass管道的外观,而不是使用命令行选项来判断您将要运行哪些传递。例如:
opt -passes =“sroa,instcombine”foo.ll |
它将首先运行SROA,然后运行指令组合器。当然还有一堆其他复杂的语法,但在这里我们只需要知道Pass管道可以通过削减这种文本描述来构造。
所以现在我们的想法是,如果我们可以“拦截”上面的解析过程,那么我们可以在出现某个Pass名称时插入我们的Pass。我们是这样做的:
extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK |
(上面省略了一些不相关的代码)
在registerPipelineParsingCallback
那里,您可以注册从文本描述中解析通行证名称时调用的回调。在这里,当管道字符串中遇到的传递名称是hello-new-pm-pass
时,我们将传递添加到管道。因此,我们可以使用opt
类似于下面的命令运行我们的传递:
opt -passes ="hello-new-pm-pass"...... |
最后,这是我们的完整代码:
#include "llvm/IR/PassManager.h" |
要构建此Pass,请使用与构建可加载旧Pass的完全相同的方式。如果您不确定,请参阅官方教程。然后使用以下命令运行pass:
opt -disable-output \ |
在我看来,PassBuilder
在新的PassManager系统扮演者核心角色,还有很多我没有涉及的主题。例如,分析管道,管道扩展点和文本管道表示的有趣语法。与往常一样,在进一步发布文档之前,深入了解源代码树始终是了解有关新PassManager的更多信息的最佳方式。