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
* 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.
(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.
* 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.
# -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.
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
# 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
# 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'`; \
# 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
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
;;
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
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
(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.
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++");
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);
(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. */
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:
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
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
#include "config.h"
+#include "defaults.h"
#include <stdio.h>
#include "rtl.h"
#include "tree.h"
/* 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. */
{
/* 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;
#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;
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
{
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.
int i;
extern FILE *asm_out_file;
- if (! doing_eh (0))
+ if (! doing_eh (0) || ! eh_table)
return;
exception_section ();
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]);
if (exceptions_via_longjmp)
return;
+#ifdef DWARF2_UNWIND_INFO
+ return;
+#endif
+
expand_eh_region_start ();
}
if (exceptions_via_longjmp)
return;
+#ifdef DWARF2_UNWIND_INFO
+ return;
+#else /* DWARF2_UNWIND_INFO */
+
assemble_external (eh_saved_pc);
expr = make_node (RTL_EXPR);
emit_barrier ();
}
#endif
+#endif /* DWARF2_UNWIND_INFO */
}
/* If necessary, emit insns for the per function unwinder for the
}
}
}
+\f
+/* 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));
+}
#include "recog.h"
#include "output.h"
#include "typeclass.h"
+#include "defaults.h"
#include "bytecode.h"
#include "bc-opcode.h"
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
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)));
\f
#ifdef L_eh
-#ifdef EH_TABLE_LOOKUP
-
-EH_TABLE_LOOKUP
+/* Shared exception handling support routines. */
-#else
+extern void *__eh_type;
void
__default_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 ()
+{
+}
+\f
+/* 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;
__sjthrow ();
}
+\f
+/* 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 */
+\f
+#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
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.
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 () {
return buf;
}
-void
-__empty ()
-{
-}
-
#if #machine(i386)
void
__unwind_function(void *ptr)
abort ();
}
#endif /* powerpc */
+\f
+#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 */
\f
#ifdef L_pure