LLVM中局部变量本质是栈地址而非值,通过alloca分配内存并返回指针,读写需显式load/store;函数参数默认为值,传址需前端按C语义降级为指针类型。

LLVM 中局部变量本质是栈地址,不是值
LLVM IR 里没有“变量”这个概念,只有内存位置和值。当你写 int a = 42;,LLVM 不会直接生成一个叫 a 的整数,而是先用 alloca i32 在栈上分配一块内存,返回一个 i32* 类型的指针(比如 %a = alloca i32),后续所有读写都基于这个指针操作。
这意味着:你不能对 %a 直接做 add 或 ret —— 它是个地址,不是值。常见错误就是忘了 load 就直接当整数用,结果编译器报错:invalid operand type for instruction。
写变量必须用 store,且目标必须是指针
store 指令严格要求第一个参数是待存的值(Value*),第二个参数是目标地址(Value*,且类型必须是对应类型的指针)。比如给 %a 赋值 42:
store i32 42, i32* %a
注意两点:
- 右边常量
42是i32类型,左边%a是i32*,类型必须匹配; - 如果用 C++ API 构造,得确保传入的是
AllocaInst*(即指针)作为 store 的 ptr 参数,而不是它 load 出来的Value*。
读变量必须用 load,且源必须是指针
从 %a 读值,必须显式 load:
%val = load i32, i32* %a
这条指令生成一个新寄存器 %val,类型是 i32,才能参与加减、传参、返回等操作。漏掉这步,%a 仍只是个地址,无法参与算术运算。
容易踩的坑:
- 在函数参数或全局变量场景下误用
load:函数参数本身已是值(如i32 %x),不需要load;全局变量是@g,需用load但得先取地址(getelementptr或load全局变量本身需配合global类型); - 对 const 局部变量(如
const int c = 5;)也生成alloca + store + load,其实可优化为直接用常量i32 5—— 但前端不做判断时,IR 就会冗余; - 作用域嵌套时,多个同名
%a是不同AllocaInst,符号表必须按作用域区分,否则load会读错栈帧。
实参传递时,指针语义要特别处理
C 语言中数组/结构体传参默认是“传地址”,但 LLVM IR 里函数参数全是值。所以当 C 函数形参是 int arr[] 或 struct S s,LLVM 通常把它们降级为指针类型(i32* 或 %S*),调用方需传入地址而非值。
这意味着:如果你在编译器里遇到 f(a),而 a 是局部变量,你不能直接把 %a(指针)传过去 —— 得确认函数签名是否期望指针;若期望值(如 void f(int)),就得 load %a 后再传。
关键点:
- LLVM 不自动推导“传值 or 传址”,一切由前端根据 C 语义决定;
- 同一个变量名,在不同上下文(左值 vs 右值、实参 vs 赋值右端)触发的 IR 指令完全不同;
- 忘记区分 LVal(产生指针)和 RVal(产生值)是新手最常卡住的地方。

















