别名
别名不会创建任何新的数据,只是现有位置的新符号和元数据。
别名有一个名字和别名,可以是全局值或者常量表达式。
别名可能具有可选的连接类型,可选的运行时抢占说明符,可选的可见性属性,可选的DLL存储类和一个可选的tls模型。
语法
@ = [Linkage] [PreemptionSpecifier] [Visibility] [DLLStorageClass] [ThreadLocal] [(unnamed_addr|local_unnamed_addr)] alias , * @
链接必须是private,internal,linkonce,weak,linkonce_odr,weak_odr,external中的一个。注意:某些系统连接器可能不能正确的处理丢弃具有别名的弱符号(内存溢出的问题)。
非unnamed_addr的别名保证具有与别名表达式相同的地址。
unnamed_addr只保证指向相同的内容。
如果给定了local_unnamed_addr属性,则知道该地址在模块中不重要。
因为别名只是第二个名字,所以会有一些限制,其中一些只能在生成object文件时检查:
**·**定义别名的表达式在汇编时必须是可计算的。因为它只是一个名称,不能使用重新定位。
**·**表达式中的任何别名都不能是弱的,因为在目标文件中不能表示中间别名被重写的可能性。
**·**表达式中的全局值不能是一个声明,因为这需要重定位,而这是不可能的。
IFUNC
IFuncs和别名一样,不会创建任何新得数据或func,它们只是动态连接器通过调用解析器函数在运行时解析的一个新符号。
IFuncs有一个名称和一个解析器,解析器是由动态链接器调用的函数,它返回与该名称相关联的另一个函数的地址。
IFunc可以有可选的链接类型和可选的可见性样式。
语法
@ = [Linkage] [PreemptionSpecifier] [Visibility] ifunc , * @
Comdats
Comdat IR提供了对对象文件Comdat /section group功能的访问,该功能表示相互关联的section。
COMDAT,即common data. 编译器将一些函数(具体是哪些函数,编译器自行决定)打包放到单独的section中,这有个专有名词叫COMDAT,即common data,意思是打包的函数或者打包的数据。按微软大拿Raymond Chen的说法,COMDAT这个概念最早来自FORTRAN语言。gcc和llvm对COMDAT都有对应的支持。链接器在链接阶段,可以对COMDAT中重复的函数进行消重(folding,折叠)。如果编译器不把函数打包成COMDAT项,链接器是不敢贸然优化掉对应的函数的,因为缺少这些函数的引用信息。
Caomdats有一个表示COMDAT键的名称和一种选择类型,用于提供关于链接器如何在两个不同的目标文件中删除重复的具有相同键的comdats的输入。如果链接器选择了某个其他键的键,则指定的这个键的所有全局对象只会在最终的对象文件中结束。一个comdat必须作为一个单元来包含或省略。可以丢弃整个comdat,但不可以丢弃一个子集。
一个全局对象最多可以是一个comdat的成员。别名放置在其别名所计算的COMDAT中(如果有的话)。
语法:
$ = comdat SelectionKind
对于nodeduplicate以外的选择类型,连接器只能保留一个重复的命令,其余命令的成员必须被丢弃。
支持以下选择类型:
类型 | 说明 |
---|---|
any | 连接器可以选择任何COMDAT键,选择是任意的 |
exactmatch | 链接器可以选择任何COMDAT键,但各section必须包含相同的数据。 |
largest | 链接器将选择包含最大COMDAT键的部分。 |
nodeduplicate | 不执行重复数据删除操作。 |
samesize | 链接器可以选择任何COMDAT键,但各section必须包含相同数量的数据。 |
·XCOFF和Mach-O不支持comdat。
·COFF支持所有选择类型。
·非节点重复选择类型需要一个非本地链接COMDAT符号。
·ELF支持任意和节点副本。
·WebAssembly只支持any。
下面是一个COFF COMDAT的示例,其中只有COMDAT键的section最大时才会选择函数:
$foo = comdat largest
@foo = global i32 2, comdat($foo)
define void @bar() comdat($foo) {
ret void
}
在COFF对象文件中,这将创建一个COMDAT section,它的选择类型是IMAGE_COMDAT_SELECT_LARGEST,包含@foo符号的内容和另一个COMDAT section;这个section的选择类型是IMAGE_COMDAT_SELECT_ASSOCIATIVE,这个选择类型与第一个COMDAT section相关并包含在@bar符号的内容。
作为一个语法糖,如果名称与全局名称相同,可以省略$name:
$foo = comdat any
@foo = global i32 2, comdat
@bar = global i32 3, comdat($foo)
全局对象的属性有一些限制。当以COFF为目标时,它或它的别名必须与COMDAT section具有相同的名称。在链接期间,可以使用此对象的内容和大小,根据选择类型确定选择了哪些COMDAT section。因为对象的名称必须与COMDAT section的名称匹配,所以全局对象的链接不能是本地的;如果在符号表中发生冲突,局部符号可以被重命名。
组合使用COMDATS和节属性可能会产生令人惊讶的结果。例如:
$foo = comdat any
$bar = comdat any
@g1 = global i32 42, section "sec", comdat($foo)
@g2 = global i32 42, section "sec", comdat($bar)
从对象文件的角度来看,这需要创建两个具有相同名称的section。这是必要的,因为两个全局变量都属于不同的COMDAT groups,而在对象文件级别,COMDAT由section表示。
注意,除了使用COMDAT IR指定的结构外,某些IR结构(如全局变量和函数)可能会在目标文件中创建COMDAT。
当代码生成器被配置为在个别区段中发出全局变量时(例如当-data-sections或-function-sections被提供给llc时),就会出现这种情况。
命名元数据
命名元数据是元数据的集合。
元数据节点(后面会有介绍)(而不是元数据字符串)是命名元数据的唯一有效操作数。
命名元数据表示为带有元数据前缀的字符串。元数据名称的规则与标识符的规则相同,但不允许使用引号。"\xx"类型转义仍然有效,它允许任何字符成为名称的一部分。
语法:
; Some unnamed metadata nodes, which are referenced by the named metadata.
!0 = !{!"zero"}
!1 = !{!"one"}
!2 = !{!"two"}
; A named metadata.
!name = !{!0, !1, !2}