可以轉載,請注明出處!
llvm 異常處理
-
- 一、相關資料介紹
- 二、學習clang編譯C++異常
- 三、工作原理
- 四、用API寫異常處理
一、相關資料介紹
建議先讀後面,在看資料,之是以把資料寫到前面是因為很重要
- 官方文檔有關于llvm異常的介紹:Exception Handling in LLVM
- 對官方文檔比較好的一個翻譯:LLVM裡的異常處理
- 基于Itanium異常處理ABI規範:Itanium C++ ABI
- 這個有點雜,不過應該也會有點幫助:https://www.zhihu.com/question/266617658
- 這個也可以看看,也可以看看這個部落客的其他文章:llvm異常重設計
二、學習clang編譯C++異常
1、 建立一個main.cpp檔案來編寫源碼,以測試異常處理機制:
class Ex1 {
};
void throw_exception(int a, int b) {
Ex1 ex1;
if (a > b) {
throw ex1;
}
}
int test_try_catch() {
try {
throw_exception(2, 1);
} catch (...) {
return 1;
}
return 0;
}
2、使用
clang++ -emit-llvm -S main.cpp -o main.ll
指令生成main.ll檔案
3、檢視main.ll檔案
; ModuleID = 'main.cpp'
source_filename = "main.cpp"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
%class.Ex1 = type { i8 }
$_ZTS3Ex1 = comdat any
$_ZTI3Ex1 = comdat any
@_ZTVN10__cxxabiv117__class_type_infoE = external global i8*
@_ZTS3Ex1 = linkonce_odr constant [5 x i8] c"3Ex1\00", comdat
@_ZTI3Ex1 = linkonce_odr constant { i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i64 2) to i8*), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @_ZTS3Ex1, i32 0, i32 0) }, comdat
; Function Attrs: noinline optnone uwtable
define void @_Z15throw_exceptionii(i32 %a, i32 %b) #0 {
entry:
%a.addr = alloca i32, align 4
%b.addr = alloca i32, align 4
%ex1 = alloca %class.Ex1, align 1
store i32 %a, i32* %a.addr, align 4
store i32 %b, i32* %b.addr, align 4
%0 = load i32, i32* %a.addr, align 4
%1 = load i32, i32* %b.addr, align 4
%cmp = icmp sgt i32 %0, %1
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
%exception = call i8* @__cxa_allocate_exception(i64 1) #1
%2 = bitcast i8* %exception to %class.Ex1*
call void @__cxa_throw(i8* %exception, i8* bitcast ({ i8*, i8* }* @_ZTI3Ex1 to i8*), i8* null) #2
unreachable
if.end: ; preds = %entry
ret void
}
declare i8* @__cxa_allocate_exception(i64)
declare void @__cxa_throw(i8*, i8*, i8*)
; Function Attrs: noinline optnone uwtable
define i32 @_Z14test_try_catchv() #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
entry:
%retval = alloca i32, align 4
%exn.slot = alloca i8*
%ehselector.slot = alloca i32
invoke void @_Z15throw_exceptionii(i32 2, i32 1)
to label %invoke.cont unwind label %lpad
invoke.cont: ; preds = %entry
br label %try.cont
lpad: ; preds = %entry
%0 = landingpad { i8*, i32 }
catch i8* null
%1 = extractvalue { i8*, i32 } %0, 0
store i8* %1, i8** %exn.slot, align 8
%2 = extractvalue { i8*, i32 } %0, 1
store i32 %2, i32* %ehselector.slot, align 4
br label %catch
catch: ; preds = %lpad
%exn = load i8*, i8** %exn.slot, align 8
%3 = call i8* @__cxa_begin_catch(i8* %exn) #1
store i32 1, i32* %retval, align 4
call void @__cxa_end_catch()
br label %return
try.cont: ; preds = %invoke.cont
store i32 0, i32* %retval, align 4
br label %return
return: ; preds = %try.cont, %catch
%4 = load i32, i32* %retval, align 4
ret i32 %4
}
declare i32 @__gxx_personality_v0(...)
declare i8* @__cxa_begin_catch(i8*)
declare void @__cxa_end_catch()
attributes #0 = { noinline optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind }
attributes #2 = { noreturn }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 6.0.0 (tags/RELEASE_600/final)"}
三、工作原理
LLVM是這樣實作異常的:當異常被抛出,運作時(runtime)會查 找異常處理器。它會查找抛出異常的那個函數對應的異常幀,而這個異 常處理器與異常幀相關聯,并且包含異常表的引用,而異常表中則包含 了異常處理的具體實作,也就是如果這門程式設計語言支援異常處理,抛出 異常時如何處理。如果這門語言不支援異常處理,那麼關于如何展開當 前活動記錄和還原前一個活動記錄的狀态的相關資訊則會在異常幀中。
讓我們通過之前的例子來看看異常處理在LLVM中是如何具體實作 的。
try區塊在LLVM中被翻譯成invoke指令:
invoke void @_Z15throw_exceptionii(i32 2, i32 1)
to label %invoke.cont unwind label %lpad
上面的代碼告訴編譯器如果throw_exception函數抛出異常,它應該 如何處理這個異常。如果throw_exception沒有抛出異常,正常執行跳轉 到
%invoke.cont
,否則跳轉到
%lpad
,即landing pad,這對應了try/catch中 的catch機制。如果程式執行在landing pad重新開始,它會接收一個異常 結構體,以及與抛出的異常類型對應的選擇器的值。這個選擇器用于決 定哪一個catch函數來真正處理這個異常。在本例中,它看起來像這樣:
%0 = landingpad { i8*, i32 }
catch i8* null
上面一段代碼描述了異常資訊。
{i8*, i32}
部分描述異常類型,
i8*
是異常指針,而
i32
是異常選擇器的值。在這裡我們隻有一個選擇器的值,是以catch函數會接受所有抛出類型的異常。
define i32 @_Z14test_try_catchv() #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*){
...
}
@__gxx_personality_v0
函數是personality函數,它接受異常的上下文(context),即一個包含 異常對象類型和值的異常結構體,以及一個目前函數異常表的引用。當 前編譯單元的personality函數在公共異常幀指定。本例中,
@__gxx_personality_v0
函數則表示我們在處理C++異常。
是以
%1 = extractvalue { i8*, i32 } %0, 0
表示異常對象,而
%2 = extractvalue { i8*, i32 } %0, 1
則表示選擇器值。
下面是一些值得注意的IR函數。 在lib c++ 規範中有詳細介紹
- __cxa_thorw:用于抛出異常的函數。
- __cxa_begin_catch:接受一個異常結構體的引用作為參數,傳回異 常對象的值。
- __cxa_end_catch:處理最近捕捉的異常,減少handler計數,如果計 數為0則停止異常捕捉。
四、用API寫異常處理
1、api對應的main.cpp代碼:
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/IR/TypeBuilder.h"
#include <iostream>
#include <algorithm>
using namespace llvm;
using namespace std;
int main() {
static LLVMContext MyGlobalContext;
LLVMContext &context = MyGlobalContext;
Module *module = new Module("test", context);
IRBuilder<> builder(context);
static Type *ret_type; //傳回值類型
static SmallVector<Type*, 3> fun_args; //函數參數類型
static FunctionType *fun_type;
//定義一個全局常量,這玩意是用來存放異常結構體類型資訊的
GlobalVariable *ZTIi = cast<GlobalVariable>(
module->getOrInsertGlobal("_ZTIi", Type::getInt8PtrTy(context)));
ZTIi->setConstant(true);
//建立一個結構體,捕獲異常的時候用
StructType *padStruct = StructType::create(context, "pad_struct");
std::vector<Type*> elements; //結構體元素
elements.push_back(builder.getInt8PtrTy());
elements.push_back(builder.getInt32Ty());
padStruct->setBody(elements);
//建立一個異常結構體類型,抛出的異常就是用這個結構體
StructType *exceptionStruct = StructType::create(context,
"exception_struct");
std::vector<Type*> elements1; //結構體元素
elements1.push_back(builder.getInt8PtrTy());
elements1.push_back(builder.getInt8PtrTy());
elements1.push_back(builder.getInt32Ty());
exceptionStruct->setBody(elements1);
//===----------------------------- 這部分為抛出異常-----------------------------------===//
//
//
//聲明 i8* @__cxa_allocate_exception(i64) 函數,參數是sizeof(抛出的異常結構體類型)
ret_type = Type::getInt8PtrTy(context);
fun_args.push_back(Type::getInt64Ty(context));
fun_type = FunctionType::get(ret_type, fun_args, /*isVarArg*/false);
Function *cxa_allocate = cast<Function>(
module->getOrInsertFunction("__cxa_allocate_exception", fun_type));
fun_args.clear();
//聲明 void @__cxa_throw(i8*, i8*, i8*) 函數,
ret_type = Type::getVoidTy(context);
fun_args.push_back(Type::getInt8PtrTy(context));
fun_args.push_back(Type::getInt8PtrTy(context));
fun_args.push_back(Type::getInt8PtrTy(context));
fun_type = FunctionType::get(ret_type, fun_args, /*isVarArg*/false);
Function *cxa_throw = cast<Function>(
module->getOrInsertFunction("__cxa_throw", fun_type));
fun_args.clear();
//聲明結束-----------------------
// 在IR中聲明一個函數foo,會用這個函數抛出異常
fun_type = TypeBuilder<int(), false>::get(context);
Function *foo = cast<Function>(
module->getOrInsertFunction("foo", fun_type));
// 建立函數foo的entry代碼塊
BasicBlock *entry = BasicBlock::Create(context, "entry", foo);
builder.SetInsertPoint(entry);
//>>>------------------------------------------------------------------------
llvm::Constant *strConst1 = llvm::ConstantDataArray::getString(context,
"exception_name");
llvm::Value *globalVar1 = new llvm::GlobalVariable(*module,
strConst1->getType(), true, llvm::GlobalValue::PrivateLinkage,
strConst1);
llvm::Constant *strConst2 = llvm::ConstantDataArray::getString(context,
"uuid");
llvm::Value *globalVar2 = new llvm::GlobalVariable(*module,
strConst2->getType(), true, llvm::GlobalValue::PrivateLinkage,
strConst2);
llvm::ConstantInt *constSqlCode = llvm::ConstantInt::get(
llvm::IntegerType::getInt32Ty(context), 52081);
//<<<------------------------------------------------------------------------
//調用@__cxa_allocate_exception(i64)
Value *arg1_value = ConstantInt::get(Type::getInt64Ty(context), 16);/*---這裡暫時先寫成死的*/
std::vector<Value*> putsargs_1;
putsargs_1.push_back(arg1_value);
ArrayRef<Value*> argsRef_1(putsargs_1);
Value *exception_alloca = builder.CreateCall(cxa_allocate, argsRef_1,
"excep");
//>>>給結構體指派------------------------------------------------------
llvm::PointerType *pstructType = llvm::PointerType::get(exceptionStruct, 0);
Value *alloca_Struct = builder.CreateBitCast(exception_alloca, pstructType);
llvm::SmallVector<llvm::Value*, 2> indexVector;
llvm::Value *const_0 = llvm::ConstantInt::get(
llvm::IntegerType::getInt32Ty(context), 0);
llvm::Value *const_1 = llvm::ConstantInt::get(
llvm::IntegerType::getInt32Ty(context), 1);
llvm::Value *const_2 = llvm::ConstantInt::get(
llvm::IntegerType::getInt32Ty(context), 2);
indexVector.push_back(const_0);
indexVector.push_back(const_0);
llvm::Value *number_ptr_1 = builder.CreateGEP(alloca_Struct, indexVector);
indexVector.clear();
indexVector.push_back(const_0);
indexVector.push_back(const_1);
llvm::Value *number_ptr_2 = builder.CreateGEP(alloca_Struct, indexVector);
indexVector.clear();
indexVector.push_back(const_0);
indexVector.push_back(const_2);
llvm::Value *number_ptr_3 = builder.CreateGEP(alloca_Struct, indexVector);
builder.CreateStore(
builder.CreatePointerCast(globalVar1, builder.getInt8PtrTy()),
number_ptr_1);
builder.CreateStore(
builder.CreatePointerCast(globalVar2, builder.getInt8PtrTy()),
number_ptr_2);
builder.CreateStore(constSqlCode, number_ptr_3);
//<<<------------------------------------------------------------------------
//調用@__cxa_throw(i8*, i8*, i8*)
std::vector<Value*> putsargs_2;
putsargs_2.push_back(exception_alloca);
Value *ZTIi_temp = builder.CreateBitCast(ZTIi, Type::getInt8PtrTy(context));
putsargs_2.push_back(ZTIi_temp);
Constant *null_value = ConstantPointerNull::get(
Type::getInt8PtrTy(context));
putsargs_2.push_back(null_value);
ArrayRef<Value*> argsRef_2(putsargs_2);
builder.CreateCall(cxa_throw, argsRef_2);
//函數傳回值
Constant *a_value = ConstantInt::get(Type::getInt32Ty(context), 0);
builder.CreateRet(a_value);
//
//
//===---------------------------------------------------------------------------------===//
//===----------------------------- 這部分為捕獲異常-----------------------------------===//
//
//
//聲明personality函數 i32 @__gxx_personality_v0(...)
Function *personalityFn = Function::Create(
FunctionType::get(Type::getInt32Ty(context), true),
Function::ExternalLinkage, "__gxx_personality_v0", module);
//聲明内置函數 i32 @llvm.eh.typeid.for(i8*)
Function *typeid_for = Intrinsic::getDeclaration(module,
Intrinsic::eh_typeid_for);
//聲明 i8* @__cxa_begin_catch(i8*) 函數,參數是sizeof(抛出的異常結構體類型)
ret_type = Type::getInt8PtrTy(context);
fun_args.push_back(Type::getInt8PtrTy(context));
fun_type = FunctionType::get(ret_type, fun_args, /*isVarArg*/false);
Function *begin_catch = cast<Function>(
module->getOrInsertFunction("__cxa_begin_catch", fun_type));
fun_args.clear();
//聲明 void @__cxa_end_catch() 函數,參數是sizeof(抛出的異常結構體類型)
ret_type = Type::getVoidTy(context);
fun_type = FunctionType::get(ret_type, fun_args, /*isVarArg*/false);
Function *end_catch = cast<Function>(
module->getOrInsertFunction("__cxa_end_catch", fun_type));
fun_args.clear();
//聲明結束-----------------------
// 在IR中聲明一個函數bar,會用這個函數捕獲異常
fun_type = TypeBuilder<int(), false>::get(context);
Function *bar = cast<Function>(
module->getOrInsertFunction("bar", fun_type));
//設定函數的personality
Function *personality0 = (Function*) builder.CreateBitCast(personalityFn,
Type::getInt8PtrTy(context));
bar->setPersonalityFn(personality0);
// entry basicblock
BasicBlock *entry_bar = BasicBlock::Create(context, "entry", bar);
builder.SetInsertPoint(entry_bar);
ConstantInt *con_1 = ConstantInt::get(Type::getInt32Ty(context), 1);
AllocaInst *res_allo = builder.CreateAlloca(Type::getInt32Ty(context),
con_1, "res");
AllocaInst *exn_slot = builder.CreateAlloca(Type::getInt8PtrTy(context),
con_1, "exn.slot");
AllocaInst *ehselector_slot = builder.CreateAlloca(
Type::getInt32Ty(context), con_1, "ehselector.slot");
AllocaInst *exc = builder.CreateAlloca(Type::getInt32Ty(context), con_1,
"exc");
ConstantInt *con_0 = ConstantInt::get(Type::getInt32Ty(context), 0);
builder.CreateStore(con_0, res_allo);
//捕獲異常用到的塊
BasicBlock *invoke_cont = BasicBlock::Create(context, "invoke.cont", bar);
BasicBlock *lpad = BasicBlock::Create(context, "lpad", bar);
BasicBlock *catch_dispatch = BasicBlock::Create(context, "catch.dispatch",
bar);
BasicBlock *catch_block = BasicBlock::Create(context, "catch", bar);
BasicBlock *try_cont = BasicBlock::Create(context, "try.cont", bar);
BasicBlock *eh_end = BasicBlock::Create(context, "eh.end", bar);
BasicBlock *eh_resume = BasicBlock::Create(context, "eh.resume", bar);
//通過invoke指令調用throw方法的異常
InvokeInst *invoke_retValue = builder.CreateInvoke(foo, invoke_cont, lpad);
//invoke.cont basicblock
builder.SetInsertPoint(invoke_cont);
builder.CreateStore(invoke_retValue, res_allo);
builder.CreateBr(try_cont);
//try.cont basicblock
builder.SetInsertPoint(try_cont);
builder.CreateBr(eh_end);
//eh.end basicblock
builder.SetInsertPoint(eh_end);
LoadInst *res_load = builder.CreateLoad(res_allo);
builder.CreateRet(res_load);
//lpad basicblock
builder.SetInsertPoint(lpad);
LandingPadInst *landingPad = builder.CreateLandingPad(padStruct, 0);
// LandingPadInst* landingPad = LandingPadInst::Create(exceptionStruct, personalityFn, 0, "lad", lpad);
landingPad->addClause(
(Constant*) builder.CreateBitCast(ZTIi,
Type::getInt8PtrTy(context)));
Value *unwindException = builder.CreateExtractValue(landingPad, 0);
builder.CreateStore(unwindException, exn_slot);
Value *retTypeInfoIndex = builder.CreateExtractValue(landingPad, 1);
builder.CreateStore(retTypeInfoIndex, ehselector_slot);
builder.CreateBr(catch_dispatch);
//catch.dispatch basicblock
builder.SetInsertPoint(catch_dispatch);
LoadInst *sel = builder.CreateLoad(ehselector_slot);
std::vector<Value*> putsargs_3;
putsargs_3.push_back(
builder.CreateBitCast(ZTIi, Type::getInt8PtrTy(context)));
ArrayRef<Value*> argsRef_3(putsargs_3);
Value *type_f = builder.CreateCall(typeid_for, argsRef_3);
Value *matches = builder.CreateICmpEQ(sel, type_f, "matches");
builder.CreateCondBr(matches, catch_block, eh_resume);
//catch basicblock
builder.SetInsertPoint(catch_block);
Value *exn = builder.CreateLoad(exn_slot);
std::vector<Value*> putsargs_4;
putsargs_4.push_back(exn);
ArrayRef<Value*> argsRef_4(putsargs_4);
Value *bc_call = builder.CreateCall(begin_catch, argsRef_4);
Value *bitCast_allo = builder.CreateBitCast(bc_call, pstructType);
indexVector.clear();
indexVector.push_back(const_0);
indexVector.push_back(const_2);
llvm::Value *allo_3 = builder.CreateGEP(bitCast_allo, indexVector);
Value *bitCast_load = builder.CreateLoad(allo_3);
builder.CreateStore(bitCast_load, exc);
builder.CreateStore(bitCast_load, res_allo);
ArrayRef<Value*> argsRef_5;
builder.CreateCall(end_catch, argsRef_5);
builder.CreateBr(eh_end);
//eh.resume basicblock
builder.SetInsertPoint(eh_resume);
Value *exn1 = builder.CreateLoad(exn_slot);
Value *sel1 = builder.CreateLoad(ehselector_slot);
Constant *undef_value = UndefValue::get(padStruct);
Value *lpad_val = builder.CreateInsertValue(undef_value, exn1, 0);
Value *lpad_val3 = builder.CreateInsertValue(lpad_val, sel1, 1);
builder.CreateResume(lpad_val3);
//
//
//===---------------------------------------------------------------------------------===//
module->dump();
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
InitializeNativeTargetAsmParser();
ExecutionEngine *ee =
EngineBuilder(std::unique_ptr<Module>(module)).setEngineKind(
EngineKind::JIT).create();
void *barAddr = ee->getPointerToFunction(bar);
//運作機器指令
typedef int (*FuncType)();
FuncType barFunc = (FuncType) barAddr;
ee->finalizeObject();
std::cout << barFunc() << std::endl;
delete module;
return 0;
}
2、使用下面指令編譯main.cpp檔案生成可執行檔案main
clang++ main.cpp -o main `llvm-config --cflags --ldflags --libs --system-libs`
3.使用
./main
指令檢視生成的ir和函數的傳回值
; ModuleID = 'test'
source_filename = "test"
%exception_struct = type { i8*, i8*, i32 }
%pad_struct = type { i8*, i32 }
@_ZTIi = external constant i8*
@0 = private constant [15 x i8] c"exception_name\00"
@1 = private constant [5 x i8] c"uuid\00"
declare i8* @__cxa_allocate_exception(i64)
declare void @__cxa_throw(i8*, i8*, i8*)
define i32 @foo() {
entry:
%excep = call i8* @__cxa_allocate_exception(i64 16)
%0 = bitcast i8* %excep to %exception_struct*
%1 = getelementptr %exception_struct, %exception_struct* %0, i32 0, i32 0
%2 = getelementptr %exception_struct, %exception_struct* %0, i32 0, i32 1
%3 = getelementptr %exception_struct, %exception_struct* %0, i32 0, i32 2
store i8* getelementptr inbounds ([15 x i8], [15 x i8]* @0, i32 0, i32 0), i8** %1
store i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0), i8** %2
store i32 52081, i32* %3
call void @__cxa_throw(i8* %excep, i8* bitcast (i8** @_ZTIi to i8*), i8* null)
ret i32 0
}
declare i32 @__gxx_personality_v0(...)
; Function Attrs: nounwind readnone
declare i32 @llvm.eh.typeid.for(i8*) #0
declare i8* @__cxa_begin_catch(i8*)
declare void @__cxa_end_catch()
define i32 @bar() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
entry:
%res = alloca i32
%exn.slot = alloca i8*
%ehselector.slot = alloca i32
%exc = alloca i32
store i32 0, i32* %res
%0 = invoke i32 @foo()
to label %invoke.cont unwind label %lpad
invoke.cont: ; preds = %entry
store i32 %0, i32* %res
br label %try.cont
lpad: ; preds = %entry
%1 = landingpad %pad_struct
catch i8* bitcast (i8** @_ZTIi to i8*)
%2 = extractvalue %pad_struct %1, 0
store i8* %2, i8** %exn.slot
%3 = extractvalue %pad_struct %1, 1
store i32 %3, i32* %ehselector.slot
br label %catch.dispatch
catch.dispatch: ; preds = %lpad
%4 = load i32, i32* %ehselector.slot
%5 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
%matches = icmp eq i32 %4, %5
br i1 %matches, label %catch, label %eh.resume
catch: ; preds = %catch.dispatch
%6 = load i8*, i8** %exn.slot
%7 = call i8* @__cxa_begin_catch(i8* %6)
%8 = bitcast i8* %7 to %exception_struct*
%9 = getelementptr %exception_struct, %exception_struct* %8, i32 0, i32 2
%10 = load i32, i32* %9
store i32 %10, i32* %exc
store i32 %10, i32* %res
call void @__cxa_end_catch()
br label %eh.end
try.cont: ; preds = %invoke.cont
br label %eh.end
eh.end: ; preds = %catch, %try.cont
%11 = load i32, i32* %res
ret i32 %11
eh.resume: ; preds = %catch.dispatch
%12 = load i8*, i8** %exn.slot
%13 = load i32, i32* %ehselector.slot
%14 = insertvalue %pad_struct undef, i8* %12, 0
%15 = insertvalue %pad_struct %14, i32 %13, 1
resume %pad_struct %15
}
attributes #0 = { nounwind readnone }
//函數傳回值
52081