LLVM IR中全局变量必须以@开头、显式声明类型和初始化器,使用global关键字;未初始化会报错,零值需用zeroinitializer;字符串常量应标记为private unnamed_addr constant以优化和保证只读性。

全局变量必须用 @ 开头,且需显式指定类型和初始化器
LLVM IR 中的全局变量不是“声明即存在”,而是必须带完整类型、初始值和链接属性。不初始化会报错(error: global variable definition must have an initializer),哪怕你想让它默认为零——得写 zeroinitializer。
常见错误是只写 @x = i32 42,这非法;正确写法必须包含 global 关键字和类型:
@x = global i32 42 @y = global i32 zeroinitializer @z = global [5 x i8] c"hello"
注意:global 是关键字,不是修饰符;i32 是变量所存数据的类型,而 @x 本身是一个指针(i32*),所有对它的 load/store 都要通过指针操作。
-
getelementptr访问数组元素时,第一个索引永远是 0(因为@z是指向数组首地址的指针) - 若想让变量只在当前 module 内可见,加
internal:@x = internal global i32 42 - 若变量来自外部(如 C 标准库),用
external:@errno = external global i32
字符串常量要用 private unnamed_addr constant 修饰
直接写 @msg = global [12 x i8] c"hello world\00" 虽能编译,但会把字符串放进可读写数据段,失去优化机会,也违背常量语义。正确做法是标记为 constant,并加 unnamed_addr 允许合并相同字面量:
@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"
关键点:
-
private:不进符号表,避免链接冲突 -
unnamed_addr:告诉 LLVM “地址不重要”,两个相同内容的字符串常量可被合并为一个 -
constant:表示运行时不可修改,后端可将其放入 .rodata 段 - 长度必须精确(含终止
\00),不能写[12 x i8]却放 13 字节内容,否则 verifier 报错
getOrInsertGlobal 是 C++ API 创建全局变量的唯一安全方式
在 LLVM C++ API 中,不能直接 new GlobalVariable;必须用 Module::getOrInsertGlobal 获取或创建,再手动设初始化器和属性:
GlobalVariable *gv = m->getOrInsertGlobal("counter", Type::getInt32Ty(c));
gv->setInitializer(ConstantInt::get(c, APInt(32, 0)));
gv->setLinkage(GlobalValue::InternalLinkage);
漏掉 setInitializer 会导致 verifyModule() 失败;如果之后调用 gv->setConstant(true),则初始化器必须是常量(不能是 undef 或 runtime 计算值)。
- 若变量需对齐(如 16 字节对齐 SSE 数据),用
gv->setAlignment(MaybeAlign(16))(LLVM 13+) - 若想指定 section(如
.data.rel.ro),调用gv->setSection(".mydata") - 不要重复调用
getOrInsertGlobal同名变量,它不会覆盖,而是返回已有实例
全局变量地址空间和链接类型选错会导致链接失败或未定义行为
比如把本该是 ExternalLinkage 的 C 函数声明写成 InternalLinkage,链接器找不到符号;又或者把跨模块共享的变量设为 PrivateLinkage,其他 module 看不到它。
最易踩的坑是误用 CommonLinkage:它只适用于未初始化的全局变量(C 里的 tentative definition),且要求所有同名 common 变量类型一致,否则行为未定义。
-
AvailableExternallyLinkage常用于模板实例化,仅提供定义供内联,不生成实际符号 - 嵌入式目标若用非零地址空间(如
addrspace(1)),必须确保所有访问指令(load/store)显式指定该地址空间 -
AppendingLinkage只支持全局数组,且只在特定后端(如 Darwin)有效,通用性差
真正难调试的不是语法错误,而是链接时符号缺失或运行时读到错误内存——根源往往在 linkage 和 address space 的配置偏差上,而不是初始化值本身。
















