LLVM 之 IR 篇(5):如何基于新 Pass 框架扩展 LLVM IR 优化器
Author: stormQ
Created: Friday, 30. July 2021 11:24PM
Last Modified: Friday, 20. August 2021 10:52PM
本文基于release/12.x
版本的 LLVM 源码,通过示例介绍了基于新 Pass 框架扩展 LLVM IR 优化器的方式:在源码中扩展
。从而,初步了解 LLVM 新 Pass 框架以便更深入地研究相关内容。
本节通过实现一个用于统计函数参数个数的 Pass(不依赖其它 Pass),介绍了如何基于新 Pass 框架(The New Pass Manager)在源码中扩展 LLVM IR 优化器。
step 1: 实现自定义的 Pass
1) 添加头文件
在llvm/include/llvm/Transforms/
目录中新建FnArgCntNew
目录。并在该目录下新建FnArgCntNew.h
文件,内容如下:
1 #ifndef LLVM_TRANSFORMS_FNARGCNTNEW_FNARGCNTNEW_H
2 #define LLVM_TRANSFORMS_FNARGCNTNEW_FNARGCNTNEW_H
3
4 #include "llvm/IR/PassManager.h"
5
6 namespace llvm {
7
8 class FnArgCntNewPass : public PassInfoMixin<FnArgCntNewPass> {
9 public:
10 PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
11 };
12
13 } // namespace llvm
14
15 #endif // LLVM_TRANSFORMS_FNARGCNTNEW_FNARGCNTNEW_H
需要注意的是, 实现上述代码中第 10 行的函数,意味着该 Pass 一次仅对一个函数进行分析。如果要一次分析一个模块,那么需要实现函数PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
。
2) 添加源文件
在llvm/lib/Transforms/
目录中新建FnArgCntNew
目录。并在该目录下新建FnArgCntNew.cpp
文件,内容如下:
1 #include "llvm/Transforms/FnArgCntNew/FnArgCntNew.h"
2
3 using namespace llvm;
4
5 PreservedAnalyses FnArgCntNewPass::run(Function &F,
6 FunctionAnalysisManager &AM) {
7 errs() << "FnArgCnt --- " << F.getName() << ": " << F.arg_size() << "\n";
8 return PreservedAnalyses::all();
9 }
3) 添加 CMakeLists.txt
在目录llvm/lib/Transforms/FnArgCntNew/
中新建CMakeLists.txt
文件。其内容如下:
add_llvm_component_library(LLVMFnArgCntNew
FnArgCntNew.cpp
DEPENDS
intrinsics_gen
LINK_COMPONENTS
Core
Support
)
4) 修改 Transforms 目录中的 CMakeLists.txt
在llvm/lib/Transforms/CMakeLists.txt
文件中添加如下内容:
add_subdirectory(FnArgCntNew)
注:这里的FnArgCntNew
即为上文中新建的目录名称。
step 2: 注册自定义的 Pass
1) 添加注册宏
在llvm/lib/Passes/PassRegistry.def
文件中添加如下内容:
FUNCTION_PASS("fnargcnt", FnArgCntNewPass())
注:注册宏FUNCTION_PASS
中的第一个参数表示 Pass 命名行选项的全称(这里为"fnargcnt"
),第二个参数表示 Pass 实例的构造方法。
添加后的内容如下:
省略 ....
190 #ifndef FUNCTION_PASS
191 #define FUNCTION_PASS(NAME, CREATE_PASS)
192 #endif
省略 ....
331 FUNCTION_PASS("fnargcnt", FnArgCntNewPass())
332 #undef FUNCTION_PASS
省略 ....
2) 添加编译依赖
在llvm/lib/Passes/PassBuilder.cpp
文件中添加如下内容:
#include "llvm/Transforms/FnArgCntNew/FnArgCntNew.h"
3) 添加链接依赖
在llvm/lib/Passes/CMakeLists.txt
文件中添加如下内容:
FnArgCntNew
添加后的内容如下:
add_llvm_component_library(LLVMPasses
省略 ....
LINK_COMPONENTS
省略 ....
FnArgCntNew
)
step 3: 编译安装
1) 编译
$ ninja -j8
编译完成后,查看新的opt
工具是否会动态链接libLLVMFnArgCntNew.so
共享库:
$ ldd bin/opt | grep FnArg
libLLVMFnArgCntNew.so.12 => /home/workspace/git-projects/llvm-project/build_ninja/bin/../lib/../lib/libLLVMFnArgCntNew.so.12 (0x00007fdedcd4c000)
2) 安装
$ sudo ninja install
安装完成后,查看默认的opt
工具是否会动态链接libLLVMFnArgCntNew.so
共享库:
$ ldd /usr/local/bin/opt | grep FnArg
libLLVMFnArgCntNew.so.12 => /usr/local/bin/../lib/../lib/libLLVMFnArgCntNew.so.12 (0x00007f0c42512000)
step 4: 运行
$ opt -disable-output -passes=fnargcnt sum_O1_clang.ll
执行上述命令后,输出如下:
FnArgCnt --- sum: 2
测试程序——sum_O1_clang.ll 中的函数如下:
; Function Attrs: norecurse nounwind readnone uwtable willreturn
define dso_local i32 @sum(i32 %a, i32 %b) local_unnamed_addr #0 {
entry:
%add = add nsw i32 %b, %a
ret i32 %add
}
需要注意的是, 当基于新 Pass 框架扩展 LLVM IR 优化器时,opt
工具不会将这些 Pass 作用于带optnone
属性的函数。
也就是说,如果测试程序是 sum_O0_clang.ll,其函数如下:
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @sum(i32 %a, i32 %b) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4
store i32 %b, i32* %b.addr, align 4
%0 = load i32, i32* %a.addr, align 4
%1 = load i32, i32* %b.addr, align 4
%add = add nsw i32 %0, %1
ret i32 %add
}
由于上述函数带optnone
属性。因此,我们上述所实现的优化fnargcnt
不会统计该函数的参数个数。
下一篇:LLVM 之 IR 篇(6):如何编写消除死代码 Pass