【问题标题】:LLVM JIT: pass C++ exception through JIT code back to host applicationLLVM JIT:通过 JIT 代码将 C++ 异常传递回主机应用程序
【发布时间】:2015-01-01 14:25:52
【问题描述】:

我正在做一个项目,我使用 clang 生成一些 LLVM IR,然后 JIT 编译并在我的主机应用程序中运行它。 JIT 代码调用主机应用程序中的一些函数,这些函数可能会引发异常。我希望通过 JIT 代码抛出异常并在主机应用程序中捕获异常。 AFAIK 这应该与 LLVM 一起使用,但不幸的是,我的测试应用程序总是因“抛出'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=ON 和 LLVM_ENABLE_RTTI=ON。是我在编译 LLVM 时遗漏了什么还是我的主机应用程序代码错误?

谢谢!

【问题讨论】:

    标签: c++ llvm jit


    【解决方案1】:

    终于成功了,这里有一些解决问题所必需的东西。

    首先确保已包含 MCJIT.h 很重要,否则未链接 MCJIT。不幸的是,如果未包含 MCJIT.h,即使 MCJIT 已由以下人员明确请求,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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-06
      • 2011-03-31
      • 2011-02-17
      • 2011-05-24
      • 2015-05-05
      • 2013-10-07
      相关资源
      最近更新 更多