概括
选择编译器IR的决策很重要,它决定了优化过程将拥有多少信息来使代码运行得更快。
一方面非常高层级的IR允许优化器轻松地提取原始源代码的相关信息。
另一方面,低层的IR更加贴近目标机器,这样编译器更容易为特定的硬件生成相应的代码,并有可能利用目标机器的特性
但是IR的层级又不能太低
- 当编译器将程序转换为更接近机器指令的表示时,将程序片段映射到源码会变得越来越困难
- 如果编译器的IR设计采用与特定目标机器非常相似的表示,将不利于为其他不同结构的机器代码生成
LLVM项目从一系列围绕LLVM IR工具展开,这证明优化器是成熟的,用于本层的优化器数量是合理的。IR有三种等价的表达形式:
1.内存表示(Instruction类等)
2.被压缩的磁盘表示(位码文件)
3.人工可读文本的磁盘表示(LLVM汇编文件)
LLVM提供的工具和库使你能处理以上所有形式的IR,并且这些工具能够对IR进行不同表示形式的转换,同时应用优化
理解LLVMIR对编译目标的依赖
LLVM IR被设计成尽可能地与编译目标无关,但它仍然对编译目标有一定的依赖性。造成该依赖性的一个普遍承认的原因是C/C++语言固有的目标以来性质。要理解这一点,可以将在Linux系统中使用标准C头文件作为例子:程序会从Linux头文件专用文件夹bits中隐式导入一些文件。此文件加包含目标相关的头文件,这些文件中有一些宏定义会强迫某些具有特定的类型,以便与该内核的系统调用期望的类型相匹配。在导入头文件之后,当前端解析源代码时,还需要对int类型使用不同的大小,以匹配运行此代码的目标机器
因此,库头文件和C的类型都已经是依赖目标的,这使得要产生可以在之后被转换成其他目标的IR变得困难
如果仅考虑依赖目标的C标准库头文件,则给定编译单元的解析AST甚至在被转换为LLVM IR之前就已经是依赖目标的
此外前端生成IR代码时,需要使用与编译目标ABI相匹配的类型大小、调用惯例和特殊库调用等。
尽管如此,LLVM IR 还是非常灵活的,具有抽象应对不用的编译目标的能力
操作IR格式的基本工具示例
生成位码
clang sum.c -emit-llvm -c -o sum.bc
生成汇编
clang sum.c -emit-llvm -s -c -o sum.ll
汇编上述LLVM IR 汇编文本
llvm-as sum.ll -o sum.bc
从位码转换为IR汇编文本,反汇编程序:
llvm-dis sum.bc -o sum.ll
通过llvm提取工具llvm-extract ,可以提取IR函数、全局变量、删除IR模块中的全局变量
Ag:
llvm-extract -func=sum sum.bc -o sum-fn.bc
这条命令可以提取出sum函数
以上这些操作在这边博客中有写https://blog.csdn.net/m0_72827793/article/details/135894096
LLVM IR 语法介绍
整个LLVM文件的内容(无论是汇编码还是位码)被视为定义一个LLVM模块
模块是LLVM IR 顶层数据结构
每个模块包含一系列函数,每个函数由包含一系列指令的一系列基本块组成
模块还包含用于支持该模型的外围实体,如全局变量、目标数据布局、外部函数原型以及数据结构声明
LLVM局部变量与汇编语言中寄存器类似,可以用任何以%符号开头的名称命名
%7=add nsw i32 %5,%6
这一指令将执行两个局部变量%5和%6的加法,并将结果至于新的局部变量%7中
用户可以自由地给这些值命名,甚至可以只用数字就像上面一样
LLVM表达的基本属性:
- 它使用的是静态单赋值(SSA)形式。请注意,该形式下每个变量都不会被重新赋值,每个变量只有唯一一条定义它的赋值语句。每次使用一个变量都可以立即回溯到负责其定义的唯一指令。使用SSA形式导致“使用定义链”(use-def链,即可以达到使用处的所有定义/赋值语句的集合)的生成变得非常简单,这个简单操作具有巨大的价值。使用定义链是经典优化(如传播和冗余表达式消除)的前提条件,如果LLVM没有使用SSA的形似,则需要运行单独的数据流分析来计算使用定义链
- 代码被组成的三地址指令,数据处理指令有两个源操作数,并将结果放在不同的目标操作数中
- 有无穷多的寄存器,注意LLVM局部变量可以使用以%符号开始的任意名称,例如%0、%1从零开始的数字,他对局部变量的最大数量没有限制
LLVM IR基本类型系统
类型系统 | 具体的子类型 |
---|---|
基本类型 | integer/floating point/label/void/metadata/x86mmx |
派生类型 | array/function/pointer/structure/opaque structures/vector |
LLVM IR指令集
指令类型 | 具体的子类型 |
---|---|
二元操作 | add/sub/mul/udiv/sdiv/urem/srem/fadd/fmul/fdiv/frem |
按位二元操作 | shl/lshr/ashr/and/or/xor |
终止指令 | ret/bt/switch/indirector/resume/unreachable/invoke |
内存访问与地址操作 | alloc/load/store/atomic instructions/getelementptr |
向量操作 | extractelement/insertelement/shufflevector |
聚合操作 | extractvalue/insertvalue |
转换操作 | trunc/zext/sext/fptrunc/fptoui/fptosi/uitofp/sitofp/ptrtoint/inttoptr/bicast |
其他 | icmp/fcmp/select/phi/call/va_arg/landingpad |