IR API(六)——LLVM异常处理(Exception Handling in 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

猜你喜欢

转载自blog.csdn.net/qq_42570601/article/details/109602543