LLVM language 参考手册(译)(1)

LLVM Language Reference Manual

摘要

  这个文档是一个LLVM汇编语言的参考手册。LLVM是一个基于Static Single Assignment(SSA - 静态单赋值)表示,提供了类型安全,低级别操作,灵活性和表现“所有”高级语言的能力。他是在LLVM编译策略的各个阶段中使用的通用代码表示。

介绍

  LLVM的代码表示形式被设计为使用三种不同的格式:1、表示为在内存中编译器中间语言,表示为在磁盘上的位码(适合于即时编译器的快速加载) ,表示为人类可读的汇编语言。LLVM为编译器的高效转换和分析提供了强大的中间语言,同时提供一个自然的方法来调试和可视化的转换。LLVM的这三种不同形式的代码表示的都是等价的。本文档描述了人类可读的代表性和符号。

  LLVM表示法的目标是实现轻量和低级别同时是有表现力的,类型化,可扩展。它的目标是成为一个“通用IR”的排序,由是在一个足够低的水平是高层次的思想可以被清晰地映射到它(类似于微处理器是如何“万能IR的” ,这让很多源语言被映射到它们) 。通过提供类型信息, LLVM可以作为优化的目的:例如,通过指针分析可以证明,一个C的自动变量是从来没有被当前函数以外的地方访问,那么就可以让它被提升到一个简单的SSA值中而不是存储单元中。

  Well-Formness(规范化)  

注意这个文档描述规范化的LLVM汇编语言。这有区别于“可以被解释的就是(well-formed)格式良好的” 的概念。例如,下面的指令在语法上是OK的,但不是'well form'(格式良好的)的:  

    %x = add i32 1, %x

这是因为%x的定义不能支配到所有使用了%X的地方。 LLVM的架构提供了一个verification pass,用于验证一个LLVM模块是否规范化。这个pass将自动于解释器解释输入汇编之后和优化程序输出bitcode之前。被verifier pass指出的违规行为表现为在transformation passes或解释器的输入的bug

Identifiers

  LLVM的identifier有两种基本类型:global和local。Global identifiers(函数,全局变量)以“@”字符开头。Local  identifiers(寄存器名称,类型)“%”字符开头。此外,有三个不同的格式identifiers,用于不同的目的:

  1、具名值表示为一段字符串加上他们的前缀。例如,%foo@DivisionByZero%a.really.long.identifier. 其正则表达式为 ‘[%@][a-zA-Z$._][a-zA-Z$._0-9]*

  (前缀后的字符串不能以数字开头)identifier当需要在名称中的其他字符的时候可以用引号包围该字符。特殊字符可以使用“ XX”,其中xx是字符的十六进制的    ASCII码进行转义。以这种方式,任何字符可以在名称中被使用,甚至“字符也可以。

  2、非具名值表示为一个无符号数值加上他的前缀,例如,%12@2%44。

  3、常量,详细描述在下面的Constant部分。

  LLVM要求值以一个前缀开始有两个原因:1、编译器不需要担心值的名称会和保留字(reserved words)冲突,保留字(reserved word)集可以在未来扩展的 时候不会出现惩罚(penalty,不会发生冲突)。除此以外,非命名的identifier允许编译器快速找出一个临时变量且不会造成符号表冲突。

  LLVM中的reserved words与其他语言的reserved words非常相似。有相应的关键字对应着不同的操作码有 (‘add‘, ‘bitcast‘, ‘ret‘, etc...),原始类型名(‘void‘, ‘i32‘, etc...)和其他。这些保留字不会与变量名冲突,因为他们之中没有一个是以前缀 ('%' or '@')开头的。

  这里有一个表示用8乘上一个整型变量‘%X‘ 的LLVM代码例子:  

  The easy way:

    %result = mul i32 %X, 8

  在强度折减后:(关于强度折减大家可以通过SSA的页面链接去了解一下)

    %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的几个重要的词法特点:

    1、注解是以 ‘;‘ 分隔且直到当前行的结尾

    2、当计算的结果不能被赋值的一个具名值的时候,非命名临时变量被创建

    3、非具名临时变量是按顺序编号的(使用一个递增计数器,从0开始)。注意整个基本块都被包含在这种编号方法中。例如,如果一个基本块的入口没有被给予    一个标签名,那么它就会获得一个编号0

  它也表明了一个在这个文档我们应该遵循的约定。当演示指令的时候,我们应该使一个定义了被创建的值的类型和名称的注释紧跟这条指令后面

高层结构(High Level Structure)

  模块结构(Module Structure) 

; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello worldA0"

; 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]* @.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
!1 = metadata !{i32 42}
!foo = !{!1, null}

这个例子由一个全局变量".str",一个"puts"函数的外部声明,一个"main"函数的函数定义和一个具名元数据"foo"组成。

一般情况下,一个模块由全局值(函数和全局变量都是全局值)的列表组成,全局值通过存储单元的指针表示(在这种情况下,一个指针指向字符数组,一个指针指向一个函数)并具有以下的linkage type之一

(链接类型)Linkage Types

(以下symbol译作符号,identifier译作标识符,但两者意义基本相同,如不可互换时会加以注释)

所有的全局变量和函数有联系的下列类型之一: 

private

  "private"链接的全局值只能由当前模块中的对象直接访问。特别地,链接代码到一个包含"private"全局值的模块时在必要情况下可能会造成"private"全局值rename来避免冲突。因为这个symbol是对当前模块私有的,所以所有对这个全局值的引用都可以被更新(名字)。这并不会在object file的任何symbol table中展示出来。"

 

linker_private

与"private"相似,但该symbol会传递到assembler(汇编器)中并通过linker(链接器)求值。不同于普通的强符号,它们会链接器从最终链接映像(可执行文件或动态库)中被移除。(但处于不同模块的linker_private symbol(符号)不会被合并)

linker_private_weak

与"linker_private"相似,但是这个symbol是weak的(弱符号)。注意 linker_private_weak symbol是受链接器合并的。 它们会链接器从最终链接映像(可执行文件或动态库)中被移除。

internal

与"private"相似,但该值在object file表现为local symbol (STB_LOCAL in the case of ELF) . 这对应于C中的“static”的关键字的概念。

在这里复习一下链接的知识:http://www.cnblogs.com/kirito/p/3561900.html

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.
extern_weak
这个链接标识的语义遵循ELF object file模型:除非被链接,否则带有extern_weak的symbol是弱的,如果没有被链接该符号会变为null而不是作为未定义引用。
linkonce_odrweak_odr
某些语言允许不同的全局变量被合并,例如具有不同的语义的两个函数。其他语言,如C + + ,确保只等效的全局变量才可以合并( “one definition rule” - “ ODR ” ) 。这些语言可以使用linkonce_odr和weak_odr链接标识来表明全局变量将只与等效的全局变量合并。这些链接标识类型的其他语义与其非ODR版本相同。
external
如果上述标识符都没被使用,那么该全局变量的是外部可见的,这意味着它参与链接,可用于解析外部符号引用。

一个函数声明拥有除“external" 或 “extern_weak"以外的链接标识是不合法的。



原文地址:https://www.cnblogs.com/kirito/p/3561039.html