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