LLVM IR 语法

译者序
    目前几乎没有关于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值,而不是一个堆变量。

Well-Formedness
   略...

标识符
    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:
    %result = mul i32 %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
        私有的全局值只能被当前模块的对象直接访问。
    internal
        和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
    不会被动态加载模块的变量。
localexec
     可执行文件中定义和使用的变量

如果没有指定明确的类型,默认使用“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
下式,仅仅声明一个全局值:
@G = external global i32
下式定义了一个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)* { ... }
参数列表使用逗号分割,每个参数语法如下:
 [parameter Attrs] [name]

别名
    别名不像函数和变量,它不会创建任何新的数据,只是有个新符号和元数据指向已经存在的地址。
    别名可以是一个函数或者常量表达式的别称。
    别名可选属性有:链接类型、可见类型、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
.....

Metadata
    LLVM IR 允许元数据被附加到程序指令中,为代码优化和生成提供额外的信息。元数据的一个应用是代码级别的调试信息。有两种元数据元素:字符串和节点。
    元数据不具有一个类型,并且不是一个值。
    所有元数据以‘!’打头。

Metadata Nodes and Metadata Strings
    元数据字符串被双引号包围。它可以包含任何字符,不可打印字符使用“\xx”转义,例如:”!"test\00"“。
     元数据节点用符号表示,好结构体常量有点类似(使用逗号分割元素,使用大括号包围)。元数据节可以包含任何操作数,例如:
!{ !"test\00", i32 10}
    元数据节关键字不唯一,metadata 、distinct都是它的关键字:
!0 = distinct !{!"test\00", i32 10}
    命名的元数据是一个元数据节点的集合,可以查找模块符号表。例如:
!foo = !{!4, !3}
    元数据可以用作函数参数。下面这个函数使用2个元数据参数:
call void @llvm.dbg.value(metadata !24, i64 0, metadata !25)
    元数据可以被附加到一个指令。这里的元数据!21是附着在加法指令,使用!dbg标识符:
%indvar.next = add i64 %indvar, 1, !dbg !21
    元数据也可以被附加到一个函数的定义。这里的元数据!22是附着在foo函数使用!dbg标识符:
define void @foo() !dbg !22 {
  ret void
}
Named metadata
    命名元数据是元数据的一个集合。他的元素是元数据节点。
语法:
; Some unnamed metadata nodes, which are referenced by the named metadata.
!0 = !{!"zero"}
!1 = !{!"one"}
!2 = !{!"two"}
; A named metadata.
!name = !{!0, !1, !2}

Parameter Attributes(参数属性)   
     参数属性是一些简单的关键字紧跟在参数类型后面,如果有多个参数属性,则用空格分割:
declare i32 @printf(i8* noalias nocapture, ...)
declare i32 @atoi(i8 zeroext)
declare signext i8 @returns_signed_char()
    目前可用的参数属性有如下几种:
zeroext
signext
inreg...

Garbage Collector Strategy Names(垃圾收集器策略名称)    
    每个函数都可以指定垃圾回收策略,如下:
define void @f() gc "name" { ... }
    注意,LLVM本身并不包含一个垃圾收集器,此功能仅限于生成机器代码,以便与外部的垃圾收集器进行交互。

Prefix data(前缀数据)
    
Prologue Data(序言数据)

Function attributes
    函数属性关于函数的额外信息的属性集。函数属性被认为是函数的一部分。
    函数属性的关键字如下。如果需要多个属性,它们的空间分离:
define void @f() noinline { ... }
define void @f() alwaysinline { ... }
define void @f() alwaysinline optsize { ... }
define void @f() optsize { ... }
    目前可用的属性有:
    alignstack()
    alwaysinline    
    builtin
    cold
    convergent
    inaccessiblememonly
    inaccessiblemem_or_argmemonly
    inlinehint
    jumptable
    minsize
    naked
    nobuiltin
    noduplicate
    ....

operand bundles







你可能感兴趣的:(LLVM IR 语法)