译者序
目前几乎没有关于LLVM IR(中间语言)的中文资料,因此本人在看英文手册的同时尝试翻译。限于水平和时间,本文只翻译了一小部分英文手册,如果发现理解有冲突之处,请以原文为准。
原文链接:http://llvm.org/docs/LangRef.html
摘要
本文档是一个LLVM汇编语言手册,LLVM 语言是一个Static Single Assignment (SSA)的中间语言,其特点:类型安全; 低级别操作;灵活;具有清晰表示“所有”高级语言的能力。它作为一个统一的代码表示,贯穿LLVM编译的各个阶段。
介绍
LLVM语言,旨在用于三个不同的形式:内存中的编译中间语言(IR),保存在硬盘上的bitcode(适合快速地被一个JIT编译器加载),一个可读性的汇编语言表示。如此,LLVM将为高效编译转换和分析提供一个强大的中间表示。事实上,LLVM的三种不同的形式都是等价的。本文档描述了人类可读的表征和符号。
LLVM语言旨在轻量、底层、同时富有表现力,类型化,易于扩展。LLVM语言目标是成为一种"通用中间语言",通过足够低层次使得高级语言可以清晰的映射到它。通过提供类型信息,LLVM语言可以作为优化的目标:例如,通过指针分析,可以证明,一个C自动变量从不当前函数之外访问,允许它被提升到一个简单的SSA值,而不是一个堆变量。
略...
标识符
LLVM标识符有2个基本类型:全局和局部。全局标识符(函数,全局变量)以“@”字符开头。本地标识符(注册名称,类型)以'%'字符开头。此外,有三种不同的格式标识符,用于不同的目的:
1、命名值以字母序列开头,例如:%foo
, @DivisionByZero
, %a.really.long.identifier
. 正则表达式为: ‘[%@][-a-zA-Z$._][-a-zA-Z$._0-9]*
‘.
2、非命名值以数字开头,例如: %12
, @2
, %44
.
3、常量,详见下文“常量”节。
LLVM语言之所以使用特定的前缀,有2个理由:编译器不需要担心命名与保留字冲突,以及保留字在未来进行扩展代价更小。此外,非命名标识符允许编译器能够迅速拿出一个临时变量,而不必以避免符号表冲突。
LLVM语言的保留字和其他语言非常类似。不同的关键字对应不同的opcode('add', 'bitcast', 'ret'....),不同的类型名('void', 'i32'...)。他们不会和变量名重复,因为他们不以'@', '%'开头。
下面是一个LLVM语言例子,int x 乘以 8:
After strength reduction:
%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
最后一个例子说明了LLVM语言的一些特征:
1、注释行以“;”开头
2、如果计算结果没有被赋值给一个命名值,那么一个临时非命名值会创建,并被赋值。
3 、临时非命名值,是从‘0’开始,按顺序递增命名的。注意基本块和非命名的函数参数也包含在这里。
High Level Structure
Module Structure
LLVM程序由模块组成,每一个模块都是输入程序的一个翻译单元。每个模块包括:函数、全局变量、符号表。模块将被LLVM linker组合起来:合并函数(全局变量)定义,解决向前声明,合并符号表。下面是一个“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}
|
在一般情况下,一个模块的组成:函数和全局变量类的全局值、一个指向一个存储器位置指针(在本例,一个指向字符数组,和一个指针的函数),和linker type(详见下文)。
Linkage Types(链接类型)
所有的全局变量和函数都有一个如下的链接类型:
private
和private类似,但是这个值作为一个局部符号出现在对象文件(ELF)中。这个和C语音中的"static"关键字保持一致。
available_externally
该值不会被输出到对象文件,一个available_externally值,等价于一个外部声明。
linkonce
.....
weak
这是用于C语音的“弱”全局变量。
common
appending
extern_weak
linkonce_odr, weak_odr
external
如果没有使用上面的类型,则是默认external,这意味着该变量参与链接,并可以被外部模块引用。
函数的链接类型只能是external或者extern_weak
Calling Conventions(调用约定)
LLVM有多个可选的函数调用约定,但是每一对调用者(caller)和被调用者(callee)的约定必须相匹配。LLVM支持的调用约定如下,未来可能会支持更多:
“ccc
” - The C calling convention
“fastcc
” - The fast calling convention
“coldcc
” - The cold calling convention
“cc 10
” - GHC convention
“cc 11
” - The HiPE calling convention
“webkit_jscc
” - WebKit’s JavaScript calling convention
“anyregcc
” - Dynamic calling convention for code patching
“preserve_mostcc
” - The PreserveMost calling convention
“preserve_allcc
” - The PreserveAll calling convention
“cxx_fast_tlscc
” - The CXX_FAST_TLS calling convention for access functions
“cc
” - Numbered convention
任一调用约定可以通过数字指定,允许使用有针对性的调用约定。调用约定开始于64。更多的调用约定可以按需求进行添加定义。
Visibility Styles (可见样式)
全局变量或者全局函数有如下可见样式:
“default”
对于ELF object file 格式,默认样式代表声明在模块外可见。对于动态库,默认样式代表声明有可能被覆盖。在Darwin系统,默认可见性意味着声明对其它模块可见。默认可见性对应于语言的“外部链接”。
“hidden
” - Hidden style
通常,隐能见度表示该符号将不被置于动态符号表,所以没有其它模块(可执行或共享库)可以直接引用它。
“protected
” - Protected style
对于ELF,受保护的可视性指示该符号将被放置在动态符号表,但该定义模块中的引用将结合到本地符号。也就是说,符号不能被另一个模块覆盖。
DLL Storage Classes (DLL存储类)
变量可以定义为thread_local,这意味着它不会被线程共享。不是所有的目标系统都支持线程局部变量。可选的,TLS模型可以指定:
localdynamic
只在当前共享库中使用的变量。
initialexec
不会被动态加载模块的变量。
如果没有指定明确的类型,默认使用“general dynamic.”
Structure Types
LLVM IR可以让你同时指定“identified”和“literal”结构类型。literal类型结构唯一,但identified类型不唯一。一个不透明(未指定结构体内容)的结构类型,也可用于向前声明。
一个 identified structure 的例子:
%mytype = type { %mytype*, i32 }
|
Global variables
全局变量的内存区域时在编译时分配,而不是在运行时。
全局变量定义时必须初始化。
全局变量在其他模块也可以声明,但不会初始化。
或者全局变量的定义,或者它的声明会有一个明确的放置位置,和一个可选的对齐指定。
一个变量可能被定义成全局常量,表明它的内容不允许被修改(允许数据被放在可执行文件的只读数据区)。注意,假如一个变量需要在运行时初始化,那么它不能被声明为常量。
LLVM明确地允许全局变量声明为常量,即使实际定义不是常量。
LLVM语言中,所有内存对象都是通过指针访问。
全局变量可以加上unnamed_addr标示,指示的地不显著要,只关心内容。如果一个常量标记了unnamed_addr,那么它有可能和另一个初始化值相等的常量合并。注意一个地址显著的常量,可以和一个unnamed_addr 常量合并,结果是一个地址显著的常量。
全局变量可以被声明在指定的地址编号空间。对于支持这点的平台,地址空间将会影响如何优化。默认地址空间编号是0,地址空间限定符必须在其他属性的前面。
LLVM允许为全局值指定明确的section,如果目标平台支持的话。
全局值可以指定对齐(大小是2的幂次方)。默认对齐为0.
全局值也可以有一个DLL storage class.属性。变量和别名可以是Thread Local Storage Model.
语法:
[@ =] [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal]
[unnamed_addr] [AddrSpace] [ExternallyInitialized]
[]
[, section "name"] [, comdat [($name)]]
[, align ]
|
例如,下面定义在一个指定初始化值、节、对齐大小、地址编号空间的全局值:
@G = addrspace(5) constant float 1.0, section "foo", align 4
|
下式,仅仅声明一个全局值:
下式定义了一个initialeexec TLS模式的全局变量:
@G = thread_local(initialexec) global i32 0, align 4
|
LLV语言函数定义的组成包括:一个关键字“define”、一个函数名、一个返回值类型、一个参数列表(可能为空)、一个可选的链接类型、一个可选的可见性类型、一个可选的DLL_storage_class、一个可选的调用约定、一个可选的unname_addr属性、一个可选的参数属性、可选的函数属性、可选的节属性、可选的对齐指定、可选的Comdat,可选的垃圾收集器的名字,一个可选的前缀,可选的序幕,一可选的个性,附加的元数据的可选列表,一个大括号,基本块列表,右大括号。
LLVM函数声明则包括:“declare”关键字,可选的链接类型,可选的可见样式,可选的DLL存储类,可选的调用约定,可选unnamed_addr属性,返回类型,返回类型可选参数属性,函数名,参数可能为空列表,可选的对齐,可选的垃圾收集器的名字,一个可选的前缀,和一个可选的序幕。
函数定义包括一个列表,列表里面的基本块来自函数的CFG(Contorl Flow Graph)。每个基本块,可选的从一个标签开始(给予基本块一个符号表项),包含一个指令列表。基本块以一个 终结指令结束(如一个跳转指令、或者一个函数返回)。如果没有指定开始标签,那么基本块被分配一个隐式编号的标签,标签序号从0开始地址(相同于上文中临时变量的命名)。例如,一个默认的块标签:“%0”,然后该块的第一个非命名的临时变量将会是“%1”。
函数的第一个基本块有2个特殊的地方:它在函数开始的时候立即被执行;它不允许有先于它执行的模块。
LLVM允许为函数指定一个明确的节。
LLVM允许为函数指定一个对齐。默认对齐为0。
如果函数被指定unname_addr属性,则它可以被和另一个完成相同的函数合并。
语法:
define [linkage] [visibility] [DLLStorageClass]
[cconv] [ret attrs]
@ ([argument list])
[unnamed_addr] [fn Attrs] [section "name"] [comdat [($name)]]
[align N] [gc] [prefix Constant] [prologue Constant]
[personality Constant] (!name !N)* { ... }
|
参数列表使用逗号分割,每个参数语法如下:
别名不像函数和变量,它不会创建任何新的数据,只是有个新符号和元数据指向已经存在的地址。
别名可以是一个函数或者常量表达式的别称。
别名可选属性有:链接类型、可见类型、DLL储存类型、tls类型。
语法:
@ = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal] [unnamed_addr] alias , * @
|
注意链接类型必须是:private, internal, linkonce, weak, linkonce_odr, weak_odr, external 其中之一。注意有些系统的链接器如果无法正确处理链接类型,会下降为弱符号别名。
由于别名只是第二个名字,因此会有一些限制:
- The expression defining the aliasee must be computable at assembly time. Since it is just a name, no relocations can be used.
- No alias in the expression can be weak as the possibility of the intermediate alias being overridden cannot be represented in an object file.
- No global value in the expression can be a declaration, since that would require a relocation, which is not possible.
COMDAT