From 5247f276f55edd85c57806708d206e8c0a8c0061 Mon Sep 17 00:00:00 2001 From: JadeSnow7 <1079249368@qq.com> Date: Thu, 21 Nov 2024 18:47:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=B3=E9=A2=86=E5=AF=B9WritingAnLLVMNewPMPa?= =?UTF-8?q?ss.md=E7=9A=84=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sources/llvm/WritingAnLLVMNewPMPass.md | 214 ++++++++----------------- 1 file changed, 65 insertions(+), 149 deletions(-) diff --git a/sources/llvm/WritingAnLLVMNewPMPass.md b/sources/llvm/WritingAnLLVMNewPMPass.md index 9e2e2b6..a348512 100644 --- a/sources/llvm/WritingAnLLVMNewPMPass.md +++ b/sources/llvm/WritingAnLLVMNewPMPass.md @@ -1,87 +1,50 @@ --- -status: collected +status: translating title: "Writing an LLVM Pass" author: Linux Kernel Community collector: tttturtle-russ collected_date: 20240807 +translator: JadeSnow7 +translating_date: 20241116 link: https://github.com/llvm/llvm-project/blob/main/llvm/docs/WritingAnLLVMNewPMPass.rst --- -# Writing an LLVM Pass +# 编写LLVM Pass -::: program -opt -::: +## 简介 \-\-- 什么是LLVM Pass? -::: {.contents local=""} -::: +本文介绍了新的pass管理机制(New Pass Manager,NPM)。LLVM对代码生成管道使用的是传统的pass管理机制(Legacy Pass Manager)。详情参阅 +`WritingAnLLVMPass` 和 +`NewPassManager`. -## Introduction \-\-- What is a pass? +LLVM pass框架是LLVM系统中一个非常重要的组成部分,因为大多数编译器最值得关注的部分都在LLVM pass上。Pass执行着编译器的各种转换和优化工作,它们提供这些转换工作所需要的分析结果,并且最重要的是,它们为编译器代码提供了一种组织技术。 -:::: warning -::: title -Warning -::: +与传统pass管理器下的pass不同,传统pass管理器通过继承定义pass接口,而新pass管理器下的pass则依赖于基于概念的多态性,这意味着没有显式的接口(有关详细信息,请参见 `PassManager.h` 中的注释)。所有LLVM pass都继承自CRTP混入 `PassInfoMixin` 。pass应该有一个run()方法,该方法返回一个 `PreservedAnalyses` ,并接受某些IR单元以及一个分析管理器作为输入。例如,函数pass将具有如下方法: + `PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)` ; -This document deals with the new pass manager. LLVM uses the legacy pass -manager for the codegen pipeline. For more details, see -`WritingAnLLVMPass`{.interpreted-text role="doc"} and -`NewPassManager`{.interpreted-text role="doc"}. -:::: +我们将向您展示如何构建一个pass,包括设置构建、创建pass,到执行和测试它。查看现有的pass总是学习细节的一个好方法。 -The LLVM pass framework is an important part of the LLVM system, because -LLVM passes are where most of the interesting parts of the compiler -exist. Passes perform the transformations and optimizations that make up -the compiler, they build the analysis results that are used by these -transformations, and they are, above all, a structuring technique for -compiler code. -Unlike passes under the legacy pass manager where the pass interface is -defined via inheritance, passes under the new pass manager rely on -concept-based polymorphism, meaning there is no explicit interface (see -comments in `PassManager.h` for more details). All LLVM passes inherit -from the CRTP mix-in `PassInfoMixin`. The pass should have a -`run()` method which returns a `PreservedAnalyses` and takes in some -unit of IR along with an analysis manager. For example, a function pass -would have a -`PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);` -method. +## 快速开始 \-\-- 编写hello world -We start by showing you how to construct a pass, from setting up the -build, creating the pass, to executing and testing it. Looking at -existing passes is always a great way to learn details. -## Quick Start \-\-- Writing hello world +在这里,我们将介绍如何编写一个类似于“Hello World”的 Pass。这个“HelloWorld” Pass 的设计目的很简单,就是打印出正在编译的程序中所有非外部函数的名字。它不会对程序进行任何修改,仅仅是对其进行检查。 -Here we describe how to write the \"hello world\" of passes. The -\"HelloWorld\" pass is designed to simply print out the name of -non-external functions that exist in the program being compiled. It does -not modify the program at all, it just inspects it. +下面的代码已经存在;您可以自由地创建一个不同名称的 Pass,并与 HelloWorld 源文件一起放置。 -The code below already exists; feel free to create a pass with a -different name alongside the HelloWorld source files. -### Setting up the build {#writing-an-llvm-npm-pass-build} +### 设置构建 {#writing-an-llvm-npm-pass-build} -First, configure and build LLVM as described in -`GettingStarted`{.interpreted-text role="doc"}. +首先,按照《入门指南》中所述配置和构建 LLVM。 -Next, we will reuse an existing directory (creating a new directory -involves messing around with more CMake files than we want). For this -example, we\'ll use `llvm/lib/Transforms/Utils/HelloWorld.cpp`, which -has already been created. If you\'d like to create your own pass, add a -new source file into `llvm/lib/Transforms/Utils/CMakeLists.txt` -(assuming you want your pass in the `Transforms/Utils` directory. +接下来,我们将重用一个已有的目录(创建一个新的目录会涉及比我们想要的更多的 CMake 文件操作)。在这个例子中,我们将使用 `llvm/lib/Transforms/Utils/HelloWorld.cpp` ,这个文件已经被创建好了。如果你想创建自己的 Pass,可以在 `llvm/lib/Transforms/Utils/CMakeLists.txt` 中添加一个新的源文件(假设你想把你的 Pass 放在 `Transforms/Utils` 目录下)。 -Now that we have the build set up for a new pass, we need to write the -code for the pass itself. +现在我们已经为新的 Pass 设置好了构建环境,接下来我们需要编写 Pass 本身的代码。 -### Basic code required {#writing-an-llvm-npm-pass-basiccode} +### 基本代码要求 {#writing-an-llvm-npm-pass-basiccode} -Now that the build is setup for a new pass, we just have to write it. +既然已经为新的 Pass 设置好了构建环境,我们现在只需要编写 Pass 的代码。 -First we need to define the pass in a header file. We\'ll create -`llvm/include/llvm/Transforms/Utils/HelloWorld.h`. The file should -contain the following boilerplate: +首先,我们需要在一个头文件中定义这个 Pass。我们将创建 `llvm/include/llvm/Transforms/Utils/HelloWorld.h` 文件。该文件应包含以下样板代码: ``` c++ #ifndef LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H @@ -101,73 +64,46 @@ public: #endif // LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H ``` -This creates the class for the pass with a declaration of the `run()` -method which actually runs the pass. Inheriting from -`PassInfoMixin` sets up some more boilerplate so that we don\'t -have to write it ourselves. -Our class is in the `llvm` namespace so that we don\'t pollute the -global namespace. +这段描述说明了如何创建一个 Pass 类,并声明 run() 方法,该方法实际执行 Pass 的功能。通过继承 `PassInfoMixin` ,我们可以避免亲自编写一些样板代码。我们的类位于 llvm 命名空间中,以避免污染全局命名空间。 -Next we\'ll create `llvm/lib/Transforms/Utils/HelloWorld.cpp`, starting -with +接下来,我们将创建 llvm/lib/Transforms/Utils/HelloWorld.cpp 文件,并从以下内容开始: ``` c++ -#include "llvm/Transforms/Utils/HelloWorld.h" -``` - -\... to include the header file we just created. - -``` c++ -using namespace llvm; -``` +#include "llvm/Transforms/Utils/HelloWorld.h" // 包含我们刚刚创建的头文件 -\... is required because the functions from the include files live in -the llvm namespace. This should only be done in non-header files. +using namespace llvm; // 因为包含文件中的函数位于 llvm 命名空间中,所以需要这条语句。这应该只在非头文件中使用。 -Next we have the pass\'s `run()` definition: - -``` c++ -PreservedAnalyses HelloWorldPass::run(Function &F, - FunctionAnalysisManager &AM) { - errs() << F.getName() << "\n"; - return PreservedAnalyses::all(); +// Pass 的 `run` 方法定义 +PreservedAnalyses HelloWorldPass::run(Function &F, FunctionAnalysisManager &AM) { + errs() << F.getName() << "\n"; // 打印函数名到标准错误输出 + return PreservedAnalyses::all(); // 返回值表示所有的分析(例如支配树)在该 Pass 之后仍然有效,因为我们没有修改任何函数 } ``` -\... which simply prints out the name of the function to stderr. The -pass manager will ensure that the pass will be run on every function in -a module. The `PreservedAnalyses` return value says that all analyses -(e.g. dominator tree) are still valid after this pass since we didn\'t -modify any functions. +该代码简单地将函数的名称打印到标准错误输出(stderr)。pass manager将确保这个pass会在模块中的每个函数上运行。`PreservedAnalyses` 的返回值表示所有分析(例如支配树)在此pass之后仍然有效,因为我们没有修改任何函数。 -That\'s it for the pass itself. Now in order to \"register\" the pass, -we need to add it to a couple places. Add the following to -`llvm/lib/Passes/PassRegistry.def` in the `FUNCTION_PASS` section +这就是该pass本身的内容。现在,为了“注册”这个 Pass,我们需要在几个地方添加一些代码。 +在 `llvm/lib/Passes/PassRegistry.def` 中添加 Pass +在 `llvm/lib/Passes/PassRegistry.def` 文件的 FUNCTION_PASS 部分添加以下内容: ``` c++ FUNCTION_PASS("helloworld", HelloWorldPass()) ``` -\... which adds the pass under the name \"helloworld\". +这将 Pass 注册为名为 `"helloworld"` 的函数级 Pass。 -`llvm/lib/Passes/PassRegistry.def` is #include\'d into -`llvm/lib/Passes/PassBuilder.cpp` multiple times for various reasons. -Since it constructs our pass, we need to also add the proper #include in -`llvm/lib/Passes/PassBuilder.cpp`: +在 `llvm/lib/Passes/PassBuilder.cpp` 中包含头文件 +由于 `llvm/lib/Passes/PassRegistry.def` 被多次包含在 `llvm/lib/Passes/PassBuilder.cpp` 中,我们需要在 `PassBuilder.cpp` 中添加正确的 `#include` 语句: ``` c++ #include "llvm/Transforms/Utils/HelloWorld.h" ``` -This should be all the code necessary for our pass, now it\'s time to -compile and run it. - -### Running a pass with `opt` +现在我们已经完成了 Pass 的所有必要代码,接下来是编译和运行它。 +### 使用 `opt` 运行pass -Now that you have a brand new shiny pass, we can build -`opt`{.interpreted-text role="program"} and use it to run some LLVM IR -through the pass. +现在你已经有了一个全新的pass,我们可以构建 `opt`并使用它将一些 LLVM IR 代码通过该pass处理。 ``` console $ ninja -C build/ opt @@ -190,12 +126,9 @@ bar Our pass ran and printed the names of functions as expected! -### Testing a pass +### 测试 Pass -Testing our pass is important to prevent future regressions. We\'ll add -a lit test at `llvm/test/Transforms/Utils/helloworld.ll`. See -`TestingGuide`{.interpreted-text role="doc"} for more information on -testing. +测试我们的 Pass 非常重要,以防止将来出现回归问题。我们将在 llvm/test/Transforms/Utils/helloworld.ll 添加一个 lit 测试。有关测试的更多信息,请参阅《测试指南》。 ``` llvm $ cat llvm/test/Transforms/Utils/helloworld.ll @@ -213,15 +146,14 @@ define void @bar() { } $ ninja -C build check-llvm -# runs our new test alongside all other llvm lit tests +# 运行我们的新测试以及其他所有的 `LLVM lit` 测试 ``` -## FAQs +## 常见问题解答 (FAQs) -### Required passes +### 必需的 Pass -A pass that defines a static `isRequired()` method that returns true is -a required pass. For example: +一个定义了静态 `isRequired()` 方法并返回 `true` 的 Pass 被认为是必需的 Pass。例如: ``` c++ class HelloWorldPass : public PassInfoMixin { @@ -232,68 +164,52 @@ public: }; ``` -A required pass is a pass that may not be skipped. An example of a -required pass is `AlwaysInlinerPass`, which must always be run to -preserve `alwaysinline` semantics. Pass managers are required since they -may contain other required passes. +必需的 Pass 是指不能被跳过的 Pass。例如,`AlwaysInlinerPass` 是一个必需的 Pass,因为它必须始终运行以保持 `alwaysinline` 语义。Pass 管理器也是必需的,因为它们可能包含其他必需的 Pass。 -An example of how a pass can be skipped is the `optnone` function -attribute, which specifies that optimizations should not be run on the -function. Required passes will still be run on `optnone` functions. +一个 Pass 可能被跳过的例子是 `optnone` 函数属性,它指定不应该对该函数运行优化。然而,即使对于带有 `optnone` 属性的函数,必需的 Pass 仍然会被运行。 -For more implementation details, see -`PassInstrumentation::runBeforePass()`. +有关更多实现细节,请参阅 `PassInstrumentation::runBeforePass()`。 -### Registering passes as plugins +### 注册 Pass 插件 -LLVM provides a mechanism to register pass plugins within various tools -like `clang` or `opt`. A pass plugin can add passes to default -optimization pipelines or to be manually run via tools like `opt`. For -more information, see `NewPassManager`{.interpreted-text role="doc"}. +LLVM 提供了一种机制,可以在诸如 clang 或 opt 等工具中注册 Pass 插件。Pass 插件可以将 Pass 添加到默认的优化管道中,或者通过像 opt 这样的工具手动运行。更多详细信息,请参阅 `NewPassManager` 文档。 -Create a CMake project at the root of the repo alongside other projects. -This project must contain the following minimal `CMakeLists.txt`: +创建 CMake 项目 +在仓库根目录下创建一个新的 CMake 项目,与其他项目并列。该项目必须包含以下最小的 `CMakeLists.txt` 文件: ``` cmake add_llvm_pass_plugin(MyPassName source.cpp) ``` -See the definition of `add_llvm_pass_plugin` for more CMake details. - -The pass must provide at least one of two entry points for the new pass -manager, one for static registration and one for dynamically loaded -plugins: - -- `llvm::PassPluginLibraryInfo get##Name##PluginInfo();` -- `extern "C" ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() LLVM_ATTRIBUTE_WEAK;` +Pass 必须至少提供两个入口点之一,一个用于静态注册,另一个用于动态加载插件: -Pass plugins are compiled and linked dynamically by default. Setting -`LLVM_${NAME}_LINK_INTO_TOOLS` to `ON` turns the project into a -statically linked extension. +- llvm::PassPluginLibraryInfo get##Name##PluginInfo(); +- extern "C" ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() LLVM_ATTRIBUTE_WEAK; +Pass 插件默认是动态编译和链接的。将 `LLVM_${NAME}_LINK_INTO_TOOLS` 设置为 ON 可以将项目转换为静态链接扩展。 -For an in-tree example, see `llvm/examples/Bye/`. +对于树中的例子,可以参考 `llvm/examples/Bye/`. -To make `PassBuilder` aware of statically linked pass plugins: +使 PassBuilder 获取静态链接的 Pass 插件: ``` c++ -// Declare plugin extension function declarations. +// 声明插件扩展函数声明 #define HANDLE_EXTENSION(Ext) llvm::PassPluginLibraryInfo get##Ext##PluginInfo(); #include "llvm/Support/Extension.def" ... -// Register plugin extensions in PassBuilder. +// 在 PassBuilder 中注册插件扩展 #define HANDLE_EXTENSION(Ext) get##Ext##PluginInfo().RegisterPassBuilderCallbacks(PB); #include "llvm/Support/Extension.def" ``` -To make `PassBuilder` aware of dynamically linked pass plugins: +使 PassBuilder 获取动态链接的 Pass 插件: ``` c++ -// Load plugin dynamically. +// 动态加载插件 auto Plugin = PassPlugin::Load(PathToPlugin); if (!Plugin) report_error(); -// Register plugin extensions in PassBuilder. -Plugin.registerPassBuilderCallbacks(PB); +// 在 PassBuilder 中注册插件扩展 +Plugin->registerPassBuilderCallbacks(PB); ```