Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

llvm exceptions; catch handler not handling, cleanup not called

I i'm trying to create a exception handler inside JIT llvm code. the current documentation regarding exception handling in LLVM is very handwavy at the moment, so i've been trying to reuse most of the snippets i get from http://llvm.org/demo in order to get a working example, but i'm not sure if those are up to date with llvm 2.9 (the version i am using). This is what the module looks after Module::dump();

; 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()

and this is what happens when i try to execute the function:

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

this shows that the function that throws gets called, it throws, but i never land in the cleanup call. (my cleanup call should have said 'inside JIT calling C/C++ Cleanup')

The function that invokes and (attempts) to catch a thrown exception is:

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

This gets called like the usual 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;
    }
}

where my function that throws is:

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

and my cleanup function is defined in a similar way:

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

I've also read this document regarding recent exception handling changes in LLVM, but is not clear how those changes translate to actual, you know, code

like image 637
lurscher Avatar asked Nov 05 '22 13:11

lurscher


1 Answers

Right now the EH code is undergoing a large amount of revision. The demo, if I recall correctly, is not version 2.9, but current development sources - meaning trying to do something with 2.9 is going to be a world of hurt if you try that way.

That said, the EH representation is much better now and numerous patches have gone in to improve the documentation just this week. If you are trying to write a language that uses exceptions via llvm I highly suggest you migrate your code to current development sources.

All of that said, I'm not sure how well exception handling works in the JIT at all right now. It's nominally supported, but you may need to debug the unwind tables that are put into memory to make sure they're correct.

like image 150
echristo Avatar answered Nov 15 '22 04:11

echristo