LLVM一些语法规则
LLVM文档
LLVM编译器基础架构支持广泛的项目,从工业强度编译器到专门的JIT应用程序,再到小型研究项目。
同样,文档分为几个针对不同受众的高级别分组:
LLVM设计概述
几篇介绍性论文和演讲。
- LLVM编译器简介: 向用户介绍LLVM的演示文稿
- LLVM简介: 本章提供了面向编译器黑客的LLVM介绍
- LLVM:终身计划分析与转型的编制框架:设计概述
- LLVM:多阶段优化的基础架构:更多细节
用户指南
这些内容适用于那些刚刚接触LLVM系统的人。
注意:如果是仅对使用基于LLVM的编译器感兴趣的用户,则应该查看Clang。此处的文档适用于需要使用中间LLVM表示的用户。
- LLVM系统入门 : 讨论如何使用LLVM基础架构快速启动和运行。从解包和编译分发到执行某些工具的一切。
- 使用CMake构建LLVM: 使用CMake构建系统的主要入门指南的附录。
- 在ARM平台上构建LLVM指南: 关于在ARM上构建和测试LLVM/Clang的注意事项。
- 如何使用配置文件引导优化构建Clang和LLVM: 使用PGO构建LLVM/Clang的注意事项。
- 如何为ARM平台交叉编译compiler-rt Builtins: 关于交叉构建和测试ARM的编译器-rt内置函数的注意事项。
- 如何使用Clang/LLVM交叉编译Clang/LLVM:关于交叉构建和测试LLVM / Clang的注意事项。
- 使用Microsoft Visual Studio开始使用LLVM系统:Windows上使用Visual Studio的主要入门指南的附录。
- LLVM命令指南: LLVM命令行实用程序的参考手册(LLVM工具的“man”页面)
- LLVM的分析和转换Passes:LLVM中实现的优化和分析列表
- LLVM FAQ: 常见问题和问题列表及其解决方案。
- 当前版本的发布说明:这描述了新功能,已知错误和其它限制。
- 如何提交LLVM错误报告: 有关正确提交有关您在LLVM系统中遇到的任何错误的信息的说明。
- sphinx模板快速入门:使用LLVM测试基础结构的参考手册。
- LLVM测试套件基础结构指南:使用LLVM测试基础结构的参考手册。
- LLVM测试套件使用指南:描述如何编译和运行测试套件基准测试。
- 如何构建C,C ++,ObjC和ObjC ++前端:从源代码构建clang前端的说明。
- LLVM词典:LLVM中使用的首字母缩略词,术语和概念的定义。
- 如何将构建配置添加到LLVM Buildbot基础结构:有关将新构建器添加到LLVM buildbot master的说明。
- YAML I/O:使用LLVM的YAML I/O库的参考指南。
- 经常被误解的GEP指令:关于LLVM最常被误解的指令的一些常见问题的答案。
- 前端作者的性能提示:前端作者关于如何生成IR的技巧的集合,LLVM能够有效地优化。
- Dockerfiles用于构建LLVM的指南:使用随LLVM提供的Dockerfiles的参考。
编程文档
对于使用LLVM作为库的应用程序的开发人员。
- LLVM语言参考手册:定义LLVM中间表示和不同节点的汇编表单。
- LLVM原子指令和并发指南:有关LLVM的并发模型的信息。
- LLVM程序员手册:介绍LLVM源代码库的总体布局,重要的类和API,以及一些提示和技巧。
- LLVM扩展:LLVM特定的工具和格式扩展LLVM寻求兼容性。
- CommandLine 2.0库手册:提供有关使用命令行解析库的信息。
- LLVM编码标准:详细介绍了LLVM编码标准,并提供了有关编写高效C ++代码的有用信息。
-
如何为类层次结构设置LLVM样式的RTTI:如何让
isa<>
,dyn_cast<>
等可供类层次的客户。 - 扩展LLVM:添加指令,内在函数,类型等:在这里查看如何向LLVM添加指令和内在函数。
- libFuzzer - 用于覆盖引导的模糊测试的库:用于编写进程中引导模糊器的库
- 模糊LLVM库和工具:有关编写和使用Fuzzers查找LLVM中的错误的信息.
- Scudo硬化分配器:一个实现安全加固的malloc()的库。
- 使用-opt-bisect-limit调试优化错误:用于调试优化引发的故障的命令行选项。
子系统文档
适用于API客户端和LLVM开发人员。
- 编写LLVM Passes:有关如何编写LLVM转换和分析的信息
- 编写LLVM后端:有关如何为机器目标编写LLVM后端的信息
- LLVM与目标无关的代码生成器:LLVM代码生成器的设计和实现。如果正在将LLVM重新定位到新架构,设计新的codegen传递或增强现有组件,则非常有用。
- 机器IR(MIR)格式参考手册:MIR序列化格式的参考手册,用于测试LLVM的代码生成过程。
- TableGen:描述了TableGen工具,LLVM代码生成器大量使用它。
- LLVM别名分析基础结构:有关如何编写新别名分析实现,或如何使用现有分析的信息。
- MemorySSA:有关LLVM中的MemorySSA实用程序的信息,以及如何使用。
- 使用LLVM进行垃圾收集:接口源语言编译器应该用于编译GC程序。
- 使用LLVM进行源级别调试:本文档描述了LLVM源代码级调试器背后的设计和理念。
- LLVM中的自动矢量化:本文档描述了LLVM中矢量化的当前状态
- LLVM中的异常处理:本文档描述了LLVM中异常处理的设计和实现
- 如何添加一个受约束的浮点内在函数:在LLVM中添加新的约束数学内在时,提供必要的步骤。
- LLVM bugpoint工具:设计和使用:自动错误查找器和测试用例减少器描述和使用信息
- LLVM Bitcode文件格式:这描述了用于LLVM“bc”文件的文件格式和编码。
- 支持库:本文档描述了LLVM支持库(lib/Support)以及如何使LLVM源代码可移植
- LLVM链接时间优化:设计和实现:本文档描述了LLVM模块间优化器与链接器及其设计之间的接口
- LLVM黄金插件:如何在Linux上使用链接时,优化来构建程序。
- 使用GDB调试JIT-ed代码:如何使用GDB调试JITed代码。
- MCJIT设计与实施:描述了MCJIT执行引擎的内部工作原理
- LLVM分支权重元数据:提供有关分支预测信息的信息。
- LLVM块频率术语:提供有关BlockFrequencyInfo 分析过程中使用的术语的信息
- LLVM中的分段堆栈:本文档描述了分段堆栈以及它们在LLVM中的使用方式
- LLVM的可选丰富的反汇编输出:本文档介绍了可选的丰富反汇编输出语法
- 如何使用属性:回答有关新属性基础结构的一些问题。
- NVPTX后端用户指南:本文档描述了使用NVPTX后端编译GPU内核。
- AMDGPU后端用户指南:本文档描述了使用AMDGPU后端编译GPU内核。
- LLVM中的堆栈映射和补丁点:LLVM支持将指令地址,映射到值的位置并允许修补代码。
- 在big endian模式下使用ARM NEON指令:LLVM支持在大端ARM目标上,生成NEON指令有点不直观。本文档解释了实施和理由。
- LLVM代码覆盖映射格式:LLVM代码覆盖映射格式
- LLVM中的垃圾收集安全点:这描述了一组垃圾收集支持的实验扩展。
- MergeFunctions Pass,它是如何工作的:描述合并优化的函数。
- InAlloca属性的设计和使用:inalloca参数属性的描述。
- FaultMaps和隐式检查:LLVM支持折叠控制流入错误机器指令。
- 用clang编译CUDA:LLVM对CUDA的支持。
- LLVM中的协同程序:LLVM中的协同程序.
- 全局指令选择:这描述了原型指令选择替换GlobalISel
- XRay仪表:有关如何在LLVM中使用XRay的高级文档。
- 使用XRay进行调试:如何使用XRay调试应用程序的示例。
- Microsoft PDB文件格式:Microsoft PDB(程序数据库)文件格式的详细说明。
- 控制流程验证工具设计文档:控制流完整性验证工具的说明
- 投机负荷强化:Spectre v1的推测负载强化缓解的描述
- 堆栈安全分析:本文档描述了局部变量的堆栈安全性分析的设计。
LLVM IR 链接类型
在LLVM程序中,所有全局变量和函数都具有以下类型的链接之一:
private
“private”链接的全局值,只能由当前模块中的对象直接访问。特别地,链接代码到一个包含”private”全局值的模块时,在必要情况下,可能会造成”private”全局值rename来避免冲突。因为这个symbol是对当前模块私有的,所以,所有对这个全局值的引用,都可以被更新(名字)。这并不会在object file的任何symbol table中展示出来。
internal
与”private”相似,但该值在object file表现为local symbol (STB_LOCAL in the case of ELF) . 这对应于C中的“static”的关键字的概念。
available_externally
带有available_externally
链接标识的全局变量,不会存放到当前object file相应的LLVM模块。存在是为了内联和其它优化行为的发生,提供当前模块的一份全局定义,这份定义是从一个外部模块中获得的。带有available_externally
链接标志的全局变量,允许在任意时刻丢弃,而其它方面与”linkonce_odr”相似。这个链接类型,只允许在定义中使用,不允许在声明中使用。
linkonce
带有linkonce
链接标识的全局变量,在链接过程中与其它同名的全局变量合并。这可以被用于实现内联函数,模板,或其它必须在每一个编译单元内使用的代码的形式,但主体可以被一个更详细的定义覆盖。
全局用linkonce
联动,合并具有相同名称的其它全局联动时发生。这可以被用来实现某些形式的内联函数,模板,或者必须在使用每个转换单元中生成的,但其中的主体,可以具有更明确的定义,以后被覆盖其它代码。
weak
除了未被引用的带”weak”链接标识的全局变量,可能会被抛弃外,“weak”链接标识,拥有与”linkonce”相同的合并语义。这个标志被使用于在C源代码中,声明为”weak”的全局变量。
common
“common” 链接标识与“weak”链接标识很相似,但“common”链接标识被使用于C中的tentative definition,例如 int X;
在全局作用域。带有“common”链接标识的符号,以一种与weak symbols相同的方式被合并,但这些符号,即使未被引用也不会被删除。 common 符号可能不会有一个明确的section,必须被0值初始化(根据ELF链接规则,0值初始化的符号只通过“.bss section”提供长度占位,但不在文件中占有位置),不可能被标志为constant。Functions和aliases 不可以带有“common” 链接标识。(因为Functions和aliases,不可能被tentative definition)。
appending
“appending”链接标识,只能用于数组类型的指针全局变量。但两个带有“appending”链接标识的全局变量被链接到一起,这两个全局数组,追加合并到一起。 may This is the LLVM, typesafe, equivalent of having the system linker append together “sections” with identical names when .o files are linked.
不幸的是,这与.o文件中的任何功能都不对应,只能用于llvm.global_ctors
llvm特别解释的变量。
extern_weak
这个链接标识的语义,遵循ELF object file模型:除非被链接,否则带有extern_weak的symbol是弱的,如果没有被链接,该符号会变为null,而不是作为未定义引用。
linkonce_odr, weak_odr
某些语言允许不同的全局变量被合并,例如具有不同的语义的两个函数。其它语言,如C++ ,确保只等效的全局变量才可以合并( “one definition rule” – “ ODR ”)。这些语言可以使用linkonce_odr和weak_odr链接标识,表明全局变量将只与等效的全局变量合并。这些链接标识类型的其它语义,与其非ODR版本相同。
external
如果上述标识符都没被使用,该全局变量的是外部可见的,意味着它参与链接,可用于解析外部符号引用。
一个函数声明拥有除external
或 extern_weak
以外的链接标识是不合法的。
LLVM IR 模块结构
LLVM程序由Module’s组成,每个程序模块都是输入程序的翻译单元。每个模块由函数,全局变量和符号表条目组成。模块可以与LLVM链接器组合在一起,LLVM链接器合并函数(和全局变量)定义,解析前向声明,合并符号表条目。
以下是“hello world”模块的示例:
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8*...
%cast210 = getelementptr [13 x i8], [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!0 = !{i32 42, null, !"string"}
!foo = !{!0}
这个例子由一个全局变量”.str”,一个”puts”函数的外部声明,一个”main”函数的函数定义和一个具名元数据”foo”组成。
LLVM IR 标识符
LLVM IR 标识符有两种基本类型:
- 全局标识符(函数,全局变量)以’@’字符开头。
- 本地标识符(寄存器名称,类型)以’%’字符开头。
标识符有三种不同的格式,分别用于不同的目的:
- 命名值表示为带有前缀的字符串。例如%foo,@DivisionByZero,%a.really.long.identifier。使用的实际正则表达式是’
[%@][-a-zA-Z$._][-a-zA-Z$._0-9]*’。名称中需要其它字符的标识符,可以用引号括起来。可以使用十六进制字符的ASCII代码”\xx”,转义特殊字符xx。通过这种方式,任何字符都可以在名称值中使用,甚至可以引用自己。该”\01″前缀可以在全局值可用于压制截断。 - 未命名的值表示为带有前缀的无符号数值。例如%12,@2,%44。
- 常量,将在后面的关于常量的部分进行描述。
LLVM要求值以一个前缀开始有两个原因:
- 编译器不需要担心值的名称会和保留字(reserved words)冲突,保留字(reserved word)集,可以在未来扩展的时候不会出现惩罚(penalty,不会发生冲突)。
- 非命名的identifier,允许编译器快速找出一个临时变量,不会造成符号表冲突。
LLVM中的保留字与其它语言的保留字非常相似。有相应的关键字对应着不同的操作码,有(add, bitcast, ret, etc…),原始类型名(void, i32,etc…)和其它。这些保留字不会与变量名冲突,没有一个是以前缀(% or @)开头的。
这里有一个表示用8乘上一个整型变量%x的LLVM代码例子:
#The easy way:
%result = mul i32 %X, 8
#在强度折减后:
%result = shl i32 %X, 3
#And the hard way:
%0 = add i32 %X, %X ; yields {i32}:%0
%1 = add i32 %0, %0 ; yields {i32}:%1
%result = add i32 %1, %1
用8乘上%x的最后的方式,说明了LLVM的几个重要的词法特点:
- 注解是以 ; 分隔,直到当前行的结尾
- 当计算的结果不能被赋值的一个具名值的时候,非命名临时变量被创建
- 非具名临时变量,按顺序编号的(使用一个递增计数器,从0开始)。注意整个基本块,都被包含在这种编号方法中。例如,如果一个基本块的入口,没有被给予一个标签名,那么,就会获得一个编号0。
也表明了一个在这个文档,应该遵循的约定。当演示指令的时候,应该使一个定义了被创建的值的类型和名称的注释,紧跟这条指令后面。
LLVM语言参考手册
LLVM是一个基于SSA(静态单赋值)表示,提供了类型安全,低级别操作,灵活性和表现“所有”高级语言的能力。他是在LLVM编译策略的各个阶段中使用的通用代码表示。
LLVM的代码表示形式被设计为使用三种不同的格式:
- 表示为在内存中编译器中间语言;
- 表示为在磁盘上的位码(适合于即时编译器的快速加载);
- 表示为人类可读的汇编语言。
LLVM为编译器的高效转换和分析,提供了强大的中间语言,同时提供一个自然的方法来调试和可视化的转换。LLVM的这三种不同形式的代码表示的,都是等价的。本文档描述了人类可读的代表性和符号。
LLVM IR的目标是实现轻量和低级别同时是有表现力的,类型化,可扩展。目标是成为一个“通用IR”的序列,在一个足够低的水平是高层次的思想,可以被清晰地映射到(类似于微处理器是如何“万能的IR”,这让很多源语言被映射到) 。通过提供类型信息,LLVM可以作为优化的目的:例如,通过指针分析可以证明,一个C的自动变量,从来没有被当前函数以外的地方访问,就可以被提升到一个简单的SSA值中,而不是存储单元中。
注意这个文档描述规范化的LLVM汇编语言。这有区别于“可以被解释的就是(well-formed)格式良好的”的概念。例如,下面的指令在语法上是OK的,但不是’well form’(格式良好的)的:
%x = add i32 1, %x
这是因为%x的定义不能支配到所有使用了%x的地方。
LLVM的架构,提供了一个验证过程,用于验证一个LLVM模块是否规范化。这个pass,将自动用解释器解释,输入汇编之后和优化程序输出bitcode之前。
验证程序验证过程中,指出的违规暗示转换过程中的错误,或输入到解析器中的错误。
参考链接:
https://llvm.liuxfe.com/docs/
http://llvm.org/docs/LangRef.htm