可见性模式()
所有全局变量和函数具有以下的可见性模式之一:
- “ default” - Default style 在那些使用ELF object file格式的平台( targets),默认可见性意味着声明对于其他模块是可见的,并且在可共享库,意味着这个声明的实体是可被覆盖的。在 ,默认可见性意味着声明对于其他模块是可见的。默认可见性与在这种语言中的 “external linkage” 是一致的。 “ hidden” - Hidden style 一个对象的带有hidden可见性的两个声明,如果它们是处于一个相同的可共享单元( shared object),那么它们会被引用到一个相同的对象。通常来说,hidden可见性表明符号不会被放置到动态符号表,所以其他模块(可执行程序 或 共享库)不可以直接引用这个符号 “ protected” - Protected style 在ELF中,protected 可见性表明符号会被放置到动态符号表,但在特定模块中的这些引用会绑定到一个本地的符号。所以这个符号不能被其他模块重写。
DLL存储类别()
所有全局变量,函数,别名(Aliases)可以拥有下面的DLL存储类别之一:
- dllimport “ dllimport” 会导致编译器通过一个指向到 被DLL导出的指针 的全局指针,来引用一个函数或变量。在微软windows平台,这个指针名的格式为 __imp_ 接上引用的函数或变量的名称(__imp_函数名)。 dllexport “ dllexport” 会导致编译器提供一个指向 一个在DLL中的指针 的全局变量,所以它可以被引用到带有 dllimport 属性的实体。在微软windows平台,这个指针名的格式为 __imp_ 接上引用的函数或变量的名称(__imp_函数名)。为了使编译器,汇编器和链接器知道某个符号是被外部引用并且防止这个符号被删除,因此这个存储类别为了定义一个dll接口而存在的。
具名类型()
LLVM IR允许你为明确类型标识其名称的别名。这使得阅读IR更加得容易且使IR更加紧凑(condensed )(实际上这与递归类型有关)。一个名字标识的例子如下:
%mytype = type { %mytype*, i32 }
你可以给定对除了“”任何类型一个名称。类型名的别名可以被使用到任何期望 “%mytype”语法的地方。
注意,这些类型名是构造类型指明的名称的别名,因此你可以对同一个类型标识多个名字。这通常会在dumping out一个 .ll文件的时候导致行为混乱。由于LLVM IR使用构造类型,所以这个名称不是这个类型的一部分。当print out LLVM IR,printer会挑选一个名称替代来同一类型的所有名称(the printer will pick one name to render all types of a particular shape. 。。。为相同形状着色,囧)。这意味着如果你有最终具有相同LLVM 类型的两个不同的源类型在一段代码中,那么dumper会打印the “wrong” 或 unexpected type。这是一个重要的设计点且将不会被改变。
全局变量()
全局变量在编译期定义存储分配范围而不是运行期。
全局变量定义必须被初始化,被放置在一个显式的section中,且有一个可选的显式对齐标识。
全局变量在其他的编译单元也可以被声明,但在这种情况下它们没有初始化式(initializer)。
一个变量可能被定义为thread_local,,这意味着这个变量不会被其他线程所共享(每一个线程会拥有这个变量的一份单独的拷贝)。
不是所有平台都支持thread-local变量。可选地,一个TLS模型可以标识:
localdynamic
标识仅用于当前可共享库的变量
initialexec
标识在模块中不会被动态加载的变量
localexec
标识在可执行程序中定义且只在可执行程序中使用的变量
- 这种模型与ELF TLS模型是一致的;详见。如果指定的模型不被支持或者有一个更合适的模型可供选择,平台可能会选择一个不同TLS模型。 一个变量可能被定义为一个全局常量,表明其内容从不会被修改(使其能够更好地被优化,允许这个全局数据被放置到可执行程序的只读section)。注意,需要在运行期被初始化的变量不能被标识为 constant,因为初始化时有一个到这个变量的存储操作。 LLVM允许明确全局变量的声明被标识为constant,计时这个全局变量的最终定义不是constant。这种能力可以使程序获得些微的优化,但这要求这个语言定义保证基于“constantness”的优化对于不包含这个定义的编译单元是有效的。 作为SSA值,全局变量定义为指针值,其作用域是程序中的所有基本块。全局变量永远定义为一个其content所对应的类型的指针因为他们描述一个存储范围,所有这些LLVM中的存储对象被通过这个指针访问。 全局变量可以被 unnamed_addr 标识,表明它的地址是没有意义的,仅仅是指向其内容。一个常量被这样标识可以被合并到其他拥有相同初始化式的常量。注意,一个地址有意义的常量可以被合并到一个 unnamed_addr 常量,且合并结果变为一个地址有意义的常量。 一个全局变量可能被声明驻留在一个平台指定的编号地址空间。对于支持编号地址空间的平台,地址空间会影响优化被怎么处理及使用什么指令来访问变量。默认的地址空间是0。地址空间限定符必须放在其他任何属性前。 LLVM允许一个明确的section用于指定全局变量。如果目标平台支持它,他会发散全局变量到这个指定section。
默认下,全局初始化式通过假设被定义在模块中的全局变量是在全局初始化式之前他们的初始值是不会被修改的。对于可能从外部被访问的变量,包括带有“external”链接标识的全局变量 或者 出现在@llvm.used 或者 dll导出的变量,这种猜想也是正确的。这种猜想会被带有"externally_initialized"的变量抑制。
一个显式对齐可能被标识于一个全局变量,这个显示对齐一定是2的次幂。如果对齐属性标识不存在,或者对齐属性被设置为0,那么这个全局变量的对齐属性将会被根据目标平台需求设置。如果一个显式的对齐属性被标识,这个全局变量被迫完全按照这个对齐属性对齐。如果这个全局变量有一个被分配到的section,目标平台和优化器不会允许over-align这个全局变量。在这种情况下,额外的对齐是显而易见的:例如,代码可能猜想全局变量被集中放置到他们的section中并尝试以数组形式遍历他们,但对齐填充会打破这个遍历过程。
全局变量同样可以拥有一个
语法:
[@ =] [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal] [AddrSpace] [unnamed_addr] [ExternallyInitialized] [, section "name"] [, align ]
例如,下面定义了一个存储在编号地址带有一个初始化式,section和对齐属性的全局常量。
@G = addrspace(5) constant float 1.0, section "foo", align 4
下面是一个只声明全局变量的例子
@G = external global i32
下面是一个定义了thread-local的带有initialexec TLS模型的全局变量
@G = thread_local(initialexec) global i32 0, align 4