Linux网络与信息安全LLVM

【LLVM】如何写一个pass

2019-05-30  本文已影响14人  bsauce

1.简介

LLVM pass是编译器中最有趣的部分,能够对代码进行转化和优化。所有pass都是Pass类的子类,通过覆盖Pass类的虚函数来实现功能,可继承的类有ModulePass , CallGraphSCCPass, FunctionPass , LoopPass, RegionPass, BasicBlockPass。

环境搭建参考:https://blog.csdn.net/l2563898960/article/details/82871826

本文参考:https://llvm.org/docs/WritingAnLLVMPass.html

2.写hello world pass

Hello pass用于打印出编译时非外部函数的函数名,不会修改程序,只是监视作用,Hello pass的源码和文件在LLVM源码的lib/Transforms/Hello目录。

(1)设置

首先,配置和安装LLVM;然后在LLVM源码目录下创建一个新目录,这里假设你创建了lib/Transforms/Hello目录;最后,设置build脚本,用于编译新的pass。将以下代码拷贝到lib/Transforms/Hello/CMakeLists.txt。

    add_llvm_library( LLVMHello MODULE
      Hello.cpp
    
      PLUGIN_TOOL
      opt
      )

将以下行加入到lib/Transforms/CMakeLists.txt

    add_subdirectory(Hello)

这个build脚本表示当前目录的Hello.cpp文件将被编译和链接成共享对象$(LEVEL)/lib/LLVMHello.so,能被opt工具通过-load选项动态加载。

(2)写Helllo.cpp

首先需要添加头文件,因为在写 Pass,在函数 Function上操作,且需要打印数据。

    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"

接下来,include文件中的函数落在llvm命名空间。

    using namespace llvm;

接下来,开始一段匿名空间,匿名空间在c++中用到,c中采用static实现,定义在匿名空间中的变量只能在当前文件可见。

    namespace{

接下来定义pass,定义Hello类,从FunctionPass类继承过来。

    struct Hello : public FunctionPass {
    static char ID;
    Hello() : FunctionPass(ID) {}

声明 runOnFunction 方法,覆盖了从 FunctionPass继承来的虚函数。runOnFunction()就是以函数为单位进行处理,LLVM会以一次一个function为单位,喂进来给你处理,一下来吗就是将喂进来的function的名字打印出来。

      bool runOnFunction(Function &F) override {
        errs() << "Hello: ";
        errs().write_escaped(F.getName()) << '\n';
        return false;
      }
    }; // end of struct Hello
    }  // end of anonymous namespace

初始化pass的ID,LLVM使用ID地址来识别pass,所以所用初始化的值不重要。

    char Hello::ID = 0;

最后,注册Hello类,给一个命令行参数"hello"和一个名字"Hello World Pass",最后两个参数描述它的行为:如果pass需要修改CFG则第3个参数设为true,若pass是一个analysis pass,例如dominator tree pass,则第4个参数设为true。

    static RegisterPass<Hello> X("hello", "Hello World Pass",
                                 false /* Only looks at CFG */,
                                 false /* Analysis Pass */);

整个代码.cpp文件如下:

    #include "llvm/Pass.h"
    #include "llvm/IR/Function.h"
    #include "llvm/Support/raw_ostream.h"
    
    using namespace llvm;
    
    namespace {
    struct Hello : public FunctionPass {
      static char ID;
      Hello() : FunctionPass(ID) {}
    
      bool runOnFunction(Function &F) override {
        errs() << "Hello: ";
        errs().write_escaped(F.getName()) << '\n';
        return false;
      }
    }; // end of struct Hello
    }  // end of anonymous namespace
    
    char Hello::ID = 0;
    static RegisterPass<Hello> X("hello", "Hello World Pass",
                                 false /* Only looks at CFG */,
                                 false /* Analysis Pass */);

在顶层LLVM根目录下创建build目录,在build目录下先"cmake ../",再"make"即可生成"lib/LLVMHello.so文件"。

(3)使用opt运行pass

生成共享目标文件后,由于已经注册过了,所以可以使用opt命令通过你的pass运行LLVM程序。首先参考Getting Started with the LLVM System编译"Hello World"为bitcode文件(hello.bc),通过以下命令运行hello.bc("-load"表示加载pass库):

    $ opt -load lib/LLVMHello.so -hello < hello.bc > /dev/null
    Hello: __main
    Hello: puts
    Hello: main

3.Pass类和需求

设计一个pass首先要考虑需要继承哪一个类,Hello World例子使用了FunctionPass类来实现,接下来看看其它可用的类。

(1)ImmutablePass类

这个类比较无聊,这个类用在不用运行不会改变状态不需要更新的pass,在转化和分析中不常用到,但能提供当前编译器配置信息。尽管这个类不常用到,但是能提供当前被编译的目标机的信息,以及影响转化的静态信息。

(2)ModulePass类

最常用的一个类,使用该类表示将整个程序当做一个单元,可以随意引用函数主体,添加和移除函数。由于不知道ModulePass子类的行为,不能作优化。

(3)CallGraphSCCPass类

在调用图上从后往前遍历程序。

(4) FunctionPass类

FunctionPass处理程序中每个函数,并不依赖其他函数,FunctionPass不需要它们按特定顺序执行,不会修改外部函数。

(5) LoopPass类

LoopPass处理函数中的loop,并不依赖函数中其他loop。

(6)RegionPass

处理函数中单入口单退出的区域。

(7)BasicBlockPass类

类似FunctionPass,但只处理和修改单个基本块。

(8)MachineFunctionPass

依赖机器类型。

(9) getAnalysisUsage方法

不同pass之间交互。

(10)RegisterAnalysisGroup模板

(11)Statistic类

上一篇下一篇

猜你喜欢

热点阅读