IR结构
- Module:每个
module
包含全局变量列表,函数列表,该模块所依赖的库(或其他module)列表,符号表以及有关目标特性的各种数据。 - Function:编程语言中的函数,包括函数签名和若干个基本块,函数内的第一个基本块叫做入口基本块。
- BasicBlock:是一组顺序执行的指令集合,只有一个入口和一个出口,控制流只能从第一个指令进入该块。非头尾指令执行时不会违背顺序跳转到其他指令上去。每个基本块最后一条指令一般是跳转指令(跳转到其它基本块上去),函数内最后一个基本块的最后条指令是函数返回指令。
- Instruction:指令是LLVM IR中的最小可执行单位,每一条指令都单占一行
IR语法
获取IR
1 | $ clang -emit-llvm -c hello.c -o hello.bc #二进制码 |
基本语法
- LLVM 汇编语言中的注解以分号
;
开始,并持续到行末 - 全局标识符要以
@
字符开始。所有的函数名和全局变量都必须以@
开始。 - LLVM 中的局部标识符以百分号 (
%
) 开始。标识符典型的正则表达式是[%@][a-zA-Z$._][a-zA-Z$._0-9]*
。 - LLVM 拥有一个强大的类型系统,LLVM 将整数类型定义为
i*N*
,其中 N 是整数占用的字节数。如:i32,i64 - 矢量或阵列类型声明为
[no. of elements X size of each element]
。对于字符串 “Hello World!”,可以使用类型[13 x i8]
,假设每个字符占用 1 个字节,再加上为 NULL 字符提供的 1 个额外字节。 hello-world
字符串的全局字符串常量进行如下声明:@hello = constant [13 x i8] c"Hello World!\00"
。使用关键字constant
来声明后面紧跟类型和值的常量。- LLVM 允许声明和定义函数。以关键字
define
开始,后面紧跟返回类型,然后是函数名。返回 32 字节整数的main
的简单定义类似于:define i32 @main() { ; some LLVM assembly code that returns i32 }
。 - 函数声明:以 puts 函数为例,
declare i32 puts(i8*)
。该声明以关键字declare
开始,后面紧跟着返回类型、函数名,以及该函数的可选参数列表。该声明必须是全局范围的。 - 每个函数均以返回语句结尾。有两种形式的返回语句:
ret <type> <value>
或ret void
。对于简单的主例程,使用ret i32 0
就足够了。 - 使用
call <function return type> <function name> <optional function arguments>
来调用函数。注意,每个函数参数都必须放在其类型的前面。返回一个 6 位的整数并接受一个 36 位的整数的函数测试的语法如下:call i6 @test( i36 %arg1 )
。 - 如果函数入口块没有明确的标签,则会分配标签
%0
,那么该块中的第一个未命名的临时块将为%1
,以此类推。
示例分析
都写在注释里了。
1 | ; ModuleID = 'hello.c' |
getelementptr
的第一个参数是全局字符串变量的指针。要单步执行全局变量的指针,则需要使用第一个索引,即 i64 0
。因为 getelementptr
指令的第一个参数必须始终是 pointer
类型的值,所以第一个索引会单步调试该指针。0 值表示从该指针起偏移 0 元素偏移量。我的开发计算机运行的是 64 位 Linux,所以该指针是 8 字节。第二个索引 (i64 0
) 用于选择字符串的第 0 个元素,该元素是作为 puts
的参数来提供的。
参考文章:
https://llvm.zcopy.site/docs/langref/
http://www.nagain.com/activity/article/7/
https://www.ibm.com/developerworks/cn/opensource/os-createcompilerllvm1/index.html