能否在LLVM JIT编译函数中调用预编译C函数?求纯C实现方案
可行
在纯C环境下,完全可以实现从LLVM JIT编译的函数中调用预编译的C函数,核心是通过LLVM C API提供的LLVMAddGlobalMapping函数完成符号映射,对应C++版本的llvm::ExecutionEngine::addGlobalMapping功能。
实现步骤
- 定义非静态的预编译C函数(确保符号可被链接器和LLVM识别)
- 使用LLVM C API构建包含目标调用逻辑的IR模块,需提前在模块中声明预编译函数的签名
- 创建ExecutionEngine实例后,通过
LLVMAddGlobalMapping将IR中的函数符号与预编译C函数的实际地址绑定 - 编译并调用JIT生成的函数
示例代码
预编译C函数(utils.c)
int add(int a, int b) { return a + b; }
主程序(jit_demo.c)
#include <llvm-c/Core.h> #include <llvm-c/ExecutionEngine.h> #include <llvm-c/Target.h> #include <stdio.h> int main() { // 初始化LLVM本地目标环境 LLVMInitializeNativeTarget(); LLVMInitializeNativeAsmPrinter(); LLVMInitializeNativeAsmParser(); // 创建LLVM上下文与模块 LLVMContextRef ctx = LLVMContextCreate(); LLVMModuleRef mod = LLVMModuleCreateWithNameInContext("jit_demo", ctx); // 定义函数类型:int(int, int) LLVMTypeRef param_types[] = {LLVMInt32Type(), LLVMInt32Type()}; LLVMTypeRef func_type = LLVMFunctionType(LLVMInt32Type(), param_types, 2, 0); // 在模块中声明预编译的add函数 LLVMValueRef add_func = LLVMAddFunction(mod, "add", func_type); // 构建JIT函数:调用add并返回结果 LLVMValueRef jit_func = LLVMAddFunction(mod, "call_add", func_type); LLVMBasicBlockRef entry = LLVMAppendBasicBlock(jit_func, "entry"); LLVMBuilderRef builder = LLVMCreateBuilder(); LLVMPositionBuilderAtEnd(builder, entry); // 获取JIT函数的参数,调用add LLVMValueRef args[] = {LLVMGetParam(jit_func, 0), LLVMGetParam(jit_func, 1)}; LLVMValueRef ret_val = LLVMBuildCall(builder, add_func, args, 2, "call_res"); LLVMBuildRet(builder, ret_val); // 创建ExecutionEngine char *err_msg = NULL; LLVMExecutionEngineRef engine; if (LLVMCreateExecutionEngineForModule(&engine, mod, &err_msg) != 0) { fprintf(stderr, "ExecutionEngine创建失败: %s\n", err_msg); LLVMDisposeMessage(err_msg); return 1; } // 绑定符号:将IR中的add映射到实际函数地址 LLVMAddGlobalMapping(engine, add_func, (void*)&add); // 获取JIT函数指针并调用 int (*call_jit)(int, int) = (int(*)(int, int))LLVMGetFunctionAddress(engine, "call_add"); printf("3 + 5 = %d\n", call_jit(3, 5)); // 清理资源 LLVMDisposeBuilder(builder); LLVMDisposeExecutionEngine(engine); LLVMContextDispose(ctx); return 0; }
编译命令(使用clang)
clang -o jit_demo jit_demo.c utils.c `llvm-config --cflags --ldflags --system-libs --libs core executionengine native`
关键注意事项
- 预编译函数不能用
static修饰,否则符号会被隐藏,无法完成映射 - 必须正确初始化LLVM的目标组件,否则ExecutionEngine会创建失败
- 确保LLVM库版本与编译环境匹配,避免因ABI不兼容导致段错误
内容的提问来源于stack exchange,提问作者SocraticMathTutor
相关产品推荐
相关产品推荐

