从cxx_init返回,在下面的lang_dependent_init中,init_asm_output为输出汇编代码准备好了文件。
lang_dependent_init (continue)
4535 init_asm_output (name);
4536
4537 /* These create various _DECL nodes, so need to be called after the
4538 front end is initialized. */
4539 init_eh ();
在配置gcc时,我们可以使用--enable-sjlj-exceptions,强制编译器使用基于C库函数setjmp及longjmp的异常处理机制。这是比较旧的异常处理设施,新的芯片现在会考虑异常处理的需求,比如提供专用的寄存器等。因此,如果不是强制要求使用setjmp/longjmp,编译器会自己判断是否需要setjmp/longjmp。
146 #ifndef MUST_USE_SJLJ_EXCEPTIONS in except.h
147 # if !(defined (EH_RETURN_DATA_REGNO) /
148 && (defined (IA64_UNWIND_INFO) /
149 || (DWARF2_UNWIND_INFO /
150 && (defined (EH_RETURN_HANDLER_RTX) /
151 || defined (HAVE_eh_return)))))
152 # define MUST_USE_SJLJ_EXCEPTIONS 1
153 # else
154 # define MUST_USE_SJLJ_EXCEPTIONS 0
155 # endif
156 #endif
上面的EH_RETURN_DATA_REGNO(N)是一个C表达式,其值是第N个可用于异常句柄的寄存器的编号,如果适用寄存器少于N则是INVALID_REGNUM(为~0)。对于x86机器,是0~1编号的寄存器(即AX,DX)。而IA64_UNWIND_INFO只为ia64芯片定义。
282 #if !defined (DWARF2_UNWIND_INFO) && defined (INCOMING_RETURN_ADDR_RTX)
283 #define DWARF2_UNWIND_INFO 1
284 #endif
INCOMING_RETURN_ADDR_RTX是一个C表达式,其值是,表示在任意函数开头,在其序幕(prologue)前,所传入返回值的位置的RTL对象。对于x86,这个位置在栈上,该宏有定义。而EH_RETURN_HANDLER_RTX,如果有定义,则是一个C表达式,其值是表示保存我们应该返回的异常处理句柄(即catch语句)的位置的RTL对象。x86没有定义该宏,但在配置阶段定义了HAVE_eh_return。
因而对于x86,MUST_USE_SJLJ_EXCEPTIONS为0,即可以不使用setjmp/longjmp。
158 #ifdef CONFIG_SJLJ_EXCEPTIONS in except.h
159 # if CONFIG_SJLJ_EXCEPTIONS == 1
160 # define USING_SJLJ_EXCEPTIONS 1
161 # endif
162 # if CONFIG_SJLJ_EXCEPTIONS == 0
163 # define USING_SJLJ_EXCEPTIONS 0
164 # ifndef EH_RETURN_DATA_REGNO
165 #error "EH_RETURN_DATA_REGNO required"
166 # endif
167 # if !defined(EH_RETURN_HANDLER_RTX) && !defined(HAVE_eh_return)
168 #error "EH_RETURN_HANDLER_RTX or eh_return required"
169 # endif
170 # if !defined(DWARF2_UNWIND_INFO) && !defined(IA64_UNWIND_INFO)
171 #error "{DWARF2,IA64}_UNWIND_INFO required"
172 # endif
173 # endif
174 #else
175 # define USING_SJLJ_EXCEPTIONS MUST_USE_SJLJ_EXCEPTIONS
176 #endif
宏CONFIG_SJLJ_EXCEPTIONS由--enable-sjlj-exceptions(或--disable-sjlj-exceptions)配置选项设置。对于禁止使用setjmp/longjmp,编译器需要检测是否可行。在我们这里, USING_SJLJ_EXCEPTIONS与MUST_USE_SJLJ_EXCEPTIONS定义相同,为0!那么下面的init_eh基本无事可做。该函数主要是为使用setjmp/longjmp的异常机制,初始化一个重要的数据结构——SjLj_Function_Context。这个结构在异常处理中,需要在栈上分配。看到,在其定义中,有用于维护的prev域。因而前端必须知道其定义来提供必要的维护。
54 struct SjLj_Function_Context in unwind-sjlj.c
55 {
56 /* This is the chain through all registered contexts. It is
57 filled in by _Unwind_SjLj_Register. */
58 struct SjLj_Function_Context *prev;
59
60 /* This is assigned in by the target function before every call
61 to the index of the call site in the lsda. It is assigned by
62 the personality routine to the landing pad index. */
63 int call_site;
64
65 /* This is how data is returned from the personality routine to
66 the target function's handler. */
67 _Unwind_Word data[4];
68
69 /* These are filled in once by the target function before any
70 exceptions are expected to be handled. */
71 _Unwind_Personality_Fn personality;
72 void *lsda;
73
74 #ifdef DONT_USE_BUILTIN_SETJMP
75 /* We don't know what sort of alignment requirements the system
76 jmp_buf has. We over estimated in except.c, and now we have
77 to match that here just in case the system *didn't* have more
78 restrictive requirements. */
79 jmp_buf jbuf __attribute__((aligned));
80 #else
81 void *jbuf[];
82 #endif
83 };
这也情有可原,因为这个上下文是要传给setjmp及longjmp的(域jbuf)。而在另一个机制中,每个上下文都是分开,不需要知道彼此,其处理机制由运行时库完整提供,并维护。前端不需要额外的处理。在这里不需要告诉前端。
366 void
367 init_eh (void) in except.c
368 {
369 if (! flag_exceptions)
370 return;
371
372 type_to_runtime_map = htab_create_ggc (31, t2r_hash, t2r_eq, NULL);
373
374 /* Create the SjLj_Function_Context structure. This should match
375 the definition in unwind-sjlj.c. */
376 if (USING_SJLJ_EXCEPTIONS)
377 {
...
455 }
456 }
通常在使用中是定义一族异常类,有基类及若干派生类。而在异常处理处,通常会使用一系列的catch语句对这些类型进行匹配,以选择匹配的句柄。因此编译器自动为异常类构建tinfo对象,但是前面我们看到,如果类没有定义虚函数,类节点没有保存tinfo对象的地方。为此,前端构建了type_to_runtime_map,用于绑定异常类及其tinfo对象。