这一期我们通过编译C 到LLVM代码来查看这部分的实现。在此之前我们需要了解一些简单的基础知识,之后我们将从一个最小抛出异常的代码开始,逐渐复杂化这个例子,查看生成不同的LLVM IR来理解整个过程。
函数调用
首先一个编译一个函数调用的过程中,LLVM常用的指令有call和invoke两类。
call是简单的一个函数调用,不会包含任何异常等。
invoke则用于调用可能抛出异常的函数,同时指令参数中还要添加用于处理异常代码的label
代码语言:javascript复制<result> = [tail | musttail | notail ] call [fast-math flags] [cconv] [ret attrs] [addrspace(<num>)]
<ty>|<fnty> <fnptrval>(<function args>) [fn attrs] [ operand bundles ]
代码语言:javascript复制<result> = invoke [cconv] [ret attrs] [addrspace(<num>)] <ty>|<fnty> <fnptrval>(<function args>) [fn attrs]
[operand bundles] to label <normal label> unwind label <exception label>
https://llvm.org/docs/LangRef.html#invoke-instruction
https://llvm.org/docs/LangRef.html#call-instruction
具体使用案例可以参考后面的throw a except without try这一部分
only throw string
这是上期中展示过的代码
代码语言:javascript复制void f1()
{
int a = 1;
}
void f2()
{
throw "error";
}
void f3()
{
f1();
f2();
}
这是使用clang生成的ll
代码语言:javascript复制@.str = private unnamed_addr constant [6 x i8] c"error 0", align 1
@_ZTIPKc = external constant i8*
; Function Attrs: noinline nounwind optnone ssp uwtable
define void @_Z2f1v() #0 {
%1 = alloca i32, align 4
store i32 1, i32* %1, align 4
ret void
}
; Function Attrs: noinline optnone ssp uwtable
define void @_Z2f2v() #1 {
%1 = call i8* @__cxa_allocate_exception(i64 8) #2
%2 = bitcast i8* %1 to i8**
store i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str, i64 0, i64 0), i8** %2, align 16
call void @__cxa_throw(i8* %1, i8* bitcast (i8** @_ZTIPKc to i8*), i8* null) #3
unreachable
}
declare i8* @__cxa_allocate_exception(i64)
declare void @__cxa_throw(i8*, i8*, i8*)
; Function Attrs: noinline optnone ssp uwtable
define void @_Z2f3v() #1 {
call void @_Z2f1v()
call void @_Z2f2v()
ret void
}
现在回来看,在需要抛出异常的函数中在进入的时候就分配了一个exception对象,并且将抛出的字符串写入,调用的方式也是简单的call。
对于调用者来说仍然使用的是call,而不是invoke,我认为因为这里throw以后不会进行捕获处理,而是直接挂掉,invoke最大的特点是要传递一个异常处理的label
这个例子只有__cxa_allocate_exception和__cxa_throw的声明(以下简称alloc和throw,其他函数用类似的简称规则),接下来我们看一个稍微复杂一些的例子。
throw a except without try
现在我们不只是抛出一个字符串,而是抛出一个异常对象。
代码语言:javascript复制#include <stdexcept>
void f2()
{
throw std::runtime_error("");
}
void f()
{
f2();
}
代码语言:javascript复制@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
@_ZTISt13runtime_error = external constant ptr
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z2f2v() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = call ptr @__cxa_allocate_exception(i64 16) #3
invoke void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16) %3, ptr noundef @.str)
to label %4 unwind label %5
4: ; preds = %0
call void @__cxa_throw(ptr %3, ptr @_ZTISt13runtime_error, ptr @_ZNSt13runtime_errorD1Ev) #4
unreachable
5: ; preds = %0
%6 = landingpad { ptr, i32 }
cleanup
%7 = extractvalue { ptr, i32 } %6, 0
store ptr %7, ptr %1, align 8
%8 = extractvalue { ptr, i32 } %6, 1
store i32 %8, ptr %2, align 4
call void @__cxa_free_exception(ptr %3) #3
br label %9
9: ; preds = %5
= load ptr, ptr %1, align 8
= load i32, ptr %2, align 4
= insertvalue { ptr, i32 } undef, ptr , 0
= insertvalue { ptr, i32 } , i32 , 1
resume { ptr, i32 }
}
declare ptr @__cxa_allocate_exception(i64)
declare void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16), ptr noundef) unnamed_addr #1
declare i32 @__gxx_personality_v0(...)
declare void @__cxa_free_exception(ptr)
; Function Attrs: nounwind
declare void @_ZNSt13runtime_errorD1Ev(ptr noundef nonnull align 8 dereferenceable(16)) unnamed_addr #2
declare void @__cxa_throw(ptr, ptr, ptr)
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z1fv() #0 {
call void @_Z2f2v()
ret void
}
首先我们来翻译一下一些mangled后的符号。
_ZNSt13runtime_errorD1Ev:构造函数
_ZTISt13runtime_error:typeinfo for std::runtime_error
这里发生了些许变化。
- 首先函数的声明增加了一个attribute,personality ptr @__gxx_personality_v0,这指定了异常处理时用什么函数进行响应。
- 构造runtime_error的过程是nounwind,即会抛出异常的,因此使用invoke调用,而这里构造过程的this指针而是传递了__cxa_allocate_exception的返回值。
- 在成功构造以后则将分配的异常对象以及runtime_error的类型传递给__cxa_throw。
- 在失败后则是针对构造时抛出异常的处理,这里具体做了以下几件事情
- clean up
- extract value(取出landing pad中的异常值和异常类型)
- free异常
- 进行resume,交给调用者进行处理。
此外这里出现了更多函数的声明。除了alloc和throw exception,以及runtime_error的构造函数外,多了__cxa_free_exception以及__gxx_personality_v0两个过程。__cxa_free_exception是用于释放最初构造的异常对象,__gxx_personality_v0则是libstdc 中的用于异常处理的函数,后续的文章中我们会提到具体实现。
throw exception with try catch
在我们抛出对象后,再引入catch
代码语言:javascript复制#include <stdexcept>
#include <iostream>
void f2()
{
throw std::runtime_error("");
}
void f()
{
try
{
f2();
}
catch(const std::exception& e)
{
std::cerr << e.what() << 'n';
}
}
代码语言:javascript复制$__clang_call_terminate = comdat any
@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
@_ZTISt13runtime_error = external constant ptr
@_ZTISt9exception = external constant ptr
@_ZSt4cerr = external global %"class.std::basic_ostream", align 8
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z2f2v() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = call ptr @__cxa_allocate_exception(i64 16) #5
invoke void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16) %3, ptr noundef @.str)
to label %4 unwind label %5
4: ; preds = %0
call void @__cxa_throw(ptr %3, ptr @_ZTISt13runtime_error, ptr @_ZNSt13runtime_errorD1Ev) #6
unreachable
5: ; preds = %0
%6 = landingpad { ptr, i32 }
cleanup
%7 = extractvalue { ptr, i32 } %6, 0
store ptr %7, ptr %1, align 8
%8 = extractvalue { ptr, i32 } %6, 1
store i32 %8, ptr %2, align 4
call void @__cxa_free_exception(ptr %3) #5
br label %9
9: ; preds = %5
= load ptr, ptr %1, align 8
= load i32, ptr %2, align 4
= insertvalue { ptr, i32 } undef, ptr , 0
= insertvalue { ptr, i32 } , i32 , 1
resume { ptr, i32 }
}
declare ptr @__cxa_allocate_exception(i64)
declare void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16), ptr noundef) unnamed_addr #1
declare i32 @__gxx_personality_v0(...)
declare void @__cxa_free_exception(ptr)
; Function Attrs: nounwind
declare void @_ZNSt13runtime_errorD1Ev(ptr noundef nonnull align 8 dereferenceable(16)) unnamed_addr #2
declare void @__cxa_throw(ptr, ptr, ptr)
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z1fv() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = alloca ptr, align 8
invoke void @_Z2f2v()
to label %4 unwind label %5
4: ; preds = %0
br label %
5: ; preds = %0
%6 = landingpad { ptr, i32 }
catch ptr @_ZTISt9exception
%7 = extractvalue { ptr, i32 } %6, 0
store ptr %7, ptr %1, align 8
%8 = extractvalue { ptr, i32 } %6, 1
store i32 %8, ptr %2, align 4
br label %9
9: ; preds = %5
= load i32, ptr %2, align 4
= call i32 @llvm.eh.typeid.for(ptr @_ZTISt9exception) #5
= icmp eq i32 ,
br i1 , label , label 1
13: ; preds = %9
= load ptr, ptr %1, align 8
= call ptr @__cxa_begin_catch(ptr ) #5
store ptr , ptr %3, align 8
= load ptr, ptr %3, align 8
= load ptr, ptr , align 8
= getelementptr inbounds ptr, ptr , i64 2
= load ptr, ptr , align 8
= call noundef ptr (ptr noundef nonnull align 8 dereferenceable(8) ) #5
! = invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cerr, ptr noundef )
to label " unwind label &
22: ; preds =
# = invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c(ptr noundef nonnull align 8 dereferenceable(8) !, i8 noundef signext 10)
to label $ unwind label &
24: ; preds = "
call void @__cxa_end_catch()
br label %
25: ; preds = $, %4
ret void
26: ; preds = ",
' = landingpad { ptr, i32 }
cleanup
( = extractvalue { ptr, i32 } ', 0
store ptr (, ptr %1, align 8
) = extractvalue { ptr, i32 } ', 1
store i32 ), ptr %2, align 4
invoke void @__cxa_end_catch()
to label 0 unwind label 6
30: ; preds = &
br label 1
31: ; preds = 0, %9
2 = load ptr, ptr %1, align 8
3 = load i32, ptr %2, align 4
4 = insertvalue { ptr, i32 } undef, ptr 2, 0
5 = insertvalue { ptr, i32 } 4, i32 3, 1
resume { ptr, i32 } 5
36: ; preds = &
7 = landingpad { ptr, i32 }
catch ptr null
8 = extractvalue { ptr, i32 } 7, 0
call void @__clang_call_terminate(ptr 8) #7
unreachable
}
; Function Attrs: nounwind readnone
declare i32 @llvm.eh.typeid.for(ptr) #3
declare ptr @__cxa_begin_catch(ptr)
declare noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c(ptr noundef nonnull align 8 dereferenceable(8), i8 noundef signext) #1
declare noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #1
declare void @__cxa_end_catch()
; Function Attrs: noinline noreturn nounwind
define linkonce_odr hidden void @__clang_call_terminate(ptr %0) #4 comdat {
%2 = call ptr @__cxa_begin_catch(ptr %0) #5
call void @_ZSt9terminatev() #7
unreachable
}
declare void @_ZSt9terminatev()
引入catch后,调用f2的f1中也增加了对应的landingpad相关的过程。此时landingpad的作用是用于catch特定类型的异常,而在f2中构造runtime_error的异常并没有被处理,因此需要clean临时生成的信息。
另外这里的IR变得更加复杂了,先看一下流程图再来看逐步的介绍。
代码语言:javascript复制flowchart LR
invoke --没有异常--> return
invoke --产生异常--> landingpad捕获
landingpad捕获 --typeid匹配--> 执行catch
landingpad捕获 --typeid不匹配--> clean_resume
执行catch --执行没有错误--> return
执行catch --执行有错误 --> 出错cleanup
出错cleanup --end_catch没错--> clean_resume
出错cleanup --end_catch出错--> terminate
在获取完异常值和类型信息后跳到了label %9,将从landingpad中获取到的异常类型信息和要catch的类型进行比较,如果失败则跳到1进行和f2中相同的resume的操作,将控制权交给调用者。
如果成功则进入label 进行catch,首先调用__cxa_begin_catch,之后进入对应的body中打印错误信息,直到catch的代码执行结束后调用__cxa_end_catch,最终返回。在这个过程中如果出现错误则又会跳到一个landingpad进行clean up,由于前面进行过begin_catch,因此这里的landingpad也要负责end_catch。
这里的end_catch有些不同,使用invoke调用,成功了会和没有catch成功后一样进行resume,失败了则会直接terminate。
当这个例子完全理清楚后,后面的例子基本上都是这个例子的变种,所以不再详细赘述IR内容的细节了。
multi try catch
代码语言:javascript复制#include <stdexcept>
#include <iostream>
void f2()
{
throw std::runtime_error("");
}
void f()
{
try
{
f2();
}
catch(const std::runtime_error&)
{
std::cerr << "runtime_error" << std::endl;
}
catch(const std::invalid_argument&)
{
std::cerr << "invalid_argument" << std::endl;
}
catch(const std::exception& e)
{
std::cerr << e.what() << 'n';
}
}
这个例子和上一个的核心区别是landingpad的时候catch了多种异常,另外在判断typeid的时候多了一些判断,就像if加上多个elseif一样,因此不再多解释了,如果有兴趣的话可以自行研究。
代码语言:javascript复制$__clang_call_terminate = comdat any
@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
@_ZTISt13runtime_error = external constant ptr
@_ZTISt16invalid_argument = external constant ptr
@_ZTISt9exception = external constant ptr
@_ZSt4cerr = external global %"class.std::basic_ostream", align 8
@.str.1 = private unnamed_addr constant [17 x i8] c"invalid_argument 0", align 1
@.str.2 = private unnamed_addr constant [14 x i8] c"runtime_error 0", align 1
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z2f2v() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = call ptr @__cxa_allocate_exception(i64 16) #5
invoke void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16) %3, ptr noundef @.str)
to label %4 unwind label %5
4: ; preds = %0
call void @__cxa_throw(ptr %3, ptr @_ZTISt13runtime_error, ptr @_ZNSt13runtime_errorD1Ev) #6
unreachable
5: ; preds = %0
%6 = landingpad { ptr, i32 }
cleanup
%7 = extractvalue { ptr, i32 } %6, 0
store ptr %7, ptr %1, align 8
%8 = extractvalue { ptr, i32 } %6, 1
store i32 %8, ptr %2, align 4
call void @__cxa_free_exception(ptr %3) #5
br label %9
9: ; preds = %5
= load ptr, ptr %1, align 8
= load i32, ptr %2, align 4
= insertvalue { ptr, i32 } undef, ptr , 0
= insertvalue { ptr, i32 } , i32 , 1
resume { ptr, i32 }
}
declare ptr @__cxa_allocate_exception(i64)
declare void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16), ptr noundef) unnamed_addr #1
declare i32 @__gxx_personality_v0(...)
declare void @__cxa_free_exception(ptr)
; Function Attrs: nounwind
declare void @_ZNSt13runtime_errorD1Ev(ptr noundef nonnull align 8 dereferenceable(16)) unnamed_addr #2
declare void @__cxa_throw(ptr, ptr, ptr)
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z1fv() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = alloca ptr, align 8
%4 = alloca ptr, align 8
%5 = alloca ptr, align 8
invoke void @_Z2f2v()
to label %6 unwind label %7
6: ; preds = %0
br label "
7: ; preds = %0
%8 = landingpad { ptr, i32 }
catch ptr @_ZTISt13runtime_error
catch ptr @_ZTISt16invalid_argument
catch ptr @_ZTISt9exception
%9 = extractvalue { ptr, i32 } %8, 0
store ptr %9, ptr %1, align 8
= extractvalue { ptr, i32 } %8, 1
store i32 , ptr %2, align 4
br label
11: ; preds = %7
= load i32, ptr %2, align 4
= call i32 @llvm.eh.typeid.for(ptr @_ZTISt13runtime_error) #5
= icmp eq i32 ,
br i1 , label , label #
15: ; preds =
= load ptr, ptr %1, align 8
= call ptr @__cxa_begin_catch(ptr ) #5
store ptr , ptr %5, align 8
= invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cerr, ptr noundef @.str.2)
to label unwind label X
19: ; preds =
= invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8) , ptr noundef @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_)
to label ! unwind label X
21: ; preds =
call void @__cxa_end_catch()
br label "
22: ; preds = !, 2, G, %6
ret void
23: ; preds =
$ = call i32 @llvm.eh.typeid.for(ptr @_ZTISt16invalid_argument) #5
% = icmp eq i32 , $
br i1 %, label &, label 3
26: ; preds = #
' = load ptr, ptr %1, align 8
( = call ptr @__cxa_begin_catch(ptr ') #5
store ptr (, ptr %4, align 8
) = invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cerr, ptr noundef @.str.1)
to label 0 unwind label S
30: ; preds = &
1 = invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8) ), ptr noundef @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_)
to label 2 unwind label S
32: ; preds = 0
call void @__cxa_end_catch()
br label "
33: ; preds = #
4 = call i32 @llvm.eh.typeid.for(ptr @_ZTISt9exception) #5
5 = icmp eq i32 , 4
br i1 5, label 6, label c
36: ; preds = 3
7 = load ptr, ptr %1, align 8
8 = call ptr @__cxa_begin_catch(ptr 7) #5
store ptr 8, ptr %3, align 8
9 = load ptr, ptr %3, align 8
@ = load ptr, ptr 9, align 8
A = getelementptr inbounds ptr, ptr @, i64 2
B = load ptr, ptr A, align 8
C = call noundef ptr B(ptr noundef nonnull align 8 dereferenceable(8) 9) #5
D = invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cerr, ptr noundef C)
to label E unwind label H
45: ; preds = 6
F = invoke noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c(ptr noundef nonnull align 8 dereferenceable(8) D, i8 noundef signext 10)
to label G unwind label H
47: ; preds = E
call void @__cxa_end_catch()
br label "
48: ; preds = E, 6
I = landingpad { ptr, i32 }
cleanup
P = extractvalue { ptr, i32 } I, 0
store ptr P, ptr %1, align 8
Q = extractvalue { ptr, i32 } I, 1
store i32 Q, ptr %2, align 4
invoke void @__cxa_end_catch()
to label R unwind label h
52: ; preds = H
br label c
53: ; preds = 0, &
T = landingpad { ptr, i32 }
cleanup
U = extractvalue { ptr, i32 } T, 0
store ptr U, ptr %1, align 8
V = extractvalue { ptr, i32 } T, 1
store i32 V, ptr %2, align 4
invoke void @__cxa_end_catch()
to label W unwind label h
57: ; preds = S
br label c
58: ; preds = ,
Y = landingpad { ptr, i32 }
cleanup
` = extractvalue { ptr, i32 } Y, 0
store ptr `, ptr %1, align 8
a = extractvalue { ptr, i32 } Y, 1
store i32 a, ptr %2, align 4
invoke void @__cxa_end_catch()
to label b unwind label h
62: ; preds = X
br label c
63: ; preds = b, W, R, 3
d = load ptr, ptr %1, align 8
e = load i32, ptr %2, align 4
f = insertvalue { ptr, i32 } undef, ptr d, 0
g = insertvalue { ptr, i32 } f, i32 e, 1
resume { ptr, i32 } g
68: ; preds = X, S, H
i = landingpad { ptr, i32 }
catch ptr null
p = extractvalue { ptr, i32 } i, 0
call void @__clang_call_terminate(ptr p) #7
unreachable
}
; Function Attrs: nounwind readnone
declare i32 @llvm.eh.typeid.for(ptr) #3
declare ptr @__cxa_begin_catch(ptr)
declare noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c(ptr noundef nonnull align 8 dereferenceable(8), i8 noundef signext) #1
declare noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #1
declare void @__cxa_end_catch()
; Function Attrs: noinline noreturn nounwind
define linkonce_odr hidden void @__clang_call_terminate(ptr %0) #4 comdat {
%2 = call ptr @__cxa_begin_catch(ptr %0) #5
call void @_ZSt9terminatev() #7
unreachable
}
declare void @_ZSt9terminatev()
declare noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #1
declare noundef nonnull align 8 dereferenceable(8) ptr @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(ptr noundef nonnull align 8 dereferenceable(8)) #1
rethrow
代码语言:javascript复制#include <stdexcept>
void f2()
{
throw std::runtime_error("");
}
void f()
{
try
{
f2();
}
catch(const std::exception& e)
{
throw e;
}
}
void outer()
{
f();
}
这个例子的IR也比较长,因此我们也先来解释一下和之前的差别。
主要差距在catch的body中,这里先分配了一个exception,然后invoke的方式调用throw的函数。throw函数是一定会触发异常的,因此成功的话会跳转到unreachable,对应的unwind label的内容则是进行clean操作,并且进行resume。这个步骤类似于前面throw exception with try catch中提到的在catch体中找不到对应类型的catch一致,直接进行clean resume。
代码语言:javascript复制$_ZNSt9exceptionC2ERKS_ = comdat any
$__clang_call_terminate = comdat any
@.str = private unnamed_addr constant [1 x i8] zeroinitializer, align 1
@_ZTISt13runtime_error = external constant ptr
@_ZTISt9exception = external constant ptr
@_ZTVSt9exception = external unnamed_addr constant { [5 x ptr] }, align 8
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z2f2v() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = call ptr @__cxa_allocate_exception(i64 16) #6
invoke void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16) %3, ptr noundef @.str)
to label %4 unwind label %5
4: ; preds = %0
call void @__cxa_throw(ptr %3, ptr @_ZTISt13runtime_error, ptr @_ZNSt13runtime_errorD1Ev) #7
unreachable
5: ; preds = %0
%6 = landingpad { ptr, i32 }
cleanup
%7 = extractvalue { ptr, i32 } %6, 0
store ptr %7, ptr %1, align 8
%8 = extractvalue { ptr, i32 } %6, 1
store i32 %8, ptr %2, align 4
call void @__cxa_free_exception(ptr %3) #6
br label %9
9: ; preds = %5
= load ptr, ptr %1, align 8
= load i32, ptr %2, align 4
= insertvalue { ptr, i32 } undef, ptr , 0
= insertvalue { ptr, i32 } , i32 , 1
resume { ptr, i32 }
}
declare ptr @__cxa_allocate_exception(i64)
declare void @_ZNSt13runtime_errorC1EPKc(ptr noundef nonnull align 8 dereferenceable(16), ptr noundef) unnamed_addr #1
declare i32 @__gxx_personality_v0(...)
declare void @__cxa_free_exception(ptr)
; Function Attrs: nounwind
declare void @_ZNSt13runtime_errorD1Ev(ptr noundef nonnull align 8 dereferenceable(16)) unnamed_addr #2
declare void @__cxa_throw(ptr, ptr, ptr)
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z1fv() #0 personality ptr @__gxx_personality_v0 {
%1 = alloca ptr, align 8
%2 = alloca i32, align 4
%3 = alloca ptr, align 8
invoke void @_Z2f2v()
to label %4 unwind label %5
4: ; preds = %0
br label #
5: ; preds = %0
%6 = landingpad { ptr, i32 }
catch ptr @_ZTISt9exception
%7 = extractvalue { ptr, i32 } %6, 0
store ptr %7, ptr %1, align 8
%8 = extractvalue { ptr, i32 } %6, 1
store i32 %8, ptr %2, align 4
br label %9
9: ; preds = %5
= load i32, ptr %2, align 4
= call i32 @llvm.eh.typeid.for(ptr @_ZTISt9exception) #6
= icmp eq i32 ,
br i1 , label , label $
13: ; preds = %9
= load ptr, ptr %1, align 8
= call ptr @__cxa_begin_catch(ptr ) #6
store ptr , ptr %3, align 8
= call ptr @__cxa_allocate_exception(i64 8) #6
= load ptr, ptr %3, align 8
call void @_ZNSt9exceptionC2ERKS_(ptr noundef nonnull align 8 dereferenceable(8) , ptr noundef nonnull align 8 dereferenceable(8) ) #6
invoke void @__cxa_throw(ptr , ptr @_ZTISt9exception, ptr @_ZNSt9exceptionD1Ev) #7
to label 2 unwind label
18: ; preds =
= landingpad { ptr, i32 }
cleanup
= extractvalue { ptr, i32 } , 0
store ptr , ptr %1, align 8
! = extractvalue { ptr, i32 } , 1
store i32 !, ptr %2, align 4
invoke void @__cxa_end_catch()
to label " unwind label )
22: ; preds =
br label $
23: ; preds = %4
ret void
24: ; preds = ", %9
% = load ptr, ptr %1, align 8
& = load i32, ptr %2, align 4
' = insertvalue { ptr, i32 } undef, ptr %, 0
( = insertvalue { ptr, i32 } ', i32 &, 1
resume { ptr, i32 } (
29: ; preds =
0 = landingpad { ptr, i32 }
catch ptr null
1 = extractvalue { ptr, i32 } 0, 0
call void @__clang_call_terminate(ptr 1) #8
unreachable
32: ; preds =
unreachable
}
; Function Attrs: nounwind readnone
declare i32 @llvm.eh.typeid.for(ptr) #3
declare ptr @__cxa_begin_catch(ptr)
; Function Attrs: noinline nounwind optnone sspstrong uwtable
define linkonce_odr dso_local void @_ZNSt9exceptionC2ERKS_(ptr noundef nonnull align 8 dereferenceable(8) %0, ptr noundef nonnull align 8 dereferenceable(8) %1) unnamed_addr #4 comdat align 2 {
%3 = alloca ptr, align 8
%4 = alloca ptr, align 8
store ptr %0, ptr %3, align 8
store ptr %1, ptr %4, align 8
%5 = load ptr, ptr %3, align 8
store ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTVSt9exception, i32 0, inrange i32 0, i32 2), ptr %5, align 8
ret void
}
; Function Attrs: nounwind
declare void @_ZNSt9exceptionD1Ev(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr #2
declare void @__cxa_end_catch()
; Function Attrs: noinline noreturn nounwind
define linkonce_odr hidden void @__clang_call_terminate(ptr %0) #5 comdat {
%2 = call ptr @__cxa_begin_catch(ptr %0) #6
call void @_ZSt9terminatev() #8
unreachable
}
declare void @_ZSt9terminatev()
; Function Attrs: mustprogress noinline optnone sspstrong uwtable
define dso_local void @_Z5outerv() #0 {
call void @_Z1fv()
ret void
}