LLVM基础中间代码概念概述
LLVM基础中间代码概念概述
Identifiers 标识符 @ 全局 % 局部 后接字符串 命名量 @name %name 无符号数字 未命名量 @42 %42 类型系统 void 空类型 <type> *
- Identifiers 标识符
- @ 全局
- % 局部
- 后接字符串 命名量 @name %name
- 无符号数字 未命名量 @42 %42
10. 类型系统
- 11.
- 12. void 空类型
- 13. <type> * 指针类型
- 14. <returntype> (<parameter list>) 函数类型
- 15. < <# elements> x <elementtype> > 向量类型
- 16. [<# elements> x <elementtype>] 数组类型
- 17. { <type list> } 普通结构体类型
- 18. <{ <type list> }> 打包结构体类型
- 19. Metadata 元数据类型
- 20. label 标签类型
- 21. token 词元类型
- 22.
23. 类型系统(例子)
- 24.
- 25. void 空
- 26. i32 * 指针
- 27. i32 (i32) 函数
- 28. <5 x i32> 向量
- 29. [5 x i32] 数组
- 30. { i32,i32,i32 } 普通结构体
- 31. <{ i32,i32 }> 打包结构体
- 32.
33. 元数据
- 34.
- 35. ; 未命名元数据节点
- 36. ; 用于被命名元数据引用
- 37. !0 = !{!"zero"}
- 38. !1 = !{!"one"}
- 39. !2 = !{!"two"}
- 40.
- 41. ; 命名元数据
- 42. !name = !{!0,!1,!2}
- 43.
- 44. !name --- !0
- 45. |-- !1
- 46. |-- !2
- 47.
48. 模块层次内联汇编
- 49.
- 50. module asm "内联汇编代码"
- 51.
52. Target Triple
- 53.
- 54. target triple = "x86_64-amd64-freebsd"
- 55.
56. First Class Types 第一类型
- 57. Single Value Types 单值类型
- 58. 只在寄存器里头有效
- 59.
- 60. Integer Type 整数类型
- 61.
- 62. iN ;N为比特数 (通用描述)
- 63.
- 64. i1 一个比特整数
- 65. i32 32为整数
- 66.
- 67. FloaTing-Point Types 浮点类型
- 68.
- 69. half - 16位浮点值
- 70. float - 32位浮点值
- 71. double - 64位浮点值
- 72. fp128 - 128位浮点值
- 73. x86_fp80 - 80位浮点值
- 74. ppc_fp128 - 128位浮点值
- 75.
76. 模块结构
- 77. 程序由模块组成,每个模块都是输入程序的翻译单元。
- 78. Hello,world 模块
- 79.
- 80. ; 定义字符串常量作为全局常量
- 81. @.str = private unnamed_addr constant [13 x i8] c"Hello world\0A\00"
- 82.
- 83. ; 外部声明的 puts 函数
- 84. declare i32 @puts(i8* nocapturE) nounwind
- 85.
- 86. ; main 函数的定义
- 87. define i32 @main() { ; i32()*
- 88. ; [13 x i8] 转换到 i8...
- 89. %cast210 = getelementptr [13 x i8],[13 x i8]* @.str,i64 0,i64 0
- 90.
- 91. ; 调用 puts 函数,将字符串写入 stdout
- 92. call i32 @puts(i8* %cast210)
- 93. ret i32 0
- 94. }
- 95.
- 96. ; 命名元信息
- 97. !0 = !{i32 42,null,!"String"}
- 98. !foo = !{!0}
- 99.
- 指令参考
- =========
- Terminator instructions
- 指示当前块完成后,执行哪个块。
- 终结指令典型的产生一个 void 值:他们影响控制流,而不是值。(invoke指令是一个例外)
- ret 返回
- br 二元条件分支/无条件转移
- switch 多条件分支
- indirectbr
- invoke 普通/带异常调用
- resume 抛出异常?
- catchswitch 捕获异常
- catchret
- cleanupret
- unreachable 不可到达(无语义)
- Binary Operations
- add 加
- fadd 浮点加
- sub 减
- fsub 浮点减
- mul 乘
- fmul 浮点乘
- udiv 无符号整数除
- sdib 带符号整数除
- fdiv 浮点除
- urem 无符号整数求余
- srem 带符号整数求余
- frem 浮点数求余
- 运算\类型 无符号整数 带符号整数 浮点数
- + add fadd
- - sub fsub
- * mul fmul
- / udiv sdiv fdiv
- % urem srem frem
- Bitwise Binary Operations
- shl 左移
- lshr 逻辑右移
- ashr 算数右移
- and 与
- or 或
- xor 异或
- Vector Operations
- extractelement 取出元素
- insertelement 插入元素
- shufflevector
- Aggregate Operations
- extractvalue 取出值
- insertvalue 插入值
- Memory Access and Addressing Operations
- alloca 分配内存
- load 从内存加载
- store 储存到内存
- fence
- cmpxchg
- atomicrmw 自动修改内存
- getelementptr 获取 aggregate(集合) 数据结构的子成员地址
- Coversion Operations
- 这个类型为转换指令(强制类型转换|铸造casTing)
- 都取一个单一运算对象和一个类型。
- 对运算对象提供一系列位转换。
- trunc .. to 截断转换
- zext .. to 零扩展转换
- sext .. to 符号位扩展转换
- fptrunc .. to 浮点截断转换
- fpext .. to 浮点扩展
- fptoui .. to 浮点转无符号整数
- fptosi .. to 浮点转带符号整数
- uitofp .. to 无符号整数转浮点
- sitofp .. to 带符号整数转浮点
- ptrtoint .. to 指针转整数
- inttoptr .. to 整数转指针
- bitcast .. to 位模式转换(重新解释,不改变任何二进制位)
- addrepacecast .. to 地址空间转换
- Other Operations
- icmp 整数比较
- fcmp 浮点数比较
- phi φ 节点
- SELEct 条件值选择
- call 简单函数调用
- va_arg 可变参数
- landingpad
- catchpad
- cleanuPPAD
- Objective-C在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)这种中间代码是跨平台通用性代码;
- 可以使用以下命令行指令生成中间代码
clang -emit-llvm -S 文件名
语法简介
- @ - 全局变量
- % - 局部变量
- alloca - 在当前执行的函数的堆栈帧中分配内存,当该函数返回到其调用者时,将自动释放内存
- i32 - 32位4字节的整数
- align - 对齐
- load - 读出,store - 写入
-icmp - 两个整数值比较,返回布尔值 - lable - 代码标签
- br - 选择分支,根据条件来转向label,不根据条件跳转的话类似goto
- call - 调用函数
getelementptr指令就是get the address of 结构体的子变量,只进行地址加计算,不涉及内存获取。
llvm没有取地址的操作
getelementptr是在对一个地址进行加操作
load与store是llvm中两个对地址与数据进行操作的指令
load是对一个地址进行取数据,store是对一个地址进行存数据。
struct munger_struct {
int f1;
int f2;
};
void munge(struct munger_struct *P) {
P[0].f1 = P[1].f1 + P[2].f2;
}
...
munger_struct Array[3];
...
munge(Array);
对应IR
void %munge(%struct.munger_struct* %P) {
entry:
%tmp = getelementptr %struct.munger_struct* %P, i32 1, i32 0
%tmp = load i32* %tmp
%tmp6 = getelementptr %struct.munger_struct* %P, i32 2, i32 1
%tmp7 = load i32* %tmp6
%tmp8 = add i32 %tmp7, %tmp
%tmp9 = getelementptr %struct.munger_struct* %P, i32 0, i32 0
store i32 %tmp8, i32* %tmp9
ret void
}
GetElementPtr指令其实是一条指针计算语句,本身并不进行任何数据的访问或修改,进行是计算指针,修改计算后指针的类型。
GetElementPtr至少有两个参数,第一个参数为要进行计算的原始指针,往往是一个结构体指针,或数组首地址指针。
第二个参数及以后的参数,都称为indices,表示要进行计算的参数,如结构体的第几个元素,数组的第几个元素。
P[0].f1
%tmp9 = getelementptr %struct.munger_struct* %P, i32 0, i32 0
P[1].f1
P[2].f2
%tmp = getelementptr %struct.munger_struct* %P, i32 1, i32 0
%tmp6 = getelementptr %struct.munger_struct* %P, i32 2, i32 1
仅有数组计算
如果仅有数组指针计算,那么就简单了许多,数组指针的移动只需要一个参数即可。
但如果是仅有结构体指针,那么还是必须两个参数才行
3d_rendering case:
Triangle_3D triangle_3ds[3192]
; <label>:11: ; preds = %4, %822
%12 = phi i64 [ 0, %4 ], [ %823, %822 ]
%13 = getelementptr inbounds %struct.Triangle_3D, %struct.Triangle_3D* %0, i64 %12
%14 = bitcast %struct.Triangle_3D* %13 to i64*
%15 = load i64, i64* %14, align 1
%16 = getelementptr inbounds %struct.Triangle_3D, %struct.Triangle_3D* %0, i64 %12, i32 8
%17 = load i8, i8* %16, align 1