diff options
author | Jason Merrill <jason@gcc.gnu.org> | 1997-09-15 22:07:50 -0400 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 1997-09-15 22:07:50 -0400 |
commit | ca55abaee78c7f75c7036d8616781bba42f1af6c (patch) | |
tree | d3f3fa9225d4040922a030638ba72ca3349ef34e | |
parent | 5168dcfc4419b301b0b083738a158b52cf32593e (diff) | |
download | gcc-ca55abaee78c7f75c7036d8616781bba42f1af6c.zip gcc-ca55abaee78c7f75c7036d8616781bba42f1af6c.tar.gz gcc-ca55abaee78c7f75c7036d8616781bba42f1af6c.tar.bz2 |
dwarf2 EH support
From-SVN: r15464
-rw-r--r-- | gcc/ChangeLog | 163 | ||||
-rw-r--r-- | gcc/Makefile.in | 66 | ||||
-rwxr-xr-x | gcc/configure | 5 | ||||
-rw-r--r-- | gcc/configure.in | 3 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 8 | ||||
-rw-r--r-- | gcc/cp/decl.c | 6 | ||||
-rw-r--r-- | gcc/emit-rtl.c | 1 | ||||
-rw-r--r-- | gcc/except.c | 369 | ||||
-rw-r--r-- | gcc/expr.c | 30 | ||||
-rw-r--r-- | gcc/libgcc2.c | 460 |
10 files changed, 882 insertions, 229 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a6daaa8..ca793d5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -12,6 +12,47 @@ Mon Sep 15 15:39:26 1997 Jeffrey A Law (law@cygnus.com) mode wider than HOST_WIDE_INT, then the high word of a CONST_INT is derived from the sign bit of the low word. +Mon Sep 15 11:43:38 1997 Jason Merrill <jason@yorick.cygnus.com> + + Support dwarf2 unwinding on PUSH_ROUNDING targets like the x86. + + * dwarf2.h: Add DW_CFA_GNU_args_size. + * frame.c (execute_cfa_insn): Likewise. + * dwarf2out.c (dwarf_cfi_name, output_cfi): Likewise. + (dwarf2out_args_size, dwarf2out_stack_adjust): New fns. + (dwarf2out_frame_debug): If this isn't a prologue or epilogue + insn, hand it off to dwarf2out_stack_adjust. + (dwarf2out_begin_prologue): Initialize args_size. + * frame.h (struct frame_state): Add args_size. + * libgcc2.c (__throw): Use args_size. + * final.c (final_scan_insn): If we push args, hand off all insns + to dwarf2out_frame_debug. + * defaults.h (DWARF2_UNWIND_INFO): OK for !ACCUMULATE_OUTGOING_ARGS. + + * dwarf2out.c dwarf2out_frame_debug): Fix typo. + Handle epilogue restore of SP from FP. + * emit-rtl.c (gen_sequence): Still generate a sequence if the + lone insn has RTX_FRAME_RELATED_P set. + + * frame.c (extract_cie_info): Handle "e" augmentation. + * dwarf2out.c (ASM_OUTPUT_DWARF_*): Provide definitions in the + absence of UNALIGNED_*_ASM_OP. + (UNALIGNED_*_ASM_OP): Only provide defaults if OBJECT_FORMAT_ELF. + (output_call_frame_info): Use "e" instead of "z" for augmentation. + Don't emit augmentation fields length. + (dwarf2out_do_frame): Move outside of #ifdefs. + * defaults.h (DWARF2_UNWIND_INFO): Don't require unaligned data + opcodes. + + * sparc.h (UNALIGNED_INT_ASM_OP et al): Don't define here after all. + * sparc/sysv4.h (UNALIGNED_INT_ASM_OP): Define here. + * sparc/sunos4.h (DWARF2_UNWIND_INFO): Define to 0. + * sparc/sun4gas.h: New file. + * configure.in: Use sun4gas.h if SunOS 4 --with-gnu-as. + + * collect2.c (write_c_file_stat, write_c_file_glob): Declare + __register_frame_table and __deregister_frame. + 1997-09-15 Brendan Kehoe <brendan@cygnus.com> * except.c (find_exception_handler_labels): Use xmalloc instead of @@ -45,6 +86,11 @@ Sat Sep 13 12:57:26 1997 Jeffrey A Law (law@cygnus.com) * haifa-sched.c (add_branch_dependences): Make each insn in a SCHED_GROUP_P block explicitly depend on the previous insn. +Fri Sep 12 13:49:58 1997 Jason Merrill <jason@yorick.cygnus.com> + + * except.h: Prototype dwarf2 hooks. + * expr.c: Adjust. + Thu Sep 11 17:43:55 1997 Jim Wilson <wilson@cygnus.com> * configure.in (native_prefix): Delete. @@ -112,6 +158,95 @@ Wed Sep 10 14:05:08 1997 H.J. Lu (hjl@gnu.ai.mit.edu) (check-gcc, check-g++): Depend on testsuite/site.exp. Don't stop for failure. +Wed Sep 10 12:59:57 1997 Jason Merrill <jason@yorick.cygnus.com> + + * expr.c (expand_builtin): Only support __builtin_dwarf_fp_regnum() + if DWARF2_UNWIND_INFO. + +Wed Sep 10 11:49:20 1997 Jason Merrill <jason@yorick.cygnus.com> + + Add support for exception handling using DWARF 2 frame unwind info. + Currently works on SPARC and MIPS, and almost on x86. + + * libgcc2.c (get_reg, put_reg, get_return_addr, put_return_addr, + next_stack_level, in_reg_window): Helper fns. + (__throw): Implement for DWARF2_UNWIND_INFO. + + * expr.c (expand_builtin): Handle builtins used by __throw. + * tree.h (enum built_in_function): Add builtins used by __throw. + * c-decl.c (init_decl_processing): Declare builtins used by __throw. + * dwarf2out.c (expand_builtin_dwarf_fp_regnum): Used by __throw. + * except.c (expand_builtin_unwind_init): Hook for dwarf2 __throw. + (expand_builtin_extract_return_addr): Likewise. + (expand_builtin_frob_return_addr): Likewise. + (expand_builtin_set_return_addr_reg): Likewise. + (expand_builtin_eh_stub): Likewise. + (expand_builtin_set_eh_regs): Likewise. + (eh_regs): Choose two call-clobbered registers for passing back values. + + * frame.c, frame.h: New files for parsing dwarf 2 frame info. + * Makefile.in (LIB2ADD): New variable. Add $(srcdir)/frame.c. + (libgcc2.a): Use it instead of $(LIB2FUNCS_EXTRA) $(LANG_LIB2FUNCS) + (stmp-multilib): Likewise. + ($(T)crtbegin.o, $(T)crtend.o): Add -fno-exceptions. + + * except.c: #include "defaults.h". + (exceptions_via_longjmp): Default depends on DWARF2_UNWIND_INFO. + (emit_throw): Don't defeat assemble_external if DWARF2_UNWIND_INFO. + (register_exception_table_p): New fn. + (start_eh_unwinder): Don't do anything if DWARF2_UNWIND_INFO. + (end_eh_unwinder): Likewise. + + * crtstuff.c: Wrap .eh_frame section, use EH_FRAME_SECTION_ASM_OP, + call __register_frame and __deregister_frame as needed. + * varasm.c (eh_frame_section): New fn if EH_FRAME_SECTION_ASM_OP. + * dwarf2out.c (EH_FRAME_SECTION): Now a function-like macro. Check + EH_FRAME_SECTION_ASM_OP. + * sparc/sysv4.h (EH_FRAME_SECTION_ASM_OP): Define. + * mips/iris6.h: (EH_FRAME_SECTION_ASM_OP): Define. + (LINK_SPEC): Add __EH_FRAME_BEGIN__ to hidden symbols. + + * dwarf2out.c (output_call_frame_info): If no support for + EXCEPTION_SECTION, mark the start of the frame info with a + collectable tag. + * collect2.c (frame_tables): New list. + (is_ctor_dtor): Recognise frame entries. + (scan_prog_file): Likewise. + (main): Pass -fno-exceptions to sub-compile. Also do collection + if there are any frame entries. + (write_c_file_stat): Call __register_frame_table and + __deregister_frame as needed. + (write_c_file_glob): Likewise. + + * defaults.h (DWARF2_UNWIND_INFO): Default to 1 if supported. + Also require unaligned reloc support. + * sparc.h (UNALIGNED_SHORT_ASM_OP, UNALIGNED_INT_ASM_OP, + UNALIGNED_DOUBLE_INT_ASM_OP): Define here. + * sparc/sysv4.h: Not here. + + * toplev.c (compile_file): Call dwarf2out_frame_{init,finish}. + * dwarf2out.c (dwarf2out_init): Don't call dwarf2out_frame_init. + (dwarf2out_finish): Don't call dwarf2out_frame_finish. + + * libgcc2.c (L_eh): Reorganize, moving code shared by different + EH implementations to the top. + (find_exception_handler): Split out. Start from 0. Compare against + end with >=. + (__find_first_exception_table_match): Use it. + * except.c (output_exception_table): Don't do anything if there's + no table. Don't output a first entry of zeroes. + (eh_outer_context): Adjust properly. + (add_eh_table_entry): Use xrealloc. + * toplev.c (compile_file): Just call output_exception_table. + +Wed Sep 10 11:30:36 1997 Jason Merrill <jason@cygnus.com> + + * i386.c (ix86_prologue): Add dwarf2 support for !do_rtl case. + +Wed Sep 10 08:17:10 1997 Torbjorn Granlund <tege@pdc.kth..se> + + * except.c (eh_outer_context): Do masking using expand_and. + Wed Sep 10 01:38:30 1997 Doug Evans <dje@cygnus.com> Add port done awhile ago for the ARC cpu. @@ -152,10 +287,38 @@ Tue Sep 9 17:07:36 1997 Stan Cox <coxs@dg-rtp.dg.com> * m88k.c (m88k_expand_prologue): Set MEM_IN_STRUCT_P of va_list template. +Tue Sep 9 09:50:02 1997 Richard Kenner <kenner@vlsi1.ultra.nyu.edu> + + * dwarf2out.c (output_call_frame_info): Call named_section. + Tue Sep 9 09:12:17 1997 Jeffrey A Law (law@cygnus.com) * haifa-sched.c (print_value): Fix last change. +Tue Sep 9 01:30:37 1997 Jason Merrill <jason@yorick.cygnus.com> + + * mips.h (DWARF_FRAME_REGNUM): Use the same numbering regardless of + write_symbols. + +Mon Sep 8 16:32:43 1997 Jason Merrill <jason@yorick.cygnus.com> + + * mips.c (function_prologue): Set up the CFA when ABI_32. + + * sparc.c (save_regs): Check dwarf2out_do_frame instead of DWARF2_DEBUG + for dwarf2 unwind info. + (output_function_prologue, sparc_flat_output_function_prologue): Same. + + * final.c (final_end_function): Check dwarf2out_do_frame instead + of DWARF2_DEBUG for dwarf2 unwind info. + (final_scan_insn): Likewise. + (final_start_function): Likewise. Initialize dwarf2 frame debug here. + (final): Not here. + + * expr.c (expand_builtin_return_addr): Only SETUP_FRAME_ADDRESSES if + count > 0. + + * varasm.c (exception_section): Check EXCEPTION_SECTION first. + Mon Sep 8 15:15:11 1997 Nick Clifton <nickc@cygnus.com> * v850.h (ASM_SPEC): Pass on target processor. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 30dbf0f..985cbd5 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -268,7 +268,7 @@ LIBGCC2 = libgcc2.a # -g1 causes output of debug info only for file-scope entities. # we use this here because that should be enough, and also # so that -g1 will be tested. -LIBGCC2_DEBUG_CFLAGS = -g1 +LIBGCC2_DEBUG_CFLAGS = -g LIBGCC2_CFLAGS = -O2 $(LIBGCC2_INCLUDES) $(GCC_CFLAGS) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED # Additional options to use when compiling libgcc2.a. @@ -949,8 +949,9 @@ libgcc2.ready: $(GCC_PASSES) $(LIBGCC2_DEPS) stmp-int-hdrs touch libgcc2.ready; \ fi -libgcc2.a: libgcc2.c libgcc2.ready $(CONFIG_H) $(LIB2FUNCS_EXTRA) \ - $(LANG_LIB2FUNCS) machmode.h longlong.h gbl-ctors.h config.status +LIB2ADD = $(srcdir)/frame.c $(LIB2FUNCS_EXTRA) $(LANG_LIB2FUNCS) +libgcc2.a: libgcc2.c libgcc2.ready $(CONFIG_H) $(LIB2ADD) \ + machmode.h longlong.h gbl-ctors.h config.status # Actually build it in tmplibgcc2.a, then rename at end, # so that libgcc2.a itself remains nonexistent if compilation is aborted. -rm -f tmplibgcc2.a @@ -975,35 +976,31 @@ libgcc2.a: libgcc2.c libgcc2.ready $(CONFIG_H) $(LIB2FUNCS_EXTRA) \ # We don't use -e here because there are if statements # that should not make the command give up when the if condition is false. # Instead, we test for failure after each command where it matters. - for file in .. $(LIB2FUNCS_EXTRA) $(LANG_LIB2FUNCS); \ - do \ - if [ x$${file} != x.. ]; then \ - name=`echo $${file} | sed -e 's/[.][cSo]$$//' -e 's/[.]asm$$//' -e 's/[.]txt$$//'`; \ - oname=` echo $${name} | sed -e 's,.*/,,'`; \ - if [ $${name}.txt = $${file} ]; then \ - for f in .. `cat $${file}`; do if [ x$${f} != x.. ]; then \ - $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ - AR="$(AR)" AR_FLAGS="$(AR_FLAGS)" CC="$(CC)" \ - CFLAGS="$(CFLAGS)" HOST_PREFIX="$(HOST_PREFIX)" \ - HOST_PREFIX_1="$(HOST_PREFIX_1)" \ - LANGUAGES="$(LANGUAGES)" \ - LIBGCC2_CFLAGS="$(LIBGCC2_CFLAGS)" $${f}; \ - if [ $$? -eq 0 ] ; then true; else exit 1; fi; \ - $(AR) $(AR_FLAGS) tmplibgcc2.a $${f}; \ - rm -f $${f}; \ - else true; \ - fi; done; \ - else \ - echo $${name}; \ - if [ $${name}.asm = $${file} ]; then \ - cp $${file} $${name}.s || exit 1; file=$${name}.s; \ - else true; fi; \ - $(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) $(INCLUDES) -c $${file}; \ + for file in $(LIB2ADD); do \ + name=`echo $${file} | sed -e 's/[.][cSo]$$//' -e 's/[.]asm$$//' -e 's/[.]txt$$//'`; \ + oname=` echo $${name} | sed -e 's,.*/,,'`; \ + if [ $${name}.txt = $${file} ]; then \ + for f in .. `cat $${file}`; do if [ x$${f} != x.. ]; then \ + $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ + AR="$(AR)" AR_FLAGS="$(AR_FLAGS)" CC="$(CC)" \ + CFLAGS="$(CFLAGS)" HOST_PREFIX="$(HOST_PREFIX)" \ + HOST_PREFIX_1="$(HOST_PREFIX_1)" \ + LANGUAGES="$(LANGUAGES)" \ + LIBGCC2_CFLAGS="$(LIBGCC2_CFLAGS)" $${f}; \ if [ $$? -eq 0 ] ; then true; else exit 1; fi; \ - $(AR) $(AR_FLAGS) tmplibgcc2.a $${oname}$(objext); \ - rm -f $${name}.s $${oname}$(objext); \ - fi; \ - else true; \ + $(AR) $(AR_FLAGS) tmplibgcc2.a $${f}; \ + rm -f $${f}; \ + else true; \ + fi; done; \ + else \ + echo $${name}; \ + if [ $${name}.asm = $${file} ]; then \ + cp $${file} $${name}.s || exit 1; file=$${name}.s; \ + else true; fi; \ + $(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) $(INCLUDES) -c $${file}; \ + if [ $$? -eq 0 ] ; then true; else exit 1; fi; \ + $(AR) $(AR_FLAGS) tmplibgcc2.a $${oname}$(objext); \ + rm -f $${name}.s $${oname}$(objext); \ fi; \ done mv tmplibgcc2.a libgcc2.a @@ -1050,8 +1047,7 @@ stamp-mlib: $(srcdir)/genmultilib Makefile # Build multiple copies of libgcc.a, one for each target switch. stmp-multilib: $(LIBGCC1) libgcc2.c libgcc2.ready $(CONFIG_H) \ - $(LIB2FUNCS_EXTRA) $(LANG_LIB2FUNCS) machmode.h longlong.h gbl-ctors.h \ - config.status + $(LIB2ADD) machmode.h longlong.h gbl-ctors.h config.status for i in `$(GCC_FOR_TARGET) --print-multi-lib`; do \ dir=`echo $$i | sed -e 's/;.*$$//'`; \ flags=`echo $$i | sed -e 's/^[^;]*;//' -e 's/@/ -/g'`; \ @@ -1121,12 +1117,12 @@ stmp-multilib-sub: # constructors. $(T)crtbegin.o: crtstuff.c $(GCC_PASSES) $(CONFIG_H) gbl-ctors.h $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ - -finhibit-size-directive -fno-inline-functions $(CRTSTUFF_T_CFLAGS) \ + -finhibit-size-directive -fno-inline-functions -fno-exceptions $(CRTSTUFF_T_CFLAGS) \ -c $(srcdir)/crtstuff.c -DCRT_BEGIN -o $(T)crtbegin$(objext) $(T)crtend.o: crtstuff.c $(GCC_PASSES) $(CONFIG_H) gbl-ctors.h $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ - -finhibit-size-directive -fno-inline-functions $(CRTSTUFF_T_CFLAGS) \ + -finhibit-size-directive -fno-inline-functions -fno-exceptions $(CRTSTUFF_T_CFLAGS) \ -c $(srcdir)/crtstuff.c -DCRT_END -o $(T)crtend$(objext) # On some systems we also want to install versions of these files diff --git a/gcc/configure b/gcc/configure index 9e7f889..1327881 100755 --- a/gcc/configure +++ b/gcc/configure @@ -2313,7 +2313,7 @@ for machine in $build $host $target; do i[3456]86-*-freebsd*) tm_file=i386/freebsd.h xm_file=i386/xm-freebsd.h - # On FreeBSD, the headers are already ok, except for math.h + # On FreeBSD, the headers are already ok, except for math.h. fixincludes=fixinc.math tmake_file=i386/t-freebsd ;; @@ -3897,6 +3897,9 @@ for machine in $build $host $target; do tm_file=sparc/sunos4.h tmake_file=sparc/t-sunos41 use_collect2=yes + if [ x$gas = xyes ]; then + tm_file="${tm_file} sparc/sun4gas.h" + fi ;; sparc-*-sunos3*) tm_file=sparc/sun4o3.h diff --git a/gcc/configure.in b/gcc/configure.in index 27d562b..7964949 100644 --- a/gcc/configure.in +++ b/gcc/configure.in @@ -2383,6 +2383,9 @@ for machine in $build $host $target; do tm_file=sparc/sunos4.h tmake_file=sparc/t-sunos41 use_collect2=yes + if [[ x$gas = xyes ]]; then + tm_file="${tm_file} sparc/sun4gas.h" + fi ;; sparc-*-sunos3*) tm_file=sparc/sun4o3.h diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 82cd0b6..4873ed6 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -36,6 +36,14 @@ Thu Sep 11 10:08:45 1997 Mark Mitchell <mmitchell@usa.net> (tsubst): Do constant folding as necessary to make sure that arguments passed to lookup_template_class really are constants. +Wed Sep 10 11:21:55 1997 Jason Merrill <jason@yorick.cygnus.com> + + * except.c (expand_builtin_throw): #ifndef DWARF2_UNWIND_INFO. + * decl2.c (finish_file): Only register exception tables if we + need to. + + * decl.c (init_decl_processing): Add __builtin_[fs]p. + Tue Sep 9 19:49:38 1997 Jason Merrill <jason@yorick.cygnus.com> * pt.c (unify): Just return 0 for a TYPENAME_TYPE. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 8527080..446befb 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -4725,7 +4725,7 @@ init_decl_processing () tree string_ftype_ptr_ptr, int_ftype_string_string; tree sizetype_endlink; tree ptr_ftype, ptr_ftype_unsigned, ptr_ftype_sizetype; - tree void_ftype, void_ftype_int, void_ftype_ptr; + tree void_ftype, void_ftype_int, void_ftype_ptr, ptr_ftype_void; /* Have to make these distinct before we try using them. */ lang_name_cplusplus = get_identifier ("C++"); @@ -5103,6 +5103,10 @@ init_decl_processing () builtin_function ("__builtin_frame_address", ptr_ftype_unsigned, BUILT_IN_FRAME_ADDRESS, NULL_PTR); + ptr_ftype_void = build_function_type (ptr_type_node, endlink); + builtin_function ("__builtin_fp", ptr_ftype_void, BUILT_IN_FP, NULL_PTR); + builtin_function ("__builtin_sp", ptr_ftype_void, BUILT_IN_SP, NULL_PTR); + builtin_function ("__builtin_alloca", ptr_ftype_sizetype, BUILT_IN_ALLOCA, "alloca"); builtin_function ("__builtin_ffs", int_ftype_int, BUILT_IN_FFS, NULL_PTR); diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index cc2834b..234b6f5 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -3196,6 +3196,7 @@ gen_sequence () (Now that we cache SEQUENCE expressions, it isn't worth special-casing the case of an empty list.) */ if (len == 1 + && ! RTX_FRAME_RELATED_P (first_insn) && (GET_CODE (first_insn) == INSN || GET_CODE (first_insn) == JUMP_INSN /* Don't discard the call usage field. */ diff --git a/gcc/except.c b/gcc/except.c index 1e77fd8..47cae20 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -87,34 +87,47 @@ Boston, MA 02111-1307, USA. */ exception region, and the address of the handler designated for that region. - At program startup each object file invokes a function named + If the target does not use the DWARF 2 frame unwind information, at + program startup each object file invokes a function named __register_exceptions with the address of its local - __EXCEPTION_TABLE__. __register_exceptions is defined in libgcc2.c, - and is responsible for recording all of the exception regions into - one list (which is kept in a static variable named exception_table_list). + __EXCEPTION_TABLE__. __register_exceptions is defined in libgcc2.c, and + is responsible for recording all of the exception regions into one list + (which is kept in a static variable named exception_table_list). + + On targets that support crtstuff.c, the unwind information + is stored in a section named .eh_frame and the information for the + entire shared object or program is registered with a call to + __register_frame. On other targets, the information for each + translation unit is registered separately with a static constructor. + __register_frame is defined in frame.c, and is responsible for + recording all of the unwind regions into one list (which is kept in a + static variable named unwind_table_list). The function __throw is actually responsible for doing the - throw. In the C++ frontend, __throw is generated on a + throw. On machines that have unwind info support, __throw is generated + by code in libgcc2.c, otherwise __throw is generated on a per-object-file basis for each source file compiled with - -fexceptions. Before __throw is invoked, the current context - of the throw needs to be placed in the global variable __eh_pc. + -fexceptions by the the C++ frontend. Before __throw is invoked, + the current context of the throw needs to be placed in the global + variable __eh_pc. __throw attempts to find the appropriate exception handler for the PC value stored in __eh_pc by calling __find_first_exception_table_match (which is defined in libgcc2.c). If __find_first_exception_table_match - finds a relevant handler, __throw jumps directly to it. - - If a handler for the context being thrown from can't be found, - __throw is responsible for unwinding the stack, determining the - address of the caller of the current function (which will be used - as the new context to throw from), and then restarting the process - of searching for a handler for the new context. __throw may also - call abort if it is unable to unwind the stack, and can also - call an external library function named __terminate if it reaches - the top of the stack without finding an appropriate handler. (By - default __terminate invokes abort, but this behavior can be - changed by the user to perform some sort of cleanup behavior before - exiting). + finds a relevant handler, __throw transfers control directly to it. + + If a handler for the context being thrown from can't be found, __throw + walks (see Walking the stack below) the stack up the dynamic call chain to + continue searching for an appropriate exception handler based upon the + caller of the function it last sought a exception handler for. It stops + then either an exception handler is found, or when the top of the + call chain is reached. + + If no handler is found, an external library function named + __terminate is called. If a handler is found, then we restart + our search for a handler at the end of the call chain, and repeat + the search process, but instead of just walking up the call chain, + we unwind the call chain as we walk up it. Internal implementation details: @@ -231,40 +244,79 @@ Boston, MA 02111-1307, USA. */ incorrect results is better than halting the program. - Unwinding the stack: + Walking the stack: - The details of unwinding the stack to the next frame can be rather - complex. While in many cases a generic __unwind_function routine - can be used by the generated exception handling code to do this, it - is often necessary to generate inline code to do the unwinding. + The stack is walked by starting with a pointer to the current + frame, and finding the pointer to the callers frame. The unwind info + tells __throw how to find it. - Whether or not these inlined unwinders are necessary is - target-specific. + Unwinding the stack: - By default, if the target-specific backend doesn't supply a - definition for __unwind_function, inlined unwinders will be used - instead. The main tradeoff here is in text space utilization. - Obviously, if inline unwinders have to be generated repeatedly, - this uses much more space than if a single routine is used. + When we use the term unwinding the stack, we mean undoing the + effects of the function prologue in a controlled fashion so that we + still have the flow of control. Otherwise, we could just return + (jump to the normal end of function epilogue). + + This is done in __throw in libgcc2.c when we know that a handler exists + in a frame higher up the call stack than its immediate caller. + + To unwind, we find the unwind data associated with the frame, if any. + If we don't find any, we call the library routine __terminate. If we do + find it, we use the information to copy the saved register values from + that frame into the register save area in the frame for __throw, return + into a stub which updates the stack pointer, and jump to the handler. + The normal function epilogue for __throw handles restoring the saved + values into registers. + + When unwinding, we use this method if we know it will + work (if DWARF2_UNWIND_INFO is defined). Otherwise, we know that + an inline unwinder will have been emitted for any function that + __unwind_function cannot unwind. The inline unwinder appears as a + normal exception handler for the entire function, for any function + that we know cannot be unwound by __unwind_function. We inform the + compiler of whether a function can be unwound with + __unwind_function by having DOESNT_NEED_UNWINDER evaluate to true + when the unwinder isn't needed. __unwind_function is used as an + action of last resort. If no other method can be used for + unwinding, __unwind_function is used. If it cannot unwind, it + should call __teminate. + + By default, if the target-specific backend doesn't supply a definition + for __unwind_function and doesn't support DWARF2_UNWIND_INFO, inlined + unwinders will be used instead. The main tradeoff here is in text space + utilization. Obviously, if inline unwinders have to be generated + repeatedly, this uses much more space than if a single routine is used. However, it is simply not possible on some platforms to write a generalized routine for doing stack unwinding without having some - form of additional data associated with each function. The current - implementation encodes this data in the form of additional machine - instructions. This is clearly not desirable, as it is extremely - inefficient. The next implementation will provide a set of metadata - for each function that will provide the needed information. + form of additional data associated with each function. The current + implementation can encode this data in the form of additional + machine instructions or as static data in tabular form. The later + is called the unwind data. - The backend macro DOESNT_NEED_UNWINDER is used to conditionalize - whether or not per-function unwinders are needed. If DOESNT_NEED_UNWINDER - is defined and has a non-zero value, a per-function unwinder is - not emitted for the current function. + The backend macro DOESNT_NEED_UNWINDER is used to conditionalize whether + or not per-function unwinders are needed. If DOESNT_NEED_UNWINDER is + defined and has a non-zero value, a per-function unwinder is not emitted + for the current function. If the static unwind data is supported, then + a per-function unwinder is not emitted. On some platforms it is possible that neither __unwind_function nor inlined unwinders are available. For these platforms it is not possible to throw through a function call, and abort will be invoked instead of performing the throw. + The reason the unwind data may be needed is that on some platforms + the order and types of data stored on the stack can vary depending + on the type of function, its arguments and returned values, and the + compilation options used (optimization versus non-optimization, + -fomit-frame-pointer, processor variations, etc). + + Unfortunately, this also means that throwing through functions that + aren't compiled with exception handling support will still not be + possible on some platforms. This problem is currently being + investigated, but no solutions have been found that do not imply + some unacceptable performance penalties. + Future directions: Currently __throw makes no differentiation between cleanups and @@ -309,28 +361,11 @@ Boston, MA 02111-1307, USA. */ query various state variables to determine what actions are to be performed next. - Another major problem that is being worked on is the issue with - stack unwinding on various platforms. Currently the only platform - that has support for __unwind_function is the Sparc; all other - ports require per-function unwinders, which causes large amounts of - code bloat. - - Ideally it would be possible to store a small set of metadata with - each function that would then make it possible to write a - __unwind_function for every platform. This would eliminate the - need for per-function unwinders. - - The main reason the data is needed is that on some platforms the - order and types of data stored on the stack can vary depending on - the type of function, its arguments and returned values, and the - compilation options used (optimization versus non-optimization, - -fomit-frame-pointer, processor variations, etc). - - Unfortunately, this also means that throwing through functions that - aren't compiled with exception handling support will still not be - possible on some platforms. This problem is currently being - investigated, but no solutions have been found that do not imply - some unacceptable performance penalties. + Another major problem that is being worked on is the issue with stack + unwinding on various platforms. Currently the only platforms that have + support for the generation of a generic unwinder are the SPARC and MIPS. + All other ports require per-function unwinders, which produce large + amounts of code bloat. For setjmp/longjmp based exception handling, some of the details are as above, but there are some additional details. This section @@ -354,6 +389,7 @@ Boston, MA 02111-1307, USA. */ #include "config.h" +#include "defaults.h" #include <stdio.h> #include "rtl.h" #include "tree.h" @@ -373,7 +409,11 @@ Boston, MA 02111-1307, USA. */ /* One to use setjmp/longjmp method of generating code for exception handling. */ +#if DWARF2_UNWIND_INFO +int exceptions_via_longjmp = 0; +#else int exceptions_via_longjmp = 1; +#endif /* One to enable asynchronous exception support. */ @@ -645,15 +685,12 @@ eh_outer_context (addr) { /* First mask out any unwanted bits. */ #ifdef MASK_RETURN_ADDR - expand_binop (Pmode, and_optab, addr, MASK_RETURN_ADDR, addr, - 1, OPTAB_LIB_WIDEN); + expand_and (addr, MASK_RETURN_ADDR, addr); #endif - /* Then subtract out enough to get into the appropriate region. If - this is defined, assume we don't need to subtract anything as it - is already within the correct region. */ -#if ! defined (RETURN_ADDR_OFFSET) - addr = plus_constant (addr, -1); + /* Then adjust to find the real return address. */ +#if defined (RETURN_ADDR_OFFSET) + addr = plus_constant (addr, RETURN_ADDR_OFFSET); #endif return addr; @@ -1107,7 +1144,10 @@ emit_throw () #ifdef JUMP_TO_THROW emit_indirect_jump (throw_libfunc); #else +#ifndef DWARF2_UNWIND_INFO + /* Prevent assemble_external from doing anything with this symbol. */ SYMBOL_REF_USED (throw_libfunc) = 1; +#endif emit_library_call (throw_libfunc, 0, VOIDmode, 0); #endif throw_used = 1; @@ -1446,10 +1486,8 @@ add_eh_table_entry (n) if (eh_table_max_size < 0) abort (); - if ((eh_table = (int *) realloc (eh_table, - eh_table_max_size * sizeof (int))) - == 0) - fatal ("virtual memory exhausted"); + eh_table = (int *) xrealloc (eh_table, + eh_table_max_size * sizeof (int)); } else { @@ -1475,6 +1513,18 @@ exception_table_p () return 0; } +/* 1 if we need a static constructor to register EH table info. */ + +int +register_exception_table_p () +{ +#if defined (DWARF2_UNWIND_INFO) + return 0; +#endif + + return exception_table_p (); +} + /* Output the entry of the exception table corresponding to to the exception region numbered N to file FILE. @@ -1512,7 +1562,7 @@ output_exception_table () int i; extern FILE *asm_out_file; - if (! doing_eh (0)) + if (! doing_eh (0) || ! eh_table) return; exception_section (); @@ -1521,11 +1571,6 @@ output_exception_table () assemble_align (GET_MODE_ALIGNMENT (ptr_mode)); assemble_label ("__EXCEPTION_TABLE__"); - assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - putc ('\n', asm_out_file); /* blank line */ - for (i = 0; i < eh_table_size; ++i) output_exception_table_entry (asm_out_file, eh_table[i]); @@ -1572,6 +1617,10 @@ start_eh_unwinder () if (exceptions_via_longjmp) return; +#ifdef DWARF2_UNWIND_INFO + return; +#endif + expand_eh_region_start (); } @@ -1598,6 +1647,10 @@ end_eh_unwinder () if (exceptions_via_longjmp) return; +#ifdef DWARF2_UNWIND_INFO + return; +#else /* DWARF2_UNWIND_INFO */ + assemble_external (eh_saved_pc); expr = make_node (RTL_EXPR); @@ -1658,6 +1711,7 @@ end_eh_unwinder () emit_barrier (); } #endif +#endif /* DWARF2_UNWIND_INFO */ } /* If necessary, emit insns for the per function unwinder for the @@ -2101,3 +2155,158 @@ exception_optimize () } } } + +/* Various hooks for the DWARF 2 __throw routine. */ + +/* Do any necessary initialization to access arbitrary stack frames. + On the SPARC, this means flushing the register windows. */ + +void +expand_builtin_unwind_init () +{ + /* Set this so all the registers get saved in our frame; we need to be + able to copy the saved values for any registers from frames we unwind. */ + current_function_has_nonlocal_label = 1; + +#ifdef SETUP_FRAME_ADDRESSES + SETUP_FRAME_ADDRESSES (); +#endif +} + +/* Given a value extracted from the return address register or stack slot, + return the actual address encoded in that value. */ + +rtx +expand_builtin_extract_return_addr (addr_tree) + tree addr_tree; +{ + rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); + return eh_outer_context (addr); +} + +/* Given an actual address in addr_tree, do any necessary encoding + and return the value to be stored in the return address register or + stack slot so the epilogue will return to that address. */ + +rtx +expand_builtin_frob_return_addr (addr_tree) + tree addr_tree; +{ + rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); +#ifdef RETURN_ADDR_OFFSET + addr = plus_constant (addr, -RETURN_ADDR_OFFSET); +#endif + return addr; +} + +/* Given an actual address in addr_tree, set the return address register up + so the epilogue will return to that address. If the return address is + not in a register, do nothing. */ + +void +expand_builtin_set_return_addr_reg (addr_tree) + tree addr_tree; +{ + rtx ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, + 0, hard_frame_pointer_rtx); + + if (GET_CODE (ra) != REG || REGNO (ra) >= FIRST_PSEUDO_REGISTER) + return; + + emit_move_insn (ra, expand_builtin_frob_return_addr (addr_tree)); +} + +/* Choose two registers for communication between the main body of + __throw and the stub for adjusting the stack pointer. The first register + is used to pass the address of the exception handler; the second register + is used to pass the stack pointer offset. + + For register 1 we use the return value register for a void *. + For register 2 we use the static chain register if it exists and is + different from register 1, otherwise some arbitrary call-clobbered + register. */ + +static void +eh_regs (r1, r2, outgoing) + rtx *r1, *r2; + int outgoing; +{ + rtx reg1, reg2; + +#ifdef FUNCTION_OUTGOING_VALUE + if (outgoing) + reg1 = FUNCTION_OUTGOING_VALUE (build_pointer_type (void_type_node), + current_function_decl); + else +#endif + reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node), + current_function_decl); + +#ifdef STATIC_CHAIN_REGNUM + if (outgoing) + reg2 = static_chain_incoming_rtx; + else + reg2 = static_chain_rtx; + if (REGNO (reg2) == REGNO (reg1)) +#endif /* STATIC_CHAIN_REGNUM */ + reg2 = NULL_RTX; + + if (reg2 == NULL_RTX) + { + int i; + for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (reg1)) + { + reg2 = gen_rtx (REG, Pmode, i); + break; + } + + if (reg2 == NULL_RTX) + abort (); + } + + *r1 = reg1; + *r2 = reg2; +} + +/* Emit inside of __throw a stub which adjusts the stack pointer and jumps + to the exception handler. __throw will set up the necessary values + and then return to the stub. */ + +rtx +expand_builtin_eh_stub () +{ + rtx stub_start = gen_label_rtx (); + rtx after_stub = gen_label_rtx (); + rtx handler, offset, temp; + + emit_jump (after_stub); + emit_label (stub_start); + + eh_regs (&handler, &offset, 0); + + adjust_stack (offset); + emit_indirect_jump (handler); + + emit_label (after_stub); + return gen_rtx (LABEL_REF, Pmode, stub_start); +} + +/* Set up the registers for passing the handler address and stack offset + to the stub above. */ + +void +expand_builtin_set_eh_regs (handler, offset) + tree handler, offset; +{ + rtx reg1, reg2; + + eh_regs (®1, ®2, 1); + + store_expr (offset, reg2, 0); + store_expr (handler, reg1, 0); + + /* These will be used by the stub. */ + emit_insn (gen_rtx (USE, VOIDmode, reg1)); + emit_insn (gen_rtx (USE, VOIDmode, reg2)); +} @@ -36,6 +36,7 @@ Boston, MA 02111-1307, USA. */ #include "recog.h" #include "output.h" #include "typeclass.h" +#include "defaults.h" #include "bytecode.h" #include "bc-opcode.h" @@ -7997,7 +7998,8 @@ expand_builtin_return_addr (fndecl_code, count, tem) arbitrary frames. For example, on the sparc, we must first flush all register windows to the stack. */ #ifdef SETUP_FRAME_ADDRESSES - SETUP_FRAME_ADDRESSES (); + if (count > 0) + SETUP_FRAME_ADDRESSES (); #endif /* On the sparc, the return address is not in the frame, it is in a @@ -9137,6 +9139,32 @@ expand_builtin (exp, target, subtarget, mode, ignore) return const0_rtx; } + /* Various hooks for the DWARF 2 __throw routine. */ + case BUILT_IN_UNWIND_INIT: + expand_builtin_unwind_init (); + return const0_rtx; + case BUILT_IN_FP: + return frame_pointer_rtx; + case BUILT_IN_SP: + return stack_pointer_rtx; +#ifdef DWARF2_UNWIND_INFO + case BUILT_IN_DWARF_FP_REGNUM: + return expand_builtin_dwarf_fp_regnum (); +#endif + case BUILT_IN_FROB_RETURN_ADDR: + return expand_builtin_frob_return_addr (TREE_VALUE (arglist)); + case BUILT_IN_EXTRACT_RETURN_ADDR: + return expand_builtin_extract_return_addr (TREE_VALUE (arglist)); + case BUILT_IN_SET_RETURN_ADDR_REG: + expand_builtin_set_return_addr_reg (TREE_VALUE (arglist)); + return const0_rtx; + case BUILT_IN_EH_STUB: + return expand_builtin_eh_stub (); + case BUILT_IN_SET_EH_REGS: + expand_builtin_set_eh_regs (TREE_VALUE (arglist), + TREE_VALUE (TREE_CHAIN (arglist))); + return const0_rtx; + default: /* just do library call, if unknown builtin */ error ("built-in function `%s' not currently supported", IDENTIFIER_POINTER (DECL_NAME (fndecl))); diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index 0e9252e..26a0e05 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -3107,11 +3107,9 @@ int _exit_dummy_decl = 0; /* prevent compiler & linker warnings */ #ifdef L_eh -#ifdef EH_TABLE_LOOKUP - -EH_TABLE_LOOKUP +/* Shared exception handling support routines. */ -#else +extern void *__eh_type; void __default_terminate () @@ -3127,14 +3125,31 @@ __terminate () (*__terminate_func)(); } +void * +__throw_type_match (void *catch_type, void *throw_type, void *obj) +{ +#if 0 + printf ("__throw_type_match (): catch_type = %s, throw_type = %s\n", + catch_type, throw_type); +#endif + if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0) + return obj; + return 0; +} + +void +__empty () +{ +} + +/* Support routines for setjmp/longjmp exception handling. */ + /* Calls to __sjthrow are generated by the compiler when an exception is raised when using the setjmp/longjmp exception handling codegen method. */ extern void longjmp (void *, int); -void *__eh_type; - static void *top_elt[2]; void **__dynamic_handler_chain = top_elt; @@ -3286,120 +3301,104 @@ __sjpopnthrow () __sjthrow (); } + +/* Support code for all exception region-based exception handling. */ + +/* This value identifies the place from which an exception is being + thrown. */ + +extern void *__eh_pc; + +#ifdef EH_TABLE_LOOKUP + +EH_TABLE_LOOKUP -typedef struct { +#else + +typedef struct exception_table { void *start; void *end; void *exception_handler; } exception_table; -struct exception_table_node { +/* This routine takes a PC and a pointer to the exception region TABLE for + its translation unit, and returns the address of the exception handler + associated with the closest exception table handler entry associated + with that PC, or 0 if there are no table entries the PC fits in. + + In the advent of a tie, we have to give the last entry, as it represents + an inner block. */ + +static void * +find_exception_handler (void *pc, exception_table *table) +{ + if (table) + { + int pos; + int best = 0; + + /* We can't do a binary search because the table isn't guaranteed + to be sorted from function to function. */ + for (pos = 0; table[pos].exception_handler != (void *) -1; ++pos) + { + if (table[pos].start <= pc && table[pos].end >= pc) + { + /* This can apply. Make sure it is at least as small as + the previous best. */ + if (best == 0 || (table[pos].end <= table[best].end + && table[pos].start >= table[best].start)) + best = pos; + } + /* But it is sorted by starting PC within a function. */ + else if (best && table[pos].start > pc) + break; + } + if (best != 0) + return table[best].exception_handler; + } + + return (void *) 0; +} +#endif /* EH_TABLE_LOOKUP */ + +#ifndef DWARF2_UNWIND_INFO +/* Support code for exception handling using inline unwinders or + __unwind_function. */ + +#ifndef EH_TABLE_LOOKUP +typedef struct exception_table_node { exception_table *table; void *start; void *end; struct exception_table_node *next; -}; +} exception_table_node; static struct exception_table_node *exception_table_list; -/* this routine takes a pc, and the address of the exception handler associated - with the closest exception table handler entry associated with that PC, - or 0 if there are no table entries the PC fits in. The algorithm works - something like this: - - while(current_entry exists) { - if(current_entry.start < pc ) - current_entry = next_entry; - else { - if(prev_entry.start <= pc && prev_entry.end > pc) { - save pointer to prev_entry; - return prev_entry.exception_handler; - } - else return 0; - } - } - return 0; - - Assuming a correctly sorted table (ascending order) this routine should - return the tightest match... - - In the advent of a tie, we have to give the last entry, as it represents - an inner block. */ - void * __find_first_exception_table_match (void *pc) { - register struct exception_table_node *tnp; - register exception_table *table; - int pos; - int best; - -#if 0 - printf ("find_first_exception_table_match (): pc = %x!\n", pc); -#endif + register exception_table_node *tnp; for (tnp = exception_table_list; tnp != 0; tnp = tnp->next) { - if (tnp->start > pc || tnp->end <= pc) - continue; - - table = tnp->table; - - pos = 0; - best = 0; -#if 0 - /* We can't do this yet, as we don't know that the table is sorted. */ - do { - ++pos; - if (table[pos].start > pc) - /* found the first table[pos].start > pc, so the previous - entry better be the one we want! */ - break; - } while (table[pos].exception_handler != (void *) -1); - - --pos; - if (table[pos].start <= pc && table[pos].end > pc) - { -#if 0 - printf ("find_first_eh_table_match (): found match: %x\n", table[pos].exception_handler); -#endif - return table[pos].exception_handler; - } -#else - while (table[++pos].exception_handler != (void *) -1) { - if (table[pos].start <= pc && table[pos].end > pc) - { - /* This can apply. Make sure it is better or as good as - the previous best. */ - /* The best one ends first. */ - if (best == 0 || (table[pos].end <= table[best].end - /* The best one starts last. */ - && table[pos].start >= table[best].start)) - best = pos; - } - } - if (best != 0) - return table[best].exception_handler; -#endif + if (tnp->start <= pc && tnp->end >= pc) + return find_exception_handler (pc, tnp->table); } -#if 0 - printf ("find_first_eh_table_match (): else: returning NULL!\n"); -#endif return (void *) 0; } void __register_exceptions (exception_table *table) { - struct exception_table_node *node; + exception_table_node *node; exception_table *range = table + 1; if (range->start == (void *) -1) return; - node = (struct exception_table_node *) - malloc (sizeof (struct exception_table_node)); + node = (exception_table_node *) malloc (sizeof (exception_table_node)); node->table = table; /* This look can be optimized away either if the table @@ -3417,19 +3416,7 @@ __register_exceptions (exception_table *table) node->next = exception_table_list; exception_table_list = node; } -#endif - -void * -__throw_type_match (void *catch_type, void *throw_type, void *obj) -{ -#if 0 - printf ("__throw_type_match (): catch_type = %s, throw_type = %s\n", - catch_type, throw_type); -#endif - if (strcmp ((const char *)catch_type, (const char *)throw_type) == 0) - return obj; - return 0; -} +#endif /* !EH_TABLE_LOOKUP */ /* Throw stub routine. @@ -3441,11 +3428,6 @@ __throw () abort (); } -/* This value identifies the place from which an exception is being - thrown. */ - -void *__eh_pc; - /* See expand_builtin_throw for details. */ void **__eh_pcnthrow () { @@ -3456,11 +3438,6 @@ void **__eh_pcnthrow () { return buf; } -void -__empty () -{ -} - #if #machine(i386) void __unwind_function(void *ptr) @@ -3539,6 +3516,267 @@ __unwind_function(void *ptr) abort (); } #endif /* powerpc */ + +#else /* DWARF2_UNWIND_INFO */ +/* Support code for exception handling using static unwind information. */ + +#include "frame.h" + +/* This type is used in get_reg and put_reg to deal with ABIs where a void* + is smaller than a word, such as the Irix 6 n32 ABI. We cast twice to + avoid a warning about casting between int and pointer of different + sizes. */ + +typedef int ptr_type __attribute__ ((mode (pointer))); + +/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a + frame called by UDATA or 0. */ + +static void* +get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata) +{ + if (udata->saved[reg] == REG_SAVED_OFFSET) + return (void *)(ptr_type) + *(word_type *)(udata->cfa + udata->reg_or_offset[reg]); + else if (udata->saved[reg] == REG_SAVED_REG && sub_udata) + return get_reg (udata->reg_or_offset[reg], sub_udata, 0); + else + abort (); +} + +/* Overwrite the saved value for register REG in frame UDATA with VAL. */ + +static void +put_reg (unsigned reg, void *val, frame_state *udata) +{ + if (udata->saved[reg] == REG_SAVED_OFFSET) + *(word_type *)(udata->cfa + udata->reg_or_offset[reg]) + = (word_type)(ptr_type) val; + else + abort (); +} + +/* Retrieve the return address for frame UDATA, where SUB_UDATA is a + frame called by UDATA or 0. */ + +static inline void * +get_return_addr (frame_state *udata, frame_state *sub_udata) +{ + return __builtin_extract_return_addr + (get_reg (udata->retaddr_column, udata, sub_udata)); +} + +/* Overwrite the return address for frame UDATA with VAL. */ + +static inline void +put_return_addr (void *val, frame_state *udata) +{ + val = __builtin_frob_return_addr (val); + put_reg (udata->retaddr_column, val, udata); +} + +/* Given the current frame UDATA and its return address PC, return the + information about the calling frame in CALLER_UDATA. */ + +static void * +next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata) +{ + caller_udata = __frame_state_for (pc, caller_udata); + if (! caller_udata) + return 0; + + /* Now go back to our caller's stack frame. If our caller's CFA register + was saved in our stack frame, restore it; otherwise, assume the CFA + register is SP and restore it to our CFA value. */ + if (udata->saved[caller_udata->cfa_reg]) + caller_udata->cfa = get_reg (caller_udata->cfa_reg, udata, 0); + else + caller_udata->cfa = udata->cfa; + caller_udata->cfa += caller_udata->cfa_offset; + + return caller_udata; +} + +#ifdef INCOMING_REGNO +/* Is the saved value for register REG in frame UDATA stored in a register + window in the previous frame? */ + +static int +in_reg_window (int reg, frame_state *udata) +{ + if (udata->saved[reg] != REG_SAVED_OFFSET) + return 0; + +#ifdef STACK_GROWS_DOWNWARD + return udata->reg_or_offset[reg] > 0; +#else + return udata->reg_or_offset[reg] < 0; +#endif +} +#endif /* INCOMING_REGNO */ + +/* We first search for an exception handler, and if we don't find + it, we call __terminate on the current stack frame so that we may + use the debugger to walk the stack and understand why no handler + was found. + + If we find one, then we unwind the frames down to the one that + has the handler and transfer control into the handler. */ + +void +__throw () +{ + void *pc, *handler, *retaddr; + frame_state ustruct, ustruct2; + frame_state *udata = &ustruct; + frame_state *sub_udata = &ustruct2; + frame_state my_ustruct, *my_udata = &my_ustruct; + long args_size; + + /* This is required for C++ semantics. We must call terminate if we + try and rethrow an exception, when there is no exception currently + active. */ + if (! __eh_type) + __terminate (); + + /* Start at our stack frame. */ +label: + udata = __frame_state_for (&&label, udata); + if (! udata) + __terminate (); + + /* We need to get the value from the CFA register. At this point in + compiling __throw we don't know whether or not we will use the frame + pointer register for the CFA, so we check our unwind info. */ + if (udata->cfa_reg == __builtin_dwarf_fp_regnum ()) + udata->cfa = __builtin_fp (); + else + udata->cfa = __builtin_sp (); + udata->cfa += udata->cfa_offset; + + memcpy (my_udata, udata, sizeof (*udata)); + + /* Do any necessary initialization to access arbitrary stack frames. + On the SPARC, this means flushing the register windows. */ + __builtin_unwind_init (); + + /* Now reset pc to the right throw point. */ + pc = __eh_pc; + + for (;;) + { + frame_state *p = udata; + udata = next_stack_level (pc, udata, sub_udata); + sub_udata = p; + + /* If we couldn't find the next frame, we lose. */ + if (! udata) + break; + + handler = find_exception_handler (pc, udata->eh_ptr); + + /* If we found one, we can stop searching. */ + if (handler) + { + args_size = udata->args_size; + break; + } + + /* Otherwise, we continue searching. */ + pc = get_return_addr (udata, sub_udata); + } + + /* If we haven't found a handler by now, this is an unhandled + exception. */ + if (! handler) + __terminate (); + + if (pc == __eh_pc) + /* We found a handler in the throw context, no need to unwind. */ + udata = my_udata; + else + { + int i; + void *val; + + /* Unwind all the frames between this one and the handler by copying + their saved register values into our register save slots. */ + + /* Remember the PC where we found the handler. */ + void *handler_pc = pc; + + /* Start from the throw context again. */ + pc = __eh_pc; + memcpy (udata, my_udata, sizeof (*udata)); + + while (pc != handler_pc) + { + frame_state *p = udata; + udata = next_stack_level (pc, udata, sub_udata); + sub_udata = p; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) + if (udata->saved[i]) + { +#ifdef INCOMING_REGNO + /* If you modify the saved value of the return address + register on the SPARC, you modify the return address for + your caller's frame. Don't do that here, as it will + confuse get_return_addr. */ + if (in_reg_window (i, udata) + && udata->saved[udata->retaddr_column] == REG_SAVED_REG + && udata->reg_or_offset[udata->retaddr_column] == i) + continue; +#endif + val = get_reg (i, udata, sub_udata); + put_reg (i, val, my_udata); + } + + pc = get_return_addr (udata, sub_udata); + } + +#ifdef INCOMING_REGNO + /* But we do need to update the saved return address register from + the last frame we unwind, or the handler frame will have the wrong + return address. */ + if (udata->saved[udata->retaddr_column] == REG_SAVED_REG) + { + i = udata->reg_or_offset[udata->retaddr_column]; + if (in_reg_window (i, udata)) + { + val = get_reg (i, udata, sub_udata); + put_reg (i, val, my_udata); + } + } +#endif + } + /* udata now refers to the frame called by the handler frame. */ + + /* Emit the stub to adjust sp and jump to the handler. */ + retaddr = __builtin_eh_stub (); + + /* And then set our return address to point to the stub. */ + if (my_udata->saved[my_udata->retaddr_column] == REG_SAVED_OFFSET) + put_return_addr (retaddr, my_udata); + else + __builtin_set_return_addr_reg (retaddr); + + /* Set up the registers we use to communicate with the stub. + We check STACK_GROWS_DOWNWARD so the stub can use adjust_stack. */ + __builtin_set_eh_regs (handler, +#ifdef STACK_GROWS_DOWNWARD + udata->cfa - my_udata->cfa +#else + my_udata->cfa - udata->cfa +#endif + + args_size + ); + + /* Epilogue: restore the handler frame's register values and return + to the stub. */ +} +#endif /* !DWARF2_UNWIND_INFO */ + #endif /* L_eh */ #ifdef L_pure |