我正在开发一个项目,我使用clang生成一些LLVM IR,然后JIT编译并在我的宿主应用程序中运行它.JIT代码调用宿主应用程序中的一些函数,这些函数可能会抛出异常.我希望通过JIT代码抛出异常并回到主机应用程序中.AFAIK这应该与LLVM一起使用,但不幸的是我的测试应用程序总是崩溃"在抛出'int'实例后调用终止".让我举一些简单的例子.
我使用clang 3.5将以下简单程序编译成LLVM IR:
extern void test() ;
extern "C" void exec(void*) {
test();
}
Run Code Online (Sandbox Code Playgroud)
同
./clang -O0 -S -emit-llvm test.cpp -c
Run Code Online (Sandbox Code Playgroud)
结果是test.ll.
; ModuleID = 'test.cpp'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: uwtable
define void @exec(i8*) #0 {
%2 = alloca i8*, align 8
store i8* %0, i8** %2, align 8
call void @_Z4testv()
ret void
}
declare void @_Z4testv() #1
attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.ident = !{!0}
!0 = metadata !{metadata !"clang version 3.5.0 (224841)"}
Run Code Online (Sandbox Code Playgroud)
我的主机应用程序如下所示:
static void test() {
throw 1;
}
int main(int, const char **) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
llvm::LLVMContext &Context = llvm::getGlobalContext();
llvm::SMDiagnostic Err;
llvm::Module *Mod = llvm::ParseIRFile("test.ll", Err, Context);
llvm::ExecutionEngine* m_EE = llvm::EngineBuilder(Mod)
.setEngineKind(llvm::EngineKind::JIT)
.create();
llvm::Function* f = Mod->getFunction("_Z4testv");
m_EE->addGlobalMapping(f, reinterpret_cast<void*>(test));
f = Mod->getFunction("exec");
void* poi = m_EE->getPointerToFunction(f);
void (*exec)(void*) = reinterpret_cast<void (*)(void*)>(poi);
try {
exec(NULL);
} catch (...) {
std::cout << "catched exception" << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用我用cmake编译的LLVM 3.5.我设置LLVM_ENABLE_EH = ON和LLVM_ENABLE_RTTI = ON.编译LLVM或主机应用程序代码错误时我是否遗漏了什么?
谢谢!
最后它起作用,这里有一些解决问题所必需的东西.
首先,确保包含MCJIT.h非常重要,否则MCJIT不会被链接.不幸的是,如果MCJIT.h未被包含,则LLVM默默地回退到旧的JIT实现,即使已明确请求MCJIT:
llvm::EngineBuilder factory(Mod);
factory.setEngineKind(llvm::EngineKind::JIT);
factory.setUseMCJIT(true);
Run Code Online (Sandbox Code Playgroud)
只有MCJIT支持propper异常处理.
在我使用的问题的例子中
Execution::Engine::addGlobalMapping()
Run Code Online (Sandbox Code Playgroud)
哪个不适用于MCJIT.必须通过以下方式重新输入外部功能
llvm::sys::DynamicLibrary::AddSymbol()
Run Code Online (Sandbox Code Playgroud)
按照完整的例子:
static void test() {
throw 1;
}
int main(int, const char **) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
llvm::LLVMContext &Context = llvm::getGlobalContext();
llvm::SMDiagnostic Err;
llvm::Module *Mod = llvm::ParseIRFile("test.ll", Err, Context);
std::unique_ptr<llvm::RTDyldMemoryManager> MemMgr(new llvm::SectionMemoryManager());
// Build engine with JIT
std::string err;
llvm::EngineBuilder factory(Mod);
factory.setErrorStr(&err);
factory.setEngineKind(llvm::EngineKind::JIT);
factory.setUseMCJIT(true);
factory.setMCJITMemoryManager(MemMgr.release());
llvm::ExecutionEngine *m_EE = factory.create();
llvm::sys::DynamicLibrary::AddSymbol("_Z4testv", reinterpret_cast<void*>(test));
llvm::Function* f = Mod->getFunction("exec");
m_EE->finalizeObject();
void* poi = m_EE->getPointerToFunction(f);
void (*exec)(void*) = reinterpret_cast<void (*)(void*)>(poi);
try {
exec(NULL);
} catch (int e) {
std::cout << "catched " << e << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此外,您现在还可以通过添加以下内容获取JIT代码的调试符号:
Opts.JITEmitDebugInfo = true;
Run Code Online (Sandbox Code Playgroud)