2011-09-27 20 views
5

Sto cercando di creare un gestore di eccezioni all'interno del codice JIT llvm. la documentazione attuale relativa a exception handling in LLVM è molto handwavy al momento, quindi ho cercato di riutilizzare la maggior parte dei frammenti che ottengo da http://llvm.org/demo per ottenere un esempio funzionante, ma non sono sicuro che quelli siano aggiornati con llvm 2.9 (la versione che sto usando). Questo è ciò che il modulo controlla dopo Module :: dump();eccezioni llvm; catch handler che non gestisce, cleanup non chiamato

; ModuleID = 'testModule' 

declare i32 @myfunc() 

define i32 @test_function_that_invokes_another() { 
entryBlock: 
    %0 = alloca i8* 
    %1 = alloca i32 
    %someName = invoke i32 @myfunc() 
      to label %exitBlock unwind label %unwindBlock 

exitBlock:          ; preds = %entryBlock 
    ret i32 1 

unwindBlock:          ; preds = %entryBlock 
    %2 = call i8* @llvm.eh.exception() 
    store i8* %2, i8** %0 
    %3 = call i32 (i8*, i8*, ...)* @llvm.eh.selector(i8* %2, i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*), i8* null) 
    store i32 1, i32* %1 
    %4 = load i8** %0 
    %5 = call i32 (...)* @__cxa_begin_catch(i8* %4) nounwind 
    %cleanup_call = call i32 @myCleanup() 
    %6 = call i32 (...)* @__cxa_end_catch() 
    ret i32 1 
} 

declare i32 @__gxx_personality_v0(...) 

declare i32 @__cxa_begin_catch(...) 

declare i32 @__cxa_end_catch(...) 

declare i8* @llvm.eh.exception() nounwind readonly 

declare i32 @llvm.eh.selector(i8*, i8*, ...) nounwind 

declare i32 @myCleanup() 

e questo è ciò che accade quando si tenta di eseguire la funzione:

inside JIT calling C/C++ call 
terminate called after throwing an instance of 'int' 
Aborted 

