diff options
author | Richard Henderson <rth@redhat.com> | 2001-03-28 03:04:51 -0800 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2001-03-28 03:04:51 -0800 |
commit | 52a11cbfcf0cfb32628b6953588b6af4037ac0b6 (patch) | |
tree | a923c8785a06871784c5177530130063c4925f5a /gcc | |
parent | ce1c98ea459813570b4588427030daa03958fda6 (diff) | |
download | gcc-52a11cbfcf0cfb32628b6953588b6af4037ac0b6.zip gcc-52a11cbfcf0cfb32628b6953588b6af4037ac0b6.tar.gz gcc-52a11cbfcf0cfb32628b6953588b6af4037ac0b6.tar.bz2 |
IA-64 ABI Exception Handling.
From-SVN: r40924
Diffstat (limited to 'gcc')
65 files changed, 6444 insertions, 7287 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8994dd2..deb12a5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,117 @@ +2001-03-28 Richard Henderson <rth@redhat.com> + + * except.c: Rewrite entirely for IA-64 ABI exception handling. + * except.h: Likewise. + + * Makefile.in (LIB2ADDEH): Mention unwind-dw2*.c + (LIB2ADDEHDEP): New. + (LIB2FUNCS_EH): Remove. + (LIB2ADD): Remove LIB2ADDEH. + (libgcc.mk): Pass LIB2ADDEHDEP, don't pass LIB2FUNCS_EH. + (LIBGCC_DEPS): Use LIB2ADDEHDEP. + (crt{begin,end}[S].o): Likewise. + (except.o): Update includes. + * mklibgcc.in: Remove LIB2FUNCS_EH, add LIB2ADDEH, LIB2ADDEHDEP. + (libgcc2_c_dep): Use LIB2ADDEHDEP. + + * basic-block.h (struct basic_block_def): Remove eh_beg, eh_end. + * bb-reorder.c (reorder_basic_blocks): Don't disable for EH. + * builtins.def (BUILT_IN_EH_RETURN_DATA_REGNO): New. + * builtins.c (expand_builtin): Implement it. + [BUILT_IN_EH_RETURN]: Update for nr arguments change. + * c-common.c (c_common_nodes_and_builtins): Declare it. + * c-decl.c (init_decl_processing): Update __builtin_eh_return. + * calls.c (libfunc_nothrow): Remove. + (emit_library_call_value_1): Don't call it. + * crtstuff.c: Include unwind-dw2-fde.h instead of frame.h. + * dwarf2.h (dwarf_call_frame_info): Add dwarf2.1 elements. + (DW_EH_PE_*): New defines for pointer encoding in .eh_frame. + * dwarf2out.c (struct dw_fde_struct): Add uses_eh_lsda, funcdef_number. + (current_funcdef_number): Globalize. + (output_call_frame_info): Emit frame data if an lsda is needed. + Generate augmentation for personality routine. Don't play with + difference symbols. + (dwarf2out_begin_prologue): Record funcdef_number. + * dwarf2out.h (current_funcdef_number): Declare. + * expr.c (expand_expr): Update for except.h name changes. + Remove POPDCC_EXPR, POPDHC_EXPR. Add EXC_PTR_EXPR. + * expr.h (LTI_throw, LTI_rethrow): Remove. + (LTI_sjthrow, LTI_sjpopnthrow, LTI_terminate): Remove. + (LTI_eh_rtime_match): Remove. + (LTI_unwind_resume, LTI_eh_personality): Add. + (LTI_unwind_sjlj_register, LTI_unwind_sjlj_unregister): Add. + * final.c (final): Don't call check_exception_handler_labels, + init_insn_eh_region, or free_insn_eh_region. + (final_scan_insn): Always emit debug labels for + NOTE_INSN_EH_REGION notes. + * flags.h (flag_new_exceptions): Remove. + * flow.c (entry_exit_blocks): Remove eh_beg, eh_end. + (record_active_eh_regions): Remove. + (count_basic_blocks): Check all instructions for REG_EH_REGION. + Use can_throw_internal. + (find_basic_blocks_1): Likewise. + (move_stray_eh_region_notes): Remove. + (find_label_refs): No eh_return_stub_label. + (make_edges): Likewise. No init/free_eh_nesting_info. Handle RESX. + (make_eh_edge): No eh_nest_info. Update for reachable_handlers + changes. + (delete_unreachable_blocks): Don't track deleted handlers. + (flow_delete_block): Use maybe_remove_eh_handler. + (delete_eh_regions): Remove. + (merge_blocks): Don't check for eh region match. + (mark_regs_live_at_end): Handle EH_RETURN_DATA_REGNO, + EH_RETURN_STACKADJ_RTX, EH_RETURN_HANDLER_RTX. + (init_propagate_block_info): Disable dead frame store optimization + when current_function_calls_eh_return. + (dump_bb): Don't print eh_beg, eh_end. + * function.c (fixup_var_refs): No catch_clauses. + (expand_function_end): Likewise. Call expand_eh_return before + the return register use. Call sjlj_emit_function_exit_after. + (expand_function_start): Force pseudo DECL_RESULT if sjlj exceptions. + * function.h (struct function): Add calls_eh_return, uses_eh_lsda. + * ifcvt.c (dead_or_predicable): Remove eh region check. + * integrate.c (function_cannot_inline_p): Disallow __builtin_eh_return. + Don't check for EH vs parameters. + (expand_inline_function_eh_labelmap, eif_eh_map): Remove. + (expand_inline_function): Call duplicate_eh_regions. + (copy_insn_list): Don't handle NOTE_INSN_EH_REGION_BEG/END. + (copy_insn_notes): Remap REG_EH_REGION notes. + (copy_rtx_and_substitute): Remove SYMBOL_REF_NEED_ADJUST check. + * integrate.h (struct inline_remap): Add local_return_label. + * jump.c (jump_optimize_1): Don't init/free_insn_eh_region, nor + check_exception_handler_labels, nor exception_optimize. + (find_cross_jump): No EH region check. + * optabs.c (init_optabs): Update for changed eh libfuncs. + * rtl.def (RESX): New. + * rtl.h (SYMBOL_REF_NEED_ADJUST): Remove. + * stmt.c (expand_decl_cleanup): Simplify using_eh_for_cleanups_p + checks. Update for except.h name changes. + (expand_cleanups): Likewise. + (expand_dcc_cleanup, expand_dhc_cleanup): Remove. + * toplev.c (dump_file_index, dump_file): Add .02.eh dump. + (compile_file): Call init_eh before init_optabs. Don't + output_exception_table here. + (rest_of_compilation): Call convert_from_eh_region_ranges, + convert_to_eh_region_ranges, output_function_exception_table. + Don't emit_eh_context. + * tree.def (POPDHC_EXPR, POPDCC_EXPR): Remove. + (EXC_PTR_EXPR): New. + + * md.texi (eh_epilogue): Remove. + (eh_return): Document. + * tm.texi (EH_RETURN_DATA_REGNO): Document. + (EH_RETURN_STACKADJ_RTX, EH_RETURN_HANDLER_RTX): Document. + + * eh-common.h: Remove file. + * frame-dwarf2.c, frame.c, frame.h: Remove files. + * libgcc2.c (L_eh): Remove. + + * unwind-dw2-fde.c: New file, largely copied from frame.c. + * unwind-dw2-fde.h: New file. + * unwind-dw2.c: New file, largely cribbed from frame-dwarf2.c. + * unwind-sjlj.c, unwind.h, unwind.inc: New files. + * libgcc-std.ver: Update for eh symbols. + 2001-03-27 Richard Henderson <rth@redhat.com> * regmove.c (perhaps_ends_bb_p): Use can_throw_internal to diff --git a/gcc/Makefile.in b/gcc/Makefile.in index eaac739..5888dcb 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -397,8 +397,10 @@ LIBGCC2_INCLUDES = # Additional target-dependent options for compiling libgcc2.a. TARGET_LIBGCC2_CFLAGS = -# Additional sources to handle exceptions; overridden by some targets. -LIB2ADDEH = $(srcdir)/frame-dwarf2.c +# Additional sources to handle exceptions; overridden on ia64. +LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \ + $(srcdir)/unwind-sjlj.c +LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h # libgcc1-test target (must also be overridable for a target) LIBGCC1_TEST = libgcc1-test @@ -794,8 +796,6 @@ LIB2FUNCS = _muldi3 _divdi3 _moddi3 _udivdi3 _umoddi3 _negdi2 \ _mulvsi3 _mulvdi3 _negvsi2 _negvdi2 \ _ctors -LIB2FUNCS_EH = _eh - FPBIT_FUNCS = _pack_sf _unpack_sf _addsub_sf _mul_sf _div_sf \ _fpcmp_parts_sf _compare_sf _eq_sf _ne_sf _gt_sf _ge_sf \ _lt_sf _le_sf _unord_sf _si_to_sf _sf_to_si _negate_sf _make_sf \ @@ -1033,7 +1033,7 @@ xlimits.h: glimits.h limitx.h limity.h # # Build libgcc.a. -LIB2ADD = $(LIB2ADDEH) $(LIB2FUNCS_EXTRA) +LIB2ADD = $(LIB2FUNCS_EXTRA) libgcc.mk: config.status Makefile mklibgcc $(LIB2ADD) xgcc$(exeext) objext='$(objext)' \ @@ -1043,8 +1043,9 @@ libgcc.mk: config.status Makefile mklibgcc $(LIB2ADD) xgcc$(exeext) LIB1ASMFUNCS='$(LIB1ASMFUNCS)' \ LIB1FUNCS_EXTRA='$(LIB1FUNCS_EXTRA)' \ LIB2FUNCS='$(LIB2FUNCS)' \ - LIB2FUNCS_EH='$(LIB2FUNCS_EH)' \ LIB2ADD='$(LIB2ADD)' \ + LIB2ADDEH='$(LIB2ADDEH)' \ + LIB2ADDEHDEP='$(LIB2ADDEHDEP)' \ FPBIT='$(FPBIT)' \ FPBIT_FUNCS='$(FPBIT_FUNCS)' \ DPBIT='$(DPBIT)' \ @@ -1064,8 +1065,9 @@ libgcc.mk: config.status Makefile mklibgcc $(LIB2ADD) xgcc$(exeext) # All the things that might cause us to want to recompile bits of libgcc. LIBGCC_DEPS = $(GCC_PASSES) $(LANGUAGES) stmp-int-hdrs $(STMP_FIXPROTO) \ libgcc.mk $(srcdir)/libgcc1.c $(srcdir)/libgcc2.c $(TCONFIG_H) \ - $(MACHMODE_H) longlong.h frame.h gbl-ctors.h config.status \ - stmp-int-hdrs tsystem.h $(FPBIT) $(DPBIT) $(LIB2ADD) $(EXTRA_PARTS) + $(MACHMODE_H) longlong.h gbl-ctors.h config.status stmp-int-hdrs \ + tsystem.h $(FPBIT) $(DPBIT) $(LIB2ADD) $(LIB2ADDEH) $(LIB2ADDEHDEP) \ + $(EXTRA_PARTS) libgcc.a: $(LIBGCC_DEPS) $(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ @@ -1125,14 +1127,14 @@ stmp-multilib: $(LIBGCC_DEPS) # linked using GCC on systems using COFF or ELF, for the sake of C++ # constructors. $(T)crtbegin.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \ - frame.h gbl-ctors.h stmp-int-hdrs tsystem.h + gbl-ctors.h stmp-int-hdrs tsystem.h $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ -finhibit-size-directive -fno-inline-functions \ -fno-exceptions $(CRTSTUFF_T_CFLAGS) @inhibit_libc@ \ -c $(srcdir)/crtstuff.c -DCRT_BEGIN -o $(T)crtbegin$(objext) $(T)crtend.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \ - frame.h gbl-ctors.h stmp-int-hdrs tsystem.h + gbl-ctors.h stmp-int-hdrs tsystem.h $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ -finhibit-size-directive -fno-inline-functions \ -fno-exceptions $(CRTSTUFF_T_CFLAGS) @inhibit_libc@ \ @@ -1140,7 +1142,7 @@ $(T)crtend.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \ # These are versions of crtbegin and crtend for shared libraries. $(T)crtbeginS.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \ - frame.h gbl-ctors.h stmp-int-hdrs tsystem.h + gbl-ctors.h stmp-int-hdrs tsystem.h $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ -finhibit-size-directive -fno-inline-functions \ -fno-exceptions $(CRTSTUFF_T_CFLAGS_S) @inhibit_libc@ \ @@ -1148,7 +1150,7 @@ $(T)crtbeginS.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \ -o $(T)crtbeginS$(objext) $(T)crtendS.o: crtstuff.c $(GCC_PASSES) $(TCONFIG_H) auto-host.h \ - frame.h gbl-ctors.h stmp-int-hdrs tsystem.h + gbl-ctors.h stmp-int-hdrs tsystem.h $(GCC_FOR_TARGET) $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \ -finhibit-size-directive -fno-inline-functions \ -fno-exceptions $(CRTSTUFF_T_CFLAGS_S) @inhibit_libc@ \ @@ -1394,8 +1396,9 @@ stmt.o : stmt.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h function.h \ insn-config.h hard-reg-set.h $(EXPR_H) except.h \ $(LOOP_H) $(RECOG_H) toplev.h output.h varray.h $(GGC_H) $(TM_P_H) except.o : except.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \ - function.h $(EXPR_H) $(REGS_H) hard-reg-set.h \ - insn-config.h $(RECOG_H) output.h except.h toplev.h intl.h $(GGC_H) $(TM_P_H) + except.h function.h $(EXPR_H) integrate.h \ + insn-config.h hard-reg-set.h $(BASIC_BLOCK_H) output.h \ + dwarf2asm.h dwarf2out.h toplev.h $(HASHTAB_H) intl.h $(GGC_H) expr.o : expr.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h function.h \ $(REGS_H) $(EXPR_H) insn-config.h $(RECOG_H) \ output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h except.h \ @@ -1414,7 +1417,7 @@ explow.o : explow.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \ toplev.h function.h $(TM_P_H) optabs.o : optabs.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \ insn-config.h $(EXPR_H) $(RECOG_H) reload.h \ - toplev.h $(GGC_H) real.h $(TM_P_H) + toplev.h $(GGC_H) real.h $(TM_P_H) except.h dbxout.o : dbxout.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) flags.h $(REGS_H) \ insn-config.h reload.h gstab.h xcoffout.h output.h dbxout.h toplev.h \ $(TM_P_H) diff --git a/gcc/basic-block.h b/gcc/basic-block.h index eae8e29..58f6aa1 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -174,14 +174,12 @@ typedef struct basic_block_def { /* The index of this block. */ int index; - /* The loop depth of this block plus one. */ - int loop_depth; - /* The active eh region before head and after end. */ - int eh_beg, eh_end; + /* The loop depth of this block. */ + int loop_depth; - int count; /* Expected number of executions: calculated in - profile.c */ + /* Expected number of executions: calculated in profile.c. */ + int count; } *basic_block; /* Number of basic blocks in the current function. */ diff --git a/gcc/bb-reorder.c b/gcc/bb-reorder.c index 93b2034..e13e5f1 100644 --- a/gcc/bb-reorder.c +++ b/gcc/bb-reorder.c @@ -92,7 +92,6 @@ #include "flags.h" #include "output.h" #include "function.h" -#include "except.h" #include "toplev.h" #include "recog.h" #include "expr.h" @@ -395,8 +394,6 @@ make_reorder_chain_1 (bb, prev) taken = probability > REG_BR_PROB_BASE / 2; /* Find the normal taken edge and the normal fallthru edge. - Note that there may in fact be other edges due to - flag_non_call_exceptions. Note, conditional jumps with other side effects may not be fully optimized. In this case it is possible for @@ -1356,17 +1353,6 @@ reorder_basic_blocks () if (n_basic_blocks <= 1) return; - /* We do not currently handle correct re-placement of EH notes. - But that does not matter unless we intend to use them. */ - if (flag_exceptions) - for (i = 0; i < n_basic_blocks; i++) - { - edge e; - for (e = BASIC_BLOCK (i)->succ; e ; e = e->succ_next) - if (e->flags & EDGE_EH) - return; - } - for (i = 0; i < n_basic_blocks; i++) BASIC_BLOCK (i)->aux = xcalloc (1, sizeof (struct reorder_block_def)); diff --git a/gcc/builtins.c b/gcc/builtins.c index 88127ca..eecf1c5 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3605,9 +3605,12 @@ expand_builtin (exp, target, subtarget, mode, ignore) return expand_builtin_extract_return_addr (TREE_VALUE (arglist)); case BUILT_IN_EH_RETURN: expand_builtin_eh_return (TREE_VALUE (arglist), - TREE_VALUE (TREE_CHAIN (arglist)), - TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)))); + TREE_VALUE (TREE_CHAIN (arglist))); return const0_rtx; +#ifdef EH_RETURN_DATA_REGNO + case BUILT_IN_EH_RETURN_DATA_REGNO: + return expand_builtin_eh_return_data_regno (arglist); +#endif case BUILT_IN_VARARGS_START: return expand_builtin_va_start (0, arglist); case BUILT_IN_STDARG_START: diff --git a/gcc/builtins.def b/gcc/builtins.def index cd715d9..1d59d78 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -97,6 +97,7 @@ DEF_BUILTIN(BUILT_IN_INIT_DWARF_REG_SIZES) DEF_BUILTIN(BUILT_IN_FROB_RETURN_ADDR) DEF_BUILTIN(BUILT_IN_EXTRACT_RETURN_ADDR) DEF_BUILTIN(BUILT_IN_EH_RETURN) +DEF_BUILTIN(BUILT_IN_EH_RETURN_DATA_REGNO) DEF_BUILTIN(BUILT_IN_VARARGS_START) DEF_BUILTIN(BUILT_IN_STDARG_START) diff --git a/gcc/c-common.c b/gcc/c-common.c index 3823405..1b00362 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -3146,6 +3146,11 @@ c_common_nodes_and_builtins () builtin_function ("__builtin_frame_address", ptr_ftype_unsigned, BUILT_IN_FRAME_ADDRESS, BUILT_IN_NORMAL, NULL_PTR); +#ifdef EH_RETURN_DATA_REGNO + builtin_function ("__builtin_eh_return_data_regno", int_ftype_int, + BUILT_IN_EH_RETURN_DATA_REGNO, BUILT_IN_NORMAL, NULL_PTR); +#endif + builtin_function ("__builtin_alloca", ptr_ftype_sizetype, BUILT_IN_ALLOCA, BUILT_IN_NORMAL, "alloca"); builtin_function_2 ("__builtin_ffs", "ffs", diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 9410832..a8c1d30 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -3084,12 +3084,11 @@ init_decl_processing () builtin_function ("__builtin_eh_return", build_function_type (void_type_node, - tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, + type_for_mode (ptr_mode, 0), tree_cons (NULL_TREE, - type_for_mode (ptr_mode, 0), - tree_cons (NULL_TREE, - ptr_type_node, - endlink)))), + ptr_type_node, + endlink))), BUILT_IN_EH_RETURN, BUILT_IN_NORMAL, NULL_PTR); pedantic_lvalues = pedantic; diff --git a/gcc/calls.c b/gcc/calls.c index 3162b46..228d9b8 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -204,7 +204,6 @@ static void compute_argument_addresses PARAMS ((struct arg_data *, static rtx rtx_for_function_call PARAMS ((tree, tree)); static void load_register_parameters PARAMS ((struct arg_data *, int, rtx *, int)); -static int libfunc_nothrow PARAMS ((rtx)); static rtx emit_library_call_value_1 PARAMS ((int, rtx, rtx, enum libcall_type, enum machine_mode, @@ -3444,22 +3443,6 @@ expand_call (exp, target, ignore) return target; } -/* Returns nonzero if FUN is the symbol for a library function which can - not throw. */ - -static int -libfunc_nothrow (fun) - rtx fun; -{ - if (fun == throw_libfunc - || fun == rethrow_libfunc - || fun == sjthrow_libfunc - || fun == sjpopnthrow_libfunc) - return 0; - - return 1; -} - /* Output a library call to function FUN (a SYMBOL_REF rtx). The RETVAL parameter specifies whether return value needs to be saved, other parameters are documented in the emit_library_call function bellow. */ @@ -3501,7 +3484,7 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) rtx valreg; int pcc_struct_value = 0; int struct_value_size = 0; - int flags = 0; + int flags; int reg_parm_stack_space = 0; int needed; rtx before_call; @@ -3525,6 +3508,9 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) #endif #endif + /* No library functions can throw. */ + flags = ECF_NOTHROW; + if (fn_type == LCT_CONST_MAKE_BLOCK) flags |= ECF_CONST; else if (fn_type == LCT_PURE_MAKE_BLOCK) @@ -3533,9 +3519,6 @@ emit_library_call_value_1 (retval, orgfun, value, fn_type, outmode, nargs, p) flags |= ECF_NORETURN; fun = orgfun; - if (libfunc_nothrow (fun)) - flags |= ECF_NOTHROW; - #ifdef PREFERRED_STACK_BOUNDARY /* Ensure current function's preferred stack boundary is at least what we need. */ diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 9540023..0db7144 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,61 @@ 2001-03-28 Richard Henderson <rth@redhat.com> + IA-64 ABI Exception Handling: + * cp-tree.def (EH_SPEC_BLOCK): New. + (MUST_NOT_THROW_EXPR): New. + * cp-tree.h: Update changed function declarations. + (CPTI_PUSH_EXCEPTION_IDENTIFIER): Remove. + (CPTI_CALL_UNEXPECTED): New. + (struct cp_language_function): Rename x_eh_spec_try_block + to x_eh_spec_block. + (EH_SPEC_STMTS, EH_SPEC_RAISES): New. + * decl.c (current_binding_level): If no current function + bindings, revert to scope_chain. + (initialize_predefined_identifiers): Remove __cp_push_exception. + (store_parm_decls): Use begin_eh_spec_block. + (finish_function): Use finish_eh_spec_block. + (mark_lang_function): Update for name changes. + * decl2.c (finish_file): No mark_all_runtime_matches. + * dump.c (cp_dump_tree): Handle new tree codes. + * error.c (dump_expr) [BIND_EXPR]: Fix typo. + * except.c (catch_language_init, catch_language): Remove. + (init_exception_processing): Don't set language code. + Initialize call_unexpected_node, protect_cleanup_actions, + eh_personality_libfunc, lang_eh_runtime_type. + (call_eh_info, push_eh_info, get_eh_info, get_eh_value): Remove. + (get_eh_type, get_eh_caught, get_eh_handlers): Remove. + (prepare_eh_type): Split out type canonicalizations ... + (build_eh_type_type): ... from here. + (build_eh_type_type_ref): Remove. + (mark_all_runtime_matches): Remove. + (build_exc_ptr): New. + (do_begin_catch, do_end_catch): New. + (do_pop_exception): Remove. + (build_terminate_handler): Remove. + (choose_personality_routine): Split out language choice from ... + (initialize_handler_parm): ... here. + Use MUST_NOT_THROW_EXPR. + (expand_start_catch_block): Use do_begin_catch. Simplify Java + exception object handling. + (expand_start_eh_spec, expand_end_eh_spec): Remove. + (expand_exception_blocks, alloc_eh_object): Remove. + (begin_eh_spec_block, finish_eh_spec_block): New. + (do_allocate_exception, do_free_exception): New. + (expand_throw): Merge into ... + (build_throw): ... here. Update for abi. + * expr.c (cplus_expand_expr): No expand_internal_throw. + Handle MUST_NOT_THROW_EXPR. + * pt.c (tsubst_expr): Handle EH_SPEC_BLOCK. + * semantics.c (*) Update for except.h name changes. + (genrtl_try_block): No protect_with_terminate. + (genrtl_eh_spec_block): New. + (genrtl_handler): Don't emit the goto here. + (cp_expand_stmt): Handle EH_SPEC_BLOCK. + (genrtl_finish_function): Don't expand_exception_blocks. + * tree.c (cp_statement_code_p): Handle EH_SPEC_BLOCK. + +2001-03-28 Richard Henderson <rth@redhat.com> + * decl.c (struct named_label_list): Rename eh_region to in_try_scope, add in_catch_scope. (struct binding_level): Rename eh_region to is_try_scope, diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 3d514b5..5e89189 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -236,8 +236,13 @@ DEFTREECODE (START_CATCH_STMT, "start_catch_stmt", 'e', 0) DEFTREECODE (CTOR_INITIALIZER, "ctor_initializer", 'e', 2) DEFTREECODE (RETURN_INIT, "return_init", 'e', 2) DEFTREECODE (TRY_BLOCK, "try_block", 'e', 2) +DEFTREECODE (EH_SPEC_BLOCK, "eh_spec_block", 'e', 2) DEFTREECODE (HANDLER, "handler", 'e', 2) +/* A MUST_NOT_THROW_EXPR wraps an expression that may not + throw, and must call terminate if it does. */ +DEFTREECODE (MUST_NOT_THROW_EXPR, "must_not_throw_expr", 'e', 1) + DEFTREECODE (TAG_DEFN, "tag_defn", 'e', 0) /* And some codes for expressing conversions for overload resolution. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 91c280c..449a3bd 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -616,7 +616,6 @@ enum cp_tree_index CPTI_PFN_IDENTIFIER, CPTI_PFN_OR_DELTA2_IDENTIFIER, CPTI_VPTR_IDENTIFIER, - CPTI_PUSH_EXCEPTION_IDENTIFIER, CPTI_STD_IDENTIFIER, CPTI_LANG_NAME_C, @@ -627,6 +626,7 @@ enum cp_tree_index CPTI_NULL, CPTI_JCLASS, CPTI_TERMINATE, + CPTI_CALL_UNEXPECTED, CPTI_ATEXIT, CPTI_DSO_HANDLE, CPTI_DCAST, @@ -740,9 +740,6 @@ extern tree cp_global_trees[CPTI_MAX]; #define pfn_identifier cp_global_trees[CPTI_PFN_IDENTIFIER] #define pfn_or_delta2_identifier cp_global_trees[CPTI_PFN_OR_DELTA2_IDENTIFIER] #define vptr_identifier cp_global_trees[CPTI_VPTR_IDENTIFIER] -/* The name of the function to call to push an exception onto the - exception stack. */ -#define cp_push_exception_identifier cp_global_trees[CPTI_PUSH_EXCEPTION_IDENTIFIER] /* The name of the std namespace. */ #define std_identifier cp_global_trees[CPTI_STD_IDENTIFIER] #define lang_name_c cp_global_trees[CPTI_LANG_NAME_C] @@ -761,6 +758,9 @@ extern tree cp_global_trees[CPTI_MAX]; /* The declaration for `std::terminate'. */ #define terminate_node cp_global_trees[CPTI_TERMINATE] +/* The declaration for "__cxa_call_unexpected". */ +#define call_unexpected_node cp_global_trees[CPTI_CALL_UNEXPECTED] + /* A pointer to `std::atexit'. */ #define atexit_node cp_global_trees[CPTI_ATEXIT] @@ -872,7 +872,7 @@ struct cp_language_function tree x_dtor_label; tree x_current_class_ptr; tree x_current_class_ref; - tree x_eh_spec_try_block; + tree x_eh_spec_block; tree x_in_charge_parm; tree x_vtt_parm; @@ -916,10 +916,10 @@ struct cp_language_function #define current_class_ref \ (cfun ? cp_function_chain->x_current_class_ref : NULL_TREE) -/* The TRY_BLOCK for the exception-specifiers for the current +/* The EH_SPEC_BLOCK for the exception-specifiers for the current function, if any. */ -#define current_eh_spec_try_block cp_function_chain->x_eh_spec_try_block +#define current_eh_spec_block cp_function_chain->x_eh_spec_block /* The `__in_chrg' parameter for the current function. Only used for constructors and destructors. */ @@ -3035,6 +3035,9 @@ extern int flag_new_for_scope; #define TRY_STMTS(NODE) TREE_OPERAND (TRY_BLOCK_CHECK (NODE), 0) #define TRY_HANDLERS(NODE) TREE_OPERAND (TRY_BLOCK_CHECK (NODE), 1) +#define EH_SPEC_STMTS(NODE) TREE_OPERAND (EH_SPEC_BLOCK_CHECK (NODE), 0) +#define EH_SPEC_RAISES(NODE) TREE_OPERAND (EH_SPEC_BLOCK_CHECK (NODE), 1) + /* Nonzero if this try block is a function try block. */ #define FN_TRY_BLOCK_P(NODE) TREE_LANG_FLAG_3 (TRY_BLOCK_CHECK (NODE)) #define HANDLER_PARMS(NODE) TREE_OPERAND (HANDLER_CHECK (NODE), 0) @@ -4000,9 +4003,9 @@ extern void init_exception_processing PARAMS ((void)); extern tree expand_start_catch_block PARAMS ((tree)); extern void expand_end_catch_block PARAMS ((tree)); extern void expand_builtin_throw PARAMS ((void)); -extern tree expand_start_eh_spec PARAMS ((void)); -extern void expand_end_eh_spec PARAMS ((tree, tree)); +extern void expand_eh_spec_block PARAMS ((tree)); extern void expand_exception_blocks PARAMS ((void)); +extern tree build_exc_ptr PARAMS ((void)); extern tree build_throw PARAMS ((tree)); extern void mark_all_runtime_matches PARAMS ((void)); extern int nothrow_libfn_p PARAMS ((tree)); @@ -4256,6 +4259,8 @@ extern tree finish_case_label PARAMS ((tree, tree)); extern tree finish_goto_stmt PARAMS ((tree)); extern tree begin_try_block PARAMS ((void)); extern void finish_try_block PARAMS ((tree)); +extern tree begin_eh_spec_block PARAMS ((void)); +extern void finish_eh_spec_block PARAMS ((tree, tree)); extern void finish_handler_sequence PARAMS ((tree)); extern tree begin_function_try_block PARAMS ((void)); extern void finish_function_try_block PARAMS ((tree)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 366d37c..b96545d 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -478,7 +478,7 @@ struct binding_level /* The binding level currently in effect. */ #define current_binding_level \ - (cfun \ + (cfun && cp_function_chain->bindings \ ? cp_function_chain->bindings \ : scope_chain->bindings) @@ -6306,7 +6306,6 @@ initialize_predefined_identifiers () { VTABLE_PFN_NAME, &pfn_identifier, 0 }, { "__pfn_or_delta2", &pfn_or_delta2_identifier, 0 }, { "_vptr", &vptr_identifier, 0 }, - { "__cp_push_exception", &cp_push_exception_identifier, 0 }, { "__vtt_parm", &vtt_parm_identifier, 0 }, { "std", &std_identifier, 0 }, { NULL, NULL, 0 } @@ -13721,7 +13720,7 @@ store_parm_decls (current_function_parms) if (flag_exceptions && !processing_template_decl && flag_enforce_eh_specs && TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))) - current_eh_spec_try_block = expand_start_eh_spec (); + current_eh_spec_block = begin_eh_spec_block (); } @@ -13966,9 +13965,9 @@ finish_function (flags) if (flag_exceptions && !processing_template_decl && flag_enforce_eh_specs && TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))) - expand_end_eh_spec (TYPE_RAISES_EXCEPTIONS - (TREE_TYPE (current_function_decl)), - current_eh_spec_try_block); + finish_eh_spec_block (TYPE_RAISES_EXCEPTIONS + (TREE_TYPE (current_function_decl)), + current_eh_spec_block); } /* If we're saving up tree structure, tie off the function now. */ @@ -14395,7 +14394,7 @@ mark_lang_function (p) ggc_mark_tree (p->x_dtor_label); ggc_mark_tree (p->x_current_class_ptr); ggc_mark_tree (p->x_current_class_ref); - ggc_mark_tree (p->x_eh_spec_try_block); + ggc_mark_tree (p->x_eh_spec_block); ggc_mark_tree_varray (p->x_local_names); mark_named_label_lists (&p->x_named_labels, &p->x_named_label_uses); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index a8300b9..35c8ca7 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -3621,10 +3621,6 @@ finish_file () } } - /* Mark all functions that might deal with exception-handling as - referenced. */ - mark_all_runtime_matches (); - /* We lie to the back-end, pretending that some functions are not defined when they really are. This keeps these functions from being put out unnecessarily. But, we must stop lying diff --git a/gcc/cp/dump.c b/gcc/cp/dump.c index a0982a6..a4033c3 100644 --- a/gcc/cp/dump.c +++ b/gcc/cp/dump.c @@ -188,6 +188,13 @@ cp_dump_tree (di, t) dump_next_stmt (di, t); break; + case EH_SPEC_BLOCK: + dump_stmt (di, t); + dump_child ("body", EH_SPEC_STMTS (t)); + dump_child ("raises", EH_SPEC_RAISES (t)); + dump_next_stmt (di, t); + break; + case PTRMEM_CST: dump_child ("clas", PTRMEM_CST_CLASS (t)); dump_child ("mbr", PTRMEM_CST_MEMBER (t)); @@ -227,6 +234,12 @@ cp_dump_tree (di, t) dump_next_stmt (di, t); break; + case MUST_NOT_THROW_EXPR: + dump_stmt (di, t); + dump_child ("body", TREE_OPERAND (t, 0)); + dump_next_stmt (di, t); + break; + case SUBOBJECT: dump_stmt (di, t); dump_child ("clnp", TREE_OPERAND (t, 0)); diff --git a/gcc/cp/error.c b/gcc/cp/error.c index feb4736..7d2f1fa 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -2104,7 +2104,7 @@ dump_expr (t, flags) break; case BIND_EXPR: - output_add_character (scratch_buffer, '}'); + output_add_character (scratch_buffer, '{'); dump_expr (TREE_OPERAND (t, 1), flags & ~TFF_EXPR_IN_PARENS); output_add_character (scratch_buffer, '}'); break; diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 4f5bde6..600a21a 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -34,299 +34,67 @@ Boston, MA 02111-1307, USA. */ #include "output.h" #include "except.h" #include "toplev.h" -#include "eh-common.h" static void push_eh_cleanup PARAMS ((tree)); +static tree prepare_eh_type PARAMS ((tree)); static tree build_eh_type_type PARAMS ((tree)); -static tree call_eh_info PARAMS ((void)); -static void push_eh_info PARAMS ((void)); -static tree get_eh_info PARAMS ((void)); -static tree get_eh_value PARAMS ((void)); -#if 0 -static tree get_eh_type PARAMS ((void)); -static tree get_eh_caught PARAMS ((void)); -static tree get_eh_handlers PARAMS ((void)); -#endif +static tree do_begin_catch PARAMS ((void)); static int dtor_nothrow PARAMS ((tree)); -static tree do_pop_exception PARAMS ((tree)); -static tree build_eh_type_type_ref PARAMS ((tree)); -static tree build_terminate_handler PARAMS ((void)); -static tree alloc_eh_object PARAMS ((tree)); +static tree do_end_catch PARAMS ((tree)); +static void push_eh_cleanup PARAMS ((tree)); +static bool decl_is_java_type PARAMS ((tree decl, int err)); +static void choose_personality_routine PARAMS ((bool)); +static void initialize_handler_parm PARAMS ((tree, tree)); +static tree do_allocate_exception PARAMS ((tree)); +static tree do_free_exception PARAMS ((tree)); static int complete_ptr_ref_or_void_ptr_p PARAMS ((tree, tree)); static bool is_admissible_throw_operand PARAMS ((tree)); static int can_convert_eh PARAMS ((tree, tree)); static void check_handlers_1 PARAMS ((tree, tree)); -static void initialize_handler_parm PARAMS ((tree)); -static tree expand_throw PARAMS ((tree)); -static int decl_is_java_type PARAMS ((tree decl, int err)); #include "decl.h" #include "obstack.h" -/* In a given translation unit we are constrained to catch only C++ - types or only Java types. `catch_language' holds the current type, - and `catch_language_init' registers whether `catch_language' has - been set. */ - -static int catch_language_init = 0; -static int catch_language; - -/* ====================================================================== - Briefly the algorithm works like this: - - When a constructor or start of a try block is encountered, - push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a - new entry in the unwind protection stack and returns a label to - output to start the protection for that block. - - When a destructor or end try block is encountered, pop_eh_entry - (&eh_stack) is called. Pop_eh_entry () returns the eh_entry it - created when push_eh_entry () was called. The eh_entry structure - contains three things at this point. The start protect label, - the end protect label, and the exception handler label. The end - protect label should be output before the call to the destructor - (if any). If it was a destructor, then its parse tree is stored - in the finalization variable in the eh_entry structure. Otherwise - the finalization variable is set to NULL to reflect the fact that - it is the end of a try block. Next, this modified eh_entry node - is enqueued in the finalizations queue by calling - enqueue_eh_entry (&queue,entry). - - +---------------------------------------------------------------+ - |XXX: Will need modification to deal with partially | - | constructed arrays of objects | - | | - | Basically, this consists of keeping track of how many | - | of the objects have been constructed already (this | - | should be in a register though, so that shouldn't be a | - | problem. | - +---------------------------------------------------------------+ - - When a catch block is encountered, there is a lot of work to be - done. - - Since we don't want to generate the catch block inline with the - regular flow of the function, we need to have some way of doing - so. Luckily, we can use sequences to defer the catch sections. - When the start of a catch block is encountered, we start the - sequence. After the catch block is generated, we end the - sequence. - - Next we must insure that when the catch block is executed, all - finalizations for the matching try block have been completed. If - any of those finalizations throw an exception, we must call - terminate according to the ARM (section r.15.6.1). What this - means is that we need to dequeue and emit finalizations for each - entry in the eh_queue until we get to an entry with a NULL - finalization field. For any of the finalization entries, if it - is not a call to terminate (), we must protect it by giving it - another start label, end label, and exception handler label, - setting its finalization tree to be a call to terminate (), and - enqueue'ing this new eh_entry to be output at an outer level. - Finally, after all that is done, we can get around to outputting - the catch block which basically wraps all the "catch (...) {...}" - statements in a big if/then/else construct that matches the - correct block to call. - - ===================================================================== */ - -/* ====================================================================== */ - -/* sets up all the global eh stuff that needs to be initialized at the +/* Sets up all the global eh stuff that needs to be initialized at the start of compilation. */ void init_exception_processing () { - /* void vtype () */ - tree vtype = build_function_type (void_type_node, void_list_node); - + tree tmp; + if (flag_honor_std) push_namespace (std_identifier); - terminate_node = build_cp_library_fn_ptr ("terminate", vtype); + + /* void std::terminate (); */ + tmp = build_function_type (void_type_node, void_list_node); + terminate_node = build_cp_library_fn_ptr ("terminate", tmp); TREE_THIS_VOLATILE (terminate_node) = 1; TREE_NOTHROW (terminate_node) = 1; if (flag_honor_std) pop_namespace (); - set_exception_lang_code (EH_LANG_C_plus_plus); - set_exception_version_code (1); - - /* If we use setjmp/longjmp EH, arrange for all cleanup actions to - be protected with __terminate. */ - protect_cleanup_actions_with_terminate = 1; -} - -/* Retrieve a pointer to the cp_eh_info node for the current exception. */ - -static tree -call_eh_info () -{ - tree fn; - - fn = get_identifier ("__start_cp_handler"); - if (IDENTIFIER_GLOBAL_VALUE (fn)) - fn = IDENTIFIER_GLOBAL_VALUE (fn); - else - { - tree eh_info_type; - tree cleanup_fn_type; - tree matcher_fn_type; - tree cp_eh_info_type; - tree exception_desc_type; - tree fields[8]; - - eh_info_type = make_aggr_type (RECORD_TYPE); - exception_desc_type = make_aggr_type (RECORD_TYPE); - - /* void * (*) (__eh_info *, void *, exception_descriptor *); */ - matcher_fn_type = tree_cons - (NULL_TREE, build_pointer_type (eh_info_type), tree_cons - (NULL_TREE, ptr_type_node, tree_cons - (NULL_TREE, build_pointer_type (exception_desc_type), - void_list_node))); - matcher_fn_type = build_function_type (ptr_type_node, matcher_fn_type); - matcher_fn_type = build_pointer_type (matcher_fn_type); - - /* void (*) (void *); */ - cleanup_fn_type = tree_cons - (NULL_TREE, ptr_type_node, void_list_node); - cleanup_fn_type = build_function_type (void_type_node, cleanup_fn_type); - cleanup_fn_type = build_pointer_type (cleanup_fn_type); - - /* eh-common.h - struct __eh_info - { - __eh_matcher match_function; - short language; - short version; - }; */ - fields[0] = build_decl (FIELD_DECL, - get_identifier ("match_function"), ptr_type_node); - fields[1] = build_decl (FIELD_DECL, - get_identifier ("language"), short_integer_type_node); - fields[2] = build_decl (FIELD_DECL, - get_identifier ("version"), short_integer_type_node); - /* N.B.: The fourth field LEN is expected to be - the number of fields - 1, not the total number of fields. */ - finish_builtin_type (eh_info_type, "__eh_info", fields, 2, ptr_type_node); - - /* exception_support.h - struct cp_eh_info - { - __eh_info eh_info; - void *value; - void *type; - cleanup_fn cleanup; - bool caught; - cp_eh_info *next; - long handlers; - void *original_value; - }; */ - cp_eh_info_type = make_aggr_type (RECORD_TYPE); - fields[0] = build_decl (FIELD_DECL, get_identifier ("eh_info"), - eh_info_type); - fields[1] = build_decl (FIELD_DECL, get_identifier ("value"), - ptr_type_node); - fields[2] = build_decl (FIELD_DECL, get_identifier ("type"), - ptr_type_node); - fields[3] = build_decl (FIELD_DECL, get_identifier ("cleanup"), - cleanup_fn_type); - fields[4] = build_decl (FIELD_DECL, get_identifier ("caught"), - boolean_type_node); - fields[5] = build_decl (FIELD_DECL, get_identifier ("next"), - build_pointer_type (cp_eh_info_type)); - fields[6] = build_decl (FIELD_DECL, get_identifier ("handlers"), - long_integer_type_node); - fields[7] = build_decl (FIELD_DECL, get_identifier ("original_value"), - ptr_type_node); - /* N.B.: The fourth field LEN is expected to be - the number of fields - 1, not the total number of fields. */ - finish_builtin_type (cp_eh_info_type, "cp_eh_info", fields, 7, ptr_type_node); - - /* And now the function. */ - fn = push_library_fn (fn, - build_function_type (build_pointer_type (cp_eh_info_type), - void_list_node)); - } - return build_function_call (fn, NULL_TREE); -} - -/* Retrieve a pointer to the cp_eh_info node for the current exception - and save it in the current binding level. */ - -static void -push_eh_info () -{ - tree decl, fn = call_eh_info (); - - /* Remember the pointer to the current exception info; it won't change - during this catch block. */ - decl = build_decl (VAR_DECL, get_identifier ("__exception_info"), - TREE_TYPE (fn)); - DECL_ARTIFICIAL (decl) = 1; - DECL_INITIAL (decl) = fn; - decl = pushdecl (decl); - cp_finish_decl (decl, fn, NULL_TREE, 0); -} - -/* Returns a reference to the cp_eh_info node for the current exception. */ - -static tree -get_eh_info () -{ - /* Look for the pointer pushed in push_eh_info. */ - tree t = lookup_name (get_identifier ("__exception_info"), 0); - return build_indirect_ref (t, NULL_PTR); -} - -/* Returns a reference to the current exception object. */ + protect_cleanup_actions = build_call (terminate_node, NULL_TREE); -static tree -get_eh_value () -{ - return build_component_ref (get_eh_info (), get_identifier ("value"), - NULL_TREE, 0); -} + /* void __cxa_call_unexpected(void *); */ + tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + tmp = build_function_type (void_type_node, tmp); + call_unexpected_node + = push_throw_library_fn (get_identifier ("__cxa_call_unexpected"), tmp); -/* Returns a reference to the current exception type. */ + eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS + ? "__gxx_personality_sj0" + : "__gxx_personality_v0"); -#if 0 -static tree -get_eh_type () -{ - return build_component_ref (get_eh_info (), get_identifier ("type"), - NULL_TREE, 0); + lang_eh_runtime_type = build_eh_type_type; } -/* Returns a reference to whether or not the current exception - has been caught. */ - static tree -get_eh_caught () -{ - return build_component_ref (get_eh_info (), get_identifier ("caught"), - NULL_TREE, 0); -} - -/* Returns a reference to whether or not the current exception - has been caught. */ - -static tree -get_eh_handlers () -{ - return build_component_ref (get_eh_info (), get_identifier ("handlers"), - NULL_TREE, 0); -} -#endif - -/* Build a type value for use at runtime for a type that is matched - against by the exception handling system. */ - -static tree -build_eh_type_type (type) +prepare_eh_type (type) tree type; { + if (type == NULL_TREE) + return type; if (type == error_mark_node) return error_mark_node; @@ -337,14 +105,14 @@ build_eh_type_type (type) /* Peel off cv qualifiers. */ type = TYPE_MAIN_VARIANT (type); - return build1 (ADDR_EXPR, ptr_type_node, get_typeid_1 (type)); + return type; } /* Build the address of a typeinfo decl for use in the runtime - matching field of the new exception model */ + matching field of the exception model. */ static tree -build_eh_type_type_ref (type) +build_eh_type_type (type) tree type; { tree exp; @@ -352,47 +120,43 @@ build_eh_type_type_ref (type) if (type == NULL_TREE || type == error_mark_node) return type; - /* peel back references, so they match. */ - if (TREE_CODE (type) == REFERENCE_TYPE) - type = TREE_TYPE (type); - - /* Peel off cv qualifiers. */ - type = TYPE_MAIN_VARIANT (type); + if (decl_is_java_type (type, 0)) + exp = build_java_class_ref (TREE_TYPE (type)); + else + exp = get_tinfo_decl (type); - exp = get_tinfo_decl (type); mark_used (exp); exp = build1 (ADDR_EXPR, ptr_type_node, exp); - return (exp); + return exp; +} + +tree +build_exc_ptr () +{ + return build (EXC_PTR_EXPR, ptr_type_node); } -/* This routine is called to mark all the symbols representing runtime - type functions in the exception table as having been referenced. - This will make sure code is emitted for them. Called from finish_file. */ +/* Build up a call to __cxa_begin_catch, to tell the runtime that the + exception has been handled. */ -void -mark_all_runtime_matches () +static tree +do_begin_catch () { - int x,num; - void **ptr; - tree exp; - - num = find_all_handler_type_matches (&ptr); - if (num == 0 || ptr == NULL) - return; - - for (x=0; x <num; x++) + tree fn; + + fn = get_identifier ("__cxa_begin_catch"); + if (IDENTIFIER_GLOBAL_VALUE (fn)) + fn = IDENTIFIER_GLOBAL_VALUE (fn); + else { - exp = (tree) ptr[x]; - if (TREE_CODE (exp) == ADDR_EXPR) - { - exp = TREE_OPERAND (exp, 0); - if (TREE_CODE (exp) == FUNCTION_DECL) - TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (exp)) = 1; - } + /* Declare void* __cxa_begin_catch (void *). */ + tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp)); } - - free (ptr); + + return build_function_call (fn, tree_cons (NULL_TREE, build_exc_ptr (), + NULL_TREE)); } /* Returns nonzero if cleaning up an exception of type TYPE (which can be @@ -415,32 +179,29 @@ dtor_nothrow (type) return TREE_NOTHROW (fn); } -/* Build up a call to __cp_pop_exception, to destroy the exception object +/* Build up a call to __cxa_end_catch, to destroy the exception object for the current catch block if no others are currently using it. */ static tree -do_pop_exception (type) +do_end_catch (type) tree type; { tree fn, cleanup; - fn = get_identifier ("__cp_pop_exception"); + + fn = get_identifier ("__cxa_end_catch"); if (IDENTIFIER_GLOBAL_VALUE (fn)) fn = IDENTIFIER_GLOBAL_VALUE (fn); else { - /* Declare void __cp_pop_exception (void *), - as defined in exception.cc. */ - fn = push_void_library_fn - (fn, tree_cons (NULL_TREE, ptr_type_node, void_list_node)); + /* Declare void __cxa_end_catch (). */ + fn = push_void_library_fn (fn, void_list_node); /* This can throw if the destructor for the exception throws. */ TREE_NOTHROW (fn) = 0; } - /* Arrange to do a dynamically scoped cleanup upon exit from this region. */ - cleanup = lookup_name (get_identifier ("__exception_info"), 0); - cleanup = build_function_call (fn, tree_cons - (NULL_TREE, cleanup, NULL_TREE)); + cleanup = build_function_call (fn, NULL_TREE); TREE_NOTHROW (cleanup) = dtor_nothrow (type); + return cleanup; } @@ -450,29 +211,20 @@ static void push_eh_cleanup (type) tree type; { - finish_decl_cleanup (NULL_TREE, do_pop_exception (type)); -} - -/* Build up a call to terminate on the function obstack, for use as an - exception handler. */ - -static tree -build_terminate_handler () -{ - return build_function_call (terminate_node, NULL_TREE); + finish_decl_cleanup (NULL_TREE, do_end_catch (type)); } /* Return nonzero value if DECL is a Java type suitable for catch or throw. */ -static int +static bool decl_is_java_type (decl, err) tree decl; int err; { - int r = (TREE_CODE (decl) == POINTER_TYPE - && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE - && TYPE_FOR_JAVA (TREE_TYPE (decl))); + bool r = (TREE_CODE (decl) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE + && TYPE_FOR_JAVA (TREE_TYPE (decl))); if (err) { @@ -508,71 +260,74 @@ decl_is_java_type (decl, err) return r; } +static void +choose_personality_routine (is_java) + bool is_java; +{ + static enum { + chose_none, + chose_cpp, + chose_java, + gave_error + } state; + + switch (state) + { + case chose_none: + /* We defaulted to C++ in init_exception_processing. + Reconfigure for Java if we changed our minds. */ + if (is_java) + eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS + ? "__gcj_personality_sj0" + : "__gcj_personality_v0"); + state = (is_java ? chose_java : chose_cpp); + break; + + case chose_cpp: + case chose_java: + if (state != (is_java ? chose_java : chose_cpp)) + { + error ("mixing C++ and Java catches in a single translation unit"); + state = gave_error; + } + break; + + case gave_error: + break; + } +} + /* Initialize the catch parameter DECL. */ static void -initialize_handler_parm (decl) +initialize_handler_parm (decl, exp) tree decl; + tree exp; { - tree exp; tree init; tree init_type; - int lang; /* Make sure we mark the catch param as used, otherwise we'll get a warning about an unused ((anonymous)). */ TREE_USED (decl) = 1; - /* Figure out the type that the initializer is. */ + /* Figure out the type that the initializer is. Pointers are returned + adjusted by value from __cxa_begin_catch. Others are returned by + reference. */ init_type = TREE_TYPE (decl); - if (TREE_CODE (init_type) != REFERENCE_TYPE - && TREE_CODE (init_type) != POINTER_TYPE) + if (TREE_CODE (init_type) != POINTER_TYPE + && TREE_CODE (init_type) != REFERENCE_TYPE) init_type = build_reference_type (init_type); - if (decl_is_java_type (init_type, 0)) - { - tree fn - = builtin_function ("_Jv_exception_info", - build_function_type (ptr_type_node, - tree_cons (NULL_TREE, - void_type_node, - NULL_TREE)), - 0, NOT_BUILT_IN, NULL_PTR); - - exp = build (CALL_EXPR, ptr_type_node, - build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), - fn), - NULL_TREE, NULL_TREE); - TREE_SIDE_EFFECTS (exp) = 1; - lang = EH_LANG_Java; - - set_exception_lang_code (EH_LANG_Java); - set_exception_version_code (1); - } - else - { - exp = get_eh_value (); - lang = EH_LANG_C_plus_plus; - } - - if (catch_language_init) - { - if (lang != catch_language) - error ("mixing C++ and Java `catch'es in single translation unit"); - } - else - { - catch_language_init = 1; - catch_language = lang; - } + choose_personality_routine (decl_is_java_type (init_type, 0)); /* Since pointers are passed by value, initialize a reference to - pointer catch parm with the address of the value slot. */ + pointer catch parm with the address of the temporary. */ if (TREE_CODE (init_type) == REFERENCE_TYPE && TREE_CODE (TREE_TYPE (init_type)) == POINTER_TYPE) exp = build_unary_op (ADDR_EXPR, exp, 1); - exp = ocp_convert (init_type , exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0); + exp = ocp_convert (init_type, exp, CONV_IMPLICIT|CONV_FORCE_TEMP, 0); init = convert_from_reference (exp); @@ -584,8 +339,7 @@ initialize_handler_parm (decl) See also expand_default_init. */ init = ocp_convert (TREE_TYPE (decl), init, CONV_IMPLICIT|CONV_FORCE_TEMP, 0); - init = build (TRY_CATCH_EXPR, TREE_TYPE (init), init, - build_terminate_handler ()); + init = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (init), init); } /* Let `cp_finish_decl' know that this initializer is ok. */ @@ -605,6 +359,9 @@ expand_start_catch_block (decl) { tree compound_stmt_1; tree compound_stmt_2; + tree exp = NULL_TREE; + tree type; + bool is_java; if (! doing_eh (1)) return NULL_TREE; @@ -618,34 +375,54 @@ expand_start_catch_block (decl) compound_stmt_1 = begin_compound_stmt (/*has_no_scope=*/0); note_level_for_catch (); - if (! decl || ! decl_is_java_type (TREE_TYPE (decl), 1)) + if (decl) + type = prepare_eh_type (TREE_TYPE (decl)); + else + type = NULL_TREE; + begin_catch_block (type); + + is_java = false; + if (decl) { - /* The ordinary C++ case. */ - tree type; + tree init; - if (decl) - type = TREE_TYPE (decl); + if (decl_is_java_type (type, 1)) + { + /* Java only passes object via pointer and doesn't require + adjusting. The java object is immediately before the + generic exception header. */ + init = build_exc_ptr (); + init = build1 (NOP_EXPR, build_pointer_type (type), init); + init = build (MINUS_EXPR, TREE_TYPE (init), init, + TYPE_SIZE_UNIT (TREE_TYPE (init))); + init = build_indirect_ref (init, NULL_PTR); + is_java = true; + } else - type = NULL_TREE; - begin_catch_block (build_eh_type_type_ref (type)); - - push_eh_info (); - push_eh_cleanup (type); + { + /* C++ requires that we call __cxa_begin_catch to get the + pointer to the actual object. */ + init = do_begin_catch (); + } + + exp = create_temporary_var (ptr_type_node); + DECL_REGISTER (exp) = 1; + cp_finish_decl (exp, init, NULL_TREE, LOOKUP_ONLYCONVERTING); + finish_expr_stmt (build_modify_expr (exp, INIT_EXPR, init)); } else - { - /* The Java case. In this case, the match_info is a pointer to - the Java class object. We assume that the class is a - compiled class. */ - tree ref = build_java_class_ref (TREE_TYPE (TREE_TYPE (decl))); - begin_catch_block (build1 (ADDR_EXPR, jclass_node, ref)); - } + finish_expr_stmt (do_begin_catch ()); + + /* C++ requires that we call __cxa_end_catch at the end of + processing the exception. */ + if (! is_java) + push_eh_cleanup (type); /* Create a binding level for the parm. */ compound_stmt_2 = begin_compound_stmt (/*has_no_scope=*/0); if (decl) - initialize_handler_parm (decl); + initialize_handler_parm (decl, exp); return build_tree_list (compound_stmt_1, compound_stmt_2); } @@ -678,204 +455,151 @@ expand_end_catch_block (blocks) finish_compound_stmt (/*has_no_scope=*/0, compound_stmt_1); } -/* An exception spec is implemented more or less like: - - try { - function body; - } catch (...) { - void *p[] = { typeid(raises) }; - __check_eh_spec (p, count); - } - - __check_eh_spec in exception.cc handles all the details. */ - tree -expand_start_eh_spec () +begin_eh_spec_block () { - return begin_try_block (); + tree r = build_stmt (EH_SPEC_BLOCK, NULL_TREE, NULL_TREE); + add_stmt (r); + return r; } void -expand_end_eh_spec (raises, try_block) - tree raises; - tree try_block; +finish_eh_spec_block (raw_raises, eh_spec_block) + tree raw_raises; + tree eh_spec_block; { - tree tmp, fn, decl, types = NULL_TREE; - tree blocks; - tree handler; - int count = 0; - - finish_try_block (try_block); - handler = begin_handler (); - blocks = finish_handler_parms (NULL_TREE, handler); - - if (TREE_VALUE (raises) == NULL_TREE) - { - fn = get_identifier ("__check_null_eh_spec"); - if (IDENTIFIER_GLOBAL_VALUE (fn)) - fn = IDENTIFIER_GLOBAL_VALUE (fn); - else - { - tmp = build_function_type (void_type_node, void_list_node); - fn = push_throw_library_fn (fn, tmp); - /* Since the spec doesn't allow any exceptions, this call will - never throw. We use push_throw_library_fn because we do want - TREE_THIS_VOLATILE to be set. */ - TREE_NOTHROW (fn) = 1; - } - tmp = NULL_TREE; - } - else - { - /* Build up an array of type_infos. */ - for (; raises && TREE_VALUE (raises); raises = TREE_CHAIN (raises)) - { - types = tree_cons - (NULL_TREE, build_eh_type_type (TREE_VALUE (raises)), types); - ++count; - } - - types = build_nt (CONSTRUCTOR, NULL_TREE, types); - TREE_HAS_CONSTRUCTOR (types) = 1; - - /* We can't pass the CONSTRUCTOR directly, so stick it in a variable. */ - tmp = build_cplus_array_type (const_ptr_type_node, NULL_TREE); - decl = build_decl (VAR_DECL, NULL_TREE, tmp); - DECL_ARTIFICIAL (decl) = 1; - DECL_INITIAL (decl) = types; - DECL_CONTEXT (decl) = current_function_decl; - cp_finish_decl (decl, types, NULL_TREE, 0); - - decl = decay_conversion (decl); - - fn = get_identifier ("__check_eh_spec"); - if (IDENTIFIER_GLOBAL_VALUE (fn)) - fn = IDENTIFIER_GLOBAL_VALUE (fn); - else - { - tmp = tree_cons - (NULL_TREE, integer_type_node, tree_cons - (NULL_TREE, TREE_TYPE (decl), void_list_node)); - tmp = build_function_type (void_type_node, tmp); - - fn = push_throw_library_fn (fn, tmp); - } + tree raises; - tmp = tree_cons (NULL_TREE, build_int_2 (count, 0), - tree_cons (NULL_TREE, decl, NULL_TREE)); - } + RECHAIN_STMTS (eh_spec_block, EH_SPEC_STMTS (eh_spec_block)); - tmp = build_call (fn, tmp); - finish_expr_stmt (tmp); + /* Strip cv quals, etc, from the specification types. */ + for (raises = NULL_TREE; + raw_raises && TREE_VALUE (raw_raises); + raw_raises = TREE_CHAIN (raw_raises)) + raises = tree_cons (NULL_TREE, prepare_eh_type (TREE_VALUE (raw_raises)), + raises); - finish_handler (blocks, handler); - finish_handler_sequence (try_block); + EH_SPEC_RAISES (eh_spec_block) = raises; } -/* This is called to expand all the toplevel exception handling - finalization for a function. It should only be called once per - function. */ +/* Return a pointer to a buffer for an exception object of type TYPE. */ -void -expand_exception_blocks () +static tree +do_allocate_exception (type) + tree type; { - do_pending_stack_adjust (); + tree fn; - if (catch_clauses) + fn = get_identifier ("__cxa_allocate_exception"); + if (IDENTIFIER_GLOBAL_VALUE (fn)) + fn = IDENTIFIER_GLOBAL_VALUE (fn); + else { - rtx funcend = gen_label_rtx (); - emit_jump (funcend); - - /* We cannot protect n regions this way if we must flow into the - EH region through the top of the region, as we have to with - the setjmp/longjmp approach. */ - if (USING_SJLJ_EXCEPTIONS == 0) - expand_eh_region_start (); - - emit_insns (catch_clauses); - catch_clauses = catch_clauses_last = NULL_RTX; - - if (USING_SJLJ_EXCEPTIONS == 0) - expand_eh_region_end (build_terminate_handler ()); - - emit_insns (catch_clauses); - catch_clauses = catch_clauses_last = NULL_RTX; - emit_label (funcend); + /* Declare void *__cxa_allocate_exception(size_t). */ + tree tmp = tree_cons (NULL_TREE, c_size_type_node, void_list_node); + fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp)); } + + return build_function_call (fn, tree_cons (NULL_TREE, size_in_bytes (type), + NULL_TREE)); } -/* Return a pointer to a buffer for an exception object of type TYPE. */ +/* Call __cxa_free_exception from a cleanup. This is invoked when + a constructor for a thrown object throws. */ static tree -alloc_eh_object (type) - tree type; +do_free_exception (ptr) + tree ptr; { - tree fn, exp; + tree fn; - fn = get_identifier ("__eh_alloc"); + fn = get_identifier ("__cxa_free_exception"); if (IDENTIFIER_GLOBAL_VALUE (fn)) fn = IDENTIFIER_GLOBAL_VALUE (fn); else { - /* Declare __eh_alloc (size_t), as defined in exception.cc. */ - tree tmp = tree_cons (NULL_TREE, sizetype, void_list_node); - fn = push_library_fn (fn, build_function_type (ptr_type_node, tmp)); + /* Declare void __cxa_free_exception (void *). */ + fn = push_void_library_fn (fn, tree_cons (NULL_TREE, ptr_type_node, + void_list_node)); } - exp = build_function_call (fn, tree_cons - (NULL_TREE, size_in_bytes (type), NULL_TREE)); - exp = build1 (NOP_EXPR, build_pointer_type (type), exp); - return exp; + return build_function_call (fn, tree_cons (NULL_TREE, ptr, NULL_TREE)); } -/* Expand a throw statement. This follows the following - algorithm: - - 1. Allocate space to save the current PC onto the stack. - 2. Generate and emit a label and save its address into the - newly allocated stack space since we can't save the pc directly. - 3. If this is the first call to throw in this function: - generate a label for the throw block - 4. jump to the throw block label. */ +/* Build a throw expression. */ -static tree -expand_throw (exp) +tree +build_throw (exp) tree exp; { tree fn; + if (exp == error_mark_node) + return exp; + + if (processing_template_decl) + return build_min (THROW_EXPR, void_type_node, exp); + + if (exp == null_node) + cp_warning ("throwing NULL, which has integral, not pointer type"); + + if (exp != NULL_TREE) + { + if (!is_admissible_throw_operand (exp)) + return error_mark_node; + } + if (! doing_eh (1)) return error_mark_node; - if (exp - && decl_is_java_type (TREE_TYPE (exp), 1)) + if (exp && decl_is_java_type (TREE_TYPE (exp), 1)) { - /* A Java `throw' statement. */ - tree args = tree_cons (NULL_TREE, exp, NULL); - - fn = get_identifier (USING_SJLJ_EXCEPTIONS - ? "_Jv_Sjlj_Throw" - : "_Jv_Throw"); + tree fn = get_identifier ("_Jv_Throw"); if (IDENTIFIER_GLOBAL_VALUE (fn)) fn = IDENTIFIER_GLOBAL_VALUE (fn); else { - /* Declare _Jv_Throw (void *), as defined in Java's - exception.cc. */ + /* Declare void _Jv_Throw (void *). */ tree tmp = tree_cons (NULL_TREE, ptr_type_node, void_list_node); tmp = build_function_type (ptr_type_node, tmp); fn = push_throw_library_fn (fn, tmp); } - exp = build_function_call (fn, args); + exp = build_function_call (fn, tree_cons (NULL_TREE, exp, NULL_TREE)); } else if (exp) { tree throw_type; - tree cleanup = NULL_TREE, e; + tree cleanup; tree stmt_expr; tree compound_stmt; tree try_block; + tree object, ptr; + tree tmp; + + fn = get_identifier ("__cxa_throw"); + if (IDENTIFIER_GLOBAL_VALUE (fn)) + fn = IDENTIFIER_GLOBAL_VALUE (fn); + else + { + /* The CLEANUP_TYPE is the internal type of a destructor. */ + if (cleanup_type == NULL_TREE) + { + tmp = void_list_node; + tmp = tree_cons (NULL_TREE, ptr_type_node, tmp); + tmp = build_function_type (void_type_node, tmp); + cleanup_type = build_pointer_type (tmp); + } + + /* Declare void __cxa_throw (void*, void*, void (*)(void*)). */ + /* ??? Second argument is supposed to be "std::type_info*". */ + tmp = void_list_node; + tmp = tree_cons (NULL_TREE, cleanup_type, tmp); + tmp = tree_cons (NULL_TREE, ptr_type_node, tmp); + tmp = tree_cons (NULL_TREE, ptr_type_node, tmp); + tmp = build_function_type (void_type_node, tmp); + fn = push_throw_library_fn (fn, tmp); + } begin_init_stmts (&stmt_expr, &compound_stmt); @@ -883,161 +607,103 @@ expand_throw (exp) /* First, decay it. */ exp = decay_conversion (exp); - /* The CLEANUP_TYPE is the internal type of a destructor. Under - the old ABI, destructors are two-argument functions; under - the new ABI they take only one argument. */ - if (cleanup_type == NULL_TREE) + /* OK, this is kind of wacky. The standard says that we call + terminate when the exception handling mechanism, after + completing evaluation of the expression to be thrown but + before the exception is caught (_except.throw_), calls a + user function that exits via an uncaught exception. + + So we have to protect the actual initialization of the + exception object with terminate(), but evaluate the + expression first. Since there could be temps in the + expression, we need to handle that, too. We also expand + the call to __cxa_allocate_exception first (which doesn't + matter, since it can't throw). */ + + my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926); + + /* Store the throw expression into a temp. This can be less + efficient than storing it into the allocated space directly, but + if we allocated the space first we would have to deal with + cleaning it up if evaluating this expression throws. */ + if (TREE_SIDE_EFFECTS (exp)) { - tree arg_types; - - arg_types = void_list_node; - arg_types = tree_cons (NULL_TREE, ptr_type_node, arg_types); - cleanup_type = (build_pointer_type - (build_function_type (void_type_node, arg_types))); + tmp = create_temporary_var (TREE_TYPE (exp)); + DECL_INITIAL (tmp) = exp; + cp_finish_decl (tmp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING); + exp = tmp; } - if (TYPE_PTR_P (TREE_TYPE (exp))) - throw_type = build_eh_type_type (TREE_TYPE (exp)); - else - { - tree object, ptr; - - /* OK, this is kind of wacky. The standard says that we call - terminate when the exception handling mechanism, after - completing evaluation of the expression to be thrown but - before the exception is caught (_except.throw_), calls a - user function that exits via an uncaught exception. - - So we have to protect the actual initialization of the - exception object with terminate(), but evaluate the - expression first. Since there could be temps in the - expression, we need to handle that, too. We also expand - the call to __eh_alloc first (which doesn't matter, since - it can't throw). */ - - my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926); - - /* Store the throw expression into a temp. This can be less - efficient than storing it into the allocated space directly, but - if we allocated the space first we would have to deal with - cleaning it up if evaluating this expression throws. */ - if (TREE_SIDE_EFFECTS (exp)) - { - tree temp = create_temporary_var (TREE_TYPE (exp)); - DECL_INITIAL (temp) = exp; - cp_finish_decl (temp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING); - exp = temp; - } + /* Allocate the space for the exception. */ + ptr = create_temporary_var (ptr_type_node); + DECL_REGISTER (ptr) = 1; + cp_finish_decl (ptr, NULL_TREE, NULL_TREE, LOOKUP_ONLYCONVERTING); + tmp = do_allocate_exception (TREE_TYPE (exp)); + tmp = build_modify_expr (ptr, INIT_EXPR, tmp); + finish_expr_stmt (tmp); - /* Allocate the space for the exception. */ - ptr = save_expr (alloc_eh_object (TREE_TYPE (exp))); - finish_expr_stmt (ptr); + object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr); + object = build_indirect_ref (object, NULL_PTR); - try_block = begin_try_block (); - object = build_indirect_ref (ptr, NULL_PTR); - exp = build_modify_expr (object, INIT_EXPR, exp); + try_block = begin_try_block (); - if (exp == error_mark_node) - error (" in thrown expression"); + exp = build_modify_expr (object, INIT_EXPR, exp); + if (exp == error_mark_node) + error (" in thrown expression"); - finish_expr_stmt (exp); - finish_cleanup_try_block (try_block); - finish_cleanup (build_terminate_handler (), try_block); + finish_expr_stmt (exp); + finish_cleanup_try_block (try_block); + finish_cleanup (do_free_exception (ptr), try_block); - throw_type = build_eh_type_type (TREE_TYPE (object)); - - if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object))) - { - cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)), - complete_dtor_identifier, - 0); - cleanup = TREE_VALUE (cleanup); - mark_used (cleanup); - mark_addressable (cleanup); - /* Pretend it's a normal function. */ - cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup); - } + throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object))); - exp = ptr; + if (TYPE_HAS_DESTRUCTOR (TREE_TYPE (object))) + { + cleanup = lookup_fnfields (TYPE_BINFO (TREE_TYPE (object)), + complete_dtor_identifier, 0); + cleanup = TREE_VALUE (cleanup); + mark_used (cleanup); + mark_addressable (cleanup); + /* Pretend it's a normal function. */ + cleanup = build1 (ADDR_EXPR, cleanup_type, cleanup); } - - /* Cast EXP to `void *' so that it will match the prototype for - __cp_push_exception. */ - exp = convert (ptr_type_node, exp); - - if (cleanup == NULL_TREE) + else { cleanup = build_int_2 (0, 0); TREE_TYPE (cleanup) = cleanup_type; } - fn = cp_push_exception_identifier; - if (IDENTIFIER_GLOBAL_VALUE (fn)) - fn = IDENTIFIER_GLOBAL_VALUE (fn); - else - { - /* Declare __cp_push_exception (void*, void*, void (*)(void*, int)), - as defined in exception.cc. */ - tree tmp; - tmp = tree_cons - (NULL_TREE, ptr_type_node, tree_cons - (NULL_TREE, ptr_type_node, tree_cons - (NULL_TREE, cleanup_type, void_list_node))); - fn = push_void_library_fn (fn, tmp); - } + tmp = tree_cons (NULL_TREE, cleanup, NULL_TREE); + tmp = tree_cons (NULL_TREE, throw_type, tmp); + tmp = tree_cons (NULL_TREE, ptr, tmp); + tmp = build_function_call (fn, tmp); + + /* ??? Indicate that this function call throws throw_type. */ - e = tree_cons (NULL_TREE, exp, tree_cons - (NULL_TREE, throw_type, tree_cons - (NULL_TREE, cleanup, NULL_TREE))); - finish_expr_stmt (build_function_call (fn, e)); + finish_expr_stmt (tmp); exp = finish_init_stmts (stmt_expr, compound_stmt); } else { - /* rethrow current exception; note that it's no longer caught. */ + /* Rethrow current exception. */ - tree fn = get_identifier ("__uncatch_exception"); + tree fn = get_identifier ("__cxa_rethrow"); if (IDENTIFIER_GLOBAL_VALUE (fn)) fn = IDENTIFIER_GLOBAL_VALUE (fn); else - /* Declare void __uncatch_exception (void) - as defined in exception.cc. */ - fn = push_void_library_fn (fn, void_list_node); + { + /* Declare void __cxa_rethrow (void). */ + fn = push_throw_library_fn + (fn, build_function_type (void_type_node, void_list_node)); + } exp = build_function_call (fn, NULL_TREE); } - return exp; -} + exp = build1 (THROW_EXPR, void_type_node, exp); -/* Build a throw expression. */ - -tree -build_throw (e) - tree e; -{ - if (e == error_mark_node) - return e; - - if (processing_template_decl) - return build_min (THROW_EXPR, void_type_node, e); - - if (e == null_node) - cp_warning ("throwing NULL, which has integral, not pointer type"); - - if (e != NULL_TREE) - { - if (!is_admissible_throw_operand (e)) - return error_mark_node; - } - - e = expand_throw (e); - e = build1 (THROW_EXPR, void_type_node, e); - TREE_SIDE_EFFECTS (e) = 1; - TREE_USED (e) = 1; - - return e; + return exp; } /* Make sure TYPE is complete, pointer to complete, reference to diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index fc22460..9a9eb86 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -88,6 +88,7 @@ cplus_expand_expr (exp, target, tmode, modifier) tree type = TREE_TYPE (exp); register enum machine_mode mode = TYPE_MODE (type); register enum tree_code code = TREE_CODE (exp); + rtx ret; /* No sense saving up arithmetic to be done if it's all in the wrong mode to form part of an address. @@ -103,16 +104,19 @@ cplus_expand_expr (exp, target, tmode, modifier) target, tmode, modifier); case OFFSET_REF: - { - return expand_expr (default_conversion (resolve_offset_ref (exp)), - target, tmode, EXPAND_NORMAL); - } + return expand_expr (default_conversion (resolve_offset_ref (exp)), + target, tmode, EXPAND_NORMAL); case THROW_EXPR: expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode, 0); - expand_internal_throw (); return NULL; + case MUST_NOT_THROW_EXPR: + expand_eh_region_start (); + ret = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier); + expand_eh_region_end_must_not_throw (build_call (terminate_node, 0)); + return ret; + case EMPTY_CLASS_EXPR: /* We don't need to generate any code for an empty class. */ return const0_rtx; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index a070002..6990e2c 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -51,6 +51,7 @@ static tree simplify_aggr_init_exprs_r PARAMS ((tree *, int *, void *)); static void deferred_type_access_control PARAMS ((void)); static void emit_associated_thunks PARAMS ((tree)); static void genrtl_try_block PARAMS ((tree)); +static void genrtl_eh_spec_block PARAMS ((tree)); static void genrtl_handler PARAMS ((tree)); static void genrtl_catch_block PARAMS ((tree)); static void genrtl_ctor_stmt PARAMS ((tree)); @@ -575,14 +576,14 @@ genrtl_try_block (t) { expand_eh_region_start (); expand_stmt (TRY_STMTS (t)); - expand_eh_region_end (protect_with_terminate (TRY_HANDLERS (t))); + expand_eh_region_end_cleanup (TRY_HANDLERS (t)); } else { if (!FN_TRY_BLOCK_P (t)) emit_line_note (input_filename, lineno); - expand_start_try_stmts (); + expand_eh_region_start (); expand_stmt (TRY_STMTS (t)); if (FN_TRY_BLOCK_P (t)) @@ -603,6 +604,21 @@ genrtl_try_block (t) } } +/* Generate the RTL for T, which is an EH_SPEC_BLOCK. */ + +static void +genrtl_eh_spec_block (t) + tree t; +{ + expand_eh_region_start (); + expand_stmt (EH_SPEC_STMTS (t)); + expand_eh_region_end_allowed (EH_SPEC_RAISES (t), + build_call (call_unexpected_node, + tree_cons (NULL_TREE, + build_exc_ptr (), + NULL_TREE))); +} + /* Begin a try-block. Returns a newly-created TRY_BLOCK if appropriate. */ @@ -706,13 +722,7 @@ genrtl_handler (t) genrtl_do_pushlevel (); expand_stmt (HANDLER_BODY (t)); if (!processing_template_decl) - { - /* Fall to outside the try statement when done executing - handler and we fall off end of handler. This is jump - Lresume in the documentation. */ - expand_goto (top_label_entry (&caught_return_label_stack)); - end_catch_handler (); - } + expand_end_catch (); } /* Begin a handler. Returns a HANDLER if appropriate. */ @@ -757,13 +767,13 @@ finish_handler_parms (decl, handler) return blocks; } -/* Generate the RTL for a CATCH_BLOCK. */ +/* Generate the RTL for a START_CATCH_STMT. */ static void genrtl_catch_block (type) tree type; { - start_catch_handler (type); + expand_start_catch (type); } /* Note the beginning of a handler for TYPE. This function is called @@ -2209,6 +2219,10 @@ cp_expand_stmt (t) genrtl_try_block (t); break; + case EH_SPEC_BLOCK: + genrtl_eh_spec_block (t); + break; + case HANDLER: genrtl_handler (t); break; @@ -2615,9 +2629,6 @@ genrtl_finish_function (fn) && ! DECL_NAME (DECL_RESULT (current_function_decl))) no_return_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - if (flag_exceptions) - expand_exception_blocks (); - /* If this function is supposed to return a value, ensure that we do not fall into the cleanups by mistake. The end of our function will look like this: diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 3023e8f..5f67840 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -1037,6 +1037,7 @@ cp_statement_code_p (code) case RETURN_INIT: case TRY_BLOCK: case HANDLER: + case EH_SPEC_BLOCK: return 1; default: diff --git a/gcc/crtstuff.c b/gcc/crtstuff.c index fc747a9..2e18e5a 100644 --- a/gcc/crtstuff.c +++ b/gcc/crtstuff.c @@ -60,15 +60,15 @@ Boston, MA 02111-1307, USA. */ #include "auto-host.h" #include "tconfig.h" #include "tsystem.h" -#include "frame.h" +#include "unwind-dw2-fde.h" #ifndef CRT_CALL_STATIC_FUNCTION # define CRT_CALL_STATIC_FUNCTION(func) func () #endif /* We do not want to add the weak attribute to the declarations of these - routines in frame.h because that will cause the definition of these - symbols to be weak as well. + routines in unwind-dw2-fde.h because that will cause the definition of + these symbols to be weak as well. This exposes a core issue, how to handle creating weak references vs how to create weak definitions. Either we have to have the definition diff --git a/gcc/dwarf2.h b/gcc/dwarf2.h index 5b60828..e6148a3 100644 --- a/gcc/dwarf2.h +++ b/gcc/dwarf2.h @@ -501,9 +501,14 @@ enum dwarf_call_frame_info DW_CFA_def_cfa_offset = 0x0e, DW_CFA_def_cfa_expression = 0x0f, DW_CFA_expression = 0x10, + /* Dwarf 2.1 */ + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + /* SGI/MIPS specific */ DW_CFA_MIPS_advance_loc8 = 0x1d, - + /* GNU extensions */ DW_CFA_GNU_window_save = 0x2d, DW_CFA_GNU_args_size = 0x2e, @@ -554,3 +559,26 @@ enum dwarf_macinfo_record_type DW_MACINFO_end_file = 4, DW_MACINFO_vendor_ext = 255 }; + + +/* @@@ For use with GNU frame unwind information. */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 + +#define DW_EH_PE_indirect 0x80 diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 68f4b3f..6f8d335 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -44,6 +44,7 @@ Boston, MA 02111-1307, USA. */ #include "regs.h" #include "insn-config.h" #include "reload.h" +#include "function.h" #include "output.h" #include "expr.h" #include "except.h" @@ -150,7 +151,9 @@ typedef struct dw_fde_struct const char *dw_fde_current_label; const char *dw_fde_end; dw_cfi_ref dw_fde_cfi; - int nothrow; + unsigned funcdef_number; + unsigned nothrow : 1; + unsigned uses_eh_lsda : 1; } dw_fde_node; @@ -217,7 +220,7 @@ static dw_cfi_ref cie_cfi_head; maximum number of function definitions contained within the current compilation unit. These numbers are used to create unique label id's unique to each function definition. */ -static unsigned current_funcdef_number = 0; +unsigned current_funcdef_number = 0; /* Some DWARF extensions (e.g., MIPS/SGI) implement a subprogram attribute that accelerates the lookup of the FDE associated @@ -1684,22 +1687,22 @@ output_call_frame_info (for_eh) register dw_fde_ref fde; register dw_cfi_ref cfi; char l1[20], l2[20]; -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - char ld[20]; -#endif - - /* Do we want to include a pointer to the exception table? */ - int eh_ptr = for_eh && exception_table_p (); + int any_lsda_needed = 0; + char augmentation[6]; /* If we don't have any functions we'll want to unwind out of, don't emit any EH unwind information. */ if (for_eh) { + int any_eh_needed = 0; for (i = 0; i < fde_table_in_use; ++i) - if (! fde_table[i].nothrow) - goto found; - return; - found:; + if (fde_table[i].uses_eh_lsda) + any_eh_needed = any_lsda_needed = 1; + else if (! fde_table[i].nothrow) + any_eh_needed = 1; + + if (! any_eh_needed) + return; } /* We're going to be generating comments, so turn on app. */ @@ -1726,14 +1729,8 @@ output_call_frame_info (for_eh) /* Output the CIE. */ ASM_GENERATE_INTERNAL_LABEL (l1, CIE_AFTER_SIZE_LABEL, for_eh); ASM_GENERATE_INTERNAL_LABEL (l2, CIE_END_LABEL, for_eh); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_GENERATE_INTERNAL_LABEL (ld, CIE_LENGTH_LABEL, for_eh); - dw2_asm_output_offset (for_eh ? 4 : DWARF_OFFSET_SIZE, ld, - "Length of Common Information Entry"); -#else dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, "Length of Common Information Entry"); -#endif ASM_OUTPUT_LABEL (asm_out_file, l1); /* Now that the CIE pointer is PC-relative for EH, @@ -1744,20 +1741,23 @@ output_call_frame_info (for_eh) dw2_asm_output_data (1, DW_CIE_VERSION, "CIE Version"); - if (eh_ptr) - { - /* The CIE contains a pointer to the exception region info for the - frame. Make the augmentation string three bytes (including the - trailing null) so the pointer is 4-byte aligned. The Solaris ld - can't handle unaligned relocs. */ - dw2_asm_output_nstring ("eh", -1, "CIE Augmentation"); - dw2_asm_output_addr (DWARF2_ADDR_SIZE, "__EXCEPTION_TABLE__", - "pointer to exception region info"); - } - else + augmentation[0] = 0; + if (for_eh) { - dw2_asm_output_data (1, 0, "CIE Augmentation (none)"); + /* Augmentation: + z Indicates that a uleb128 is present to size the + augmentation section. + R Indicates a pointer encoding for CIE and FDE pointers. + P Indicates the presence of a language personality + routine in the CIE augmentation and an LSDA in the + FDE augmentation. */ + + /* ??? Handle pointer encodings. */ + + if (any_lsda_needed) + strcpy (augmentation, "zP"); } + dw2_asm_output_nstring (augmentation, -1, "CIE Augmentation"); dw2_asm_output_data_uleb128 (1, "CIE Code Alignment Factor"); @@ -1766,38 +1766,37 @@ output_call_frame_info (for_eh) dw2_asm_output_data (1, DWARF_FRAME_RETURN_COLUMN, "CIE RA Column"); + if (augmentation[0]) + { + dw2_asm_output_data_uleb128 (DWARF2_ADDR_SIZE, "Augmentation size"); + if (eh_personality_libfunc) + dw2_asm_output_addr_rtx (DWARF2_ADDR_SIZE, eh_personality_libfunc, + "Personality"); + else + dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, "Personality (none)"); + } + for (cfi = cie_cfi_head; cfi != NULL; cfi = cfi->dw_cfi_next) output_cfi (cfi, NULL); /* Pad the CIE out to an address sized boundary. */ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DWARF2_ADDR_SIZE)); ASM_OUTPUT_LABEL (asm_out_file, l2); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL (asm_out_file, ld, l2, l1); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s CIE Length Symbol", ASM_COMMENT_START); - fputc ('\n', asm_out_file); -#endif /* Loop through all of the FDE's. */ for (i = 0; i < fde_table_in_use; ++i) { fde = &fde_table[i]; - /* Don't emit EH unwind info for leaf functions. */ - if (for_eh && fde->nothrow) + /* Don't emit EH unwind info for leaf functions that don't need it. */ + if (for_eh && fde->nothrow && ! fde->uses_eh_lsda) continue; ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, FDE_LABEL, for_eh + i * 2); ASM_GENERATE_INTERNAL_LABEL (l1, FDE_AFTER_SIZE_LABEL, for_eh + i * 2); ASM_GENERATE_INTERNAL_LABEL (l2, FDE_END_LABEL, for_eh + i * 2); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_GENERATE_INTERNAL_LABEL (ld, FDE_LENGTH_LABEL, for_eh + i * 2); - dw2_asm_output_offset (for_eh ? 4 : DWARF_OFFSET_SIZE, ld, "FDE Length"); -#else dw2_asm_output_delta (for_eh ? 4 : DWARF_OFFSET_SIZE, l2, l1, "FDE Length"); -#endif ASM_OUTPUT_LABEL (asm_out_file, l1); /* ??? This always emits a 4 byte offset when for_eh is true, but it @@ -1821,6 +1820,21 @@ output_call_frame_info (for_eh) dw2_asm_output_delta (DWARF2_ADDR_SIZE, fde->dw_fde_end, fde->dw_fde_begin, "FDE address range"); + if (augmentation[0]) + { + dw2_asm_output_data_uleb128 (DWARF2_ADDR_SIZE, "Augmentation size"); + + if (fde->uses_eh_lsda) + { + ASM_GENERATE_INTERNAL_LABEL (l1, "LLSDA", fde->funcdef_number); + dw2_asm_output_offset (DWARF2_ADDR_SIZE, l1, + "Language Specific Data Area"); + } + else + dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, + "Language Specific Data Area (none)"); + } + /* Loop through the Call Frame Instructions associated with this FDE. */ fde->dw_fde_current_label = fde->dw_fde_begin; @@ -1830,12 +1844,6 @@ output_call_frame_info (for_eh) /* Pad the FDE out to an address sized boundary. */ ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (DWARF2_ADDR_SIZE)); ASM_OUTPUT_LABEL (asm_out_file, l2); -#ifdef ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL - ASM_OUTPUT_DEFINE_LABEL_DIFFERENCE_SYMBOL (asm_out_file, ld, l2, l1); - if (flag_debug_asm) - fprintf (asm_out_file, "\t%s FDE Length Symbol", ASM_COMMENT_START); - fputc ('\n', asm_out_file); -#endif } #ifndef EH_FRAME_SECTION @@ -1888,7 +1896,9 @@ dwarf2out_begin_prologue () fde->dw_fde_current_label = NULL; fde->dw_fde_end = NULL; fde->dw_fde_cfi = NULL; + fde->funcdef_number = current_funcdef_number; fde->nothrow = current_function_nothrow; + fde->uses_eh_lsda = cfun->uses_eh_lsda; args_size = old_args_size = 0; } diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h index a48dbf8..1f42626 100644 --- a/gcc/dwarf2out.h +++ b/gcc/dwarf2out.h @@ -41,3 +41,5 @@ extern void debug_dwarf_die PARAMS ((struct die_struct *)); extern void dwarf2out_set_demangle_name_func PARAMS ((const char *(*) (const char *))); extern void dwarf2out_abstract_function PARAMS ((tree)); extern void dwarf2out_add_library_unit_info PARAMS ((const char *, const char *)); + +extern unsigned current_funcdef_number; diff --git a/gcc/eh-common.h b/gcc/eh-common.h deleted file mode 100644 index dffe682..0000000 --- a/gcc/eh-common.h +++ /dev/null @@ -1,162 +0,0 @@ -/* EH stuff - Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. - -This file is part of GNU CC. - -This program is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - - -/* This file contains the structures required for the language - independent exception handling model. Both the static compiler and - the runtime library share this file. */ - -/* The runtime flag flag_new_exceptions is used to determine whether the - compiler supports the new runtime typechecking mechanism or not. Under - the new model, runtime info is contained in the exception table, and - the __throw() library routine determines which handler to call based - on the results of a call to a matching function provided by the expcetion - thrower. Otherwise the old scheme of calling any handler which matches - an exception range is used, and the handler is responsible for all - checking of runtime conditions. If the handler wasn't suppose to - get the exception, it performs a re-throw. */ - - -/* The handler_label field MUST be the first field in this structure. The - __throw() library routine expects uses __eh_stub() from except.c, which - simply dereferences the context pointer to get the handler. - The routine get_dynamic_handler_chain() also has a dependancy on - the location of 'dynamic_handler_chain'. If its location is changed, - that routine must be modified as well. */ -#ifndef EH_ALLOC_SIZE -/* 192 bytes means the entire eh_context plus malloc overhead fits in 256 - bytes (assuming 8 byte pointers). 192 bytes gives an eh_info and object - size limit of 96 bytes. This should be sufficient for throwing bad_alloc. */ -#define EH_ALLOC_SIZE 192 -#endif -#ifndef EH_ALLOC_ALIGN -/* We can't use BIGGEST_ALIGNMENT, because on some systems, that expands to - a check on a compile time switch like - 'target_flags & MASK_ALIGN_DOUBLE ? 64 : 32'. There's no macro for - 'largest alignment for any code this compiler can build for', which is - really what is needed. */ -#define EH_ALLOC_ALIGN 16 -#endif - -struct eh_context -{ - void *handler_label; - void **dynamic_handler_chain; - /* This is language dependent part of the eh context. */ - void *info; - /* This is used to remember where we threw for re-throws */ - void *table_index; /* address of exception table entry to rethrow from */ - /* emergency fallback space, if malloc fails during handling */ - char alloc_buffer[EH_ALLOC_SIZE] - __attribute__((__aligned__(EH_ALLOC_ALIGN))); - unsigned alloc_mask; -}; - -#ifndef EH_TABLE_LOOKUP - -typedef struct old_exception_table -{ - void *start_region; - void *end_region; - void *exception_handler; -} old_exception_table; - -typedef struct exception_table -{ - void *start_region; - void *end_region; - void *exception_handler; - void *match_info; /* runtime type info */ -} exception_table; - - -/* The language identifying portion of an exception table */ - -typedef struct exception_lang_info -{ - short language; - short version; -} exception_lang_info; - -/* This value in the first field of the exception descriptor - identifies the descriptor as the new model format. This value would never - be present in this location under the old model */ - -#define NEW_EH_RUNTIME ((void *) -2) - -/* Each function has an exception_descriptor which contains the - language info, and a table of exception ranges and handlers */ - -typedef struct exception_descriptor -{ - void *runtime_id_field; - exception_lang_info lang; - exception_table table[1]; -} exception_descriptor; - -struct __eh_info; /* forward declaration */ - -/* A pointer to a matching function is initialized at runtime by the - specific language if run-time exceptions are supported. - The function takes 3 parameters - 1 - runtime exception that has been thrown info. (__eh_info *) - 2 - Match info pointer from the region being considered (void *) - 3 - exception table region is in (exception descriptor *) -*/ - -typedef void * (*__eh_matcher) PARAMS ((struct __eh_info *, void *, - struct exception_descriptor *)); - -/* This value is to be checked as a 'match all' case in the runtime field. */ - -#define CATCH_ALL_TYPE ((void *) -1) - -/* This is the runtime exception information. This forms the minimum required - information for an exception info pointer in an eh_context structure. */ - - -typedef struct __eh_info -{ - __eh_matcher match_function; - short language; - short version; -} __eh_info; - -/* Convienient language codes for ID the originating language. Similar - to the codes in dwarf2.h. */ - -enum exception_source_language - { - EH_LANG_C89 = 0x0001, - EH_LANG_C = 0x0002, - EH_LANG_Ada83 = 0x0003, - EH_LANG_C_plus_plus = 0x0004, - EH_LANG_Cobol74 = 0x0005, - EH_LANG_Cobol85 = 0x0006, - EH_LANG_Fortran77 = 0x0007, - EH_LANG_Fortran90 = 0x0008, - EH_LANG_Pascal83 = 0x0009, - EH_LANG_Modula2 = 0x000a, - EH_LANG_Java = 0x000b, - EH_LANG_Mips_Assembler = 0x8001 - }; - -#endif /* EH_TABLE_LOOKUP */ - - diff --git a/gcc/except.c b/gcc/except.c index bc865552..ec13ed0 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -44,1070 +44,935 @@ Boston, MA 02111-1307, USA. */ exception, and thus there is the concept of "throwing" the exception up the call stack. - There are two major codegen options for exception handling. The - flag -fsjlj-exceptions can be used to select the setjmp/longjmp - approach, which is the default. -fno-sjlj-exceptions can be used to - get the PC range table approach. While this is a compile time - flag, an entire application must be compiled with the same codegen - option. The first is a PC range table approach, the second is a - setjmp/longjmp based scheme. We will first discuss the PC range - table approach, after that, we will discuss the setjmp/longjmp - based approach. - - It is appropriate to speak of the "context of a throw". This - context refers to the address where the exception is thrown from, - and is used to determine which exception region will handle the - exception. - - Regions of code within a function can be marked such that if it - contains the context of a throw, control will be passed to a - designated "exception handler". These areas are known as "exception - regions". Exception regions cannot overlap, but they can be nested - to any arbitrary depth. Also, exception regions cannot cross - function boundaries. - - Exception handlers can either be specified by the user (which we - will call a "user-defined handler") or generated by the compiler - (which we will designate as a "cleanup"). Cleanups are used to - perform tasks such as destruction of objects allocated on the - stack. - - In the current implementation, cleanups are handled by allocating an - exception region for the area that the cleanup is designated for, - and the handler for the region performs the cleanup and then - rethrows the exception to the outer exception region. From the - standpoint of the current implementation, there is little - distinction made between a cleanup and a user-defined handler, and - the phrase "exception handler" can be used to refer to either one - equally well. (The section "Future Directions" below discusses how - this will change). - - Each object file that is compiled with exception handling contains - a static array of exception handlers named __EXCEPTION_TABLE__. - Each entry contains the starting and ending addresses of the - exception region, and the address of the handler designated for - that region. - - 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). - - 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_info. On other targets, the information for each - translation unit is registered from the file generated by collect2. - __register_frame_info 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. 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 by 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 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: - - To associate a user-defined handler with a block of statements, the - function expand_start_try_stmts is used to mark the start of the - block of statements with which the handler is to be associated - (which is known as a "try block"). All statements that appear - afterwards will be associated with the try block. - - A call to expand_start_all_catch marks the end of the try block, - and also marks the start of the "catch block" (the user-defined - handler) associated with the try block. - - This user-defined handler will be invoked for *every* exception - thrown with the context of the try block. It is up to the handler - to decide whether or not it wishes to handle any given exception, - as there is currently no mechanism in this implementation for doing - this. (There are plans for conditionally processing an exception - based on its "type", which will provide a language-independent - mechanism). - - If the handler chooses not to process the exception (perhaps by - looking at an "exception type" or some other additional data - supplied with the exception), it can fall through to the end of the - handler. expand_end_all_catch and expand_leftover_cleanups - add additional code to the end of each handler to take care of - rethrowing to the outer exception handler. - - The handler also has the option to continue with "normal flow of - code", or in other words to resume executing at the statement - immediately after the end of the exception region. The variable - caught_return_label_stack contains a stack of labels, and jumping - to the topmost entry's label via expand_goto will resume normal - flow to the statement immediately after the end of the exception - region. If the handler falls through to the end, the exception will - be rethrown to the outer exception region. - - The instructions for the catch block are kept as a separate - sequence, and will be emitted at the end of the function along with - the handlers specified via expand_eh_region_end. The end of the - catch block is marked with expand_end_all_catch. - - Any data associated with the exception must currently be handled by - some external mechanism maintained in the frontend. For example, - the C++ exception mechanism passes an arbitrary value along with - the exception, and this is handled in the C++ frontend by using a - global variable to hold the value. (This will be changing in the - future.) - - The mechanism in C++ for handling data associated with the - exception is clearly not thread-safe. For a thread-based - environment, another mechanism must be used (possibly using a - per-thread allocation mechanism if the size of the area that needs - to be allocated isn't known at compile time.) - - Internally-generated exception regions (cleanups) are marked by - calling expand_eh_region_start to mark the start of the region, - and expand_eh_region_end (handler) is used to both designate the - end of the region and to associate a specified handler/cleanup with - the region. The rtl code in HANDLER will be invoked whenever an - exception occurs in the region between the calls to - expand_eh_region_start and expand_eh_region_end. After HANDLER is - executed, additional code is emitted to handle rethrowing the - exception to the outer exception handler. The code for HANDLER will - be emitted at the end of the function. - - TARGET_EXPRs can also be used to designate exception regions. A - TARGET_EXPR gives an unwind-protect style interface commonly used - in functional languages such as LISP. The associated expression is - evaluated, and whether or not it (or any of the functions that it - calls) throws an exception, the protect expression is always - invoked. This implementation takes care of the details of - associating an exception table entry with the expression and - generating the necessary code (it actually emits the protect - expression twice, once for normal flow and once for the exception - case). As for the other handlers, the code for the exception case - will be emitted at the end of the function. - - Cleanups can also be specified by using add_partial_entry (handler) - and end_protect_partials. add_partial_entry creates the start of - a new exception region; HANDLER will be invoked if an exception is - thrown with the context of the region between the calls to - add_partial_entry and end_protect_partials. end_protect_partials is - used to mark the end of these regions. add_partial_entry can be - called as many times as needed before calling end_protect_partials. - However, end_protect_partials should only be invoked once for each - group of calls to add_partial_entry as the entries are queued - and all of the outstanding entries are processed simultaneously - when end_protect_partials is invoked. Similarly to the other - handlers, the code for HANDLER will be emitted at the end of the - function. - - The generated RTL for an exception region includes - NOTE_INSN_EH_REGION_BEG and NOTE_INSN_EH_REGION_END notes that mark - the start and end of the exception region. A unique label is also - generated at the start of the exception region, which is available - by looking at the ehstack variable. The topmost entry corresponds - to the current region. - - In the current implementation, an exception can only be thrown from - a function call (since the mechanism used to actually throw an - exception involves calling __throw). If an exception region is - created but no function calls occur within that region, the region - can be safely optimized away (along with its exception handlers) - since no exceptions can ever be caught in that region. This - optimization is performed unless -fasynchronous-exceptions is - given. If the user wishes to throw from a signal handler, or other - asynchronous place, -fasynchronous-exceptions should be used when - compiling for maximally correct code, at the cost of additional - exception regions. Using -fasynchronous-exceptions only produces - code that is reasonably safe in such situations, but a correct - program cannot rely upon this working. It can be used in failsafe - code, where trying to continue on, and proceeding with potentially - incorrect results is better than halting the program. - - - Walking the stack: - - 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. - - Unwinding the stack: - - 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 __terminate. - - 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 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. 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 - user-defined exception regions. While this makes the implementation - simple, it also implies that it is impossible to determine if a - user-defined exception handler exists for a given exception without - completely unwinding the stack in the process. This is undesirable - from the standpoint of debugging, as ideally it would be possible - to trap unhandled exceptions in the debugger before the process of - unwinding has even started. - - This problem can be solved by marking user-defined handlers in a - special way (probably by adding additional bits to exception_table_list). - A two-pass scheme could then be used by __throw to iterate - through the table. The first pass would search for a relevant - user-defined handler for the current context of the throw, and if - one is found, the second pass would then invoke all needed cleanups - before jumping to the user-defined handler. - - Many languages (including C++ and Ada) make execution of a - user-defined handler conditional on the "type" of the exception - thrown. (The type of the exception is actually the type of the data - that is thrown with the exception.) It will thus be necessary for - __throw to be able to determine if a given user-defined - exception handler will actually be executed, given the type of - exception. - - One scheme is to add additional information to exception_table_list - as to the types of exceptions accepted by each handler. __throw - can do the type comparisons and then determine if the handler is - actually going to be executed. - - There is currently no significant level of debugging support - available, other than to place a breakpoint on __throw. While - this is sufficient in most cases, it would be helpful to be able to - know where a given exception was going to be thrown to before it is - actually thrown, and to be able to choose between stopping before - every exception region (including cleanups), or just user-defined - exception regions. This should be possible to do in the two-pass - scheme by adding additional labels to __throw for appropriate - breakpoints, and additional debugger commands could be added to - 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 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 - discusses the details. - - We don't use NOTE_INSN_EH_REGION_{BEG,END} pairs. We don't - optimize EH regions yet. We don't have to worry about machine - specific issues with unwinding the stack, as we rely upon longjmp - for all the machine specific details. There is no variable context - of a throw, just the one implied by the dynamic handler stack - pointed to by the dynamic handler chain. There is no exception - table, and no calls to __register_exceptions. __sjthrow is used - instead of __throw, and it works by using the dynamic handler - chain, and longjmp. -fasynchronous-exceptions has no effect, as - the elimination of trivial exception regions is not yet performed. - - A frontend can set protect_cleanup_actions_with_terminate when all - the cleanup actions should be protected with an EH region that - calls terminate when an unhandled exception is throw. C++ does - this, Ada does not. */ + [ Add updated documentation on how to use this. ] */ #include "config.h" -#include "eh-common.h" #include "system.h" #include "rtl.h" #include "tree.h" #include "flags.h" -#include "except.h" #include "function.h" #include "expr.h" -#include "regs.h" -#include "hard-reg-set.h" #include "insn-config.h" -#include "recog.h" +#include "except.h" +#include "integrate.h" +#include "hard-reg-set.h" +#include "basic-block.h" #include "output.h" +#include "dwarf2asm.h" +#include "dwarf2out.h" #include "toplev.h" +#include "hashtab.h" #include "intl.h" -#include "obstack.h" #include "ggc.h" #include "tm_p.h" -/* ??? Temporary hack before this entire file is replaced. */ -#ifdef IA64_UNWIND_INFO -#define flag_new_exceptions 0 -#else -#define flag_new_exceptions 1 + +/* Provide defaults for stuff that may not be defined when using + sjlj exceptions. */ +#ifndef EH_RETURN_STACKADJ_RTX +#define EH_RETURN_STACKADJ_RTX 0 +#endif +#ifndef EH_RETURN_HANDLER_RTX +#define EH_RETURN_HANDLER_RTX 0 +#endif +#ifndef EH_RETURN_DATA_REGNO +#define EH_RETURN_DATA_REGNO(N) INVALID_REGNUM #endif -/* One to enable asynchronous exception support. */ -int flag_non_call_exceptions = 0; +/* Nonzero means enable synchronous exceptions for non-call instructions. */ +int flag_non_call_exceptions; -/* One to protect cleanup actions with a handler that calls - __terminate, zero otherwise. */ +/* Protect cleanup actions with must-not-throw regions, with a call + to the given failure handler. */ +tree protect_cleanup_actions; -int protect_cleanup_actions_with_terminate; +/* Return true if type A catches type B. */ +int (*lang_eh_type_covers) PARAMS ((tree a, tree b)); -/* A list of labels used for exception handlers. Created by - find_exception_handler_labels for the optimization passes. */ +/* Map a type to a runtime object to match type. */ +tree (*lang_eh_runtime_type) PARAMS ((tree)); +/* A list of labels used for exception handlers. */ rtx exception_handler_labels; -/* Keeps track of the label used as the context of a throw to rethrow an - exception to the outer exception region. */ +static int call_site_base; +static int sjlj_funcdef_number; +static htab_t type_to_runtime_map; + +/* Describe the SjLj_Function_Context structure. */ +static tree sjlj_fc_type_node; +static int sjlj_fc_call_site_ofs; +static int sjlj_fc_data_ofs; +static int sjlj_fc_personality_ofs; +static int sjlj_fc_lsda_ofs; +static int sjlj_fc_jbuf_ofs; + +/* Describes one exception region. */ +struct eh_region +{ + /* The immediately surrounding region. */ + struct eh_region *outer; -struct label_node *outer_context_label_stack = NULL; + /* The list of immediately contained regions. */ + struct eh_region *inner; + struct eh_region *next_peer; -/* Pseudos used to hold exception return data in the interim between - __builtin_eh_return and the end of the function. */ + /* An identifier for this region. */ + int region_number; -static rtx eh_return_context; -static rtx eh_return_stack_adjust; -static rtx eh_return_handler; + /* Each region does exactly one thing. */ + enum eh_region_type + { + ERT_CLEANUP = 1, + ERT_TRY, + ERT_CATCH, + ERT_ALLOWED_EXCEPTIONS, + ERT_MUST_NOT_THROW, + ERT_THROW, + ERT_FIXUP + } type; + + /* Holds the action to perform based on the preceeding type. */ + union { + /* A list of catch blocks, a surrounding try block, + and the label for continuing after a catch. */ + struct { + struct eh_region *catch; + struct eh_region *last_catch; + struct eh_region *prev_try; + rtx continue_label; + } try; + + /* The list through the catch handlers, the type object + matched, and a pointer to the generated code. */ + struct { + struct eh_region *next_catch; + struct eh_region *prev_catch; + tree type; + int filter; + } catch; + + /* A tree_list of allowed types. */ + struct { + tree type_list; + int filter; + } allowed; + + /* The type given by a call to "throw foo();", or discovered + for a throw. */ + struct { + tree type; + } throw; + + /* Retain the cleanup expression even after expansion so that + we can match up fixup regions. */ + struct { + tree exp; + } cleanup; + + /* The real region (by expression and by pointer) that fixup code + should live in. */ + struct { + tree cleanup_exp; + struct eh_region *real_region; + } fixup; + } u; + + /* The region of code generated, or contained within, the region. */ + rtx label, last; + + /* Entry point for this region from the runtime eh library. */ + rtx landing_pad; + + /* Entry point for this region from an inner region. */ + rtx post_landing_pad; +}; -/* This is used for targets which can call rethrow with an offset instead - of an address. This is subtracted from the rethrow label we are - interested in. */ +/* Used to save exception status for each function. */ +struct eh_status +{ + /* The tree of all regions for this function. */ + struct eh_region *region_tree; -static rtx first_rethrow_symbol = NULL_RTX; -static rtx final_rethrow = NULL_RTX; -static rtx last_rethrow_symbol = NULL_RTX; + /* The same information as an indexable array. */ + struct eh_region **region_array; + /* The most recently open region. */ + struct eh_region *cur_region; -/* Prototypes for local functions. */ + /* This is the region for which we are processing catch blocks. */ + struct eh_region *try_region; -static void push_eh_entry PARAMS ((struct eh_stack *)); -static struct eh_entry * pop_eh_entry PARAMS ((struct eh_stack *)); -static void enqueue_eh_entry PARAMS ((struct eh_queue *, struct eh_entry *)); -static struct eh_entry * dequeue_eh_entry PARAMS ((struct eh_queue *)); -static rtx call_get_eh_context PARAMS ((void)); -static void start_dynamic_cleanup PARAMS ((tree, tree)); -static void start_dynamic_handler PARAMS ((void)); -static void expand_rethrow PARAMS ((rtx)); -static void output_exception_table_entry PARAMS ((FILE *, int)); -static rtx scan_region PARAMS ((rtx, int, int *)); -static void eh_regs PARAMS ((rtx *, rtx *, rtx *, int)); -static void set_insn_eh_region PARAMS ((rtx *, int)); -#ifdef DONT_USE_BUILTIN_SETJMP -static void jumpif_rtx PARAMS ((rtx, rtx)); -#endif -static void find_exception_handler_labels_1 PARAMS ((rtx)); -static void mark_eh_node PARAMS ((struct eh_node *)); -static void mark_eh_stack PARAMS ((struct eh_stack *)); -static void mark_eh_queue PARAMS ((struct eh_queue *)); -static void mark_tree_label_node PARAMS ((struct label_node *)); -static void mark_func_eh_entry PARAMS ((void *)); -static rtx create_rethrow_ref PARAMS ((int)); -static void push_entry PARAMS ((struct eh_stack *, struct eh_entry*)); -static void receive_exception_label PARAMS ((rtx)); -static int new_eh_region_entry PARAMS ((int, rtx)); -static int find_func_region PARAMS ((int)); -static int find_func_region_from_symbol PARAMS ((rtx)); -static void clear_function_eh_region PARAMS ((void)); -static void process_nestinfo PARAMS ((int, eh_nesting_info *, int *)); -rtx expand_builtin_return_addr PARAMS ((enum built_in_function, int, rtx)); -static void emit_cleanup_handler PARAMS ((struct eh_entry *)); -static int eh_region_from_symbol PARAMS ((rtx)); + /* A stack (TREE_LIST) of lists of handlers. The TREE_VALUE of each + node is itself a TREE_CHAINed list of handlers for regions that + are not yet closed. The TREE_VALUE of each entry contains the + handler for the corresponding entry on the ehstack. */ + tree protect_list; - -/* Various support routines to manipulate the various data structures - used by the exception handling code. */ + rtx filter; + rtx exc_ptr; -extern struct obstack permanent_obstack; + int built_landing_pads; + int last_region_number; -/* Generate a SYMBOL_REF for rethrow to use */ + varray_type ttype_data; + varray_type ehspec_data; + varray_type action_record_data; -static rtx -create_rethrow_ref (region_num) - int region_num; -{ - rtx def; - const char *ptr; - char buf[60]; + struct call_site_record + { + rtx landing_pad; + int action; + } *call_site_data; + int call_site_data_used; + int call_site_data_size; + + rtx ehr_stackadj; + rtx ehr_handler; + rtx ehr_label; + + rtx sjlj_fc; + rtx sjlj_exit_after; +}; - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", region_num); - ptr = ggc_strdup (buf); - def = gen_rtx_SYMBOL_REF (Pmode, ptr); - SYMBOL_REF_NEED_ADJUST (def) = 1; + +static void mark_eh_region PARAMS ((struct eh_region *)); + +static int t2r_eq PARAMS ((const PTR, + const PTR)); +static hashval_t t2r_hash PARAMS ((const PTR)); +static int t2r_mark_1 PARAMS ((PTR *, PTR)); +static void t2r_mark PARAMS ((PTR)); +static void add_type_for_runtime PARAMS ((tree)); +static tree lookup_type_for_runtime PARAMS ((tree)); + +static struct eh_region *expand_eh_region_end PARAMS ((void)); + +static void collect_eh_region_array PARAMS ((void)); +static void resolve_fixup_regions PARAMS ((void)); +static void remove_fixup_regions PARAMS ((void)); +static void convert_from_eh_region_ranges_1 PARAMS ((rtx *, int *, int)); + +static struct eh_region *duplicate_eh_region_1 PARAMS ((struct eh_region *, + struct inline_remap *)); +static void duplicate_eh_region_2 PARAMS ((struct eh_region *, + struct eh_region **)); +static int ttypes_filter_eq PARAMS ((const PTR, + const PTR)); +static hashval_t ttypes_filter_hash PARAMS ((const PTR)); +static int ehspec_filter_eq PARAMS ((const PTR, + const PTR)); +static hashval_t ehspec_filter_hash PARAMS ((const PTR)); +static int add_ttypes_entry PARAMS ((htab_t, tree)); +static int add_ehspec_entry PARAMS ((htab_t, htab_t, + tree)); +static void assign_filter_values PARAMS ((void)); +static void build_post_landing_pads PARAMS ((void)); +static void connect_post_landing_pads PARAMS ((void)); +static void dw2_build_landing_pads PARAMS ((void)); + +struct sjlj_lp_info; +static bool sjlj_find_directly_reachable_regions + PARAMS ((struct sjlj_lp_info *)); +static void sjlj_assign_call_site_values + PARAMS ((rtx, struct sjlj_lp_info *)); +static void sjlj_mark_call_sites + PARAMS ((struct sjlj_lp_info *)); +static void sjlj_emit_function_enter PARAMS ((rtx)); +static void sjlj_emit_function_exit PARAMS ((void)); +static void sjlj_emit_dispatch_table + PARAMS ((rtx, struct sjlj_lp_info *)); +static void sjlj_build_landing_pads PARAMS ((void)); + +static void remove_exception_handler_label PARAMS ((rtx)); +static void remove_eh_handler PARAMS ((struct eh_region *)); + +struct reachable_info; + +/* The return value of reachable_next_level. */ +enum reachable_code +{ + /* The given exception is not processed by the given region. */ + RNL_NOT_CAUGHT, + /* The given exception may need processing by the given region. */ + RNL_MAYBE_CAUGHT, + /* The given exception is completely processed by the given region. */ + RNL_CAUGHT, + /* The given exception is completely processed by the runtime. */ + RNL_BLOCKED +}; - return def; -} +static int check_handled PARAMS ((tree, tree)); +static void add_reachable_handler + PARAMS ((struct reachable_info *, struct eh_region *, + struct eh_region *)); +static enum reachable_code reachable_next_level + PARAMS ((struct eh_region *, tree, struct reachable_info *)); + +static int action_record_eq PARAMS ((const PTR, + const PTR)); +static hashval_t action_record_hash PARAMS ((const PTR)); +static int add_action_record PARAMS ((htab_t, int, int)); +static int collect_one_action_chain PARAMS ((htab_t, + struct eh_region *)); +static int add_call_site PARAMS ((rtx, int)); + +static void push_uleb128 PARAMS ((varray_type *, + unsigned int)); +static void push_sleb128 PARAMS ((varray_type *, int)); +static const char *eh_data_format_name PARAMS ((int)); +#ifndef HAVE_AS_LEB128 +static int dw2_size_of_call_site_table PARAMS ((void)); +static int sjlj_size_of_call_site_table PARAMS ((void)); +#endif +static void dw2_output_call_site_table PARAMS ((void)); +static void sjlj_output_call_site_table PARAMS ((void)); -/* Push a label entry onto the given STACK. */ + +/* Routine to see if exception handling is turned on. + DO_WARN is non-zero if we want to inform the user that exception + handling is turned off. -void -push_label_entry (stack, rlabel, tlabel) - struct label_node **stack; - rtx rlabel; - tree tlabel; -{ - struct label_node *newnode - = (struct label_node *) xmalloc (sizeof (struct label_node)); + This is used to ensure that -fexceptions has been specified if the + compiler tries to use any exception-specific functions. */ - if (rlabel) - newnode->u.rlabel = rlabel; - else - newnode->u.tlabel = tlabel; - newnode->chain = *stack; - *stack = newnode; +int +doing_eh (do_warn) + int do_warn; +{ + if (! flag_exceptions) + { + static int warned = 0; + if (! warned && do_warn) + { + error ("exception handling disabled, use -fexceptions to enable"); + warned = 1; + } + return 0; + } + return 1; } -/* Pop a label entry from the given STACK. */ - -rtx -pop_label_entry (stack) - struct label_node **stack; + +void +init_eh () { - rtx label; - struct label_node *tempnode; - - if (! *stack) - return NULL_RTX; - - tempnode = *stack; - label = tempnode->u.rlabel; - *stack = (*stack)->chain; - free (tempnode); + ggc_add_rtx_root (&exception_handler_labels, 1); + ggc_add_tree_root (&protect_cleanup_actions, 1); - return label; -} + if (! flag_exceptions) + return; -/* Return the top element of the given STACK. */ + type_to_runtime_map = htab_create (31, t2r_hash, t2r_eq, NULL); + ggc_add_root (&type_to_runtime_map, 1, sizeof (htab_t), t2r_mark); -tree -top_label_entry (stack) - struct label_node **stack; -{ - if (! *stack) - return NULL_TREE; + /* Create the SjLj_Function_Context structure. This should match + the definition in unwind-sjlj.c. */ + if (USING_SJLJ_EXCEPTIONS) + { + tree f_jbuf, f_per, f_lsda, f_prev, f_cs, f_data, tmp; - return (*stack)->u.tlabel; -} + sjlj_fc_type_node = make_lang_type (RECORD_TYPE); + ggc_add_tree_root (&sjlj_fc_type_node, 1); -/* Get an exception label. */ + f_prev = build_decl (FIELD_DECL, get_identifier ("__prev"), + build_pointer_type (sjlj_fc_type_node)); + DECL_FIELD_CONTEXT (f_prev) = sjlj_fc_type_node; -rtx -gen_exception_label () -{ - rtx lab; - lab = gen_label_rtx (); - return lab; -} + f_cs = build_decl (FIELD_DECL, get_identifier ("__call_site"), + integer_type_node); + DECL_FIELD_CONTEXT (f_cs) = sjlj_fc_type_node; -/* Push a new eh_node entry onto STACK. */ + tmp = build_index_type (build_int_2 (4 - 1, 0)); + tmp = build_array_type (type_for_mode (word_mode, 1), tmp); + f_data = build_decl (FIELD_DECL, get_identifier ("__data"), tmp); + DECL_FIELD_CONTEXT (f_data) = sjlj_fc_type_node; -static void -push_eh_entry (stack) - struct eh_stack *stack; -{ - struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); - struct eh_entry *entry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry)); - - rtx rlab = gen_exception_label (); - entry->finalization = NULL_TREE; - entry->label_used = 0; - entry->exception_handler_label = rlab; - entry->false_label = NULL_RTX; - if (! flag_new_exceptions) - entry->outer_context = gen_label_rtx (); - else - entry->outer_context = create_rethrow_ref (CODE_LABEL_NUMBER (rlab)); - entry->rethrow_label = entry->outer_context; - entry->goto_entry_p = 0; + f_per = build_decl (FIELD_DECL, get_identifier ("__personality"), + ptr_type_node); + DECL_FIELD_CONTEXT (f_per) = sjlj_fc_type_node; - node->entry = entry; - node->chain = stack->top; - stack->top = node; -} + f_lsda = build_decl (FIELD_DECL, get_identifier ("__lsda"), + ptr_type_node); + DECL_FIELD_CONTEXT (f_lsda) = sjlj_fc_type_node; -/* Push an existing entry onto a stack. */ - -static void -push_entry (stack, entry) - struct eh_stack *stack; - struct eh_entry *entry; -{ - struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); - node->entry = entry; - node->chain = stack->top; - stack->top = node; +#ifdef DONT_USE_BUILTIN_SETJMP +#ifdef JMP_BUF_SIZE + tmp = build_int_2 (JMP_BUF_SIZE - 1, 0); +#else + /* Should be large enough for most systems, if it is not, + JMP_BUF_SIZE should be defined with the proper value. It will + also tend to be larger than necessary for most systems, a more + optimal port will define JMP_BUF_SIZE. */ + tmp = build_int_2 (FIRST_PSEUDO_REGISTER + 2 - 1, 0); +#endif +#else + /* This is 2 for builtin_setjmp, plus whatever the target requires + via STACK_SAVEAREA_MODE (SAVE_NONLOCAL). */ + tmp = build_int_2 ((GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL)) + / GET_MODE_SIZE (Pmode)) + 2 - 1, 0); +#endif + tmp = build_index_type (tmp); + tmp = build_array_type (ptr_type_node, tmp); + f_jbuf = build_decl (FIELD_DECL, get_identifier ("__jbuf"), tmp); +#ifdef DONT_USE_BUILTIN_SETJMP + /* We don't know what the alignment requirements of the + runtime's jmp_buf has. Overestimate. */ + DECL_ALIGN (f_jbuf) = BIGGEST_ALIGNMENT; + DECL_USER_ALIGN (f_jbuf) = 1; +#endif + DECL_FIELD_CONTEXT (f_jbuf) = sjlj_fc_type_node; + + TYPE_FIELDS (sjlj_fc_type_node) = f_prev; + TREE_CHAIN (f_prev) = f_cs; + TREE_CHAIN (f_cs) = f_data; + TREE_CHAIN (f_data) = f_per; + TREE_CHAIN (f_per) = f_lsda; + TREE_CHAIN (f_lsda) = f_jbuf; + + layout_type (sjlj_fc_type_node); + + /* Cache the interesting field offsets so that we have + easy access from rtl. */ + sjlj_fc_call_site_ofs + = (tree_low_cst (DECL_FIELD_OFFSET (f_cs), 1) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (f_cs), 1) / BITS_PER_UNIT); + sjlj_fc_data_ofs + = (tree_low_cst (DECL_FIELD_OFFSET (f_data), 1) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (f_data), 1) / BITS_PER_UNIT); + sjlj_fc_personality_ofs + = (tree_low_cst (DECL_FIELD_OFFSET (f_per), 1) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (f_per), 1) / BITS_PER_UNIT); + sjlj_fc_lsda_ofs + = (tree_low_cst (DECL_FIELD_OFFSET (f_lsda), 1) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (f_lsda), 1) / BITS_PER_UNIT); + sjlj_fc_jbuf_ofs + = (tree_low_cst (DECL_FIELD_OFFSET (f_jbuf), 1) + + tree_low_cst (DECL_FIELD_BIT_OFFSET (f_jbuf), 1) / BITS_PER_UNIT); + } } -/* Pop an entry from the given STACK. */ - -static struct eh_entry * -pop_eh_entry (stack) - struct eh_stack *stack; +void +init_eh_for_function () { - struct eh_node *tempnode; - struct eh_entry *tempentry; - - tempnode = stack->top; - tempentry = tempnode->entry; - stack->top = stack->top->chain; - free (tempnode); - - return tempentry; + cfun->eh = (struct eh_status *) xcalloc (1, sizeof (struct eh_status)); } -/* Enqueue an ENTRY onto the given QUEUE. */ +/* Mark EH for GC. */ static void -enqueue_eh_entry (queue, entry) - struct eh_queue *queue; - struct eh_entry *entry; +mark_eh_region (region) + struct eh_region *region; { - struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); + if (! region) + return; - node->entry = entry; - node->chain = NULL; + switch (region->type) + { + case ERT_CLEANUP: + ggc_mark_tree (region->u.cleanup.exp); + break; + case ERT_TRY: + ggc_mark_rtx (region->u.try.continue_label); + break; + case ERT_CATCH: + ggc_mark_tree (region->u.catch.type); + break; + case ERT_ALLOWED_EXCEPTIONS: + ggc_mark_tree (region->u.allowed.type_list); + break; + case ERT_MUST_NOT_THROW: + break; + case ERT_THROW: + ggc_mark_tree (region->u.throw.type); + break; + case ERT_FIXUP: + ggc_mark_tree (region->u.fixup.cleanup_exp); + break; + default: + abort (); + } - if (queue->head == NULL) - queue->head = node; - else - queue->tail->chain = node; - queue->tail = node; + ggc_mark_rtx (region->label); + ggc_mark_rtx (region->last); + ggc_mark_rtx (region->landing_pad); + ggc_mark_rtx (region->post_landing_pad); } -/* Dequeue an entry from the given QUEUE. */ - -static struct eh_entry * -dequeue_eh_entry (queue) - struct eh_queue *queue; +void +mark_eh_status (eh) + struct eh_status *eh; { - struct eh_node *tempnode; - struct eh_entry *tempentry; + int i; + + if (eh == 0) + return; + + /* If we've called collect_eh_region_array, use it. Otherwise walk + the tree non-recursively. */ + if (eh->region_array) + { + for (i = eh->last_region_number; i > 0; --i) + { + struct eh_region *r = eh->region_array[i]; + if (r && r->region_number == i) + mark_eh_region (r); + } + } + else if (eh->region_tree) + { + struct eh_region *r = eh->region_tree; + while (1) + { + mark_eh_region (r); + if (r->inner) + r = r->inner; + else if (r->next_peer) + r = r->next_peer; + else + { + do { + r = r->outer; + if (r == NULL) + goto tree_done; + } while (r->next_peer == NULL); + r = r->next_peer; + } + } + tree_done:; + } - if (queue->head == NULL) - return NULL; + ggc_mark_tree (eh->protect_list); + ggc_mark_rtx (eh->filter); + ggc_mark_rtx (eh->exc_ptr); + ggc_mark_tree_varray (eh->ttype_data); - tempnode = queue->head; - queue->head = queue->head->chain; + if (eh->call_site_data) + { + for (i = eh->call_site_data_used - 1; i >= 0; --i) + ggc_mark_rtx (eh->call_site_data[i].landing_pad); + } - tempentry = tempnode->entry; - free (tempnode); + ggc_mark_rtx (eh->ehr_stackadj); + ggc_mark_rtx (eh->ehr_handler); + ggc_mark_rtx (eh->ehr_label); - return tempentry; + ggc_mark_rtx (eh->sjlj_fc); + ggc_mark_rtx (eh->sjlj_exit_after); } -static void -receive_exception_label (handler_label) - rtx handler_label; +void +free_eh_status (f) + struct function *f; { - rtx around_label = NULL_RTX; - - if (! flag_new_exceptions || USING_SJLJ_EXCEPTIONS) - { - around_label = gen_label_rtx (); - emit_jump (around_label); - emit_barrier (); - } + struct eh_status *eh = f->eh; - emit_label (handler_label); - - if (! USING_SJLJ_EXCEPTIONS) + if (eh->region_array) { -#ifdef HAVE_exception_receiver - if (HAVE_exception_receiver) - emit_insn (gen_exception_receiver ()); - else -#endif -#ifdef HAVE_nonlocal_goto_receiver - if (HAVE_nonlocal_goto_receiver) - emit_insn (gen_nonlocal_goto_receiver ()); - else -#endif - { /* Nothing */ } + int i; + for (i = eh->last_region_number; i > 0; --i) + { + struct eh_region *r = eh->region_array[i]; + /* Mind we don't free a region struct more than once. */ + if (r && r->region_number == i) + free (r); + } + free (eh->region_array); } - else + else if (eh->region_tree) { -#ifndef DONT_USE_BUILTIN_SETJMP - expand_builtin_setjmp_receiver (handler_label); -#endif + struct eh_region *next, *r = eh->region_tree; + while (1) + { + if (r->inner) + r = r->inner; + else if (r->next_peer) + { + next = r->next_peer; + free (r); + r = next; + } + else + { + do { + next = r->outer; + free (r); + r = next; + if (r == NULL) + goto tree_done; + } while (r->next_peer == NULL); + next = r->next_peer; + free (r); + r = next; + } + } + tree_done:; } - if (around_label) - emit_label (around_label); + VARRAY_FREE (eh->ttype_data); + VARRAY_FREE (eh->ehspec_data); + VARRAY_FREE (eh->action_record_data); + if (eh->call_site_data) + free (eh->call_site_data); + + free (eh); + f->eh = NULL; } + +/* Start an exception handling region. All instructions emitted + after this point are considered to be part of the region until + expand_eh_region_end is invoked. */ -struct func_eh_entry +void +expand_eh_region_start () { - int range_number; /* EH region number from EH NOTE insn's. */ - rtx rethrow_label; /* Label for rethrow. */ - int rethrow_ref; /* Is rethrow_label referenced? */ - int emitted; /* 1 if this entry has been emitted in assembly file. */ - struct handler_info *handlers; -}; + struct eh_region *new_region; + struct eh_region *cur_region; + rtx note; + if (! doing_eh (0)) + return; -/* table of function eh regions */ -static struct func_eh_entry *function_eh_regions = NULL; -static int num_func_eh_entries = 0; -static int current_func_eh_entry = 0; + /* We need a new block to record the start and end of the dynamic + handler chain. We also want to prevent jumping into a try block. */ + expand_start_bindings (2); -#define SIZE_FUNC_EH(X) (sizeof (struct func_eh_entry) * X) + /* But we don't need or want a new temporary level. */ + pop_temp_slots (); -/* Add a new eh_entry for this function. The number returned is an - number which uniquely identifies this exception range. */ + /* Mark this block as created by expand_eh_region_start. This is so + that we can pop the block with expand_end_bindings automatically. */ + mark_block_as_eh_region (); -static int -new_eh_region_entry (note_eh_region, rethrow) - int note_eh_region; - rtx rethrow; -{ - if (current_func_eh_entry == num_func_eh_entries) + /* Insert a new blank region as a leaf in the tree. */ + new_region = (struct eh_region *) xcalloc (1, sizeof (*new_region)); + cur_region = cfun->eh->cur_region; + new_region->outer = cur_region; + if (cur_region) { - if (num_func_eh_entries == 0) - { - function_eh_regions = - (struct func_eh_entry *) xmalloc (SIZE_FUNC_EH (50)); - num_func_eh_entries = 50; - } - else - { - num_func_eh_entries = num_func_eh_entries * 3 / 2; - function_eh_regions = (struct func_eh_entry *) - xrealloc (function_eh_regions, SIZE_FUNC_EH (num_func_eh_entries)); - } - } - function_eh_regions[current_func_eh_entry].range_number = note_eh_region; - if (rethrow == NULL_RTX) - function_eh_regions[current_func_eh_entry].rethrow_label = - create_rethrow_ref (note_eh_region); + new_region->next_peer = cur_region->inner; + cur_region->inner = new_region; + } else - function_eh_regions[current_func_eh_entry].rethrow_label = rethrow; - function_eh_regions[current_func_eh_entry].handlers = NULL; - function_eh_regions[current_func_eh_entry].emitted = 0; - - return current_func_eh_entry++; -} - -/* Add new handler information to an exception range. The first parameter - specifies the range number (returned from new_eh_entry()). The second - parameter specifies the handler. By default the handler is inserted at - the end of the list. A handler list may contain only ONE NULL_TREE - typeinfo entry. Regardless where it is positioned, a NULL_TREE entry - is always output as the LAST handler in the exception table for a region. */ - -void -add_new_handler (region, newhandler) - int region; - struct handler_info *newhandler; -{ - struct handler_info *last; - - /* If find_func_region returns -1, callers might attempt to pass us - this region number. If that happens, something has gone wrong; - -1 is never a valid region. */ - if (region == -1) - abort (); - - newhandler->next = NULL; - last = function_eh_regions[region].handlers; - if (last == NULL) - function_eh_regions[region].handlers = newhandler; - else { - for ( ; ; last = last->next) - { - if (last->type_info == CATCH_ALL_TYPE) - pedwarn ("additional handler after ..."); - if (last->next == NULL) - break; - } - last->next = newhandler; + new_region->next_peer = cfun->eh->region_tree; + cfun->eh->region_tree = new_region; } + cfun->eh->cur_region = new_region; + + /* Create a note marking the start of this region. */ + new_region->region_number = ++cfun->eh->last_region_number; + note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG); + NOTE_EH_HANDLER (note) = new_region->region_number; } -/* Remove a handler label. The handler label is being deleted, so all - regions which reference this handler should have it removed from their - list of possible handlers. Any region which has the final handler - removed can be deleted. */ +/* Common code to end a region. Returns the region just ended. */ -void remove_handler (removing_label) - rtx removing_label; +static struct eh_region * +expand_eh_region_end () { - struct handler_info *handler, *last; - int x; - for (x = 0 ; x < current_func_eh_entry; ++x) + struct eh_region *cur_region = cfun->eh->cur_region; + rtx note; + + /* Create a nute marking the end of this region. */ + note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END); + NOTE_EH_HANDLER (note) = cur_region->region_number; + + /* Pop. */ + cfun->eh->cur_region = cur_region->outer; + + /* If we have already started ending the bindings, don't recurse. */ + if (is_eh_region ()) { - last = NULL; - handler = function_eh_regions[x].handlers; - for ( ; handler; last = handler, handler = handler->next) - if (handler->handler_label == removing_label) - { - if (last) - { - last->next = handler->next; - handler = last; - } - else - function_eh_regions[x].handlers = handler->next; - } + /* Because we don't need or want a new temporary level and + because we didn't create one in expand_eh_region_start, + create a fake one now to avoid removing one in + expand_end_bindings. */ + push_temp_slots (); + + mark_block_as_not_eh_region (); + + expand_end_bindings (NULL_TREE, 0, 0); } + + return cur_region; } -/* This function will return a malloc'd pointer to an array of - void pointer representing the runtime match values that - currently exist in all regions. */ +/* End an exception handling region for a cleanup. HANDLER is an + expression to expand for the cleanup. */ -int -find_all_handler_type_matches (array) - void ***array; +void +expand_eh_region_end_cleanup (handler) + tree handler; { - struct handler_info *handler, *last; - int x,y; - void *val; - void **ptr; - int max_ptr; - int n_ptr = 0; + struct eh_region *region; + rtx around_label; - *array = NULL; + if (! doing_eh (0)) + return; - if (!doing_eh (0) || ! flag_new_exceptions) - return 0; + region = expand_eh_region_end (); + region->type = ERT_CLEANUP; + region->label = gen_label_rtx (); + region->u.cleanup.exp = handler; - max_ptr = 100; - ptr = (void **) xmalloc (max_ptr * sizeof (void *)); + around_label = gen_label_rtx (); + emit_jump (around_label); - for (x = 0 ; x < current_func_eh_entry; x++) - { - last = NULL; - handler = function_eh_regions[x].handlers; - for ( ; handler; last = handler, handler = handler->next) - { - val = handler->type_info; - if (val != NULL && val != CATCH_ALL_TYPE) - { - /* See if this match value has already been found. */ - for (y = 0; y < n_ptr; y++) - if (ptr[y] == val) - break; + emit_label (region->label); - /* If we break early, we already found this value. */ - if (y < n_ptr) - continue; + if (protect_cleanup_actions) + expand_eh_region_start (); - /* Do we need to allocate more space? */ - if (n_ptr >= max_ptr) - { - max_ptr += max_ptr / 2; - ptr = (void **) xrealloc (ptr, max_ptr * sizeof (void *)); - } - ptr[n_ptr] = val; - n_ptr++; - } - } - } + expand_expr (handler, const0_rtx, VOIDmode, 0); - if (n_ptr == 0) - { - free (ptr); - ptr = NULL; - } - *array = ptr; - return n_ptr; + if (protect_cleanup_actions) + expand_eh_region_end_must_not_throw (protect_cleanup_actions); + + /* We delay the generation of the _Unwind_Resume until we generate + landing pads. We emit a marker here so as to get good control + flow data in the meantime. */ + emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number)); + emit_barrier (); + + region->last = get_last_insn (); + + emit_label (around_label); } -/* Create a new handler structure initialized with the handler label and - typeinfo fields passed in. */ +/* End an exception handling region for a try block, and prepares + for subsequent calls to expand_start_catch. */ -struct handler_info * -get_new_handler (handler, typeinfo) - rtx handler; - void *typeinfo; +void +expand_start_all_catch () { - struct handler_info* ptr; - ptr = (struct handler_info *) xmalloc (sizeof (struct handler_info)); - ptr->handler_label = handler; - ptr->handler_number = CODE_LABEL_NUMBER (handler); - ptr->type_info = typeinfo; - ptr->next = NULL; + struct eh_region *region; - return ptr; -} + if (! doing_eh (1)) + return; + + region = expand_eh_region_end (); + region->type = ERT_TRY; + region->u.try.prev_try = cfun->eh->try_region; + region->u.try.continue_label = gen_label_rtx (); + cfun->eh->try_region = region; + emit_jump (region->u.try.continue_label); +} -/* Find the index in function_eh_regions associated with a NOTE region. If - the region cannot be found, a -1 is returned. */ +/* Begin a catch clause. TYPE is the type caught, or null if this is + a catch-all clause. */ -static int -find_func_region (insn_region) - int insn_region; +void +expand_start_catch (type) + tree type; { - int x; - for (x = 0; x < current_func_eh_entry; x++) - if (function_eh_regions[x].range_number == insn_region) - return x; + struct eh_region *t, *c, *l; + + if (! doing_eh (0)) + return; + + if (type) + add_type_for_runtime (type); + expand_eh_region_start (); + + t = cfun->eh->try_region; + c = cfun->eh->cur_region; + c->type = ERT_CATCH; + c->u.catch.type = type; + c->label = gen_label_rtx (); - return -1; + l = t->u.try.last_catch; + c->u.catch.prev_catch = l; + if (l) + l->u.catch.next_catch = c; + else + t->u.try.catch = c; + t->u.try.last_catch = c; + + emit_label (c->label); } -/* Get a pointer to the first handler in an exception region's list. */ +/* End a catch clause. Control will resume after the try/catch block. */ -struct handler_info * -get_first_handler (region) - int region; +void +expand_end_catch () { - int r = find_func_region (region); - if (r == -1) - abort (); - return function_eh_regions[r].handlers; + struct eh_region *try_region, *catch_region; + + if (! doing_eh (0)) + return; + + catch_region = expand_eh_region_end (); + try_region = cfun->eh->try_region; + + emit_jump (try_region->u.try.continue_label); + + catch_region->last = get_last_insn (); } -/* Clean out the function_eh_region table and free all memory */ +/* End a sequence of catch handlers for a try block. */ -static void -clear_function_eh_region () +void +expand_end_all_catch () { - int x; - struct handler_info *ptr, *next; - for (x = 0; x < current_func_eh_entry; x++) - for (ptr = function_eh_regions[x].handlers; ptr != NULL; ptr = next) - { - next = ptr->next; - free (ptr); - } - if (function_eh_regions) - free (function_eh_regions); - num_func_eh_entries = 0; - current_func_eh_entry = 0; + struct eh_region *try_region; + + if (! doing_eh (0)) + return; + + try_region = cfun->eh->try_region; + cfun->eh->try_region = try_region->u.try.prev_try; + + emit_label (try_region->u.try.continue_label); } -/* Make a duplicate of an exception region by copying all the handlers - for an exception region. Return the new handler index. The final - parameter is a routine which maps old labels to new ones. */ +/* End an exception region for an exception type filter. ALLOWED is a + TREE_LIST of types to be matched by the runtime. FAILURE is an + expression to invoke if a mismatch ocurrs. */ -int -duplicate_eh_handlers (old_note_eh_region, new_note_eh_region, map) - int old_note_eh_region, new_note_eh_region; - rtx (*map) PARAMS ((rtx)); +void +expand_eh_region_end_allowed (allowed, failure) + tree allowed, failure; { - struct handler_info *ptr, *new_ptr; - int new_region, region; + struct eh_region *region; + rtx around_label; - region = find_func_region (old_note_eh_region); - if (region == -1) - /* Cannot duplicate non-existant exception region. */ - abort (); + if (! doing_eh (0)) + return; - /* duplicate_eh_handlers may have been called during a symbol remap. */ - new_region = find_func_region (new_note_eh_region); - if (new_region != -1) - return (new_region); + region = expand_eh_region_end (); + region->type = ERT_ALLOWED_EXCEPTIONS; + region->u.allowed.type_list = allowed; + region->label = gen_label_rtx (); - new_region = new_eh_region_entry (new_note_eh_region, NULL_RTX); + for (; allowed ; allowed = TREE_CHAIN (allowed)) + add_type_for_runtime (TREE_VALUE (allowed)); - ptr = function_eh_regions[region].handlers; + /* We must emit the call to FAILURE here, so that if this function + throws a different exception, that it will be processed by the + correct region. */ - for ( ; ptr; ptr = ptr->next) - { - new_ptr = get_new_handler (map (ptr->handler_label), ptr->type_info); - add_new_handler (new_region, new_ptr); - } + around_label = gen_label_rtx (); + emit_jump (around_label); + + emit_label (region->label); + expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL); - return new_region; + region->last = get_last_insn (); + + emit_label (around_label); } +/* End an exception region for a must-not-throw filter. FAILURE is an + expression invoke if an uncaught exception propagates this far. -/* Given a rethrow symbol, find the EH region number this is for. */ + This is conceptually identical to expand_eh_region_end_allowed with + an empty allowed list (if you passed "std::terminate" instead of + "__cxa_call_unexpected"), but they are represented differently in + the C++ LSDA. */ -static int -eh_region_from_symbol (sym) - rtx sym; +void +expand_eh_region_end_must_not_throw (failure) + tree failure; { - int x; - if (sym == last_rethrow_symbol) - return 1; - for (x = 0; x < current_func_eh_entry; x++) - if (function_eh_regions[x].rethrow_label == sym) - return function_eh_regions[x].range_number; - return -1; -} + struct eh_region *region; + rtx around_label; -/* Like find_func_region, but using the rethrow symbol for the region - rather than the region number itself. */ + if (! doing_eh (0)) + return; -static int -find_func_region_from_symbol (sym) - rtx sym; -{ - return find_func_region (eh_region_from_symbol (sym)); -} + region = expand_eh_region_end (); + region->type = ERT_MUST_NOT_THROW; + region->label = gen_label_rtx (); -/* When inlining/unrolling, we have to map the symbols passed to - __rethrow as well. This performs the remap. If a symbol isn't foiund, - the original one is returned. This is not an efficient routine, - so don't call it on everything!! */ + /* We must emit the call to FAILURE here, so that if this function + throws a different exception, that it will be processed by the + correct region. */ -rtx -rethrow_symbol_map (sym, map) - rtx sym; - rtx (*map) PARAMS ((rtx)); -{ - int x, y; + around_label = gen_label_rtx (); + emit_jump (around_label); - if (! flag_new_exceptions) - return sym; + emit_label (region->label); + expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL); - for (x = 0; x < current_func_eh_entry; x++) - if (function_eh_regions[x].rethrow_label == sym) - { - /* We've found the original region, now lets determine which region - this now maps to. */ - rtx l1 = function_eh_regions[x].handlers->handler_label; - rtx l2 = map (l1); - y = CODE_LABEL_NUMBER (l2); /* This is the new region number */ - x = find_func_region (y); /* Get the new permanent region */ - if (x == -1) /* Hmm, Doesn't exist yet */ - { - x = duplicate_eh_handlers (CODE_LABEL_NUMBER (l1), y, map); - /* Since we're mapping it, it must be used. */ - function_eh_regions[x].rethrow_ref = 1; - } - return function_eh_regions[x].rethrow_label; - } - return sym; + region->last = get_last_insn (); + + emit_label (around_label); } -/* Returns nonzero if the rethrow label for REGION is referenced - somewhere (i.e. we rethrow out of REGION or some other region - masquerading as REGION). */ +/* End an exception region for a throw. No handling goes on here, + but it's the easiest way for the front-end to indicate what type + is being thrown. */ -int -rethrow_used (region) - int region; +void +expand_eh_region_end_throw (type) + tree type; { - if (flag_new_exceptions) - { - int ret = function_eh_regions[find_func_region (region)].rethrow_ref; - return ret; - } - return 0; + struct eh_region *region; + + if (! doing_eh (0)) + return; + + region = expand_eh_region_end (); + region->type = ERT_THROW; + region->u.throw.type = type; } - -/* Routine to see if exception handling is turned on. - DO_WARN is non-zero if we want to inform the user that exception - handling is turned off. +/* End a fixup region. Within this region the cleanups for the immediately + enclosing region are _not_ run. This is used for goto cleanup to avoid + destroying an object twice. - This is used to ensure that -fexceptions has been specified if the - compiler tries to use any exception-specific functions. */ + This would be an extraordinarily simple prospect, were it not for the + fact that we don't actually know what the immediately enclosing region + is. This surprising fact is because expand_cleanups is currently + generating a sequence that it will insert somewhere else. We collect + the proper notion of "enclosing" in convert_from_eh_region_ranges. */ -int -doing_eh (do_warn) - int do_warn; +void +expand_eh_region_end_fixup (handler) + tree handler; { - if (! flag_exceptions) - { - static int warned = 0; - if (! warned && do_warn) - { - error ("exception handling disabled, use -fexceptions to enable"); - warned = 1; - } - return 0; - } - return 1; + struct eh_region *fixup; + + if (! doing_eh (0)) + return; + + fixup = expand_eh_region_end (); + fixup->type = ERT_FIXUP; + fixup->u.fixup.cleanup_exp = handler; } -/* Given a return address in ADDR, determine the address we should use - to find the corresponding EH region. */ +/* Return a tree expression for a pointer to the exception object + within a handler. */ rtx -eh_outer_context (addr) - rtx addr; +get_exception_pointer () { - /* First mask out any unwanted bits. */ -#ifdef MASK_RETURN_ADDR - expand_and (addr, MASK_RETURN_ADDR, addr); -#endif + rtx exc_ptr = cfun->eh->exc_ptr; + if (! exc_ptr) + { + exc_ptr = gen_reg_rtx (Pmode); + cfun->eh->exc_ptr = exc_ptr; + } + return exc_ptr; +} - /* Then adjust to find the real return address. */ -#if defined (RETURN_ADDR_OFFSET) - addr = plus_constant (addr, RETURN_ADDR_OFFSET); -#endif + +/* Begin a region that will contain entries created with + add_partial_entry. */ - return addr; +void +begin_protect_partials () +{ + /* Push room for a new list. */ + cfun->eh->protect_list + = tree_cons (NULL_TREE, NULL_TREE, cfun->eh->protect_list); } /* Start a new exception region for a region of code that has a @@ -1121,1819 +986,1886 @@ add_partial_entry (handler) { expand_eh_region_start (); - /* Because this is a cleanup action, we may have to protect the handler - with __terminate. */ - handler = protect_with_terminate (handler); - + /* ??? This comment was old before the most recent rewrite. We + really ought to fix the callers at some point. */ /* For backwards compatibility, we allow callers to omit calls to begin_protect_partials for the outermost region. So, we must explicitly do so here. */ - if (!protect_list) + if (!cfun->eh->protect_list) begin_protect_partials (); /* Add this entry to the front of the list. */ - TREE_VALUE (protect_list) - = tree_cons (NULL_TREE, handler, TREE_VALUE (protect_list)); + TREE_VALUE (cfun->eh->protect_list) + = tree_cons (NULL_TREE, handler, TREE_VALUE (cfun->eh->protect_list)); } -/* Emit code to get EH context to current function. */ +/* End all the pending exception regions on protect_list. */ -static rtx -call_get_eh_context () +void +end_protect_partials () { - static tree fn; - tree expr; - - if (fn == NULL_TREE) - { - tree fntype; - fn = get_identifier ("__get_eh_context"); - fntype = build_pointer_type (build_pointer_type - (build_pointer_type (void_type_node))); - fntype = build_function_type (fntype, NULL_TREE); - fn = build_decl (FUNCTION_DECL, fn, fntype); - DECL_EXTERNAL (fn) = 1; - TREE_PUBLIC (fn) = 1; - DECL_ARTIFICIAL (fn) = 1; - TREE_READONLY (fn) = 1; - make_decl_rtl (fn, NULL_PTR); - assemble_external (fn); + tree t; - ggc_add_tree_root (&fn, 1); - } + /* ??? This comment was old before the most recent rewrite. We + really ought to fix the callers at some point. */ + /* For backwards compatibility, we allow callers to omit the call to + begin_protect_partials for the outermost region. So, + PROTECT_LIST may be NULL. */ + if (!cfun->eh->protect_list) + return; - expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); - expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), - expr, NULL_TREE, NULL_TREE); - TREE_SIDE_EFFECTS (expr) = 1; + /* Pop the topmost entry. */ + t = TREE_VALUE (cfun->eh->protect_list); + cfun->eh->protect_list = TREE_CHAIN (cfun->eh->protect_list); - return copy_to_reg (expand_expr (expr, NULL_RTX, VOIDmode, 0)); + /* End all the exception regions. */ + for (; t; t = TREE_CHAIN (t)) + expand_eh_region_end_cleanup (TREE_VALUE (t)); } -/* Get a reference to the EH context. - We will only generate a register for the current function EH context here, - and emit a USE insn to mark that this is a EH context register. - - Later, emit_eh_context will emit needed call to __get_eh_context - in libgcc2, and copy the value to the register we have generated. */ - -rtx -get_eh_context () -{ - if (current_function_ehc == 0) - { - rtx insn; + +/* This section is for the exception handling specific optimization pass. */ - current_function_ehc = gen_reg_rtx (Pmode); - - insn = gen_rtx_USE (GET_MODE (current_function_ehc), - current_function_ehc); - insn = emit_insn_before (insn, get_first_nonparm_insn ()); - - REG_NOTES (insn) - = gen_rtx_EXPR_LIST (REG_EH_CONTEXT, current_function_ehc, - REG_NOTES (insn)); - } - return current_function_ehc; -} - -/* Get a reference to the dynamic handler chain. It points to the - pointer to the next element in the dynamic handler chain. It ends - when there are no more elements in the dynamic handler chain, when - the value is &top_elt from libgcc2.c. Immediately after the - pointer, is an area suitable for setjmp/longjmp when - DONT_USE_BUILTIN_SETJMP is defined, and an area suitable for - __builtin_setjmp/__builtin_longjmp when DONT_USE_BUILTIN_SETJMP - isn't defined. */ +/* Random access the exception region tree. It's just as simple to + collect the regions this way as in expand_eh_region_start, but + without having to realloc memory. */ -rtx -get_dynamic_handler_chain () +static void +collect_eh_region_array () { - rtx ehc, dhc, result; - - ehc = get_eh_context (); + struct eh_region **array, *i; - /* This is the offset of dynamic_handler_chain in the eh_context struct - declared in eh-common.h. If its location is change, change this offset */ - dhc = plus_constant (ehc, POINTER_SIZE / BITS_PER_UNIT); + i = cfun->eh->region_tree; + if (! i) + return; - result = copy_to_reg (dhc); + array = xcalloc (cfun->eh->last_region_number + 1, sizeof (*array)); + cfun->eh->region_array = array; - /* We don't want a copy of the dcc, but rather, the single dcc. */ - return gen_rtx_MEM (Pmode, result); + while (1) + { + array[i->region_number] = i; + + /* If there are sub-regions, process them. */ + if (i->inner) + i = i->inner; + /* If there are peers, process them. */ + else if (i->next_peer) + i = i->next_peer; + /* Otherwise, step back up the tree to the next peer. */ + else + { + do { + i = i->outer; + if (i == NULL) + return; + } while (i->next_peer == NULL); + i = i->next_peer; + } + } } -/* Get a reference to the dynamic cleanup chain. It points to the - pointer to the next element in the dynamic cleanup chain. - Immediately after the pointer, are two Pmode variables, one for a - pointer to a function that performs the cleanup action, and the - second, the argument to pass to that function. */ - -rtx -get_dynamic_cleanup_chain () +static void +resolve_fixup_regions () { - rtx dhc, dcc, result; - - dhc = get_dynamic_handler_chain (); - dcc = plus_constant (dhc, POINTER_SIZE / BITS_PER_UNIT); + int i, j, n = cfun->eh->last_region_number; - result = copy_to_reg (dcc); + for (i = 1; i <= n; ++i) + { + struct eh_region *fixup = cfun->eh->region_array[i]; + struct eh_region *cleanup; - /* We don't want a copy of the dcc, but rather, the single dcc. */ - return gen_rtx_MEM (Pmode, result); -} + if (! fixup || fixup->type != ERT_FIXUP) + continue; -#ifdef DONT_USE_BUILTIN_SETJMP -/* Generate code to evaluate X and jump to LABEL if the value is nonzero. - LABEL is an rtx of code CODE_LABEL, in this function. */ + for (j = 1; j <= n; ++j) + { + cleanup = cfun->eh->region_array[j]; + if (cleanup->type == ERT_CLEANUP + && cleanup->u.cleanup.exp == fixup->u.fixup.cleanup_exp) + break; + } + if (j > n) + abort (); -static void -jumpif_rtx (x, label) - rtx x; - rtx label; -{ - jumpif (make_tree (type_for_mode (GET_MODE (x), 0), x), label); + fixup->u.fixup.real_region = cleanup->outer; + } } -#endif - -/* Start a dynamic cleanup on the EH runtime dynamic cleanup stack. - We just need to create an element for the cleanup list, and push it - into the chain. - A dynamic cleanup is a cleanup action implied by the presence of an - element on the EH runtime dynamic cleanup stack that is to be - performed when an exception is thrown. The cleanup action is - performed by __sjthrow when an exception is thrown. Only certain - actions can be optimized into dynamic cleanup actions. For the - restrictions on what actions can be performed using this routine, - see expand_eh_region_start_tree. */ +/* Now that we've discovered what region actually encloses a fixup, + we can shuffle pointers and remove them from the tree. */ static void -start_dynamic_cleanup (func, arg) - tree func; - tree arg; +remove_fixup_regions () { - rtx dcc; - rtx new_func, new_arg; - rtx x, buf; - int size; - - /* We allocate enough room for a pointer to the function, and - one argument. */ - size = 2; + int i; - /* XXX, FIXME: The stack space allocated this way is too long lived, - but there is no allocation routine that allocates at the level of - the last binding contour. */ - buf = assign_stack_local (BLKmode, - GET_MODE_SIZE (Pmode)*(size+1), - 0); + for (i = cfun->eh->last_region_number; i > 0; --i) + { + struct eh_region *fixup = cfun->eh->region_array[i]; - buf = change_address (buf, Pmode, NULL_RTX); + if (! fixup) + continue; - /* Store dcc into the first word of the newly allocated buffer. */ + /* Allow GC to maybe free some memory. */ + if (fixup->type == ERT_CLEANUP) + fixup->u.cleanup.exp = NULL_TREE; - dcc = get_dynamic_cleanup_chain (); - emit_move_insn (buf, dcc); + if (fixup->type != ERT_FIXUP) + continue; - /* Store func and arg into the cleanup list element. */ + if (fixup->inner) + { + struct eh_region *parent, *p, **pp; - new_func = gen_rtx_MEM (Pmode, plus_constant (XEXP (buf, 0), - GET_MODE_SIZE (Pmode))); - new_arg = gen_rtx_MEM (Pmode, plus_constant (XEXP (buf, 0), - GET_MODE_SIZE (Pmode)*2)); - x = expand_expr (func, new_func, Pmode, 0); - if (x != new_func) - emit_move_insn (new_func, x); + parent = fixup->u.fixup.real_region; - x = expand_expr (arg, new_arg, Pmode, 0); - if (x != new_arg) - emit_move_insn (new_arg, x); + /* Fix up the children's parent pointers; find the end of + the list. */ + for (p = fixup->inner; ; p = p->next_peer) + { + p->outer = parent; + if (! p->next_peer) + break; + } - /* Update the cleanup chain. */ + /* In the tree of cleanups, only outer-inner ordering matters. + So link the children back in anywhere at the correct level. */ + if (parent) + pp = &parent->inner; + else + pp = &cfun->eh->region_tree; + p->next_peer = *pp; + *pp = fixup->inner; + fixup->inner = NULL; + } - x = force_operand (XEXP (buf, 0), dcc); - if (x != dcc) - emit_move_insn (dcc, x); + remove_eh_handler (fixup); + } } -/* Emit RTL to start a dynamic handler on the EH runtime dynamic - handler stack. This should only be used by expand_eh_region_start - or expand_eh_region_start_tree. */ +/* Turn NOTE_INSN_EH_REGION notes into REG_EH_REGION notes for each + can_throw instruction in the region. */ static void -start_dynamic_handler () +convert_from_eh_region_ranges_1 (pinsns, orig_sp, cur) + rtx *pinsns; + int *orig_sp; + int cur; { - rtx dhc, dcc; - rtx arg, buf; - int size; + int *sp = orig_sp; + rtx insn, next; -#ifndef DONT_USE_BUILTIN_SETJMP - /* The number of Pmode words for the setjmp buffer, when using the - builtin setjmp/longjmp, see expand_builtin, case BUILT_IN_LONGJMP. */ - /* We use 2 words here before calling expand_builtin_setjmp. - expand_builtin_setjmp uses 2 words, and then calls emit_stack_save. - emit_stack_save needs space of size STACK_SAVEAREA_MODE (SAVE_NONLOCAL). - Subtract one, because the assign_stack_local call below adds 1. */ - size = (2 + 2 + (GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL)) - / GET_MODE_SIZE (Pmode)) - - 1); -#else -#ifdef JMP_BUF_SIZE - size = JMP_BUF_SIZE; -#else - /* Should be large enough for most systems, if it is not, - JMP_BUF_SIZE should be defined with the proper value. It will - also tend to be larger than necessary for most systems, a more - optimal port will define JMP_BUF_SIZE. */ - size = FIRST_PSEUDO_REGISTER+2; -#endif -#endif - /* XXX, FIXME: The stack space allocated this way is too long lived, - but there is no allocation routine that allocates at the level of - the last binding contour. */ - arg = assign_stack_local (BLKmode, - GET_MODE_SIZE (Pmode)*(size+1), - 0); + for (insn = *pinsns; insn ; insn = next) + { + next = NEXT_INSN (insn); + if (GET_CODE (insn) == NOTE) + { + int kind = NOTE_LINE_NUMBER (insn); + if (kind == NOTE_INSN_EH_REGION_BEG + || kind == NOTE_INSN_EH_REGION_END) + { + if (kind == NOTE_INSN_EH_REGION_BEG) + { + struct eh_region *r; - arg = change_address (arg, Pmode, NULL_RTX); + *sp++ = cur; + cur = NOTE_EH_HANDLER (insn); - /* Store dhc into the first word of the newly allocated buffer. */ + r = cfun->eh->region_array[cur]; + if (r->type == ERT_FIXUP) + { + r = r->u.fixup.real_region; + cur = r ? r->region_number : 0; + } + else if (r->type == ERT_CATCH) + { + r = r->outer; + cur = r ? r->region_number : 0; + } + } + else + cur = *--sp; + + /* Removing the first insn of a CALL_PLACEHOLDER sequence + requires extra care to adjust sequence start. */ + if (insn == *pinsns) + *pinsns = next; + remove_insn (insn); + continue; + } + } + else if (INSN_P (insn)) + { + if (cur > 0 + && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX) + /* Calls can always potentially throw exceptions, unless + they have a REG_EH_REGION note with a value of 0 or less. + Which should be the only possible kind so far. */ + && (GET_CODE (insn) == CALL_INSN + /* If we wanted exceptions for non-call insns, then + any may_trap_p instruction could throw. */ + || (flag_non_call_exceptions + && may_trap_p (PATTERN (insn))))) + { + REG_NOTES (insn) = alloc_EXPR_LIST (REG_EH_REGION, GEN_INT (cur), + REG_NOTES (insn)); + } - dhc = get_dynamic_handler_chain (); - dcc = gen_rtx_MEM (Pmode, plus_constant (XEXP (arg, 0), - GET_MODE_SIZE (Pmode))); - emit_move_insn (arg, dhc); + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) + { + convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 0), + sp, cur); + convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 1), + sp, cur); + convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 2), + sp, cur); + } + } + } - /* Zero out the start of the cleanup chain. */ - emit_move_insn (dcc, const0_rtx); + if (sp != orig_sp) + abort (); +} - /* The jmpbuf starts two words into the area allocated. */ - buf = plus_constant (XEXP (arg, 0), GET_MODE_SIZE (Pmode)*2); +void +convert_from_eh_region_ranges () +{ + int *stack; + rtx insns; -#ifdef DONT_USE_BUILTIN_SETJMP - { - rtx x; - x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_CONST, - TYPE_MODE (integer_type_node), 1, - buf, Pmode); - /* If we come back here for a catch, transfer control to the handler. */ - jumpif_rtx (x, ehstack.top->entry->exception_handler_label); - } -#else - expand_builtin_setjmp_setup (buf, - ehstack.top->entry->exception_handler_label); -#endif + collect_eh_region_array (); + resolve_fixup_regions (); - /* We are committed to this, so update the handler chain. */ + stack = xmalloc (sizeof (int) * (cfun->eh->last_region_number + 1)); + insns = get_insns (); + convert_from_eh_region_ranges_1 (&insns, stack, 0); + free (stack); - emit_move_insn (dhc, force_operand (XEXP (arg, 0), NULL_RTX)); + remove_fixup_regions (); } -/* Start an exception handling region for the given cleanup action. - All instructions emitted after this point are considered to be part - of the region until expand_eh_region_end is invoked. CLEANUP is - the cleanup action to perform. The return value is true if the - exception region was optimized away. If that case, - expand_eh_region_end does not need to be called for this cleanup, - nor should it be. - - This routine notices one particular common case in C++ code - generation, and optimizes it so as to not need the exception - region. It works by creating a dynamic cleanup action, instead of - a using an exception region. */ - -int -expand_eh_region_start_tree (decl, cleanup) - tree decl; - tree cleanup; +void +find_exception_handler_labels () { - /* This is the old code. */ - if (! doing_eh (0)) - return 0; + rtx list = NULL_RTX; + int i; - /* The optimization only applies to actions protected with - terminate, and only applies if we are using the setjmp/longjmp - codegen method. */ - if (USING_SJLJ_EXCEPTIONS - && protect_cleanup_actions_with_terminate) - { - tree func, arg; - tree args; + free_EXPR_LIST_list (&exception_handler_labels); - /* Ignore any UNSAVE_EXPR. */ - if (TREE_CODE (cleanup) == UNSAVE_EXPR) - cleanup = TREE_OPERAND (cleanup, 0); - - /* Further, it only applies if the action is a call, if there - are 2 arguments, and if the second argument is 2. */ + if (cfun->eh->region_tree == NULL) + return; - if (TREE_CODE (cleanup) == CALL_EXPR - && (args = TREE_OPERAND (cleanup, 1)) - && (func = TREE_OPERAND (cleanup, 0)) - && (arg = TREE_VALUE (args)) - && (args = TREE_CHAIN (args)) + for (i = cfun->eh->last_region_number; i > 0; --i) + { + struct eh_region *region = cfun->eh->region_array[i]; + rtx lab; - /* is the second argument 2? */ - && TREE_CODE (TREE_VALUE (args)) == INTEGER_CST - && compare_tree_int (TREE_VALUE (args), 2) == 0 + if (! region) + continue; + if (cfun->eh->built_landing_pads) + lab = region->landing_pad; + else + lab = region->label; - /* Make sure there are no other arguments. */ - && TREE_CHAIN (args) == NULL_TREE) - { - /* Arrange for returns and gotos to pop the entry we make on the - dynamic cleanup stack. */ - expand_dcc_cleanup (decl); - start_dynamic_cleanup (func, arg); - return 1; - } + if (lab) + list = alloc_EXPR_LIST (0, lab, list); } - expand_eh_region_start_for_decl (decl); - ehstack.top->entry->finalization = cleanup; + /* For sjlj exceptions, need the return label to remain live until + after landing pad generation. */ + if (USING_SJLJ_EXCEPTIONS && ! cfun->eh->built_landing_pads) + list = alloc_EXPR_LIST (0, return_label, list); - return 0; + exception_handler_labels = list; } -/* Just like expand_eh_region_start, except if a cleanup action is - entered on the cleanup chain, the TREE_PURPOSE of the element put - on the chain is DECL. DECL should be the associated VAR_DECL, if - any, otherwise it should be NULL_TREE. */ - -void -expand_eh_region_start_for_decl (decl) - tree decl; + +static struct eh_region * +duplicate_eh_region_1 (o, map) + struct eh_region *o; + struct inline_remap *map; { - rtx note; + struct eh_region *n + = (struct eh_region *) xcalloc (1, sizeof (struct eh_region)); - /* This is the old code. */ - if (! doing_eh (0)) - return; + n->region_number = o->region_number + cfun->eh->last_region_number; + n->type = o->type; - /* We need a new block to record the start and end of the - dynamic handler chain. We also want to prevent jumping into - a try block. */ - expand_start_bindings (2); + switch (n->type) + { + case ERT_CLEANUP: + case ERT_MUST_NOT_THROW: + break; - /* But we don't need or want a new temporary level. */ - pop_temp_slots (); + case ERT_TRY: + if (o->u.try.continue_label) + n->u.try.continue_label + = get_label_from_map (map, + CODE_LABEL_NUMBER (o->u.try.continue_label)); + break; - /* Mark this block as created by expand_eh_region_start. This - is so that we can pop the block with expand_end_bindings - automatically. */ - mark_block_as_eh_region (); + case ERT_CATCH: + n->u.catch.type = o->u.catch.type; + break; - if (USING_SJLJ_EXCEPTIONS) + case ERT_ALLOWED_EXCEPTIONS: + n->u.allowed.type_list = o->u.allowed.type_list; + break; + + case ERT_THROW: + n->u.throw.type = o->u.throw.type; + + default: + abort (); + } + + if (o->label) + n->label = get_label_from_map (map, CODE_LABEL_NUMBER (o->label)); + if (o->last) { - /* Arrange for returns and gotos to pop the entry we make on the - dynamic handler stack. */ - expand_dhc_cleanup (decl); + n->last = map->insn_map[INSN_UID (o->last)]; + if (n->last == NULL) + abort (); } - push_eh_entry (&ehstack); - note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG); - NOTE_EH_HANDLER (note) - = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label); - if (USING_SJLJ_EXCEPTIONS) - start_dynamic_handler (); + return n; } -/* Start an exception handling region. All instructions emitted after - this point are considered to be part of the region until - expand_eh_region_end is invoked. */ - -void -expand_eh_region_start () +static void +duplicate_eh_region_2 (o, n_array) + struct eh_region *o; + struct eh_region **n_array; { - expand_eh_region_start_for_decl (NULL_TREE); -} + struct eh_region *n = n_array[o->region_number]; -/* End an exception handling region. The information about the region - is found on the top of ehstack. + switch (n->type) + { + case ERT_TRY: + n->u.try.catch = n_array[o->u.try.catch->region_number]; + n->u.try.last_catch = n_array[o->u.try.last_catch->region_number]; + break; - HANDLER is either the cleanup for the exception region, or if we're - marking the end of a try block, HANDLER is integer_zero_node. + case ERT_CATCH: + if (o->u.catch.next_catch) + n->u.catch.next_catch = n_array[o->u.catch.next_catch->region_number]; + if (o->u.catch.prev_catch) + n->u.catch.prev_catch = n_array[o->u.catch.prev_catch->region_number]; + break; - HANDLER will be transformed to rtl when expand_leftover_cleanups - is invoked. */ + default: + break; + } -void -expand_eh_region_end (handler) - tree handler; + if (o->outer) + n->outer = n_array[o->outer->region_number]; + if (o->inner) + n->inner = n_array[o->inner->region_number]; + if (o->next_peer) + n->next_peer = n_array[o->next_peer->region_number]; +} + +int +duplicate_eh_regions (ifun, map) + struct function *ifun; + struct inline_remap *map; { - struct eh_entry *entry; - struct eh_node *node; - rtx note; - int ret, r; + int ifun_last_region_number = ifun->eh->last_region_number; + struct eh_region **n_array, *root, *cur; + int i; - if (! doing_eh (0)) - return; + if (ifun_last_region_number == 0) + return 0; - entry = pop_eh_entry (&ehstack); + n_array = xcalloc (ifun_last_region_number + 1, sizeof (*n_array)); - note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END); - ret = NOTE_EH_HANDLER (note) - = CODE_LABEL_NUMBER (entry->exception_handler_label); - if (USING_SJLJ_EXCEPTIONS == 0 && ! flag_new_exceptions - /* We share outer_context between regions; only emit it once. */ - && INSN_UID (entry->outer_context) == 0) + for (i = 1; i <= ifun_last_region_number; ++i) { - rtx label; - - label = gen_label_rtx (); - emit_jump (label); - - /* Emit a label marking the end of this exception region that - is used for rethrowing into the outer context. */ - emit_label (entry->outer_context); - expand_internal_throw (); - - emit_label (label); + cur = ifun->eh->region_array[i]; + if (!cur || cur->region_number != i) + continue; + n_array[i] = duplicate_eh_region_1 (cur, map); } - - entry->finalization = handler; - - /* create region entry in final exception table */ - r = new_eh_region_entry (NOTE_EH_HANDLER (note), entry->rethrow_label); - - enqueue_eh_entry (ehqueue, entry); - - /* If we have already started ending the bindings, don't recurse. */ - if (is_eh_region ()) + for (i = 1; i <= ifun_last_region_number; ++i) { - /* Because we don't need or want a new temporary level and - because we didn't create one in expand_eh_region_start, - create a fake one now to avoid removing one in - expand_end_bindings. */ - push_temp_slots (); + cur = ifun->eh->region_array[i]; + if (!cur || cur->region_number != i) + continue; + duplicate_eh_region_2 (cur, n_array); + } - mark_block_as_not_eh_region (); + root = n_array[ifun->eh->region_tree->region_number]; + cur = cfun->eh->cur_region; + if (cur) + { + struct eh_region *p = cur->inner; + if (p) + { + while (p->next_peer) + p = p->next_peer; + p->next_peer = root; + } + else + cur->inner = root; - expand_end_bindings (NULL_TREE, 0, 0); + for (i = 1; i <= ifun_last_region_number; ++i) + if (n_array[i]->outer == NULL) + n_array[i]->outer = cur; + } + else + { + struct eh_region *p = cfun->eh->region_tree; + if (p) + { + while (p->next_peer) + p = p->next_peer; + p->next_peer = root; + } + else + cfun->eh->region_tree = root; } - /* Go through the goto handlers in the queue, emitting their - handlers if we now have enough information to do so. */ - for (node = ehqueue->head; node; node = node->chain) - if (node->entry->goto_entry_p - && node->entry->outer_context == entry->rethrow_label) - emit_cleanup_handler (node->entry); + free (n_array); - /* We can't emit handlers for goto entries until their scopes are - complete because we don't know where they need to rethrow to, - yet. */ - if (entry->finalization != integer_zero_node - && (!entry->goto_entry_p - || find_func_region_from_symbol (entry->outer_context) != -1)) - emit_cleanup_handler (entry); + i = cfun->eh->last_region_number; + cfun->eh->last_region_number = i + ifun_last_region_number; + return i; } -/* End the EH region for a goto fixup. We only need them in the region-based - EH scheme. */ + +/* ??? Move from tree.c to tree.h. */ +#define TYPE_HASH(TYPE) ((HOST_WIDE_INT) (TYPE) & 0777777) -void -expand_fixup_region_start () +static int +t2r_eq (pentry, pdata) + const PTR pentry; + const PTR pdata; { - if (! doing_eh (0) || USING_SJLJ_EXCEPTIONS) - return; + tree entry = (tree) pentry; + tree data = (tree) pdata; - expand_eh_region_start (); - /* Mark this entry as the entry for a goto. */ - ehstack.top->entry->goto_entry_p = 1; + return TREE_PURPOSE (entry) == data; } -/* End the EH region for a goto fixup. CLEANUP is the cleanup we just - expanded; to avoid running it twice if it throws, we look through the - ehqueue for a matching region and rethrow from its outer_context. */ +static hashval_t +t2r_hash (pentry) + const PTR pentry; +{ + tree entry = (tree) pentry; + return TYPE_HASH (TREE_PURPOSE (entry)); +} -void -expand_fixup_region_end (cleanup) - tree cleanup; +static int +t2r_mark_1 (slot, data) + PTR *slot; + PTR data ATTRIBUTE_UNUSED; { - struct eh_node *node; - int dont_issue; + tree contents = (tree) *slot; + ggc_mark_tree (contents); + return 1; +} - if (! doing_eh (0) || USING_SJLJ_EXCEPTIONS) - return; +static void +t2r_mark (addr) + PTR addr; +{ + htab_traverse (*(htab_t *)addr, t2r_mark_1, NULL); +} - for (node = ehstack.top; node && node->entry->finalization != cleanup; ) - node = node->chain; - if (node == 0) - for (node = ehqueue->head; node && node->entry->finalization != cleanup; ) - node = node->chain; - if (node == 0) - abort (); +static void +add_type_for_runtime (type) + tree type; +{ + tree *slot; + + slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type, + TYPE_HASH (type), INSERT); + if (*slot == NULL) + { + tree runtime = (*lang_eh_runtime_type) (type); + *slot = tree_cons (type, runtime, NULL_TREE); + } +} + +static tree +lookup_type_for_runtime (type) + tree type; +{ + tree *slot; - /* If the outer context label has not been issued yet, we don't want - to issue it as a part of this region, unless this is the - correct region for the outer context. If we did, then the label for - the outer context will be WITHIN the begin/end labels, - and we could get an infinte loop when it tried to rethrow, or just - generally incorrect execution following a throw. */ + slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type, + TYPE_HASH (type), NO_INSERT); - if (flag_new_exceptions) - dont_issue = 0; - else - dont_issue = ((INSN_UID (node->entry->outer_context) == 0) - && (ehstack.top->entry != node->entry)); + /* We should have always inserrted the data earlier. */ + return TREE_VALUE (*slot); +} - ehstack.top->entry->outer_context = node->entry->outer_context; + +/* Represent an entry in @TTypes for either catch actions + or exception filter actions. */ +struct ttypes_filter +{ + tree t; + int filter; +}; - /* Since we are rethrowing to the OUTER region, we know we don't need - a jump around sequence for this region, so we'll pretend the outer - context label has been issued by setting INSN_UID to 1, then clearing - it again afterwards. */ +/* Compare ENTRY (a ttypes_filter entry in the hash table) with DATA + (a tree) for a @TTypes type node we are thinking about adding. */ - if (dont_issue) - INSN_UID (node->entry->outer_context) = 1; +static int +ttypes_filter_eq (pentry, pdata) + const PTR pentry; + const PTR pdata; +{ + const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry; + tree data = (tree) pdata; - /* Just rethrow. size_zero_node is just a NOP. */ - expand_eh_region_end (size_zero_node); + return entry->t == data; +} - if (dont_issue) - INSN_UID (node->entry->outer_context) = 0; +static hashval_t +ttypes_filter_hash (pentry) + const PTR pentry; +{ + const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry; + return TYPE_HASH (entry->t); } -/* If we are using the setjmp/longjmp EH codegen method, we emit a - call to __sjthrow. Otherwise, we emit a call to __throw. */ +/* Compare ENTRY with DATA (both struct ttypes_filter) for a @TTypes + exception specification list we are thinking about adding. */ +/* ??? Currently we use the type lists in the order given. Someone + should put these in some canonical order. */ -void -emit_throw () +static int +ehspec_filter_eq (pentry, pdata) + const PTR pentry; + const PTR pdata; { - if (USING_SJLJ_EXCEPTIONS) - { - emit_library_call (sjthrow_libfunc, 0, VOIDmode, 0); - } - else - { -#ifdef JUMP_TO_THROW - emit_indirect_jump (throw_libfunc); -#else - emit_library_call (throw_libfunc, 0, VOIDmode, 0); -#endif - } - emit_barrier (); + const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry; + const struct ttypes_filter *data = (const struct ttypes_filter *) pdata; + + return type_list_equal (entry->t, data->t); } -/* Throw the current exception. If appropriate, this is done by jumping - to the next handler. */ +/* Hash function for exception specification lists. */ -void -expand_internal_throw () +static hashval_t +ehspec_filter_hash (pentry) + const PTR pentry; { - emit_throw (); + const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry; + hashval_t h = 0; + tree list; + + for (list = entry->t; list ; list = TREE_CHAIN (list)) + h = (h << 5) + (h >> 27) + TYPE_HASH (TREE_VALUE (list)); + return h; } -/* Called from expand_exception_blocks and expand_end_catch_block to - emit any pending handlers/cleanups queued from expand_eh_region_end. */ +/* Add TYPE to cfun->eh->ttype_data, using TYPES_HASH to speed + up the search. Return the filter value to be used. */ -void -expand_leftover_cleanups () +static int +add_ttypes_entry (ttypes_hash, type) + htab_t ttypes_hash; + tree type; { - struct eh_entry *entry; + struct ttypes_filter **slot, *n; + + slot = (struct ttypes_filter **) + htab_find_slot_with_hash (ttypes_hash, type, TYPE_HASH (type), INSERT); - for (entry = dequeue_eh_entry (ehqueue); - entry; - entry = dequeue_eh_entry (ehqueue)) + if ((n = *slot) == NULL) { - /* A leftover try block. Shouldn't be one here. */ - if (entry->finalization == integer_zero_node) - abort (); + /* Filter value is a 1 based table index. */ - free (entry); - } -} + n = (struct ttypes_filter *) xmalloc (sizeof (*n)); + n->t = type; + n->filter = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) + 1; + *slot = n; -/* Called at the start of a block of try statements. */ -void -expand_start_try_stmts () -{ - if (! doing_eh (1)) - return; + VARRAY_PUSH_TREE (cfun->eh->ttype_data, type); + } - expand_eh_region_start (); + return n->filter; } -/* Called to begin a catch clause. The parameter is the object which - will be passed to the runtime type check routine. */ -void -start_catch_handler (rtime) - tree rtime; +/* Add LIST to cfun->eh->ehspec_data, using EHSPEC_HASH and TYPES_HASH + to speed up the search. Return the filter value to be used. */ + +static int +add_ehspec_entry (ehspec_hash, ttypes_hash, list) + htab_t ehspec_hash; + htab_t ttypes_hash; + tree list; { - rtx handler_label; - int insn_region_num; - int eh_region_entry; + struct ttypes_filter **slot, *n; + struct ttypes_filter dummy; - if (! doing_eh (1)) - return; + dummy.t = list; + slot = (struct ttypes_filter **) + htab_find_slot (ehspec_hash, &dummy, INSERT); - handler_label = catchstack.top->entry->exception_handler_label; - insn_region_num = CODE_LABEL_NUMBER (handler_label); - eh_region_entry = find_func_region (insn_region_num); + if ((n = *slot) == NULL) + { + /* Filter value is a -1 based byte index into a uleb128 buffer. */ + + n = (struct ttypes_filter *) xmalloc (sizeof (*n)); + n->t = list; + n->filter = -(VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) + 1); + *slot = n; + + /* Look up each type in the list and encode its filter + value as a uleb128. Terminate the list with 0. */ + for (; list ; list = TREE_CHAIN (list)) + push_uleb128 (&cfun->eh->ehspec_data, + add_ttypes_entry (ttypes_hash, TREE_VALUE (list))); + VARRAY_PUSH_UCHAR (cfun->eh->ehspec_data, 0); + } - /* If we've already issued this label, pick a new one */ - if (catchstack.top->entry->label_used) - handler_label = gen_exception_label (); - else - catchstack.top->entry->label_used = 1; + return n->filter; +} - receive_exception_label (handler_label); +/* Generate the action filter values to be used for CATCH and + ALLOWED_EXCEPTIONS regions. When using dwarf2 exception regions, + we use lots of landing pads, and so every type or list can share + the same filter value, which saves table space. */ - add_new_handler (eh_region_entry, get_new_handler (handler_label, rtime)); +static void +assign_filter_values () +{ + int i; + htab_t ttypes, ehspec; - if (flag_new_exceptions && ! USING_SJLJ_EXCEPTIONS) - return; + VARRAY_TREE_INIT (cfun->eh->ttype_data, 16, "ttype_data"); + VARRAY_UCHAR_INIT (cfun->eh->ehspec_data, 64, "ehspec_data"); - /* Under the old mechanism, as well as setjmp/longjmp, we need to - issue code to compare 'rtime' to the value in eh_info, via the - matching function in eh_info. If its is false, we branch around - the handler we are about to issue. */ + ttypes = htab_create (31, ttypes_filter_hash, ttypes_filter_eq, free); + ehspec = htab_create (31, ehspec_filter_hash, ehspec_filter_eq, free); - if (rtime != NULL_TREE && rtime != CATCH_ALL_TYPE) + for (i = cfun->eh->last_region_number; i > 0; --i) { - rtx call_rtx, rtime_address; + struct eh_region *r = cfun->eh->region_array[i]; - if (catchstack.top->entry->false_label != NULL_RTX) - { - error ("Never issued previous false_label"); - abort (); - } - catchstack.top->entry->false_label = gen_exception_label (); + /* Mind we don't process a region more than once. */ + if (!r || r->region_number != i) + continue; - rtime_address = expand_expr (rtime, NULL_RTX, Pmode, EXPAND_INITIALIZER); -#ifdef POINTERS_EXTEND_UNSIGNED - rtime_address = convert_memory_address (Pmode, rtime_address); -#endif - rtime_address = force_reg (Pmode, rtime_address); + switch (r->type) + { + case ERT_CATCH: + r->u.catch.filter = add_ttypes_entry (ttypes, r->u.catch.type); + break; - /* Now issue the call, and branch around handler if needed */ - call_rtx = emit_library_call_value (eh_rtime_match_libfunc, NULL_RTX, - LCT_NORMAL, - TYPE_MODE (integer_type_node), - 1, rtime_address, Pmode); + case ERT_ALLOWED_EXCEPTIONS: + r->u.allowed.filter + = add_ehspec_entry (ehspec, ttypes, r->u.allowed.type_list); + break; - /* Did the function return true? */ - emit_cmp_and_jump_insns (call_rtx, const0_rtx, EQ, NULL_RTX, - GET_MODE (call_rtx), 0, 0, - catchstack.top->entry->false_label); + default: + break; + } } -} -/* Called to end a catch clause. If we aren't using the new exception - model tabel mechanism, we need to issue the branch-around label - for the end of the catch block. */ + htab_delete (ttypes); + htab_delete (ehspec); +} -void -end_catch_handler () +static void +build_post_landing_pads () { - if (! doing_eh (1)) - return; + int i; - if (flag_new_exceptions && ! USING_SJLJ_EXCEPTIONS) + for (i = cfun->eh->last_region_number; i > 0; --i) { - emit_barrier (); - return; - } - - /* A NULL label implies the catch clause was a catch all or cleanup */ - if (catchstack.top->entry->false_label == NULL_RTX) - return; + struct eh_region *region = cfun->eh->region_array[i]; + rtx seq; - emit_label (catchstack.top->entry->false_label); - catchstack.top->entry->false_label = NULL_RTX; -} + /* Mind we don't process a region more than once. */ + if (!region || region->region_number != i) + continue; -/* Save away the current ehqueue. */ + switch (region->type) + { + case ERT_TRY: + /* ??? Collect the set of all non-overlapping catch handlers + all the way up the chain until blocked by a cleanup. */ + /* ??? Outer try regions can share landing pads with inner + try regions if the types are completely non-overlapping, + and there are no interveaning cleanups. */ -void -push_ehqueue () -{ - struct eh_queue *q; - q = (struct eh_queue *) xcalloc (1, sizeof (struct eh_queue)); - q->next = ehqueue; - ehqueue = q; -} + region->post_landing_pad = gen_label_rtx (); -/* Restore a previously pushed ehqueue. */ + start_sequence (); -void -pop_ehqueue () -{ - struct eh_queue *q; - expand_leftover_cleanups (); - q = ehqueue->next; - free (ehqueue); - ehqueue = q; -} + emit_label (region->post_landing_pad); + + /* ??? It is mighty inconvenient to call back into the + switch statement generation code in expand_end_case. + Rapid prototyping sez a sequence of ifs. */ + { + struct eh_region *c; + for (c = region->u.try.catch; c ; c = c->u.catch.next_catch) + { + /* ??? _Unwind_ForcedUnwind wants no match here. */ + if (c->u.catch.type == NULL) + emit_jump (c->label); + else + emit_cmp_and_jump_insns (cfun->eh->filter, + GEN_INT (c->u.catch.filter), + EQ, NULL_RTX, word_mode, + 0, 0, c->label); + } + } -/* Emit the handler specified by ENTRY. */ + seq = get_insns (); + end_sequence (); -static void -emit_cleanup_handler (entry) - struct eh_entry *entry; -{ - rtx prev; - rtx handler_insns; + region->last = emit_insns_before (seq, region->u.try.catch->label); + break; - /* Since the cleanup could itself contain try-catch blocks, we - squirrel away the current queue and replace it when we are done - with this function. */ - push_ehqueue (); + case ERT_ALLOWED_EXCEPTIONS: + region->post_landing_pad = gen_label_rtx (); - /* Put these handler instructions in a sequence. */ - do_pending_stack_adjust (); - start_sequence (); + start_sequence (); - /* Emit the label for the cleanup handler for this region, and - expand the code for the handler. - - Note that a catch region is handled as a side-effect here; for a - try block, entry->finalization will contain integer_zero_node, so - no code will be generated in the expand_expr call below. But, the - label for the handler will still be emitted, so any code emitted - after this point will end up being the handler. */ - - receive_exception_label (entry->exception_handler_label); + emit_label (region->post_landing_pad); - /* register a handler for this cleanup region */ - add_new_handler (find_func_region (CODE_LABEL_NUMBER (entry->exception_handler_label)), - get_new_handler (entry->exception_handler_label, NULL)); + emit_cmp_and_jump_insns (cfun->eh->filter, + GEN_INT (region->u.allowed.filter), + EQ, NULL_RTX, word_mode, 0, 0, + region->label); - /* And now generate the insns for the cleanup handler. */ - expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); + seq = get_insns (); + end_sequence (); - prev = get_last_insn (); - if (prev == NULL || GET_CODE (prev) != BARRIER) - /* Code to throw out to outer context when we fall off end of the - handler. We can't do this here for catch blocks, so it's done - in expand_end_all_catch instead. */ - expand_rethrow (entry->outer_context); + region->last = emit_insns_before (seq, region->label); + break; - /* Finish this sequence. */ - do_pending_stack_adjust (); - handler_insns = get_insns (); - end_sequence (); + case ERT_CLEANUP: + case ERT_MUST_NOT_THROW: + region->post_landing_pad = region->label; + break; - /* And add it to the CATCH_CLAUSES. */ - push_to_full_sequence (catch_clauses, catch_clauses_last); - emit_insns (handler_insns); - end_full_sequence (&catch_clauses, &catch_clauses_last); + case ERT_CATCH: + case ERT_THROW: + /* Nothing to do. */ + break; - /* Now we've left the handler. */ - pop_ehqueue (); + default: + abort (); + } + } } -/* Generate RTL for the start of a group of catch clauses. +static void +connect_post_landing_pads () +{ + int i; - It is responsible for starting a new instruction sequence for the - instructions in the catch block, and expanding the handlers for the - internally-generated exception regions nested within the try block - corresponding to this catch block. */ + for (i = cfun->eh->last_region_number; i > 0; --i) + { + struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *outer; + rtx before = NULL_RTX, after = NULL_RTX, seq; -void -expand_start_all_catch () -{ - struct eh_entry *entry; - tree label; - rtx outer_context; + /* Mind we don't process a region more than once. */ + if (!region || region->region_number != i) + continue; - if (! doing_eh (1)) - return; + switch (region->type) + { + case ERT_CLEANUP: + after = region->last; + if (GET_CODE (after) == BARRIER + && GET_CODE (PREV_INSN (after)) == JUMP_INSN + && GET_CODE (PATTERN (PREV_INSN (after))) == RESX) + { + before = PREV_INSN (after); + after = NULL_RTX; + } + break; - outer_context = ehstack.top->entry->outer_context; + case ERT_TRY: + after = region->last; + break; - /* End the try block. */ - expand_eh_region_end (integer_zero_node); + case ERT_ALLOWED_EXCEPTIONS: + before = region->label; + break; - emit_line_note (input_filename, lineno); - label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + case ERT_MUST_NOT_THROW: + case ERT_CATCH: + case ERT_THROW: + continue; - /* The label for the exception handling block that we will save. - This is Lresume in the documentation. */ - expand_label (label); - - /* Push the label that points to where normal flow is resumed onto - the top of the label stack. */ - push_label_entry (&caught_return_label_stack, NULL_RTX, label); + default: + abort (); + } - /* Start a new sequence for all the catch blocks. We will add this - to the global sequence catch_clauses when we have completed all - the handlers in this handler-seq. */ - start_sequence (); + /* If there's no fallthru, no need to add branches. */ + if (after && GET_CODE (after) == BARRIER) + continue; - /* Throw away entries in the queue that we won't need anymore. We - need entries for regions that have ended but to which there might - still be gotos pending. */ - for (entry = dequeue_eh_entry (ehqueue); - entry->finalization != integer_zero_node; - entry = dequeue_eh_entry (ehqueue)) - free (entry); + /* Search for another landing pad in this function. */ + for (outer = region->outer; outer ; outer = outer->outer) + if (outer->post_landing_pad) + break; - /* At this point, all the cleanups are done, and the ehqueue now has - the current exception region at its head. We dequeue it, and put it - on the catch stack. */ - push_entry (&catchstack, entry); + start_sequence (); - /* If we are not doing setjmp/longjmp EH, because we are reordered - out of line, we arrange to rethrow in the outer context. We need to - do this because we are not physically within the region, if any, that - logically contains this catch block. */ - if (! USING_SJLJ_EXCEPTIONS) - { - expand_eh_region_start (); - ehstack.top->entry->outer_context = outer_context; - } + if (outer) + emit_jump (outer->post_landing_pad); + else + emit_library_call (unwind_resume_libfunc, LCT_NORETURN, + VOIDmode, 1, cfun->eh->exc_ptr, Pmode); + seq = get_insns (); + end_sequence (); + if (before) + emit_insns_before (seq, before); + else + emit_insns_after (seq, after); + } } -/* Finish up the catch block. At this point all the insns for the - catch clauses have already been generated, so we only have to add - them to the catch_clauses list. We also want to make sure that if - we fall off the end of the catch clauses that we rethrow to the - outer EH region. */ - -void -expand_end_all_catch () + +static void +dw2_build_landing_pads () { - rtx new_catch_clause; - struct eh_entry *entry; + int i, j; - if (! doing_eh (1)) - return; + for (i = cfun->eh->last_region_number; i > 0; --i) + { + struct eh_region *region = cfun->eh->region_array[i]; + rtx seq; - /* Dequeue the current catch clause region. */ - entry = pop_eh_entry (&catchstack); - free (entry); + /* Mind we don't process a region more than once. */ + if (!region || region->region_number != i) + continue; - if (! USING_SJLJ_EXCEPTIONS) - { - rtx outer_context = ehstack.top->entry->outer_context; + if (region->type != ERT_CLEANUP + && region->type != ERT_TRY + && region->type != ERT_ALLOWED_EXCEPTIONS) + continue; - /* Finish the rethrow region. size_zero_node is just a NOP. */ - expand_eh_region_end (size_zero_node); - /* New exceptions handling models will never have a fall through - of a catch clause */ - if (!flag_new_exceptions) - expand_rethrow (outer_context); - } - else - expand_rethrow (NULL_RTX); + start_sequence (); - /* Code to throw out to outer context, if we fall off end of catch - handlers. This is rethrow (Lresume, same id, same obj) in the - documentation. We use Lresume because we know that it will throw - to the correct context. + region->landing_pad = gen_label_rtx (); + emit_label (region->landing_pad); - In other words, if the catch handler doesn't exit or return, we - do a "throw" (using the address of Lresume as the point being - thrown from) so that the outer EH region can then try to process - the exception. */ +#ifdef HAVE_exception_receiver + if (HAVE_exception_receiver) + emit_insn (gen_exception_receiver ()); + else +#endif +#ifdef HAVE_nonlocal_goto_receiver + if (HAVE_nonlocal_goto_receiver) + emit_insn (gen_nonlocal_goto_receiver ()); + else +#endif + { /* Nothing */ } - /* Now we have the complete catch sequence. */ - new_catch_clause = get_insns (); - end_sequence (); - - /* This level of catch blocks is done, so set up the successful - catch jump label for the next layer of catch blocks. */ - pop_label_entry (&caught_return_label_stack); - pop_label_entry (&outer_context_label_stack); - - /* Add the new sequence of catches to the main one for this function. */ - push_to_full_sequence (catch_clauses, catch_clauses_last); - emit_insns (new_catch_clause); - end_full_sequence (&catch_clauses, &catch_clauses_last); - - /* Here we fall through into the continuation code. */ -} + /* If the eh_return data registers are call-saved, then we + won't have considered them clobbered from the call that + threw. Kill them now. */ + for (j = 0; ; ++j) + { + unsigned r = EH_RETURN_DATA_REGNO (j); + if (r == INVALID_REGNUM) + break; + if (! call_used_regs[r]) + emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, r))); + } -/* Rethrow from the outer context LABEL. */ + emit_move_insn (cfun->eh->exc_ptr, + gen_rtx_REG (Pmode, EH_RETURN_DATA_REGNO (0))); + emit_move_insn (cfun->eh->filter, + gen_rtx_REG (Pmode, EH_RETURN_DATA_REGNO (1))); -static void -expand_rethrow (label) - rtx label; -{ - if (USING_SJLJ_EXCEPTIONS) - emit_throw (); - else - if (flag_new_exceptions) - { - rtx insn; - int region; - if (label == NULL_RTX) - label = last_rethrow_symbol; - emit_library_call (rethrow_libfunc, 0, VOIDmode, 1, label, Pmode); - region = find_func_region (eh_region_from_symbol (label)); - /* If the region is -1, it doesn't exist yet. We shouldn't be - trying to rethrow there yet. */ - if (region == -1) - abort (); - function_eh_regions[region].rethrow_ref = 1; - - /* Search backwards for the actual call insn. */ - insn = get_last_insn (); - while (GET_CODE (insn) != CALL_INSN) - insn = PREV_INSN (insn); - delete_insns_since (insn); - - /* Mark the label/symbol on the call. */ - REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EH_RETHROW, label, - REG_NOTES (insn)); - emit_barrier (); - } - else - emit_jump (label); + seq = get_insns (); + end_sequence (); + + emit_insns_before (seq, region->post_landing_pad); + } } -/* Begin a region that will contain entries created with - add_partial_entry. */ + +struct sjlj_lp_info +{ + int directly_reachable; + int action_index; + int dispatch_index; + int call_site_index; +}; -void -begin_protect_partials () +static bool +sjlj_find_directly_reachable_regions (lp_info) + struct sjlj_lp_info *lp_info; { - /* Push room for a new list. */ - protect_list = tree_cons (NULL_TREE, NULL_TREE, protect_list); -} + rtx insn; + bool found_one = false; -/* End all the pending exception regions on protect_list. The handlers - will be emitted when expand_leftover_cleanups is invoked. */ + for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) + { + struct eh_region *region; + tree type_thrown; + rtx note; -void -end_protect_partials () -{ - tree t; - - /* For backwards compatibility, we allow callers to omit the call to - begin_protect_partials for the outermost region. So, - PROTECT_LIST may be NULL. */ - if (!protect_list) - return; + if (! INSN_P (insn)) + continue; - /* End all the exception regions. */ - for (t = TREE_VALUE (protect_list); t; t = TREE_CHAIN (t)) - expand_eh_region_end (TREE_VALUE (t)); + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note || INTVAL (XEXP (note, 0)) <= 0) + continue; - /* Pop the topmost entry. */ - protect_list = TREE_CHAIN (protect_list); - -} + region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; -/* Arrange for __terminate to be called if there is an unhandled throw - from within E. */ + type_thrown = NULL_TREE; + if (region->type == ERT_THROW) + { + type_thrown = region->u.throw.type; + region = region->outer; + } + + /* Find the first containing region that might handle the exception. + That's the landing pad to which we will transfer control. */ + for (; region; region = region->outer) + if (reachable_next_level (region, type_thrown, 0) != RNL_NOT_CAUGHT) + break; + + if (region) + { + lp_info[region->region_number].directly_reachable = 1; + found_one = true; + } + } + + return found_one; +} -tree -protect_with_terminate (e) - tree e; +static void +sjlj_assign_call_site_values (dispatch_label, lp_info) + rtx dispatch_label; + struct sjlj_lp_info *lp_info; { - /* We only need to do this when using setjmp/longjmp EH and the - language requires it, as otherwise we protect all of the handlers - at once, if we need to. */ - if (USING_SJLJ_EXCEPTIONS && protect_cleanup_actions_with_terminate) - { - tree handler, result; + htab_t ar_hash; + int i, index; - handler = make_node (RTL_EXPR); - TREE_TYPE (handler) = void_type_node; - RTL_EXPR_RTL (handler) = const0_rtx; - TREE_SIDE_EFFECTS (handler) = 1; - start_sequence_for_rtl_expr (handler); + /* First task: build the action table. */ - emit_library_call (terminate_libfunc, 0, VOIDmode, 0); - emit_barrier (); + VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data"); + ar_hash = htab_create (31, action_record_hash, action_record_eq, free); - RTL_EXPR_SEQUENCE (handler) = get_insns (); - end_sequence (); - - result = build (TRY_CATCH_EXPR, TREE_TYPE (e), e, handler); - TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (e); - TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (e); - TREE_READONLY (result) = TREE_READONLY (e); + for (i = cfun->eh->last_region_number; i > 0; --i) + if (lp_info[i].directly_reachable) + { + struct eh_region *r = cfun->eh->region_array[i]; + r->landing_pad = dispatch_label; + lp_info[i].action_index = collect_one_action_chain (ar_hash, r); + if (lp_info[i].action_index != -1) + cfun->uses_eh_lsda = 1; + } - e = result; - } + htab_delete (ar_hash); - return e; -} - -/* The exception table that we build that is used for looking up and - dispatching exceptions, the current number of entries, and its - maximum size before we have to extend it. + /* Next: assign dispatch values. In dwarf2 terms, this would be the + landing pad label for the region. For sjlj though, there is one + common landing pad from which we dispatch to the post-landing pads. - The number in eh_table is the code label number of the exception - handler for the region. This is added by add_eh_table_entry and - used by output_exception_table_entry. */ + A region receives a dispatch index if it is directly reachable + and requires in-function processing. Regions that share post-landing + pads may share dispatch indicies. */ + /* ??? Post-landing pad sharing doesn't actually happen at the moment + (see build_post_landing_pads) so we don't bother checking for it. */ -static int *eh_table = NULL; -static int eh_table_size = 0; -static int eh_table_max_size = 0; + index = 0; + for (i = cfun->eh->last_region_number; i > 0; --i) + if (lp_info[i].directly_reachable + && lp_info[i].action_index >= 0) + lp_info[i].dispatch_index = index++; -/* Note the need for an exception table entry for region N. If we - don't need to output an explicit exception table, avoid all of the - extra work. + /* Finally: assign call-site values. If dwarf2 terms, this would be + the region number assigned by convert_to_eh_region_ranges, but + handles no-action and must-not-throw differently. */ - Called from final_scan_insn when a NOTE_INSN_EH_REGION_BEG is seen. - (Or NOTE_INSN_EH_REGION_END sometimes) - N is the NOTE_EH_HANDLER of the note, which comes from the code - label number of the exception handler for the region. */ + call_site_base = 1; + for (i = cfun->eh->last_region_number; i > 0; --i) + if (lp_info[i].directly_reachable) + { + int action = lp_info[i].action_index; + + /* Map must-not-throw to otherwise unused call-site index 0. */ + if (action == -2) + index = 0; + /* Map no-action to otherwise unused call-site index -1. */ + else if (action == -1) + index = -1; + /* Otherwise, look it up in the table. */ + else + index = add_call_site (GEN_INT (lp_info[i].dispatch_index), action); + + lp_info[i].call_site_index = index; + } +} -void -add_eh_table_entry (n) - int n; +static void +sjlj_mark_call_sites (lp_info) + struct sjlj_lp_info *lp_info; { -#ifndef OMIT_EH_TABLE - if (eh_table_size >= eh_table_max_size) + int last_call_site = -2; + rtx insn, mem; + + mem = change_address (cfun->eh->sjlj_fc, TYPE_MODE (integer_type_node), + plus_constant (XEXP (cfun->eh->sjlj_fc, 0), + sjlj_fc_call_site_ofs)); + + for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) { - if (eh_table) - { - eh_table_max_size += eh_table_max_size>>1; + struct eh_region *region; + int this_call_site; + rtx note, before, p; + + /* Reset value tracking at extended basic block boundaries. */ + if (GET_CODE (insn) == CODE_LABEL) + last_call_site = -2; - if (eh_table_max_size < 0) - abort (); + if (! INSN_P (insn)) + continue; - eh_table = (int *) xrealloc (eh_table, - eh_table_max_size * sizeof (int)); + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note) + { + /* Calls (and trapping insns) without notes are outside any + exception handling region in this function. Mark them as + no action. */ + if (GET_CODE (insn) == CALL_INSN + || (flag_non_call_exceptions + && may_trap_p (PATTERN (insn)))) + this_call_site = -1; + else + continue; } else { - eh_table_max_size = 252; - eh_table = (int *) xmalloc (eh_table_max_size * sizeof (int)); + /* Calls that are known to not throw need not be marked. */ + if (INTVAL (XEXP (note, 0)) <= 0) + continue; + + region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; + this_call_site = lp_info[region->region_number].call_site_index; } - } - eh_table[eh_table_size++] = n; - - if (flag_new_exceptions) - { - /* We will output the exception table late in the compilation. That - references type_info objects which should have already been output - by that time. We explicitly mark those objects as being - referenced now so we know to emit them. */ - struct handler_info *handler = get_first_handler (n); - - for (; handler; handler = handler->next) - if (handler->type_info && handler->type_info != CATCH_ALL_TYPE) - { - tree tinfo = (tree)handler->type_info; - tinfo = TREE_OPERAND (tinfo, 0); - TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (tinfo)) = 1; - } + if (this_call_site == last_call_site) + continue; + + /* Don't separate a call from it's argument loads. */ + before = insn; + if (GET_CODE (insn) == CALL_INSN) + { + HARD_REG_SET parm_regs; + int nparm_regs; + + /* Since different machines initialize their parameter registers + in different orders, assume nothing. Collect the set of all + parameter registers. */ + CLEAR_HARD_REG_SET (parm_regs); + nparm_regs = 0; + for (p = CALL_INSN_FUNCTION_USAGE (insn); p ; p = XEXP (p, 1)) + if (GET_CODE (XEXP (p, 0)) == USE + && GET_CODE (XEXP (XEXP (p, 0), 0)) == REG) + { + if (REGNO (XEXP (XEXP (p, 0), 0)) >= FIRST_PSEUDO_REGISTER) + abort (); + + SET_HARD_REG_BIT (parm_regs, REGNO (XEXP (XEXP (p, 0), 0))); + nparm_regs++; + } + + /* Search backward for the first set of a register in this set. */ + while (nparm_regs) + { + before = PREV_INSN (before); + + /* Given that we've done no other optimizations yet, + the arguments should be immediately available. */ + if (GET_CODE (before) == CODE_LABEL) + abort (); + + p = single_set (before); + if (p && GET_CODE (SET_DEST (p)) == REG + && REGNO (SET_DEST (p)) < FIRST_PSEUDO_REGISTER + && TEST_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p)))) + { + CLEAR_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p))); + nparm_regs--; + } + } + } + + start_sequence (); + emit_move_insn (mem, GEN_INT (this_call_site)); + p = get_insns (); + end_sequence (); + + emit_insns_before (p, before); + last_call_site = this_call_site; } -#endif } -/* Return a non-zero value if we need to output an exception table. - - On some platforms, we don't have to output a table explicitly. - This routine doesn't mean we don't have one. */ +/* Construct the SjLj_Function_Context. */ -int -exception_table_p () +static void +sjlj_emit_function_enter (dispatch_label) + rtx dispatch_label; { - if (eh_table) - return 1; + rtx fn_begin, fc, mem, seq; - return 0; -} + fc = cfun->eh->sjlj_fc; -/* Output the entry of the exception table corresponding to the - exception region numbered N to file FILE. + start_sequence (); - N is the code label number corresponding to the handler of the - region. */ + mem = change_address (fc, Pmode, + plus_constant (XEXP (fc, 0), sjlj_fc_personality_ofs)); + emit_move_insn (mem, eh_personality_libfunc); -static void -output_exception_table_entry (file, n) - FILE *file; - int n; -{ - char buf[256]; - rtx sym; - struct handler_info *handler = get_first_handler (n); - int index = find_func_region (n); - rtx rethrow; - - /* Form and emit the rethrow label, if needed */ - if (flag_new_exceptions - && (handler || function_eh_regions[index].rethrow_ref)) - rethrow = function_eh_regions[index].rethrow_label; + mem = change_address (fc, Pmode, + plus_constant (XEXP (fc, 0), sjlj_fc_lsda_ofs)); + if (cfun->uses_eh_lsda) + { + char buf[20]; + ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", sjlj_funcdef_number); + emit_move_insn (mem, gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf))); + } else - rethrow = NULL_RTX; + emit_move_insn (mem, const0_rtx); + +#ifdef DONT_USE_BUILTIN_SETJMP + { + rtx x, note; + x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_NORMAL, + TYPE_MODE (integer_type_node), 1, + plus_constant (XEXP (fc, 0), + sjlj_fc_jbuf_ofs), Pmode); + + note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE); + NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, x, const0_rtx); + + emit_cmp_and_jump_insns (x, const0_rtx, NE, 0, + TYPE_MODE (integer_type_node), 0, 0, + dispatch_label); + } +#else + expand_builtin_setjmp_setup (plus_constant (XEXP (fc, 0), sjlj_fc_jbuf_ofs), + dispatch_label); +#endif - if (function_eh_regions[index].emitted) - return; - function_eh_regions[index].emitted = 1; + emit_library_call (unwind_sjlj_register_libfunc, LCT_NORMAL, VOIDmode, + 1, XEXP (fc, 0), Pmode); - for ( ; handler != NULL || rethrow != NULL_RTX; handler = handler->next) - { - /* rethrow label should indicate the LAST entry for a region */ - if (rethrow != NULL_RTX && (handler == NULL || handler->next == NULL)) - { - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", n); - assemble_eh_label(buf); - rethrow = NULL_RTX; - } + seq = get_insns (); + end_sequence (); - ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n); - sym = gen_rtx_SYMBOL_REF (Pmode, buf); - assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); + /* ??? Instead of doing this at the beginning of the function, + do this in a block that is at loop level 0 and dominates all + can_throw_internal instructions. */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n); - sym = gen_rtx_SYMBOL_REF (Pmode, buf); - assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); - - if (handler == NULL) - assemble_eh_integer (GEN_INT (0), POINTER_SIZE / BITS_PER_UNIT, 1); - else - { - ASM_GENERATE_INTERNAL_LABEL (buf, "L", handler->handler_number); - sym = gen_rtx_SYMBOL_REF (Pmode, buf); - assemble_eh_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); - } - - if (flag_new_exceptions) - { - if (handler == NULL || handler->type_info == NULL) - assemble_eh_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); - else - if (handler->type_info == CATCH_ALL_TYPE) - assemble_eh_integer (GEN_INT (CATCH_ALL_TYPE), - POINTER_SIZE / BITS_PER_UNIT, 1); - else - output_constant ((tree)(handler->type_info), - POINTER_SIZE / BITS_PER_UNIT); - } - putc ('\n', file); /* blank line */ - /* We only output the first label under the old scheme */ - if (! flag_new_exceptions || handler == NULL) - break; - } -} - -/* Output the exception table if we have and need one. */ - -static short language_code = 0; -static short version_code = 0; - -/* This routine will set the language code for exceptions. */ -void -set_exception_lang_code (code) - int code; -{ - language_code = code; + for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin)) + if (GET_CODE (fn_begin) == NOTE + && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG) + break; + emit_insns_after (seq, fn_begin); } -/* This routine will set the language version code for exceptions. */ -void -set_exception_version_code (code) - int code; -{ - version_code = code; -} +/* Call back from expand_function_end to know where we should put + the call to unwind_sjlj_unregister_libfunc if needed. */ -/* Free the EH table structures. */ void -free_exception_table () +sjlj_emit_function_exit_after (after) + rtx after; { - if (eh_table) - free (eh_table); - clear_function_eh_region (); + cfun->eh->sjlj_exit_after = after; } - -/* Output the common content of an exception table. */ -void -output_exception_table_data () + +static void +sjlj_emit_function_exit () { - int i; - char buf[256]; - extern FILE *asm_out_file; + rtx seq; - if (flag_new_exceptions) - { - assemble_eh_integer (GEN_INT (NEW_EH_RUNTIME), - POINTER_SIZE / BITS_PER_UNIT, 1); - assemble_eh_integer (GEN_INT (language_code), 2 , 1); - assemble_eh_integer (GEN_INT (version_code), 2 , 1); + start_sequence (); - /* Add enough padding to make sure table aligns on a pointer boundry. */ - i = GET_MODE_ALIGNMENT (ptr_mode) / BITS_PER_UNIT - 4; - for ( ; i < 0; i = i + GET_MODE_ALIGNMENT (ptr_mode) / BITS_PER_UNIT) - ; - if (i != 0) - assemble_eh_integer (const0_rtx, i , 1); + emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL, VOIDmode, + 1, XEXP (cfun->eh->sjlj_fc, 0), Pmode); - /* Generate the label for offset calculations on rethrows. */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", 0); - assemble_eh_label(buf); - } + seq = get_insns (); + end_sequence (); - for (i = 0; i < eh_table_size; ++i) - output_exception_table_entry (asm_out_file, eh_table[i]); + /* ??? Really this can be done in any block at loop level 0 that + post-dominates all can_throw_internal instructions. This is + the last possible moment. */ + emit_insns_after (seq, cfun->eh->sjlj_exit_after); } -/* Output an exception table for the entire compilation unit. */ -void -output_exception_table () +static void +sjlj_emit_dispatch_table (dispatch_label, lp_info) + rtx dispatch_label; + struct sjlj_lp_info *lp_info; { - char buf[256]; - extern FILE *asm_out_file; + int i, first_reachable; + rtx mem, dispatch, seq, fc; - if (! doing_eh (0) || ! eh_table) - return; + fc = cfun->eh->sjlj_fc; - exception_section (); + start_sequence (); + + emit_label (dispatch_label); + +#ifndef DONT_USE_BUILTIN_SETJMP + expand_builtin_setjmp_receiver (dispatch_label); +#endif + + /* Load up dispatch index, exc_ptr and filter values from the + function context. */ + mem = change_address (fc, TYPE_MODE (integer_type_node), + plus_constant (XEXP (fc, 0), sjlj_fc_call_site_ofs)); + dispatch = copy_to_reg (mem); + + mem = change_address (fc, word_mode, + plus_constant (XEXP (fc, 0), sjlj_fc_data_ofs)); + if (word_mode != Pmode) + { +#ifdef POINTERS_EXTEND_UNSIGNED + mem = convert_memory_address (Pmode, mem); +#else + mem = convert_to_mode (Pmode, mem, 0); +#endif + } + emit_move_insn (cfun->eh->exc_ptr, mem); + + mem = change_address (fc, word_mode, + plus_constant (XEXP (fc, 0), + sjlj_fc_data_ofs + UNITS_PER_WORD)); + emit_move_insn (cfun->eh->filter, mem); - /* Beginning marker for table. */ - assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode)); - assemble_eh_label ("__EXCEPTION_TABLE__"); + /* Jump to one of the directly reachable regions. */ + /* ??? This really ought to be using a switch statement. */ - output_exception_table_data (); + first_reachable = 0; + for (i = cfun->eh->last_region_number; i > 0; --i) + { + if (! lp_info[i].directly_reachable + || lp_info[i].action_index < 0) + continue; + + if (! first_reachable) + { + first_reachable = i; + continue; + } - /* Ending marker for table. */ - /* Generate the label for end of table. */ - ASM_GENERATE_INTERNAL_LABEL (buf, "LRTH", CODE_LABEL_NUMBER (final_rethrow)); - assemble_eh_label(buf); - assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); + emit_cmp_and_jump_insns (dispatch, + GEN_INT (lp_info[i].dispatch_index), EQ, + NULL_RTX, TYPE_MODE (integer_type_node), 0, 0, + cfun->eh->region_array[i]->post_landing_pad); + } - /* For binary compatibility, the old __throw checked the second - position for a -1, so we should output at least 2 -1's */ - if (! flag_new_exceptions) - assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); + seq = get_insns (); + end_sequence (); - putc ('\n', asm_out_file); /* blank line */ + emit_insns_before (seq, (cfun->eh->region_array[first_reachable] + ->post_landing_pad)); } -/* Used by the ia64 unwind format to output data for an individual - function. */ -void -output_function_exception_table () +static void +sjlj_build_landing_pads () { - extern FILE *asm_out_file; + struct sjlj_lp_info *lp_info; - if (! doing_eh (0) || ! eh_table) - return; + lp_info = (struct sjlj_lp_info *) xcalloc (cfun->eh->last_region_number + 1, + sizeof (struct sjlj_lp_info)); -#ifdef HANDLER_SECTION - HANDLER_SECTION; -#endif - - output_exception_table_data (); + if (sjlj_find_directly_reachable_regions (lp_info)) + { + rtx dispatch_label = gen_label_rtx (); - /* Ending marker for table. */ - assemble_eh_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); + cfun->eh->sjlj_fc + = assign_stack_local (TYPE_MODE (sjlj_fc_type_node), + int_size_in_bytes (sjlj_fc_type_node), + TYPE_ALIGN (sjlj_fc_type_node)); - putc ('\n', asm_out_file); /* blank line */ -} + sjlj_assign_call_site_values (dispatch_label, lp_info); + sjlj_mark_call_sites (lp_info); - -/* Emit code to get EH context. - - We have to scan thru the code to find possible EH context registers. - Inlined functions may use it too, and thus we'll have to be able - to change them too. + sjlj_emit_function_enter (dispatch_label); + sjlj_emit_dispatch_table (dispatch_label, lp_info); + sjlj_emit_function_exit (); + } - This is done only if using USING_SJLJ_EXCEPTIONS. */ + free (lp_info); +} void -emit_eh_context () +finish_eh_generation () { - rtx insn; - rtx ehc = 0; - - if (! doing_eh (0)) + /* Nothing to do if no regions created. */ + if (cfun->eh->region_tree == NULL) return; - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == INSN - && GET_CODE (PATTERN (insn)) == USE) - { - rtx reg = find_reg_note (insn, REG_EH_CONTEXT, 0); - if (reg) - { - rtx insns; - - start_sequence (); - - /* If this is the first use insn, emit the call here. This - will always be at the top of our function, because if - expand_inline_function notices a REG_EH_CONTEXT note, it - adds a use insn to this function as well. */ - if (ehc == 0) - ehc = call_get_eh_context (); + /* The object here is to provide find_basic_blocks with detailed + information (via reachable_handlers) on how exception control + flows within the function. In this first pass, we can include + type information garnered from ERT_THROW and ERT_ALLOWED_EXCEPTIONS + regions, and hope that it will be useful in deleting unreachable + handlers. Subsequently, we will generate landing pads which will + connect many of the handlers, and then type information will not + be effective. Still, this is a win over previous implementations. */ + + jump_optimize_minimal (get_insns ()); + find_basic_blocks (get_insns (), max_reg_num (), 0); + cleanup_cfg (); + + /* These registers are used by the landing pads. Make sure they + have been generated. */ + get_exception_pointer (); + cfun->eh->filter = gen_reg_rtx (word_mode); + + /* Construct the landing pads. */ + + assign_filter_values (); + build_post_landing_pads (); + connect_post_landing_pads (); + if (USING_SJLJ_EXCEPTIONS) + sjlj_build_landing_pads (); + else + dw2_build_landing_pads (); - emit_move_insn (XEXP (reg, 0), ehc); - insns = get_insns (); - end_sequence (); + cfun->eh->built_landing_pads = 1; - emit_insns_before (insns, insn); - } - } + /* We've totally changed the CFG. Start over. */ + find_exception_handler_labels (); + jump_optimize_minimal (get_insns ()); + find_basic_blocks (get_insns (), max_reg_num (), 0); + cleanup_cfg (); } + +/* This section handles removing dead code for flow. */ -/* Scan the insn chain F and build a list of handler labels. The - resulting list is placed in the global variable exception_handler_labels. */ +/* Remove LABEL from the exception_handler_labels list. */ static void -find_exception_handler_labels_1 (f) - rtx f; +remove_exception_handler_label (label) + rtx label; { - rtx insn; + rtx *pl, l; - /* For each start of a region, add its label to the list. */ + for (pl = &exception_handler_labels, l = *pl; + XEXP (l, 0) != label; + pl = &XEXP (l, 1), l = *pl) + continue; - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - struct handler_info* ptr; - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - ptr = get_first_handler (NOTE_EH_HANDLER (insn)); - for ( ; ptr; ptr = ptr->next) - { - /* make sure label isn't in the list already */ - rtx x; - for (x = exception_handler_labels; x; x = XEXP (x, 1)) - if (XEXP (x, 0) == ptr->handler_label) - break; - if (! x) - exception_handler_labels = gen_rtx_EXPR_LIST (VOIDmode, - ptr->handler_label, exception_handler_labels); - } - } - else if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) - { - find_exception_handler_labels_1 (XEXP (PATTERN (insn), 0)); - find_exception_handler_labels_1 (XEXP (PATTERN (insn), 1)); - find_exception_handler_labels_1 (XEXP (PATTERN (insn), 2)); - } - } + *pl = XEXP (l, 1); + free_EXPR_LIST_node (l); } -/* Scan the current insns and build a list of handler labels. The - resulting list is placed in the global variable exception_handler_labels. +/* Splice REGION from the region tree etc. */ - It is called after the last exception handling region is added to - the current function (when the rtl is almost all built for the - current function) and before the jump optimization pass. */ -void -find_exception_handler_labels () +static void +remove_eh_handler (region) + struct eh_region *region; { - exception_handler_labels = NULL_RTX; - - /* If we aren't doing exception handling, there isn't much to check. */ - if (! doing_eh (0)) - return; - - find_exception_handler_labels_1 (get_insns ()); -} + struct eh_region **pp, *p; + rtx lab; + int i; -/* Return a value of 1 if the parameter label number is an exception handler - label. Return 0 otherwise. */ + /* For the benefit of efficiently handling REG_EH_REGION notes, + replace this region in the region array with its containing + region. Note that previous region deletions may result in + multiple copies of this region in the array, so we have to + search the whole thing. */ + for (i = cfun->eh->last_region_number; i > 0; --i) + if (cfun->eh->region_array[i] == region) + cfun->eh->region_array[i] = region->outer; + + if (cfun->eh->built_landing_pads) + lab = region->landing_pad; + else + lab = region->label; + if (lab) + remove_exception_handler_label (lab); -int -is_exception_handler_label (lab) - int lab; -{ - rtx x; - for (x = exception_handler_labels ; x ; x = XEXP (x, 1)) - if (lab == CODE_LABEL_NUMBER (XEXP (x, 0))) - return 1; - return 0; -} + if (region->outer) + pp = ®ion->outer->inner; + else + pp = &cfun->eh->region_tree; + for (p = *pp; p != region; pp = &p->next_peer, p = *pp) + continue; -/* Perform sanity checking on the exception_handler_labels list. + if (region->inner) + { + for (p = region->inner; p->next_peer ; p = p->next_peer) + p->outer = region->outer; + p->next_peer = region->next_peer; + p->outer = region->outer; + *pp = region->inner; + } + else + *pp = region->next_peer; - Can be called after find_exception_handler_labels is called to - build the list of exception handlers for the current function and - before we finish processing the current function. */ + if (region->type == ERT_CATCH) + { + struct eh_region *try, *next, *prev; -void -check_exception_handler_labels () -{ - rtx insn, insn2; + for (try = region->next_peer; + try->type == ERT_CATCH; + try = try->next_peer) + continue; + if (try->type != ERT_TRY) + abort (); - /* If we aren't doing exception handling, there isn't much to check. */ - if (! doing_eh (0)) - return; + next = region->u.catch.next_catch; + prev = region->u.catch.prev_catch; - /* Make sure there is no more than 1 copy of a label */ - for (insn = exception_handler_labels; insn; insn = XEXP (insn, 1)) - { - int count = 0; - for (insn2 = exception_handler_labels; insn2; insn2 = XEXP (insn2, 1)) - if (XEXP (insn, 0) == XEXP (insn2, 0)) - count++; - if (count != 1) - warning ("Counted %d copies of EH region %d in list.\n", count, - CODE_LABEL_NUMBER (insn)); + if (next) + next->u.catch.prev_catch = prev; + else + try->u.try.last_catch = prev; + if (prev) + prev->u.catch.next_catch = next; + else + { + try->u.try.catch = next; + if (! next) + remove_eh_handler (try); + } } + free (region); } -/* Mark the children of NODE for GC. */ +/* LABEL heads a basic block that is about to be deleted. If this + label corresponds to an exception region, we may be able to + delete the region. */ -static void -mark_eh_node (node) - struct eh_node *node; +void +maybe_remove_eh_handler (label) + rtx label; { - while (node) + int i; + + /* ??? After generating landing pads, it's not so simple to determine + if the region data is completely unused. One must examine the + landing pad and the post landing pad, and whether an inner try block + is referencing the catch handlers directly. */ + if (cfun->eh->built_landing_pads) + return; + + for (i = cfun->eh->last_region_number; i > 0; --i) { - if (node->entry) + struct eh_region *region = cfun->eh->region_array[i]; + if (region && region->label == label) { - ggc_mark_rtx (node->entry->outer_context); - ggc_mark_rtx (node->entry->exception_handler_label); - ggc_mark_tree (node->entry->finalization); - ggc_mark_rtx (node->entry->false_label); - ggc_mark_rtx (node->entry->rethrow_label); + /* Flow will want to remove MUST_NOT_THROW regions as unreachable + because there is no path to the fallback call to terminate. + But the region continues to affect call-site data until there + are no more contained calls, which we don't see here. */ + if (region->type == ERT_MUST_NOT_THROW) + { + remove_exception_handler_label (region->label); + region->label = NULL_RTX; + } + else + remove_eh_handler (region); + break; } - node = node ->chain; } } -/* Mark S for GC. */ + +/* This section describes CFG exception edges for flow. */ -static void -mark_eh_stack (s) - struct eh_stack *s; +/* For communicating between calls to reachable_next_level. */ +struct reachable_info { - if (s) - mark_eh_node (s->top); -} + tree types_caught; + tree types_allowed; + rtx handlers; +}; -/* Mark Q for GC. */ +/* A subroutine of reachable_next_level. Return true if TYPE, or a + base class of TYPE, is in HANDLED. */ -static void -mark_eh_queue (q) - struct eh_queue *q; +static int +check_handled (handled, type) + tree handled, type; { - while (q) + tree t; + + /* We can check for exact matches without front-end help. */ + if (! lang_eh_type_covers) + { + for (t = handled; t ; t = TREE_CHAIN (t)) + if (TREE_VALUE (t) == type) + return 1; + } + else { - mark_eh_node (q->head); - q = q->next; + for (t = handled; t ; t = TREE_CHAIN (t)) + if ((*lang_eh_type_covers) (TREE_VALUE (t), type)) + return 1; } + + return 0; } -/* Mark NODE for GC. A label_node contains a union containing either - a tree or an rtx. This label_node will contain a tree. */ +/* A subroutine of reachable_next_level. If we are collecting a list + of handlers, add one. After landing pad generation, reference + it instead of the handlers themselves. Further, the handlers are + all wired together, so by referencing one, we've got them all. + Before landing pad generation we reference each handler individually. + + LP_REGION contains the landing pad; REGION is the handler. */ static void -mark_tree_label_node (node) - struct label_node *node; +add_reachable_handler (info, lp_region, region) + struct reachable_info *info; + struct eh_region *lp_region; + struct eh_region *region; { - while (node) + if (! info) + return; + + if (cfun->eh->built_landing_pads) { - ggc_mark_tree (node->u.tlabel); - node = node->chain; + if (! info->handlers) + info->handlers = alloc_INSN_LIST (lp_region->landing_pad, NULL_RTX); } + else + info->handlers = alloc_INSN_LIST (region->label, info->handlers); } -/* Mark EH for GC. */ +/* Process one level of exception regions for reachability. + If TYPE_THROWN is non-null, then it is the *exact* type being + propagated. If INFO is non-null, then collect handler labels + and caught/allowed type information between invocations. */ -void -mark_eh_status (eh) - struct eh_status *eh; +static enum reachable_code +reachable_next_level (region, type_thrown, info) + struct eh_region *region; + tree type_thrown; + struct reachable_info *info; { - if (eh == 0) - return; + switch (region->type) + { + case ERT_CLEANUP: + /* Before landing-pad generation, we model control flow + directly to the individual handlers. In this way we can + see that catch handler types may shadow one another. */ + add_reachable_handler (info, region, region); + return RNL_MAYBE_CAUGHT; + + case ERT_TRY: + { + struct eh_region *c; + enum reachable_code ret = RNL_NOT_CAUGHT; - mark_eh_stack (&eh->x_ehstack); - mark_eh_stack (&eh->x_catchstack); - mark_eh_queue (eh->x_ehqueue); - ggc_mark_rtx (eh->x_catch_clauses); + for (c = region->u.try.catch; c ; c = c->u.catch.next_catch) + { + /* A catch-all handler ends the search. */ + /* ??? _Unwind_ForcedUnwind will want outer cleanups + to be run as well. */ + if (c->u.catch.type == NULL) + { + add_reachable_handler (info, region, c); + return RNL_CAUGHT; + } + + if (type_thrown) + { + /* If we have a type match, end the search. */ + if (c->u.catch.type == type_thrown + || (lang_eh_type_covers + && (*lang_eh_type_covers) (c->u.catch.type, + type_thrown))) + { + add_reachable_handler (info, region, c); + return RNL_CAUGHT; + } + + /* If we have definitive information of a match failure, + the catch won't trigger. */ + if (lang_eh_type_covers) + return RNL_NOT_CAUGHT; + } + + if (! info) + ret = RNL_MAYBE_CAUGHT; + + /* A type must not have been previously caught. */ + else if (! check_handled (info->types_caught, c->u.catch.type)) + { + add_reachable_handler (info, region, c); + info->types_caught = tree_cons (NULL, c->u.catch.type, + info->types_caught); + + /* ??? If the catch type is a base class of every allowed + type, then we know we can stop the search. */ + ret = RNL_MAYBE_CAUGHT; + } + } - if (lang_mark_false_label_stack) - (*lang_mark_false_label_stack) (eh->x_false_label_stack); - mark_tree_label_node (eh->x_caught_return_label_stack); + return ret; + } - ggc_mark_tree (eh->x_protect_list); - ggc_mark_rtx (eh->ehc); - ggc_mark_rtx (eh->x_eh_return_stub_label); -} + case ERT_ALLOWED_EXCEPTIONS: + /* An empty list of types definitely ends the search. */ + if (region->u.allowed.type_list == NULL_TREE) + { + add_reachable_handler (info, region, region); + return RNL_CAUGHT; + } -/* Mark ARG (which is really a struct func_eh_entry**) for GC. */ + /* Collect a list of lists of allowed types for use in detecting + when a catch may be transformed into a catch-all. */ + if (info) + info->types_allowed = tree_cons (NULL_TREE, + region->u.allowed.type_list, + info->types_allowed); + + /* If we have definitive information about the type heirarchy, + then we can tell if the thrown type will pass through the + filter. */ + if (type_thrown && lang_eh_type_covers) + { + if (check_handled (region->u.allowed.type_list, type_thrown)) + return RNL_NOT_CAUGHT; + else + { + add_reachable_handler (info, region, region); + return RNL_CAUGHT; + } + } -static void -mark_func_eh_entry (arg) - void *arg; -{ - struct func_eh_entry *fee; - struct handler_info *h; - int i; + add_reachable_handler (info, region, region); + return RNL_MAYBE_CAUGHT; - fee = *((struct func_eh_entry **) arg); + case ERT_CATCH: + /* Catch regions are handled by their controling try region. */ + return RNL_NOT_CAUGHT; - for (i = 0; i < current_func_eh_entry; ++i) - { - ggc_mark_rtx (fee->rethrow_label); - for (h = fee->handlers; h; h = h->next) + case ERT_MUST_NOT_THROW: + /* Here we end our search, since no exceptions may propagate. + If we've touched down at some landing pad previous, then the + explicit function call we generated may be used. Otherwise + the call is made by the runtime. */ + if (info && info->handlers) { - ggc_mark_rtx (h->handler_label); - if (h->type_info != CATCH_ALL_TYPE) - ggc_mark_tree ((tree) h->type_info); + add_reachable_handler (info, region, region); + return RNL_CAUGHT; } + else + return RNL_BLOCKED; - /* Skip to the next entry in the array. */ - ++fee; + case ERT_THROW: + case ERT_FIXUP: + /* Shouldn't see these here. */ + break; } -} -/* This group of functions initializes the exception handling data - structures at the start of the compilation, initializes the data - structures at the start of a function, and saves and restores the - exception handling data structures for the start/end of a nested - function. */ + abort (); +} -/* Toplevel initialization for EH things. */ +/* Retrieve a list of labels of exception handlers which can be + reached by a given insn. */ -void -init_eh () +rtx +reachable_handlers (insn) + rtx insn; { - first_rethrow_symbol = create_rethrow_ref (0); - final_rethrow = gen_exception_label (); - last_rethrow_symbol = create_rethrow_ref (CODE_LABEL_NUMBER (final_rethrow)); + struct reachable_info info; + struct eh_region *region; + tree type_thrown; + int region_number; - ggc_add_rtx_root (&exception_handler_labels, 1); - ggc_add_rtx_root (&eh_return_context, 1); - ggc_add_rtx_root (&eh_return_stack_adjust, 1); - ggc_add_rtx_root (&eh_return_handler, 1); - ggc_add_rtx_root (&first_rethrow_symbol, 1); - ggc_add_rtx_root (&final_rethrow, 1); - ggc_add_rtx_root (&last_rethrow_symbol, 1); - ggc_add_root (&function_eh_regions, 1, sizeof (function_eh_regions), - mark_func_eh_entry); -} - -/* Initialize the per-function EH information. */ + if (GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) == RESX) + region_number = XINT (PATTERN (insn), 0); + else + { + rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note || INTVAL (XEXP (note, 0)) <= 0) + return NULL; + region_number = INTVAL (XEXP (note, 0)); + } -void -init_eh_for_function () -{ - cfun->eh = (struct eh_status *) xcalloc (1, sizeof (struct eh_status)); - ehqueue = (struct eh_queue *) xcalloc (1, sizeof (struct eh_queue)); - eh_return_context = NULL_RTX; - eh_return_stack_adjust = NULL_RTX; - eh_return_handler = NULL_RTX; -} + memset (&info, 0, sizeof (info)); -void -free_eh_status (f) - struct function *f; -{ - free (f->eh->x_ehqueue); - free (f->eh); - f->eh = NULL; + region = cfun->eh->region_array[region_number]; + + type_thrown = NULL_TREE; + if (region->type == ERT_THROW) + { + type_thrown = region->u.throw.type; + region = region->outer; + } + + for (; region; region = region->outer) + if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT) + break; + + return info.handlers; } - -/* This section is for the exception handling specific optimization - pass. */ -/* Determine if the given INSN can throw an exception. */ +/* Determine if the given INSN can throw an exception that is caught + within the function. */ -int +bool can_throw_internal (insn) rtx insn; { + struct eh_region *region; + tree type_thrown; + rtx note; + + if (! INSN_P (insn)) + return false; + if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) insn = XVECEXP (PATTERN (insn), 0, 0); - /* Calls can always potentially throw exceptions, unless they have - a REG_EH_REGION note with a value of 0 or less. */ - if (GET_CODE (insn) == CALL_INSN) + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) { - rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note || INTVAL (XEXP (note, 0)) > 0) - return 1; - } - - if (flag_non_call_exceptions) - { - /* If we wanted asynchronous exceptions, then everything but NOTEs - and CODE_LABELs could throw. */ - if (GET_CODE (insn) != NOTE && GET_CODE (insn) != CODE_LABEL) - return 1; + int i; + for (i = 0; i < 3; ++i) + { + rtx sub = XEXP (PATTERN (insn), i); + for (; sub ; sub = NEXT_INSN (sub)) + if (can_throw_internal (sub)) + return true; + } + return false; } - return 0; -} + /* Every insn that might throw has an EH_REGION note. */ + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note || INTVAL (XEXP (note, 0)) <= 0) + return false; -/* Return nonzero if nothing in this function can throw. */ - -int -nothrow_function_p () -{ - rtx insn; + region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; - if (! flag_exceptions) - return 1; + type_thrown = NULL_TREE; + if (region->type == ERT_THROW) + { + type_thrown = region->u.throw.type; + region = region->outer; + } - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (can_throw_internal (insn)) - return 0; - for (insn = current_function_epilogue_delay_list; insn; - insn = XEXP (insn, 1)) - if (can_throw_internal (insn)) - return 0; + /* If this exception is ignored by each and every containing region, + then control passes straight out. The runtime may handle some + regions, which also do not require processing internally. */ + for (; region; region = region->outer) + { + enum reachable_code how = reachable_next_level (region, type_thrown, 0); + if (how == RNL_BLOCKED) + return false; + if (how != RNL_NOT_CAUGHT) + return true; + } - return 1; + return false; } -/* Scan a exception region looking for the matching end and then - remove it if possible. INSN is the start of the region, N is the - region number, and DELETE_OUTER is to note if anything in this - region can throw. - - Regions are removed if they cannot possibly catch an exception. - This is determined by invoking can_throw_internal on each insn within the - region; if can_throw_internal returns true for any of the instructions, the - region can catch an exception, since there is an insn within the - region that is capable of throwing an exception. +/* Determine if the given INSN can throw an exception that is + visible outside the function. */ - Returns the NOTE_INSN_EH_REGION_END corresponding to this region, or - calls abort if it can't find one. - - Can abort if INSN is not a NOTE_INSN_EH_REGION_BEGIN, or if N doesn't - correspond to the region number, or if DELETE_OUTER is NULL. */ - -static rtx -scan_region (insn, n, delete_outer) +bool +can_throw_external (insn) rtx insn; - int n; - int *delete_outer; { - rtx start = insn; - - /* Assume we can delete the region. */ - int delete = 1; + struct eh_region *region; + tree type_thrown; + rtx note; - /* Can't delete something which is rethrown from. */ - if (rethrow_used (n)) - delete = 0; + if (! INSN_P (insn)) + return false; - if (insn == NULL_RTX - || GET_CODE (insn) != NOTE - || NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG - || NOTE_EH_HANDLER (insn) != n - || delete_outer == NULL) - abort (); - - insn = NEXT_INSN (insn); + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); - /* Look for the matching end. */ - while (! (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) + if (GET_CODE (insn) == CALL_INSN + && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) { - /* If anything can throw, we can't remove the region. */ - if (delete && can_throw_internal (insn)) + int i; + for (i = 0; i < 3; ++i) { - delete = 0; + rtx sub = XEXP (PATTERN (insn), i); + for (; sub ; sub = NEXT_INSN (sub)) + if (can_throw_external (sub)) + return true; } - - /* Watch out for and handle nested regions. */ - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - insn = scan_region (insn, NOTE_EH_HANDLER (insn), &delete); - } - - insn = NEXT_INSN (insn); + return false; } - /* The _BEG/_END NOTEs must match and nest. */ - if (NOTE_EH_HANDLER (insn) != n) - abort (); - - /* If anything in this exception region can throw, we can throw. */ - if (! delete) - *delete_outer = 0; - else + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note) { - /* Delete the start and end of the region. */ - delete_insn (start); - delete_insn (insn); - -/* We no longer removed labels here, since flow will now remove any - handler which cannot be called any more. */ - -#if 0 - /* Only do this part if we have built the exception handler - labels. */ - if (exception_handler_labels) - { - rtx x, *prev = &exception_handler_labels; - - /* Find it in the list of handlers. */ - for (x = exception_handler_labels; x; x = XEXP (x, 1)) - { - rtx label = XEXP (x, 0); - if (CODE_LABEL_NUMBER (label) == n) - { - /* If we are the last reference to the handler, - delete it. */ - if (--LABEL_NUSES (label) == 0) - delete_insn (label); - - if (optimize) - { - /* Remove it from the list of exception handler - labels, if we are optimizing. If we are not, then - leave it in the list, as we are not really going to - remove the region. */ - *prev = XEXP (x, 1); - XEXP (x, 1) = 0; - XEXP (x, 0) = 0; - } - - break; - } - prev = &XEXP (x, 1); - } - } -#endif + /* Calls (and trapping insns) without notes are outside any + exception handling region in this function. We have to + assume it might throw. Given that the front end and middle + ends mark known NOTHROW functions, this isn't so wildly + inaccurate. */ + return (GET_CODE (insn) == CALL_INSN + || (flag_non_call_exceptions + && may_trap_p (PATTERN (insn)))); } - return insn; -} + if (INTVAL (XEXP (note, 0)) <= 0) + return false; -/* Perform various interesting optimizations for exception handling - code. - - We look for empty exception regions and make them go (away). The - jump optimization code will remove the handler if nothing else uses - it. */ - -void -exception_optimize () -{ - rtx insn; - int n; + region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; - /* Remove empty regions. */ - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + type_thrown = NULL_TREE; + if (region->type == ERT_THROW) { - if (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - /* Since scan_region will return the NOTE_INSN_EH_REGION_END - insn, we will indirectly skip through all the insns - inbetween. We are also guaranteed that the value of insn - returned will be valid, as otherwise scan_region won't - return. */ - insn = scan_region (insn, NOTE_EH_HANDLER (insn), &n); - } + type_thrown = region->u.throw.type; + region = region->outer; } + + /* If the exception is caught or blocked by any containing region, + then it is not seen by any calling function. */ + for (; region ; region = region->outer) + if (reachable_next_level (region, type_thrown, NULL) >= RNL_CAUGHT) + return false; + + return true; } -/* This function determines whether the rethrow labels for any of the - exception regions in the current function are used or not, and set - the reference flag according. */ +/* True if nothing in this function can throw outside this function. */ -void -update_rethrow_references () +bool +nothrow_function_p () { rtx insn; - int x, region; - int *saw_region, *saw_rethrow; - if (!flag_new_exceptions) - return; - - saw_region = (int *) xcalloc (current_func_eh_entry, sizeof (int)); - saw_rethrow = (int *) xcalloc (current_func_eh_entry, sizeof (int)); + if (! flag_exceptions) + return true; - /* Determine what regions exist, and whether there are any rethrows - from those regions or not. */ for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == CALL_INSN) - { - rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX); - if (note) - { - region = eh_region_from_symbol (XEXP (note, 0)); - region = find_func_region (region); - saw_rethrow[region] = 1; - } - } - else - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - region = find_func_region (NOTE_EH_HANDLER (insn)); - saw_region[region] = 1; - } - } - - /* For any regions we did see, set the referenced flag. */ - for (x = 0; x < current_func_eh_entry; x++) - if (saw_region[x]) - function_eh_regions[x].rethrow_ref = saw_rethrow[x]; + if (can_throw_external (insn)) + return false; + for (insn = current_function_epilogue_delay_list; insn; + insn = XEXP (insn, 1)) + if (can_throw_external (insn)) + return false; - /* Clean up. */ - free (saw_region); - free (saw_rethrow); + return true; } + -/* Various hooks for the DWARF 2 __throw routine. */ +/* Various hooks for unwind library. */ /* Do any necessary initialization to access arbitrary stack frames. On the SPARC, this means flushing the register windows. */ @@ -2950,6 +2882,33 @@ expand_builtin_unwind_init () #endif } +rtx +expand_builtin_eh_return_data_regno (arglist) + tree arglist; +{ + tree which = TREE_VALUE (arglist); + unsigned HOST_WIDE_INT iwhich; + + if (TREE_CODE (which) != INTEGER_CST) + { + error ("argument of `__builtin_eh_return_regno' must be constant"); + return constm1_rtx; + } + + iwhich = tree_low_cst (which, 1); + iwhich = EH_RETURN_DATA_REGNO (iwhich); + if (iwhich == INVALID_REGNUM) + return constm1_rtx; + +#ifdef DWARF_FRAME_REGNUM + iwhich = DWARF_FRAME_REGNUM (iwhich); +#else + iwhich = DBX_REGISTER_NUMBER (iwhich); +#endif + + return GEN_INT (iwhich); +} + /* Given a value extracted from the return address register or stack slot, return the actual address encoded in that value. */ @@ -2958,7 +2917,18 @@ 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); + + /* First mask out any unwanted bits. */ +#ifdef MASK_RETURN_ADDR + expand_and (addr, MASK_RETURN_ADDR, addr); +#endif + + /* Then adjust to find the real return address. */ +#if defined (RETURN_ADDR_OFFSET) + addr = plus_constant (addr, RETURN_ADDR_OFFSET); +#endif + + return addr; } /* Given an actual address in addr_tree, do any necessary encoding @@ -2970,540 +2940,773 @@ 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 = force_reg (Pmode, addr); addr = plus_constant (addr, -RETURN_ADDR_OFFSET); #endif + return addr; } -/* Choose three registers for communication between the main body of - __throw and the epilogue (or eh stub) and the exception handler. - We must do this with hard registers because the epilogue itself - will be generated after reload, at which point we may not reference - pseudos at all. - - The first passes the exception context to the handler. For this - we use the return value register for a void*. - - The second holds the stack pointer value to be restored. For this - we use the static chain register if it exists, is different from - the previous, and is call-clobbered; otherwise some arbitrary - call-clobbered register. - - The third holds the address of the handler itself. Here we use - some arbitrary call-clobbered register. */ +/* Set up the epilogue with the magic bits we'll need to return to the + exception handler. */ -static void -eh_regs (pcontext, psp, pra, outgoing) - rtx *pcontext, *psp, *pra; - int outgoing ATTRIBUTE_UNUSED; +void +expand_builtin_eh_return (stackadj_tree, handler_tree) + tree stackadj_tree, handler_tree; { - rtx rcontext, rsp, rra; - unsigned int i; - tree t; + rtx stackadj, handler; - t = build_pointer_type (void_type_node); -#ifdef FUNCTION_OUTGOING_VALUE - if (outgoing) - rcontext = FUNCTION_OUTGOING_VALUE (t, current_function_decl); - else -#endif - rcontext = FUNCTION_VALUE (t, current_function_decl); + stackadj = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0); + handler = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0); -#ifdef STATIC_CHAIN_REGNUM - if (outgoing) - rsp = static_chain_incoming_rtx; + if (! cfun->eh->ehr_label) + { + cfun->eh->ehr_stackadj = copy_to_reg (stackadj); + cfun->eh->ehr_handler = copy_to_reg (handler); + cfun->eh->ehr_label = gen_label_rtx (); + } else - rsp = static_chain_rtx; - if (REGNO (rsp) == REGNO (rcontext) - || ! call_used_regs [REGNO (rsp)]) -#endif /* STATIC_CHAIN_REGNUM */ - rsp = NULL_RTX; - - if (rsp == NULL_RTX) { - for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) - if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (rcontext)) - break; - if (i == FIRST_PSEUDO_REGISTER) - abort(); - - rsp = gen_rtx_REG (Pmode, i); + if (stackadj != cfun->eh->ehr_stackadj) + emit_move_insn (cfun->eh->ehr_stackadj, stackadj); + if (handler != cfun->eh->ehr_handler) + emit_move_insn (cfun->eh->ehr_handler, handler); } - for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) - if (call_used_regs[i] && ! fixed_regs[i] - && i != REGNO (rcontext) && i != REGNO (rsp)) - break; - if (i == FIRST_PSEUDO_REGISTER) - abort(); - - rra = gen_rtx_REG (Pmode, i); - - *pcontext = rcontext; - *psp = rsp; - *pra = rra; -} - -/* Retrieve the register which contains the pointer to the eh_context - structure set the __throw. */ - -#if 0 -rtx -get_reg_for_handler () -{ - rtx reg1; - reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node), - current_function_decl); - return reg1; -} -#endif - -/* Set up the epilogue with the magic bits we'll need to return to the - exception handler. */ - -void -expand_builtin_eh_return (context, stack, handler) - tree context, stack, handler; -{ - if (eh_return_context) - error("Duplicate call to __builtin_eh_return"); - - eh_return_context - = copy_to_reg (expand_expr (context, NULL_RTX, VOIDmode, 0)); - eh_return_stack_adjust - = copy_to_reg (expand_expr (stack, NULL_RTX, VOIDmode, 0)); - eh_return_handler - = copy_to_reg (expand_expr (handler, NULL_RTX, VOIDmode, 0)); + emit_jump (cfun->eh->ehr_label); } void expand_eh_return () { - rtx reg1, reg2, reg3; - rtx stub_start, after_stub; - rtx ra, tmp; + rtx sa, ra, around_label; - if (!eh_return_context) + if (! cfun->eh->ehr_label) return; - current_function_cannot_inline = N_("function uses __builtin_eh_return"); - - eh_regs (®1, ®2, ®3, 1); -#ifdef POINTERS_EXTEND_UNSIGNED - eh_return_context = convert_memory_address (Pmode, eh_return_context); - eh_return_stack_adjust = - convert_memory_address (Pmode, eh_return_stack_adjust); - eh_return_handler = convert_memory_address (Pmode, eh_return_handler); -#endif - emit_move_insn (reg1, eh_return_context); - emit_move_insn (reg2, eh_return_stack_adjust); - emit_move_insn (reg3, eh_return_handler); - - /* Talk directly to the target's epilogue code when possible. */ - -#ifdef HAVE_eh_epilogue - if (HAVE_eh_epilogue) + sa = EH_RETURN_STACKADJ_RTX; + if (! sa) { - emit_insn (gen_eh_epilogue (reg1, reg2, reg3)); + error ("__builtin_eh_return not supported on this target"); return; } -#endif - - /* Otherwise, use the same stub technique we had before. */ - eh_return_stub_label = stub_start = gen_label_rtx (); - after_stub = gen_label_rtx (); + current_function_calls_eh_return = 1; - /* Set the return address to the stub label. */ + around_label = gen_label_rtx (); + emit_move_insn (sa, const0_rtx); + emit_jump (around_label); - ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, - 0, hard_frame_pointer_rtx); - if (GET_CODE (ra) == REG && REGNO (ra) >= FIRST_PSEUDO_REGISTER) - abort(); + emit_label (cfun->eh->ehr_label); + clobber_return_register (); - tmp = memory_address (Pmode, gen_rtx_LABEL_REF (Pmode, stub_start)); -#ifdef RETURN_ADDR_OFFSET - tmp = plus_constant (tmp, -RETURN_ADDR_OFFSET); +#ifdef HAVE_eh_return + if (HAVE_eh_return) + emit_insn (gen_eh_return (cfun->eh->ehr_stackadj, cfun->eh->ehr_handler)); + else #endif - tmp = force_operand (tmp, ra); - if (tmp != ra) - emit_move_insn (ra, tmp); - - /* Indicate that the registers are in fact used. */ - emit_insn (gen_rtx_USE (VOIDmode, reg1)); - emit_insn (gen_rtx_USE (VOIDmode, reg2)); - emit_insn (gen_rtx_USE (VOIDmode, reg3)); - if (GET_CODE (ra) == REG) - emit_insn (gen_rtx_USE (VOIDmode, ra)); - - /* Generate the stub. */ - - emit_jump (after_stub); - emit_label (stub_start); + { + ra = EH_RETURN_HANDLER_RTX; + if (! ra) + { + error ("__builtin_eh_return not supported on this target"); + ra = gen_reg_rtx (Pmode); + } - eh_regs (®1, ®2, ®3, 0); - adjust_stack (reg2); - emit_indirect_jump (reg3); + emit_move_insn (sa, cfun->eh->ehr_stackadj); + emit_move_insn (ra, cfun->eh->ehr_handler); + } - emit_label (after_stub); + emit_label (around_label); } +struct action_record +{ + int offset; + int filter; + int next; +}; -/* This contains the code required to verify whether arbitrary instructions - are in the same exception region. */ +static int +action_record_eq (pentry, pdata) + const PTR pentry; + const PTR pdata; +{ + const struct action_record *entry = (const struct action_record *) pentry; + const struct action_record *data = (const struct action_record *) pdata; + return entry->filter == data->filter && entry->next == data->next; +} -static int *insn_eh_region = (int *)0; -static int maximum_uid; +static hashval_t +action_record_hash (pentry) + const PTR pentry; +{ + const struct action_record *entry = (const struct action_record *) pentry; + return entry->next * 1009 + entry->filter; +} -static void -set_insn_eh_region (first, region_num) - rtx *first; - int region_num; +static int +add_action_record (ar_hash, filter, next) + htab_t ar_hash; + int filter, next; { - rtx insn; - int rnum; + struct action_record **slot, *new, tmp; - for (insn = *first; insn; insn = NEXT_INSN (insn)) + tmp.filter = filter; + tmp.next = next; + slot = (struct action_record **) htab_find_slot (ar_hash, &tmp, INSERT); + + if ((new = *slot) == NULL) { - if ((GET_CODE (insn) == NOTE) - && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)) - { - rnum = NOTE_EH_HANDLER (insn); - insn_eh_region[INSN_UID (insn)] = rnum; - insn = NEXT_INSN (insn); - set_insn_eh_region (&insn, rnum); - /* Upon return, insn points to the EH_REGION_END of nested region */ - continue; - } - insn_eh_region[INSN_UID (insn)] = region_num; - if ((GET_CODE (insn) == NOTE) && - (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) - break; + new = (struct action_record *) xmalloc (sizeof (*new)); + new->offset = VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) + 1; + new->filter = filter; + new->next = next; + *slot = new; + + /* The filter value goes in untouched. The link to the next + record is a "self-relative" byte offset, or zero to indicate + that there is no next record. So convert the absolute 1 based + indicies we've been carrying around into a displacement. */ + + push_sleb128 (&cfun->eh->action_record_data, filter); + if (next) + next -= VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) + 1; + push_sleb128 (&cfun->eh->action_record_data, next); } - *first = insn; -} -/* Free the insn table, an make sure it cannot be used again. */ + return new->offset; +} -void -free_insn_eh_region () +static int +collect_one_action_chain (ar_hash, region) + htab_t ar_hash; + struct eh_region *region; { - if (!doing_eh (0)) - return; + struct eh_region *c; + int next; - if (insn_eh_region) + /* If we've reached the top of the region chain, then we have + no actions, and require no landing pad. */ + if (region == NULL) + return -1; + + switch (region->type) { - free (insn_eh_region); - insn_eh_region = (int *)0; + case ERT_CLEANUP: + /* A cleanup adds a zero filter to the beginning of the chain, but + there are special cases to look out for. If there are *only* + cleanups along a path, then it compresses to a zero action. + Further, if there are multiple cleanups along a path, we only + need to represent one of them, as that is enough to trigger + entry to the landing pad at runtime. */ + next = collect_one_action_chain (ar_hash, region->outer); + if (next <= 0) + return 0; + for (c = region->outer; c ; c = c->outer) + if (c->type == ERT_CLEANUP) + return next; + return add_action_record (ar_hash, 0, next); + + case ERT_TRY: + /* Process the associated catch regions in reverse order. + If there's a catch-all handler, then we don't need to + search outer regions. Use a magic -3 value to record + that we havn't done the outer search. */ + next = -3; + for (c = region->u.try.last_catch; c ; c = c->u.catch.prev_catch) + { + if (c->u.catch.type == NULL) + next = add_action_record (ar_hash, c->u.catch.filter, 0); + else + { + if (next == -3) + { + next = collect_one_action_chain (ar_hash, region->outer); + if (next < 0) + next = 0; + } + next = add_action_record (ar_hash, c->u.catch.filter, next); + } + } + return next; + + case ERT_ALLOWED_EXCEPTIONS: + /* An exception specification adds its filter to the + beginning of the chain. */ + next = collect_one_action_chain (ar_hash, region->outer); + return add_action_record (ar_hash, region->u.allowed.filter, + next < 0 ? 0 : next); + + case ERT_MUST_NOT_THROW: + /* A must-not-throw region with no inner handlers or cleanups + requires no call-site entry. Note that this differs from + the no handler or cleanup case in that we do require an lsda + to be generated. Return a magic -2 value to record this. */ + return -2; + + case ERT_CATCH: + case ERT_THROW: + /* CATCH regions are handled in TRY above. THROW regions are + for optimization information only and produce no output. */ + return collect_one_action_chain (ar_hash, region->outer); + + default: + abort (); } } -/* Initialize the table. max_uid must be calculated and handed into - this routine. If it is unavailable, passing a value of 0 will - cause this routine to calculate it as well. */ - -void -init_insn_eh_region (first, max_uid) - rtx first; - int max_uid; +static int +add_call_site (landing_pad, action) + rtx landing_pad; + int action; { - rtx insn; + struct call_site_record *data = cfun->eh->call_site_data; + int used = cfun->eh->call_site_data_used; + int size = cfun->eh->call_site_data_size; - if (!doing_eh (0)) - return; + if (used >= size) + { + size = (size ? size * 2 : 64); + data = (struct call_site_record *) + xrealloc (data, sizeof (*data) * size); + cfun->eh->call_site_data = data; + cfun->eh->call_site_data_size = size; + } - if (insn_eh_region) - free_insn_eh_region(); + data[used].landing_pad = landing_pad; + data[used].action = action; - if (max_uid == 0) - for (insn = first; insn; insn = NEXT_INSN (insn)) - if (INSN_UID (insn) > max_uid) /* find largest UID */ - max_uid = INSN_UID (insn); + cfun->eh->call_site_data_used = used + 1; - maximum_uid = max_uid; - insn_eh_region = (int *) xmalloc ((max_uid + 1) * sizeof (int)); - insn = first; - set_insn_eh_region (&insn, 0); + return used + call_site_base; } +/* Turn REG_EH_REGION notes back into NOTE_INSN_EH_REGION notes. + The new note numbers will not refer to region numbers, but + instead to call site entries. */ -/* Check whether 2 instructions are within the same region. */ - -int -in_same_eh_region (insn1, insn2) - rtx insn1, insn2; +void +convert_to_eh_region_ranges () { - int ret, uid1, uid2; + rtx insn, iter, note; + htab_t ar_hash; + int last_action = -3; + rtx last_action_insn = NULL_RTX; + rtx last_landing_pad = NULL_RTX; + rtx first_no_action_insn = NULL_RTX; + int call_site; - /* If no exceptions, instructions are always in same region. */ - if (!doing_eh (0)) - return 1; + if (USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL) + return; - /* If the table isn't allocated, assume the worst. */ - if (!insn_eh_region) - return 0; + VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data"); - uid1 = INSN_UID (insn1); - uid2 = INSN_UID (insn2); + ar_hash = htab_create (31, action_record_hash, action_record_eq, free); - /* if instructions have been allocated beyond the end, either - the table is out of date, or this is a late addition, or - something... Assume the worst. */ - if (uid1 > maximum_uid || uid2 > maximum_uid) - return 0; + for (iter = get_insns (); iter ; iter = NEXT_INSN (iter)) + if (INSN_P (iter)) + { + struct eh_region *region; + int this_action; + rtx this_landing_pad; - ret = (insn_eh_region[uid1] == insn_eh_region[uid2]); - return ret; -} - + insn = iter; + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SEQUENCE) + insn = XVECEXP (PATTERN (insn), 0, 0); -/* This function will initialize the handler list for a specified block. - It may recursively call itself if the outer block hasn't been processed - yet. At some point in the future we can trim out handlers which we - know cannot be called. (ie, if a block has an INT type handler, - control will never be passed to an outer INT type handler). */ - -static void -process_nestinfo (block, info, nested_eh_region) - int block; - eh_nesting_info *info; - int *nested_eh_region; -{ - handler_info *ptr, *last_ptr = NULL; - int x, y, count = 0; - int extra = 0; - handler_info **extra_handlers = 0; - int index = info->region_index[block]; - - /* If we've already processed this block, simply return. */ - if (info->num_handlers[index] > 0) - return; + note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); + if (!note) + { + if (! (GET_CODE (insn) == CALL_INSN + || (flag_non_call_exceptions + && may_trap_p (PATTERN (insn))))) + continue; + this_action = -1; + region = NULL; + } + else + { + if (INTVAL (XEXP (note, 0)) <= 0) + continue; + region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; + this_action = collect_one_action_chain (ar_hash, region); + } - for (ptr = get_first_handler (block); ptr; last_ptr = ptr, ptr = ptr->next) - count++; + /* Existence of catch handlers, or must-not-throw regions + implies that an lsda is needed (even if empty). */ + if (this_action != -1) + cfun->uses_eh_lsda = 1; - /* pick up any information from the next outer region. It will already - contain a summary of itself and all outer regions to it. */ + /* Delay creation of region notes for no-action regions + until we're sure that an lsda will be required. */ + else if (last_action == -3) + { + first_no_action_insn = iter; + last_action = -1; + } + + /* Cleanups and handlers may share action chains but not + landing pads. Collect the landing pad for this region. */ + if (this_action >= 0) + { + struct eh_region *o; + for (o = region; ! o->landing_pad ; o = o->outer) + continue; + this_landing_pad = o->landing_pad; + } + else + this_landing_pad = NULL_RTX; - if (nested_eh_region [block] != 0) + /* Differing actions or landing pads implies a change in call-site + info, which implies some EH_REGION note should be emitted. */ + if (last_action != this_action + || last_landing_pad != this_landing_pad) + { + /* If we'd not seen a previous action (-3) or the previous + action was must-not-throw (-2), then we do not need an + end note. */ + if (last_action >= -1) + { + /* If we delayed the creation of the begin, do it now. */ + if (first_no_action_insn) + { + call_site = add_call_site (NULL_RTX, 0); + note = emit_note_before (NOTE_INSN_EH_REGION_BEG, + first_no_action_insn); + NOTE_EH_HANDLER (note) = call_site; + first_no_action_insn = NULL_RTX; + } + + note = emit_note_after (NOTE_INSN_EH_REGION_END, + last_action_insn); + NOTE_EH_HANDLER (note) = call_site; + } + + /* If the new action is must-not-throw, then no region notes + are created. */ + if (this_action >= -1) + { + call_site = add_call_site (this_landing_pad, + this_action < 0 ? 0 : this_action); + note = emit_note_before (NOTE_INSN_EH_REGION_BEG, iter); + NOTE_EH_HANDLER (note) = call_site; + } + + last_action = this_action; + last_landing_pad = this_landing_pad; + } + last_action_insn = iter; + } + + if (last_action >= -1 && ! first_no_action_insn) { - int nested_index = info->region_index[nested_eh_region[block]]; - process_nestinfo (nested_eh_region[block], info, nested_eh_region); - extra = info->num_handlers[nested_index]; - extra_handlers = info->handlers[nested_index]; - info->outer_index[index] = nested_index; + note = emit_note_after (NOTE_INSN_EH_REGION_END, last_action_insn); + NOTE_EH_HANDLER (note) = call_site; } - /* If the last handler is either a CATCH_ALL or a cleanup, then we - won't use the outer ones since we know control will not go past the - catch-all or cleanup. */ + htab_delete (ar_hash); +} - if (last_ptr != NULL && (last_ptr->type_info == NULL - || last_ptr->type_info == CATCH_ALL_TYPE)) - extra = 0; + +static void +push_uleb128 (data_area, value) + varray_type *data_area; + unsigned int value; +{ + do + { + unsigned char byte = value & 0x7f; + value >>= 7; + if (value) + byte |= 0x80; + VARRAY_PUSH_UCHAR (*data_area, byte); + } + while (value); +} - info->num_handlers[index] = count + extra; - info->handlers[index] = (handler_info **) xmalloc ((count + extra) - * sizeof (handler_info **)); +static void +push_sleb128 (data_area, value) + varray_type *data_area; + int value; +{ + unsigned char byte; + int more; - /* First put all our handlers into the list. */ - ptr = get_first_handler (block); - for (x = 0; x < count; x++) + do { - info->handlers[index][x] = ptr; - ptr = ptr->next; + byte = value & 0x7f; + value >>= 7; + more = ! ((value == 0 && (byte & 0x40) == 0) + || (value == -1 && (byte & 0x40) != 0)); + if (more) + byte |= 0x80; + VARRAY_PUSH_UCHAR (*data_area, byte); } + while (more); +} - /* Now add all the outer region handlers, if they aren't they same as - one of the types in the current block. We won't worry about - derived types yet, we'll just look for the exact type. */ - for (y =0, x = 0; x < extra ; x++) + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 + +static const char * +eh_data_format_name (format) + int format; +{ + switch (format) { - int i, ok; - ok = 1; - /* Check to see if we have a type duplication. */ - for (i = 0; i < count; i++) - if (info->handlers[index][i]->type_info == extra_handlers[x]->type_info) - { - ok = 0; - /* Record one less handler. */ - (info->num_handlers[index])--; - break; - } - if (ok) - { - info->handlers[index][y + count] = extra_handlers[x]; - y++; - } + case DW_EH_PE_absptr: return "absolute"; + case DW_EH_PE_omit: return "omit"; + + case DW_EH_PE_uleb128: return "uleb128"; + case DW_EH_PE_udata2: return "udata2"; + case DW_EH_PE_udata4: return "udata4"; + case DW_EH_PE_udata8: return "udata8"; + case DW_EH_PE_sleb128: return "sleb128"; + case DW_EH_PE_sdata2: return "sdata2"; + case DW_EH_PE_sdata4: return "sdata4"; + case DW_EH_PE_sdata8: return "sdata8"; + + case DW_EH_PE_uleb128 | DW_EH_PE_pcrel: return "pcrel uleb128"; + case DW_EH_PE_udata2 | DW_EH_PE_pcrel: return "pcrel udata2"; + case DW_EH_PE_udata4 | DW_EH_PE_pcrel: return "pcrel udata4"; + case DW_EH_PE_udata8 | DW_EH_PE_pcrel: return "pcrel udata8"; + case DW_EH_PE_sleb128 | DW_EH_PE_pcrel: return "pcrel sleb128"; + case DW_EH_PE_sdata2 | DW_EH_PE_pcrel: return "pcrel sdata2"; + case DW_EH_PE_sdata4 | DW_EH_PE_pcrel: return "pcrel sdata4"; + case DW_EH_PE_sdata8 | DW_EH_PE_pcrel: return "pcrel sdata8"; + + case DW_EH_PE_uleb128 | DW_EH_PE_textrel: return "textrel uleb128"; + case DW_EH_PE_udata2 | DW_EH_PE_textrel: return "textrel udata2"; + case DW_EH_PE_udata4 | DW_EH_PE_textrel: return "textrel udata4"; + case DW_EH_PE_udata8 | DW_EH_PE_textrel: return "textrel udata8"; + case DW_EH_PE_sleb128 | DW_EH_PE_textrel: return "textrel sleb128"; + case DW_EH_PE_sdata2 | DW_EH_PE_textrel: return "textrel sdata2"; + case DW_EH_PE_sdata4 | DW_EH_PE_textrel: return "textrel sdata4"; + case DW_EH_PE_sdata8 | DW_EH_PE_textrel: return "textrel sdata8"; + + case DW_EH_PE_uleb128 | DW_EH_PE_datarel: return "datarel uleb128"; + case DW_EH_PE_udata2 | DW_EH_PE_datarel: return "datarel udata2"; + case DW_EH_PE_udata4 | DW_EH_PE_datarel: return "datarel udata4"; + case DW_EH_PE_udata8 | DW_EH_PE_datarel: return "datarel udata8"; + case DW_EH_PE_sleb128 | DW_EH_PE_datarel: return "datarel sleb128"; + case DW_EH_PE_sdata2 | DW_EH_PE_datarel: return "datarel sdata2"; + case DW_EH_PE_sdata4 | DW_EH_PE_datarel: return "datarel sdata4"; + case DW_EH_PE_sdata8 | DW_EH_PE_datarel: return "datarel sdata8"; + + case DW_EH_PE_uleb128 | DW_EH_PE_funcrel: return "funcrel uleb128"; + case DW_EH_PE_udata2 | DW_EH_PE_funcrel: return "funcrel udata2"; + case DW_EH_PE_udata4 | DW_EH_PE_funcrel: return "funcrel udata4"; + case DW_EH_PE_udata8 | DW_EH_PE_funcrel: return "funcrel udata8"; + case DW_EH_PE_sleb128 | DW_EH_PE_funcrel: return "funcrel sleb128"; + case DW_EH_PE_sdata2 | DW_EH_PE_funcrel: return "funcrel sdata2"; + case DW_EH_PE_sdata4 | DW_EH_PE_funcrel: return "funcrel sdata4"; + case DW_EH_PE_sdata8 | DW_EH_PE_funcrel: return "funcrel sdata8"; + + default: + abort (); } } -/* This function will allocate and initialize an eh_nesting_info structure. - It returns a pointer to the completed data structure. If there are - no exception regions, a NULL value is returned. */ - -eh_nesting_info * -init_eh_nesting_info () +#ifndef HAVE_AS_LEB128 +static int +dw2_size_of_call_site_table () { - int *nested_eh_region; - int region_count = 0; - rtx eh_note = NULL_RTX; - eh_nesting_info *info; - rtx insn; - int x; + int n = cfun->eh->call_site_data_used; + int size = n * (4 + 4 + 4); + int i; - if (! flag_exceptions) - return 0; + for (i = 0; i < n; ++i) + { + struct call_site_record *cs = &cfun->eh->call_site_data[i]; + size += size_of_uleb128 (cs->action); + } - info = (eh_nesting_info *) xmalloc (sizeof (eh_nesting_info)); - info->region_index = (int *) xcalloc ((max_label_num () + 1), sizeof (int)); - nested_eh_region = (int *) xcalloc (max_label_num () + 1, sizeof (int)); + return size; +} - /* Create the nested_eh_region list. If indexed with a block number, it - returns the block number of the next outermost region, if any. - We can count the number of regions and initialize the region_index - vector at the same time. */ - for (insn = get_insns(); insn; insn = NEXT_INSN (insn)) +static int +sjlj_size_of_call_site_table () +{ + int n = cfun->eh->call_site_data_used; + int size = 0; + int i; + + for (i = 0; i < n; ++i) { - if (GET_CODE (insn) == NOTE) - { - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - { - int block = NOTE_EH_HANDLER (insn); - region_count++; - info->region_index[block] = region_count; - if (eh_note) - nested_eh_region [block] = - NOTE_EH_HANDLER (XEXP (eh_note, 0)); - else - nested_eh_region [block] = 0; - eh_note = gen_rtx_EXPR_LIST (VOIDmode, insn, eh_note); - } - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) - eh_note = XEXP (eh_note, 1); - } + struct call_site_record *cs = &cfun->eh->call_site_data[i]; + size += size_of_uleb128 (INTVAL (cs->landing_pad)); + size += size_of_uleb128 (cs->action); } - - /* If there are no regions, wrap it up now. */ - if (region_count == 0) + + return size; +} +#endif + +static void +dw2_output_call_site_table () +{ + const char *function_start_lab + = IDENTIFIER_POINTER (current_function_func_begin_label); + int n = cfun->eh->call_site_data_used; + int i; + + for (i = 0; i < n; ++i) { - free (info->region_index); - free (info); - free (nested_eh_region); - return NULL; + struct call_site_record *cs = &cfun->eh->call_site_data[i]; + char reg_start_lab[32]; + char reg_end_lab[32]; + char landing_pad_lab[32]; + + ASM_GENERATE_INTERNAL_LABEL (reg_start_lab, "LEHB", call_site_base + i); + ASM_GENERATE_INTERNAL_LABEL (reg_end_lab, "LEHE", call_site_base + i); + + if (cs->landing_pad) + ASM_GENERATE_INTERNAL_LABEL (landing_pad_lab, "L", + CODE_LABEL_NUMBER (cs->landing_pad)); + + /* ??? Perhaps use insn length scaling if the assembler supports + generic arithmetic. */ + /* ??? Perhaps use attr_length to choose data1 or data2 instead of + data4 if the function is small enough. */ +#ifdef HAVE_AS_LEB128 + dw2_asm_output_delta_uleb128 (reg_start_lab, function_start_lab, + "region %d start", i); + dw2_asm_output_delta_uleb128 (reg_end_lab, reg_start_lab, + "length"); + if (cs->landing_pad) + dw2_asm_output_delta_uleb128 (landing_pad_lab, function_start_lab, + "landing pad"); + else + dw2_asm_output_data_uleb128 (0, "landing pad"); +#else + dw2_asm_output_delta (4, reg_start_lab, function_start_lab, + "region %d start", i); + dw2_asm_output_delta (4, reg_end_lab, reg_start_lab, "length"); + if (cs->landing_pad) + dw2_asm_output_delta (4, landing_pad_lab, function_start_lab, + "landing pad"); + else + dw2_asm_output_data (4, 0, "landing pad"); +#endif + dw2_asm_output_data_uleb128 (cs->action, "action"); } - region_count++; - info->handlers = (handler_info ***) xcalloc (region_count, - sizeof (handler_info ***)); - info->num_handlers = (int *) xcalloc (region_count, sizeof (int)); - info->outer_index = (int *) xcalloc (region_count, sizeof (int)); + call_site_base += n; +} + +static void +sjlj_output_call_site_table () +{ + int n = cfun->eh->call_site_data_used; + int i; - /* Now initialize the handler lists for all exception blocks. */ - for (x = 0; x <= max_label_num (); x++) + for (i = 0; i < n; ++i) { - if (info->region_index[x] != 0) - process_nestinfo (x, info, nested_eh_region); - } - info->region_count = region_count; + struct call_site_record *cs = &cfun->eh->call_site_data[i]; - /* Clean up. */ - free (nested_eh_region); + dw2_asm_output_data_uleb128 (INTVAL (cs->landing_pad), + "region %d landing pad", i); + dw2_asm_output_data_uleb128 (cs->action, "action"); + } - return info; + call_site_base += n; } +void +output_function_exception_table () +{ + int format, i, n; +#ifdef HAVE_AS_LEB128 + char ttype_label[32]; + char cs_after_size_label[32]; + char cs_end_label[32]; +#else + int call_site_len; +#endif + int have_tt_data; + int funcdef_number; -/* This function is used to retreive the vector of handlers which - can be reached by a given insn in a given exception region. - BLOCK is the exception block the insn is in. - INFO is the eh_nesting_info structure. - INSN is the (optional) insn within the block. If insn is not NULL_RTX, - it may contain reg notes which modify its throwing behavior, and - these will be obeyed. If NULL_RTX is passed, then we simply return the - handlers for block. - HANDLERS is the address of a pointer to a vector of handler_info pointers. - Upon return, this will have the handlers which can be reached by block. - This function returns the number of elements in the handlers vector. */ + /* Not all functions need anything. */ + if (! cfun->uses_eh_lsda) + return; -int -reachable_handlers (block, info, insn, handlers) - int block; - eh_nesting_info *info; - rtx insn ; - handler_info ***handlers; -{ - int index = 0; - *handlers = NULL; + funcdef_number = (USING_SJLJ_EXCEPTIONS + ? sjlj_funcdef_number + : current_funcdef_number); - if (info == NULL) - return 0; - if (block > 0) - index = info->region_index[block]; - - if (insn && GET_CODE (insn) == CALL_INSN) - { - /* RETHROWs specify a region number from which we are going to rethrow. - This means we won't pass control to handlers in the specified - region, but rather any region OUTSIDE the specified region. - We accomplish this by setting block to the outer_index of the - specified region. */ - rtx note = find_reg_note (insn, REG_EH_RETHROW, NULL_RTX); - if (note) + exception_section (); + + have_tt_data = (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) > 0 + || VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) > 0); + + if (have_tt_data) + assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode)); + + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LLSDA", funcdef_number); + + /* The LSDA header. */ + + /* Indicate the format of the landing pad start pointer. An omitted + field implies @LPStart == @Start. */ + /* Currently we always put @LPStart == @Start. This field would + be most useful in moving the landing pads completely out of + line to another section, but it could also be used to minimize + the size of uleb128 landing pad offsets. */ + format = DW_EH_PE_omit; + dw2_asm_output_data (1, format, "@LPStart format (%s)", + eh_data_format_name (format)); + + /* @LPStart pointer would go here. */ + + /* Indicate the format of the @TType entries. */ + if (! have_tt_data) + format = DW_EH_PE_omit; + else + { + /* ??? Define a ASM_PREFERRED_DATA_FORMAT to say what + sort of dynamic-relocation-free reference to emit. */ + format = 0; +#ifdef HAVE_AS_LEB128 + ASM_GENERATE_INTERNAL_LABEL (ttype_label, "LLSDATT", funcdef_number); +#endif + } + dw2_asm_output_data (1, format, "@TType format (%s)", + eh_data_format_name (format)); + +#ifndef HAVE_AS_LEB128 + if (USING_SJLJ_EXCEPTIONS) + call_site_len = sjlj_size_of_call_site_table (); + else + call_site_len = dw2_size_of_call_site_table (); +#endif + + /* A pc-relative 4-byte displacement to the @TType data. */ + if (have_tt_data) + { +#ifdef HAVE_AS_LEB128 + char ttype_after_disp_label[32]; + ASM_GENERATE_INTERNAL_LABEL (ttype_after_disp_label, "LLSDATTD", + funcdef_number); + dw2_asm_output_delta_uleb128 (ttype_label, ttype_after_disp_label, + "@TType base offset"); + ASM_OUTPUT_LABEL (asm_out_file, ttype_after_disp_label); +#else + /* Ug. Alignment queers things. */ + unsigned int before_disp, after_disp, last_disp, disp, align; + + align = POINTER_SIZE / BITS_PER_UNIT; + before_disp = 1 + 1; + after_disp = (1 + size_of_uleb128 (call_site_len) + + call_site_len + + VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) + + VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) * align); + + disp = after_disp; + do { - index = eh_region_from_symbol (XEXP (note, 0)); - index = info->region_index[index]; - if (index) - index = info->outer_index[index]; - } - else - { - /* If there is no rethrow, we look for a REG_EH_REGION, and - we'll throw from that block. A value of 0 or less - indicates that this insn cannot throw. */ - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (note) - { - int b = INTVAL (XEXP (note, 0)); - if (b <= 0) - index = 0; - else - index = info->region_index[b]; - } + unsigned int disp_size, pad; + + last_disp = disp; + disp_size = size_of_uleb128 (disp); + pad = before_disp + disp_size + after_disp; + if (pad % align) + pad = align - (pad % align); + else + pad = 0; + disp = after_disp + pad; } + while (disp != last_disp); + + dw2_asm_output_data_uleb128 (disp, "@TType base offset"); +#endif } - /* If we reach this point, and index is 0, there is no throw. */ - if (index == 0) - return 0; - - *handlers = info->handlers[index]; - return info->num_handlers[index]; -} + /* Indicate the format of the call-site offsets. */ +#ifdef HAVE_AS_LEB128 + format = DW_EH_PE_uleb128; +#else + format = DW_EH_PE_udata4; +#endif + dw2_asm_output_data (1, format, "call-site format (%s)", + eh_data_format_name (format)); + +#ifdef HAVE_AS_LEB128 + ASM_GENERATE_INTERNAL_LABEL (cs_after_size_label, "LLSDACSB", + funcdef_number); + ASM_GENERATE_INTERNAL_LABEL (cs_end_label, "LLSDACSE", + funcdef_number); + dw2_asm_output_delta_uleb128 (cs_end_label, cs_after_size_label, + "Call-site table length"); + ASM_OUTPUT_LABEL (asm_out_file, cs_after_size_label); + if (USING_SJLJ_EXCEPTIONS) + sjlj_output_call_site_table (); + else + dw2_output_call_site_table (); + ASM_OUTPUT_LABEL (asm_out_file, cs_end_label); +#else + dw2_asm_output_data_uleb128 (call_site_len,"Call-site table length"); + if (USING_SJLJ_EXCEPTIONS) + sjlj_output_call_site_table (); + else + dw2_output_call_site_table (); +#endif + + /* ??? Decode and interpret the data for flag_debug_asm. */ + n = VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data); + for (i = 0; i < n; ++i) + dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->action_record_data, i), + (i ? NULL : "Action record table")); -/* This function will free all memory associated with the eh_nesting info. */ + if (have_tt_data) + assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode)); -void -free_eh_nesting_info (info) - eh_nesting_info *info; -{ - int x; - if (info != NULL) + i = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data); + while (i-- > 0) { - if (info->region_index) - free (info->region_index); - if (info->num_handlers) - free (info->num_handlers); - if (info->outer_index) - free (info->outer_index); - if (info->handlers) - { - for (x = 0; x < info->region_count; x++) - if (info->handlers[x]) - free (info->handlers[x]); - free (info->handlers); - } - free (info); + tree type = VARRAY_TREE (cfun->eh->ttype_data, i); + + if (type == NULL_TREE) + type = integer_zero_node; + else + type = lookup_type_for_runtime (type); + + /* ??? Handle ASM_PREFERRED_DATA_FORMAT. */ + output_constant (type, GET_MODE_SIZE (ptr_mode)); } + +#ifdef HAVE_AS_LEB128 + if (have_tt_data) + ASM_OUTPUT_LABEL (asm_out_file, ttype_label); +#endif + + /* ??? Decode and interpret the data for flag_debug_asm. */ + n = VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data); + for (i = 0; i < n; ++i) + dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->ehspec_data, i), + (i ? NULL : "Exception specification table")); + + function_section (current_function_decl); + + if (USING_SJLJ_EXCEPTIONS) + sjlj_funcdef_number += 1; } diff --git a/gcc/except.h b/gcc/except.h index 2b281bb..d1688a2 100644 --- a/gcc/except.h +++ b/gcc/except.h @@ -19,516 +19,187 @@ along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#if !defined(NULL_RTX) && !defined(rtx) -typedef struct rtx_def *_except_rtx; -#define rtx _except_rtx -#endif - -#ifdef TREE_CODE - -/* A stack of labels. CHAIN points to the next entry in the stack. */ - -struct label_node { - union { - rtx rlabel; - tree tlabel; - } u; - struct label_node *chain; -}; - -/* An eh_entry is used to describe one exception handling region. - - OUTER_CONTEXT is the label used for rethrowing into the outer context. - - EXCEPTION_HANDLER_LABEL is the label corresponding to the handler - for this region. - - LABEL_USED indicates whether a CATCH block has already used this - label or not. New ones are needed for additional catch blocks if - it has. - FALSE_LABEL is used when either setjmp/longjmp exceptions are in - use, or old style table exceptions. It contains the label for - branching to the next runtime type check as handlers are processed. - - FINALIZATION is the tree codes for the handler, or is NULL_TREE if - one hasn't been generated yet, or is integer_zero_node to mark the - end of a group of try blocks. */ - -struct eh_entry { - rtx outer_context; - rtx exception_handler_label; - tree finalization; - int label_used; - rtx false_label; - rtx rethrow_label; - /* If non-zero, this entry is for a handler created when we left an - exception-region via goto. */ - unsigned goto_entry_p : 1; -}; -#else -struct label_node; -struct eh_entry; +#ifndef TREE_CODE +union tree_node; +#define tree union tree_node * #endif -/* A list of EH_ENTRYs. ENTRY is the entry; CHAIN points to the next - entry in the list, or is NULL if this is the last entry. */ - -struct eh_node { - struct eh_entry *entry; - struct eh_node *chain; -}; - -/* A stack of EH_ENTRYs. TOP is the topmost entry on the stack. TOP is - NULL if the stack is empty. */ - -struct eh_stack { - struct eh_node *top; -}; - -/* A queue of EH_ENTRYs. HEAD is the front of the queue; TAIL is the - end (the latest entry). HEAD and TAIL are NULL if the queue is - empty. */ - -struct eh_queue { - struct eh_node *head; - struct eh_node *tail; - struct eh_queue *next; -}; - -/* Used to save exception handling status for each function. */ -struct eh_status -{ - /* A stack used for keeping track of the currently active exception - handling region. As each exception region is started, an entry - describing the region is pushed onto this stack. The current - region can be found by looking at the top of the stack, and as we - exit regions, the corresponding entries are popped. - - Entries cannot overlap; they can be nested. So there is only one - entry at most that corresponds to the current instruction, and that - is the entry on the top of the stack. */ - struct eh_stack x_ehstack; - /* This stack is used to represent what the current eh region is - for the catch blocks beings processed */ - struct eh_stack x_catchstack; - /* A queue used for tracking which exception regions have closed. - As we exit a region, we enqueue a new entry. The entries are then - dequeued during expand_leftover_cleanups and - expand_start_all_catch. */ - struct eh_queue *x_ehqueue; - /* Insns for all of the exception handlers for the current function. - They are currently emitted by the frontend code. */ - rtx x_catch_clauses; - /* End of exception handler insn sequence. */ - rtx x_catch_clauses_last; - /* A random data area for the front end's own use. */ - struct label_node *x_false_label_stack; - /* Keeps track of the label to resume to should one want to resume - normal control flow out of a handler (instead of, say, returning to - the caller of the current function or exiting the program). */ - struct label_node *x_caught_return_label_stack; - /* A stack (TREE_LIST) of lists of handlers. The TREE_VALUE of each - node is itself a TREE_CHAINed list of handlers for regions that - are not yet closed. The TREE_VALUE of each entry contains the - handler for the corresponding entry on the ehstack. */ - union tree_node *x_protect_list; - /* The EH context. Nonzero if the function has already - fetched a pointer to the EH context for exception handling. */ - rtx ehc; - /* The label generated by expand_builtin_eh_return. */ - rtx x_eh_return_stub_label; -}; - -#define ehstack (cfun->eh->x_ehstack) -#define catchstack (cfun->eh->x_catchstack) -#define ehqueue (cfun->eh->x_ehqueue) -#define catch_clauses (cfun->eh->x_catch_clauses) -#define catch_clauses_last (cfun->eh->x_catch_clauses_last) -#define false_label_stack (cfun->eh->x_false_label_stack) -#define caught_return_label_stack (cfun->eh->x_caught_return_label_stack) -#define protect_list (cfun->eh->x_protect_list) -#define current_function_ehc (cfun->eh->ehc) -#define eh_return_stub_label (cfun->eh->x_eh_return_stub_label) - -#ifdef TREE_CODE -/* Start an exception handling region. All instructions emitted after - this point are considered to be part of the region until - expand_eh_region_end () is invoked. */ - -extern void expand_eh_region_start PARAMS ((void)); - -/* Just like expand_eh_region_start, except if a cleanup action is - entered on the cleanup chain, the TREE_PURPOSE of the element put - on the chain is DECL. DECL should be the associated VAR_DECL, if - any, otherwise it should be NULL_TREE. */ - -extern void expand_eh_region_start_for_decl PARAMS ((tree)); - -/* Start an exception handling region for the given cleanup action. - All instructions emitted after this point are considered to be part - of the region until expand_eh_region_end () is invoked. CLEANUP is - the cleanup action to perform. The return value is true if the - exception region was optimized away. If that case, - expand_eh_region_end does not need to be called for this cleanup, - nor should it be. - - This routine notices one particular common case in C++ code - generation, and optimizes it so as to not need the exception - region. */ - -extern int expand_eh_region_start_tree PARAMS ((tree, tree)); - -/* End an exception handling region. The information about the region - is found on the top of ehstack. - - HANDLER is either the cleanup for the exception region, or if we're - marking the end of a try block, HANDLER is integer_zero_node. - - HANDLER will be transformed to rtl when expand_leftover_cleanups () - is invoked. */ - -extern void expand_eh_region_end PARAMS ((tree)); - -/* Push RLABEL or TLABEL onto LABELSTACK. Only one of RLABEL or TLABEL - should be set; the other must be NULL. */ - -extern void push_label_entry PARAMS ((struct label_node **labelstack, - rtx rlabel, tree tlabel)); - -/* Pop the topmost entry from LABELSTACK and return its value as an - rtx node. If LABELSTACK is empty, return NULL. */ - -extern rtx pop_label_entry PARAMS ((struct label_node **labelstack)); - -/* Return the topmost entry of LABELSTACK as a tree node, or return - NULL_TREE if LABELSTACK is empty. */ - -extern tree top_label_entry PARAMS ((struct label_node **labelstack)); - +#ifndef RTX_CODE +struct rtx_def; +#define rtx struct rtx_def * #endif -/* Test: is exception handling turned on? */ - -extern int doing_eh PARAMS ((int)); - -/* Toplevel initialization for EH. */ - -void set_exception_lang_code PARAMS ((int)); -void set_exception_version_code PARAMS ((int)); - -/* A list of handlers asocciated with an exception region. HANDLER_LABEL - is the the label that control should be transfered to if the data - in TYPE_INFO matches an exception. a value of NULL_TREE for TYPE_INFO - means This is a cleanup, and must always be called. A value of - CATCH_ALL_TYPE works like a cleanup, but a call to the runtime matcher - is still performed to avoid being caught by a different language - exception. NEXT is a pointer to the next handler for this region. - NULL means there are no more. */ - -typedef struct handler_info -{ - rtx handler_label; - int handler_number; - void *type_info; - struct handler_info *next; -} handler_info; - - -/* Add new handler information to an exception range. The first parameter - specifies the range number (returned from new_eh_entry()). The second - parameter specifies the handler. By default the handler is inserted at - the end of the list. A handler list may contain only ONE NULL_TREE - typeinfo entry. Regardless where it is positioned, a NULL_TREE entry - is always output as the LAST handler in the exception table for a region. */ - -void add_new_handler PARAMS ((int, struct handler_info *)); - -/* Remove a handler label. The handler label is being deleted, so all - regions which reference this handler should have it removed from their - list of possible handlers. Any region which has the final handler - removed can be deleted. */ - -void remove_handler PARAMS ((rtx)); - -/* Create a new handler structure initialized with the handler label and - typeinfo fields passed in. */ - -struct handler_info *get_new_handler PARAMS ((rtx, void *)); - -/* Make a duplicate of an exception region by copying all the handlers - for an exception region. Return the new handler index. */ - -int duplicate_eh_handlers PARAMS ((int, int, rtx (*)(rtx))); - -/* map symbol refs for rethrow */ - -rtx rethrow_symbol_map PARAMS ((rtx, rtx (*)(rtx))); - -/* Is the rethrow label for a region used? */ - -int rethrow_used PARAMS ((int)); - -/* Update the rethrow references to reflect rethrows which have been - optimized away. */ - -void update_rethrow_references PARAMS ((void)); - -/* Get a pointer to the first handler in an exception region's list. */ - -struct handler_info *get_first_handler PARAMS ((int)); - -/* Find all the runtime handlers type matches currently referenced */ - -int find_all_handler_type_matches PARAMS ((void ***)); - -/* The eh_nesting_info structure is used to find a list of valid handlers - for any arbitrary exception region. When init_eh_nesting_info is called, - the information is all pre-calculated and entered in this structure. - REGION_INDEX is a vector over all possible region numbers. Since the - number of regions is typically much smaller than the range of block - numbers, this is a sparse vector and the other data structures are - represented as dense vectors. Indexed with an exception region number, this - returns the index to use in the other data structures to retreive the - correct information. - HANDLERS is an array of vectors which point to handler_info structures. - when indexed, it gives the list of all possible handlers which can - be reached by a throw from this exception region. - NUM_HANDLERS is the equivilent array indicating how many handler - pointers there are in the HANDLERS vector. - OUTER_INDEX indicates which index represents the information for the - outer block. 0 indicates there is no outer context. - REGION_COUNT is the number of regions. */ - -typedef struct eh_nesting -{ - int *region_index; - handler_info ***handlers; - int *num_handlers; - int *outer_index; - int region_count; -} eh_nesting_info; - -/* Initialize the eh_nesting_info structure. */ - -eh_nesting_info *init_eh_nesting_info PARAMS ((void)); - -/* Get a list of handlers reachable from a an exception region/insn. */ - -int reachable_handlers PARAMS ((int, eh_nesting_info *, rtx, - handler_info ***handlers)); - -/* Free the eh_nesting_info structure. */ - -void free_eh_nesting_info PARAMS ((eh_nesting_info *)); - -extern void init_eh PARAMS ((void)); - -/* Initialization for the per-function EH data. */ - -extern void init_eh_for_function PARAMS ((void)); - -/* Generate an exception label. Use instead of gen_label_rtx */ - -extern rtx gen_exception_label PARAMS ((void)); - -/* Adds an EH table entry for EH entry number N. Called from - final_scan_insn for NOTE_INSN_EH_REGION_BEG. */ - -extern void add_eh_table_entry PARAMS ((int n)); - -/* Start a catch clause, triggered by runtime value paramter. */ - -#ifdef TREE_CODE -extern void start_catch_handler PARAMS ((tree)); +#ifndef _VARRAY_H_ +struct varray_head_tag; +#define varray_type struct varray_head_tag * #endif -/* End an individual catch clause. */ - -extern void end_catch_handler PARAMS ((void)); - -/* Returns a non-zero value if we need to output an exception table. */ - -extern int exception_table_p PARAMS ((void)); - -/* Outputs the exception table if we have one. */ -extern void output_exception_table PARAMS ((void)); -extern void output_exception_table_data PARAMS ((void)); +/* Per-function EH data. Used only in except.c, but GC and others + manipulate pointers to the opaque type. */ +struct eh_status; -/* Free the exception table. */ +/* Internal structure describing a region. */ +struct eh_region; -extern void free_exception_table PARAMS((void)); +/* Test: is exception handling turned on? */ +extern int doing_eh PARAMS ((int)); -/* Used by the ia64 unwind format to output data for an individual - function. */ +/* Start an exception handling region. All instructions emitted after + this point are considered to be part of the region until an + expand_eh_region_end variant is invoked. */ +extern void expand_eh_region_start PARAMS ((void)); -extern void output_function_exception_table PARAMS((void)); +/* End an exception handling region for a cleanup. HANDLER is an + expression to expand for the cleanup. */ +extern void expand_eh_region_end_cleanup PARAMS ((tree)); -/* Given a return address in ADDR, determine the address we should use - to find the corresponding EH region. */ +/* End an exception handling region for a try block, and prepares + for subsequent calls to expand_start_catch. */ +extern void expand_start_all_catch PARAMS ((void)); -extern rtx eh_outer_context PARAMS ((rtx addr)); +/* Begin a catch clause. TYPE is an object to be matched by the + runtime, or null if this is a catch-all clause. */ +extern void expand_start_catch PARAMS ((tree)); -/* Called at the start of a block of try statements for which there is - a supplied catch handler. */ +/* End a catch clause. Control will resume after the try/catch block. */ +extern void expand_end_catch PARAMS ((void)); -extern void expand_start_try_stmts PARAMS ((void)); +/* End a sequence of catch handlers for a try block. */ +extern void expand_end_all_catch PARAMS ((void)); -/* Called at the start of a block of catch statements. It terminates the - previous set of try statements. */ +/* End an exception region for an exception type filter. ALLOWED is a + TREE_LIST of TREE_VALUE objects to be matched by the runtime. + FAILURE is a function to invoke if a mismatch ocurrs. */ +extern void expand_eh_region_end_allowed PARAMS ((tree, tree)); -extern void expand_start_all_catch PARAMS ((void)); +/* End an exception region for a must-not-throw filter. FAILURE is a + function to invoke if an uncaught exception propagates this far. */ +extern void expand_eh_region_end_must_not_throw PARAMS ((tree)); -/* Called at the end of a block of catch statements. */ +/* End an exception region for a throw. No handling goes on here, + but it's the easiest way for the front-end to indicate what type + is being thrown. */ +extern void expand_eh_region_end_throw PARAMS ((tree)); -extern void expand_end_all_catch PARAMS ((void)); +/* End a fixup region. Within this region the cleanups for the immediately + enclosing region are _not_ run. This is used for goto cleanup to avoid + destroying an object twice. */ +extern void expand_eh_region_end_fixup PARAMS ((tree)); /* Begin a region that will contain entries created with add_partial_entry. */ - extern void begin_protect_partials PARAMS ((void)); -#ifdef TREE_CODE /* Create a new exception region and add the handler for the region - onto a list. These regions will be ended (and their handlers - emitted) when end_protect_partials is invoked. */ - -extern void add_partial_entry PARAMS ((tree handler)); -#endif + onto a list. These regions will be ended (and their handlers emitted) + when end_protect_partials is invoked. */ +extern void add_partial_entry PARAMS ((tree)); /* End all of the pending exception regions that have handlers added with - push_protect_entry (). */ - + add_partial_entry. */ extern void end_protect_partials PARAMS ((void)); -/* An internal throw. */ - -extern void expand_internal_throw PARAMS ((void)); - -/* Called from expand_exception_blocks and expand_end_catch_block to - expand and pending handlers. */ - -extern void expand_leftover_cleanups PARAMS ((void)); - -/* If necessary, emit insns to get EH context for the current - function. */ - -extern void emit_eh_context PARAMS ((void)); - -/* Builds a list of handler labels and puts them in the global - variable exception_handler_labels. */ - -extern void find_exception_handler_labels PARAMS ((void)); - -/* Determine if an arbitrary label is an exception label */ - -extern int is_exception_handler_label PARAMS ((int)); - -/* Performs sanity checking on the check_exception_handler_labels - list. */ - -extern void check_exception_handler_labels PARAMS ((void)); - -/* Keeps track of the label used as the context of a throw to rethrow an - exception to the outer exception region. */ - -extern struct label_node *outer_context_label_stack; - -/* A list of labels used for exception handlers. It is created by - find_exception_handler_labels for the optimization passes. */ +/* A list of labels used for exception handlers. */ extern rtx exception_handler_labels; /* Determine if the given INSN can throw an exception. */ - -extern int can_throw_internal PARAMS ((rtx)); +extern bool can_throw_internal PARAMS ((rtx)); +extern bool can_throw_external PARAMS ((rtx)); /* Return nonzero if nothing in this function can throw. */ +extern bool nothrow_function_p PARAMS ((void)); -extern int nothrow_function_p PARAMS ((void)); +/* After initial rtl generation, call back to finish generating + exception support code. */ +extern void finish_eh_generation PARAMS ((void)); -/* Performs optimizations for exception handling, such as removing - unnecessary exception regions. Invoked from jump_optimize (). */ +extern void init_eh PARAMS ((void)); +extern void init_eh_for_function PARAMS ((void)); -extern void exception_optimize PARAMS ((void)); +extern rtx reachable_handlers PARAMS ((rtx)); +extern void maybe_remove_eh_handler PARAMS ((rtx)); -/* Return EH context (and set it up once per fn). */ -extern rtx get_eh_context PARAMS ((void)); +extern void convert_from_eh_region_ranges PARAMS ((void)); +extern void convert_to_eh_region_ranges PARAMS ((void)); +extern void find_exception_handler_labels PARAMS ((void)); +extern void output_function_exception_table PARAMS ((void)); -/* Get the dynamic handler chain. */ -extern rtx get_dynamic_handler_chain PARAMS ((void)); +extern void expand_builtin_unwind_init PARAMS ((void)); +extern rtx expand_builtin_eh_return_data_regno PARAMS ((tree)); +extern rtx expand_builtin_extract_return_addr PARAMS ((tree)); +extern rtx expand_builtin_frob_return_addr PARAMS ((tree)); +extern void expand_builtin_eh_return PARAMS ((tree, tree)); +extern void expand_eh_return PARAMS ((void)); -/* Get the dynamic cleanup chain. */ -extern rtx get_dynamic_cleanup_chain PARAMS ((void)); +extern rtx get_exception_pointer PARAMS ((void)); -/* Throw an exception. */ +struct function; +struct inline_remap; +extern int duplicate_eh_regions PARAMS ((struct function *, + struct inline_remap *)); -extern void emit_throw PARAMS ((void)); +extern void sjlj_emit_function_exit_after PARAMS ((rtx)); -/* Save away the current ehqueue. */ -extern void push_ehqueue PARAMS ((void)); -/* Restore a previously pushed ehqueue. */ -extern void pop_ehqueue PARAMS ((void)); +/* Nonzero to protect cleanup actions with must-not-throw regions. */ +extern tree protect_cleanup_actions; -/* One to protect cleanup actions with a handler that calls - __terminate, zero otherwise. */ +/* Return true if type A catches type B. */ +int (*lang_eh_type_covers) PARAMS ((tree a, tree b)); -extern int protect_cleanup_actions_with_terminate; +/* Map a type to a runtime object to match type. */ +tree (*lang_eh_runtime_type) PARAMS ((tree)); -#ifdef TREE_CODE -extern tree protect_with_terminate PARAMS ((tree)); +#ifndef TREE_CODE +#undef tree #endif -extern void expand_fixup_region_start PARAMS ((void)); -#ifdef TREE_CODE -extern void expand_fixup_region_end PARAMS ((tree)); +#ifndef RTX_CODE +#undef rtx #endif -/* Various hooks for the DWARF 2 __throw routine. */ - -void expand_builtin_unwind_init PARAMS ((void)); -rtx expand_builtin_dwarf_fp_regnum PARAMS ((void)); -#ifdef TREE_CODE -rtx expand_builtin_frob_return_addr PARAMS ((tree)); -rtx expand_builtin_extract_return_addr PARAMS ((tree)); -void expand_builtin_init_dwarf_reg_sizes PARAMS ((tree)); -void expand_builtin_eh_return PARAMS ((tree, tree, tree)); +#ifndef _VARRAY_H_ +#undef varray_type #endif -void expand_eh_return PARAMS ((void)); - - -/* Checking whether 2 instructions are within the same exception region. */ -int in_same_eh_region PARAMS ((rtx, rtx)); -void free_insn_eh_region PARAMS ((void)); -void init_insn_eh_region PARAMS ((rtx, int)); - -#ifdef rtx -#undef rtx -#endif /* Just because the user configured --with-sjlj-exceptions=no doesn't mean that we can use call frame exceptions. Detect that the target has appropriate support. */ +#if !defined (EH_RETURN_DATA_REGNO) \ + || !defined(EH_RETURN_STACKADJ_RTX) \ + || ! (defined(EH_RETURN_HANDLER_RTX) \ + || defined(HAVE_eh_return)) \ + || ! (defined(DWARF2_UNWIND_INFO) \ + || defined(IA64_UNWIND_INFO)) +#define MUST_USE_SJLJ_EXCEPTIONS 1 +#else +#define MUST_USE_SJLJ_EXCEPTIONS 0 +#endif + #ifdef CONFIG_SJLJ_EXCEPTIONS # if CONFIG_SJLJ_EXCEPTIONS == 1 # define USING_SJLJ_EXCEPTIONS 1 # endif # if CONFIG_SJLJ_EXCEPTIONS == 0 # define USING_SJLJ_EXCEPTIONS 0 +# ifndef EH_RETURN_DATA_REGNO + #error "EH_RETURN_DATA_REGNO required" +# endif +# ifndef EH_RETURN_STACKADJ_RTX + #error "EH_RETURN_STACKADJ_RTX required" +# endif +# if !defined(EH_RETURN_HANDLER_RTX) && !defined(HAVE_eh_return) + #error "EH_RETURN_HANDLER_RTX or eh_return required" +# endif # if !defined(DWARF2_UNWIND_INFO) && !defined(IA64_UNWIND_INFO) #error "{DWARF2,IA64}_UNWIND_INFO required" # endif # endif #else -# ifdef IA64_UNWIND_INFO -# define USING_SJLJ_EXCEPTIONS (!IA64_UNWIND_INFO) -# else -# ifdef DWARF2_UNWIND_INFO -# define USING_SJLJ_EXCEPTIONS (!DWARF2_UNWIND_INFO) -# endif -# endif +# define USING_SJLJ_EXCEPTIONS MUST_USE_SJLJ_EXCEPTIONS #endif @@ -8741,7 +8741,7 @@ expand_expr (exp, target, tmode, modifier) op0 = expand_expr (TREE_OPERAND (exp, 0), 0, VOIDmode, 0); - expand_eh_region_end (handler); + expand_eh_region_end_cleanup (handler); return op0; } @@ -8788,23 +8788,12 @@ expand_expr (exp, target, tmode, modifier) return const0_rtx; } - case POPDCC_EXPR: - { - rtx dcc = get_dynamic_cleanup_chain (); - emit_move_insn (dcc, validize_mem (gen_rtx_MEM (Pmode, dcc))); - return const0_rtx; - } - - case POPDHC_EXPR: - { - rtx dhc = get_dynamic_handler_chain (); - emit_move_insn (dhc, validize_mem (gen_rtx_MEM (Pmode, dhc))); - return const0_rtx; - } - case VA_ARG_EXPR: return expand_builtin_va_arg (TREE_OPERAND (exp, 0), type); + case EXC_PTR_EXPR: + return get_exception_pointer (); + default: return (*lang_expand_expr) (exp, original_target, tmode, modifier); } @@ -495,14 +495,12 @@ enum libfunc_index LTI_memset, LTI_bzero, - LTI_throw, - LTI_rethrow, - LTI_sjthrow, - LTI_sjpopnthrow, - LTI_terminate, + LTI_unwind_resume, + LTI_eh_personality, LTI_setjmp, LTI_longjmp, - LTI_eh_rtime_match, + LTI_unwind_sjlj_register, + LTI_unwind_sjlj_unregister, LTI_eqhf2, LTI_nehf2, @@ -628,14 +626,13 @@ extern rtx libfunc_table[LTI_MAX]; #define memset_libfunc (libfunc_table[LTI_memset]) #define bzero_libfunc (libfunc_table[LTI_bzero]) -#define throw_libfunc (libfunc_table[LTI_throw]) -#define rethrow_libfunc (libfunc_table[LTI_rethrow]) -#define sjthrow_libfunc (libfunc_table[LTI_sjthrow]) -#define sjpopnthrow_libfunc (libfunc_table[LTI_sjpopnthrow]) -#define terminate_libfunc (libfunc_table[LTI_terminate]) +#define unwind_resume_libfunc (libfunc_table[LTI_unwind_resume]) +#define eh_personality_libfunc (libfunc_table[LTI_eh_personality]) #define setjmp_libfunc (libfunc_table[LTI_setjmp]) #define longjmp_libfunc (libfunc_table[LTI_longjmp]) -#define eh_rtime_match_libfunc (libfunc_table[LTI_eh_rtime_match]) +#define unwind_sjlj_register_libfunc (libfunc_table[LTI_unwind_sjlj_register]) +#define unwind_sjlj_unregister_libfunc \ + (libfunc_table[LTI_unwind_sjlj_unregister]) #define eqhf2_libfunc (libfunc_table[LTI_eqhf2]) #define nehf2_libfunc (libfunc_table[LTI_nehf2]) diff --git a/gcc/final.c b/gcc/final.c index 7b65a02..fef6641 100644 --- a/gcc/final.c +++ b/gcc/final.c @@ -1945,8 +1945,6 @@ final (first, file, optimize, prescan) last_ignored_compare = 0; new_block = 1; - check_exception_handler_labels (); - /* Make a map indicating which line numbers appear in this function. When producing SDB debugging info, delete troublesome line number notes from inlined functions in other files as well as duplicate @@ -2003,10 +2001,6 @@ final (first, file, optimize, prescan) #endif } - /* Initialize insn_eh_region table if eh is being used. */ - - init_insn_eh_region (first, max_uid); - init_recog (); CC_STATUS_INIT; @@ -2040,7 +2034,6 @@ final (first, file, optimize, prescan) if (profile_block_flag && new_block) add_bb (file); - free_insn_eh_region (); free (line_note_exists); line_note_exists = NULL; } @@ -2126,24 +2119,13 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes) break; case NOTE_INSN_EH_REGION_BEG: - if (! USING_SJLJ_EXCEPTIONS) - { - ASM_OUTPUT_INTERNAL_LABEL (file, "LEHB", NOTE_EH_HANDLER (insn)); -#ifdef ASM_OUTPUT_EH_REGION_BEG - ASM_OUTPUT_EH_REGION_BEG (file, NOTE_EH_HANDLER (insn)); -#endif - } + ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LEHB", + NOTE_EH_HANDLER (insn)); break; case NOTE_INSN_EH_REGION_END: - if (! USING_SJLJ_EXCEPTIONS) - { - ASM_OUTPUT_INTERNAL_LABEL (file, "LEHE", NOTE_EH_HANDLER (insn)); - add_eh_table_entry (NOTE_EH_HANDLER (insn)); -#ifdef ASM_OUTPUT_EH_REGION_END - ASM_OUTPUT_EH_REGION_END (file, NOTE_EH_HANDLER (insn)); -#endif - } + ASM_OUTPUT_DEBUG_LABEL (asm_out_file, "LEHE", + NOTE_EH_HANDLER (insn)); break; case NOTE_INSN_PROLOGUE_END: @@ -204,7 +204,6 @@ struct basic_block_def entry_exit_blocks[2] NULL, /* aux */ ENTRY_BLOCK, /* index */ 0, /* loop_depth */ - -1, -1, /* eh_beg, eh_end */ 0 /* count */ }, { @@ -219,7 +218,6 @@ struct basic_block_def entry_exit_blocks[2] NULL, /* aux */ EXIT_BLOCK, /* index */ 0, /* loop_depth */ - -1, -1, /* eh_beg, eh_end */ 0 /* count */ } }; @@ -368,16 +366,12 @@ static void clear_edges PARAMS ((void)); static void make_edges PARAMS ((rtx)); static void make_label_edge PARAMS ((sbitmap *, basic_block, rtx, int)); -static void make_eh_edge PARAMS ((sbitmap *, eh_nesting_info *, - basic_block, rtx, int)); +static void make_eh_edge PARAMS ((sbitmap *, basic_block, rtx)); static void mark_critical_edges PARAMS ((void)); -static void move_stray_eh_region_notes PARAMS ((void)); -static void record_active_eh_regions PARAMS ((rtx)); static void commit_one_edge_insertion PARAMS ((edge)); static void delete_unreachable_blocks PARAMS ((void)); -static void delete_eh_regions PARAMS ((void)); static int can_delete_note_p PARAMS ((rtx)); static void expunge_block PARAMS ((basic_block)); static int can_delete_label_p PARAMS ((rtx)); @@ -537,7 +531,6 @@ find_basic_blocks (f, nregs, file) compute_bb_for_insn (max_uid); /* Discover the edges of our cfg. */ - record_active_eh_regions (f); make_edges (label_value_list); /* Do very simple cleanup now, for the benefit of code that runs between @@ -599,46 +592,45 @@ count_basic_blocks (f) register rtx insn; register RTX_CODE prev_code; register int count = 0; - int eh_region = 0; - int call_had_abnormal_edge = 0; + int saw_abnormal_edge = 0; prev_code = JUMP_INSN; for (insn = f; insn; insn = NEXT_INSN (insn)) { - register RTX_CODE code = GET_CODE (insn); + enum rtx_code code = GET_CODE (insn); if (code == CODE_LABEL || (GET_RTX_CLASS (code) == 'i' && (prev_code == JUMP_INSN || prev_code == BARRIER - || (prev_code == CALL_INSN && call_had_abnormal_edge)))) - count++; + || saw_abnormal_edge))) + { + saw_abnormal_edge = 0; + count++; + } - /* Record whether this call created an edge. */ + /* Record whether this insn created an edge. */ if (code == CALL_INSN) { - rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - int region = (note ? INTVAL (XEXP (note, 0)) : 1); - - call_had_abnormal_edge = 0; - - /* If there is an EH region or rethrow, we have an edge. */ - if ((eh_region && region > 0) - || find_reg_note (insn, REG_EH_RETHROW, NULL_RTX)) - call_had_abnormal_edge = 1; - else if (nonlocal_goto_handler_labels && region >= 0) - /* If there is a nonlocal goto label and the specified - region number isn't -1, we have an edge. (0 means - no throw, but might have a nonlocal goto). */ - call_had_abnormal_edge = 1; + rtx note; + + /* If there is a nonlocal goto label and the specified + region number isn't -1, we have an edge. */ + if (nonlocal_goto_handler_labels + && ((note = find_reg_note (insn, REG_EH_REGION, NULL_RTX)) == 0 + || INTVAL (XEXP (note, 0)) >= 0)) + saw_abnormal_edge = 1; + + else if (can_throw_internal (insn)) + saw_abnormal_edge = 1; } + else if (flag_non_call_exceptions + && code == INSN + && can_throw_internal (insn)) + saw_abnormal_edge = 1; if (code != NOTE) prev_code = code; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - ++eh_region; - else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END) - --eh_region; } /* The rest of the compiler works a bit smoother when we don't have to @@ -672,9 +664,6 @@ find_label_refs (f, lvl) Make a special exception for labels followed by an ADDR*VEC, as this would be a part of the tablejump setup code. - Make a special exception for the eh_return_stub_label, which - we know isn't part of any otherwise visible control flow. - Make a special exception to registers loaded with label values just before jump insns that use them. */ @@ -683,9 +672,7 @@ find_label_refs (f, lvl) { rtx lab = XEXP (note, 0), next; - if (lab == eh_return_stub_label) - ; - else if ((next = next_nonnote_insn (lab)) != NULL + if ((next = next_nonnote_insn (lab)) != NULL && GET_CODE (next) == JUMP_INSN && (GET_CODE (PATTERN (next)) == ADDR_VEC || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)) @@ -815,7 +802,6 @@ find_basic_blocks_1 (f) register rtx insn, next; int i = 0; rtx bb_note = NULL_RTX; - rtx eh_list = NULL_RTX; rtx lvl = NULL_RTX; rtx trll = NULL_RTX; rtx head = NULL_RTX; @@ -839,22 +825,11 @@ find_basic_blocks_1 (f) { int kind = NOTE_LINE_NUMBER (insn); - /* Keep a LIFO list of the currently active exception notes. */ - if (kind == NOTE_INSN_EH_REGION_BEG) - eh_list = alloc_INSN_LIST (insn, eh_list); - else if (kind == NOTE_INSN_EH_REGION_END) - { - rtx t = eh_list; - - eh_list = XEXP (eh_list, 1); - free_INSN_LIST_node (t); - } - /* Look for basic block notes with which to keep the basic_block_info pointers stable. Unthread the note now; we'll put it back at the right place in create_basic_block. Or not at all if we've already found a note in this block. */ - else if (kind == NOTE_INSN_BASIC_BLOCK) + if (kind == NOTE_INSN_BASIC_BLOCK) { if (bb_note == NULL_RTX) bb_note = insn; @@ -938,8 +913,7 @@ find_basic_blocks_1 (f) { /* Record whether this call created an edge. */ rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - int region = (note ? INTVAL (XEXP (note, 0)) : 1); - int call_has_abnormal_edge = 0; + int region = (note ? INTVAL (XEXP (note, 0)) : 0); if (GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) { @@ -952,19 +926,10 @@ find_basic_blocks_1 (f) trll = alloc_EXPR_LIST (0, XEXP (PATTERN (insn), 3), trll); } - /* If there is an EH region or rethrow, we have an edge. */ - if ((eh_list && region > 0) - || find_reg_note (insn, REG_EH_RETHROW, NULL_RTX)) - call_has_abnormal_edge = 1; - else if (nonlocal_goto_handler_labels && region >= 0) - /* If there is a nonlocal goto label and the specified - region number isn't -1, we have an edge. (0 means - no throw, but might have a nonlocal goto). */ - call_has_abnormal_edge = 1; - /* A basic block ends at a call that can either throw or do a non-local goto. */ - if (call_has_abnormal_edge) + if ((nonlocal_goto_handler_labels && region >= 0) + || can_throw_internal (insn)) { new_bb_inclusive: if (head == NULL_RTX) @@ -980,18 +945,21 @@ find_basic_blocks_1 (f) } /* Fall through. */ - default: - if (GET_RTX_CLASS (code) == 'i') - { - if (head == NULL_RTX) - head = insn; - end = insn; - } + case INSN: + /* Non-call exceptions generate new blocks just like calls. */ + if (flag_non_call_exceptions && can_throw_internal (insn)) + goto new_bb_inclusive; + + if (head == NULL_RTX) + head = insn; + end = insn; break; + + default: + abort (); } - if (GET_RTX_CLASS (code) == 'i' - && GET_CODE (insn) != JUMP_INSN) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) { rtx note; @@ -1000,9 +968,6 @@ find_basic_blocks_1 (f) Make a special exception for labels followed by an ADDR*VEC, as this would be a part of the tablejump setup code. - Make a special exception for the eh_return_stub_label, which - we know isn't part of any otherwise visible control flow. - Make a special exception to registers loaded with label values just before jump insns that use them. */ @@ -1011,9 +976,7 @@ find_basic_blocks_1 (f) { rtx lab = XEXP (note, 0), next; - if (lab == eh_return_stub_label) - ; - else if ((next = next_nonnote_insn (lab)) != NULL + if ((next = next_nonnote_insn (lab)) != NULL && GET_CODE (next) == JUMP_INSN && (GET_CODE (PATTERN (next)) == ADDR_VEC || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC)) @@ -1047,8 +1010,6 @@ void cleanup_cfg () { delete_unreachable_blocks (); - move_stray_eh_region_notes (); - record_active_eh_regions (get_insns ()); try_merge_blocks (); mark_critical_edges (); @@ -1201,7 +1162,6 @@ make_edges (label_value_list) rtx label_value_list; { int i; - eh_nesting_info *eh_nest_info = init_eh_nesting_info (); sbitmap *edge_cache = NULL; /* Assume no computed jump; revise as we create edges. */ @@ -1241,9 +1201,13 @@ make_edges (label_value_list) { rtx tmp; + /* Recognize exception handling placeholders. */ + if (GET_CODE (PATTERN (insn)) == RESX) + make_eh_edge (edge_cache, bb, insn); + /* Recognize a non-local goto as a branch outside the current function. */ - if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX)) + else if (find_reg_note (insn, REG_NON_LOCAL_GOTO, NULL_RTX)) ; /* ??? Recognize a tablejump and do the right thing. */ @@ -1318,37 +1282,15 @@ make_edges (label_value_list) EDGE_ABNORMAL | EDGE_ABNORMAL_CALL); /* If this is a CALL_INSN, then mark it as reaching the active EH - handler for this CALL_INSN. If we're handling asynchronous + handler for this CALL_INSN. If we're handling non-call exceptions then any insn can reach any of the active handlers. Also mark the CALL_INSN as reaching any nonlocal goto handler. */ else if (code == CALL_INSN || flag_non_call_exceptions) { - /* Add any appropriate EH edges. We do this unconditionally - since there may be a REG_EH_REGION or REG_EH_RETHROW note - on the call, and this needn't be within an EH region. */ - make_eh_edge (edge_cache, eh_nest_info, bb, insn, bb->eh_end); - - /* If we have asynchronous exceptions, do the same for *all* - exception regions active in the block. */ - if (flag_non_call_exceptions - && bb->eh_beg != bb->eh_end) - { - if (bb->eh_beg >= 0) - make_eh_edge (edge_cache, eh_nest_info, bb, - NULL_RTX, bb->eh_beg); - - for (x = bb->head; x != bb->end; x = NEXT_INSN (x)) - if (GET_CODE (x) == NOTE - && (NOTE_LINE_NUMBER (x) == NOTE_INSN_EH_REGION_BEG - || NOTE_LINE_NUMBER (x) == NOTE_INSN_EH_REGION_END)) - { - int region = NOTE_EH_HANDLER (x); - make_eh_edge (edge_cache, eh_nest_info, bb, - NULL_RTX, region); - } - } + /* Add any appropriate EH edges. */ + make_eh_edge (edge_cache, bb, insn); if (code == CALL_INSN && nonlocal_goto_handler_labels) { @@ -1369,14 +1311,6 @@ make_edges (label_value_list) } } - /* We know something about the structure of the function __throw in - libgcc2.c. It is the only function that ever contains eh_stub - labels. It modifies its return address so that the last block - returns to one of the eh_stub labels within it. So we have to - make additional edges in the flow graph. */ - if (i + 1 == n_basic_blocks && eh_return_stub_label != 0) - make_label_edge (edge_cache, bb, eh_return_stub_label, EDGE_EH); - /* Find out if we can drop through to the next block. */ insn = next_nonnote_insn (insn); if (!insn || (i + 1 == n_basic_blocks && force_fallthru)) @@ -1391,7 +1325,6 @@ make_edges (label_value_list) } } - free_eh_nesting_info (eh_nest_info); if (edge_cache) sbitmap_vector_free (edge_cache); } @@ -1479,115 +1412,21 @@ make_label_edge (edge_cache, src, label, flags) /* Create the edges generated by INSN in REGION. */ static void -make_eh_edge (edge_cache, eh_nest_info, src, insn, region) +make_eh_edge (edge_cache, src, insn) sbitmap *edge_cache; - eh_nesting_info *eh_nest_info; basic_block src; rtx insn; - int region; -{ - handler_info **handler_list; - int num, is_call; - - is_call = (insn && GET_CODE (insn) == CALL_INSN ? EDGE_ABNORMAL_CALL : 0); - num = reachable_handlers (region, eh_nest_info, insn, &handler_list); - while (--num >= 0) - { - make_label_edge (edge_cache, src, handler_list[num]->handler_label, - EDGE_ABNORMAL | EDGE_EH | is_call); - } -} - -/* EH_REGION notes appearing between basic blocks is ambiguous, and even - dangerous if we intend to move basic blocks around. Move such notes - into the following block. */ - -static void -move_stray_eh_region_notes () -{ - int i; - basic_block b1, b2; - - if (n_basic_blocks < 2) - return; - - b2 = BASIC_BLOCK (n_basic_blocks - 1); - for (i = n_basic_blocks - 2; i >= 0; --i, b2 = b1) - { - rtx insn, next, list = NULL_RTX; - - b1 = BASIC_BLOCK (i); - for (insn = NEXT_INSN (b1->end); insn != b2->head; insn = next) - { - next = NEXT_INSN (insn); - if (GET_CODE (insn) == NOTE - && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG - || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) - { - /* Unlink from the insn chain. */ - NEXT_INSN (PREV_INSN (insn)) = next; - PREV_INSN (next) = PREV_INSN (insn); - - /* Queue it. */ - NEXT_INSN (insn) = list; - list = insn; - } - } - - if (list == NULL_RTX) - continue; - - /* Find where to insert these things. */ - insn = b2->head; - if (GET_CODE (insn) == CODE_LABEL) - insn = NEXT_INSN (insn); - - while (list) - { - next = NEXT_INSN (list); - add_insn_after (list, insn); - list = next; - } - } -} - -/* Recompute eh_beg/eh_end for each basic block. */ - -static void -record_active_eh_regions (f) - rtx f; { - rtx insn, eh_list = NULL_RTX; - int i = 0; - basic_block bb = BASIC_BLOCK (0); + int is_call = (GET_CODE (insn) == CALL_INSN ? EDGE_ABNORMAL_CALL : 0); + rtx handlers, i; - for (insn = f; insn; insn = NEXT_INSN (insn)) - { - if (bb->head == insn) - bb->eh_beg = (eh_list ? NOTE_EH_HANDLER (XEXP (eh_list, 0)) : -1); + handlers = reachable_handlers (insn); - if (GET_CODE (insn) == NOTE) - { - int kind = NOTE_LINE_NUMBER (insn); - if (kind == NOTE_INSN_EH_REGION_BEG) - eh_list = alloc_INSN_LIST (insn, eh_list); - else if (kind == NOTE_INSN_EH_REGION_END) - { - rtx t = XEXP (eh_list, 1); - free_INSN_LIST_node (eh_list); - eh_list = t; - } - } + for (i = handlers; i; i = XEXP (i, 1)) + make_label_edge (edge_cache, src, XEXP (i, 0), + EDGE_ABNORMAL | EDGE_EH | is_call); - if (bb->end == insn) - { - bb->eh_end = (eh_list ? NOTE_EH_HANDLER (XEXP (eh_list, 0)) : -1); - i += 1; - if (i == n_basic_blocks) - break; - bb = BASIC_BLOCK (i); - } - } + free_INSN_LIST_list (&handlers); } /* Identify critical edges and set the bits appropriately. */ @@ -2223,7 +2062,6 @@ static void delete_unreachable_blocks () { basic_block *worklist, *tos; - int deleted_handler; edge e; int i, n; @@ -2261,10 +2099,9 @@ delete_unreachable_blocks () } } - /* Delete all unreachable basic blocks. Count down so that we don't - interfere with the block renumbering that happens in flow_delete_block. */ - - deleted_handler = 0; + /* Delete all unreachable basic blocks. Count down so that we + don't interfere with the block renumbering that happens in + flow_delete_block. */ for (i = n - 1; i >= 0; --i) { @@ -2274,46 +2111,14 @@ delete_unreachable_blocks () /* This block was found. Tidy up the mark. */ b->aux = NULL; else - deleted_handler |= flow_delete_block (b); + flow_delete_block (b); } tidy_fallthru_edges (); - /* If we deleted an exception handler, we may have EH region begin/end - blocks to remove as well. */ - if (deleted_handler) - delete_eh_regions (); - free (worklist); } -/* Find EH regions for which there is no longer a handler, and delete them. */ - -static void -delete_eh_regions () -{ - rtx insn; - - update_rethrow_references (); - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE) - { - if ((NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - || (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) - { - int num = NOTE_EH_HANDLER (insn); - /* A NULL handler indicates a region is no longer needed, - as long as its rethrow label isn't used. */ - if (get_first_handler (num) == NULL && ! rethrow_used (num)) - { - NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; - NOTE_SOURCE_FILE (insn) = 0; - } - } - } -} - /* Return true if NOTE is not one of the ones that must be kept paired, so that we may simply delete them. */ @@ -2387,26 +2192,7 @@ flow_delete_block (b) never_reached_warning (insn); if (GET_CODE (insn) == CODE_LABEL) - { - rtx x, *prev = &exception_handler_labels; - - for (x = exception_handler_labels; x; x = XEXP (x, 1)) - { - if (XEXP (x, 0) == insn) - { - /* Found a match, splice this label out of the EH label list. */ - *prev = XEXP (x, 1); - XEXP (x, 1) = NULL_RTX; - XEXP (x, 0) = NULL_RTX; - - /* Remove the handler from all regions */ - remove_handler (insn); - deleted_handler = 1; - break; - } - prev = &XEXP (x, 1); - } - } + maybe_remove_eh_handler (insn); /* Include any jump table following the basic block. */ end = b->end; @@ -2804,7 +2590,6 @@ merge_blocks (e, b, c) else { edge tmp_edge; - basic_block d; int c_has_outgoing_fallthru; int b_has_incoming_fallthru; @@ -2832,37 +2617,22 @@ merge_blocks (e, b, c) break; b_has_incoming_fallthru = (tmp_edge != NULL); - /* If B does not have an incoming fallthru, and the exception regions - match, then it can be moved immediately before C without introducing - or modifying jumps. - - C can not be the first block, so we do not have to worry about + /* If B does not have an incoming fallthru, then it can be moved + immediately before C without introducing or modifying jumps. + C cannot be the first block, so we do not have to worry about accessing a non-existent block. */ - d = BASIC_BLOCK (c->index - 1); - if (! b_has_incoming_fallthru - && d->eh_end == b->eh_beg - && b->eh_end == c->eh_beg) + if (! b_has_incoming_fallthru) return merge_blocks_move_predecessor_nojumps (b, c); - /* Otherwise, we're going to try to move C after B. Make sure the - exception regions match. + /* Otherwise, we're going to try to move C after B. If C does + not have an outgoing fallthru, then it can be moved + immediately after B without introducing or modifying jumps. */ + if (! c_has_outgoing_fallthru) + return merge_blocks_move_successor_nojumps (b, c); - If B is the last basic block, then we must not try to access the - block structure for block B + 1. Luckily in that case we do not - need to worry about matching exception regions. */ - d = (b->index + 1 < n_basic_blocks ? BASIC_BLOCK (b->index + 1) : NULL); - if (b->eh_end == c->eh_beg - && (d == NULL || c->eh_end == d->eh_beg)) - { - /* If C does not have an outgoing fallthru, then it can be moved - immediately after B without introducing or modifying jumps. */ - if (! c_has_outgoing_fallthru) - return merge_blocks_move_successor_nojumps (b, c); - - /* Otherwise, we'll need to insert an extra jump, and possibly - a new block to contain it. */ - /* ??? Not implemented yet. */ - } + /* Otherwise, we'll need to insert an extra jump, and possibly + a new block to contain it. */ + /* ??? Not implemented yet. */ return 0; } @@ -3502,14 +3272,44 @@ mark_regs_live_at_end (set) if (global_regs[i] || EPILOGUE_USES (i)) SET_REGNO_REG_SET (set, i); - /* Mark all call-saved registers that we actaully used. */ if (HAVE_epilogue && reload_completed) { + /* Mark all call-saved registers that we actually used. */ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) if (regs_ever_live[i] && ! call_used_regs[i] && ! LOCAL_REGNO (i)) SET_REGNO_REG_SET (set, i); } +#ifdef EH_RETURN_DATA_REGNO + /* Mark the registers that will contain data for the handler. */ + if (reload_completed && current_function_calls_eh_return) + for (i = 0; ; ++i) + { + unsigned regno = EH_RETURN_DATA_REGNO(i); + if (regno == INVALID_REGNUM) + break; + SET_REGNO_REG_SET (set, regno); + } +#endif +#ifdef EH_RETURN_STACKADJ_RTX + if ((! HAVE_epilogue || ! reload_completed) + && current_function_calls_eh_return) + { + rtx tmp = EH_RETURN_STACKADJ_RTX; + if (tmp && REG_P (tmp)) + mark_reg (tmp, set); + } +#endif +#ifdef EH_RETURN_HANDLER_RTX + if ((! HAVE_epilogue || ! reload_completed) + && current_function_calls_eh_return) + { + rtx tmp = EH_RETURN_HANDLER_RTX; + if (tmp && REG_P (tmp)) + mark_reg (tmp, set); + } +#endif + /* Mark function return value. */ diddle_return_value (mark_reg, set); } @@ -4249,7 +4049,8 @@ init_propagate_block_info (bb, live, local_set, cond_local_set, flags) && (flags & PROP_SCAN_DEAD_CODE) && (bb->succ == NULL || (bb->succ->succ_next == NULL - && bb->succ->dest == EXIT_BLOCK_PTR))) + && bb->succ->dest == EXIT_BLOCK_PTR + && ! current_function_calls_eh_return))) { rtx insn, set; for (insn = bb->end; insn != bb->head; insn = PREV_INSN (insn)) @@ -6631,8 +6432,6 @@ dump_bb (bb, outf) fprintf (outf, ";; Basic block %d, loop depth %d, count %d", bb->index, bb->loop_depth, bb->count); - if (bb->eh_beg != -1 || bb->eh_end != -1) - fprintf (outf, ", eh regions %d/%d", bb->eh_beg, bb->eh_end); putc ('\n', outf); fputs (";; Predecessors: ", outf); diff --git a/gcc/frame-dwarf2.c b/gcc/frame-dwarf2.c deleted file mode 100644 index 7fbcf87..0000000 --- a/gcc/frame-dwarf2.c +++ /dev/null @@ -1,778 +0,0 @@ -/* Subroutines needed for unwinding DWARF 2 format stack frame info - for exception handling. */ -/* Compile this one with gcc. */ -/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. - Contributed by Jason Merrill <jason@cygnus.com>. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -In addition to the permissions in the GNU General Public License, the -Free Software Foundation gives you unlimited permission to link the -compiled version of this file into combinations with other programs, -and to distribute those combinations without any restriction coming -from the use of this file. (The General Public License restrictions -do apply in other respects; for example, they cover modification of -the file, and distribution when not linked into a combine -executable.) - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* It is incorrect to include config.h here, because this file is being - compiled for the target, and hence definitions concerning only the host - do not apply. */ - -#include "tconfig.h" -#include "tsystem.h" - -#ifdef DWARF2_UNWIND_INFO -#include "dwarf2.h" -#include "frame.h" -#include "gthr.h" - -#ifdef __GTHREAD_MUTEX_INIT -static __gthread_mutex_t object_mutex = __GTHREAD_MUTEX_INIT; -#else -static __gthread_mutex_t object_mutex; -#endif - -/* Don't use `fancy_abort' here even if config.h says to use it. */ -#ifdef abort -#undef abort -#endif - -/* Some types used by the DWARF 2 spec. */ - -typedef int sword __attribute__ ((mode (SI))); -typedef unsigned int uword __attribute__ ((mode (SI))); -typedef unsigned int uaddr __attribute__ ((mode (pointer))); -typedef int saddr __attribute__ ((mode (pointer))); -typedef unsigned char ubyte; - -/* Terminology: - CIE - Common Information Element - FDE - Frame Descriptor Element - - There is one per function, and it describes where the function code - is located, and what the register lifetimes and stack layout are - within the function. - - The data structures are defined in the DWARF specfication, although - not in a very readable way (see LITERATURE). - - Every time an exception is thrown, the code needs to locate the FDE - for the current function, and starts to look for exception regions - from that FDE. This works in a two-level search: - a) in a linear search, find the shared image (i.e. DLL) containing - the PC - b) using the FDE table for that shared object, locate the FDE using - binary search (which requires the sorting). */ - -/* The first few fields of a CIE. The CIE_id field is 0 for a CIE, - to distinguish it from a valid FDE. FDEs are aligned to an addressing - unit boundary, but the fields within are unaligned. */ - -struct dwarf_cie { - uword length; - sword CIE_id; - ubyte version; - char augmentation[0]; -} __attribute__ ((packed, aligned (__alignof__ (void *)))); - -/* The first few fields of an FDE. */ - -struct dwarf_fde { - uword length; - sword CIE_delta; - void* pc_begin; - uaddr pc_range; -} __attribute__ ((packed, aligned (__alignof__ (void *)))); - -typedef struct dwarf_fde fde; - -/* Objects to be searched for frame unwind info. */ - -static struct object *objects; - -/* The information we care about from a CIE. */ - -struct cie_info { - char *augmentation; - void *eh_ptr; - int code_align; - int data_align; - unsigned ra_regno; -}; - -/* The current unwind state, plus a saved copy for DW_CFA_remember_state. */ - -struct frame_state_internal -{ - struct frame_state s; - struct frame_state_internal *saved_state; -}; - -/* This is undefined below if we need it to be an actual function. */ -#define init_object_mutex_once() - -#if __GTHREADS -#ifdef __GTHREAD_MUTEX_INIT_FUNCTION - -/* Helper for init_object_mutex_once. */ - -static void -init_object_mutex (void) -{ - __GTHREAD_MUTEX_INIT_FUNCTION (&object_mutex); -} - -/* Call this to arrange to initialize the object mutex. */ - -#undef init_object_mutex_once -static void -init_object_mutex_once (void) -{ - static __gthread_once_t once = __GTHREAD_ONCE_INIT; - __gthread_once (&once, init_object_mutex); -} - -#endif /* __GTHREAD_MUTEX_INIT_FUNCTION */ -#endif /* __GTHREADS */ - -/* Decode the unsigned LEB128 constant at BUF into the variable pointed to - by R, and return the new value of BUF. */ - -static void * -decode_uleb128 (unsigned char *buf, unsigned *r) -{ - unsigned shift = 0; - unsigned result = 0; - - while (1) - { - unsigned byte = *buf++; - result |= (byte & 0x7f) << shift; - if ((byte & 0x80) == 0) - break; - shift += 7; - } - *r = result; - return buf; -} - -/* Decode the signed LEB128 constant at BUF into the variable pointed to - by R, and return the new value of BUF. */ - -static void * -decode_sleb128 (unsigned char *buf, int *r) -{ - unsigned shift = 0; - unsigned result = 0; - unsigned byte; - - while (1) - { - byte = *buf++; - result |= (byte & 0x7f) << shift; - shift += 7; - if ((byte & 0x80) == 0) - break; - } - if (shift < (sizeof (*r) * 8) && (byte & 0x40) != 0) - result |= - (1 << shift); - - *r = result; - return buf; -} - -/* Read unaligned data from the instruction buffer. */ - -union unaligned { - void *p; - unsigned b2 __attribute__ ((mode (HI))); - unsigned b4 __attribute__ ((mode (SI))); - unsigned b8 __attribute__ ((mode (DI))); -} __attribute__ ((packed)); -static inline void * -read_pointer (void *p) -{ union unaligned *up = p; return up->p; } -static inline unsigned -read_1byte (void *p) -{ return *(unsigned char *)p; } -static inline unsigned -read_2byte (void *p) -{ union unaligned *up = p; return up->b2; } -static inline unsigned -read_4byte (void *p) -{ union unaligned *up = p; return up->b4; } -static inline unsigned long -read_8byte (void *p) -{ union unaligned *up = p; return up->b8; } - -/* Ordering function for FDEs. Functions can't overlap, so we just compare - their starting addresses. */ - -static inline saddr -fde_compare (fde *x, fde *y) -{ - return (saddr)x->pc_begin - (saddr)y->pc_begin; -} - -/* Return the address of the FDE after P. */ - -static inline fde * -next_fde (fde *p) -{ - return (fde *)(((char *)p) + p->length + sizeof (p->length)); -} - -#include "frame.c" - -static size_t -count_fdes (fde *this_fde) -{ - size_t count; - - for (count = 0; this_fde->length != 0; this_fde = next_fde (this_fde)) - { - /* Skip CIEs and linked once FDE entries. */ - if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) - continue; - - ++count; - } - - return count; -} - -static void -add_fdes (fde *this_fde, fde_accumulator *accu, void **beg_ptr, void **end_ptr) -{ - void *pc_begin = *beg_ptr; - void *pc_end = *end_ptr; - - for (; this_fde->length != 0; this_fde = next_fde (this_fde)) - { - /* Skip CIEs and linked once FDE entries. */ - if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) - continue; - - fde_insert (accu, this_fde); - - if (this_fde->pc_begin < pc_begin) - pc_begin = this_fde->pc_begin; - if (this_fde->pc_begin + this_fde->pc_range > pc_end) - pc_end = this_fde->pc_begin + this_fde->pc_range; - } - - *beg_ptr = pc_begin; - *end_ptr = pc_end; -} - -/* search this fde table for the one containing the pc */ -static fde * -search_fdes (fde *this_fde, void *pc) -{ - for (; this_fde->length != 0; this_fde = next_fde (this_fde)) - { - /* Skip CIEs and linked once FDE entries. */ - if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) - continue; - - if ((uaddr)((char *)pc - (char *)this_fde->pc_begin) < this_fde->pc_range) - return this_fde; - } - return NULL; -} - -/* Set up a sorted array of pointers to FDEs for a loaded object. We - count up the entries before allocating the array because it's likely to - be faster. We can be called multiple times, should we have failed to - allocate a sorted fde array on a previous occasion. */ - -static void -frame_init (struct object* ob) -{ - size_t count; - fde_accumulator accu; - void *pc_begin, *pc_end; - fde **array; - - if (ob->pc_begin) - count = ob->count; - else if (ob->fde_array) - { - fde **p = ob->fde_array; - for (count = 0; *p; ++p) - count += count_fdes (*p); - } - else - count = count_fdes (ob->fde_begin); - ob->count = count; - - if (!start_fde_sort (&accu, count) && ob->pc_begin) - return; - - pc_begin = (void*)(uaddr)-1; - pc_end = 0; - - if (ob->fde_array) - { - fde **p = ob->fde_array; - for (; *p; ++p) - add_fdes (*p, &accu, &pc_begin, &pc_end); - } - else - add_fdes (ob->fde_begin, &accu, &pc_begin, &pc_end); - - array = end_fde_sort (&accu, count); - if (array) - ob->fde_array = array; - ob->pc_begin = pc_begin; - ob->pc_end = pc_end; -} - -/* Return a pointer to the FDE for the function containing PC. */ - -static fde * -find_fde (void *pc) -{ - struct object *ob; - size_t lo, hi; - - init_object_mutex_once (); - __gthread_mutex_lock (&object_mutex); - - /* Linear search through the objects, to find the one containing the pc. */ - for (ob = objects; ob; ob = ob->next) - { - if (ob->pc_begin == 0) - frame_init (ob); - if (pc >= ob->pc_begin && pc < ob->pc_end) - break; - } - - if (ob == 0) - { - __gthread_mutex_unlock (&object_mutex); - return 0; - } - - if (!ob->fde_array || (void *)ob->fde_array == (void *)ob->fde_begin) - frame_init (ob); - - if (ob->fde_array && (void *)ob->fde_array != (void *)ob->fde_begin) - { - __gthread_mutex_unlock (&object_mutex); - - /* Standard binary search algorithm. */ - for (lo = 0, hi = ob->count; lo < hi; ) - { - size_t i = (lo + hi) / 2; - fde *f = ob->fde_array[i]; - - if (pc < f->pc_begin) - hi = i; - else if (pc >= f->pc_begin + f->pc_range) - lo = i + 1; - else - return f; - } - } - else - { - /* Long slow labourious linear search, cos we've no memory. */ - fde *f; - - if (ob->fde_array) - { - fde **p = ob->fde_array; - - do - { - f = search_fdes (*p, pc); - if (f) - break; - p++; - } - while (*p); - } - else - f = search_fdes (ob->fde_begin, pc); - __gthread_mutex_unlock (&object_mutex); - return f; - } - return 0; -} - -static inline struct dwarf_cie * -get_cie (fde *f) -{ - return ((void *)&f->CIE_delta) - f->CIE_delta; -} - -/* Extract any interesting information from the CIE for the translation - unit F belongs to. */ - -static void * -extract_cie_info (fde *f, struct cie_info *c) -{ - void *p; - int i; - - c->augmentation = get_cie (f)->augmentation; - - if (strcmp (c->augmentation, "") != 0 - && strcmp (c->augmentation, "eh") != 0 - && c->augmentation[0] != 'z') - return 0; - - p = c->augmentation + strlen (c->augmentation) + 1; - - if (strcmp (c->augmentation, "eh") == 0) - { - c->eh_ptr = read_pointer (p); - p += sizeof (void *); - } - else - c->eh_ptr = 0; - - p = decode_uleb128 (p, &c->code_align); - p = decode_sleb128 (p, &c->data_align); - c->ra_regno = *(unsigned char *)p++; - - /* If the augmentation starts with 'z', we now see the length of the - augmentation fields. */ - if (c->augmentation[0] == 'z') - { - p = decode_uleb128 (p, &i); - p += i; - } - - return p; -} - -/* Decode a DW_OP stack operation. */ - -static void * -decode_stack_op (unsigned char *buf, struct frame_state *state) -{ - enum dwarf_location_atom op; - int offset; - - op = *buf++; - switch (op) - { - case DW_OP_reg0: - case DW_OP_reg1: - case DW_OP_reg2: - case DW_OP_reg3: - case DW_OP_reg4: - case DW_OP_reg5: - case DW_OP_reg6: - case DW_OP_reg7: - case DW_OP_reg8: - case DW_OP_reg9: - case DW_OP_reg10: - case DW_OP_reg11: - case DW_OP_reg12: - case DW_OP_reg13: - case DW_OP_reg14: - case DW_OP_reg15: - case DW_OP_reg16: - case DW_OP_reg17: - case DW_OP_reg18: - case DW_OP_reg19: - case DW_OP_reg20: - case DW_OP_reg21: - case DW_OP_reg22: - case DW_OP_reg23: - case DW_OP_reg24: - case DW_OP_reg25: - case DW_OP_reg26: - case DW_OP_reg27: - case DW_OP_reg28: - case DW_OP_reg29: - case DW_OP_reg30: - case DW_OP_reg31: - state->cfa_reg = op - DW_OP_reg0; - break; - case DW_OP_regx: - buf = decode_sleb128 (buf, &offset); - state->cfa_reg = offset; - break; - case DW_OP_breg0: - case DW_OP_breg1: - case DW_OP_breg2: - case DW_OP_breg3: - case DW_OP_breg4: - case DW_OP_breg5: - case DW_OP_breg6: - case DW_OP_breg7: - case DW_OP_breg8: - case DW_OP_breg9: - case DW_OP_breg10: - case DW_OP_breg11: - case DW_OP_breg12: - case DW_OP_breg13: - case DW_OP_breg14: - case DW_OP_breg15: - case DW_OP_breg16: - case DW_OP_breg17: - case DW_OP_breg18: - case DW_OP_breg19: - case DW_OP_breg20: - case DW_OP_breg21: - case DW_OP_breg22: - case DW_OP_breg23: - case DW_OP_breg24: - case DW_OP_breg25: - case DW_OP_breg26: - case DW_OP_breg27: - case DW_OP_breg28: - case DW_OP_breg29: - case DW_OP_breg30: - case DW_OP_breg31: - state->cfa_reg = op - DW_OP_breg0; - buf = decode_sleb128 (buf, &offset); - state->base_offset = offset; - break; - case DW_OP_bregx: - buf = decode_sleb128 (buf, &offset); - state->cfa_reg = offset; - buf = decode_sleb128 (buf, &offset); - state->base_offset = offset; - break; - case DW_OP_deref: - state->indirect = 1; - break; - case DW_OP_plus_uconst: - buf = decode_uleb128 (buf, &offset); - state->cfa_offset = offset; - break; - default: - abort (); - } - return buf; -} -/* Decode one instruction's worth of DWARF 2 call frame information. - Used by __frame_state_for. Takes pointers P to the instruction to - decode, STATE to the current register unwind information, INFO to the - current CIE information, and PC to the current PC value. Returns a - pointer to the next instruction. */ - -static void * -execute_cfa_insn (void *p, struct frame_state_internal *state, - struct cie_info *info, void **pc) -{ - unsigned insn = *(unsigned char *)p++; - unsigned reg; - int offset; - - if (insn & DW_CFA_advance_loc) - *pc += ((insn & 0x3f) * info->code_align); - else if (insn & DW_CFA_offset) - { - reg = (insn & 0x3f); - p = decode_uleb128 (p, &offset); - if (reg == state->s.cfa_reg) - /* Don't record anything about this register; it's only used to - reload SP in the epilogue. We don't want to copy in SP - values for outer frames; we handle restoring SP specially. */; - else - { - offset *= info->data_align; - state->s.saved[reg] = REG_SAVED_OFFSET; - state->s.reg_or_offset[reg] = offset; - } - } - else if (insn & DW_CFA_restore) - { - reg = (insn & 0x3f); - state->s.saved[reg] = REG_UNSAVED; - } - else switch (insn) - { - case DW_CFA_set_loc: - *pc = read_pointer (p); - p += sizeof (void *); - break; - case DW_CFA_advance_loc1: - *pc += read_1byte (p); - p += 1; - break; - case DW_CFA_advance_loc2: - *pc += read_2byte (p); - p += 2; - break; - case DW_CFA_advance_loc4: - *pc += read_4byte (p); - p += 4; - break; - - case DW_CFA_offset_extended: - p = decode_uleb128 (p, ®); - p = decode_uleb128 (p, &offset); - if (reg == state->s.cfa_reg) - /* Don't record anything; see above. */; - else - { - offset *= info->data_align; - state->s.saved[reg] = REG_SAVED_OFFSET; - state->s.reg_or_offset[reg] = offset; - } - break; - case DW_CFA_restore_extended: - p = decode_uleb128 (p, ®); - state->s.saved[reg] = REG_UNSAVED; - break; - - case DW_CFA_undefined: - case DW_CFA_same_value: - case DW_CFA_nop: - break; - - case DW_CFA_register: - { - unsigned reg2; - p = decode_uleb128 (p, ®); - p = decode_uleb128 (p, ®2); - state->s.saved[reg] = REG_SAVED_REG; - state->s.reg_or_offset[reg] = reg2; - } - break; - - case DW_CFA_def_cfa: - p = decode_uleb128 (p, ®); - p = decode_uleb128 (p, &offset); - state->s.cfa_reg = reg; - state->s.cfa_offset = offset; - break; - case DW_CFA_def_cfa_register: - p = decode_uleb128 (p, ®); - state->s.cfa_reg = reg; - break; - case DW_CFA_def_cfa_offset: - p = decode_uleb128 (p, &offset); - state->s.cfa_offset = offset; - break; - case DW_CFA_def_cfa_expression: - { - void *end; - state->s.cfa_reg = 0; - state->s.cfa_offset = 0; - state->s.base_offset = 0; - state->s.indirect = 0; - - p = decode_uleb128 (p, &offset); - end = p + offset; - while (p < end) - p = decode_stack_op (p, &(state->s)); - break; - } - - case DW_CFA_remember_state: - { - struct frame_state_internal *save = - (struct frame_state_internal *) - malloc (sizeof (struct frame_state_internal)); - memcpy (save, state, sizeof (struct frame_state_internal)); - state->saved_state = save; - } - break; - case DW_CFA_restore_state: - { - struct frame_state_internal *save = state->saved_state; - memcpy (state, save, sizeof (struct frame_state_internal)); - free (save); - } - break; - - /* FIXME: Hardcoded for SPARC register window configuration. */ - case DW_CFA_GNU_window_save: - for (reg = 16; reg < 32; ++reg) - { - state->s.saved[reg] = REG_SAVED_OFFSET; - state->s.reg_or_offset[reg] = (reg - 16) * sizeof (void *); - } - break; - - case DW_CFA_GNU_args_size: - p = decode_uleb128 (p, &offset); - state->s.args_size = offset; - break; - - case DW_CFA_GNU_negative_offset_extended: - p = decode_uleb128 (p, ®); - p = decode_uleb128 (p, &offset); - offset *= info->data_align; - state->s.saved[reg] = REG_SAVED_OFFSET; - state->s.reg_or_offset[reg] = -offset; - break; - - default: - abort (); - } - return p; -} - -/* Called from __throw to find the registers to restore for a given - PC_TARGET. The caller should allocate a local variable of `struct - frame_state' (declared in frame.h) and pass its address to STATE_IN. */ - -struct frame_state * -__frame_state_for (void *pc_target, struct frame_state *state_in) -{ - fde *f; - void *insn, *end, *pc; - struct cie_info info; - struct frame_state_internal state; - - f = find_fde (pc_target); - if (f == 0) - return 0; - - insn = extract_cie_info (f, &info); - if (insn == 0) - return 0; - - memset (&state, 0, sizeof (state)); - state.s.retaddr_column = info.ra_regno; - state.s.eh_ptr = info.eh_ptr; - - /* First decode all the insns in the CIE. */ - end = next_fde ((fde*) get_cie (f)); - while (insn < end) - insn = execute_cfa_insn (insn, &state, &info, 0); - - insn = ((fde *)f) + 1; - - if (info.augmentation[0] == 'z') - { - int i; - insn = decode_uleb128 (insn, &i); - insn += i; - } - - /* Then the insns in the FDE up to our target PC. */ - end = next_fde (f); - pc = f->pc_begin; - while (insn < end && pc <= pc_target) - insn = execute_cfa_insn (insn, &state, &info, &pc); - - memcpy (state_in, &state.s, sizeof (state.s)); - return state_in; -} -#endif /* DWARF2_UNWIND_INFO */ diff --git a/gcc/frame.h b/gcc/frame.h deleted file mode 100644 index 2257d52..0000000 --- a/gcc/frame.h +++ /dev/null @@ -1,287 +0,0 @@ -/* Header file for unwinding stack frames for exception handling. */ -/* Compile this one with gcc. */ -/* Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. - Contributed by Jason Merrill <jason@cygnus.com>. - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU CC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - - -#ifndef DWARF_FRAME_REGISTERS -#define DWARF_FRAME_REGISTERS FIRST_PSEUDO_REGISTER -#endif - -typedef struct frame_state -{ - void *cfa; - void *eh_ptr; - long cfa_offset; - long args_size; - long reg_or_offset[DWARF_FRAME_REGISTERS+1]; - unsigned short cfa_reg; - unsigned short retaddr_column; - char saved[DWARF_FRAME_REGISTERS+1]; - long base_offset; - char indirect; -} frame_state; - -/* Values for 'saved' above. */ -#define REG_UNSAVED 0 -#define REG_SAVED_OFFSET 1 -#define REG_SAVED_REG 2 - -/* The representation for an "object" to be searched for frame unwind info. - For targets with named sections, one object is an executable or shared - library; for other targets, one object is one translation unit. - - A copy of this structure declaration is printed by collect2.c; - keep the copies synchronized! */ - -struct object { -#ifdef IA64_UNWIND_INFO - void *pc_base; /* This field will be set by find_fde. */ -#endif - void *pc_begin; - void *pc_end; - struct dwarf_fde *fde_begin; -#ifdef IA64_UNWIND_INFO - struct dwarf_fde *fde_end; -#endif - struct dwarf_fde **fde_array; - size_t count; - struct object *next; -}; - -/* Note the following routines are exported interfaces from libgcc; do not - change these interfaces. Instead create new interfaces. Also note - references to these functions may be made weak in files where they - are referenced. */ - -extern void __register_frame (void * ); -extern void __register_frame_table (void *); -extern void __deregister_frame (void *); - -/* Called either from crtbegin.o or a static constructor to register the - unwind info for an object or translation unit, respectively. */ - -extern void __register_frame_info (void *, struct object *); - -/* Similar, but BEGIN is actually a pointer to a table of unwind entries - for different translation units. Called from the file generated by - collect2. */ -extern void __register_frame_info_table (void *, struct object *); - -/* Called from crtend.o to deregister the unwind info for an object. */ - -extern void *__deregister_frame_info (void *); - -/* Called from __throw to find the registers to restore for a given - PC_TARGET. The caller should allocate a local variable of `struct - frame_state' (declared in frame.h) and pass its address to STATE_IN. - Returns NULL on failure, otherwise returns STATE_IN. */ - -extern struct frame_state *__frame_state_for (void *, struct frame_state *); - -#ifdef IA64_UNWIND_INFO - -/* This is the information required for unwind records in an ia64 - object file. This is required by GAS and the compiler runtime. */ - -/* These are the starting point masks for the various types of - unwind records. To create a record of type R3 for instance, one - starts by using the value UNW_R3 and or-ing in any other required values. - These values are also unique (in context), so they can be used to identify - the various record types as well. UNW_Bx and some UNW_Px do have the - same value, but Px can only occur in a prologue context, and Bx in - a body context. */ - -#define UNW_R1 0x00 -#define UNW_R2 0x40 -#define UNW_R3 0x60 -#define UNW_P1 0x80 -#define UNW_P2 0xA0 -#define UNW_P3 0xB0 -#define UNW_P4 0xB8 -#define UNW_P5 0xB9 -#define UNW_P6 0xC0 -#define UNW_P7 0xE0 -#define UNW_P8 0xF0 -#define UNW_P9 0xF1 -#define UNW_P10 0xFF -#define UNW_X1 0xF9 -#define UNW_X2 0xFA -#define UNW_X3 0xFB -#define UNW_X4 0xFC -#define UNW_B1 0x80 -#define UNW_B2 0xC0 -#define UNW_B3 0xE0 -#define UNW_B4 0xF0 - -/* These are all the various types of unwind records. */ - -typedef enum -{ - prologue, prologue_gr, body, mem_stack_f, mem_stack_v, psp_gr, psp_sprel, - rp_when, rp_gr, rp_br, rp_psprel, rp_sprel, pfs_when, pfs_gr, pfs_psprel, - pfs_sprel, preds_when, preds_gr, preds_psprel, preds_sprel, - fr_mem, frgr_mem, gr_gr, gr_mem, br_mem, br_gr, spill_base, spill_mask, - unat_when, unat_gr, unat_psprel, unat_sprel, lc_when, lc_gr, lc_psprel, - lc_sprel, fpsr_when, fpsr_gr, fpsr_psprel, fpsr_sprel, - priunat_when_gr, priunat_when_mem, priunat_gr, priunat_psprel, - priunat_sprel, bsp_when, bsp_gr, bsp_psprel, bsp_sprel, bspstore_when, - bspstore_gr, bspstore_psprel, bspstore_sprel, rnat_when, rnat_gr, - rnat_psprel, rnat_sprel, epilogue, label_state, copy_state, - spill_psprel, spill_sprel, spill_reg, spill_psprel_p, spill_sprel_p, - spill_reg_p -} unw_record_type; - - -/* These structures declare the fields that can be used in each of the - 4 record formats, R, P, B and X. */ - -typedef struct unw_r_record -{ - unsigned long rlen; - unsigned short mask; - unsigned short grsave; -} unw_r_record; - -typedef struct unw_p_record -{ - void *imask; - unsigned long t; - unsigned long size; - unsigned long spoff; - unsigned long br; - unsigned long pspoff; - unsigned short gr; - unsigned short rmask; - unsigned short grmask; - unsigned long frmask; - unsigned short brmask; -} unw_p_record; - -typedef struct unw_b_record -{ - unsigned long t; - unsigned long label; - unsigned short ecount; -} unw_b_record; - -typedef struct unw_x_record -{ - unsigned long t; - unsigned long spoff; - unsigned long pspoff; - unsigned short reg; - unsigned short treg; - unsigned short qp; - unsigned short xy; /* Value of the XY field.. */ -} unw_x_record; - -/* This structure is used to determine the specific record type and - its fields. */ -typedef struct unwind_record -{ - unw_record_type type; - union { - unw_r_record r; - unw_p_record p; - unw_b_record b; - unw_x_record x; - } record; -} unwind_record; - -#define IA64_UNW_LOC_TYPE_NONE 0 -#define IA64_UNW_LOC_TYPE_MEM 1 -#define IA64_UNW_LOC_TYPE_GR 2 -#define IA64_UNW_LOC_TYPE_FR 3 -#define IA64_UNW_LOC_TYPE_BR 4 -#define IA64_UNW_LOC_TYPE_SPOFF 5 -#define IA64_UNW_LOC_TYPE_PSPOFF 6 -#define IA64_UNW_LOC_TYPE_OFFSET 7 -#define IA64_UNW_LOC_TYPE_SPILLBASE 8 - -typedef struct ia64_reg_loc -{ - long when; /* PC relative offset from start of function. */ - union { /* In memory or another register? */ - void *mem; - int regno; - int offset; - } l; - short loc_type; /* Where to find value. */ - short reg_size; -} ia64_reg_loc; - -/* Frame information record. */ - -typedef struct ia64_frame_state -{ - ia64_reg_loc gr[4]; /* gr4 to gr7. */ - ia64_reg_loc fr[20]; /* fr2 to fr5, fr16 to fr31. */ - ia64_reg_loc br[5]; /* br1 to br5. */ - ia64_reg_loc rp; - ia64_reg_loc fpsr; - ia64_reg_loc bsp; - ia64_reg_loc bspstore; - ia64_reg_loc rnat; - ia64_reg_loc pfs; - ia64_reg_loc unat; - ia64_reg_loc lc; - ia64_reg_loc pr; - ia64_reg_loc priunat; - ia64_reg_loc sp; - ia64_reg_loc psp; - ia64_reg_loc spill_base; - void *my_psp; - void *my_sp; - void *my_bsp; -} ia64_frame_state; - -/* This structure represents the start of an unwind information pointer. - 'unwind_descriptors' is the beginninng of the unwind descriptors, which - use up 'length' bytes of storage. */ - -typedef struct unwind_info_ptr -{ - unsigned long header; /* version, flags, & length */ - unsigned char unwind_descriptors[1]; -} unwind_info_ptr; - -#define IA64_UNW_HDR_LENGTH(x) ((x) & 0x00000000ffffffffUL) -#define IA64_UNW_HDR_FLAGS(x) (((x) >> 32) & 0xffffUL) -#define IA64_UNW_HDR_VERSION(x) (((x) >> 48) & 0xffffUL) - -/* Header flag bits, after extraction by IA64_UNW_HDR_FLAGS. */ -#define IA64_UNW_EHANDLER 0x1 -#define IA64_UNW_UHANDLER 0x2 - -extern void * __ia64_personality_v1 (void *pc, old_exception_table *table); - -extern unwind_info_ptr *__build_ia64_frame_state (unsigned char *, - ia64_frame_state *, - void *, void *, - void **); -extern void *__get_real_reg_value (ia64_reg_loc *); -extern void *__get_personality (unwind_info_ptr *); -extern void *__get_except_table (unwind_info_ptr *); -extern void __set_real_reg_value (ia64_reg_loc *, void *); -void *__calc_caller_bsp (long, unsigned char *); -void __copy_saved_reg_state (ia64_frame_state *, ia64_frame_state *); -#endif /* IA64_UNWIND_INFO */ - diff --git a/gcc/function.c b/gcc/function.c index 71bdf9d..6eebf34 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -1580,11 +1580,6 @@ fixup_var_refs (var, promoted_mode, unsignedp, ht) end_sequence (); } } - - /* Scan the catch clauses for exception handling too. */ - push_to_full_sequence (catch_clauses, catch_clauses_last); - fixup_var_refs_insns (catch_clauses, var, promoted_mode, unsignedp, 0); - end_full_sequence (&catch_clauses, &catch_clauses_last); } /* REPLACEMENTS is a pointer to a list of the struct fixup_replacement and X is @@ -6315,20 +6310,10 @@ expand_function_start (subr, parms_have_cleanups) else cleanup_label = 0; - /* Make the label for return statements to jump to, if this machine - does not have a one-instruction return and uses an epilogue, - or if it returns a structure, or if it has parm cleanups. */ -#ifdef HAVE_return - if (cleanup_label == 0 && HAVE_return - && ! current_function_instrument_entry_exit - && ! current_function_returns_pcc_struct - && ! (current_function_returns_struct && ! optimize)) - return_label = 0; - else - return_label = gen_label_rtx (); -#else + /* Make the label for return statements to jump to. Do not special + case machines with special return instructions -- they will be + handled later during jump, ifcvt, or epilogue creation. */ return_label = gen_label_rtx (); -#endif /* Initialize rtx used to return the value. */ /* Do this before assign_parms so that we copy the struct value address @@ -6370,7 +6355,9 @@ expand_function_start (subr, parms_have_cleanups) else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode) /* If return mode is void, this decl rtl should not be used. */ SET_DECL_RTL (DECL_RESULT (subr), NULL_RTX); - else if (parms_have_cleanups || current_function_instrument_entry_exit) + else if (parms_have_cleanups + || current_function_instrument_entry_exit + || (flag_exceptions && USING_SJLJ_EXCEPTIONS)) { /* If function will end with cleanup code for parms, compute the return values into a pseudo reg, @@ -6801,27 +6788,6 @@ expand_function_end (filename, line, end_bindings) if (end_bindings) expand_end_bindings (0, 0, 0); - /* Now handle any leftover exception regions that may have been - created for the parameters. */ - { - rtx last = get_last_insn (); - rtx label; - - expand_leftover_cleanups (); - - /* If there are any catch_clauses remaining, output them now. */ - emit_insns (catch_clauses); - catch_clauses = catch_clauses_last = NULL_RTX; - /* If the above emitted any code, may sure we jump around it. */ - if (last != get_last_insn ()) - { - label = gen_label_rtx (); - last = emit_jump_insn_after (gen_jump (label), last); - last = emit_barrier_after (last); - emit_label (label); - } - } - if (current_function_instrument_entry_exit) { rtx fun = DECL_RTL (current_function_decl); @@ -6837,6 +6803,11 @@ expand_function_end (filename, line, end_bindings) Pmode); } + /* Let except.c know where it should emit the call to unregister + the function context for sjlj exceptions. */ + if (flag_exceptions && USING_SJLJ_EXCEPTIONS) + sjlj_emit_function_exit_after (get_last_insn ()); + /* If we had calls to alloca, and this machine needs an accurate stack pointer to exit the function, insert some code to save and restore the stack pointer. */ @@ -6944,16 +6915,16 @@ expand_function_end (filename, line, end_bindings) current_function_return_rtx = outgoing; } + /* If this is an implementation of throw, do what's necessary to + communicate between __builtin_eh_return and the epilogue. */ + expand_eh_return (); + /* ??? This should no longer be necessary since stupid is no longer with us, but there are some parts of the compiler (eg reload_combine, and sh mach_dep_reorg) that still try and compute their own lifetime info instead of using the general framework. */ use_return_register (); - /* If this is an implementation of __throw, do what's necessary to - communicate between __builtin_eh_return and the epilogue. */ - expand_eh_return (); - /* Output a return insn if we are using one. Otherwise, let the rtl chain end here, to drop through into the epilogue. */ diff --git a/gcc/function.h b/gcc/function.h index bd4f38c..5621c6b 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -409,6 +409,9 @@ struct function either as a subroutine or builtin. */ unsigned int calls_alloca : 1; + /* Nonzero if the function calls __builtin_eh_return. */ + unsigned int calls_eh_return : 1; + /* Nonzero if function being compiled receives nonlocal gotos from nested functions. */ unsigned int has_nonlocal_label : 1; @@ -488,6 +491,7 @@ extern struct function *all_functions; #define current_function_calls_setjmp (cfun->calls_setjmp) #define current_function_calls_alloca (cfun->calls_alloca) #define current_function_calls_longjmp (cfun->calls_longjmp) +#define current_function_calls_eh_return (cfun->calls_eh_return) #define current_function_has_computed_jump (cfun->has_computed_jump) #define current_function_contains_functions (cfun->contains_functions) #define current_function_is_thunk (cfun->is_thunk) diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c index d21cb4d..3297107 100644 --- a/gcc/ifcvt.c +++ b/gcc/ifcvt.c @@ -1905,7 +1905,7 @@ find_if_block (test_bb, then_edge, else_edge) /* Make sure IF, THEN, and ELSE, blocks are adjacent. Actually, we get the first condition for free, since we've already asserted that there's a fallthru edge from IF to THEN. */ - /* ??? As an enhancement, move the ELSE block. Have to deal with EH and + /* ??? As an enhancement, move the ELSE block. Have to deal with BLOCK notes, if by no other means than aborting the merge if they exist. Sticky enough I don't want to think about it now. */ next_index = then_bb->index; @@ -2188,15 +2188,6 @@ dead_or_predicable (test_bb, merge_bb, other_bb, new_dest, reversep) { rtx head, end, jump, earliest, old_dest; - /* No code movement can occur if we'd be scrogging EH regions. - Within MERGE_BB, ensure that we've not got stray EH_BEG or EH_END - notes within the block. Between the blocks, checking that the end - region numbers match ensures that we won't disrupt the nesting - between regions. */ - if (merge_bb->eh_beg != merge_bb->eh_end - || merge_bb->eh_end != test_bb->eh_end) - return FALSE; - jump = test_bb->end; /* Find the extent of the real code in the merge block. */ diff --git a/gcc/integrate.c b/gcc/integrate.c index bedfd14..ed6540e 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -81,12 +81,12 @@ static void set_block_abstract_flags PARAMS ((tree, int)); static void process_reg_param PARAMS ((struct inline_remap *, rtx, rtx)); void set_decl_abstract_flags PARAMS ((tree, int)); -static rtx expand_inline_function_eh_labelmap PARAMS ((rtx)); static void mark_stores PARAMS ((rtx, rtx, void *)); static void save_parm_insns PARAMS ((rtx, rtx)); static void copy_insn_list PARAMS ((rtx, struct inline_remap *, rtx)); -static void copy_insn_notes PARAMS ((rtx, struct inline_remap *)); +static void copy_insn_notes PARAMS ((rtx, struct inline_remap *, + int)); static int compare_blocks PARAMS ((const PTR, const PTR)); static int find_block PARAMS ((const PTR, const PTR)); @@ -152,6 +152,9 @@ function_cannot_inline_p (fndecl) if (current_function_calls_setjmp) return N_("function using setjmp cannot be inline"); + if (current_function_calls_eh_return) + return N_("function uses __builtin_eh_return"); + if (current_function_contains_functions) return N_("function with nested functions cannot be inline"); @@ -221,19 +224,6 @@ function_cannot_inline_p (fndecl) if (current_function_has_nonlocal_goto) return N_("function with nonlocal goto cannot be inline"); - /* This is a hack, until the inliner is taught about eh regions at - the start of the function. */ - for (insn = get_insns (); - insn - && ! (GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG); - insn = NEXT_INSN (insn)) - { - if (insn && GET_CODE (insn) == NOTE - && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) - return N_("function with complex parameters cannot be inline"); - } - /* We can't inline functions that return a PARALLEL rtx. */ if (DECL_RTL_SET_P (DECL_RESULT (fndecl))) { @@ -548,17 +538,6 @@ process_reg_param (map, loc, copy) map->reg_map[REGNO (loc)] = copy; } -/* Used by duplicate_eh_handlers to map labels for the exception table */ -static struct inline_remap *eif_eh_map; - -static rtx -expand_inline_function_eh_labelmap (label) - rtx label; -{ - int index = CODE_LABEL_NUMBER (label); - return get_label_from_map (eif_eh_map, index); -} - /* Compare two BLOCKs for qsort. The key we sort on is the BLOCK_ABSTRACT_ORIGIN of the blocks. */ @@ -634,6 +613,7 @@ expand_inline_function (fndecl, parms, target, ignore, type, rtvec arg_vector = (rtvec) inl_f->original_arg_vector; rtx static_chain_value = 0; int inl_max_uid; + int eh_region_offset; /* The pointer used to track the true location of the memory used for MAP->LABEL_MAP. */ @@ -1140,8 +1120,14 @@ expand_inline_function (fndecl, parms, target, ignore, type, /* Now copy the insns one by one. */ copy_insn_list (insns, map, static_chain_value); + /* Duplicate the EH regions. This will create an offset from the + region numbers in the function we're inlining to the region + numbers in the calling function. This must wait until after + copy_insn_list, as we need the insn map to be complete. */ + eh_region_offset = duplicate_eh_regions (inl_f, map); + /* Now copy the REG_NOTES for those insns. */ - copy_insn_notes (insns, map); + copy_insn_notes (insns, map, eh_region_offset); /* If the insn sequence required one, emit the return label. */ if (map->local_return_label) @@ -1260,12 +1246,6 @@ copy_insn_list (insns, map, static_chain_value) inline_target. */ break; - /* If the inline fn needs eh context, make sure that - the current fn has one. */ - if (GET_CODE (pattern) == USE - && find_reg_note (insn, REG_EH_CONTEXT, 0) != 0) - get_eh_context (); - /* Ignore setting a function value that we don't want to use. */ if (map->inline_target == 0 && set != 0 @@ -1526,31 +1506,9 @@ copy_insn_list (insns, map, static_chain_value) copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); if (copy - && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG - || NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_END)) - { - rtx label - = get_label_from_map (map, NOTE_EH_HANDLER (copy)); - - /* We have to duplicate the handlers for the original. */ - if (NOTE_LINE_NUMBER (copy) == NOTE_INSN_EH_REGION_BEG) - { - /* We need to duplicate the handlers for the EH region - and we need to indicate where the label map is */ - eif_eh_map = map; - duplicate_eh_handlers (NOTE_EH_HANDLER (copy), - CODE_LABEL_NUMBER (label), - expand_inline_function_eh_labelmap); - } - - /* We have to forward these both to match the new exception - region. */ - NOTE_EH_HANDLER (copy) = CODE_LABEL_NUMBER (label); - } - else if (copy - && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG - || NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END) - && NOTE_BLOCK (insn)) + && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG + || NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END) + && NOTE_BLOCK (insn)) { tree *mapped_block_p; @@ -1587,9 +1545,10 @@ copy_insn_list (insns, map, static_chain_value) that are valid across the entire function. */ static void -copy_insn_notes (insns, map) +copy_insn_notes (insns, map, eh_region_offset) rtx insns; struct inline_remap *map; + int eh_region_offset; { rtx insn, new_insn; @@ -1620,6 +1579,9 @@ copy_insn_notes (insns, map) next = XEXP (note, 1); if (REG_NOTE_KIND (note) == REG_LABEL) remove_note (new_insn, note); + else if (REG_NOTE_KIND (note) == REG_EH_REGION) + XEXP (note, 0) = GEN_INT (INTVAL (XEXP (note, 0)) + + eh_region_offset); } } @@ -1628,8 +1590,12 @@ copy_insn_notes (insns, map) { int i; for (i = 0; i < 3; i++) - copy_insn_notes (XEXP (PATTERN (insn), i), map); + copy_insn_notes (XEXP (PATTERN (insn), i), map, eh_region_offset); } + + if (GET_CODE (insn) == JUMP_INSN + && GET_CODE (PATTERN (insn)) == RESX) + XINT (PATTERN (new_insn), 0) += eh_region_offset; } } @@ -2071,12 +2037,6 @@ copy_rtx_and_substitute (orig, map, for_lhs) copy_rtx_and_substitute (constant, map, for_lhs)), 0); } - else if (SYMBOL_REF_NEED_ADJUST (orig)) - { - eif_eh_map = map; - return rethrow_symbol_map (orig, - expand_inline_function_eh_labelmap); - } return orig; diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog index 8ccb4b2..48dc795 100644 --- a/gcc/java/ChangeLog +++ b/gcc/java/ChangeLog @@ -1,5 +1,36 @@ 2001-03-28 Richard Henderson <rth@redhat.com> + IA-64 ABI Exception Handling: + * Make-lang.in (except.o): Don't depend on eh-common.h. + * check-init.c (check_init): Handle EXC_PTR_EXPR. + * decl.c (init_decl_processing) [throw_node]: No _Jv_Sjlj_Throw. + [soft_exceptioninfo_call_node]: Remove. + [eh_personality_libfunc, lang_eh_runtime_type]: New. + (end_java_method): No emit_handlers. + * except.c (java_set_exception_lang_code): Remove. + (method_init_exceptions): Don't call it. + (prepare_eh_table_type): No CATCH_ALL_TYPE. + (build_exception_object_ref): New. + (expand_end_java_handler): Update for except.h name changes. + (emit_handlers, expand_resume_after_catch): Remove. + * expr.c (java_lang_expand_expr): Update for except.h name changes. + (process_jvm_instruction): Use build_exception_object_ref. + * java-tree.h (JTI_SOFT_EXCEPTIONINFO_CALL_NODE): Remove. + (soft_exceptioninfo_call_node): Remove. + (build_exception_object_ref): Declare. + * jcf-write.c (generate_bytecode_insns) [CALL_EXPR]: No + soft_exceptioninfo_call_node. Move processing ... + [EXC_PTR_EXPR]: ... here. + * parse.h (BUILD_ASSIGN_EXCEPTION_INFO): Remove dead code. + * parse.y (catch_clause_parameter): Use build_exception_object_ref. + (source_end_java_method): No java_set_exception_lang_code or + emit_handlers. + (build_dot_class_method): Use build_exception_object_ref. + (try_reference_assignconv): Check EXC_PTR_EXPR not + soft_exceptioninfo_call_node. + +2001-03-28 Richard Henderson <rth@redhat.com> + * java-tree.h (throw_node): Define as a single member of java_global_trees instead of a separate array. (JTI_THROW_NODE): New. diff --git a/gcc/java/Make-lang.in b/gcc/java/Make-lang.in index a4f5e48..ebfbd73 100644 --- a/gcc/java/Make-lang.in +++ b/gcc/java/Make-lang.in @@ -254,7 +254,7 @@ java/decl.o: java/decl.c $(CONFIG_H) $(JAVA_TREE_H) java/jcf.h \ toplev.h $(SYSTEM_H) function.h gcc.h java/except.o: java/except.c $(CONFIG_H) $(JAVA_TREE_H) java/jcf.h real.h \ $(RTL_H) java/javaop.h java/java-opcodes.h except.h java/java-except.h \ - eh-common.h toplev.h $(SYSTEM_H) function.h + toplev.h $(SYSTEM_H) function.h java/expr.o: java/expr.c $(CONFIG_H) $(JAVA_TREE_H) java/jcf.h real.h \ $(RTL_H) $(EXPR_H) java/javaop.h java/java-opcodes.h except.h \ java/java-except.h java/java-except.h java/parse.h toplev.h \ diff --git a/gcc/java/check-init.c b/gcc/java/check-init.c index 7414402..c348220 100644 --- a/gcc/java/check-init.c +++ b/gcc/java/check-init.c @@ -681,6 +681,7 @@ check_init (exp, before) case INTEGER_CST: case REAL_CST: case STRING_CST: + case EXC_PTR_EXPR: break; case NEW_CLASS_EXPR: diff --git a/gcc/java/decl.c b/gcc/java/decl.c index c0b67b2..7d03f8a 100644 --- a/gcc/java/decl.c +++ b/gcc/java/decl.c @@ -29,12 +29,14 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "config.h" #include "system.h" #include "tree.h" +#include "rtl.h" #include "toplev.h" #include "flags.h" #include "java-tree.h" #include "jcf.h" #include "toplev.h" #include "function.h" +#include "expr.h" #include "except.h" #include "java-except.h" #include "ggc.h" @@ -725,13 +727,14 @@ init_decl_processing () t), 0, NOT_BUILT_IN, NULL_PTR); - throw_node = builtin_function ((USING_SJLJ_EXCEPTIONS - ? "_Jv_Throw" : "_Jv_Sjlj_Throw"), + + throw_node = builtin_function ("_Jv_Throw", build_function_type (ptr_type_node, t), 0, NOT_BUILT_IN, NULL_PTR); /* Mark throw_nodes as `noreturn' functions with side effects. */ TREE_THIS_VOLATILE (throw_node) = 1; TREE_SIDE_EFFECTS (throw_node) = 1; + t = build_function_type (int_type_node, endlink); soft_monitorenter_node = builtin_function ("_Jv_MonitorEnter", t, 0, NOT_BUILT_IN, @@ -834,15 +837,6 @@ init_decl_processing () build_function_type (double_type_node, t), BUILT_IN_FMOD, BUILT_IN_NORMAL, "fmod"); - soft_exceptioninfo_call_node - = build (CALL_EXPR, - ptr_type_node, - build_address_of - (builtin_function ("_Jv_exception_info", - build_function_type (ptr_type_node, endlink), - 0, NOT_BUILT_IN, NULL_PTR)), - NULL_TREE, NULL_TREE); - TREE_SIDE_EFFECTS (soft_exceptioninfo_call_node) = 1; #if 0 t = tree_cons (NULL_TREE, float_type_node, tree_cons (NULL_TREE, float_type_node, endlink)); @@ -872,6 +866,12 @@ init_decl_processing () build_function_type (long_type_node, t), 0, NOT_BUILT_IN, NULL_PTR); + /* Initialize variables for except.c. */ + eh_personality_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS + ? "__gcj_personality_sj0" + : "__gcj_personality_v0"); + lang_eh_runtime_type = prepare_eh_table_type; + init_jcf_parse (); /* Register nodes with the garbage collector. */ @@ -1828,8 +1828,6 @@ end_java_method () BLOCK_SUPERCONTEXT (DECL_INITIAL (fndecl)) = fndecl; - emit_handlers (); - /* Generate rtl for function exit. */ expand_function_end (input_filename, lineno, 0); diff --git a/gcc/java/except.c b/gcc/java/except.c index f14d943..184f7e5 100644 --- a/gcc/java/except.c +++ b/gcc/java/except.c @@ -34,7 +34,6 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "function.h" #include "except.h" #include "java-except.h" -#include "eh-common.h" #include "toplev.h" static void expand_start_java_handler PARAMS ((struct eh_range *)); @@ -250,14 +249,6 @@ method_init_exceptions () whole_range.first_child = NULL; whole_range.next_sibling = NULL; cache_range_start = 0xFFFFFF; - java_set_exception_lang_code (); -} - -void -java_set_exception_lang_code () -{ - set_exception_lang_code (EH_LANG_Java); - set_exception_version_code (1); } /* Add an exception range. If we already have an exception range @@ -339,7 +330,7 @@ prepare_eh_table_type (type) * (which yields a value with low-order bit 1). */ if (type == NULL_TREE) - exp = CATCH_ALL_TYPE; + exp = NULL_TREE; else if (is_compiled_class (type)) exp = build_class_ref (type); else @@ -350,7 +341,27 @@ prepare_eh_table_type (type) return exp; } -/* if there are any handlers for this range, isssue end of range, + +/* Build a reference to the jthrowable object being carried in the + exception header. */ + +tree +build_exception_object_ref (type) + tree type; +{ + tree obj; + + /* Java only passes object via pointer and doesn't require adjusting. + The java object is immediately before the generic exception header. */ + obj = build (EXC_PTR_EXPR, build_pointer_type (type)); + obj = build (MINUS_EXPR, TREE_TYPE (obj), obj, + TYPE_SIZE_UNIT (TREE_TYPE (obj))); + obj = build1 (INDIRECT_REF, type, obj); + + return obj; +} + +/* If there are any handlers for this range, isssue end of range, and then all handler blocks */ static void expand_end_java_handler (range) @@ -361,11 +372,9 @@ expand_end_java_handler (range) expand_start_all_catch (); for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) { - start_catch_handler (prepare_eh_table_type (TREE_PURPOSE (handler))); - /* Push the thrown object on the top of the stack */ + expand_start_catch (TREE_PURPOSE (handler)); expand_goto (TREE_VALUE (handler)); - expand_resume_after_catch (); - end_catch_handler (); + expand_end_catch (); } expand_end_all_catch (); #if defined(DEBUG_JAVA_BINDING_LEVELS) @@ -432,30 +441,3 @@ maybe_end_try (start_pc, end_pc) current_range = current_range->outer; } } - -/* Emit the handler labels and their code */ - -void -emit_handlers () -{ - if (catch_clauses) - { - rtx funcend = gen_label_rtx (); - emit_jump (funcend); - - emit_insns (catch_clauses); - catch_clauses = catch_clauses_last = NULL_RTX; - expand_leftover_cleanups (); - - emit_label (funcend); - } -} - -/* Resume executing at the statement immediately after the end of an - exception region. */ - -void -expand_resume_after_catch () -{ - expand_goto (top_label_entry (&caught_return_label_stack)); -} diff --git a/gcc/java/expr.c b/gcc/java/expr.c index 42c43aa..072be6d 100644 --- a/gcc/java/expr.c +++ b/gcc/java/expr.c @@ -2502,15 +2502,13 @@ java_lang_expand_expr (exp, target, tmode, modifier) for (current = TREE_OPERAND (exp, 1); current; current = TREE_CHAIN (current)) { - tree type; tree catch = TREE_OPERAND (current, 0); tree decl = BLOCK_EXPR_DECLS (catch); - type = (decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE); - start_catch_handler (prepare_eh_table_type (type)); - expand_expr_stmt (TREE_OPERAND (current, 0)); + tree type = (decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE); - expand_resume_after_catch (); - end_catch_handler (); + expand_start_catch (type); + expand_expr_stmt (TREE_OPERAND (current, 0)); + expand_end_catch (); } expand_end_all_catch (); return const0_rtx; @@ -2812,7 +2810,7 @@ process_jvm_instruction (PC, byte_ops, length) if (instruction_bits [PC] & BCODE_EXCEPTION_TARGET) { tree type = pop_type (ptr_type_node); - push_value (build1 (NOP_EXPR, type, soft_exceptioninfo_call_node)); + push_value (build_exception_object_ref (type)); } switch (byte_ops[PC++]) diff --git a/gcc/java/java-tree.h b/gcc/java/java-tree.h index 01a0722..598caf4 100644 --- a/gcc/java/java-tree.h +++ b/gcc/java/java-tree.h @@ -339,7 +339,6 @@ enum java_tree_index JTI_SOFT_GETJNIENVNEWFRAME_NODE, JTI_SOFT_JNIPOPSYSTEMFRAME_NODE, JTI_SOFT_FMOD_NODE, - JTI_SOFT_EXCEPTIONINFO_CALL_NODE, JTI_SOFT_IDIV_NODE, JTI_SOFT_IREM_NODE, JTI_SOFT_LDIV_NODE, @@ -581,8 +580,6 @@ extern tree java_global_trees[JTI_MAX]; java_global_trees[JTI_SOFT_JNIPOPSYSTEMFRAME_NODE] #define soft_fmod_node \ java_global_trees[JTI_SOFT_FMOD_NODE] -#define soft_exceptioninfo_call_node \ - java_global_trees[JTI_SOFT_EXCEPTIONINFO_CALL_NODE] #define soft_idiv_node \ java_global_trees[JTI_SOFT_IDIV_NODE] #define soft_irem_node \ @@ -1026,6 +1023,7 @@ extern tree build_instanceof PARAMS ((tree, tree)); extern tree create_label_decl PARAMS ((tree)); extern void push_labeled_block PARAMS ((tree)); extern tree prepare_eh_table_type PARAMS ((tree)); +extern tree build_exception_object_ref PARAMS ((tree)); extern void java_set_exception_lang_code PARAMS ((void)); extern tree generate_name PARAMS ((void)); extern void pop_labeled_block PARAMS ((void)); diff --git a/gcc/java/jcf-write.c b/gcc/java/jcf-write.c index b84c86e..79ac691 100644 --- a/gcc/java/jcf-write.c +++ b/gcc/java/jcf-write.c @@ -2451,6 +2451,9 @@ generate_bytecode_insns (exp, target, state) } } break; + case EXC_PTR_EXPR: + NOTE_PUSH (1); /* Pushed by exception system. */ + break; case NEW_CLASS_EXPR: { tree class = TREE_TYPE (TREE_TYPE (exp)); @@ -2527,11 +2530,6 @@ generate_bytecode_insns (exp, target, state) NOTE_POP (1); break; } - else if (exp == soft_exceptioninfo_call_node) - { - NOTE_PUSH (1); /* Pushed by exception system. */ - break; - } for ( ; x != NULL_TREE; x = TREE_CHAIN (x)) { generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state); diff --git a/gcc/java/parse.h b/gcc/java/parse.h index 9ca55d4..d17dbc0 100644 --- a/gcc/java/parse.h +++ b/gcc/java/parse.h @@ -661,14 +661,6 @@ typedef struct _jdeplist { build_new_invocation (wfl_string_buffer, \ (ARG ? build_tree_list (NULL, (ARG)) : NULL_TREE)) -/* For exception handling, build diverse function calls */ -#define BUILD_ASSIGN_EXCEPTION_INFO(WHERE, TO) \ - { \ - (WHERE) = build (MODIFY_EXPR, void_type_node, (TO), \ - soft_exceptioninfo_call_node); \ - TREE_SIDE_EFFECTS (WHERE) = 1; \ - } - #define BUILD_THROW(WHERE, WHAT) \ { \ (WHERE) = \ diff --git a/gcc/java/parse.y b/gcc/java/parse.y index e09af7a..d377e8f 100644 --- a/gcc/java/parse.y +++ b/gcc/java/parse.y @@ -1885,9 +1885,9 @@ catch_clause_parameter: declared initialized by the appropriate function call */ tree ccpb = enter_block (); - tree init = build_assignment (ASSIGN_TK, $2.location, - TREE_PURPOSE ($3), - soft_exceptioninfo_call_node); + tree init = build_assignment + (ASSIGN_TK, $2.location, TREE_PURPOSE ($3), + build_exception_object_ref (ptr_type_node)); declare_local_variables (0, TREE_VALUE ($3), build_tree_list (TREE_PURPOSE ($3), init)); @@ -7124,9 +7124,6 @@ source_end_java_method () java_parser_context_save_global (); lineno = ctxp->last_ccb_indent1; - /* Set EH language codes */ - java_set_exception_lang_code (); - /* Turn function bodies with only a NOP expr null, so they don't get generated at all and we won't get warnings when using the -W -Wall flags. */ @@ -7148,8 +7145,6 @@ source_end_java_method () if (! flag_emit_class_files && ! flag_emit_xref) { lineno = DECL_SOURCE_LINE_LAST (fndecl); - /* Emit catch-finally clauses */ - emit_handlers (); expand_function_end (input_filename, lineno, 0); /* Run the optimizers and output assembler code for this function. */ @@ -8405,7 +8400,7 @@ build_dot_class_method (class) /* We initialize the variable with the exception handler. */ catch = build (MODIFY_EXPR, NULL_TREE, catch_clause_param, - soft_exceptioninfo_call_node); + build_exception_object_ref (ptr_type_node)); add_stmt_to_block (catch_block, NULL_TREE, catch); /* We add the statement throwing the new exception */ @@ -12609,7 +12604,7 @@ try_reference_assignconv (lhs_type, rhs) else if (valid_ref_assignconv_cast_p (rhs_type, lhs_type, 0)) new_rhs = rhs; /* This is a magic assignment that we process differently */ - else if (rhs == soft_exceptioninfo_call_node) + else if (TREE_CODE (rhs) == EXC_PTR_EXPR) new_rhs = rhs; } return new_rhs; @@ -213,12 +213,6 @@ jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, cross_jump_death_matters = (cross_jump == 2); max_uid = init_label_info (f) + 1; - /* If we are performing cross jump optimizations, then initialize - tables mapping UIDs to EH regions to avoid incorrect movement - of insns from one EH region to another. */ - if (flag_exceptions && cross_jump) - init_insn_eh_region (f, max_uid); - if (! mark_labels_only) delete_barrier_successors (f); @@ -237,8 +231,6 @@ jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, if (GET_CODE (XEXP (insn, 0)) == CODE_LABEL) LABEL_NUSES (XEXP (insn, 0))++; - check_exception_handler_labels (); - /* Keep track of labels used for marking handlers for exception regions; they cannot usually be deleted. */ @@ -251,9 +243,6 @@ jump_optimize_1 (f, cross_jump, noop_moves, after_regscan, if (mark_labels_only) goto end; - if (! minimal) - exception_optimize (); - last_insn = delete_unreferenced_labels (f); if (noop_moves) @@ -1444,13 +1433,6 @@ find_cross_jump (e1, e2, minimum, f1, f2) if (i2 == 0 || GET_CODE (i1) != GET_CODE (i2)) break; - /* Avoid moving insns across EH regions if either of the insns - can throw. */ - if (flag_exceptions - && (flag_non_call_exceptions || GET_CODE (i1) == CALL_INSN) - && !in_same_eh_region (i1, i2)) - break; - p1 = PATTERN (i1); p2 = PATTERN (i2); diff --git a/gcc/libgcc-std.ver b/gcc/libgcc-std.ver index 8efdd22..483d95d 100644 --- a/gcc/libgcc-std.ver +++ b/gcc/libgcc-std.ver @@ -114,28 +114,27 @@ GCC_3.0 { __gcc_bcmp # EH symbols - __default_terminate + _Unwind_DeleteException + _Unwind_ForcedUnwind + _Unwind_GetGR + _Unwind_GetIP + _Unwind_GetLanguageSpecificData + _Unwind_GetRegionStart + _Unwind_RaiseException + _Unwind_Resume + _Unwind_SetGR + _Unwind_SetIP __deregister_frame __deregister_frame_info - __eh_alloc - __eh_free - __eh_rtime_match - __frame_state_for - __get_dynamic_handler_chain - __get_eh_context - __get_eh_info - __get_eh_table_language - __get_eh_table_version __register_frame __register_frame_info __register_frame_info_table __register_frame_table - __rethrow - __sjpopnthrow - __sjthrow - __terminate - __terminate_set_func - __throw - __throw_type_match - __unwinding_cleanup + + # SjLj EH symbols + _Unwind_SjLj_Register + _Unwind_SjLj_Unregister + _Unwind_SjLj_RaiseException + _Unwind_SjLj_ForcedUnwind + _Unwind_SjLj_Resume } diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index 9e7aa99..140f1f7 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -3129,1221 +3129,3 @@ atexit (func_ptr func) #endif /* NEED_ATEXIT */ #endif /* L_exit */ - -#ifdef L_eh - -#include "gthr.h" - -/* Shared exception handling support routines. */ - -void -__default_terminate (void) -{ - abort (); -} - -static __terminate_func_ptr __terminate_func = - __default_terminate; - -void __attribute__((__noreturn__)) -__terminate (void) -{ - (*__terminate_func)(); -} - -__terminate_func_ptr -__terminate_set_func (__terminate_func_ptr newfunc) -{ - __terminate_func_ptr oldfunc = __terminate_func; - - __terminate_func = newfunc; - return (oldfunc); -} - -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 (void) -{ -} - - -/* Include definitions of EH context and table layout */ - -#include "eh-common.h" -#ifndef inhibit_libc -#include <stdio.h> -#endif - -/* Allocate and return a new EH context structure. */ - -#if __GTHREADS -static void * -new_eh_context (void) -{ - struct eh_full_context { - struct eh_context c; - void *top_elt[2]; - } *ehfc = (struct eh_full_context *) malloc (sizeof *ehfc); - - if (! ehfc) - __terminate (); - - memset (ehfc, 0, sizeof *ehfc); - - ehfc->c.dynamic_handler_chain = (void **) ehfc->top_elt; - - /* This should optimize out entirely. This should always be true, - but just in case it ever isn't, don't allow bogus code to be - generated. */ - - if ((void*)(&ehfc->c) != (void*)ehfc) - __terminate (); - - return &ehfc->c; -} - -static __gthread_key_t eh_context_key; - -/* Destructor for struct eh_context. */ -static void -eh_context_free (void *ptr) -{ - __gthread_key_dtor (eh_context_key, ptr); - if (ptr) - free (ptr); -} -#endif - -/* Pointer to function to return EH context. */ - -static struct eh_context *eh_context_initialize (void); -static struct eh_context *eh_context_static (void); -#if __GTHREADS -static struct eh_context *eh_context_specific (void); -#endif - -static struct eh_context *(*get_eh_context) (void) = &eh_context_initialize; - -/* Routine to get EH context. - This one will simply call the function pointer. */ - -void * -__get_eh_context (void) -{ - return (void *) (*get_eh_context) (); -} - -/* Get and set the language specific info pointer. */ - -void ** -__get_eh_info (void) -{ - struct eh_context *eh = (*get_eh_context) (); - return &eh->info; -} - -#ifdef DWARF2_UNWIND_INFO -static int dwarf_reg_size_table_initialized = 0; -static char dwarf_reg_size_table[DWARF_FRAME_REGISTERS]; - -static void -init_reg_size_table (void) -{ - __builtin_init_dwarf_reg_size_table (dwarf_reg_size_table); - dwarf_reg_size_table_initialized = 1; -} -#endif - -#if __GTHREADS -static void -eh_threads_initialize (void) -{ - /* Try to create the key. If it fails, revert to static method, - otherwise start using thread specific EH contexts. */ - if (__gthread_key_create (&eh_context_key, &eh_context_free) == 0) - get_eh_context = &eh_context_specific; - else - get_eh_context = &eh_context_static; -} -#endif /* no __GTHREADS */ - -/* Initialize EH context. - This will be called only once, since we change GET_EH_CONTEXT - pointer to another routine. */ - -static struct eh_context * -eh_context_initialize (void) -{ -#if __GTHREADS - - static __gthread_once_t once = __GTHREAD_ONCE_INIT; - /* Make sure that get_eh_context does not point to us anymore. - Some systems have dummy thread routines in their libc that - return a success (Solaris 2.6 for example). */ - if (__gthread_once (&once, eh_threads_initialize) != 0 - || get_eh_context == &eh_context_initialize) - { - /* Use static version of EH context. */ - get_eh_context = &eh_context_static; - } -#ifdef DWARF2_UNWIND_INFO - { - static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT; - if (__gthread_once (&once_regsizes, init_reg_size_table) != 0 - || ! dwarf_reg_size_table_initialized) - init_reg_size_table (); - } -#endif - -#else /* no __GTHREADS */ - - /* Use static version of EH context. */ - get_eh_context = &eh_context_static; - -#ifdef DWARF2_UNWIND_INFO - init_reg_size_table (); -#endif - -#endif /* no __GTHREADS */ - - return (*get_eh_context) (); -} - -/* Return a static EH context. */ - -static struct eh_context * -eh_context_static (void) -{ - static struct eh_context eh; - static int initialized; - static void *top_elt[2]; - - if (! initialized) - { - initialized = 1; - memset (&eh, 0, sizeof eh); - eh.dynamic_handler_chain = top_elt; - } - return &eh; -} - -#if __GTHREADS -/* Return a thread specific EH context. */ - -static struct eh_context * -eh_context_specific (void) -{ - struct eh_context *eh; - eh = (struct eh_context *) __gthread_getspecific (eh_context_key); - if (! eh) - { - eh = new_eh_context (); - if (__gthread_setspecific (eh_context_key, (void *) eh) != 0) - __terminate (); - } - - return eh; -} -#endif /* __GTHREADS */ - -/* Support routines for alloc/free during exception handling */ - -/* __eh_alloc and __eh_free attempt allocation using malloc, but fall back to - the small arena in the eh_context. This is needed because throwing an - out-of-memory exception would fail otherwise. The emergency space is - allocated in blocks of size EH_ALLOC_ALIGN, the - minimum allocation being two blocks. A bitmask indicates which blocks - have been allocated. To indicate the size of an allocation, the bit for - the final block is not set. Hence each allocation is a run of 1s followed - by a zero. */ -void * -__eh_alloc (size_t size) -{ - void *p; - - if (!size) - abort(); - p = malloc (size); - if (p == 0) - { - struct eh_context *eh = __get_eh_context (); - unsigned blocks = (size + EH_ALLOC_ALIGN - 1) / EH_ALLOC_ALIGN; - unsigned real_mask = eh->alloc_mask | (eh->alloc_mask << 1); - unsigned our_mask; - unsigned ix; - - if (blocks > EH_ALLOC_SIZE / EH_ALLOC_ALIGN) - __terminate (); - blocks += blocks == 1; - our_mask = (1 << blocks) - 1; - - for (ix = EH_ALLOC_SIZE / EH_ALLOC_ALIGN - blocks; ix; ix--) - if (! ((real_mask >> ix) & our_mask)) - { - /* found some space */ - p = &eh->alloc_buffer[ix * EH_ALLOC_ALIGN]; - eh->alloc_mask |= (our_mask >> 1) << ix; - return p; - } - __terminate (); - } - return p; -} - -/* Free the memory for an cp_eh_info and associated exception, given - a pointer to the cp_eh_info. */ -void -__eh_free (void *p) -{ - struct eh_context *eh = __get_eh_context (); - - ptrdiff_t diff = (char *)p - &eh->alloc_buffer[0]; - if (diff >= 0 && diff < EH_ALLOC_SIZE) - { - unsigned mask = eh->alloc_mask; - unsigned bit = 1 << (diff / EH_ALLOC_ALIGN); - - do - { - mask ^= bit; - bit <<= 1; - } - while (mask & bit); - eh->alloc_mask = mask; - } - else - free (p); -} - -/* 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. */ - -#ifdef DONT_USE_BUILTIN_SETJMP -extern void longjmp (void *, int); -#endif - -/* Routine to get the head of the current thread's dynamic handler chain - use for exception handling. */ - -void *** -__get_dynamic_handler_chain (void) -{ - struct eh_context *eh = (*get_eh_context) (); - return &eh->dynamic_handler_chain; -} - -/* This is used to throw an exception when the setjmp/longjmp codegen - method is used for exception handling. - - We call __terminate if there are no handlers left. Otherwise we run the - cleanup actions off the dynamic cleanup stack, and pop the top of the - dynamic handler chain, and use longjmp to transfer back to the associated - handler. */ - -void -__sjthrow (void) -{ - struct eh_context *eh = (*get_eh_context) (); - void ***dhc = &eh->dynamic_handler_chain; - void *jmpbuf; - void (*func)(void *, int); - void *arg; - /* The cleanup chain is one word into the buffer. Get the cleanup chain. */ - void ***cleanup = (void***)&(*dhc)[1]; - - /* If there are any cleanups in the chain, run them now. */ - if (cleanup[0]) - { - double store[200]; - void **buf = (void**)store; - buf[1] = 0; - buf[0] = (*dhc); - - /* try { */ -#ifdef DONT_USE_BUILTIN_SETJMP - if (! setjmp (&buf[2])) -#else - if (! __builtin_setjmp (&buf[2])) -#endif - { - *dhc = buf; - while (cleanup[0]) - { - func = (void(*)(void*, int))cleanup[0][1]; - arg = (void*)cleanup[0][2]; - - /* Update this before running the cleanup. */ - cleanup[0] = (void **)cleanup[0][0]; - - (*func)(arg, 2); - } - *dhc = buf[0]; - } - /* catch (...) */ - else - { - __terminate (); - } - } - - /* We must call terminate if we try and rethrow an exception, when - there is no exception currently active and when there are no - handlers left. */ - if (! eh->info || (*dhc)[0] == 0) - __terminate (); - - /* Find the jmpbuf associated with the top element of the dynamic - handler chain. The jumpbuf starts two words into the buffer. */ - jmpbuf = &(*dhc)[2]; - - /* Then we pop the top element off the dynamic handler chain. */ - *dhc = (void**)(*dhc)[0]; - - /* And then we jump to the handler. */ - -#ifdef DONT_USE_BUILTIN_SETJMP - longjmp (jmpbuf, 1); -#else - __builtin_longjmp (jmpbuf, 1); -#endif -} - -/* Run cleanups on the dynamic cleanup stack for the current dynamic - handler, then pop the handler off the dynamic handler stack, and - then throw. This is used to skip the first handler, and transfer - control to the next handler in the dynamic handler stack. */ - -void -__sjpopnthrow (void) -{ - struct eh_context *eh = (*get_eh_context) (); - void ***dhc = &eh->dynamic_handler_chain; - void (*func)(void *, int); - void *arg; - /* The cleanup chain is one word into the buffer. Get the cleanup chain. */ - void ***cleanup = (void***)&(*dhc)[1]; - - /* If there are any cleanups in the chain, run them now. */ - if (cleanup[0]) - { - double store[200]; - void **buf = (void**)store; - buf[1] = 0; - buf[0] = (*dhc); - - /* try { */ -#ifdef DONT_USE_BUILTIN_SETJMP - if (! setjmp (&buf[2])) -#else - if (! __builtin_setjmp (&buf[2])) -#endif - { - *dhc = buf; - while (cleanup[0]) - { - func = (void(*)(void*, int))cleanup[0][1]; - arg = (void*)cleanup[0][2]; - - /* Update this before running the cleanup. */ - cleanup[0] = (void **)cleanup[0][0]; - - (*func)(arg, 2); - } - *dhc = buf[0]; - } - /* catch (...) */ - else - { - __terminate (); - } - } - - /* Then we pop the top element off the dynamic handler chain. */ - *dhc = (void**)(*dhc)[0]; - - __sjthrow (); -} - -/* Support code for all exception region-based exception handling. */ - -int -__eh_rtime_match (void *rtime) -{ - void *info; - __eh_matcher matcher; - void *ret; - - info = *(__get_eh_info ()); - matcher = ((__eh_info *)info)->match_function; - if (! matcher) - { -#ifndef inhibit_libc - fprintf (stderr, "Internal Compiler Bug: No runtime type matcher."); -#endif - return 0; - } - ret = (*matcher) (info, rtime, (void *)0); - return (ret != NULL); -} - -/* This value identifies the place from which an exception is being - thrown. */ - -#ifdef EH_TABLE_LOOKUP - -EH_TABLE_LOOKUP - -#else - -#ifdef DWARF2_UNWIND_INFO - -/* Return the table version of an exception descriptor */ - -short -__get_eh_table_version (exception_descriptor *table) -{ - return table->lang.version; -} - -/* Return the originating table language of an exception descriptor */ - -short -__get_eh_table_language (exception_descriptor *table) -{ - return table->lang.language; -} - -/* 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 * -old_find_exception_handler (void *pc, old_exception_table *table) -{ - if (table) - { - int pos; - int best = -1; - - /* 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].start_region != (void *) -1; ++pos) - { - if (table[pos].start_region <= pc && table[pos].end_region > pc) - { - /* This can apply. Make sure it is at least as small as - the previous best. */ - if (best == -1 || (table[pos].end_region <= table[best].end_region - && table[pos].start_region >= table[best].start_region)) - best = pos; - } - /* But it is sorted by starting PC within a function. */ - else if (best >= 0 && table[pos].start_region > pc) - break; - } - if (best != -1) - return table[best].exception_handler; - } - - return (void *) 0; -} - -/* find_exception_handler finds the correct handler, if there is one, to - handle an exception. - returns a pointer to the handler which controlled should be transferred - to, or NULL if there is nothing left. - Parameters: - PC - pc where the exception originates. If this is a rethrow, - then this starts out as a pointer to the exception table - entry we wish to rethrow out of. - TABLE - exception table for the current module. - EH_INFO - eh info pointer for this exception. - RETHROW - 1 if this is a rethrow. (see incoming value of PC). - CLEANUP - returned flag indicating whether this is a cleanup handler. -*/ -static void * -find_exception_handler (void *pc, exception_descriptor *table, - __eh_info *eh_info, int rethrow, int *cleanup) -{ - - void *retval = NULL; - *cleanup = 1; - if (table) - { - int pos = 0; - /* The new model assumed the table is sorted inner-most out so the - first region we find which matches is the correct one */ - - exception_table *tab = &(table->table[0]); - - /* Subtract 1 from the PC to avoid hitting the next region */ - if (rethrow) - { - /* pc is actually the region table entry to rethrow out of */ - pos = ((exception_table *) pc) - tab; - pc = ((exception_table *) pc)->end_region - 1; - - /* The label is always on the LAST handler entry for a region, - so we know the next entry is a different region, even if the - addresses are the same. Make sure its not end of table tho. */ - if (tab[pos].start_region != (void *) -1) - pos++; - } - else - pc--; - - /* We can't do a binary search because the table is in inner-most - to outermost address ranges within functions */ - for ( ; tab[pos].start_region != (void *) -1; pos++) - { - if (tab[pos].start_region <= pc && tab[pos].end_region > pc) - { - if (tab[pos].match_info) - { - __eh_matcher matcher = eh_info->match_function; - /* match info but no matcher is NOT a match */ - if (matcher) - { - void *ret = (*matcher)((void *) eh_info, - tab[pos].match_info, table); - if (ret) - { - if (retval == NULL) - retval = tab[pos].exception_handler; - *cleanup = 0; - break; - } - } - } - else - { - if (retval == NULL) - retval = tab[pos].exception_handler; - } - } - } - } - return retval; -} -#endif /* DWARF2_UNWIND_INFO */ -#endif /* EH_TABLE_LOOKUP */ - -#ifdef 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))); - -typedef struct -{ - word_type *reg[DWARF_FRAME_REGISTERS]; -} saved_regs_t; - -#ifdef INCOMING_REGNO -/* Is the saved value for register REG in frame UDATA stored in a register - window in the previous frame? */ - -/* ??? The Sparc INCOMING_REGNO references TARGET_FLAT. This allows us - to use the macro here. One wonders, though, that perhaps TARGET_FLAT - compiled functions won't work with the frame-unwind stuff here. - Perhaps the entireity of in_reg_window should be conditional on having - seen a DW_CFA_GNU_window_save? */ -#define target_flags 0 - -static int -in_reg_window (int reg, frame_state *udata) -{ - if (udata->saved[reg] == REG_SAVED_REG) - return INCOMING_REGNO (reg) == reg; - 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 -} -#else -static inline int -in_reg_window (int reg __attribute__ ((__unused__)), - frame_state *udata __attribute__ ((__unused__))) -{ - return 0; -} -#endif /* INCOMING_REGNO */ - -/* Get the address of register REG as saved in UDATA, where SUB_UDATA is a - frame called by UDATA or 0. */ - -static word_type * -get_reg_addr (unsigned reg, frame_state *udata, frame_state *sub_udata) -{ - while (udata->saved[reg] == REG_SAVED_REG) - { - reg = udata->reg_or_offset[reg]; - if (in_reg_window (reg, udata)) - { - udata = sub_udata; - sub_udata = NULL; - } - } - if (udata->saved[reg] == REG_SAVED_OFFSET) - return (word_type *)(udata->cfa + udata->reg_or_offset[reg]); - else - /* We don't have a saved copy of this register. */ - return NULL; -} - -/* Get the value of register REG as saved in UDATA, where SUB_UDATA is a - frame called by UDATA or 0. */ - -static inline void * -get_reg (unsigned reg, frame_state *udata, frame_state *sub_udata) -{ - return (void *)(ptr_type) *get_reg_addr (reg, udata, sub_udata); -} - -/* Overwrite the saved value for register REG in frame UDATA with VAL. */ - -static inline void -put_reg (unsigned reg, void *val, frame_state *udata) -{ - *get_reg_addr (reg, udata, NULL) = (word_type)(ptr_type) val; -} - -/* Copy the saved value for register REG from PTREG to frame - TARGET_UDATA. Unlike the previous two functions, this can handle - registers that are not one word large. */ - -static void -copy_reg (unsigned reg, word_type *preg, frame_state *target_udata) -{ - word_type *ptreg = get_reg_addr (reg, target_udata, NULL); - memcpy (ptreg, preg, dwarf_reg_size_table [reg]); -} - -/* Retrieve the return address for frame UDATA. */ - -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 and update the - register array in SAVED_REGS. */ - -static void * -next_stack_level (void *pc, frame_state *udata, frame_state *caller_udata, - saved_regs_t *saved_regs) -{ - int i; - word_type *p; - - /* Collect all of the registers for the current frame. */ - for (i = 0; i < DWARF_FRAME_REGISTERS; i++) - if (udata->saved[i]) - saved_regs->reg[i] = get_reg_addr (i, udata, 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 was - saved in a register in this stack frame or a previous one, restore it; - otherwise, assume CFA register is SP and restore it to our CFA value - (which is defined to be the value of SP in the caller's frame). */ - - p = saved_regs->reg[caller_udata->cfa_reg]; - if (p) - caller_udata->cfa = (void *)(ptr_type)*p; - else - caller_udata->cfa = udata->cfa; - - if (caller_udata->indirect) - caller_udata->cfa = * (void **) ((unsigned char *)caller_udata->cfa - + caller_udata->base_offset); - caller_udata->cfa += caller_udata->cfa_offset; - - return caller_udata; -} - -/* Hook to call before __terminate if only cleanup handlers remain. */ -void -__unwinding_cleanup (void) -{ -} - -/* throw_helper performs some of the common grunt work for a throw. This - routine is called by throw and rethrows. This is pretty much split - out from the old __throw routine. An addition has been added which allows - for a dummy call to a routine __unwinding_cleanup() when there are nothing - but cleanups remaining. This allows a debugger to examine the state - at which the throw was executed, before any cleanups, rather than - at the terminate point after the stack has been unwound. - - EH is the current eh_context structure. - PC is the address of the call to __throw. - MY_UDATA is the unwind information for __throw. - OFFSET_P is where we return the SP adjustment offset. */ - -static void * -throw_helper (struct eh_context *eh, void *pc, frame_state *my_udata, - long *offset_p) -{ - frame_state ustruct2, *udata = &ustruct2; - frame_state ustruct; - frame_state *sub_udata = &ustruct; - void *saved_pc = pc; - void *handler; - void *handler_p = 0; - void *pc_p = 0; - void *callee_cfa = 0; - frame_state saved_ustruct; - int new_eh_model; - int cleanup = 0; - int only_cleanup = 0; - int rethrow = 0; - int saved_state = 0; - long args_size; - saved_regs_t saved_regs, cleanup_regs; - __eh_info *eh_info = (__eh_info *)eh->info; - int i; - - memset (saved_regs.reg, 0, sizeof saved_regs.reg); - memset (sub_udata->saved, REG_UNSAVED, sizeof sub_udata->saved); - - /* Do we find a handler based on a re-throw PC? */ - if (eh->table_index != (void *) 0) - rethrow = 1; - - memcpy (udata, my_udata, sizeof (*udata)); - - handler = (void *) 0; - for (;;) - { - frame_state *p = udata; - - udata = next_stack_level (pc, udata, sub_udata, &saved_regs); - sub_udata = p; - - /* If we couldn't find the next frame, we lose. */ - if (! udata) - break; - - if (udata->eh_ptr == NULL) - new_eh_model = 0; - else - new_eh_model = (((exception_descriptor *)(udata->eh_ptr))-> - runtime_id_field == NEW_EH_RUNTIME); - - if (rethrow) - { - rethrow = 0; - handler = find_exception_handler (eh->table_index, udata->eh_ptr, - eh_info, 1, &cleanup); - eh->table_index = (void *)0; - } - else - if (new_eh_model) - handler = find_exception_handler (pc, udata->eh_ptr, eh_info, - 0, &cleanup); - else - handler = old_find_exception_handler (pc, udata->eh_ptr); - - /* If we found one, we can stop searching, if its not a cleanup. - for cleanups, we save the state, and keep looking. This allows - us to call a debug hook if there are nothing but cleanups left. */ - if (handler) - { - /* sub_udata now refers to the frame called by the handler frame. */ - - if (cleanup) - { - if (!saved_state) - { - saved_ustruct = *udata; - cleanup_regs = saved_regs; - handler_p = handler; - pc_p = pc; - saved_state = 1; - only_cleanup = 1; - /* Save the CFA of the frame called by the handler - frame. */ - callee_cfa = sub_udata->cfa; - } - } - else - { - only_cleanup = 0; - if (!saved_state) - callee_cfa = sub_udata->cfa; - break; - } - } - - /* Otherwise, we continue searching. We subtract 1 from PC to avoid - hitting the beginning of the next region. */ - pc = get_return_addr (udata, sub_udata) - 1; - } - - if (saved_state) - { - udata = &saved_ustruct; - saved_regs = cleanup_regs; - handler = handler_p; - pc = pc_p; - if (only_cleanup) - __unwinding_cleanup (); - } - - /* If we haven't found a handler by now, this is an unhandled - exception. */ - if (! handler) - __terminate(); - - eh->handler_label = handler; - - args_size = udata->args_size; - - /* We adjust SP by the difference between __throw's CFA and the CFA for - the frame called by the handler frame, because those CFAs correspond - to the SP values at the two call sites. We need to further adjust by - the args_size of the handler frame itself to get the handler frame's - SP from before the args were pushed for that call. */ -#ifdef STACK_GROWS_DOWNWARD - *offset_p = callee_cfa - my_udata->cfa + args_size; -#else - *offset_p = my_udata->cfa - callee_cfa - args_size; -#endif - - /* If we found a handler in the throw context there's no need to - unwind. */ - if (pc != saved_pc) - { - /* Copy saved register values into our register save slots. */ - for (i = 0; i < DWARF_FRAME_REGISTERS; i++) - if (i != udata->retaddr_column && saved_regs.reg[i]) - copy_reg (i, saved_regs.reg[i], my_udata); - } - - return handler; -} - - -/* 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. */ - -/*extern void __throw(void) __attribute__ ((__noreturn__));*/ - -void -__throw (void) -{ - struct eh_context *eh = (*get_eh_context) (); - void *pc, *handler; - long offset; - - /* XXX maybe make my_ustruct static so we don't have to look it up for - each throw. */ - frame_state my_ustruct, *my_udata = &my_ustruct; - - /* 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->info) - __terminate (); - - /* Start at our stack frame. */ -label: - my_udata = __frame_state_for (&&label, my_udata); - if (! my_udata) - __terminate (); - - /* We need to get the value from the CFA register. */ - my_udata->cfa = __builtin_dwarf_cfa (); - - /* 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. The return address points to - the instruction after the call to __throw; we subtract 1 so that pc - points into the call insn itself. Since we work with PC ranges (as - opposed to specific call sites), it isn't important for it to point to - the very beginning of the call insn, and making it do so would be - hard on targets with variable length call insns. */ - pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; - - handler = throw_helper (eh, pc, my_udata, &offset); - - /* Now go! */ - - __builtin_eh_return ((void *)eh, offset, handler); - - /* Epilogue: restore the handler frame's register values and return - to the stub. */ -} - -/*extern void __rethrow(void *) __attribute__ ((__noreturn__));*/ - -void -__rethrow (void *index) -{ - struct eh_context *eh = (*get_eh_context) (); - void *pc, *handler; - long offset; - - /* XXX maybe make my_ustruct static so we don't have to look it up for - each throw. */ - frame_state my_ustruct, *my_udata = &my_ustruct; - - /* 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->info) - __terminate (); - - /* This is the table index we want to rethrow from. The value of - the END_REGION label is used for the PC of the throw, and the - search begins with the next table entry. */ - eh->table_index = index; - - /* Start at our stack frame. */ -label: - my_udata = __frame_state_for (&&label, my_udata); - if (! my_udata) - __terminate (); - - /* We need to get the value from the CFA register. */ - my_udata->cfa = __builtin_dwarf_cfa (); - - /* 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. The return address points to - the instruction after the call to __throw; we subtract 1 so that pc - points into the call insn itself. Since we work with PC ranges (as - opposed to specific call sites), it isn't important for it to point to - the very beginning of the call insn, and making it do so would be - hard on targets with variable length call insns. */ - pc = __builtin_extract_return_addr (__builtin_return_address (0)) - 1; - - handler = throw_helper (eh, pc, my_udata, &offset); - - /* Now go! */ - - __builtin_eh_return ((void *)eh, offset, handler); - - /* Epilogue: restore the handler frame's register values and return - to the stub. */ -} -#endif /* DWARF2_UNWIND_INFO */ - -#ifdef IA64_UNWIND_INFO -#include "frame.h" - -/* Return handler to which we want to transfer control, NULL if we don't - intend to handle this exception here. */ -void * -__ia64_personality_v1 (void *pc, old_exception_table *table) -{ - if (table) - { - int pos; - int best = -1; - - for (pos = 0; table[pos].start_region != (void *) -1; ++pos) - { - if (table[pos].start_region <= pc && table[pos].end_region > pc) - { - /* This can apply. Make sure it is at least as small as - the previous best. */ - if (best == -1 || (table[pos].end_region <= table[best].end_region - && table[pos].start_region >= table[best].start_region)) - best = pos; - } - /* It is sorted by starting PC within a function. */ - else if (best >= 0 && table[pos].start_region > pc) - break; - } - if (best != -1) - return table[best].exception_handler; - } - return (void *) 0; -} - -static void -ia64_throw_helper (ia64_frame_state *throw_frame, ia64_frame_state *caller, - void *throw_bsp, void *throw_sp) -{ - void *throw_pc = __builtin_return_address (0); - unwind_info_ptr *info; - void *pc, *handler = NULL; - void *pc_base; - int frame_count; - void *bsp; - - __builtin_ia64_flushrs (); /* Make the local register stacks available. */ - - /* Start at our stack frame, get our state. */ - __build_ia64_frame_state (throw_pc, throw_frame, throw_bsp, throw_sp, - &pc_base); - - /* Now we have to find the proper frame for pc, and see if there - is a handler for it. if not, we keep going back frames until - we do find one. Otherwise we call uncaught (). */ - - frame_count = 0; - memcpy (caller, throw_frame, sizeof (*caller)); - while (!handler) - { - void *(*personality) (void *, old_exception_table *); - void *eh_table; - - frame_count++; - /* We only care about the RP right now, so we dont need to keep - any other information about a call frame right now. */ - pc = __get_real_reg_value (&caller->rp) - 1; - bsp = __calc_caller_bsp ((long)__get_real_reg_value (&caller->pfs), - caller->my_bsp); - info = __build_ia64_frame_state (pc, caller, bsp, caller->my_psp, - &pc_base); - - /* If we couldn't find the next frame, we lose. */ - if (! info) - break; - - personality = __get_personality (info); - /* TODO Haven't figured out how to actually load the personality address - yet, so just always default to the one we expect for now. */ - if (personality != 0) - personality = __ia64_personality_v1; - eh_table = __get_except_table (info); - /* If there is no personality routine, we'll keep unwinding. */ - if (personality) - /* Pass a segment relative PC address to the personality routine, - because the unwind_info section uses segrel relocs. */ - handler = personality ((void *)(pc - pc_base), eh_table); - } - - if (!handler) - __terminate (); - - /* Handler is a segment relative address, so we must adjust it here. */ - handler += (long) pc_base; - - /* If we found a handler, we need to unwind the stack to that point. - We do this by copying saved values from previous frames into the - save slot for the throw_frame saved slots. when __throw returns, - it'll pickup the correct values. */ - - /* Start with where __throw saved things, and copy each saved register - of each previous frame until we get to the one before we're - throwing back to. */ - memcpy (caller, throw_frame, sizeof (*caller)); - for ( ; frame_count > 0; frame_count--) - { - pc = __get_real_reg_value (&caller->rp) - 1; - bsp = __calc_caller_bsp ((long)__get_real_reg_value (&caller->pfs), - caller->my_bsp); - __build_ia64_frame_state (pc, caller, bsp, caller->my_psp, &pc_base); - /* Any regs that were saved can be put in the throw frame now. */ - /* We don't want to copy any saved register from the - target destination, but we do want to load up it's frame. */ - if (frame_count > 1) - __copy_saved_reg_state (throw_frame, caller); - } - - /* Set return address of the throw frame to the handler. */ - __set_real_reg_value (&throw_frame->rp, handler); - - /* TODO, do we need to do anything to make the values we wrote 'stick'? */ - /* DO we need to go through the whole loadrs seqeunce? */ -} - - -void -__throw () -{ - register void *stack_pointer __asm__("r12"); - struct eh_context *eh = (*get_eh_context) (); - ia64_frame_state my_frame; - ia64_frame_state originator; /* For the context handler is in. */ - void *bsp, *tmp_bsp; - long offset; - - /* 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->info) - __terminate (); - - __builtin_unwind_init (); - - /* We have to call another routine to actually process the frame - information, which will force all of __throw's local registers into - backing store. */ - - /* Get the value of ar.bsp while we're here. */ - - bsp = __builtin_ia64_bsp (); - ia64_throw_helper (&my_frame, &originator, bsp, stack_pointer); - - /* Now we have to fudge the bsp by the amount in our (__throw) - frame marker, since the return is going to adjust it by that much. */ - - tmp_bsp = __calc_caller_bsp ((long)__get_real_reg_value (&my_frame.pfs), - my_frame.my_bsp); - offset = (char *)my_frame.my_bsp - (char *)tmp_bsp; - tmp_bsp = (char *)originator.my_bsp + offset; - - __builtin_eh_return (tmp_bsp, offset, originator.my_sp); - - /* The return address was already set by throw_helper. */ -} - -#endif /* IA64_UNWIND_INFO */ - -#endif /* L_eh */ diff --git a/gcc/md.texi b/gcc/md.texi index 411f67f..047f256 100644 --- a/gcc/md.texi +++ b/gcc/md.texi @@ -2848,26 +2848,24 @@ You will not normally need to define this pattern unless you also define @code{builtin_setjmp_setup}. The single argument is a pointer to the @code{jmp_buf}. -@cindex @code{eh_epilogue} instruction pattern -@item @samp{eh_epilogue} +@cindex @code{eh_return} instruction pattern +@item @samp{eh_return} This pattern, if defined, affects the way @code{__builtin_eh_return}, -and thence @code{__throw} are built. It is intended to allow communication -between the exception handling machinery and the normal epilogue code -for the target. - -The pattern takes three arguments. The first is the exception context -pointer. This will have already been copied to the function return -register appropriate for a pointer; normally this can be ignored. The -second argument is an offset to be added to the stack pointer. It will -have been copied to some arbitrary call-clobbered hard reg so that it -will survive until after reload to when the normal epilogue is generated. -The final argument is the address of the exception handler to which +and thence the call frame exception handling library routines, are +built. It is intended to handle non-trivial actions needed along +the abnormal return path. + +The pattern takes two arguments. The first is an offset to be applied +to the stack pointer. It will have been copied to some appropriate +location (typically @code{EH_RETURN_STACKADJ_RTX}) which will survive +until after reload to when the normal epilogue is generated. +The second argument is the address of the exception handler to which the function should return. This will normally need to copied by the -pattern to some special register. +pattern to some special register or memory location. -This pattern must be defined if @code{RETURN_ADDR_RTX} does not yield -something that can be reliably and permanently modified, i.e. a fixed -hard register or a stack memory reference. +This pattern only needs to be defined if call frame exception handling +is to be used, and simple moves to @code{EH_RETURN_STACKADJ_RTX} and +@code{EH_RETURN_HANDLER_RTX} are not sufficient. @cindex @code{prologue} instruction pattern @item @samp{prologue} diff --git a/gcc/mklibgcc.in b/gcc/mklibgcc.in index 6341b30..70fc55a 100644 --- a/gcc/mklibgcc.in +++ b/gcc/mklibgcc.in @@ -14,8 +14,9 @@ # LIB1ASMFUNCS # LIB1FUNCS_EXTRA # LIB2FUNCS -# LIB2FUNCS_EH # LIB2ADD +# LIB2ADDEH +# LIB2ADDEHDEP # FPBIT # FPBIT_FUNCS # DPBIT @@ -79,7 +80,7 @@ make_compile='$(MAKE) GCC_FOR_TARGET="$(GCC_FOR_TARGET)" \ libgcc1_c_dep='stmp-dirs $(srcdir)/libgcc1.c $(CONFIG_H)' # Dependancies for libgcc2.c -libgcc2_c_dep='stmp-dirs $(srcdir)/libgcc2.c $(CONFIG_H) $(MACHMODE_H) longlong.h frame.h gbl-ctors.h config.status stmp-int-hdrs tsystem.h' +libgcc2_c_dep='stmp-dirs $(srcdir)/libgcc2.c $(CONFIG_H) $(MACHMODE_H) longlong.h gbl-ctors.h config.status stmp-int-hdrs tsystem.h'" $LIB2ADDEHDEP" # Dependancies for fp-bit.c fpbit_c_dep='stmp-dirs config.status tsystem.h' @@ -180,19 +181,6 @@ for name in $LIB2FUNCS; do libgcc2_objs="$libgcc2_objs ${name}${objext}" done -for name in $LIB2FUNCS_EH; do - for ml in $MULTILIBS; do - dir=`echo ${ml} | sed -e 's/;.*$//' -e 's/=/$(EQ)/g'` - flags=`echo ${ml} | sed -e 's/^[^;]*;//' -e 's/@/ -/g'`; - out="libgcc/${dir}/${name}${objext}" - - echo $out: $libgcc2_c_dep - echo " $gcc_compile" '$(MAYBE_USE_COLLECT2)' -fexceptions \ - $flags -DL$name -c '$(srcdir)/libgcc2.c' -o $out - done - libgcc2_objs="$libgcc2_objs ${name}${objext}" -done - if [ "$FPBIT" ]; then for name in $FPBIT_FUNCS; do for ml in $MULTILIBS; do @@ -241,6 +229,24 @@ for file in $LIB2ADD; do libgcc2_objs="$libgcc2_objs ${oname}${objext}" done +for file in $LIB2ADDEH; do + name=`echo $file | sed -e 's/[.][cSo]$//' -e 's/[.]asm$//' -e 's/[.]txt$//'` + oname=`echo $name | sed -e 's,.*/,,'` + + for ml in $MULTILIBS; do + dir=`echo ${ml} | sed -e 's/;.*$//' -e 's/=/$(EQ)/g'` + flags=`echo ${ml} | sed -e 's/^[^;]*;//' -e 's/@/ -/g'`; + out="libgcc/${dir}/${oname}${objext}" + if [ ${name}.asm = ${file} ]; then + flags="$flags -xassembler-with-cpp" + fi + + echo $out: stmp-dirs $file + echo " $gcc_compile" $flags -fexceptions -c $file -o $out + done + libgcc2_objs="$libgcc2_objs ${oname}${objext}" +done + # SHLIB_MKMAP # SHLIB_MAPFILES for ml in $MULTILIBS; do diff --git a/gcc/optabs.c b/gcc/optabs.c index 0322510..0be8d6a 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -32,6 +32,7 @@ Boston, MA 02111-1307, USA. */ #include "tm_p.h" #include "flags.h" #include "function.h" +#include "except.h" #include "expr.h" #include "recog.h" #include "reload.h" @@ -4758,12 +4759,9 @@ init_optabs () memset_libfunc = init_one_libfunc ("memset"); bzero_libfunc = init_one_libfunc ("bzero"); - throw_libfunc = init_one_libfunc ("__throw"); - rethrow_libfunc = init_one_libfunc ("__rethrow"); - sjthrow_libfunc = init_one_libfunc ("__sjthrow"); - sjpopnthrow_libfunc = init_one_libfunc ("__sjpopnthrow"); - terminate_libfunc = init_one_libfunc ("__terminate"); - eh_rtime_match_libfunc = init_one_libfunc ("__eh_rtime_match"); + unwind_resume_libfunc = init_one_libfunc (USING_SJLJ_EXCEPTIONS + ? "_Unwind_SjLj_Resume" + : "_Unwind_Resume"); #ifndef DONT_USE_BUILTIN_SETJMP setjmp_libfunc = init_one_libfunc ("__builtin_setjmp"); longjmp_libfunc = init_one_libfunc ("__builtin_longjmp"); @@ -4771,6 +4769,9 @@ init_optabs () setjmp_libfunc = init_one_libfunc ("setjmp"); longjmp_libfunc = init_one_libfunc ("longjmp"); #endif + unwind_sjlj_register_libfunc = init_one_libfunc ("_Unwind_SjLj_Register"); + unwind_sjlj_unregister_libfunc + = init_one_libfunc ("_Unwind_SjLj_Unregister"); eqhf2_libfunc = init_one_libfunc ("__eqhf2"); nehf2_libfunc = init_one_libfunc ("__nehf2"); diff --git a/gcc/rtl.def b/gcc/rtl.def index 6f0aec8..906255d 100644 --- a/gcc/rtl.def +++ b/gcc/rtl.def @@ -547,6 +547,11 @@ DEF_RTL_EXPR(RETURN, "return", "", 'x') For an unconditional trap, make the condition (const_int 1). */ DEF_RTL_EXPR(TRAP_IF, "trap_if", "ee", 'x') +/* Placeholder for _Unwind_Resume before we know if a function call + or a branch is needed. Operand 1 is the exception region from + which control is flowing. */ +DEF_RTL_EXPR(RESX, "resx", "i", 'x') + /* ---------------------------------------------------------------------- Primitive values for use in expressions. ---------------------------------------------------------------------- */ @@ -924,10 +924,6 @@ extern const char * const note_insn_name[NOTE_INSN_MAX - NOTE_INSN_BIAS]; /* Flag in a SYMBOL_REF for machine-specific purposes. */ #define SYMBOL_REF_FLAG(RTX) ((RTX)->volatil) -/* 1 in a SYMBOL_REF if it represents a symbol which might have to change - if its inlined or unrolled. */ -#define SYMBOL_REF_NEED_ADJUST(RTX) ((RTX)->in_struct) - /* 1 means a SYMBOL_REF has been the library function in emit_library_call. */ #define SYMBOL_REF_USED(RTX) ((RTX)->used) @@ -4072,12 +4072,11 @@ expand_decl_cleanup (decl, cleanup) start_sequence (); } - /* If this was optimized so that there is no exception region for the - cleanup, then mark the TREE_LIST node, so that we can later tell - if we need to call expand_eh_region_end. */ - if (! using_eh_for_cleanups_p - || expand_eh_region_start_tree (decl, cleanup)) + if (! using_eh_for_cleanups_p) TREE_ADDRESSABLE (t) = 1; + else + expand_eh_region_start (); + /* If that started a new EH region, we're in a new block. */ thisblock = block_stack; @@ -4106,82 +4105,6 @@ expand_decl_cleanup (decl, cleanup) } return 1; } - -/* Arrange for the top element of the dynamic cleanup chain to be - popped if we exit the current binding contour. DECL is the - associated declaration, if any, otherwise NULL_TREE. If the - current contour is left via an exception, then __sjthrow will pop - the top element off the dynamic cleanup chain. The code that - avoids doing the action we push into the cleanup chain in the - exceptional case is contained in expand_cleanups. - - This routine is only used by expand_eh_region_start, and that is - the only way in which an exception region should be started. This - routine is only used when using the setjmp/longjmp codegen method - for exception handling. */ - -int -expand_dcc_cleanup (decl) - tree decl; -{ - struct nesting *thisblock; - tree cleanup; - - /* Error if we are not in any block. */ - if (cfun == 0 || block_stack == 0) - return 0; - thisblock = block_stack; - - /* Record the cleanup for the dynamic handler chain. */ - - cleanup = make_node (POPDCC_EXPR); - - /* Add the cleanup in a manner similar to expand_decl_cleanup. */ - thisblock->data.block.cleanups - = tree_cons (decl, cleanup, thisblock->data.block.cleanups); - - /* If this block has a cleanup, it belongs in stack_block_stack. */ - stack_block_stack = thisblock; - return 1; -} - -/* Arrange for the top element of the dynamic handler chain to be - popped if we exit the current binding contour. DECL is the - associated declaration, if any, otherwise NULL_TREE. If the current - contour is left via an exception, then __sjthrow will pop the top - element off the dynamic handler chain. The code that avoids doing - the action we push into the handler chain in the exceptional case - is contained in expand_cleanups. - - This routine is only used by expand_eh_region_start, and that is - the only way in which an exception region should be started. This - routine is only used when using the setjmp/longjmp codegen method - for exception handling. */ - -int -expand_dhc_cleanup (decl) - tree decl; -{ - struct nesting *thisblock; - tree cleanup; - - /* Error if we are not in any block. */ - if (cfun == 0 || block_stack == 0) - return 0; - thisblock = block_stack; - - /* Record the cleanup for the dynamic handler chain. */ - - cleanup = make_node (POPDHC_EXPR); - - /* Add the cleanup in a manner similar to expand_decl_cleanup. */ - thisblock->data.block.cleanups - = tree_cons (decl, cleanup, thisblock->data.block.cleanups); - - /* If this block has a cleanup, it belongs in stack_block_stack. */ - stack_block_stack = thisblock; - return 1; -} /* DECL is an anonymous union. CLEANUP is a cleanup for DECL. DECL_ELTS is the list of elements that belong to DECL's type. @@ -4286,20 +4209,8 @@ expand_cleanups (list, dont_do, in_fixup, reachable) expand_cleanups (TREE_VALUE (tail), dont_do, in_fixup, reachable); else { - if (! in_fixup) - { - tree cleanup = TREE_VALUE (tail); - - /* See expand_d{h,c}c_cleanup for why we avoid this. */ - if (TREE_CODE (cleanup) != POPDHC_EXPR - && TREE_CODE (cleanup) != POPDCC_EXPR - /* See expand_eh_region_start_tree for this case. */ - && ! TREE_ADDRESSABLE (tail)) - { - cleanup = protect_with_terminate (cleanup); - expand_eh_region_end (cleanup); - } - } + if (! in_fixup && using_eh_for_cleanups_p) + expand_eh_region_end_cleanup (TREE_VALUE (tail)); if (reachable) { @@ -4312,19 +4223,18 @@ expand_cleanups (list, dont_do, in_fixup, reachable) times, the control paths are non-overlapping so the cleanups will not be executed twice. */ - /* We may need to protect fixups with rethrow regions. */ - int protect = (in_fixup && ! TREE_ADDRESSABLE (tail)); + /* We may need to protect from outer cleanups. */ + if (in_fixup && using_eh_for_cleanups_p) + { + expand_eh_region_start (); + + expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0); - if (protect) - expand_fixup_region_start (); + expand_eh_region_end_fixup (TREE_VALUE (tail)); + } + else + expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0); - /* The cleanup might contain try-blocks, so we have to - preserve our current queue. */ - push_ehqueue (); - expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0); - pop_ehqueue (); - if (protect) - expand_fixup_region_end (TREE_VALUE (tail)); free_temp_slots (); } } diff --git a/gcc/tm.texi b/gcc/tm.texi index 9f20053..e3dd530 100644 --- a/gcc/tm.texi +++ b/gcc/tm.texi @@ -2505,6 +2505,54 @@ You only need to define this macro if the default is incorrect, and you want to support call frame debugging information like that provided by DWARF 2. +@findex EH_RETURN_DATA_REGNO +@item EH_RETURN_DATA_REGNO (@var{N}) +A C expression whose value is the @var{N}th register number used for +data by exception handlers, or @code{INVALID_REGNUM} if fewer than +@var{N} registers are usable. + +The exception handling library routines communicate with the exception +handlers via a set of agreed upon registers. Ideally these registers +should be call-clobbered; it is possible to use call-saved registers, +but may negatively impact code size. The target must support at least +2 data registers, but should define 4 if there are enough free registers. + +You must define this macro if you want to support call frame exception +handling like that provided by DWARF 2. + +@findex EH_RETURN_STACKADJ_RTX +@item EH_RETURN_STACKADJ_RTX +A C expression whose value is RTL representing a location in which +to store a stack adjustment to be applied before function return. +This is used to unwind the stack to an exception handler's call frame. +It will be assigned zero on code paths that return normally. + +Typically this is a call-clobbered hard register that is otherwise +untouched by the epilogue, but could also be a stack slot. + +You must define this macro if you want to support call frame exception +handling like that provided by DWARF 2. + +@findex EH_RETURN_HANDLER_RTX +@item EH_RETURN_HANDLER_RTX +A C expression whose value is RTL representing a location in which +to store the address of an exception handler to which we should +return. It will not be assigned on code paths that return normally. + +Typically this is the location in the call frame at which the normal +return address is stored. For targets that return by popping an +address off the stack, this might be a memory address just below +the @emph{target} call frame rather than inside the current call +frame. @code{EH_RETURN_STACKADJ_RTX} will have already been assigned, +so it may be used to calculate the location of the target call frame. + +Some targets have more complex requirements than storing to an +address calculable during initial code generation. In that case +the @code{eh_return} instruction pattern should be used instead. + +If you want to support call frame exception handling, you must +define either this macro or the @code{eh_return} instruction pattern. + @findex SMALL_STACK @item SMALL_STACK Define this macro if the stack size for the target is very small. This diff --git a/gcc/toplev.c b/gcc/toplev.c index 6016ca6..b029c70 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -252,6 +252,7 @@ enum dump_file_index { DFI_rtl, DFI_sibling, + DFI_eh, DFI_jump, DFI_cse, DFI_addressof, @@ -289,7 +290,7 @@ enum dump_file_index Remaining -d letters: - " h o q u " + " o q u " " H K OPQ TUVW YZ" */ @@ -297,6 +298,7 @@ struct dump_file_info dump_file[DFI_MAX] = { { "rtl", 'r', 0, 0, 0 }, { "sibling", 'i', 0, 0, 0 }, + { "eh", 'h', 0, 0, 0 }, { "jump", 'j', 0, 0, 0 }, { "cse", 's', 0, 0, 0 }, { "addressof", 'F', 0, 0, 0 }, @@ -2162,9 +2164,9 @@ compile_file (name) init_regs (); init_alias_once (); init_decl_processing (); + init_eh (); init_optabs (); init_stmt (); - init_eh (); init_loop (); init_reload (); init_function_once (); @@ -2402,14 +2404,6 @@ compile_file (name) loop above. */ output_func_start_profiler (); - /* Now that all possible functions have been output, we can dump - the exception table. */ - -#ifndef IA64_UNWIND_INFO - output_exception_table (); -#endif - free_exception_table (); - check_global_declarations (vec, len); /* Clean up. */ @@ -2805,6 +2799,11 @@ rest_of_compilation (decl) close_dump_file (DFI_rtl, print_rtl, insns); } + /* Convert from NOTE_INSN_EH_REGION style notes, and do other + sorts of eh initialization. Delay this until after the + initial rtl dump so that we can see the original nesting. */ + convert_from_eh_region_ranges (); + /* If function is inline, and we don't yet know whether to compile it by itself, defer decision till end of compilation. finish_compilation will call rest_of_compilation again @@ -2884,9 +2883,6 @@ rest_of_compilation (decl) if ((rtl_dump_and_exit || flag_syntax_only) && !warn_return_type) goto exit_rest_of_compilation; - /* Emit code to get eh context, if needed. */ - emit_eh_context (); - /* We may have potential sibling or tail recursion sites. Select one (of possibly multiple) methods of performing the call. */ if (flag_optimize_sibling_calls) @@ -2900,6 +2896,19 @@ rest_of_compilation (decl) timevar_pop (TV_JUMP); } + /* Complete generation of exception handling code. */ + find_exception_handler_labels (); + if (doing_eh (0)) + { + timevar_push (TV_JUMP); + open_dump_file (DFI_eh, decl); + + finish_eh_generation (); + + close_dump_file (DFI_eh, print_rtl, get_insns ()); + timevar_pop (TV_JUMP); + } + #ifdef FINALIZE_PIC /* If we are doing position-independent code generation, now is the time to output special prologues and epilogues. @@ -2923,9 +2932,6 @@ rest_of_compilation (decl) /* Instantiate all virtual registers. */ instantiate_virtual_regs (current_function_decl, insns); - /* Find all the EH handlers. */ - find_exception_handler_labels (); - open_dump_file (DFI_jump, decl); /* Always do one jump optimization pass to ensure that JUMP_LABEL fields @@ -3679,6 +3685,12 @@ rest_of_compilation (decl) } #endif +#ifndef STACK_REGS + /* ??? Do this before shorten branches so that we aren't creating + insns too late and fail sanity checks in final. */ + convert_to_eh_region_ranges (); +#endif + /* Shorten branches. Note this must run before reg-stack because of death note (ab)use @@ -3697,6 +3709,8 @@ rest_of_compilation (decl) timevar_pop (TV_REG_STACK); ggc_collect (); + + convert_to_eh_region_ranges (); #endif current_function_nothrow = nothrow_function_p (); @@ -3728,6 +3742,9 @@ rest_of_compilation (decl) final (insns, asm_out_file, optimize, 0); final_end_function (insns, asm_out_file, optimize); assemble_end_function (decl, fnname); + + output_function_exception_table (); + if (! quiet_flag) fflush (asm_out_file); diff --git a/gcc/tree.def b/gcc/tree.def index 399e057..760600b 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -770,17 +770,6 @@ DEFTREECODE (TRY_FINALLY_EXPR, "try_finally", 'e', 2) Operand 1 is the rtx for a variable in which to store the address of where the subroutine should return to. */ DEFTREECODE (GOTO_SUBROUTINE_EXPR, "goto_subroutine", 'e', 2) - -/* Pop the top element off the dynamic handler chain. Used in - conjunction with setjmp/longjmp based exception handling, see - except.c for more details. This is meant to be used only by the - exception handling backend, expand_dhc_cleanup specifically. */ -DEFTREECODE (POPDHC_EXPR, "popdhc_expr", 's', 0) - -/* Pop the top element off the dynamic cleanup chain. Used in - conjunction with the exception handling. This is meant to be used - only by the exception handling backend. */ -DEFTREECODE (POPDCC_EXPR, "popdcc_expr", 's', 0) /* These types of expressions have no useful value, and always have side effects. */ @@ -834,6 +823,10 @@ DEFTREECODE (EXPR_WITH_FILE_LOCATION, "expr_with_file_location", 'e', 3) Operand 1 contains the case values. The way they're organized is front-end implementation defined. */ DEFTREECODE (SWITCH_EXPR, "switch_expr", 'e', 2) + +/* The exception object from the runtime. */ +DEFTREECODE (EXC_PTR_EXPR, "exc_ptr_expr", 'e', 0) + /* Local variables: mode:c diff --git a/gcc/frame.c b/gcc/unwind-dw2-fde.c index 399df57..6f450c5 100644 --- a/gcc/frame.c +++ b/gcc/unwind-dw2-fde.c @@ -1,6 +1,5 @@ /* Subroutines needed for unwinding stack frames for exception handling. */ -/* Compile this one with gcc. */ -/* Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. +/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. Contributed by Jason Merrill <jason@cygnus.com>. This file is part of GNU CC. @@ -29,6 +28,129 @@ along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "tconfig.h" +#include "tsystem.h" +#include "unwind-dw2-fde.h" +#include "gthr.h" + +static struct object *objects; + +#ifdef __GTHREAD_MUTEX_INIT +static __gthread_mutex_t object_mutex = __GTHREAD_MUTEX_INIT; +#else +static __gthread_mutex_t object_mutex; +#endif + +#ifdef __GTHREAD_MUTEX_INIT_FUNCTION +static void +init_object_mutex (void) +{ + __GTHREAD_MUTEX_INIT_FUNCTION (&object_mutex); +} + +static void +init_object_mutex_once (void) +{ + static __gthread_once_t once = __GTHREAD_ONCE_INIT; + __gthread_once (&once, init_object_mutex); +} +#else +#define init_object_mutex_once() +#endif + +/* Called from crtbegin.o to register the unwind info for an object. */ + +void +__register_frame_info (void *begin, struct object *ob) +{ + ob->pc_begin = ob->pc_end = 0; + ob->fde_begin = begin; + ob->fde_array = 0; + ob->count = 0; + + init_object_mutex_once (); + __gthread_mutex_lock (&object_mutex); + + ob->next = objects; + objects = ob; + + __gthread_mutex_unlock (&object_mutex); +} + +void +__register_frame (void *begin) +{ + struct object *ob = (struct object *) malloc (sizeof (struct object)); + __register_frame_info (begin, ob); +} + +/* Similar, but BEGIN is actually a pointer to a table of unwind entries + for different translation units. Called from the file generated by + collect2. */ + +void +__register_frame_info_table (void *begin, struct object *ob) +{ + ob->pc_begin = ob->pc_end = 0; + ob->fde_begin = begin; + ob->fde_array = begin; + ob->count = 0; + + init_object_mutex_once (); + __gthread_mutex_lock (&object_mutex); + + ob->next = objects; + objects = ob; + + __gthread_mutex_unlock (&object_mutex); +} + +void +__register_frame_table (void *begin) +{ + struct object *ob = (struct object *) malloc (sizeof (struct object)); + __register_frame_info_table (begin, ob); +} + +/* Called from crtbegin.o to deregister the unwind info for an object. */ + +void * +__deregister_frame_info (void *begin) +{ + struct object **p; + + init_object_mutex_once (); + __gthread_mutex_lock (&object_mutex); + + p = &objects; + while (*p) + { + if ((*p)->fde_begin == begin) + { + struct object *ob = *p; + *p = (*p)->next; + + /* If we've run init_frame for this object, free the FDE array. */ + if (ob->fde_array && ob->fde_array != begin) + free (ob->fde_array); + + __gthread_mutex_unlock (&object_mutex); + return (void *) ob; + } + p = &((*p)->next); + } + + __gthread_mutex_unlock (&object_mutex); + abort (); +} + +void +__deregister_frame (void *begin) +{ + free (__deregister_frame_info (begin)); +} + + /* Sorting an array of FDEs by address. (Ideally we would have the linker sort the FDEs so we don't have to do it at run time. But the linkers are not yet prepared for this.) */ @@ -56,6 +178,12 @@ typedef struct fde_accumulator fde_vector erratic; } fde_accumulator; +static inline saddr +fde_compare (fde *x, fde *y) +{ + return (saddr)x->pc_begin - (saddr)y->pc_begin; +} + static inline int start_fde_sort (fde_accumulator *accu, size_t count) { @@ -241,97 +369,174 @@ end_fde_sort (fde_accumulator *accu, size_t count) return accu->linear.array; } -/* Called from crtbegin.o to register the unwind info for an object. */ + +static size_t +count_fdes (fde *this_fde) +{ + size_t count; -void -__register_frame_info (void *begin, struct object *ob) + for (count = 0; this_fde->length != 0; this_fde = next_fde (this_fde)) + /* Skip CIEs and omitted link-once FDE entries. */ + if (this_fde->CIE_delta != 0 && this_fde->pc_begin != 0) + ++count; + + return count; +} + +static void +add_fdes (fde *this_fde, fde_accumulator *accu, void **beg_ptr, void **end_ptr) { - ob->fde_begin = begin; + void *pc_begin = *beg_ptr; + void *pc_end = *end_ptr; - ob->pc_begin = ob->pc_end = 0; - ob->fde_array = 0; - ob->count = 0; + for (; this_fde->length != 0; this_fde = next_fde (this_fde)) + { + /* Skip CIEs and linked once FDE entries. */ + if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) + continue; - init_object_mutex_once (); - __gthread_mutex_lock (&object_mutex); + fde_insert (accu, this_fde); - ob->next = objects; - objects = ob; + if (this_fde->pc_begin < pc_begin) + pc_begin = this_fde->pc_begin; + if (this_fde->pc_begin + this_fde->pc_range > pc_end) + pc_end = this_fde->pc_begin + this_fde->pc_range; + } - __gthread_mutex_unlock (&object_mutex); + *beg_ptr = pc_begin; + *end_ptr = pc_end; } -void -__register_frame (void *begin) +static fde * +search_fdes (fde *this_fde, void *pc) { - struct object *ob = (struct object *) malloc (sizeof (struct object)); - __register_frame_info (begin, ob); + for (; this_fde->length != 0; this_fde = next_fde (this_fde)) + { + /* Skip CIEs and linked once FDE entries. */ + if (this_fde->CIE_delta == 0 || this_fde->pc_begin == 0) + continue; + + if ((uaddr)((char *)pc - (char *)this_fde->pc_begin) < this_fde->pc_range) + return this_fde; + } + return NULL; } -/* Similar, but BEGIN is actually a pointer to a table of unwind entries - for different translation units. Called from the file generated by - collect2. */ +/* Set up a sorted array of pointers to FDEs for a loaded object. We + count up the entries before allocating the array because it's likely to + be faster. We can be called multiple times, should we have failed to + allocate a sorted fde array on a previous occasion. */ -void -__register_frame_info_table (void *begin, struct object *ob) +static void +frame_init (struct object* ob) { - ob->fde_begin = begin; - ob->fde_array = begin; - - ob->pc_begin = ob->pc_end = 0; - ob->count = 0; + size_t count; + fde_accumulator accu; + void *pc_begin, *pc_end; + fde **array; - init_object_mutex_once (); - __gthread_mutex_lock (&object_mutex); + if (ob->pc_begin) + count = ob->count; + else if (ob->fde_array) + { + fde **p = ob->fde_array; + for (count = 0; *p; ++p) + count += count_fdes (*p); + } + else + count = count_fdes (ob->fde_begin); + ob->count = count; - ob->next = objects; - objects = ob; + if (!start_fde_sort (&accu, count) && ob->pc_begin) + return; - __gthread_mutex_unlock (&object_mutex); -} + pc_begin = (void*)(uaddr)-1; + pc_end = 0; -void -__register_frame_table (void *begin) -{ - struct object *ob = (struct object *) malloc (sizeof (struct object)); - __register_frame_info_table (begin, ob); + if (ob->fde_array) + { + fde **p = ob->fde_array; + for (; *p; ++p) + add_fdes (*p, &accu, &pc_begin, &pc_end); + } + else + add_fdes (ob->fde_begin, &accu, &pc_begin, &pc_end); + array = end_fde_sort (&accu, count); + if (array) + ob->fde_array = array; + ob->pc_begin = pc_begin; + ob->pc_end = pc_end; } -/* Called from crtbegin.o to deregister the unwind info for an object. */ - -void * -__deregister_frame_info (void *begin) +fde * +_Unwind_Find_FDE (void *pc, struct dwarf_eh_bases *bases) { - struct object **p; + struct object *ob; + size_t lo, hi; init_object_mutex_once (); __gthread_mutex_lock (&object_mutex); - p = &objects; - while (*p) + /* Linear search through the objects, to find the one containing the pc. */ + for (ob = objects; ob; ob = ob->next) { - if ((*p)->fde_begin == begin) - { - struct object *ob = *p; - *p = (*p)->next; + if (ob->pc_begin == 0) + frame_init (ob); + if (pc >= ob->pc_begin && pc < ob->pc_end) + break; + } - /* If we've run init_frame for this object, free the FDE array. */ - if (ob->fde_array && ob->fde_array != begin) - free (ob->fde_array); + if (ob == 0) + { + __gthread_mutex_unlock (&object_mutex); + return 0; + } - __gthread_mutex_unlock (&object_mutex); - return (void *) ob; - } - p = &((*p)->next); + if (!ob->fde_array || (void *)ob->fde_array == (void *)ob->fde_begin) + frame_init (ob); + + if (ob->fde_array && (void *)ob->fde_array != (void *)ob->fde_begin) + { + __gthread_mutex_unlock (&object_mutex); + + /* Standard binary search algorithm. */ + for (lo = 0, hi = ob->count; lo < hi; ) + { + size_t i = (lo + hi) / 2; + fde *f = ob->fde_array[i]; + + if (pc < f->pc_begin) + hi = i; + else if (pc >= f->pc_begin + f->pc_range) + lo = i + 1; + else + return f; + } } + else + { + /* Long slow labourious linear search, cos we've no memory. */ + fde *f; + + if (ob->fde_array) + { + fde **p = ob->fde_array; + + do + { + f = search_fdes (*p, pc); + if (f) + break; + p++; + } + while (*p); + } + else + f = search_fdes (ob->fde_begin, pc); - __gthread_mutex_unlock (&object_mutex); - abort (); -} + __gthread_mutex_unlock (&object_mutex); + return f; + } -void -__deregister_frame (void *begin) -{ - free (__deregister_frame_info (begin)); + return 0; } - diff --git a/gcc/unwind-dw2-fde.h b/gcc/unwind-dw2-fde.h new file mode 100644 index 0000000..83b519a --- /dev/null +++ b/gcc/unwind-dw2-fde.h @@ -0,0 +1,120 @@ +/* Subroutines needed for unwinding stack frames for exception handling. */ +/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + Contributed by Jason Merrill <jason@cygnus.com>. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* Describes data used to hold onto one shared object or object file. */ +struct object +{ + void *pc_begin; + void *pc_end; + struct dwarf_fde *fde_begin; + struct dwarf_fde **fde_array; + size_t count; + struct object *next; +}; + +struct dwarf_eh_bases +{ + void *tbase; + void *dbase; + void *func; +}; + + +extern void __register_frame_info (void *, struct object *); +extern void __register_frame (void *); +extern void __register_frame_info_table (void *, struct object *); +extern void __register_frame_table (void *); +extern void *__deregister_frame_info (void *); +extern void __deregister_frame (void *); + + +typedef int sword __attribute__ ((mode (SI))); +typedef unsigned int uword __attribute__ ((mode (SI))); +typedef unsigned int uaddr __attribute__ ((mode (pointer))); +typedef int saddr __attribute__ ((mode (pointer))); +typedef unsigned char ubyte; + +/* Terminology: + CIE - Common Information Element + FDE - Frame Descriptor Element + + There is one per function, and it describes where the function code + is located, and what the register lifetimes and stack layout are + within the function. + + The data structures are defined in the DWARF specfication, although + not in a very readable way (see LITERATURE). + + Every time an exception is thrown, the code needs to locate the FDE + for the current function, and starts to look for exception regions + from that FDE. This works in a two-level search: + a) in a linear search, find the shared image (i.e. DLL) containing + the PC + b) using the FDE table for that shared object, locate the FDE using + binary search (which requires the sorting). */ + +/* The first few fields of a CIE. The CIE_id field is 0 for a CIE, + to distinguish it from a valid FDE. FDEs are aligned to an addressing + unit boundary, but the fields within are unaligned. */ +struct dwarf_cie +{ + uword length; + sword CIE_id; + ubyte version; + unsigned char augmentation[]; +} __attribute__ ((packed, aligned (__alignof__ (void *)))); + +/* The first few fields of an FDE. */ +struct dwarf_fde +{ + uword length; + sword CIE_delta; + void * pc_begin; + uaddr pc_range; +} __attribute__ ((packed, aligned (__alignof__ (void *)))); + +typedef struct dwarf_fde fde; + +/* Locate the CIE for a given FDE. */ + +static inline struct dwarf_cie * +get_cie (struct dwarf_fde *f) +{ + return (void *)&f->CIE_delta - f->CIE_delta; +} + +static inline fde * +next_fde (fde *f) +{ + return (fde *)((char *)f + f->length + sizeof (f->length)); +} + +extern fde * _Unwind_Find_FDE (void *, struct dwarf_eh_bases *); diff --git a/gcc/unwind-dw2.c b/gcc/unwind-dw2.c new file mode 100644 index 0000000..e2f20c9 --- /dev/null +++ b/gcc/unwind-dw2.c @@ -0,0 +1,1217 @@ +/* DWARF2 exception handling and frame unwind runtime interface routines. + Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + This file is part of GNU CC. + + GNU CC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU CC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "dwarf2.h" +#include "unwind.h" +#include "unwind-dw2-fde.h" +#include "gthr.h" + + +#if !USING_SJLJ_EXCEPTIONS + +#ifndef STACK_GROWS_DOWNWARD +#define STACK_GROWS_DOWNWARD 0 +#else +#undef STACK_GROWS_DOWNWARD +#define STACK_GROWS_DOWNWARD 1 +#endif + +/* A target can override (perhaps for backward compatibility) how + many dwarf2 columns are unwound. */ +#ifndef DWARF_FRAME_REGISTERS +#define DWARF_FRAME_REGISTERS FIRST_PSEUDO_REGISTER +#endif + +/* This is the register and unwind state for a particular frame. */ +struct _Unwind_Context +{ + void *reg[DWARF_FRAME_REGISTERS+1]; + void *cfa; + void *ra; + void *lsda; + struct dwarf_eh_bases bases; + _Unwind_Word args_size; +}; + +/* Byte size of every register managed by these routines. */ +static unsigned char dwarf_reg_size_table[DWARF_FRAME_REGISTERS]; + + +/* The result of interpreting the frame unwind info for a frame. + This is all symbolic at this point, as none of the values can + be resolved until the target pc is located. */ +typedef struct +{ + /* Each register save state can be described in terms of a CFA slot, + another register, or a location expression. */ + struct frame_state_reg_info + { + struct { + union { + unsigned int reg; + _Unwind_Sword offset; + unsigned char *exp; + } loc; + enum { + REG_UNSAVED, + REG_SAVED_OFFSET, + REG_SAVED_REG, + REG_SAVED_EXP, + } how; + } reg[DWARF_FRAME_REGISTERS+1]; + + /* Used to implement DW_CFA_remember_state. */ + struct frame_state_reg_info *prev; + } regs; + + /* The CFA can be described in terms of a reg+offset or a + location expression. */ + _Unwind_Sword cfa_offset; + _Unwind_Word cfa_reg; + unsigned char *cfa_exp; + enum { + CFA_UNSET, + CFA_REG_OFFSET, + CFA_EXP, + } cfa_how; + + /* The PC described by the current frame state. */ + void *pc; + + /* The information we care about from the CIE/FDE. */ + _Unwind_Personality_Fn personality; + signed int data_align; + unsigned int code_align; + unsigned char retaddr_column; + unsigned char addr_encoding; + unsigned char saw_z; + unsigned char saw_lsda; +} _Unwind_FrameState; + +/* Decode the unsigned LEB128 constant at BUF into the variable pointed to + by R, and return the new value of BUF. */ + +static unsigned char * +read_uleb128 (unsigned char *buf, _Unwind_Word *r) +{ + unsigned shift = 0; + _Unwind_Word result = 0; + + while (1) + { + unsigned char byte = *buf++; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + *r = result; + return buf; +} + +/* Decode the signed LEB128 constant at BUF into the variable pointed to + by R, and return the new value of BUF. */ + +static unsigned char * +read_sleb128 (unsigned char *buf, _Unwind_Sword *r) +{ + unsigned shift = 0; + _Unwind_Sword result = 0; + unsigned char byte; + + while (1) + { + byte = *buf++; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) + break; + } + if (shift < (sizeof (*r) * 8) && (byte & 0x40) != 0) + result |= - (1 << shift); + + *r = result; + return buf; +} + +/* Read unaligned data from the instruction buffer. */ + +union unaligned +{ + void *p; + unsigned u2 __attribute__ ((mode (HI))); + unsigned u4 __attribute__ ((mode (SI))); + unsigned u8 __attribute__ ((mode (DI))); + signed s2 __attribute__ ((mode (HI))); + signed s4 __attribute__ ((mode (SI))); + signed s8 __attribute__ ((mode (DI))); +} __attribute__ ((packed)); + +static inline void * +read_pointer (void *p) { union unaligned *up = p; return up->p; } + +static inline int +read_1u (void *p) { return *(unsigned char *)p; } + +static inline int +read_1s (void *p) { return *(signed char *)p; } + +static inline int +read_2u (void *p) { union unaligned *up = p; return up->u2; } + +static inline int +read_2s (void *p) { union unaligned *up = p; return up->s2; } + +static inline unsigned int +read_4u (void *p) { union unaligned *up = p; return up->u4; } + +static inline int +read_4s (void *p) { union unaligned *up = p; return up->s4; } + +static inline unsigned long +read_8u (void *p) { union unaligned *up = p; return up->u8; } + +static inline unsigned long +read_8s (void *p) { union unaligned *up = p; return up->s8; } + +static unsigned char * +read_encoded_pointer (unsigned char *p, unsigned char encoding, + struct dwarf_eh_bases *bases, void **pptr) +{ + signed long val; + unsigned char *ret; + + switch (encoding & 0x0f) + { + case DW_EH_PE_absptr: + val = (_Unwind_Ptr) read_pointer (p); + ret = p + sizeof (void *); + break; + + case DW_EH_PE_uleb128: + ret = read_uleb128 (p, &val); + break; + case DW_EH_PE_sleb128: + ret = read_sleb128 (p, &val); + break; + + case DW_EH_PE_udata2: + val = read_2u (p); + ret = p + 2; + break; + case DW_EH_PE_udata4: + val = read_4u (p); + ret = p + 4; + break; + case DW_EH_PE_udata8: + val = read_8u (p); + ret = p + 8; + break; + + case DW_EH_PE_sdata2: + val = read_2s (p); + ret = p + 2; + break; + case DW_EH_PE_sdata4: + val = read_4s (p); + ret = p + 4; + break; + case DW_EH_PE_sdata8: + val = read_8s (p); + ret = p + 8; + break; + + default: + abort (); + } + + if (val != 0) + switch (encoding & 0xf0) + { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + val += (_Unwind_Ptr) p; + break; + case DW_EH_PE_textrel: + val += (_Unwind_Ptr) bases->tbase; + break; + case DW_EH_PE_datarel: + val += (_Unwind_Ptr) bases->dbase; + break; + case DW_EH_PE_funcrel: + val += (_Unwind_Ptr) bases->func; + break; + default: + abort (); + } + + *pptr = (void *) (_Unwind_Ptr) val; + return ret; +} + +/* Get the value of register REG as saved in CONTEXT. */ + +inline _Unwind_Word +_Unwind_GetGR (struct _Unwind_Context *context, int index) +{ + /* This will segfault if the register hasn't been saved. */ + return * (_Unwind_Word *) context->reg[index]; +} + +/* Overwrite the saved value for register REG in CONTEXT with VAL. */ + +inline void +_Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) +{ + * (_Unwind_Word *) context->reg[index] = val; +} + +/* Retrieve the return address for CONTEXT. */ + +inline _Unwind_Ptr +_Unwind_GetIP (struct _Unwind_Context *context) +{ + return (_Unwind_Ptr) context->ra; +} + +/* Overwrite the return address for CONTEXT with VAL. */ + +inline void +_Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val) +{ + context->ra = (void *) val; +} + +void * +_Unwind_GetLanguageSpecificData (struct _Unwind_Context *context) +{ + return context->lsda; +} + +_Unwind_Ptr +_Unwind_GetRegionStart (struct _Unwind_Context *context) +{ + return (_Unwind_Ptr) context->bases.func; +} + + +/* Extract any interesting information from the CIE for the translation + unit F belongs to. Return a pointer to the byte after the augmentation, + or NULL if we encountered an undecipherable augmentation. */ + +static unsigned char * +extract_cie_info (struct dwarf_cie *cie, struct _Unwind_Context *context, + _Unwind_FrameState *fs) +{ + unsigned char *aug = cie->augmentation; + unsigned char *p = aug + strlen (aug) + 1; + unsigned char *ret = NULL; + _Unwind_Word code_align; + _Unwind_Sword data_align; + + /* Immediately following the augmentation are the code and + data alignment and return address column. */ + p = read_uleb128 (p, &code_align); + p = read_sleb128 (p, &data_align); + fs->code_align = code_align; + fs->data_align = data_align; + fs->retaddr_column = *p++; + + /* If the augmentation starts with 'z', then a uleb128 immediately + follows containing the length of the augmentation field following + the size. */ + if (*aug == 'z') + { + _Unwind_Word i; + p = read_uleb128 (p, &i); + ret = p + i; + + fs->saw_z = 1; + ++aug; + } + + /* Iterate over recognized augmentation subsequences. */ + while (*aug != '\0') + { + /* "eh" was used by g++ v2; recognize and skip. */ + if (aug[0] == 'e' && aug[1] == 'h') + { + p += sizeof (void *); + aug += 2; + } + + /* "R" indicates a byte indicating how addresses are encoded. */ + else if (aug[0] == 'R') + { + fs->addr_encoding = *p++; + aug += 1; + } + + /* "P" indicates a personality routine in the CIE augmentation + and an lsda pointer in the FDE augmentation. */ + else if (aug[0] == 'P') + { + p = read_encoded_pointer (p, fs->addr_encoding, &context->bases, + (void **) &fs->personality); + fs->saw_lsda = 1; + aug += 1; + } + + /* Otherwise we have an unknown augmentation string. + Bail unless we saw a 'z' prefix. */ + else + return ret; + } + + return ret ? ret : p; +} + + +/* Decode a DW_OP stack program. Return the top of stack. Push INITIAL + onto the stack to start. */ + +static _Unwind_Word +execute_stack_op (unsigned char *op_ptr, unsigned char *op_end, + struct _Unwind_Context *context, _Unwind_Word initial) +{ + _Unwind_Word stack[64]; /* ??? Assume this is enough. */ + int stack_elt; + + stack[0] = initial; + stack_elt = 1; + + while (op_ptr < op_end) + { + enum dwarf_location_atom op = *op_ptr++; + _Unwind_Word result, reg; + _Unwind_Sword offset; + + switch (op) + { + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + result = op - DW_OP_lit0; + break; + + case DW_OP_addr: + result = (_Unwind_Word) (_Unwind_Ptr) read_pointer (op_ptr); + op_ptr += sizeof (void *); + break; + + case DW_OP_const1u: + result = read_1u (op_ptr); + op_ptr += 1; + break; + case DW_OP_const1s: + result = read_1s (op_ptr); + op_ptr += 1; + break; + case DW_OP_const2u: + result = read_2u (op_ptr); + op_ptr += 2; + break; + case DW_OP_const2s: + result = read_2s (op_ptr); + op_ptr += 2; + break; + case DW_OP_const4u: + result = read_4u (op_ptr); + op_ptr += 4; + break; + case DW_OP_const4s: + result = read_4s (op_ptr); + op_ptr += 4; + break; + case DW_OP_const8u: + result = read_8u (op_ptr); + op_ptr += 8; + break; + case DW_OP_const8s: + result = read_8s (op_ptr); + op_ptr += 8; + break; + case DW_OP_constu: + op_ptr = read_uleb128 (op_ptr, &result); + break; + case DW_OP_consts: + op_ptr = read_sleb128 (op_ptr, &offset); + result = offset; + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + result = _Unwind_GetGR (context, op - DW_OP_reg0); + break; + case DW_OP_regx: + op_ptr = read_uleb128 (op_ptr, ®); + result = _Unwind_GetGR (context, reg); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + op_ptr = read_sleb128 (op_ptr, &offset); + result = _Unwind_GetGR (context, op - DW_OP_breg0) + offset; + break; + case DW_OP_bregx: + op_ptr = read_uleb128 (op_ptr, ®); + op_ptr = read_sleb128 (op_ptr, &offset); + result = _Unwind_GetGR (context, reg) + offset; + break; + + case DW_OP_dup: + if (stack_elt < 1) + abort (); + result = stack[stack_elt - 1]; + break; + + case DW_OP_drop: + if (--stack_elt < 0) + abort (); + goto no_push; + + case DW_OP_pick: + offset = *op_ptr++; + if (offset >= stack_elt - 1) + abort (); + result = stack[stack_elt - 1 - offset]; + break; + + case DW_OP_over: + if (stack_elt < 2) + abort (); + result = stack[stack_elt - 2]; + break; + + case DW_OP_rot: + { + _Unwind_Word t1, t2, t3; + + if (stack_elt < 3) + abort (); + t1 = stack[stack_elt - 1]; + t2 = stack[stack_elt - 2]; + t3 = stack[stack_elt - 3]; + stack[stack_elt - 1] = t2; + stack[stack_elt - 2] = t3; + stack[stack_elt - 3] = t1; + goto no_push; + } + + case DW_OP_deref: + case DW_OP_deref_size: + case DW_OP_abs: + case DW_OP_neg: + case DW_OP_not: + case DW_OP_plus_uconst: + /* Unary operations. */ + if (--stack_elt < 0) + abort (); + result = stack[stack_elt]; + + switch (op) + { + case DW_OP_deref: + { + void *ptr = (void *)(_Unwind_Ptr) result; + result = (_Unwind_Ptr) read_pointer (ptr); + } + break; + + case DW_OP_deref_size: + { + void *ptr = (void *)(_Unwind_Ptr) result; + switch (*op_ptr++) + { + case 1: + result = read_1u (ptr); + break; + case 2: + result = read_2u (ptr); + break; + case 4: + result = read_4u (ptr); + break; + case 8: + result = read_8u (ptr); + break; + default: + abort (); + } + } + break; + + case DW_OP_abs: + if ((_Unwind_Sword) result < 0) + result = -result; + break; + case DW_OP_neg: + result = -result; + break; + case DW_OP_not: + result = ~result; + break; + case DW_OP_plus_uconst: + op_ptr = read_uleb128 (op_ptr, ®); + result += reg; + break; + } + break; + + case DW_OP_and: + case DW_OP_div: + case DW_OP_minus: + case DW_OP_mod: + case DW_OP_mul: + case DW_OP_or: + case DW_OP_plus: + case DW_OP_le: + case DW_OP_ge: + case DW_OP_eq: + case DW_OP_lt: + case DW_OP_gt: + case DW_OP_ne: + { + /* Binary operations. */ + _Unwind_Word first, second; + if ((stack_elt -= 2) < 0) + abort (); + second = stack[stack_elt]; + first = stack[stack_elt + 1]; + + switch (op) + { + case DW_OP_and: + result = second & first; + break; + case DW_OP_div: + result = (_Unwind_Sword)second / (_Unwind_Sword)first; + break; + case DW_OP_minus: + result = second - first; + break; + case DW_OP_mod: + result = (_Unwind_Sword)second % (_Unwind_Sword)first; + break; + case DW_OP_mul: + result = second * first; + break; + case DW_OP_or: + result = second | first; + break; + case DW_OP_plus: + result = second + first; + break; + case DW_OP_shl: + result = second << first; + break; + case DW_OP_shr: + result = second >> first; + break; + case DW_OP_shra: + result = (_Unwind_Sword)second >> first; + break; + case DW_OP_xor: + result = second ^ first; + break; + case DW_OP_le: + result = (_Unwind_Sword)first <= (_Unwind_Sword)second; + break; + case DW_OP_ge: + result = (_Unwind_Sword)first >= (_Unwind_Sword)second; + break; + case DW_OP_eq: + result = (_Unwind_Sword)first == (_Unwind_Sword)second; + break; + case DW_OP_lt: + result = (_Unwind_Sword)first < (_Unwind_Sword)second; + break; + case DW_OP_gt: + result = (_Unwind_Sword)first > (_Unwind_Sword)second; + break; + case DW_OP_ne: + result = (_Unwind_Sword)first != (_Unwind_Sword)second; + break; + } + } + break; + + case DW_OP_skip: + offset = read_2s (op_ptr); + op_ptr += 2; + op_ptr += offset; + goto no_push; + + case DW_OP_bra: + if (--stack_elt < 0) + abort (); + offset = read_2s (op_ptr); + op_ptr += 2; + if (stack[stack_elt] != 0) + op_ptr += offset; + goto no_push; + + case DW_OP_nop: + goto no_push; + + default: + abort (); + } + + /* Most things push a result value. */ + if ((size_t) stack_elt >= sizeof(stack)/sizeof(*stack)) + abort (); + stack[++stack_elt] = result; + no_push:; + } + + /* We were executing this program to get a value. It should be + at top of stack. */ + if (--stack_elt < 0) + abort (); + return stack[stack_elt]; +} + + +/* Decode DWARF 2 call frame information. Takes pointers the + instruction sequence to decode, current register information and + CIE info, and the PC range to evaluate. */ + +static void +execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, + struct _Unwind_Context *context, _Unwind_FrameState *fs) +{ + struct frame_state_reg_info *unused_rs = NULL; + + /* Don't allow remember/restore between CIE and FDE programs. */ + fs->regs.prev = NULL; + + while (insn_ptr < insn_end && fs->pc < context->ra) + { + unsigned char insn = *insn_ptr++; + _Unwind_Word reg, uoffset; + _Unwind_Sword offset; + + if (insn & DW_CFA_advance_loc) + fs->pc += (insn & 0x3f) * fs->code_align; + else if (insn & DW_CFA_offset) + { + reg = insn & 0x3f; + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + offset = (_Unwind_Sword)uoffset * fs->data_align; + fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = offset; + } + else if (insn & DW_CFA_restore) + { + reg = insn & 0x3f; + fs->regs.reg[reg].how = REG_UNSAVED; + } + else switch (insn) + { + case DW_CFA_set_loc: + insn_ptr = read_encoded_pointer (insn_ptr, fs->addr_encoding, + &context->bases, &fs->pc); + break; + + case DW_CFA_advance_loc1: + fs->pc += read_1u (insn_ptr); + insn_ptr += 1; + break; + case DW_CFA_advance_loc2: + fs->pc += read_2u (insn_ptr); + insn_ptr += 2; + break; + case DW_CFA_advance_loc4: + fs->pc += read_4u (insn_ptr); + insn_ptr += 4; + break; + + case DW_CFA_offset_extended: + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + offset = (_Unwind_Sword)uoffset * fs->data_align; + fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = offset; + break; + + case DW_CFA_restore_extended: + insn_ptr = read_uleb128 (insn_ptr, ®); + fs->regs.reg[reg].how = REG_UNSAVED; + break; + + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_nop: + break; + + case DW_CFA_register: + { + _Unwind_Word reg2; + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, ®2); + fs->regs.reg[reg].how = REG_SAVED_REG; + fs->regs.reg[reg].loc.reg = reg2; + } + break; + + case DW_CFA_remember_state: + { + struct frame_state_reg_info *new_rs; + if (unused_rs) + { + new_rs = unused_rs; + unused_rs = unused_rs->prev; + } + else + new_rs = alloca (sizeof (struct frame_state_reg_info)); + + *new_rs = fs->regs; + fs->regs.prev = new_rs; + } + break; + + case DW_CFA_restore_state: + { + struct frame_state_reg_info *old_rs = fs->regs.prev; + fs->regs = *old_rs; + old_rs->prev = unused_rs; + unused_rs = old_rs; + } + break; + + case DW_CFA_def_cfa: + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg); + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + fs->cfa_offset = uoffset; + fs->cfa_how = CFA_REG_OFFSET; + break; + + case DW_CFA_def_cfa_register: + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg); + fs->cfa_how = CFA_REG_OFFSET; + break; + + case DW_CFA_def_cfa_offset: + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + fs->cfa_offset = uoffset; + /* cfa_how deliberately not set. */ + break; + + case DW_CFA_def_cfa_expression: + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + fs->cfa_exp = insn_ptr; + fs->cfa_how = CFA_EXP; + insn_ptr += uoffset; + break; + + case DW_CFA_expression: + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + fs->regs.reg[reg].how = REG_SAVED_EXP; + fs->regs.reg[reg].loc.exp = insn_ptr; + insn_ptr += uoffset; + break; + + /* From the 2.1 draft. */ + case DW_CFA_offset_extended_sf: + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_sleb128 (insn_ptr, &offset); + offset *= fs->data_align; + fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = offset; + break; + + case DW_CFA_def_cfa_sf: + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_reg); + insn_ptr = read_sleb128 (insn_ptr, &fs->cfa_offset); + fs->cfa_how = CFA_REG_OFFSET; + break; + + case DW_CFA_def_cfa_offset_sf: + insn_ptr = read_uleb128 (insn_ptr, &fs->cfa_offset); + /* cfa_how deliberately not set. */ + break; + + case DW_CFA_GNU_window_save: + /* ??? Hardcoded for SPARC register window configuration. */ + for (reg = 16; reg < 32; ++reg) + { + fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *); + } + break; + + case DW_CFA_GNU_args_size: + insn_ptr = read_uleb128 (insn_ptr, &context->args_size); + break; + + case DW_CFA_GNU_negative_offset_extended: + /* Obsoleted by DW_CFA_offset_extended_sf, but used by + older PowerPC code. */ + insn_ptr = read_uleb128 (insn_ptr, ®); + insn_ptr = read_uleb128 (insn_ptr, &uoffset); + offset = (_Unwind_Sword)uoffset * fs->data_align; + fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = -offset; + break; + + default: + abort (); + } + } +} + +static _Unwind_Reason_Code +uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) +{ + struct dwarf_fde *fde; + struct dwarf_cie *cie; + unsigned char *aug, *insn, *end; + + memset (fs, 0, sizeof (*fs)); + context->args_size = 0; + context->lsda = 0; + + fde = _Unwind_Find_FDE (context->ra - 1, &context->bases); + if (fde == NULL) + { + /* Couldn't find frame unwind info for this function. Try a + target-specific fallback mechanism. This will necessarily + not profide a personality routine or LSDA. */ +#ifdef MD_FALLBACK_FRAME_STATE_FOR + MD_FALLBACK_FRAME_STATE_FOR (context, fs, success); + return _URC_END_OF_STACK; + success: + return _URC_NO_REASON; +#else + return _URC_END_OF_STACK; +#endif + } + + context->bases.func = fde->pc_begin; + fs->pc = fde->pc_begin; + + cie = get_cie (fde); + insn = extract_cie_info (cie, context, fs); + if (insn == NULL) + /* CIE contained unknown augmentation. */ + return _URC_FATAL_PHASE1_ERROR; + + /* First decode all the insns in the CIE. */ + end = (unsigned char *) next_fde ((struct dwarf_fde *) cie); + execute_cfa_program (insn, end, context, fs); + + /* Locate augmentation for the fde. */ + aug = (unsigned char *)fde + sizeof (*fde); + insn = NULL; + if (fs->saw_z) + { + _Unwind_Word i; + aug = read_uleb128 (aug, &i); + insn = aug + i; + } + if (fs->saw_lsda) + aug = read_encoded_pointer (aug, fs->addr_encoding, + &context->bases, &context->lsda); + + /* Then the insns in the FDE up to our target PC. */ + if (insn == NULL) + insn = aug; + end = (unsigned char *) next_fde (fde); + execute_cfa_program (insn, end, context, fs); + + return _URC_NO_REASON; +} + + +static void +uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs) +{ + struct _Unwind_Context orig_context = *context; + void *cfa; + long i; + + /* Compute this frame's CFA. */ + switch (fs->cfa_how) + { + case CFA_REG_OFFSET: + /* Special handling here: Many machines do not use a frame pointer, + and track the CFA only through offsets from the stack pointer from + one frame to the next. In this case, the stack pointer is never + stored, so it has no saved address in the context. What we do + have is the CFA from the previous stack frame. */ + if (context->reg[fs->cfa_reg] == NULL) + cfa = context->cfa; + else + cfa = (void *) (_Unwind_Ptr) _Unwind_GetGR (context, fs->cfa_reg); + cfa += fs->cfa_offset; + break; + + case CFA_EXP: + /* ??? No way of knowing what register number is the stack pointer + to do the same sort of handling as above. Assume that if the + CFA calculation is so complicated as to require a stack program + that this will not be a problem. */ + { + unsigned char *exp = fs->cfa_exp; + _Unwind_Word len; + + exp = read_uleb128 (exp, &len); + cfa = (void *) (_Unwind_Ptr) + execute_stack_op (exp, exp + len, context, 0); + break; + } + + default: + abort (); + } + context->cfa = cfa; + + /* Compute the addresses of all registers saved in this frame. */ + for (i = 0; i < DWARF_FRAME_REGISTERS + 1; ++i) + switch (fs->regs.reg[i].how) + { + case REG_UNSAVED: + break; + case REG_SAVED_OFFSET: + context->reg[i] = cfa + fs->regs.reg[i].loc.offset; + break; + case REG_SAVED_REG: + context->reg[i] = orig_context.reg[fs->regs.reg[i].loc.reg]; + break; + case REG_SAVED_EXP: + { + unsigned char *exp = fs->regs.reg[i].loc.exp; + _Unwind_Word len; + _Unwind_Ptr val; + + exp = read_uleb128 (exp, &len); + val = execute_stack_op (exp, exp + len, &orig_context, + (_Unwind_Ptr) cfa); + context->reg[i] = (void *) val; + } + break; + } +} + +static void +uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) +{ + uw_update_context_1 (context, fs); + + /* Compute the return address now, since the return address column + can change from frame to frame. */ + context->ra = __builtin_extract_return_addr + ((void *) (_Unwind_Ptr) _Unwind_GetGR (context, fs->retaddr_column)); +} + +/* Fill in CONTEXT for top-of-stack. The only valid registers at this + level will be the return address and the CFA. */ + +#define uw_init_context(CONTEXT) \ +do { \ + /* Do any necessary initialization to access arbitrary stack frames. \ + On the SPARC, this means flushing the register windows. */ \ + __builtin_unwind_init (); \ + uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (), \ + __builtin_return_address (0)); \ +} while (0) + +static void +uw_init_context_1 (struct _Unwind_Context *context, + void *outer_cfa, void *outer_ra) +{ + void *ra = __builtin_extract_return_addr (__builtin_return_address (0)); + _Unwind_FrameState fs; + + memset (context, 0, sizeof (struct _Unwind_Context)); + context->ra = ra; + + if (uw_frame_state_for (context, &fs) != _URC_NO_REASON) + abort (); + + /* Force the frame state to use the known cfa value. */ + context->cfa = outer_cfa; + fs.cfa_how = CFA_REG_OFFSET; + fs.cfa_reg = 0; + fs.cfa_offset = 0; + + uw_update_context_1 (context, &fs); + + /* If the return address column was saved in a register in the + initialization context, then we can't see it in the given + call frame data. So have the initialization context tell us. */ + context->ra = __builtin_extract_return_addr (outer_ra); +} + + +/* Install TARGET into CURRENT so that we can return to it. This is a + macro because __builtin_eh_return must be invoked in the context of + our caller. */ + +#define uw_install_context(CURRENT, TARGET) \ +do { \ + long offset = uw_install_context_1 ((CURRENT), (TARGET)); \ + void *handler = __builtin_frob_return_addr ((TARGET)->ra); \ + __builtin_eh_return (offset, handler); \ +} while (0) + +static inline void +init_dwarf_reg_size_table (void) +{ + __builtin_init_dwarf_reg_size_table (dwarf_reg_size_table); +} + +static long +uw_install_context_1 (struct _Unwind_Context *current, + struct _Unwind_Context *target) +{ + long i; + +#if __GTHREADS + { + static __gthread_once_t once_regsizes = __GTHREAD_ONCE_INIT; + if (__gthread_once (&once_regsizes, init_dwarf_reg_size_table) != 0 + || dwarf_reg_size_table[0] == 0) + init_dwarf_reg_size_table (); + } +#else + if (dwarf_reg_size_table[0] == 0) + init_dwarf_reg_size_table (); +#endif + + for (i = 0; i < DWARF_FRAME_REGISTERS; ++i) + { + void *c = current->reg[i]; + void *t = target->reg[i]; + if (t && c && t != c) + memcpy (c, t, dwarf_reg_size_table[i]); + } + + /* We adjust SP by the difference between CURRENT and TARGET's CFA. */ + if (STACK_GROWS_DOWNWARD) + return target->cfa - current->cfa + target->args_size; + else + return current->cfa - target->cfa - target->args_size; +} + +static inline _Unwind_Ptr +uw_identify_context (struct _Unwind_Context *context) +{ + return _Unwind_GetIP (context); +} + + +#include "unwind.inc" + +#endif /* !USING_SJLJ_EXCEPTIONS */ diff --git a/gcc/unwind-sjlj.c b/gcc/unwind-sjlj.c new file mode 100644 index 0000000..662968b --- /dev/null +++ b/gcc/unwind-sjlj.c @@ -0,0 +1,259 @@ +/* DWARF2 exception handling and frame unwind runtime interface routines. + Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + This file is part of GNU CC. + + GNU CC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU CC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "tconfig.h" +#include "tsystem.h" +#include "unwind.h" +#include "gthr.h" + +#if USING_SJLJ_EXCEPTIONS + +#ifdef DONT_USE_BUILTIN_SETJMP +#include <setjmp.h> +#else +#define setjmp __builtin_setjmp +#define longjmp __builtin_longjmp +#endif + +/* This structure is allocated on the stack of the target function. + This must match the definition created in except.c:init_eh. */ +struct SjLj_Function_Context +{ + /* This is the chain through all registered contexts. It is + filled in by _Unwind_SjLj_Register. */ + struct SjLj_Function_Context *prev; + + /* This is assigned in by the target function before every call + to the index of the call site in the lsda. It is assigned by + the personality routine to the landing pad index. */ + int call_site; + + /* This is how data is returned from the personality routine to + the target function's handler. */ + _Unwind_Word data[4]; + + /* These are filled in once by the target function before any + exceptions are expected to be handled. */ + _Unwind_Personality_Fn personality; + void *lsda; + +#ifdef DONT_USE_BUILTIN_SETJMP + /* We don't know what sort of alignment requirements the system + jmp_buf has. We over estimated in except.c, and now we have + to match that here just in case the system *didn't* have more + restrictive requirements. */ + jmp_buf jbuf __attribute__((aligned)); +#else + void *jbuf[]; +#endif +}; + +struct _Unwind_Context +{ + struct SjLj_Function_Context *fc; +}; + +typedef struct +{ + _Unwind_Personality_Fn personality; +} _Unwind_FrameState; + + +/* Manage the chain of registered function contexts. */ + +/* Single threaded fallback chain. */ +static struct SjLj_Function_Context *fc_static; + +#if __GTHREADS +static __gthread_key_t fc_key; +static int use_fc_key = -1; + +static void +fc_key_dtor (void *ptr) +{ + __gthread_key_dtor (fc_key, ptr); +} + +static void +fc_key_init (void) +{ + use_fc_key = __gthread_key_create (&fc_key, fc_key_dtor) == 0; +} + +static void +fc_key_init_once (void) +{ + static __gthread_once_t once = __GTHREAD_ONCE_INIT; + if (__gthread_once (&once, fc_key_init) != 0 || use_fc_key < 0) + use_fc_key = 0; +} +#endif + +void +_Unwind_SjLj_Register (struct SjLj_Function_Context *fc) +{ +#if __GTHREADS + if (use_fc_key < 0) + fc_key_init_once (); + + if (use_fc_key) + { + fc->prev = __gthread_getspecific (fc_key); + __gthread_setspecific (fc_key, fc); + } + else +#endif + { + fc->prev = fc_static; + fc_static = fc; + } +} + +static inline struct SjLj_Function_Context * +_Unwind_SjLj_GetContext (void) +{ +#if __GTHREADS + if (use_fc_key < 0) + fc_key_init_once (); + + if (use_fc_key) + return __gthread_getspecific (fc_key); +#endif + return fc_static; +} + +static inline void +_Unwind_SjLj_SetContext (struct SjLj_Function_Context *fc) +{ +#if __GTHREADS + if (use_fc_key < 0) + fc_key_init_once (); + + if (use_fc_key) + __gthread_setspecific (fc_key, fc); + else +#endif + fc_static = fc; +} + +void +_Unwind_SjLj_Unregister (struct SjLj_Function_Context *fc) +{ + _Unwind_SjLj_SetContext (fc->prev); +} + + +/* Get/set the return data value at INDEX in CONTEXT. */ + +_Unwind_Word +_Unwind_GetGR (struct _Unwind_Context *context, int index) +{ + return context->fc->data[index]; +} + +void +_Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) +{ + context->fc->data[index] = val; +} + +/* Get the call-site index as saved in CONTEXT. */ + +_Unwind_Ptr +_Unwind_GetIP (struct _Unwind_Context *context) +{ + return context->fc->call_site + 1; +} + +/* Set the return landing pad index in CONTEXT. */ + +void +_Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val) +{ + context->fc->call_site = val - 1; +} + +void * +_Unwind_GetLanguageSpecificData (struct _Unwind_Context *context) +{ + return context->fc->lsda; +} + +_Unwind_Ptr +_Unwind_GetRegionStart (struct _Unwind_Context *context) +{ + return 0; +} + + +static inline _Unwind_Reason_Code +uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) +{ + if (context->fc == NULL) + { + fs->personality = NULL; + return _URC_END_OF_STACK; + } + else + { + fs->personality = context->fc->personality; + return _URC_NO_REASON; + } +} + +static inline void +uw_update_context (struct _Unwind_Context *context, + _Unwind_FrameState *fs __attribute__((unused)) ) +{ + context->fc = context->fc->prev; +} + +static inline void +uw_init_context (struct _Unwind_Context *context) +{ + context->fc = _Unwind_SjLj_GetContext (); +} + +/* ??? There appear to be bugs in integrate.c wrt __builtin_longjmp and + virtual-stack-vars. An inline version of this segfaults on Sparc. */ +#define uw_install_context(CURRENT, TARGET) \ + do { \ + _Unwind_SjLj_SetContext ((TARGET)->fc); \ + longjmp ((TARGET)->fc->jbuf, 1); \ + } while (0) + + +static inline _Unwind_Ptr +uw_identify_context (struct _Unwind_Context *context) +{ + return (_Unwind_Ptr) context->fc; +} + + +/* Play games with unwind symbols so that we can have call frame + and sjlj symbols in the same shared library. Not that you can + use them simultaneously... */ +#define _Unwind_RaiseException _Unwind_SjLj_RaiseException +#define _Unwind_ForcedUnwind _Unwind_SjLj_ForcedUnwind +#define _Unwind_Resume _Unwind_SjLj_Resume + +#include "unwind.inc" + +#endif /* USING_SJLJ_EXCEPTIONS */ diff --git a/gcc/unwind.h b/gcc/unwind.h new file mode 100644 index 0000000..8df0930 --- /dev/null +++ b/gcc/unwind.h @@ -0,0 +1,166 @@ +/* Exception handling and frame unwind runtime interface routines. + Copyright (C) 2001 Free Software Foundation, Inc. + + This file is part of GNU CC. + + GNU CC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU CC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This is derived from the C++ ABI for IA-64. Where we diverge + for cross-architecture compatibility are noted with "@@@". */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Level 1: Base ABI */ + +/* @@@ The IA-64 ABI uses uint64 throughout. Most places this is + inefficient for 32-bit and smaller machines. */ +typedef unsigned _Unwind_Word __attribute__((__mode__(__word__))); +typedef signed _Unwind_Sword __attribute__((__mode__(__word__))); +typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__))); + +/* @@@ The IA-64 ABI uses a 64-bit word to identify the producer and + consumer of an exception. We'll go along with this for now even on + 32-bit machines. We'll need to provide some other option for + 16-bit machines and for machines with > 8 bits per byte. */ +typedef unsigned _Unwind_Exception_Class __attribute__((__mode__(__DI__))); + +/* The unwind interface uses reason codes in several contexts to + identify the reasons for failures or other actions. */ +typedef enum +{ + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 +} _Unwind_Reason_Code; + + +/* The unwind interface uses a pointer to an exception header object + as its representation of an exception being thrown. In general, the + full representation of an exception object is language- and + implementation-specific, but it will be prefixed by a header + understood by the unwind interface. */ + +struct _Unwind_Exception; + +typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, + struct _Unwind_Exception *); + +struct _Unwind_Exception +{ + _Unwind_Exception_Class exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + _Unwind_Word private_1; + _Unwind_Word private_2; + + /* @@@ The IA-64 ABI says that this structure must be double-word aligned. + Taking that literally does not make much sense generically. Instead we + provide the maximum alignment required by any type for the machine. */ +} __attribute__((__aligned__)); + + +/* The ACTIONS argument to the personality routine is a bitwise OR of one + or more of the following constants. */ +typedef int _Unwind_Action; + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + +/* This is an opaque type used to refer to a system-specific data + structure used by the system unwinder. This context is created and + destroyed by the system, and passed to the personality routine + during unwinding. */ +struct _Unwind_Context; + +/* Raise an exception, passing along the given exception object. */ +extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *); + +/* Raise an exception for forced unwinding. */ + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (int, _Unwind_Action, _Unwind_Exception_Class, + struct _Unwind_Exception *, struct _Unwind_Context *, void *); + +extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *, + _Unwind_Stop_Fn, + void *); + +/* Helper to invoke the exception_cleanup routine. */ +extern void _Unwind_DeleteException (struct _Unwind_Exception *); + +/* Resume propagation of an existing exception. This is used after + e.g. executing cleanup code, and not to implement rethrowing. */ +extern void _Unwind_Resume (struct _Unwind_Exception *); + +/* These functions are used for communicating information about the unwind + context (i.e. the unwind descriptors and the user register state) between + the unwind library and the personality routine and landing pad. Only + selected registers maybe manipulated. */ + +extern _Unwind_Word _Unwind_GetGR (struct _Unwind_Context *, int); +extern void _Unwind_SetGR (struct _Unwind_Context *, int, _Unwind_Word); + +extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); +extern void _Unwind_SetIP (struct _Unwind_Context *, _Unwind_Ptr); + +extern void *_Unwind_GetLanguageSpecificData (struct _Unwind_Context *); + +extern _Unwind_Ptr _Unwind_GetRegionStart (struct _Unwind_Context *); + + +/* The personality routine is the function in the C++ (or other language) + runtime library which serves as an interface between the system unwind + library and language-specific exception handling semantics. It is + specific to the code fragment described by an unwind info block, and + it is always referenced via the pointer in the unwind info block, and + hence it has no ABI-specified name. + + Note that this implies that two different C++ implementations can + use different names, and have different contents in the language + specific data area. Moreover, that the language specific data + area contains no version info because name of the function invoked + provides more effective versioning by detecting at link time the + lack of code to handle the different data format. */ + +typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn) + (int, _Unwind_Action, _Unwind_Exception_Class, + struct _Unwind_Exception *, struct _Unwind_Context *); + +/* @@@ The following alternate entry points are for setjmp/longjmp + based unwinding. */ + +struct SjLj_Function_Context; +extern void _Unwind_SjLj_Register (struct SjLj_Function_Context *); +extern void _Unwind_SjLj_Unregister (struct SjLj_Function_Context *); + +extern _Unwind_Reason_Code _Unwind_SjLj_RaiseException + (struct _Unwind_Exception *); +extern _Unwind_Reason_Code _Unwind_SjLj_ForcedUnwind + (struct _Unwind_Exception *, _Unwind_Stop_Fn, void *); +extern void _Unwind_SjLj_Resume (struct _Unwind_Exception *); + +#ifdef __cplusplus +} +#endif diff --git a/gcc/unwind.inc b/gcc/unwind.inc new file mode 100644 index 0000000..ba31536 --- /dev/null +++ b/gcc/unwind.inc @@ -0,0 +1,232 @@ +/* Exception handling and frame unwind runtime interface routines. + Copyright (C) 2001 Free Software Foundation, Inc. + + This file is part of GNU CC. + + GNU CC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU CC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU CC; see the file COPYING. If not, write to + the Free Software Foundation, 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This is derived from the C++ ABI for IA-64. Where we diverge + for cross-architecture compatibility are noted with "@@@". + This file is included from unwind-dw2.c or unwind-ia64.c. */ + +/* Subroutine of _Unwind_RaiseException also invoked from _Unwind_Resume. + + Unwind the stack calling the personality routine to find both the + exception handler and intermediary cleanup code. We'll only locate + the first such frame here. Cleanup code will call back into + _Unwind_Resume and we'll continue Phase 2 there. */ + +static _Unwind_Reason_Code +_Unwind_RaiseException_Phase2(struct _Unwind_Exception *exc, + struct _Unwind_Context *context) +{ + _Unwind_Reason_Code code; + + while (1) + { + _Unwind_FrameState fs; + int match_handler; + + code = uw_frame_state_for (context, &fs); + + /* Identify when we've reached the designated handler context. */ + match_handler = (uw_identify_context (context) == exc->private_2 + ? _UA_HANDLER_FRAME : 0); + + if (code != _URC_NO_REASON) + /* Some error encountered. Ususally the unwinder doesn't + diagnose these and merely crashes. */ + return _URC_FATAL_PHASE2_ERROR; + + /* Unwind successful. Run the personality routine, if any. */ + if (fs.personality) + { + code = (*fs.personality) (1, _UA_CLEANUP_PHASE | match_handler, + exc->exception_class, exc, context); + if (code == _URC_INSTALL_CONTEXT) + break; + if (code != _URC_CONTINUE_UNWIND) + return _URC_FATAL_PHASE2_ERROR; + } + + /* Don't let us unwind past the handler context. */ + if (match_handler) + abort (); + + uw_update_context (context, &fs); + } + + return code; +} + + +/* Raise an exception, passing along the given exception object. */ + +_Unwind_Reason_Code +_Unwind_RaiseException(struct _Unwind_Exception *exc) +{ + struct _Unwind_Context this_context, cur_context; + _Unwind_Reason_Code code; + + uw_init_context (&this_context); + cur_context = this_context; + + /* Phase 1: Search. Unwind the stack, calling the personality routine + with the _UA_SEARCH_PHASE flag set. Do not modify the stack yet. */ + while (1) + { + _Unwind_FrameState fs; + + code = uw_frame_state_for (&cur_context, &fs); + + if (code == _URC_END_OF_STACK) + /* Hit end of stack with no handler found. */ + return _URC_END_OF_STACK; + + if (code != _URC_NO_REASON) + /* Some error encountered. Ususally the unwinder doesn't + diagnose these and merely crashes. */ + return _URC_FATAL_PHASE1_ERROR; + + /* Unwind successful. Run the personality routine, if any. */ + if (fs.personality) + { + code = (*fs.personality) (1, _UA_SEARCH_PHASE, exc->exception_class, + exc, &cur_context); + if (code == _URC_HANDLER_FOUND) + break; + else if (code != _URC_CONTINUE_UNWIND) + return _URC_FATAL_PHASE1_ERROR; + } + + uw_update_context (&cur_context, &fs); + } + + /* Indicate to _Unwind_Resume and associated subroutines that this + is not a forced unwind. Further, note where we found a handler. */ + exc->private_1 = 0; + exc->private_2 = uw_identify_context (&cur_context); + + cur_context = this_context; + code = _Unwind_RaiseException_Phase2 (exc, &cur_context); + if (code != _URC_INSTALL_CONTEXT) + return code; + + uw_install_context (&this_context, &cur_context); +} + + +/* Subroutine of _Unwind_ForcedUnwind also invoked from _Unwind_Resume. */ + +static _Unwind_Reason_Code +_Unwind_ForcedUnwind_Phase2(struct _Unwind_Exception *exc, + struct _Unwind_Context *context) +{ + _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) exc->private_1; + void *stop_argument = (void *) exc->private_2; + _Unwind_Reason_Code code, stop_code; + + while (1) + { + _Unwind_FrameState fs; + + code = uw_frame_state_for (context, &fs); + if (code != _URC_NO_REASON && code != _URC_END_OF_STACK) + return _URC_FATAL_PHASE2_ERROR; + + /* Unwind successful. */ + stop_code = (*stop) (1, _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE, + exc->exception_class, exc, context, stop_argument); + if (stop_code != _URC_NO_REASON) + return _URC_FATAL_PHASE2_ERROR; + + /* Stop didn't want to do anything. Invoke the personality + handler, if applicable, to run cleanups. */ + if (code == _URC_END_OF_STACK) + break; + + if (fs.personality) + { + code = (*fs.personality) (1, _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE, + exc->exception_class, exc, context); + if (code == _URC_INSTALL_CONTEXT) + break; + if (code != _URC_CONTINUE_UNWIND) + return _URC_FATAL_PHASE2_ERROR; + } + + uw_update_context (context, &fs); + } + + return code; +} + + +/* Raise an exception for forced unwinding. */ + +_Unwind_Reason_Code +_Unwind_ForcedUnwind (struct _Unwind_Exception *exc, + _Unwind_Stop_Fn stop, void * stop_argument) +{ + struct _Unwind_Context this_context, cur_context; + _Unwind_Reason_Code code; + + uw_init_context (&this_context); + cur_context = this_context; + + exc->private_1 = (_Unwind_Ptr) stop; + exc->private_2 = (_Unwind_Ptr) stop_argument; + + code = _Unwind_ForcedUnwind_Phase2 (exc, &cur_context); + if (code != _URC_INSTALL_CONTEXT) + return code; + + uw_install_context (&this_context, &cur_context); +} + + +/* Resume propagation of an existing exception. This is used after + e.g. executing cleanup code, and not to implement rethrowing. */ + +void +_Unwind_Resume (struct _Unwind_Exception *exc) +{ + struct _Unwind_Context this_context, cur_context; + _Unwind_Reason_Code code; + + uw_init_context (&this_context); + cur_context = this_context; + + /* Choose between continuing to process _Unwind_RaiseException + or _Unwind_ForcedUnwind. */ + if (exc->private_1 == 0) + code = _Unwind_RaiseException_Phase2 (exc, &cur_context); + else + code = _Unwind_ForcedUnwind_Phase2 (exc, &cur_context); + + if (code != _URC_INSTALL_CONTEXT) + abort (); + + uw_install_context (&this_context, &cur_context); +} + +/* A convenience function that calls the exception_cleanup field. */ + +void +_Unwind_DeleteException (struct _Unwind_Exception *exc) +{ + (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc); +} |