LLVM学习之路(二)编写自己的第一个Pass

声明:使用的LLVM版本为5.0.1,由于网上大多是教程和博客均以低版本为例,故部分目录结构以及命令可能不同,特此说明。

        Pass是LLVM至关重要的组成,所谓Pass,个人理解是LLVM中的一个优化或者转换工作单元,通常做分析或者转换的工作,分析类的Pass主要提供信息,转换类的Pass则要修改中间代码,所有的Pass均作用于中间代码(IR),需要继承自Pass类。另外,LLVM提供了很多已经写好的Pass,并且在编译安装过程中已经编译为.so动态链接库供我们使用,由于还没有详细深入研究,个人见解仅供参考。Pass类的官方文档:http://llvm.org/doxygen/classllvm_1_1Pass.html

下面首先说一下如何根据官方文档,运行其简单的Pass示例——LLVMHello,然后再来记录在其他目录下编写并调用自己的第一个Pass(注意:这里说的是Pass,而不是建立LLVM工程

一.简单的Pass示例——LLVMHello

Pass的编写推荐阅读官方文档(1)http://llvm.org/docs/WritingAnLLVMPass.html 

官方文档(1)中的介绍,是以源码目录llvm/lib/Transforms/Hello中的Hello.cpp为例,可以不进行编写,阅读Hello.cpp中的代码,根据文档修改Hello目录下的CMakeLists.txt如下:

add_llvm_loadable_module( LLVMHello
  Hello.cpp

  PLUGIN_TOOL
  opt
  )

然后再修改Transforms目录下的CMakeLists.txt,向其中加入:

add_subdirectory(Hello)

之后需要重新执行编译安装LLVM(参考LLVM学习之路(一)

注:看上去很麻烦,并且很慢。3.7版本之前是不需要这样的,可以直接在写Pass的文件下make,3.7版本之后则必须这样做

以上步骤之后即可通过opt命令调用该Pass:

opt -load ../llvm的build路径/lib/LLVMHello.so -hello hello.bc

这里的hello.bc与之前提到的Hello.cpp没有任何关系,hello.bc是我们自己在其他目录下写的一个最简单的helloworld程序,通过clang编译成为二进制.bc文件。并在该目录下执行以上命令

得到如下结果:

WARNING: You're attempting to print out a bitcode file.
This is inadvisable as it may cause display problems. If
you REALLY want to taste LLVM bitcode first-hand, you
can force output with the `-f' option.

Hello: main

        以上就是官方文档(1)提供的Pass示例。当然,我们也可以在Transform目录下新建我们自己的Pass文件夹,然后重复上述步骤来编译并且调用我们自己的Pass,其中关于文件编写的一些注意事项,会在下面的探讨中说明。

二.我的第一个Pass

        作为一个处女座,强迫症患者,虽然是开源的东西,我也是及其不希望破坏官方源码的结构以及原始性的,因此接下来探讨一下如何在其他目录下编写并且编译调用自己的Pass。

网上会有个别大牛推荐建立如下目录:

mypass
  -CMakeLists.txt  ...(1)
  -src
      -mypass.cpp
      -CMakeLists.txt  ...(2)
  -build

或者

<project dir>/
    | /
    CMakeLists.txt
    <pass name>/
        |
        CMakeLists.txt
        Pass.cpp
        ...

这个结构出自官方文档(2)http://llvm.org/docs/CMake.html

这是LLVM工程的推荐结构,但是如果只是学习Pass的编写,并非必需。我们可以直接建立一个文件夹来存放我们的Pass和CmakeLists.txt,然后在目录下建立build文件夹来进行编译。

即:

为第一层目录

为第二层目录

关于mypass.cpp的编写,还是推荐阅读 官方文档(1)以及 LLVM编程手册编程规范。mypass.cpp内容如下:
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

//使用不具名的空间,保持对象的局部性,具有内链特性

namespace {

//定义结构体mypass,继承FunctionPass类

struct mypass : public FunctionPass {

  static char ID;

//类似于构造方法

  mypass() : FunctionPass(ID) {}

//实现虚函数runOnFunction(Function &F),此函数声明在FunctionPass类中

  bool runOnFunction(Function &F) override {

//errs是LLVM中定义的C++输出流

    errs() << "我自己的: ";
    errs().write_escaped(F.getName()) << '\n';
    return false;
  }
}; 
// 结构体定义结束
}  
// 不具名空间结束

char mypass::ID = 0;
//注册Pass,所有的Pass必须经过注册才能使用
static RegisterPass<mypass> X("my", "我自己的Pass",
                             false /* Only looks at CFG */,
                             false /* Analysis Pass */);

注释写的应该比较清楚了。这其实只是对Hello.cpp的一个简单重现,其中输出部分改为了中文。另外需要注意的是,注册Pass是,括号内的第一个参数,是你将来在opt命令中的命令行参数,即:

opt -load ../build的路径/你在CMakeLists.txt中定义的名称.so 命令行参数[这里应该是 -my] hello.bc

关于CMakeLists.txt的编写,我的CMakeLists.txt内容如下:

add_library(mypass MODULE mypass.cpp)
# 使用c++11
target_compile_features(mypass PRIVATE cxx_range_for cxx_auto_type)
# 不使用C++ RTTI.
set_target_properties(mypass PROPERTIES
    COMPILE_FLAGS "-fno-rtti"
)

比较简单,第一行就是声明你要生成的.so库使用的是哪个.cpp文件,然后MODULE名称为mypass(个人理解);

接下来就是设置默认的编译选项,使用C++11进行编译,不然会出现如下错误:

error: #error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.

对于官方文档(2)中提到的其他语句,我都进行了逐一调试,除了:

add_subdirectory(<pass name>)

add_llvm_loadable_module(LLVMPassname
  Pass.cpp
  )

这两句分别因为没有子目录和找不到add_llvm_loadable_module报错之外,其他的均可成功。

第一个报错是因为目录结构的原因,第二个我使用了add_library代替。贴出来的是我精简之后的(连最低版本限制都被我删了,最好加上),也不知道会不会有什么影响,仅供参考。

完成编写后,可以在命令行进入build目录,执行:

cmake ../
make
之后可以在build目录下看到libmypass.so文件(lib是默认加上的,mypass实在CMakeLists.txt中写了的):


调用Pass分析hello.bc:

opt -load llvm-pass/mypass/build/libmypass.so -my hello.bc

至此,我的第一个Pass算是完成了。







猜你喜欢

转载自blog.csdn.net/wang_shiling/article/details/80178079