元数据是LLVM IR中不参与计算、不影响机器码的“旁路提示”,用于携带调试(如!dbg)、优化(如!range、!nonnull)和安全(如!instrumentation)信息;它以!开头,需显式挂载到指令或命名节点才生效,且不可修改,仅供特定pass读取响应。

Metadata 用于携带调试、优化和安全相关的上下文信息
LLVM IR 中的 metadata 不参与实际计算,也不影响生成的机器码逻辑,但它为编译器提供了关键的“旁路提示”——比如某条指令对应源码哪一行、某个变量是否可被优化掉、某段内存访问是否需要加屏障。它像代码里的便签纸,贴在 IR 指令旁边,只供编译器内部 pass 读取和响应。
常见用途:调试信息(!dbg)、优化提示(!range, !nonnull)、安全加固(!instrumentation)
最典型的是调试元数据:!dbg 附着在 load、store 或 call 指令后,告诉调试器该指令映射到 C 源文件的哪一行哪一列。而优化相关元数据更隐蔽但影响直接:
-
!range告诉优化器某个整数参数的取值范围(如i32 0 to 100),可能触发条件分支消除 -
!nonnull标记指针参数非空,使nullptr检查被删掉 -
!dereferenceable表明指针至少可安全解引用 N 字节,影响别名分析结果 -
!instrumentation或自定义!myprof等标签,常被 sanitizer 或 profiling pass 主动查找并插桩
Metadata 不是指令,不能被 opt pass 默认处理
这是最容易踩的坑:元数据不是 IR 指令,不进入 SSA 链,也不在基本块指令列表里出现。你用 opt -O2 跑一遍,!dbg 可能还在,但 !range 很可能被丢掉——除非你显式启用对应 pass(比如 -enable-loop-idiom 会读 !dereferenceable)。另外,元数据节点本身是只读的,一旦创建就不能修改;要“更新”元数据,必须新建节点并替换旧引用。
自定义 metadata 需通过命名节点(named metadata)或 !tag 显式挂载
直接写 !foo = !{i32 42} 只是声明一个元数据节点,它不会自动关联到任何指令。真正起作用的方式有两种:
- 在指令末尾追加:例如
%x = add i32 %a, %b !range !1,其中!1是前面定义的!range节点 - 挂到命名节点上:例如
!llvm.module.flags = !{!0},用于模块级配置(如!denormal-fp-math)
漏掉挂载步骤,或者把元数据类型写错(比如该用 !nonnull 却写了 !noundef),都会导致后端完全忽略它——既不报错,也不生效。

















