必须使用 ORC JIT(尤其是 LLJIT),因 LLVM 14+ 已彻底移除 MCJIT 和 ExecutionEngine::Create();旧 MCJIT 缺乏符号隔离、不支持现代目标特性且无法增量加载,强行使用会导致链接失败或运行时错误。

直接用 MCJIT 已过时且不推荐;LLVM 14+ 必须用 ORC JIT(尤其是 LLJIT),否则连基本符号解析都会失败。
为什么不能直接 new ExecutionEngine?
LLVM 从 10.x 开始弃用 ExecutionEngine::Create(),14+ 彻底移除。旧式 MCJIT 缺乏符号隔离、无法处理现代目标特性(如 Windows SEH、ARM64 PAC)、也不支持增量模块加载。强行链接 llvm-executionengine 并调用已废弃 API 会导致链接失败或运行时段错误——尤其在启用 LTO 或使用非 x86_64 目标时。
-
MCJIT的getPointerToFunction在 ORC 环境下返回空指针,且无明确报错 - 所有官方示例(包括
lli和 Kaleidoscope 教程)自 LLVM 15 起全部迁移到orc::LLJIT - 若 CMake 中仍看到
add_definitions(-DLLVM_USE_MCJIT),说明构建配置严重滞后
最简可用的 ORC JIT 启动代码(LLVM ≥16)
跳过底层 layer 拼装,用 LLJIT 封装体——它默认配好 IRCompileLayer、ObjectLinkingLayer 和符号解析器,适合 90% 的动态执行场景。
- 必须启用 RTTI 和异常支持:
set(LLVM_ENABLE_RTTI ON)+set(LLVM_ENABLE_EH ON) - 链接库至少包含:
llvm-core、llvm-orc、llvm-irreader、llvm-target、llvm-executionengine - 初始化目标后端不可省略:
llvm::InitializeNativeTarget()、llvm::InitializeNativeTargetAsmPrinter() - 创建实例只需三行:
auto jit = llvm::orc::LLJITBuilder().create();
if (!jit) { /* 处理 llvm::Error */ }
jit->getMainJITDylib().addGenerator(
llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
jit->getDataLayout().getGlobalPrefix()));
最后一行让 JIT 能解析 printf、malloc 等 host 进程符号——漏掉就会报 Symbol not found: printf。
如何把 IR 模块注入并调用函数?
核心是两步:封装 Module 为 ThreadSafeModule,再用 addIRModule 注入;之后通过 lookup 提取地址并强转。
-
Module必须归属唯一LLVMContext,跨 context 传入会触发断言失败 - 函数必须设为
ExternalLinkage,否则lookup返回 null - 调用前检查地址有效性:
if (auto addr = cantFail(jit->lookup("add"))) { ... } - 强转需匹配签名,例如
int(*)(int, int),用reinterpret_cast,不是static_cast
示例调用:
auto funcPtr = reinterpret_cast<int int>(cantFail(jit->lookup("add")).getAddress());
int result = funcPtr(3, 5); // 返回 8
</int>
常见崩溃点和静默失败原因
ORC JIT 的错误往往不抛异常,而是返回 llvm::Error 或让 lookup 返回空符号——但程序继续跑,直到调用空指针才 SIGSEGV。
- 忘记
InitializeNativeTargetAsmPrinter()→IRCompileLayer编译失败,addIRModule返回 error,但你不检查就继续 - 函数名含 C++ mangling(如
_Z3addii)→lookup("add")找不到,必须用createMangleAndInterner或手动 demangle - 模块中引用未注册的全局变量(如
int g = 42;)→ JIT 编译成功,但运行时访问非法地址 - 重复
addIRModule同名函数 → 默认报Duplicate definition of symbol,需提前mainJD.setAutoClaimResponsibilityForObjectSymbols(true)
真正难调试的从来不是“怎么写”,而是“为什么没报错却没执行”——ORC 的资源生命周期(ExecutionSession、JITDylib)和符号可见性规则必须严格对齐,差一行初始化或一个 flag 就静默失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











