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 框架以便更深入地研究相关内容。


如何在源码中扩展 LLVM IR 优化器

本节通过实现一个用于统计函数参数个数的 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不会统计该函数的参数个数。


References


下一篇:LLVM 之 IR 篇(6):如何编写消除死代码 Pass

上一篇:LLVM 之 IR 篇(4):如何基于传统 Pass 框架扩展 LLVM IR 优化器

首页