LLVM下载编译以及Pass开发大致流程
一、LLVM下载编译
创建个文件夹放llvm相关的,所有操作没明确表示的都默认在这个文件夹下面
1、下载源码
git clone https://git.llvm.org/git/llvm.git/
//clang不能复制到根目录下!
git clone https://git.llvm.org/git/clang.git/
- clang是llvm的子项目,但是源码是分开的,下载好的clang放在下载好的llvm文件夹的tools目录下
2、使用cmake进行编译生成clang可执行文件
*我们在终端用的clang是Xcode默认的,但是想要进行llvm开发需要编译属于自己的clang编译器
确保安装了cmake
brew install make
xcode编译
新建build_xcode文件夹下,cd到这个文件夹下
//编译有点慢,等着吧
cmake -G Xcode ../llvm
编译好了之后可以看到build_xcode文件夹下面出现一个LLVM.xcodeproj,双击打开
选择Manually Manage Schemes

选择需要编译的目标为clang

直接运行
运行成功之后会在build_xcode/Debug/bin文件夹下面看到clang的可执行文件

cd到bin文件夹下
./clang -v
可以看到输出clang的版本号等信息
一、Pass开发流程
1、配置编译环境
-
在llvm源码下面的lib/Transforms/文件夹下创建一个新的文件夹,名为PassDemo
-
复制lib/Transforms/文件夹下的Hello文件夹下的内容过去,Hello这个是官方给的demo
-
修改cpp和exports文件名为PassDemo,CMakeLists里面修改三处
(这里我按照书上的名字是add_llvm_loadable_mudule,会出现Unknown CMake command "add_llvm_loadable_mudule"错误,所以我改成了add_llvm_library,并加上 MODULE BUILDTREE_ONLY这样才会运行完后找到PassDemo的可执行文件)
# 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}/PassDemo.exports)
endif()
endif()
if(WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS Core Support)
endif()
add_llvm_library( LLVMPassDemo MODULE BUILDTREE_ONLY
PassDemo.cpp
DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
- PassDemo文件夹同级也有个CMakeLists.txt,在里面添加一条
add_subdirectory(PassDemo)
cd到build_xcode文件夹下,重新
cmake -G Xcode ../llvm
然后打开xcode
添加LLVMPassDemo的目标文件为Scheme
edit scheme ---- > manage schemes ---->添加

在工程中找到PassDemo.cpp文件就可以编辑了
2、写代码
//导入头文件
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Analysis/LoopInfo.h"
//指定使用的命名空间
using namespace llvm;
namespace {
//定义LCYPassDemo类继承自FunctionPass(不同功能的pass继承自不同的父类)
struct LCYPassDemo : public FunctionPass {
//定义可以供LLVM标示的Pass ID
static char ID;
LCYPassDemo() : FunctionPass(ID){}
//定义一个runOnFunction重载继承自父类的抽象虚函数
bool runOnFunction(Function &F) override{
//这里只打印每个方法的名字
errs() << "LCYPassDemo: ";
errs().write_escaped(F.getName()) << "\n";
//这里打印方法内c循环个数
LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
int loopCounter = 0;
for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i) {
loopCounter++;
}
errs() << "loopNum:" << loopCounter << "\n";
return false;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<LoopInfoWrapperPass>();
AU.setPreservesAll();
}
};
}
//初始化Pass ID
char LCYPassDemo::ID = 0;
//注册Pass Hello
static RegisterPass<LCYPassDemo> X("hello", "hello world pass", false, false);
command+b,编译后会在build_xcode文件夹下的Debug/lib下出现LLVMPassDemo.dylib
3、opt加载
创建一个test.mm,以下为代码
#include <stdio.h>
int add(int x, int y) {
for (int i = 0; i < 10; i ++) {
printf("%d", i);
}
for (int i = 0; i < 1; i ++) {
printf("%d", i);
}
return x+y;
}
int main() {
printf("%d", add(3, 4));
return 0;
}
终端编译生成bitcode,cd到之前生成的clang可执行文件的文件夹下
./clang -emit-llvm -c test.mm -o test.bc
Xcode中添加opt的目标文件到scheme中,编辑scheme的启动参数,这里添加的-hello就是之前代码里面注册的

command+r运行,可以看到控制台有输出test.mm中的两个方法名
- 可以在Xcode中直接打断点调试
终端通过-help参数可以查看到注册的Pass参数
cd到build_xcode/Debug/bin文件夹下
./opt -load ../lib/LLVMPassDemo.dylib -help
可以搜索查看到有一行内容是这样的

终端通过-time-passes参数,查看输出Pass的时间占比
./opt -load ../lib/LLVMPassDemo.dylib -hello -time-passes -disable-output test.bc路径
可以看到
