LLVM编译C结构体时IR类型与访问指令的源码位置咨询
LLVM解析C结构体的源码位置解析
示例代码
C源码
struct test_struct1 { int *a; int b; }; int main(int argc, char *argv[]) { int a = 1; struct test_struct1 t1 = {&a, 0}; return 0; }
生成的LLVM IR
; ModuleID = 'test.c' source_filename = "test.c" target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" target triple = "wasm32" %struct.test_struct1 = type { ptr, i32 } ; ① 生成的结构体类型 ; Function Attrs: noinline nounwind optnone define hidden i32 @__main_argc_argv(i32 noundef %0, ptr noundef %1) #0 { %3 = alloca i32, align 4 %4 = alloca i32, align 4 %5 = alloca ptr, align 4 %6 = alloca i32, align 4 %7 = alloca %struct.test_struct1, align 4 store i32 0, ptr %3, align 4 store i32 %0, ptr %4, align 4 store ptr %1, ptr %5, align 4 store i32 1, ptr %6, align 4 ; ② 生成的store访问指令 %8 = getelementptr inbounds %struct.test_struct1, ptr %7, i32 0, i32 0 store ptr %6, ptr %8, align 4 %9 = getelementptr inbounds %struct.test_struct1, ptr %7, i32 0, i32 1 store i32 0, ptr %9, align 4 ret i32 0 } attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+mutable-globals,+sign-ext" } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"wchar_size", i32 4} !1 = !{!"clang version 16.0.6"}
一、结构体类型定义(①)的源码位置
C结构体到LLVM IR类型的转换由Clang前端完成,核心代码位置如下:
- 结构体布局计算(成员偏移、对齐规则):
clang/lib/AST/RecordLayoutBuilder.cpp,这里负责处理结构体的内存布局逻辑。 - AST结构体到LLVM IR类型的转换:
clang/lib/CodeGen/CGRecordLayout.cpp,CGRecordLayoutBuilder类会将Clang的RecordDecl节点转换为LLVM的StructType,最终通过CodeGenModule::getTypes().ConvertTypeForMem等接口输出到IR中。 - 结构体语义分析与AST构建:
clang/lib/Sema/SemaDecl.cpp的ActOnTagDecl函数,完成结构体的语法校验与AST节点创建,为后续CodeGen阶段提供基础。
二、访问指令(②)的源码位置
结构体成员访问、变量存储等IR指令同样由Clang的CodeGen模块生成:
store指令生成:clang/lib/CodeGen/CGExpr.cpp中的CodeGenFunction::EmitStoreOfScalar函数,负责处理标量数据的存储操作,对应示例中变量初始化的store指令。- 结构体成员地址计算(
getelementptr指令):clang/lib/CodeGen/CGExpr.cpp的CodeGenFunction::EmitMemberExpr函数,处理结构体成员访问时,会调用GetElementPtrInst::Create生成GEP指令,定位到结构体成员的内存地址,再配合store完成赋值。 - 栈空间分配(
alloca指令):clang/lib/CodeGen/CGStack.cpp,负责为局部变量(包括结构体实例)分配栈内存。
排查思路
- 跟踪结构体类型生成:在
CGRecordLayout.cpp中设置断点,观察RecordDecl到StructType的转换流程;或通过Clang的-debug编译选项,打印类型转换阶段的日志。 - 跟踪访问指令生成:在
CGExpr.cpp的EmitStoreOfScalar和EmitMemberExpr函数中设置断点,跟随代码执行,查看变量初始化、结构体成员赋值时的IR指令生成过程。 - 结合Clang前端流程:Clang处理C代码分为预处理→语法分析→语义分析→CodeGen四个阶段,结构体类型在语义分析阶段完成AST构建,CodeGen阶段转换为IR类型;访问指令则是在CodeGen阶段处理表达式时生成。
内容的提问来源于stack exchange,提问作者Starry
相关产品推荐
相关产品推荐

