在C++ LLVM Builder API中如何获取栈分配变量的地址并赋值给新变量?
LLVM栈变量地址赋值的IR疑问与解答
问题描述
我有一个通过alloca指令创建的栈分配值,类型为Value* v。想要编写C++ LLVM Builder代码,将栈上v的地址赋值给新变量p,对应C语言等效代码:
int c = 10; int* p = &c;
通过GodBolt得到对应的LLVM IR:
%2 = alloca i32, align 4 %3 = alloca i32*, align 8 store i32 %0, i32* %2, align 4 store i32* %2, i32** %3, align 8, !dbg !18 %4 = load i32*, i32** %3, align 8, !dbg !19
对此有以下疑问:
- 为何需要第一个
store指令?它看起来像是把%0冗余复制到%2;为何不能直接取%0的地址? - 第二个
store指令似乎是将值转换为指针类型,这是否实际是复制%2的地址? - 这段IR是否是我需要生成的正确代码?有没有更优的写法?
相关参考
- 如何获取LLVM中alloca指令实际分配的地址?
- 有没有办法知道LLVM中用alloca指令创建的栈分配局部变量的栈地址?
解答
1. 第一个store指令的必要性
%0属于LLVM的SSA临时值(对应C代码里的10,可能是常数或函数参数),这类值仅存在于寄存器中,没有可寻址的内存位置。而alloca i32是在栈上分配了一块i32大小的内存空间,%2是指向这块空间的指针。第一个store指令的作用就是把%0的值写入这块栈内存,完全对应C代码里的int c = 10;——C中的局部变量c本身就是栈上的内存,必须把初始值存入其中,所以这个store并非冗余操作。
你无法直接取%0的地址,因为SSA值没有内存地址,只有通过alloca分配栈内存后,才能得到一个可寻址的指针,进而实现“取变量地址”的操作。
2. 第二个store指令的本质
第二个store不是类型转换,而是把%2的地址值写入%3指向的栈内存:
%3是alloca i32*分配的栈内存,用于存储指针类型的值(对应C中的int* p变量);%2是c的栈地址(i32*类型),store i32* %2, i32** %3就是把c的地址存入p的栈内存中,完全对应C代码里的int* p = &c;。
3. 代码正确性与优化写法
这段IR是正确的,但存在优化空间:
- 如果后续不需要对
p本身进行取地址或修改操作,完全不需要为p分配栈内存,直接用%2作为p的SSA值即可,简化后的IR如下:
%c = alloca i32, align 4 store i32 10, i32* %c, align 4 ; 后续直接使用%c即可,等效于C中的p = &c
- 如果必须保留栈上的
p变量(比如需要修改p指向的对象),原IR的结构是必要的,但可以去掉最后那个冗余的load指令(如果后续没有用到%4的话)。
对应LLVM C++ Builder的实现代码示例:
// 获取LLVM上下文 LLVMContext &context = ...; IRBuilder<> builder(...); // 创建栈变量c的分配 Value* c_alloca = builder.CreateAlloca(Type::getInt32Ty(context), nullptr, "c"); // 将初始值10存入c的栈内存 builder.CreateStore(ConstantInt::get(context, APInt(32, 10)), c_alloca); // 场景1:不需要栈上的p变量,直接用c_alloca作为p的SSA值 Value* p = c_alloca; // 场景2:需要栈上的p变量 Value* p_alloca = builder.CreateAlloca(Type::getInt32PtrTy(context), nullptr, "p"); // 将c的地址存入p的栈内存 builder.CreateStore(c_alloca, p_alloca);
内容的提问来源于Stack Exchange,提问作者NHDaly
相关产品推荐
相关产品推荐

