您的位置:首页 > 其它

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

2017-12-17 17:35 204 查看
LLVM是一个自由软件项目,它是一种编译器基础设施,以C++写成。它的发展起源于2000年伊利诺伊大学厄巴纳-香槟分校(UIUC)的维克拉姆·艾夫(Vikram Adve)与其第一博士生克里斯·拉特纳(Chris Lattner)的研究,彼时他们想要为所有静态及动态语言创造出动态的编译技术。

LLVM的命名最早源自于底层虚拟机(Low Level Virtual Machine)的首字母缩写,但现在这个项目的范围早已大大超越其最初的意思。当前,LLVM已经发展成为被用于开发从编译器前端到后端的“一套模块及可重用的编译器及工具链技术的集合”("collection of modular and reusable compiler and toolchain technologies")。


2005年,苹果电脑雇用了克里斯·拉特纳及他的团队为苹果电脑开发应用程序系统,LLVM为现今Mac OS X及iOS开发工具的一部分,Xcode开发环境的内核使用的即是LLVM。因LLVM对产业的贡献,ACM于2012年将ACM软件系统奖授与Adve、Lattner及Evan Cheng。
----------------------------------------------------------------------------------------------------------------------------------------

在前面的文章中,我们已经通过一个简单的Hello World程序演示了在LLVM中编写(前端)Pass的基本方法。本文将介绍如何通过编写Pass,从而在一个函数中遍历Basic Blocks的方法。

首先,我们要知道,在LLVM中,程序的基本单位是模块(Module)。(The ModulePass class is the most general of all superclasses that you can use. Deriving from ModulePass indicates that your pass uses the entire program as a unit.)

函数是模块的基本组成单位,所以一个Module由一个或多个函数组成。Basic Block是函数的基本组成单位,所以一个Function由一个或多个Basic Block组成。指令(Instructions)是Basic Block的基本组成单位,所以一个Basic Block由一个或多个Instructions组成。

跟之前一样,假设你的LLVM之安装目录为 ... .../llvm,那么你首先在路径 ... .../llvm/lib/Transforms 中创建一个子文件夹,例如名字叫做IterInsideBB。



然后在此文件夹下创建如下三个文件:CMakeLists.txt、IterInsideBB.exports、IterInsideBB.cpp。因为LLVM中作为pass的一个示例,提供了另外一个现成的pass,即在Transforms中的Hello文件夹,你可以从该文件夹下面把三个名为CMakeLists.txt、Hello.exports、Hello.cpp的文件拷贝到IterInsideBB文件夹中并修改相应的文件名。



然后修改上层目录(即Transforms)中的CMakeLists.txt,在其末尾添加:add_subdirectory(IterInsideBB)。然后修改... .../llvm/lib/Transforms/IterInsideBB 中的CMakeLists.txt。注意这个文件是你从... .../llvm/lib/Transforms/Hello 中拷贝过来的。You must set up a build script that will compile the source code for the new pass,具体来说,你需要把其中出现了三次的Hello,都修改成IterInsideBB,修改后该文件的内容如下:
# 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}/IterInsideBB.exports)
endif()
endif()

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

add_llvm_loadable_module( LLVMIterInsideBB
IterInsideBB.cpp

DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)接下来,需要编写pass文件的具体内容。这个pass文件其实就是一个.cpp文件,就当前这个例子而言,我们要做的就是编辑IterInsideBB.cpp的内容。该文件内容如下:

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

using namespace llvm;

namespace {

struct IterInsideBB : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
IterInsideBB() : FunctionPass(ID) {}

bool runOnFunction(Function &F) override {
errs() << "Function name: ";
errs() << F.getName() << '\n';
for(Function::iterator bb = F.begin(), e = F.end(); bb!=e; bb++)
{
errs()<<"BasicBlock name = "<< bb->getName() <<"\n";
errs()<<"BasicBlock size = "<< bb->size() << "\n\n";

for(BasicBlock::iterator i = bb->begin(), i2 = bb->end(); i!=i2; i++)
{
outs()<<" "<< *i <<"\n";
}
}
return false;
}
};
}

char IterInsideBB::ID = 0;
static RegisterPass<IterInsideBB> X("IterInsideBB", "Iterate inside basicblocks inside a Function");
接下来要做的就是重新bulid LLVM。进入... .../llvm/build文件夹下面,直接使用make。整个过程大概需要几分钟的样子。然后来试用一下上面编写的Pass,为此你需要在你期望的位置(例如Desktop)上建立一个新的测试文件,例如名为test.c的文件内容如下:

#include "stdio.h"

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

int main()
{
int a;
scanf("%d",&a);

if(a>=0)
printf("%d\n", add(a, 7));
else
printf("a<0\n");

return 0;
}
然后在命令行使用下面的命令,编译生成.ll文件:

clang test.c -O0 -S -emit-llvm -o test.ll

然后执行我们的pass,具体方法是在命令行中执行如下命令:

 opt -load /Users/fzuo/llvm/build/lib/LLVMIterInsideBB.dylib -IterInsideBB test.ll
然后,你便会得到如下输出:

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.

Function name: add
BasicBlock name = entry
BasicBlock size = 8

%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
Function name: main
BasicBlock name = entry
BasicBlock size = 7

%retval = alloca i32, align 4
%a = alloca i32, align 4
store i32 0, i32* %retval, align 4
%call = call i32 (i8*, ...) @scanf(i8* getelementptr
inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i32* %a)
%0 = load i32, i32* %a, align 4
%cmp = icmp sge i32 %0, 0
br i1 %cmp, label %if.then, label %if.else
BasicBlock name = if.then
BasicBlock size = 4

%1 = load i32, i32* %a, align 4
%call1 = call i32 @add(i32 %1, i32 7)
%call2 = call i32 (i8*, ...) @printf(i8* getelementptr
inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %call1)
br label %if.end
BasicBlock name = if.else
BasicBlock size = 2

%call3 = call i32 (i8*, ...) @printf(i8* getelementptr
inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0))
br label %if.end
BasicBlock name = if.end
BasicBlock size = 1

ret i32 0

上面的输出遍历了源程序代码中的两个函数add和main,然后又分别遍历了其中的所有Basic Blocks,其中BasicBlock size输出的是每个Basic Block中命令的条数。

【本系列文章目录】
在LLVM中编写Pass的详细教程(1):一个Hello World的Pass

在LLVM中编写Pass的详细教程(2):遍历一个函数中的Basic Blocks
在LLVM中编写Pass的详细教程(3)
在LLVM中编写Backend Pass的详细教程(1)
在LLVM中编写Backend Pass的详细教程(2)

(本文完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: