llvm学习(二十二):使用llvm prebuilt动态加载pass

llvm pass学习的第一步是编译llvm-project,曾经我对这个前提坚定不移,痛点是耗时长、占硬盘。本文介绍不编译llvm-project,但也能进行学习的方案,极大降低初学者的门槛。

背景

2018年,小花椒说brew安装的llvm有坑,没有进行尝试;2022年5月,帮immortal学弟验证问题时发现llvm也有官方prebuilt,本文不编译llvm,使用prebuilt进行尝试,记录结果,结论是prebuilt没问题、很适合学习。

汇总

测试环境 能否动态加载pass 大小 备注
linux + 官方release 2.5G 完美
ubuntu + apt / 完美
macos + 官方release 2.4G 有-lSystem问题
macos + brew 950M 完美

linux + 官方release

下载、解压

1
2
3
cd /tmp
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz
tar xf clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04.tar.xz

设置 LLVM_HOME,使用我github上常年用作测试的测试集

1
2
3
4
5
6
7
export LLVM_HOME=/tmp/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04/
git clone https://github.com/LeadroyaL/llvm-pass-tutorial.git
cd llvm-pass-tutorial
mkdir b
cd b
cmake ../
make

直接进行测试,直接成功。。。

1
2
$LLVM_HOME/bin/clang -Xclang -load -Xclang /tmp/llvm-pass-tutorial/b/skeleton/libSkeletonPass.so /tmp/test.c
I saw a function called main!

查看一下这份 prebuilt 的基本属性,lib下文件全,target支持全,大小合适。

1
2
3
4
5
6
7
8
9
10
11
12
ls lib | wc -l
367

ls lib/cmake
clang lld llvm mlir polly

$LLVM_HOME/bin/clang -print-targets
Registered Targets:
xxxxxxxxxx

du -sh
2.5G .

ubuntu + apt

用docker开一个容器

1
2
docker pull skybro/ubuntu-cn:20.04
docker run -it skybro/ubuntu-cn:20.04 /bin/bash

安装 clang 和 llvm

1
apt install clang llvm

观察apt安装时的提醒,无论是单独安装 llvm 还是 单独安装clang,都会安装一大堆东西,包括 头文件、lib文件,llvm安装的是llvm的可执行文件,clang安装的是clang的可执行文件。 我懒得测了,反正两个我都安装了。

观察 /usr/include/usr/lib,发现头文件、lib文件都在而且比较全。

1
2
3
4
5
6
7
8
9
root@5cfd3d943123:/usr/include# ls llvm*
llvm-10:
llvm

llvm-c-10:
llvm-c

root@5cfd3d943123:/usr/lib# ls llvm-10/
bin build cmake include lib share

甚至 clang 也是软连接到 /usr/bin 下的

1
2
root@5cfd3d943123:/usr/lib# realpath /usr/bin/clang
/usr/lib/llvm-10/bin/clang

执行 llvm-config ,可以看到输出结果,看现在这个样子,使用 cmake 的 find_library 应该能直接找到 llvm 的环境。

继续拿我们的样例测,需要将我 github 项目里的 LLVM_HOME 相关的都干掉,使用 ubuntu 默认的寻找逻辑。

1
2
3
4
5
6
7
 # we need LLVM_HOME in order not automatically set LLVM_DIR
-if(NOT DEFINED ENV{LLVM_HOME})
- message(FATAL_ERROR "$LLVM_HOME is not defined")
-else ()
- set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib/cmake/llvm)
-endif()
-

进行一个编译,成功

1
2
3
4
5
mkdir b
cd b
cmake ../
make
[100%] Built target SkeletonPass

进行一个测试,成功

1
2
clang -Xclang -load -Xclang ./skeleton/libSkeletonPass.so /tmp/test.c
I saw a function called main!

毕竟是 apt 安装的,target同样也很全,就是靠谱!

1
2
3
4
5
6
7
8
9
10
root@5cfd3d943123:/llvm-pass-tutorial/b# llc --version
LLVM (http://llvm.org/):
LLVM version 10.0.0

Optimized build.
Default target: x86_64-pc-linux-gnu
Host CPU: skylake

Registered Targets:
xxxxxxxxxx

macos + 官方release

操作步骤与 linux + 官方release 完全一致。

测试结果:成功,功能和linux完全一致,大小一模一样。

但会遇到问题,连接失败

1
ld: library not found for -lSystem

也是老生常谈了,因为 macos 的工具链在 /Library/Developer/CommandLineTools 下,llvm不爱访问这个目录,需要自行解决,不在本文讨论范围内。

macos + brew

brew不会遇到上面连接失败的问题,它专门针对该问题做过适配。

恰好小花椒以前说 brew 安装的加载 pass 会有问题,我倒要看看有什么问题。

安装,2022年05月15日时,llvm对应的是llvm13。

1
brew install llvm

设置 LLVM_HOME,编译

1
2
3
4
5
6
7
8
9
export LLVM_HOME=/usr/local/opt/llvm
cd llvm-pass-tutorial
mkdir b
cd b
cmake ../
make
[ 50%] Building CXX object skeleton/CMakeFiles/SkeletonPass.dir/Skeleton.cpp.o
[100%] Linking CXX shared module libSkeletonPass.so
[100%] Built target SkeletonPass

直接进行测试,直接成功。。。

1
2
$LLVM_HOME/bin/clang -flegacy-pass-manager -Xclang -load -Xclang skeleton/libSkeletonPass.so /tmp/test.c
I saw a function called main!

查看一下这份 prebuilt 的基本属性,lib下文件全,target支持全,大小合适。

1
2
3
4
5
6
7
8
9
10
11
➜  pwd
/usr/local/opt/llvm
➜ ls lib | wc -l
448
➜ ls lib/cmake
clang lld llvm mlir polly
➜ bin/clang -print-targets
Registered Targets:
xxxxxxxxxx
➜ du -sh
952M .

所以,我也不知道小花椒说的坑在哪里,可能时代变了吧。