对于之前我们已经生成了对应的 so文件。
使用这个 动态库。
1 | # 生成对应的 test.ll 文件 |
test.c
文件
1 | int test(int aa, int bb, int cc) { |
HelloPass.cpp
文件内容
1 |
|
运行
这样我们就 实现了 第一个 pass 文件。
pass 介绍
pass 用来处理 IR (中间语言 xxx.ll)文件的。
IR 介绍
编译 .ll 文件
1 | clang -emit-llvm test.c -S -o test.ll # 编译ll文件 -S 参数 |
生成 IR 语言
test.c 文件
编译后的 test.ll
在编译的时候 可以加 -O3 参数设置编译的优化 -O3 就是3级优化
1 | clang -S -O3 -emit-llvm test.c |
优化编译的 test.ll
clang基本语法介绍
命令 | 功能 |
---|---|
-fmodules | 允许modules的语言特性 |
-fsyntax-only | 防止编译器生成代码,只是语法级别的说明和修改 |
-Xclang | 向clang编译器传递参数 |
-dump-tokens | 运行预处理器,拆分内部代码段为各种token |
-ast-dump | 构建抽象语法树AST,然后对其进行拆解和调试 |
-S | 只运行预处理和编译步骤 |
-fobjc-arc | 为OC对象生成retain和release的调用 |
-emit-llvm | 使用LLVM描述汇编和对象文件 |
-o | 输出到目标文件 |
-c | 只运行预处理,编译和汇编步骤 |
IR 语言介绍
test.ll clang -S -emit-llvm test.c
1 | ; ModuleID = 'test.c' |
里面的 @main 是定义一个全局变量 %1、%2等 是一个局部变量
%1 = alloca i32, align 4
在栈上分配 32位 4字节大小的内存。align 4
且4字节对齐load
读出内容store
写入内容i32
为32位 。i64
为64位。
pass 认识
我们的 hello.cpp 生成的 pass
1 |
|
定义 一个命名空间。
1 | namespace {} |
定义一个 Hello
名字的结构体 继承 llvm 的 FunctionPass
且这个 类需要实现 bool runOnFunction(Function &F) override {}
接口,通过这个函数来实现我们这个 pass 要实现的功能。
这个数据 必须要有的 结构 static char ID
这个 ID 能让我们定位到这个 pass
1 | struct Hello : public FunctionPass { |
让然 struct
和可以换成 class
1 | namespace llvm { |
然后我们调用一个 注册方法
先给我们的 Hello 结构体定义的 ID 初始化一个值。然后用 RegisterPass<Hello> X
注册着个 PASS
这个 pass 的名字是 "hello"
描述是 "Hello World Pass"
。
1 | char Hello::ID = 0; |
Pass 利用
pass 生成
在 llvm(二) 中已经知道怎么 设置的 CMakeLists.txt了
这里。我们之间生成 so 文件。
执行命令
1 | mkdir build |
在我的 /build/src
目录下生成了 so 动态链接库。
加载pass
接下来我们 利用 opt
工具对我们的 中文文件 .ll 文件进行加载这个 so 动态库。
opt -load .so文件 -注册的pass的名字 < test.ll > /dev/null
我们的 test.c 测试文件中 里面定义了 三个方法函数。
我们的 pass 的意思是 答应我们的函数名。
1 | // |
然后我们进行测试
opt -load /Users/0xc3m4l/CLionProjects/llvmlearn/LLVMtest/build/src/libHelloPass.so -hello < test.ll > /dev/null
实现功能 答应了我们的 函数名。