应使用llvm::WriteBitcodeToFile保存为.bc文件、llvm::print保存为.ll文件,且必须确保Module所属LLVMContext在写入全程有效,否则将导致段错误或空文件;务必调用flush(),禁用Module::dump()作序列化。

直接调用 llvm::WriteBitcodeToFile 或 llvm::print 即可,但必须注意 Module 的所有权和上下文生命周期 —— 错误地提前释放 LLVMContext 会导致段错误或写入空内容。
保存为 .bc 文件:用 llvm::WriteBitcodeToFile
这是最稳妥的二进制序列化方式,适用于后续被 llvm-dis、opt、llc 等工具消费。
-
llvm::WriteBitcodeToFile接收一个const Module &和一个raw_ostream &,不接管 Module 生命周期 - 务必确保
Module所属的LLVMContext在整个写入过程中保持有效(不能在WriteBitcodeToFile调用前析构) - 示例片段:
std::error_code EC; llvm::raw_fd_ostream OS("out.bc", EC, llvm::sys::fs::OF_None); if (!EC) llvm::WriteBitcodeToFile(*M, OS); // M 是 std::unique_ptr<Module> OS.flush(); - 不要用
Module::dump()替代 —— 它只输出到 stderr,且格式不可靠,不能被llvm-as识别
保存为 .ll 文件:用 llvm::print
llvm::print 输出的是人类可读的 IR 文本,等价于命令行 llvm-dis 的行为,但它是 API 层面的直接调用。
- 函数签名是
void llvm::print(const Module &, raw_ostream &),同样不移动或销毁 Module - 输出内容严格遵循 LLVM IR 语法规范,可被
llvm-as无损反汇编回 .bc - 注意:如果 Module 中含调试信息(如
DIBuilder插入的元数据),llvm::print会保留它们;但若用自定义raw_string_ostream并未预留足够缓冲区,可能截断长行 - 示例:
std::string Buf; llvm::raw_string_ostream ROS(Buf); llvm::print(*M, ROS); ROS.flush(); std::ofstream F("out.ll"); F << Buf;
常见坑:Context 销毁早于写入、忘记 flush、误用 dump
三个最容易导致静默失败的操作:
- 在
LLVMContext析构后调用WriteBitcodeToFile或llvm::print—— 表现为写入空文件或崩溃,因为 Module 内部的类型/常量池已失效 - 写入
raw_fd_ostream后没调用flush(),尤其在异常路径下 —— 文件存在但大小为 0 - 把
Module::dump()当作序列化接口用 —— 它不保证格式稳定性,不同 LLVM 版本输出可能不兼容llvm-as
真正关键的不是“怎么调函数”,而是确保 Module 和它的 LLVMContext 活得比写入操作更久;一旦 context 被 delete,Module 就变成悬垂引用 —— 这点在 Pass 开发或 JIT 场景里特别容易忽略。

















