编译入门那些事儿(6):LLVM Metadata System
发表于 2023/08/30
0
Metadata 背景介绍
由于 LLVM IR Opcode 的表达能力有限,为了将额外的信息传递给优化器和代码生成器,LLVM 通常有三个解决方法,分别是 Attribute、Metadata 和 Intrinsic。如下图所示,三种方法各有优劣,使用哪种方法取决于需要传递的信息量。
Metadata 根据命名可知,代表元数据,它没有类型,也不是直接表示值,是用于描述 LLVM IR 的元数据格式。可以用来存储关于 LLVM IR 中各种实体(如:函数、变量、指令等)的附加信息,例如调试信息、优化提示、源代码位置等。
Metadata 组成
所有的 Metadata 都会用一个感叹号 ! 定义。在 LLVM 中 Metadata 的子类有四个:DistinctMDOperandPlaceholder、MDNode、MDString 和 ValueAsMetadata,Metadata 常见的组成可以是如下几种形态:
(1)DistinctMDOperandPlaceholder 是一个占位符,为了解决前置引用问题,在 Operand 创建后可以用 RAUW 函数替换,使用场景较少。
(2)MDNode 是一种复合的类型,可以随意组合各种 Metadata,比如:!{!"BiShengCompiler", i32 666666}。
(3)MDString 可以表示字符串,比如:!"BiShengCompiler"。
(4)ValueAsMetadata 是可以将 Value 类转化成 Metadata,通常会把 constant 类转为 Metadata,比如:i32 666666。
Metadata 使用场景
上一节我们知道了 Metadata 的组成元素,本节主要讲如何应用到 IR 中。
(1)作为函数的参数,比如:
call void @llvm.dbg.value(metadata !24, metadata !25, metadata !26)
(2)作为指令参数,比如:
%indvar.next = add i64 %indvar, 1, !dbg !21
(3)作为函数或者变量的参数,比如:
declare !dbg !22 void @f1()
define void @f2() !dbg !22 {
ret void
}
@g1 = global i32 0, !dbg !22
@g2 = external global i32, !dbg !22
(4)Named Metadata,它是 Metadata 的集合,可以在 Module 的符号表中查找,比如:
!foo = !{!4, !3}
Metadata 存储
Metadata 在 IR 中存储的时候可能会有关键字限定,比如如下两个写法就会有很大的差异:
!0 = !{ !"test\00", i32 10}
!0 = distinct !{!"test\00", i32 10}
Metadata 的存储可分为如下三种模式:
(1)uniqued:顾名思义,表示 Metadata 唯一,在 Metadata 中不会显式打印,如果和其它的 Metadata 相同,会被合并。例如:已经有 !0 = !{ !"test\00", i32 10},需要再生成一个类似于 !1 = !{ !"test\00", i32 10} 的 Metadata 时,不会直接生成 !1,而是把使用 !1 的地方改成使用 !0。
(2)distinct:与 uniqued 相反,表示不唯一,不能和其他相同的合并,即上例中会生成新的 !1。
(3)temporary:表示临时存在,为了解决前置引用的问题,比如构建如下的 Metadata:

Metadata 总结与应用
LLVM Metadata 是 LLVM IR 中非常重要的一部分,它提供了丰富的信息,帮助开发人员更好地理解和优化 LLVM IR,因此 LLVM Metadata 在编译器中被广泛使用,特别是在调试和优化方面。常见的场景有:
(1)调试:可以保存源代码位置、变量名、类型等信息,用于调试器或其他工具的调试。
(2)优化:可以保存代码结构、数据流等信息,用于优化器的优化。
(3)其他:可以保存任何与 LLVM IR 相关的信息,例如源代码注释、版本信息等。