首页
社区
课程
招聘
[原创]使用LLVM的New Pass Manager编写和使用Pass
发表于: 13小时前 273

[原创]使用LLVM的New Pass Manager编写和使用Pass

13小时前
273

本文主要讲在Ubuntu22.04.5环境中如何配置编译环境和编写Pass,编译时直接在CLion的运行配置中找到对应的项目即可进行编译即可,或者在 llvm build 目录下执行ninja EncodeFunctionName

操作环境:Ubuntu22.04.5
LLVM版本:22.1.5
编译环境:CLion2026.1 + clang

CMake配置选项如下:
-G Ninja
-DLLVM_ENABLE_PROJECTS="clang"
-DBUILD_SHARED_LIBS=ON
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_C_COMPILER=clang
-DCMAKE_CXX_COMPILER=clang++

在llvm-project-22.1.5.src\llvm\lib\Transforms路径下新建一个目录,名称任意。
然后在新建的目录下创建一个cpp文件和CMakeLists.txt文件
在CMakeLists.txt中写入下面这些内容:

其中EncodeFunction决定着生成的插件文件的名称。EncodeFunctionName.cppPass实现的源文件,根据实际情况来填。

这是 LLVM 自定义 CMake 宏,定义在:llvm/cmake/modules/AddLLVM.cmake,它是专门为 Pass 插件设计的宏

它会自动帮你做:

这是 插件目标名(Target Name)

它的作用包括:

在编译项目时,CMake会根据CMakeLists.txt文件生成对应的 Makefile / build.ninja文件,用于指导编译。而一个项目中有多个CMakeLists.txt,就需要建立CMake连接。

这里新建了一个CMakeLists.txt文件,但是并没有与原来项目的CMake连接起来,构建时不会自动寻找新建目录的CMakeLists.txt,但是llvm-project-22.1.5.src\llvm\lib\Transforms路径下的CMakeLists.txt文件是在CMake连接中的,所以我们可以将新建的CMakeLists.txt添加到这个CMakeLists.txt文件中。

llvm-project-22.1.5.src\llvm\lib\Transforms路径下的CMakeLists.txt文件中添加一行add_subdirectory(新建的目录名)
add_subdirectory(新建的目录名)的作用是:让 CMake 在处理当前 CMakeLists.txt时,进入到新建的目录中,并执行该目录下的 CMakeLists.txt。

Pass的源码分成两部分:Pass功能源码和插件注册源码。

这段代码就是一个Function Pass的功能源码,它的功能就是打印出函数名。

static bool isRequired() { return true; }在 LLVM 新 Pass 管理器(New PM) 里是一个强制策略声明,它的作用是告诉 LLVM:这个 Pass 是“必需 Pass”,在任何情况下都不允许被跳过。

即使出现以下情况:

它是**插件入口函数,**由 llvmGetPassPluginInfo()调用,在 opt/ clang加载 .so时执行

这是一个 PassPluginLibraryInfo结构体,包含:

当插件被加载后:

它是 Lambda 表达式,在 C++中是 匿名函数对象

registerPipelineStartEPCallback主要是为 clang(或任何构建默认优化管线的驱动)准备的。如果要通过opt来使用Pass,还是使用egisterPipelineParsingCallback。

回调函数内容:

MPM:当前正在构建的模块级 Pass 管理器

Level:-O0 ~ -O3

FunctionPassManager FPM;的作用是创建一个 Function Pass 管理器

FPM.addPass(HelloWorldPass());的作用是把你的 Pass 加入 Function Pass 管线

它的作用是把 ModuleToFunctionPassAdaptor对象存入 MPM 的内部容器

在 LLVM 新 PM 中:

这里的PassFunction Pass 只能被FunctionPassManager FPM;管理。但这里的Pass要放到MPM 层面 ,也就是MPM.addPass(),这里的 MPMModulePassManager, 不能直接往 MPM 里塞 Function Pass

createModuleToFunctionPassAdaptor的作用是把一个 FunctionPassManager适配成一个“看起来像 Module Pass 的东西”,也就是ModuleToFunctionPassAdaptor

ModuleToFunctionPassAdaptor的声明:class ModuleToFunctionPassAdaptor : public ModulePass

ModuleToFunctionPassAdaptor是 ModulePass,所以可以放进 ModulePassManager

FPM的声明:FunctionPassManager FPM;,它是 LLVM 的新 Pass 管理器。

而LLVM的新Pass管理器都具有如下特点:

所以不能createModuleToFunctionPassAdaptor(FPM);,即使是其它函数也不能直接将FPM作为参数传递给函数,因为在C++中直接以对象为参数时会触发拷贝构造函数,对对象进行拷贝,所以不能直接将FPM作为参数传递给函数。

createModuleToFunctionPassAdaptor(std::move(FPM));本质上是:

std::move的作用是将FunctionPassManager类型转换成FunctionPassManager&&类型,这时候不会新创建任何对象,FunctionPassManager&&还是原来的对象

FunctionPassManager&是“左值引用”,表示“我借用你,不动你”,通过它可以引用对象,来修改或读取对象数据

FunctionPassManager&&是“右值引用”,表示“我不仅可以借,还可以直接拿走你的资源”,通过它也可以引用对象,来修改或读取对象数据。但是它也可以移动对象。

在以它为参数调用构造函数时,就会触发移动构造函数,比如FunctionPassManager newFPM(std::move(oldFPM));这样,使用FunctionPassManager&&创建一个新的FunctionPassManager对象,它会将普通成员(int/bool等类型的成员)的值拷贝给新对象,并将指针成员所指的数据复制到新对象的指针成员所指的内存中,然后将旧对象的成员指针变成nullptr。这就是移动拷贝。

这里的:

可以直接和下面的一样变成:

MPM.addPass(createModuleToFunctionPassAdaptor(HelloWorldPass()))在执行过程中,内部一定会把 HelloWorldPass()加入到某个 FunctionPassManager中。

这段代码的作用是:注册一个“管线解析回调”,当 LLVM 解析 -passes=字符串时,可以获取这个字符串并调用回调函数。通过下面的指令使用Pass

llvm::StringRef Name:-passes=里的一个名字。

llvm::ModulePassManager &MPM:当前正在构建的模块级 Pass 管线

llvm::ArrayRef<llvm::PassBuilder::PipelineElement>表示管线元素的上下文(嵌套管线),大多数简单 Pass 用不到,可以忽略。

这段代码的作用是让 LLVM 在加载你的 .so / .dylib插件时能够发现并初始化这个 Pass 插件

它的作用是:


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 1小时前 被白衣23编辑 ,原因: 修改一些错误
收藏
免费 9
支持
分享
最新回复 (1)
雪    币: 76
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
1
3小时前
0
游客
登录 | 注册 方可回帖
返回