OPT-Pass
LLVM Pass Framework 是 对 LLVM 中间表示(IR, Intermediate Representation)进行分析和转换的模块化结构。
- 安装LLVM
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main"
# 注意这里将 "llvm-toolchain-jammy-19" 改为 "llvm-toolchain-jammy-13"
sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-13 main"
sudo apt-get update
sudo apt-get install -y llvm-19 llvm-19-dev llvm-19-tools clang-19
# 安装 LLVM 13 和 Clang 13
sudo apt-get install -y llvm-13 llvm-13-dev llvm-13-tools clang-13
从源构建LLVM (Ubuntu 24.04.2 LTS)(4小时)
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout release/19.x
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=host -DLLVM_ENABLE_PROJECTS=clang <llvm-project/root/dir>/llvm/
cmake --build .
- 构建llvm-tutor
cd ~/llvm-tutor/build
cmake -DLT_LLVM_INSTALL_DIR=/usr/lib/llvm-19 ~/llvm-tutor
make
安装lit
pipx install lit
test
lit ~/llvm-tutor/build/test
HelloWorld Pass
- 清除cmake缓存
rm -rf build
mkdir build
cd build
cmake -DLT_LLVM_INSTALL_DIR=/usr/lib/llvm-19 ~/llvm-tutor/HelloWorld/
make
- 准备测试文件(LLVM IR文件)
# Generate an LLVM test file
/usr/lib/llvm-19/bin/clang -O1 -S -emit-llvm ~/llvm-tutor/inputs/input_for_hello.c -o input_for_hello.ll
#-S 用于生成*.ll文件
- opt运行
/usr/lib/llvm-19/bin/opt -load-pass-plugin ./libHelloWorld.so -passes=hello-world -disable-output input_for_hello.ll
编译pass、生成IR、运行opt
Overview Pass
OpcodeCounter
- 类型:分析Pass
- 作用:分析每个函数中使用了哪些 LLVM 指令(Opcode),打印统计摘要。
顶层构建包括所有Pass
rm -rf build
mkdir build
cd build
cmake -DLT_LLVM_INSTALL_DIR=/usr/lib/llvm-19 ..
make
export LLVM_DIR=/usr/lib/llvm-19
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -c ~/llvm-tutor/inputs/input_for_cc.c -o input_for_cc.bc
# Run the pass through opt
$LLVM_DIR/bin/opt -load-pass-plugin ~/llvm-tutor/build/lib/libOpcodeCounter.so --passes="print<opcode-counter>" -disable-output input_for_cc.bc
InjectFuncCall
- 类型:代码插桩(转换Pass)
- 作用:对每个函数插入一条
printf
调用,打印函数信息。
export LLVM_DIR=/usr/lib/llvm-19
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -O0 -emit-llvm -c ~/llvm-tutor/inputs/input_for_hello.c -o input_for_hello.bc
# Run the pass through opt
$LLVM_DIR/bin/opt -load-pass-plugin ~/llvm-tutor/build/lib/libInjectFuncCall.so --passes="inject-func-call" input_for_hello.bc -o instrumented.bin
$LLVM_DIR/bin/lli instrumented.bin
对比:
StaticCallCounter
类型:分析Pass
作用:用于统计静态直接函数调用次数的分析 Pass,不考虑运行时行为或函数指针调用。
export LLVM_DIR=/usr/lib/llvm-19
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -c ~/llvm-tutor/inputs/input_for_cc.c -o input_for_cc.bc
# Run the pass through opt
$LLVM_DIR/bin/opt -load-pass-plugin ~/llvm-tutor/build/lib/libStaticCallCounter.so -passes="print<static-cc>" -disable-output input_for_cc.bc
通过static启动
~/llvm-tutor/build/bin/static input_for_cc.bc
DynamicCallCounter
类型:分析Pass
作用:统计定义在当前模块中的函数调用次数。
对于 DynamicCallCounter,我们必须运行插桩的二进制文件才能看到输出。
export LLVM_DIR=/usr/lib/llvm-19
# Generate an LLVM file to analyze
$LLVM_DIR/bin/clang -emit-llvm -c ~/llvm-tutor/inputs/input_for_cc.c -o input_for_cc.bc
# Instrument the input file
$LLVM_DIR/bin/opt -load-pass-plugin /home/zcm230/llvm-tutor/build/lib/libDynamicCallCounter.so -passes="dynamic-cc" input_for_cc.bc -o instrumented_bin
# Run the instrumented binary
$LLVM_DIR/bin/lli ./instrumented_bin
对比:
Mixed Boolean Arithmetic Transformations
MBASub
export LLVM_DIR=/usr/lib/llvm-19
$LLVM_DIR/bin/clang -emit-llvm -S ~/llvm-tutor/inputs/input_for_mba_sub.c -o input_for_sub.ll
$LLVM_DIR/bin/opt -load-pass-plugin ~/llvm-tutor/build/lib/libMBASub.so -passes="mba-sub" -S input_for_sub.ll -o out.ll
input_for_sub.ll
经pass优化后的out.ll:
MBAAdd
a + b == (((a ^ b) + 2 * (a & b)) * 39 + 23) * 151 + 111
export LLVM_DIR=/usr/lib/llvm-19
$LLVM_DIR/bin/clang -O1 -emit-llvm -S ~/llvm-tutor/inputs/input_for_mba.c -o input_for_mba.ll
$LLVM_DIR/bin/opt -load-pass-plugin ~/llvm-tutor/build/lib/libMBAAdd.so -passes="mba-add" -S input_for_mba.ll -o out.ll