2015-01-01 17 views
9

Pracuję nad projektem, w którym użyję klang do wygenerowania LLVM IR, a następnie kompilacji JIT i uruchomienia go z poziomu mojej aplikacji hosta. Kod JIT wywołuje niektóre funkcje w aplikacji hosta, które mogą generować wyjątek. Spodziewam się, że wyjątek zostanie wyrzucony przez kod JIT i przechwycony w aplikacji hosta. AFAIK to ma działać z LLVM, ale niestety moja aplikacja testowa zawsze zawiesza się z "zakończeniem wywołanym po rzuceniu instancji" int "". Pozwolę sobie podać prosty przykład.JIT LLVM: przekazywanie wyjątku C++ poprzez kod JIT z powrotem do aplikacji hosta

używam szczęk 3.5 skompilować następujący prosty program do LLVM IR:

extern void test() ; 

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

z

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

Rezultatem jest 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)"} 

Mój wygląd aplikacji hosta tak:

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

Używam LLVM 3.5, który skompilowałem z cmake. Ustawiłem LLVM_ENABLE_EH = ON i LLVM_ENABLE_RTTI = ON. Czy coś mi pominęło podczas kompilowania LLVM, czy mój kod aplikacji hosta jest nieprawidłowy?

Dzięki!

Odpowiedz

6

Wreszcie działa i jest kilka rzeczy, które są niezbędne do rozwiązania problemu.

Po pierwsze ważne jest, aby upewnić się, że MCJIT.h został dołączony, w przeciwnym razie MCJIT nie jest połączony. Niestety LLVM po cichu wraca do starej implementacji JIT, jeśli MCJIT.h nie został uwzględniony, mimo że MCJIT został wyraźnie zażądany przez :

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

Tylko usługa MCJIT obsługuje obsługę wyjątków przez narzędzie.

Na przykład w pytaniu użyłem

Execution::Engine::addGlobalMapping() 

który nie działa z MCJIT. Funkcja zewnętrzna musi być reqistered poprzez

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

Po całkowitym przykład:

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

Dodatkowo można teraz również uzyskać Symbole debugowania kodu JIT dodając:

Opts.JITEmitDebugInfo = true;