LLVM一些语法规则

LLVM一些语法规则

LLVM文档

LLVM编译器基础架构支持广泛的项目,从工业强度编译器到专门的JIT应用程序,再到小型研究项目。

同样,文档分为几个针对不同受众的高级别分组:

LLVM设计概述

几篇介绍性论文和演讲。

用户指南

这些内容适用于那些刚刚接触LLVM系统的人。

注意:如果是仅对使用基于LLVM的编译器感兴趣的用户,则应该查看Clang。此处的文档适用于需要使用中间LLVM表示的用户。

编程文档

对于使用LLVM作为库的应用程序的开发人员。

子系统文档

适用于API客户端和LLVM开发人员。

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 标识符有两种基本类型:

  • 全局标识符(函数,全局变量)以’@’字符开头。
  • 本地标识符(寄存器名称,类型)以’%’字符开头。

标识符有三种不同的格式,分别用于不同的目的:

  1. 命名值表示为带有前缀的字符串。例如%foo,@DivisionByZero,%a.really.long.identifier。使用的实际正则表达式是’
    [%@][-a-zA-Z$._][-a-zA-Z$._0-9]*’。名称中需要其它字符的标识符,可以用引号括起来。可以使用十六进制字符的ASCII代码”\xx”,转义特殊字符xx。通过这种方式,任何字符都可以在名称值中使用,甚至可以引用自己。该”\01″前缀可以在全局值可用于压制截断。
  2. 未命名的值表示为带有前缀的无符号数值。例如%12,@2,%44。
  3. 常量,将在后面的关于常量的部分进行描述。

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

  1. 注解是以 ; 分隔,直到当前行的结尾
  2. 当计算的结果不能被赋值的一个具名值的时候,非命名临时变量被创建
  3. 非具名临时变量,按顺序编号的(使用一个递增计数器,从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

 

 

posted @ 2021-09-16 05:54  吴建明wujianming  阅读(670)  评论(0编辑  收藏  举报