questo dimostra che la funzione che genera viene chiamato, viene generata, ma non ho mai atterrare nella chiamata di pulizia. (La mia chiamata di pulizia avrebbe dovuto dire 'all'interno JIT chiamando C/C++ Cleanup')

La funzione che richiama e (tentativi) per prendere un eccezione generata è:

const inline llvm::FunctionType* getTestFunctionSignature(llvm::LLVMContext& context) { 
      return llvm::TypeBuilder< unsigned int(), false > ::get(context); 
     } 

llvm::Function* createFunctionThatInvokesAnother(llvm::LLVMContext& ctx, llvm::Module* mod , llvm::Function* another) { 
    llvm::Function* result = llvm::Function::Create(getTestFunctionSignature(ctx), 
          llvm::GlobalValue::ExternalLinkage, 
          "test_function_that_invokes_another", 
          mod); 
    llvm::BasicBlock* entry_block = llvm::BasicBlock::Create(ctx, "entryBlock", result); 
    llvm::BasicBlock* exit_block = llvm::BasicBlock::Create(ctx, "exitBlock", result); 
    llvm::BasicBlock* unwind_block = llvm::BasicBlock::Create(ctx, "unwindBlock", result); 
    llvm::IRBuilder<> builder(entry_block); 
    llvm::ConstantInt* ci = llvm::ConstantInt::get(mod->getContext() , llvm::APInt(32 , llvm::StringRef("1"), 10)); 
    llvm::PointerType* pty3 = llvm::PointerType::get(llvm::IntegerType::get(mod->getContext(), 8), 0); 
    llvm::AllocaInst* ptr_24 = new llvm::AllocaInst(pty3, "", entry_block); 
    llvm::AllocaInst* ptr_25 = new llvm::AllocaInst(llvm::IntegerType::get(mod->getContext(), 32), "", entry_block); 
    llvm::Twine name("someName"); 
    builder.CreateInvoke(another , exit_block , unwind_block , "someName"); 

    builder.SetInsertPoint(exit_block); 
    builder.CreateRet(ci); 

    builder.SetInsertPoint(unwind_block); 
    llvm::Function* func___gxx_personality_v0 = func__gxx_personality_v0(mod); 
    llvm::Function* func___cxa_begin_catch = func__cxa_begin_catch(mod); 
    llvm::Function* func___cxa_end_catch = func__cxa_end_catch(mod); 
    llvm::Function* func_eh_ex = func_llvm_eh_exception(mod); 
    llvm::Function* func_eh_sel = func__llvm_eh_selector(mod); 
    llvm::Constant* const_ptr_17 = llvm::ConstantExpr::getCast(llvm::Instruction::BitCast, func___gxx_personality_v0, pty3); 
    llvm::ConstantPointerNull* const_ptr_18 = llvm::ConstantPointerNull::get(pty3); 

    llvm::CallInst* get_ex = llvm::CallInst::Create(func_eh_ex, "", unwind_block); 
    get_ex->setCallingConv(llvm::CallingConv::C); 
    get_ex->setTailCall(false); 
    new llvm::StoreInst(get_ex, ptr_24, false, unwind_block); 

    std::vector<llvm::Value*> int32_37_params; 
    int32_37_params.push_back(get_ex); 
    int32_37_params.push_back(const_ptr_17); 
    int32_37_params.push_back(const_ptr_18); 
    llvm::CallInst* eh_sel = llvm::CallInst::Create(func_eh_sel, int32_37_params.begin(), int32_37_params.end(), "", unwind_block); 
    eh_sel->setCallingConv(llvm::CallingConv::C); 
    eh_sel->setTailCall(false); 
    new llvm::StoreInst(ci, ptr_25, false, unwind_block); 

    llvm::LoadInst* ptr_29 = new llvm::LoadInst(ptr_24, "", false, unwind_block); 
    llvm::CallInst* ptr_30 = llvm::CallInst::Create(func___cxa_begin_catch, ptr_29, "", unwind_block); 
    ptr_30->setCallingConv(llvm::CallingConv::C); 
    ptr_30->setTailCall(false); 
    llvm::AttrListPtr ptr_30_PAL; 
    { 
     llvm::SmallVector<llvm::AttributeWithIndex, 4 > Attrs; 
     llvm::AttributeWithIndex PAWI; 
     PAWI.Index = 4294967295U; 
     PAWI.Attrs = 0 | llvm::Attribute::NoUnwind; 
     Attrs.push_back(PAWI); 
     ptr_30_PAL = llvm::AttrListPtr::get(Attrs.begin(), Attrs.end()); 

    } 
    ptr_30->setAttributes(ptr_30_PAL); 
    llvm::Function* cleanup = call_myCleanup(mod); 
    builder.CreateCall(cleanup , "cleanup_call"); 
    llvm::CallInst* end_catch = llvm::CallInst::Create(func___cxa_end_catch, "", unwind_block); 
    builder.CreateRet(ci); 
    //createCatchHandler(mod , unwind_block); 
    return result; 
} 

Questo viene chiamato come il solito di business :

testMain() { 
llvm::LLVMContext ctx; 
    llvm::InitializeNativeTarget(); 
    llvm::StringRef idRef("testModule"); 
    llvm::Module* module = new llvm::Module(idRef, ctx); 
    std::string jitErrorString; 
    llvm::ExecutionEngine* execEngine = executionEngine(module , jitErrorString); 
    llvm::FunctionPassManager* OurFPM = new llvm::FunctionPassManager(module); 

llvm::Function *thr = call_my_func_that_throws(module); 
    llvm::Function* result = createFunctionThatInvokesAnother(ctx, module ,thr); 


    std::string errorInfo; 
    llvm::verifyModule(* module, llvm::PrintMessageAction, & errorInfo); 
    module->dump(); 


    void *fptr = execEngine->getPointerToFunction(result); 
    unsigned int (*fp)() = (unsigned int (*)())fptr; 
    try { 
    unsigned int value = fp(); 
    } catch (...) { 
     std::cout << " handled a throw from JIT function" << std::endl; 
    } 
} 

dove la mia funzione che lancia è:

int myfunc() { 
    std::cout << " inside JIT calling C/C++ call" << std::endl; 
    throw 0; 
}; 

llvm::Function* call_my_func_that_throws (llvm::Module* mod) { 
    std::vector< const llvm::Type* > FuncTy_ex_args; 
    llvm::FunctionType* FuncTy_ex = llvm::FunctionType::get(llvm::IntegerType::get(mod->getContext() , 32) , FuncTy_ex_args , false); 
    llvm::Function* result = llvm::Function::Create(FuncTy_ex, llvm::GlobalValue::ExternalLinkage, "myfunc", mod); 
    result->setCallingConv(llvm::CallingConv::C); 
    llvm::AttrListPtr PAL; 
    result->setAttributes(PAL); 
    llvm::sys::DynamicLibrary::AddSymbol("myfunc" , (void*) &myfunc); 
    return result; 
} 

e la mia pulizia funzione è definita in modo simile:

int myCleanup() { 
    std::cout << " inside JIT calling C/C++ Cleanup" << std::endl; 
    return 18; 
}; 

llvm::Function* call_myCleanup (llvm::Module* mod) { 
    std::vector< const llvm::Type* > FuncTy_ex_args; 
    llvm::FunctionType* FuncTy_ex = llvm::FunctionType::get(llvm::IntegerType::get(mod->getContext() , 32) , FuncTy_ex_args , false); 
    llvm::Function* result = llvm::Function::Create(FuncTy_ex, llvm::GlobalValue::ExternalLinkage, "myCleanup", mod); 
    result->setCallingConv(llvm::CallingConv::C); 
    llvm::AttrListPtr PAL; 
    result->setAttributes(PAL); 
    llvm::sys::DynamicLibrary::AddSymbol("myCleanup" , (void*) &myCleanup); 
    return result; 
} 

Ho anche letto this document per quanto riguarda i recenti cambiamenti di gestione delle eccezioni in LLVM, ma non è chiaro come questi cambiamenti si traducono in reale, si sa, il codice

risposta

2

In questo momento il codice EH sta subendo una grande quantità di revisione. La demo, se ricordo bene, non è la versione 2.9, ma le attuali fonti di sviluppo - il che significa provare a fare qualcosa con 2.9 sarà un mondo di dolore se proverai in questo modo.

Detto questo, la rappresentazione EH è molto meglio ora e numerose patch sono state introdotte per migliorare la documentazione proprio questa settimana. Se stai provando a scrivere una lingua che usa le eccezioni tramite llvm, ti suggerisco caldamente di migrare il tuo codice alle attuali fonti di sviluppo.

Tutto ciò detto, non sono sicuro di quanto bene funzioni la gestione delle eccezioni nel JIT in questo momento. È supportato nominalmente, ma potrebbe essere necessario eseguire il debug delle tabelle di unwind che vengono memorizzate per assicurarsi che siano corrette.

+0

eccellente, puoi indicare qualche esempio utilizzando il nuovo approccio, ho letto questo post: http://lists.cs.uiuc.edu/pipermail/llvmdev/2011-July/041768.html ma i ' Non sono sicuro di cosa dovrei fare – lurscher

+0

I documenti eh sono stati riscritti quindi dovrebbe essere accurato. Un'altra opzione è guardare l'output clang per avere un'idea di come dovrebbe apparire. – echristo

Problemi correlati