c++ - LLVM 异常;捕获处理程序未处理,未调用清理

标签 c++ exception-handling llvm

我正在尝试在 JIT llvm 代码中创建一个异常处理程序。关于 exception handling in LLVM 的当前文档目前非常麻烦,所以我一直在尝试重用我从 http://llvm.org/demo 获得的大部分片段为了获得一个工作示例,但我不确定这些是否与 llvm 2.9(我正在使用的版本)保持同步。 这就是模块在 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()

这就是我尝试执行函数时发生的情况:

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

这表明抛出的函数被调用,它抛出,但我从未进入清理调用。 (我的清理调用应该说“在 JIT 内部调用 C/C++ 清理”)

调用并(尝试)捕获抛出异常的函数是:

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

这就像通常的业务一样被调用:

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

我抛出的函数是:

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

我的清理函数是以类似的方式定义的:

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

我也读过 this document关于 LLVM 中最近的异常处理更改,但不清楚这些更改如何转化为实际代码

最佳答案

目前EH代码正在进行大量修改。如果我没记错的话,该演示不是 2.9 版,而是当前的开发源 - 这意味着如果您尝试使用 2.9 做一些事情,将会是一个痛苦的世界。

也就是说,EH 表示现在好多了,并且就在本周,已经添加了许多补丁来改进文档。如果您正在尝试编写一种通过 llvm 使用异常的语言,我强烈建议您将代码迁移到当前的开发源。

综上所述,我现在完全不确定 JIT 中的异常处理效果如何。它名义上受支持,但您可能需要调试放入内存的展开表以确保它们是正确的。

关于c++ - LLVM 异常;捕获处理程序未处理,未调用清理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7575703/

相关文章:

c++ - 我如何理解 llvm 何时更改签名以发挥作用

c++ - 已创建 vector 中的奇怪 std::bad_alloc

c++ - 功能的重新定义

exception-handling - 你能从原始 AspectJ 抛出异常吗?

c++ - 如果我在项目中禁用 C++ 异常,究竟会发生什么?

c++ - 结构化异常情况下的堆栈展开

ruby - 使用 Mountain Lion、Xcode 4.5 和 RVM 安装 Ruby?

c++ - 如何找到类模板成员函数定义的外层模板参数列表的SourceLocation?

c++ - catch 在此 C++ 代码中的用法是什么

c++一次检查所有数组值