LLVM JIT:将C++异常通过JIT代码传递回主机应用程序

12

我正在开发一个项目,在其中使用clang生成一些LLVM IR,然后在我的主机应用程序中进行即时编译和运行。JIT代码调用主机应用程序中的一些函数,这些函数可能会引发异常。我希望异常通过JIT代码抛出并在主机应用程序中捕获回来。据我所知,LLVM应该可以做到这一点,但不幸的是,我的测试应用程序总是崩溃并显示“terminate called after throwing an instance of 'int'”错误。让我举个简单的例子。

我使用clang 3.5将以下简单程序编译为LLVM IR:

extern void test() ;

extern "C" void exec(void*) {
        test();
}

随着

./clang -O0 -S -emit-llvm test.cpp -c

结果是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)"}

我的主机应用程序长这样:

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;
}

我使用CMake编译的LLVM 3.5版本。在编译时,我将LLVM_ENABLE_EH和LLVM_ENABLE_RTTI参数设置为ON。请问我是否在编译LLVM时遗漏了什么或者我的主机应用程序代码有问题?

谢谢!

1个回答

7

最终它可用了,以下是需要修复此问题的一些必要事项。

首先,确保已经包括了MCJIT.h文件,否则MCJIT无法链接。不幸的是,即使明确请求使用MCJIT,如果未包括MCJIT.h,则LLVM会悄悄地回退到旧的JIT实现。

llvm::EngineBuilder factory(Mod);
factory.setEngineKind(llvm::EngineKind::JIT);
factory.setUseMCJIT(true);

只有MCJIT支持适当的异常处理。

在问题的示例中,我使用了

Execution::Engine::addGlobalMapping()

这不适用于MCJIT。外部函数必须通过进行注册

llvm::sys::DynamicLibrary::AddSymbol()

完整的示例代码如下:
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;
}

此外,您现在还可以通过添加以下内容获取JIT代码的调试符号:

Opts.JITEmitDebugInfo = true;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接