您的位置:首页 > 其它

在LLVM中编写pass的详细教程(1)

2017-11-25 15:49 465 查看
可能你已经大概知道了LLVM中pass是什么,那么你或许正跃跃欲试想在LLVM中实际编写一个pass试试;即使你对pass的概念还很朦胧,实际practice一下或许也恰恰可以帮助你更加形象和具体的理解LLVM中的pass。所以本文就是要向你介绍如何在LLVM中实际编写并使用一个pass的入门教程。特别说明,以下示例是在MacOS上完成的,但其实在Ubuntu上也同样适用。



LLVM 的pass框架是LLVM系统的一个很重要的部分。LLVM的优化和转换工作就是由多个pass来一起完成的。类似流水线操作一样,每个pass完成特定的优化工作。 要想真正发挥LLVM的威力,掌握pass是不可或缺的一环。LLVM中pass架构的可重用性和可控制性都非常好,这允许用户自己开发pass或者关闭一些默认提供的pass。总的来说,所有的pass大致可以分为两类:分析和转换分析类的pass以提供信息为主,转换类的会修改中间代码。

假设你的LLVM之安装目录为 ... .../llvm,那么你首先在路径 ... .../llvm/lib/Transforms 中创建一个子文件夹,例如名字叫做MyHello。然后在此文件夹下创建如下三个文件:CMakeLists.txt、MyHello.exports、MyHello.cpp。因为LLVM中作为pass的一个示例,提供了另外一个现成的pass,即在Transforms中的Hello文件夹,你可以从该文件夹下面把三个名为CMakeLists.txt、Hello.exports、Hello.cpp的文件拷贝到MyHello文件夹中并修改相应的文件名。

然后修改上层目录(即Transforms)中的CMakeLists.txt:末尾添加:add_subdirectory(MyHello)。然后修改... .../llvm/lib/Transforms/MyHello 中的CMakeLists.txt。注意这个文件是你从... .../llvm/lib/Transforms/Hello 中拷贝过来的。You must set up a build script that will compile the source code for the new pass,具体来说,你需要把其中出现了三次的Hello,都修改成MyHello,修改后该文件的内容如下:
# If we don't need RTTI or EH, there's no reason to export anything
# from the hello plugin.
if( NOT LLVM_REQUIRES_RTTI )
if( NOT LLVM_REQUIRES_EH )
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/MyHello.exports)
endif()
endif()

if(WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS Core Support)
endif()

add_llvm_loadable_module( LLVMMyHello
MyHello.cpp

DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)

上述build script中的如下部分add_llvm_loadable_module( LLVMMyHello
MyHello.cpp

PLUGIN_TOOL
opt
)specifies that MyHello.cpp file in the current directory is to be compiled and linked into a shared object $(LEVEL)/lib/LLVMHello.so that can be dynamically loaded by the opt tool via its -load option. If your operating system uses a suffix other than .so (such as Windows or Mac OS X), the appropriate extension will be used. 下面你会看到,在Mac OS 中,后缀是dylib。

接下来,需要编写pass文件的具体内容。这个pass文件其实就是一个.cpp文件,就当前这个例子而言,我们要做的就是编辑MyHello.cpp的内容。该文件内容如下,我们把对该文件内容的解读放在文末。
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

#define DEBUG_TYPE "hello"

namespace {
// Hello - The first implementation, without getAnalysisUsage.
struct MyHello : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
MyHello() : FunctionPass(ID) {}

bool runOnFunction(Function &F) override {
errs() << "Hello: ";
errs() << F.getName() << '\n';
return false;
}
};
}

char MyHello::ID = 0;
static RegisterPass<MyHello> X("myhello", "Hello World Pass");接下来要做的就是重新bulid LLVM。这跟【6】中所介绍的过程类似。进入... .../llvm/build文件夹下面,直接使用make。如下图所示:


整个过程大概需要几分钟的样子,总之耗时并不长。出现下图时,表示已经make完成了。这时你就会在在llvm/lib/下生成一个MyHello.dylib文件。


然后你需要在你期望的位置(例如Desktop)上建立一个新的测试文件,例如名为test.c的文件内容如下:

int add(int a, int b)
{
return a+b;
}

int sub(int a, int b)
{
return a-b;
}然后使用下面的命令,编译生成.ll文件(内含LLVM的IR表示):



然后执行我们的pass。执行的方法如下,可见pass的作用是逐个地在提示符“Hello:”后面输出程序代码中函数的名字:


最后,回头来解读一下之前编写的MyHello.cpp文件中的内容。首先,我们引入了要用到的 LLVM 的头文件:#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Support/raw_ostream.h"之后,DEBUG_TYPE 是 LLVM 拿来统计该 pass 被呼叫几次或者是使用 DEBUG 输出除错讯息的时候用的
#define DEBUG_TYPE "hello"
把 llvm namespace 的东西都弄进来, 虽然这是不好的习惯, 不过由于方便起见直接 using
using namespace llvm;
匿名 namespace, 避免 export 里面的 symbol,其中 MyHello 继承 FunctionPass, 代表 MyHello Pass 的输入是以 Function 为单位
namespace {

struct MyHello : public FunctionPass {

static char ID; // 给 LLVM 用来识别这个 Pass 的 id
MyHello() : FunctionPass(ID) {}  //构造函数

//此处略去之代码在后面解释
};
}

char MyHello::ID = 0; //定义下一下 Hello::ID 的实体
static RegisterPass<myhello> X("myhello", "Hello World Pass"); //跟 LLVM 注册你的这个 Pass, 第一个参数是 pass name, 第二个则是说明用的文字

下面解释上面略去的代码,这也是整个 pass 的精华所在, LLVM 会以一次一个 Function 为单位, 喂进来给你玩,大致上里面的行为就是将喂进来的 Function 的名称印出来。
bool runOnFunction(Function &F) override{
errs() << "Hello: ";
errs() << F.getName()) << '\n';
return false;
}
All FunctionPass execute on each function in the program independent of all of the other functions in the program. Implementing a FunctionPass is usually straightforward. FunctionPasses may overload three virtual methods(例如runOnFunction)to do their work. All of these methods should return true if they modified the program, or false if they didn’t.

参考文献
【1】官方文档中的一个Tutorial http://llvm.org/docs/WritingAnLLVMPass.html#quick-start-writing-hello-world
【2】台湾人写的一份LLVM中pass入门的博文 https://kitoslab.blogspot.com/2012/10/llvm-pass.html(内容较旧)
【3】另外一份介绍LLVM中pass的入门级博文 http://www.nagain.com/activity/article/14/
【4】知乎上关于LLVM中pass的一个问答 https://www.zhihu.com/question/47631469/answer/106968480
【5】油管上一个印度人介绍LLVM的Tutorial视频 https://www.youtube.com/watch?v=-7YY5xdNgfI(需自备梯子)
【6】在Mac OS上Build、配置并使用LLVM http://blog.csdn.net/baimafujinji/article/details/78598658
(全文完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: