LLVM中如何获取程序全部StructType?多模块去重及首次出现记录方法
在LLVM中跨Module获取全局结构体类型并记录首次出现位置
一、获取整个程序的去重结构体类型
LLVM中单个Module的结构体可通过Module::getIdentifiedStructTypes()直接获取,但多源文件对应多个独立Module时,相同结构体可能重复出现,可通过两种方式实现全局去重获取:
方式1:手动遍历所有Module并维护去重集合
遍历每个待处理的Module,对每个结构体类型,用唯一标识判断是否已记录,避免重复:
- 对于已命名结构体,可结合结构体名称+数据布局作为唯一键(布局相同是类型等价的核心标准,名称避免同布局不同类型的误判);
- 对于匿名结构体,可依赖调试信息或布局特征识别,不过这类结构体通常不会跨源文件复用,按需处理即可。
示例代码片段(基于LLVM C++ API):
#include "llvm/IR/Module.h" #include "llvm/IR/StructType.h" #include <map> // 全局存储:唯一键到结构体类型的映射 std::map<std::pair<llvm::StringRef, llvm::DataLayout>, llvm::StructType*> GlobalStructs; void processSingleModule(llvm::Module &M) { llvm::DataLayout DL(&M); for (auto *ST : M.getIdentifiedStructTypes()) { if (ST->isOpaque()) continue; // 跳过未完全定义的不透明结构体 auto key = std::make_pair(ST->getName(), DL); if (!GlobalStructs.count(key)) { GlobalStructs[key] = ST; } } } // 遍历所有Module时调用processSingleModule即可
方式2:合并所有Module后获取
使用LLVM的Linker工具将多个Module合并为一个全局Module,Linker会自动处理类型合并,此时直接从合并后的Module中获取结构体即可得到去重后的全局集合:
#include "llvm/Linker/Linker.h" llvm::Module* mergeAllModules(llvm::Module &Dest, std::vector<llvm::Module*> &SrcModules) { llvm::Linker L(Dest); for (auto *Src : SrcModules) { if (auto Err = L.linkInModule(std::unique_ptr<llvm::Module>(Src))) { // 处理链接错误 return nullptr; } } return &Dest; } // 合并后直接从Dest中获取结构体 // for (auto *ST : Dest.getIdentifiedStructTypes()) { ... }
二、记录结构体首次出现位置
在遍历或合并Module的过程中,可同步记录结构体首次出现的位置信息,核心逻辑是首次加入全局集合时,记录其所在Module的源文件及调试信息:
实现思路
- 定义结构体存储位置信息:包含Module源文件路径、结构体定义的源文件、行号;
- 遍历Module时,对首次识别到的结构体,通过
Module::getSourceFileName()获取Module路径,通过StructType::getDebugInfo()获取调试信息中的定义位置(如果存在)。
示例代码片段:
#include "llvm/IR/DebugInfoMetadata.h" struct StructOccurrenceInfo { llvm::StringRef ModulePath; llvm::StringRef SourceFile; unsigned LineNum = 0; }; std::map<std::pair<llvm::StringRef, llvm::DataLayout>, StructOccurrenceInfo> StructFirstOccur; void recordFirstOccurrence(llvm::Module &M) { llvm::DataLayout DL(&M); for (auto *ST : M.getIdentifiedStructTypes()) { if (ST->isOpaque()) continue; auto key = std::make_pair(ST->getName(), DL); if (StructFirstOccur.count(key)) continue; StructOccurrenceInfo info; info.ModulePath = M.getSourceFileName(); // 从调试信息中提取定义位置 if (auto *DI = ST->getDebugInfo()) { if (auto *File = DI->getFile()) { info.SourceFile = File->getFilename(); } info.LineNum = DI->getLine(); } StructFirstOccur[key] = info; } }
注意事项
- 若结构体无调试信息(编译时未加
-g参数),则只能记录所在Module的源文件路径,无法获取具体行号; - 合并Module时,Linker可能保留首次出现的结构体调试信息,但若需精准控制,建议手动遍历记录更可靠。
内容的提问来源于stack exchange,提问作者Yerson.Dong
相关产品推荐
相关产品推荐

