X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fexcept.c;h=0fbda613961a02536354bc119e804dca2ee34b79;hb=e0f40ead16de7b33aecee5fcdebca730298b5fdc;hp=65aa8b5c4cc27ec9115fad48d79d6398f843a644;hpb=bf6102ae18353af8e717f549a58acd2a2ab46285;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/except.c b/gcc/except.c index 65aa8b5c4cc..0fbda613961 100644 --- a/gcc/except.c +++ b/gcc/except.c @@ -1,13 +1,14 @@ /* Implements exception handling. Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. Contributed by Mike Stump . This file is part of GCC. GCC 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 +Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY @@ -16,9 +17,8 @@ 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 GCC; see the file COPYING. If not, write to the Free -Software Foundation, 59 Temple Place - Suite 330, Boston, MA -02111-1307, USA. */ +along with GCC; see the file COPYING3. If not see +. */ /* An exception is an event that can be signaled from within a @@ -74,6 +74,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "target.h" #include "langhooks.h" #include "cgraph.h" +#include "diagnostic.h" +#include "tree-pass.h" +#include "timevar.h" /* Provide defaults for stuff that may not be defined when using sjlj exceptions. */ @@ -82,9 +85,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #endif -/* Nonzero means enable synchronous exceptions for non-call instructions. */ -int flag_non_call_exceptions; - /* Protect cleanup actions with must-not-throw regions, with a call to the given failure handler. */ tree (*lang_protect_cleanup_actions) (void); @@ -134,15 +134,14 @@ struct eh_region GTY(()) /* Each region does exactly one thing. */ enum eh_region_type - { + { ERT_UNKNOWN = 0, ERT_CLEANUP, ERT_TRY, ERT_CATCH, ERT_ALLOWED_EXCEPTIONS, ERT_MUST_NOT_THROW, - ERT_THROW, - ERT_FIXUP + ERT_THROW } type; /* Holds the action to perform based on the preceding type. */ @@ -152,8 +151,6 @@ struct eh_region GTY(()) struct eh_region_u_try { struct eh_region *catch; struct eh_region *last_catch; - struct eh_region *prev_try; - rtx continue_label; } GTY ((tag ("ERT_TRY"))) try; /* The list through the catch handlers, the list of type objects @@ -180,17 +177,8 @@ struct eh_region GTY(()) /* Retain the cleanup expression even after expansion so that we can match up fixup regions. */ struct eh_region_u_cleanup { - tree exp; struct eh_region *prev_try; } GTY ((tag ("ERT_CLEANUP"))) cleanup; - - /* The real region (by expression and by pointer) that fixup code - should live in. */ - struct eh_region_u_fixup { - tree cleanup_exp; - struct eh_region *real_region; - bool resolved; - } GTY ((tag ("ERT_FIXUP"))) fixup; } GTY ((desc ("%0.type"))) u; /* Entry point for this region's handler before landing pads are built. */ @@ -211,12 +199,17 @@ struct eh_region GTY(()) unsigned may_contain_throw : 1; }; +typedef struct eh_region *eh_region; + struct call_site_record GTY(()) { rtx landing_pad; int action; }; +DEF_VEC_P(eh_region); +DEF_VEC_ALLOC_P(eh_region, gc); + /* Used to save exception status for each function. */ struct eh_status GTY(()) { @@ -224,7 +217,7 @@ struct eh_status GTY(()) struct eh_region *region_tree; /* The same information as an indexable array. */ - struct eh_region ** GTY ((length ("%h.last_region_number"))) region_array; + VEC(eh_region,gc) *region_array; /* The most recently open region. */ struct eh_region *cur_region; @@ -238,7 +231,7 @@ struct eh_status GTY(()) int built_landing_pads; int last_region_number; - varray_type ttype_data; + VEC(tree,gc) *ttype_data; varray_type ehspec_data; varray_type action_record_data; @@ -255,24 +248,17 @@ struct eh_status GTY(()) rtx sjlj_fc; rtx sjlj_exit_after; -}; + htab_t GTY((param_is (struct throw_stmt_node))) throw_stmt_table; +}; static int t2r_eq (const void *, const void *); static hashval_t t2r_hash (const void *); static void add_type_for_runtime (tree); static tree lookup_type_for_runtime (tree); -static struct eh_region *expand_eh_region_end (void); - -static void resolve_fixup_regions (void); -static void remove_fixup_regions (void); static void remove_unreachable_regions (rtx); -static void convert_from_eh_region_ranges_1 (rtx *, int *, int); -static struct eh_region *duplicate_eh_region_1 (struct eh_region *, - struct inline_remap *); -static void duplicate_eh_region_2 (struct eh_region *, struct eh_region **); static int ttypes_filter_eq (const void *, const void *); static hashval_t ttypes_filter_hash (const void *); static int ehspec_filter_eq (const void *, const void *); @@ -381,7 +367,7 @@ init_eh (void) integer_type_node); DECL_FIELD_CONTEXT (f_cs) = sjlj_fc_type_node; - tmp = build_index_type (build_int_2 (4 - 1, 0)); + tmp = build_index_type (build_int_cst (NULL_TREE, 4 - 1)); tmp = build_array_type (lang_hooks.types.type_for_mode (word_mode, 1), tmp); f_data = build_decl (FIELD_DECL, get_identifier ("__data"), tmp); @@ -397,17 +383,17 @@ init_eh (void) #ifdef DONT_USE_BUILTIN_SETJMP #ifdef JMP_BUF_SIZE - tmp = build_int_2 (JMP_BUF_SIZE - 1, 0); + tmp = build_int_cst (NULL_TREE, JMP_BUF_SIZE - 1); #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); + tmp = build_int_cst (NULL_TREE, FIRST_PSEUDO_REGISTER + 2 - 1); #endif #else /* builtin_setjmp takes a pointer to 5 words. */ - tmp = build_int_2 (5 * BITS_PER_WORD / POINTER_SIZE - 1, 0); + tmp = build_int_cst (NULL_TREE, 5 * BITS_PER_WORD / POINTER_SIZE - 1); #endif tmp = build_index_type (tmp); tmp = build_array_type (ptr_type_node, tmp); @@ -455,7 +441,7 @@ init_eh_for_function (void) cfun->eh = ggc_alloc_cleared (sizeof (struct eh_status)); } -/* Routines to generate the exception tree somewhat directly. +/* Routines to generate the exception tree somewhat directly. These are used from tree-eh.c when processing exception related nodes during tree optimization. */ @@ -465,8 +451,7 @@ gen_eh_region (enum eh_region_type type, struct eh_region *outer) struct eh_region *new; #ifdef ENABLE_CHECKING - if (! doing_eh (0)) - abort (); + gcc_assert (doing_eh (0)); #endif /* Insert a new blank region as a leaf in the tree. */ @@ -577,342 +562,18 @@ set_eh_region_tree_label (struct eh_region *region, tree lab) region->tree_label = lab; } -/* 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 (void) -{ - struct eh_region *new; - rtx note; - - if (! doing_eh (0)) - return; - - new = gen_eh_region (ERT_UNKNOWN, cfun->eh->cur_region); - cfun->eh->cur_region = new; - - /* Create a note marking the start of this region. */ - note = emit_note (NOTE_INSN_EH_REGION_BEG); - NOTE_EH_HANDLER (note) = new->region_number; -} - -/* Common code to end a region. Returns the region just ended. */ - -static struct eh_region * -expand_eh_region_end (void) -{ - struct eh_region *cur_region = cfun->eh->cur_region; - rtx note; - - /* Create a note marking the end of this region. */ - note = emit_note (NOTE_INSN_EH_REGION_END); - NOTE_EH_HANDLER (note) = cur_region->region_number; - - /* Pop. */ - cfun->eh->cur_region = cur_region->outer; - - return cur_region; -} - -/* Expand HANDLER, which is the operand 1 of a TRY_CATCH_EXPR. Catch - blocks and C++ exception-specifications are handled specially. */ - -void -expand_eh_handler (tree handler) -{ - tree inner = expr_first (handler); - - switch (TREE_CODE (inner)) - { - case CATCH_EXPR: - expand_start_all_catch (); - expand_expr (handler, const0_rtx, VOIDmode, 0); - expand_end_all_catch (); - break; - - case EH_FILTER_EXPR: - if (EH_FILTER_MUST_NOT_THROW (handler)) - expand_eh_region_end_must_not_throw (EH_FILTER_FAILURE (handler)); - else - expand_eh_region_end_allowed (EH_FILTER_TYPES (handler), - EH_FILTER_FAILURE (handler)); - break; - - default: - expand_eh_region_end_cleanup (handler); - break; - } -} - -/* End an exception handling region for a cleanup. HANDLER is an - expression to expand for the cleanup. */ - -void -expand_eh_region_end_cleanup (tree handler) -{ - struct eh_region *region; - tree protect_cleanup_actions; - rtx around_label; - rtx data_save[2]; - - if (! doing_eh (0)) - return; - - region = expand_eh_region_end (); - region->type = ERT_CLEANUP; - region->label = gen_label_rtx (); - region->u.cleanup.exp = handler; - region->u.cleanup.prev_try = cfun->eh->try_region; - - around_label = gen_label_rtx (); - emit_jump (around_label); - - emit_label (region->label); - - if (flag_non_call_exceptions || region->may_contain_throw) - { - /* Give the language a chance to specify an action to be taken if an - exception is thrown that would propagate out of the HANDLER. */ - protect_cleanup_actions - = (lang_protect_cleanup_actions - ? (*lang_protect_cleanup_actions) () - : NULL_TREE); - - if (protect_cleanup_actions) - expand_eh_region_start (); - - /* In case this cleanup involves an inline destructor with a try block in - it, we need to save the EH return data registers around it. */ - data_save[0] = gen_reg_rtx (ptr_mode); - emit_move_insn (data_save[0], get_exception_pointer (cfun)); - data_save[1] = gen_reg_rtx (word_mode); - emit_move_insn (data_save[1], get_exception_filter (cfun)); - - expand_expr (handler, const0_rtx, VOIDmode, 0); - - emit_move_insn (cfun->eh->exc_ptr, data_save[0]); - emit_move_insn (cfun->eh->filter, data_save[1]); - - if (protect_cleanup_actions) - expand_eh_region_end_must_not_throw (protect_cleanup_actions); - - /* We need any stack adjustment complete before the around_label. */ - do_pending_stack_adjust (); - } - - /* 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. */ - region->resume - = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number)); - emit_barrier (); - - emit_label (around_label); -} - void expand_resx_expr (tree exp) { int region_nr = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)); - struct eh_region *reg = cfun->eh->region_array[region_nr]; + struct eh_region *reg = VEC_index (eh_region, + cfun->eh->region_array, region_nr); + gcc_assert (!reg->resume); reg->resume = emit_jump_insn (gen_rtx_RESX (VOIDmode, region_nr)); emit_barrier (); } -/* End an exception handling region for a try block, and prepares - for subsequent calls to expand_start_catch. */ - -void -expand_start_all_catch (void) -{ - struct eh_region *region; - - 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); -} - -/* Begin a catch clause. TYPE is the type caught, a list of such - types, (in the case of Java) an ADDR_EXPR which points to the - runtime type to match, or null if this is a catch-all - clause. Providing a type list enables to associate the catch region - with potentially several exception types, which is useful e.g. for - Ada. */ - -void -expand_start_catch (tree type_or_list) -{ - struct eh_region *c; - rtx note; - - if (! doing_eh (0)) - return; - - c = gen_eh_region_catch (cfun->eh->try_region, type_or_list); - cfun->eh->cur_region = c; - - c->label = gen_label_rtx (); - emit_label (c->label); - - note = emit_note (NOTE_INSN_EH_REGION_BEG); - NOTE_EH_HANDLER (note) = c->region_number; -} - -/* End a catch clause. Control will resume after the try/catch block. */ - -void -expand_end_catch (void) -{ - if (! doing_eh (0)) - return; - - expand_eh_region_end (); - emit_jump (cfun->eh->try_region->u.try.continue_label); -} - -/* End a sequence of catch handlers for a try block. */ - -void -expand_end_all_catch (void) -{ - 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); -} - -/* 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 occurs. - - ??? We could use these semantics for calls to rethrow, too; if we can - see the surrounding catch clause, we know that the exception we're - rethrowing satisfies the "filter" of the catch type. */ - -void -expand_eh_region_end_allowed (tree allowed, tree failure) -{ - struct eh_region *region; - rtx around_label; - - if (! doing_eh (0)) - return; - - region = expand_eh_region_end (); - region->type = ERT_ALLOWED_EXCEPTIONS; - region->u.allowed.type_list = allowed; - region->label = gen_label_rtx (); - - for (; allowed ; allowed = TREE_CHAIN (allowed)) - add_type_for_runtime (TREE_VALUE (allowed)); - - /* 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. */ - - around_label = gen_label_rtx (); - emit_jump (around_label); - - emit_label (region->label); - expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL); - /* We must adjust the stack before we reach the AROUND_LABEL because - the call to FAILURE does not occur on all paths to the - AROUND_LABEL. */ - do_pending_stack_adjust (); - - 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. - - 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. */ - -void -expand_eh_region_end_must_not_throw (tree failure) -{ - struct eh_region *region; - rtx around_label; - - if (! doing_eh (0)) - return; - - region = expand_eh_region_end (); - region->type = ERT_MUST_NOT_THROW; - region->label = gen_label_rtx (); - - /* 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. */ - - around_label = gen_label_rtx (); - emit_jump (around_label); - - emit_label (region->label); - expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL); - - emit_label (around_label); -} - -/* 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. */ - -void -expand_eh_region_end_throw (tree type) -{ - struct eh_region *region; - - if (! doing_eh (0)) - return; - - region = expand_eh_region_end (); - region->type = ERT_THROW; - region->u.throw.type = type; -} - -/* 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 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. */ - -void -expand_eh_region_end_fixup (tree handler) -{ - struct eh_region *fixup; - - if (! doing_eh (0)) - return; - - fixup = expand_eh_region_end (); - fixup->type = ERT_FIXUP; - fixup->u.fixup.cleanup_exp = handler; -} - /* Note that the current EH region (if any) may contain a throw, or a call to a function which itself may contain a throw. */ @@ -957,7 +618,7 @@ get_exception_filter (struct function *fun) rtx filter = fun->eh->filter; if (fun == cfun && ! filter) { - filter = gen_reg_rtx (word_mode); + filter = gen_reg_rtx (targetm.eh_return_filter_mode ()); fun->eh->filter = filter; } return filter; @@ -965,26 +626,24 @@ get_exception_filter (struct function *fun) /* This section is for the exception handling specific optimization pass. */ -/* 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. */ +/* Random access the exception region tree. */ void collect_eh_region_array (void) { - struct eh_region **array, *i; + struct eh_region *i; i = cfun->eh->region_tree; if (! i) return; - array = ggc_alloc_cleared ((cfun->eh->last_region_number + 1) - * sizeof (*array)); - cfun->eh->region_array = array; + VEC_safe_grow (eh_region, gc, cfun->eh->region_array, + cfun->eh->last_region_number + 1); + VEC_replace (eh_region, cfun->eh->region_array, 0, 0); while (1) { - array[i->region_number] = i; + VEC_replace (eh_region, cfun->eh->region_array, i->region_number, i); /* If there are sub-regions, process them. */ if (i->inner) @@ -1005,124 +664,6 @@ collect_eh_region_array (void) } } -static void -resolve_one_fixup_region (struct eh_region *fixup) -{ - struct eh_region *cleanup, *real; - int j, n; - - n = cfun->eh->last_region_number; - cleanup = 0; - - for (j = 1; j <= n; ++j) - { - cleanup = cfun->eh->region_array[j]; - if (cleanup && cleanup->type == ERT_CLEANUP - && cleanup->u.cleanup.exp == fixup->u.fixup.cleanup_exp) - break; - } - if (j > n) - abort (); - - real = cleanup->outer; - if (real && real->type == ERT_FIXUP) - { - if (!real->u.fixup.resolved) - resolve_one_fixup_region (real); - real = real->u.fixup.real_region; - } - - fixup->u.fixup.real_region = real; - fixup->u.fixup.resolved = true; -} - -static void -resolve_fixup_regions (void) -{ - int i, n = cfun->eh->last_region_number; - - for (i = 1; i <= n; ++i) - { - struct eh_region *fixup = cfun->eh->region_array[i]; - - if (!fixup || fixup->type != ERT_FIXUP || fixup->u.fixup.resolved) - continue; - - resolve_one_fixup_region (fixup); - } -} - -/* Now that we've discovered what region actually encloses a fixup, - we can shuffle pointers and remove them from the tree. */ - -static void -remove_fixup_regions (void) -{ - int i; - rtx insn, note; - struct eh_region *fixup; - - /* Walk the insn chain and adjust the REG_EH_REGION numbers - for instructions referencing fixup regions. This is only - strictly necessary for fixup regions with no parent, but - doesn't hurt to do it for all regions. */ - for (insn = get_insns(); insn ; insn = NEXT_INSN (insn)) - if (INSN_P (insn) - && (note = find_reg_note (insn, REG_EH_REGION, NULL)) - && INTVAL (XEXP (note, 0)) > 0 - && (fixup = cfun->eh->region_array[INTVAL (XEXP (note, 0))]) - && fixup->type == ERT_FIXUP) - { - if (fixup->u.fixup.real_region) - XEXP (note, 0) = GEN_INT (fixup->u.fixup.real_region->region_number); - else - remove_note (insn, note); - } - - /* Remove the fixup regions from the tree. */ - for (i = cfun->eh->last_region_number; i > 0; --i) - { - fixup = cfun->eh->region_array[i]; - if (! fixup) - continue; - - /* Allow GC to maybe free some memory. */ - if (fixup->type == ERT_CLEANUP) - fixup->u.cleanup.exp = NULL_TREE; - - if (fixup->type != ERT_FIXUP) - continue; - - if (fixup->inner) - { - struct eh_region *parent, *p, **pp; - - parent = fixup->u.fixup.real_region; - - /* 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; - } - - /* 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; - } - - remove_eh_handler (fixup); - } -} - /* Remove all regions whose labels are not reachable from insns. */ static void @@ -1138,20 +679,18 @@ remove_unreachable_regions (rtx insns) for (i = cfun->eh->last_region_number; i > 0; --i) { - r = cfun->eh->region_array[i]; + r = VEC_index (eh_region, cfun->eh->region_array, i); if (!r || r->region_number != i) continue; if (r->resume) { - if (uid_region_num[INSN_UID (r->resume)]) - abort (); + gcc_assert (!uid_region_num[INSN_UID (r->resume)]); uid_region_num[INSN_UID (r->resume)] = i; } if (r->label) { - if (uid_region_num[INSN_UID (r->label)]) - abort (); + gcc_assert (!uid_region_num[INSN_UID (r->label)]); uid_region_num[INSN_UID (r->label)] = i; } } @@ -1161,7 +700,7 @@ remove_unreachable_regions (rtx insns) for (i = cfun->eh->last_region_number; i > 0; --i) { - r = cfun->eh->region_array[i]; + r = VEC_index (eh_region, cfun->eh->region_array, i); if (r && r->region_number == i && !reachable[i]) { bool kill_it = true; @@ -1198,7 +737,7 @@ remove_unreachable_regions (rtx insns) default: break; } - + if (kill_it) remove_eh_handler (r); } @@ -1208,114 +747,25 @@ remove_unreachable_regions (rtx insns) free (uid_region_num); } -/* Turn NOTE_INSN_EH_REGION notes into REG_EH_REGION notes for each - can_throw instruction in the region. */ - -static void -convert_from_eh_region_ranges_1 (rtx *pinsns, int *orig_sp, int cur) -{ - int *sp = orig_sp; - rtx insn, next; - - 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; - - *sp++ = cur; - cur = NOTE_EH_HANDLER (insn); - - 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; - - 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 - && GET_CODE (PATTERN (insn)) != CLOBBER - && GET_CODE (PATTERN (insn)) != USE - && may_trap_p (PATTERN (insn))))) - { - REG_NOTES (insn) = alloc_EXPR_LIST (REG_EH_REGION, GEN_INT (cur), - REG_NOTES (insn)); - } - } - } - - if (sp != orig_sp) - abort (); -} - -static void -collect_rtl_labels_from_trees (void) -{ - int i, n = cfun->eh->last_region_number; - for (i = 1; i <= n; ++i) - { - struct eh_region *reg = cfun->eh->region_array[i]; - if (reg && reg->tree_label) - reg->label = DECL_RTL_IF_SET (reg->tree_label); - } -} +/* Set up EH labels for RTL. */ void convert_from_eh_region_ranges (void) { rtx insns = get_insns (); + int i, n = cfun->eh->last_region_number; - if (cfun->eh->region_array) - { - /* If the region array already exists, assume we're coming from - optimize_function_tree. In this case all we need to do is - collect the rtl labels that correspond to the tree labels - that we allocated earlier. */ - collect_rtl_labels_from_trees (); - } - else + /* Most of the work is already done at the tree level. All we need to + do is collect the rtl labels that correspond to the tree labels that + collect the rtl labels that correspond to the tree labels + we allocated earlier. */ + for (i = 1; i <= n; ++i) { - int *stack; - - collect_eh_region_array (); - resolve_fixup_regions (); - - stack = xmalloc (sizeof (int) * (cfun->eh->last_region_number + 1)); - convert_from_eh_region_ranges_1 (&insns, stack, 0); - free (stack); + struct eh_region *region; - remove_fixup_regions (); + region = VEC_index (eh_region, cfun->eh->region_array, i); + if (region && region->tree_label) + region->label = DECL_RTL_IF_SET (region->tree_label); } remove_unreachable_regions (insns); @@ -1339,8 +789,7 @@ add_ehl_entry (rtx label, struct eh_region *region) label. After landing pad creation, the exception handlers may share landing pads. This is ok, since maybe_remove_eh_handler only requires the 1-1 mapping before landing pad creation. */ - if (*slot && !cfun->eh->built_landing_pads) - abort (); + gcc_assert (!*slot || cfun->eh->built_landing_pads); *slot = entry; } @@ -1366,9 +815,10 @@ find_exception_handler_labels (void) for (i = cfun->eh->last_region_number; i > 0; --i) { - struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *region; rtx lab; + region = VEC_index (eh_region, cfun->eh->region_array, i); if (! region || region->region_number != i) continue; if (cfun->eh->built_landing_pads) @@ -1386,6 +836,8 @@ find_exception_handler_labels (void) add_ehl_entry (return_label, NULL); } +/* Returns true if the current function has exception handling regions. */ + bool current_function_has_exception_handlers (void) { @@ -1393,167 +845,294 @@ current_function_has_exception_handlers (void) for (i = cfun->eh->last_region_number; i > 0; --i) { - struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *region; - if (! region || region->region_number != i) - continue; - if (region->type != ERT_THROW) + region = VEC_index (eh_region, cfun->eh->region_array, i); + if (region + && region->region_number == i + && region->type != ERT_THROW) return true; } return false; } -static struct eh_region * -duplicate_eh_region_1 (struct eh_region *o, struct inline_remap *map) +/* A subroutine of duplicate_eh_regions. Search the region tree under O + for the minimum and maximum region numbers. Update *MIN and *MAX. */ + +static void +duplicate_eh_regions_0 (eh_region o, int *min, int *max) +{ + if (o->region_number < *min) + *min = o->region_number; + if (o->region_number > *max) + *max = o->region_number; + + if (o->inner) + { + o = o->inner; + duplicate_eh_regions_0 (o, min, max); + while (o->next_peer) + { + o = o->next_peer; + duplicate_eh_regions_0 (o, min, max); + } + } +} + +/* A subroutine of duplicate_eh_regions. Copy the region tree under OLD. + Root it at OUTER, and apply EH_OFFSET to the region number. Don't worry + about the other internal pointers just yet, just the tree-like pointers. */ + +static eh_region +duplicate_eh_regions_1 (eh_region old, eh_region outer, int eh_offset) { - struct eh_region *n = ggc_alloc_cleared (sizeof (struct eh_region)); + eh_region ret, n; + + ret = n = ggc_alloc (sizeof (struct eh_region)); - n->region_number = o->region_number + cfun->eh->last_region_number; - n->type = o->type; + *n = *old; + n->outer = outer; + n->next_peer = NULL; + gcc_assert (!old->aka); - switch (n->type) + n->region_number += eh_offset; + VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n); + + if (old->inner) { - case ERT_CLEANUP: - case ERT_MUST_NOT_THROW: - break; + old = old->inner; + n = n->inner = duplicate_eh_regions_1 (old, ret, eh_offset); + while (old->next_peer) + { + old = old->next_peer; + n = n->next_peer = duplicate_eh_regions_1 (old, ret, eh_offset); + } + } - 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; + return ret; +} - case ERT_CATCH: - n->u.catch.type_list = o->u.catch.type_list; - break; +/* Duplicate the EH regions of IFUN, rooted at COPY_REGION, into current + function and root the tree below OUTER_REGION. Remap labels using MAP + callback. The special case of COPY_REGION of 0 means all regions. */ - case ERT_ALLOWED_EXCEPTIONS: - n->u.allowed.type_list = o->u.allowed.type_list; - break; +int +duplicate_eh_regions (struct function *ifun, duplicate_eh_regions_map map, + void *data, int copy_region, int outer_region) +{ + eh_region cur, prev_try, outer, *splice; + int i, min_region, max_region, eh_offset, cfun_last_region_number; + int num_regions; - case ERT_THROW: - n->u.throw.type = o->u.throw.type; + if (!ifun->eh->region_tree) + return 0; - default: - abort (); + /* Find the range of region numbers to be copied. The interface we + provide here mandates a single offset to find new number from old, + which means we must look at the numbers present, instead of the + count or something else. */ + if (copy_region > 0) + { + min_region = INT_MAX; + max_region = 0; + + cur = VEC_index (eh_region, ifun->eh->region_array, copy_region); + duplicate_eh_regions_0 (cur, &min_region, &max_region); + } + else + min_region = 1, max_region = ifun->eh->last_region_number; + num_regions = max_region - min_region + 1; + cfun_last_region_number = cfun->eh->last_region_number; + eh_offset = cfun_last_region_number + 1 - min_region; + + /* If we've not yet created a region array, do so now. */ + VEC_safe_grow (eh_region, gc, cfun->eh->region_array, + cfun_last_region_number + 1 + num_regions); + cfun->eh->last_region_number = max_region + eh_offset; + + /* We may have just allocated the array for the first time. + Make sure that element zero is null. */ + VEC_replace (eh_region, cfun->eh->region_array, 0, 0); + + /* Zero all entries in the range allocated. */ + memset (VEC_address (eh_region, cfun->eh->region_array) + + cfun_last_region_number + 1, 0, num_regions * sizeof (eh_region)); + + /* Locate the spot at which to insert the new tree. */ + if (outer_region > 0) + { + outer = VEC_index (eh_region, cfun->eh->region_array, outer_region); + splice = &outer->inner; + } + else + { + outer = NULL; + splice = &cfun->eh->region_tree; + } + while (*splice) + splice = &(*splice)->next_peer; + + /* Copy all the regions in the subtree. */ + if (copy_region > 0) + { + cur = VEC_index (eh_region, ifun->eh->region_array, copy_region); + *splice = duplicate_eh_regions_1 (cur, outer, eh_offset); + } + else + { + eh_region n; + + cur = ifun->eh->region_tree; + *splice = n = duplicate_eh_regions_1 (cur, outer, eh_offset); + while (cur->next_peer) + { + cur = cur->next_peer; + n = n->next_peer = duplicate_eh_regions_1 (cur, outer, eh_offset); + } } - if (o->label) - n->label = get_label_from_map (map, CODE_LABEL_NUMBER (o->label)); - if (o->resume) + /* Remap all the labels in the new regions. */ + for (i = cfun_last_region_number + 1; + VEC_iterate (eh_region, cfun->eh->region_array, i, cur); ++i) + if (cur && cur->tree_label) + cur->tree_label = map (cur->tree_label, data); + + /* Search for the containing ERT_TRY region to fix up + the prev_try short-cuts for ERT_CLEANUP regions. */ + prev_try = NULL; + if (outer_region > 0) + for (prev_try = VEC_index (eh_region, cfun->eh->region_array, outer_region); + prev_try && prev_try->type != ERT_TRY; + prev_try = prev_try->outer) + if (prev_try->type == ERT_MUST_NOT_THROW + || (prev_try->type == ERT_ALLOWED_EXCEPTIONS + && !prev_try->u.allowed.type_list)) + { + prev_try = NULL; + break; + } + + /* Remap all of the internal catch and cleanup linkages. Since we + duplicate entire subtrees, all of the referenced regions will have + been copied too. And since we renumbered them as a block, a simple + bit of arithmetic finds us the index for the replacement region. */ + for (i = cfun_last_region_number + 1; + VEC_iterate (eh_region, cfun->eh->region_array, i, cur); ++i) { - n->resume = map->insn_map[INSN_UID (o->resume)]; - if (n->resume == NULL) - abort (); + if (cur == NULL) + continue; + +#define REMAP(REG) \ + (REG) = VEC_index (eh_region, cfun->eh->region_array, \ + (REG)->region_number + eh_offset) + + switch (cur->type) + { + case ERT_TRY: + if (cur->u.try.catch) + REMAP (cur->u.try.catch); + if (cur->u.try.last_catch) + REMAP (cur->u.try.last_catch); + break; + + case ERT_CATCH: + if (cur->u.catch.next_catch) + REMAP (cur->u.catch.next_catch); + if (cur->u.catch.prev_catch) + REMAP (cur->u.catch.prev_catch); + break; + + case ERT_CLEANUP: + if (cur->u.cleanup.prev_try) + REMAP (cur->u.cleanup.prev_try); + else + cur->u.cleanup.prev_try = prev_try; + break; + + default: + break; + } + +#undef REMAP } - return n; + return eh_offset; } -static void -duplicate_eh_region_2 (struct eh_region *o, struct eh_region **n_array) +/* Return true if REGION_A is outer to REGION_B in IFUN. */ + +bool +eh_region_outer_p (struct function *ifun, int region_a, int region_b) { - struct eh_region *n = n_array[o->region_number]; + struct eh_region *rp_a, *rp_b; - 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; + gcc_assert (ifun->eh->last_region_number > 0); + gcc_assert (ifun->eh->region_tree); - 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; + rp_a = VEC_index (eh_region, ifun->eh->region_array, region_a); + rp_b = VEC_index (eh_region, ifun->eh->region_array, region_b); + gcc_assert (rp_a != NULL); + gcc_assert (rp_b != NULL); - default: - break; + do + { + if (rp_a == rp_b) + return true; + rp_b = rp_b->outer; } + while (rp_b); - 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]; + return false; } +/* Return region number of region that is outer to both if REGION_A and + REGION_B in IFUN. */ + int -duplicate_eh_regions (struct function *ifun, struct inline_remap *map) +eh_region_outermost (struct function *ifun, int region_a, int region_b) { - int ifun_last_region_number = ifun->eh->last_region_number; - struct eh_region **n_array, *root, *cur; - int i; + struct eh_region *rp_a, *rp_b; + sbitmap b_outer; - if (ifun_last_region_number == 0) - return 0; + gcc_assert (ifun->eh->last_region_number > 0); + gcc_assert (ifun->eh->region_tree); - n_array = xcalloc (ifun_last_region_number + 1, sizeof (*n_array)); + rp_a = VEC_index (eh_region, ifun->eh->region_array, region_a); + rp_b = VEC_index (eh_region, ifun->eh->region_array, region_b); + gcc_assert (rp_a != NULL); + gcc_assert (rp_b != NULL); - for (i = 1; i <= ifun_last_region_number; ++i) - { - cur = ifun->eh->region_array[i]; - if (!cur || cur->region_number != i) - continue; - n_array[i] = duplicate_eh_region_1 (cur, map); - } - for (i = 1; i <= ifun_last_region_number; ++i) - { - cur = ifun->eh->region_array[i]; - if (!cur || cur->region_number != i) - continue; - duplicate_eh_region_2 (cur, n_array); - } + b_outer = sbitmap_alloc (ifun->eh->last_region_number + 1); + sbitmap_zero (b_outer); - root = n_array[ifun->eh->region_tree->region_number]; - cur = cfun->eh->cur_region; - if (cur) + do { - struct eh_region *p = cur->inner; - if (p) - { - while (p->next_peer) - p = p->next_peer; - p->next_peer = root; - } - else - cur->inner = root; - - for (i = 1; i <= ifun_last_region_number; ++i) - if (n_array[i] && n_array[i]->outer == NULL) - n_array[i]->outer = cur; + SET_BIT (b_outer, rp_b->region_number); + rp_b = rp_b->outer; } - else + while (rp_b); + + do { - struct eh_region *p = cfun->eh->region_tree; - if (p) + if (TEST_BIT (b_outer, rp_a->region_number)) { - while (p->next_peer) - p = p->next_peer; - p->next_peer = root; + sbitmap_free (b_outer); + return rp_a->region_number; } - else - cfun->eh->region_tree = root; + rp_a = rp_a->outer; } + while (rp_a); - free (n_array); - - i = cfun->eh->last_region_number; - cfun->eh->last_region_number = i + ifun_last_region_number; - return i; + sbitmap_free (b_outer); + return -1; } - static int t2r_eq (const void *pentry, const void *pdata) { - tree entry = (tree) pentry; - tree data = (tree) pdata; + const_tree const entry = (const_tree) pentry; + const_tree const data = (const_tree) pdata; return TREE_PURPOSE (entry) == data; } @@ -1561,7 +1140,7 @@ t2r_eq (const void *pentry, const void *pdata) static hashval_t t2r_hash (const void *pentry) { - tree entry = (tree) pentry; + const_tree const entry = (const_tree) pentry; return TREE_HASH (TREE_PURPOSE (entry)); } @@ -1606,8 +1185,9 @@ struct ttypes_filter GTY(()) static int ttypes_filter_eq (const void *pentry, const void *pdata) { - const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry; - tree data = (tree) pdata; + const struct ttypes_filter *const entry + = (const struct ttypes_filter *) pentry; + const_tree const data = (const_tree) pdata; return entry->t == data; } @@ -1662,12 +1242,12 @@ add_ttypes_entry (htab_t ttypes_hash, tree type) { /* Filter value is a 1 based table index. */ - n = xmalloc (sizeof (*n)); + n = XNEW (struct ttypes_filter); n->t = type; - n->filter = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) + 1; + n->filter = VEC_length (tree, cfun->eh->ttype_data) + 1; *slot = n; - VARRAY_PUSH_TREE (cfun->eh->ttype_data, type); + VEC_safe_push (tree, gc, cfun->eh->ttype_data, type); } return n->filter; @@ -1690,17 +1270,28 @@ add_ehspec_entry (htab_t ehspec_hash, htab_t ttypes_hash, tree list) { /* Filter value is a -1 based byte index into a uleb128 buffer. */ - n = xmalloc (sizeof (*n)); + n = XNEW (struct ttypes_filter); 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. */ + /* Generate a 0 terminated list of filter values. */ 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 (targetm.arm_eabi_unwinder) + VARRAY_PUSH_TREE (cfun->eh->ehspec_data, TREE_VALUE (list)); + else + { + /* Look up each type in the list and encode its filter + value as a uleb128. */ + push_uleb128 (&cfun->eh->ehspec_data, + add_ttypes_entry (ttypes_hash, TREE_VALUE (list))); + } + } + if (targetm.arm_eabi_unwinder) + VARRAY_PUSH_TREE (cfun->eh->ehspec_data, NULL_TREE); + else + VARRAY_PUSH_UCHAR (cfun->eh->ehspec_data, 0); } return n->filter; @@ -1717,15 +1308,20 @@ assign_filter_values (void) int i; htab_t ttypes, ehspec; - VARRAY_TREE_INIT (cfun->eh->ttype_data, 16, "ttype_data"); - VARRAY_UCHAR_INIT (cfun->eh->ehspec_data, 64, "ehspec_data"); + cfun->eh->ttype_data = VEC_alloc (tree, gc, 16); + if (targetm.arm_eabi_unwinder) + VARRAY_TREE_INIT (cfun->eh->ehspec_data, 64, "ehspec_data"); + else + VARRAY_UCHAR_INIT (cfun->eh->ehspec_data, 64, "ehspec_data"); ttypes = htab_create (31, ttypes_filter_hash, ttypes_filter_eq, free); ehspec = htab_create (31, ehspec_filter_hash, ehspec_filter_eq, free); for (i = cfun->eh->last_region_number; i > 0; --i) { - struct eh_region *r = cfun->eh->region_array[i]; + struct eh_region *r; + + r = VEC_index (eh_region, cfun->eh->region_array, i); /* Mind we don't process a region more than once. */ if (!r || r->region_number != i) @@ -1747,7 +1343,7 @@ assign_filter_values (void) for (;tp_node; tp_node = TREE_CHAIN (tp_node)) { int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node)); - tree flt_node = build_int_2 (flt, 0); + tree flt_node = build_int_cst (NULL_TREE, flt); r->u.catch.filter_list = tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list); @@ -1758,7 +1354,7 @@ assign_filter_values (void) /* Get a filter value for the NULL list also since it will need an action record anyway. */ int flt = add_ttypes_entry (ttypes, NULL); - tree flt_node = build_int_2 (flt, 0); + tree flt_node = build_int_cst (NULL_TREE, flt); r->u.catch.filter_list = tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list); @@ -1789,15 +1385,18 @@ emit_to_new_bb_before (rtx seq, rtx insn) rtx last; basic_block bb; edge e; + edge_iterator ei; - /* If there happens to be an fallthru edge (possibly created by cleanup_cfg - call), we don't want it to go into newly created landing pad or other EH + /* If there happens to be a fallthru edge (possibly created by cleanup_cfg + call), we don't want it to go into newly created landing pad or other EH construct. */ - for (e = BLOCK_FOR_INSN (insn)->pred; e; e = e->pred_next) + for (ei = ei_start (BLOCK_FOR_INSN (insn)->preds); (e = ei_safe_edge (ei)); ) if (e->flags & EDGE_FALLTHRU) force_nonfallthru (e); + else + ei_next (&ei); last = emit_insn_before (seq, insn); - if (GET_CODE (last) == BARRIER) + if (BARRIER_P (last)) last = PREV_INSN (last); bb = create_basic_block (seq, last, BLOCK_FOR_INSN (insn)->prev_bb); update_bb_for_insn (bb); @@ -1815,9 +1414,10 @@ build_post_landing_pads (void) for (i = cfun->eh->last_region_number; i > 0; --i) { - struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *region; rtx seq; + region = VEC_index (eh_region, cfun->eh->region_array, i); /* Mind we don't process a region more than once. */ if (!region || region->region_number != i) continue; @@ -1859,7 +1459,8 @@ build_post_landing_pads (void) emit_cmp_and_jump_insns (cfun->eh->filter, GEN_INT (tree_low_cst (TREE_VALUE (flt_node), 0)), - EQ, NULL_RTX, word_mode, 0, c->label); + EQ, NULL_RTX, + targetm.eh_return_filter_mode (), 0, c->label); tp_node = TREE_CHAIN (tp_node); flt_node = TREE_CHAIN (flt_node); @@ -1891,7 +1492,8 @@ build_post_landing_pads (void) emit_cmp_and_jump_insns (cfun->eh->filter, GEN_INT (region->u.allowed.filter), - EQ, NULL_RTX, word_mode, 0, region->label); + EQ, NULL_RTX, + targetm.eh_return_filter_mode (), 0, region->label); /* We delay the generation of the _Unwind_Resume until we generate landing pads. We emit a marker here so as to get good control @@ -1917,7 +1519,7 @@ build_post_landing_pads (void) break; default: - abort (); + gcc_unreachable (); } } } @@ -1932,11 +1534,12 @@ connect_post_landing_pads (void) for (i = cfun->eh->last_region_number; i > 0; --i) { - struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *region; struct eh_region *outer; rtx seq; rtx barrier; + region = VEC_index (eh_region, cfun->eh->region_array, i); /* Mind we don't process a region more than once. */ if (!region || region->region_number != i) continue; @@ -1961,8 +1564,8 @@ connect_post_landing_pads (void) emit_jump (outer->post_landing_pad); src = BLOCK_FOR_INSN (region->resume); dest = BLOCK_FOR_INSN (outer->post_landing_pad); - while (src->succ) - remove_edge (src->succ); + while (EDGE_COUNT (src->succs) > 0) + remove_edge (EDGE_SUCC (src, 0)); e = make_edge (src, dest, 0); e->probability = REG_BR_PROB_BASE; e->count = src->count; @@ -1988,8 +1591,7 @@ connect_post_landing_pads (void) end_sequence (); barrier = emit_insn_before (seq, region->resume); /* Avoid duplicate barrier. */ - if (GET_CODE (barrier) != BARRIER) - abort (); + gcc_assert (BARRIER_P (barrier)); delete_insn (barrier); delete_insn (region->resume); @@ -2006,16 +1608,15 @@ static void dw2_build_landing_pads (void) { int i; - unsigned int j; for (i = cfun->eh->last_region_number; i > 0; --i) { - struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *region; rtx seq; basic_block bb; - bool clobbers_hard_regs = false; edge e; + region = VEC_index (eh_region, cfun->eh->region_array, i); /* Mind we don't process a region more than once. */ if (!region || region->region_number != i) continue; @@ -2042,34 +1643,11 @@ dw2_build_landing_pads (void) #endif { /* Nothing */ } - /* 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))); - clobbers_hard_regs = true; - } - } - - if (clobbers_hard_regs) - { - /* @@@ This is a kludge. Not all machine descriptions define a - blockage insn, but we must not allow the code we just generated - to be reordered by scheduling. So emit an ASM_INPUT to act as - blockage insn. */ - emit_insn (gen_rtx_ASM_INPUT (VOIDmode, "")); - } - emit_move_insn (cfun->eh->exc_ptr, gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0))); emit_move_insn (cfun->eh->filter, - gen_rtx_REG (word_mode, EH_RETURN_DATA_REGNO (1))); + gen_rtx_REG (targetm.eh_return_filter_mode (), + EH_RETURN_DATA_REGNO (1))); seq = get_insns (); end_sequence (); @@ -2110,7 +1688,7 @@ sjlj_find_directly_reachable_regions (struct sjlj_lp_info *lp_info) if (!note || INTVAL (XEXP (note, 0)) <= 0) continue; - region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; + region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); type_thrown = NULL_TREE; if (region->type == ERT_THROW) @@ -2152,7 +1730,8 @@ sjlj_assign_call_site_values (rtx dispatch_label, struct sjlj_lp_info *lp_info) 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]; + struct eh_region *r = VEC_index (eh_region, 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) @@ -2213,7 +1792,7 @@ sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) rtx note, before, p; /* Reset value tracking at extended basic block boundaries. */ - if (GET_CODE (insn) == CODE_LABEL) + if (LABEL_P (insn)) last_call_site = -2; if (! INSN_P (insn)) @@ -2225,7 +1804,7 @@ sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) /* 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 + if (CALL_P (insn) || (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))) this_call_site = -1; @@ -2238,7 +1817,7 @@ sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) if (INTVAL (XEXP (note, 0)) <= 0) continue; - region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; + region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); this_call_site = lp_info[region->region_number].call_site_index; } @@ -2247,7 +1826,7 @@ sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) /* Don't separate a call from it's argument loads. */ before = insn; - if (GET_CODE (insn) == CALL_INSN) + if (CALL_P (insn)) before = find_first_parameter_load (insn, NULL_RTX); start_sequence (); @@ -2268,6 +1847,7 @@ static void sjlj_emit_function_enter (rtx dispatch_label) { rtx fn_begin, fc, mem, seq; + bool fn_begin_outside_block; fc = cfun->eh->sjlj_fc; @@ -2296,17 +1876,15 @@ sjlj_emit_function_enter (rtx dispatch_label) #ifdef DONT_USE_BUILTIN_SETJMP { - rtx x, note; + rtx x; x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_RETURNS_TWICE, TYPE_MODE (integer_type_node), 1, plus_constant (XEXP (fc, 0), sjlj_fc_jbuf_ofs), Pmode); - note = emit_note (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, dispatch_label); + add_reg_br_prob_note (get_insns (), REG_BR_PROB_BASE/100); } #else expand_builtin_setjmp_setup (plus_constant (XEXP (fc, 0), sjlj_fc_jbuf_ofs), @@ -2323,23 +1901,20 @@ sjlj_emit_function_enter (rtx dispatch_label) do this in a block that is at loop level 0 and dominates all can_throw_internal instructions. */ + fn_begin_outside_block = true; 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 - || NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK)) - break; - if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG) - insert_insn_on_edge (seq, ENTRY_BLOCK_PTR->succ); - else - { - rtx last = BB_END (ENTRY_BLOCK_PTR->succ->dest); - for (; ; fn_begin = NEXT_INSN (fn_begin)) - if ((GET_CODE (fn_begin) == NOTE - && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG) - || fn_begin == last) + if (NOTE_P (fn_begin)) + { + if (NOTE_KIND (fn_begin) == NOTE_INSN_FUNCTION_BEG) break; - emit_insn_after (seq, fn_begin); - } + else if (NOTE_INSN_BASIC_BLOCK_P (fn_begin)) + fn_begin_outside_block = false; + } + + if (fn_begin_outside_block) + insert_insn_on_edge (seq, single_succ_edge (ENTRY_BLOCK_PTR)); + else + emit_insn_after (seq, fn_begin); } /* Call back from expand_function_end to know where we should put @@ -2356,6 +1931,7 @@ sjlj_emit_function_exit (void) { rtx seq; edge e; + edge_iterator ei; start_sequence (); @@ -2369,7 +1945,7 @@ sjlj_emit_function_exit (void) post-dominates all can_throw_internal instructions. This is the last possible moment. */ - for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next) + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds) if (e->flags & EDGE_FALLTHRU) break; if (e) @@ -2379,20 +1955,20 @@ sjlj_emit_function_exit (void) /* Figure out whether the place we are supposed to insert libcall is inside the last basic block or after it. In the other case we need to emit to edge. */ - if (e->src->next_bb != EXIT_BLOCK_PTR) - abort (); - for (insn = NEXT_INSN (BB_END (e->src)); insn; insn = NEXT_INSN (insn)) - if (insn == cfun->eh->sjlj_exit_after) - break; - if (insn) - insert_insn_on_edge (seq, e); - else + gcc_assert (e->src->next_bb == EXIT_BLOCK_PTR); + for (insn = BB_HEAD (e->src); ; insn = NEXT_INSN (insn)) { - insn = cfun->eh->sjlj_exit_after; - if (GET_CODE (insn) == CODE_LABEL) - insn = NEXT_INSN (insn); - emit_insn_after (seq, insn); + if (insn == cfun->eh->sjlj_exit_after) + { + if (LABEL_P (insn)) + insn = NEXT_INSN (insn); + emit_insn_after (seq, insn); + return; + } + if (insn == BB_END (e->src)) + break; } + insert_insn_on_edge (seq, e); } } @@ -2452,13 +2028,15 @@ sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info) emit_cmp_and_jump_insns (dispatch, GEN_INT (lp_info[i].dispatch_index), EQ, NULL_RTX, TYPE_MODE (integer_type_node), 0, - cfun->eh->region_array[i]->post_landing_pad); + ((struct eh_region *)VEC_index (eh_region, cfun->eh->region_array, i)) + ->post_landing_pad); } seq = get_insns (); end_sequence (); - before = cfun->eh->region_array[first_reachable]->post_landing_pad; + before = (((struct eh_region *)VEC_index (eh_region, cfun->eh->region_array, first_reachable)) + ->post_landing_pad); bb = emit_to_new_bb_before (seq, before); e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU); @@ -2471,8 +2049,7 @@ sjlj_build_landing_pads (void) { struct sjlj_lp_info *lp_info; - lp_info = xcalloc (cfun->eh->last_region_number + 1, - sizeof (struct sjlj_lp_info)); + lp_info = XCNEWVEC (struct sjlj_lp_info, cfun->eh->last_region_number + 1); if (sjlj_find_directly_reachable_regions (lp_info)) { @@ -2512,8 +2089,6 @@ finish_eh_generation (void) connect many of the handlers, and then type information will not be effective. Still, this is a win over previous implementations. */ - cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL); - /* These registers are used by the landing pads. Make sure they have been generated. */ get_exception_pointer (cfun); @@ -2534,31 +2109,35 @@ finish_eh_generation (void) /* We've totally changed the CFG. Start over. */ find_exception_handler_labels (); break_superblocks (); - if (USING_SJLJ_EXCEPTIONS) + if (USING_SJLJ_EXCEPTIONS + /* Kludge for Alpha/Tru64 (see alpha_gp_save_rtx). */ + || single_succ_edge (ENTRY_BLOCK_PTR)->insns.r) commit_edge_insertions (); FOR_EACH_BB (bb) { - edge e, next; + edge e; + edge_iterator ei; bool eh = false; - for (e = bb->succ; e; e = next) + for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) { - next = e->succ_next; if (e->flags & EDGE_EH) { remove_edge (e); eh = true; } + else + ei_next (&ei); } if (eh) rtl_make_eh_edge (NULL, bb, BB_END (bb)); } - cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL); } static hashval_t ehl_hash (const void *pentry) { - struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry; + const struct ehl_map_entry *const entry + = (const struct ehl_map_entry *) pentry; /* 2^32 * ((sqrt(5) - 1) / 2) */ const hashval_t scaled_golden_ratio = 0x9e3779b9; @@ -2568,8 +2147,10 @@ ehl_hash (const void *pentry) static int ehl_eq (const void *pentry, const void *pdata) { - struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry; - struct ehl_map_entry *data = (struct ehl_map_entry *) pdata; + const struct ehl_map_entry *const entry + = (const struct ehl_map_entry *) pentry; + const struct ehl_map_entry *const data + = (const struct ehl_map_entry *) pdata; return entry->label == data->label; } @@ -2591,8 +2172,7 @@ remove_exception_handler_label (rtx label) tmp.label = label; slot = (struct ehl_map_entry **) htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT); - if (! slot) - abort (); + gcc_assert (slot); htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot); } @@ -2612,12 +2192,16 @@ remove_eh_handler (struct eh_region *region) list of alternate numbers by which we are known. */ outer = region->outer; - cfun->eh->region_array[region->region_number] = outer; + VEC_replace (eh_region, cfun->eh->region_array, region->region_number, outer); if (region->aka) { - int i; - EXECUTE_IF_SET_IN_BITMAP (region->aka, 0, i, - { cfun->eh->region_array[i] = outer; }); + unsigned i; + bitmap_iterator bi; + + EXECUTE_IF_SET_IN_BITMAP (region->aka, 0, i, bi) + { + VEC_replace (eh_region, cfun->eh->region_array, i, outer); + } } if (outer) @@ -2625,7 +2209,7 @@ remove_eh_handler (struct eh_region *region) if (!outer->aka) outer->aka = BITMAP_GGC_ALLOC (); if (region->aka) - bitmap_a_or_b (outer->aka, outer->aka, region->aka); + bitmap_ior_into (outer->aka, region->aka); bitmap_set_bit (outer->aka, region->region_number); } @@ -2663,8 +2247,7 @@ remove_eh_handler (struct eh_region *region) try->type == ERT_CATCH; try = try->next_peer) continue; - if (try->type != ERT_TRY) - abort (); + gcc_assert (try->type == ERT_TRY); next = region->u.catch.next_catch; prev = region->u.catch.prev_catch; @@ -2751,7 +2334,9 @@ for_each_eh_region (void (*callback) (struct eh_region *)) int i, n = cfun->eh->last_region_number; for (i = 1; i <= n; ++i) { - struct eh_region *region = cfun->eh->region_array[i]; + struct eh_region *region; + + region = VEC_index (eh_region, cfun->eh->region_array, i); if (region) (*callback) (region); } @@ -2772,7 +2357,7 @@ struct reachable_info /* A subroutine of reachable_next_level. Return true if TYPE, or a base class of TYPE, is in HANDLED. */ -int +static int check_handled (tree handled, tree type) { tree t; @@ -2962,8 +2547,13 @@ reachable_next_level (struct eh_region *region, tree type_thrown, /* 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->saw_any_handlers) + the call is made by the runtime. + + Before inlining, do not perform this optimization. We may + inline a subroutine that contains handlers, and that will + change the value of saw_any_handlers. */ + + if ((info && info->saw_any_handlers) || !cfun->after_inlining) { add_reachable_handler (info, region, region); return RNL_CAUGHT; @@ -2972,13 +2562,13 @@ reachable_next_level (struct eh_region *region, tree type_thrown, return RNL_BLOCKED; case ERT_THROW: - case ERT_FIXUP: case ERT_UNKNOWN: /* Shouldn't see these here. */ + gcc_unreachable (); break; + default: + gcc_unreachable (); } - - abort (); } /* Invoke CALLBACK on each region reachable from REGION_NUMBER. */ @@ -2996,7 +2586,7 @@ foreach_reachable_handler (int region_number, bool is_resx, info.callback = callback; info.callback_data = callback_data; - region = cfun->eh->region_array[region_number]; + region = VEC_index (eh_region, cfun->eh->region_array, region_number); type_thrown = NULL_TREE; if (is_resx) @@ -3053,7 +2643,7 @@ reachable_handlers (rtx insn) rtx handlers = NULL; int region_number; - if (GET_CODE (insn) == JUMP_INSN + if (JUMP_P (insn) && GET_CODE (PATTERN (insn)) == RESX) { region_number = XINT (PATTERN (insn), 0); @@ -3080,15 +2670,17 @@ reachable_handlers (rtx insn) within the function. */ bool -can_throw_internal_1 (int region_number) +can_throw_internal_1 (int region_number, bool is_resx) { struct eh_region *region; tree type_thrown; - region = cfun->eh->region_array[region_number]; + region = VEC_index (eh_region, cfun->eh->region_array, region_number); type_thrown = NULL_TREE; - if (region->type == ERT_THROW) + if (is_resx) + region = region->outer; + else if (region->type == ERT_THROW) { type_thrown = region->u.throw.type; region = region->outer; @@ -3110,14 +2702,19 @@ can_throw_internal_1 (int region_number) } bool -can_throw_internal (rtx insn) +can_throw_internal (const_rtx insn) { rtx note; if (! INSN_P (insn)) return false; - if (GET_CODE (insn) == INSN + if (JUMP_P (insn) + && GET_CODE (PATTERN (insn)) == RESX + && XINT (PATTERN (insn), 0) > 0) + return can_throw_internal_1 (XINT (PATTERN (insn), 0), true); + + if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) insn = XVECEXP (PATTERN (insn), 0, 0); @@ -3126,22 +2723,24 @@ can_throw_internal (rtx insn) if (!note || INTVAL (XEXP (note, 0)) <= 0) return false; - return can_throw_internal_1 (INTVAL (XEXP (note, 0))); + return can_throw_internal_1 (INTVAL (XEXP (note, 0)), false); } /* Determine if the given INSN can throw an exception that is visible outside the function. */ bool -can_throw_external_1 (int region_number) +can_throw_external_1 (int region_number, bool is_resx) { struct eh_region *region; tree type_thrown; - region = cfun->eh->region_array[region_number]; + region = VEC_index (eh_region, cfun->eh->region_array, region_number); type_thrown = NULL_TREE; - if (region->type == ERT_THROW) + if (is_resx) + region = region->outer; + else if (region->type == ERT_THROW) { type_thrown = region->u.throw.type; region = region->outer; @@ -3157,14 +2756,19 @@ can_throw_external_1 (int region_number) } bool -can_throw_external (rtx insn) +can_throw_external (const_rtx insn) { rtx note; if (! INSN_P (insn)) return false; - if (GET_CODE (insn) == INSN + if (JUMP_P (insn) + && GET_CODE (PATTERN (insn)) == RESX + && XINT (PATTERN (insn), 0) > 0) + return can_throw_external_1 (XINT (PATTERN (insn), 0), true); + + if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) insn = XVECEXP (PATTERN (insn), 0, 0); @@ -3176,24 +2780,30 @@ can_throw_external (rtx insn) 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 + return (CALL_P (insn) || (flag_non_call_exceptions && may_trap_p (PATTERN (insn)))); } if (INTVAL (XEXP (note, 0)) <= 0) return false; - return can_throw_external_1 (INTVAL (XEXP (note, 0))); + return can_throw_external_1 (INTVAL (XEXP (note, 0)), false); } -/* Set current_function_nothrow and cfun->all_throwers_are_sibcalls. */ +/* Set TREE_NOTHROW and cfun->all_throwers_are_sibcalls. */ -void +unsigned int set_nothrow_function_flags (void) { rtx insn; - current_function_nothrow = 1; + /* If we don't know that this implementation of the function will + actually be used, then we must not set TREE_NOTHROW, since + callers must not assume that this function does not throw. */ + if (DECL_REPLACEABLE_P (current_function_decl)) + return 0; + + TREE_NOTHROW (current_function_decl) = 1; /* Assume cfun->all_throwers_are_sibcalls until we encounter something that can throw an exception. We specifically exempt @@ -3204,17 +2814,17 @@ set_nothrow_function_flags (void) cfun->all_throwers_are_sibcalls = 1; if (! flag_exceptions) - return; + return 0; for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) if (can_throw_external (insn)) { - current_function_nothrow = 0; + TREE_NOTHROW (current_function_decl) = 0; - if (GET_CODE (insn) != CALL_INSN || !SIBLING_CALL_P (insn)) + if (!CALL_P (insn) || !SIBLING_CALL_P (insn)) { cfun->all_throwers_are_sibcalls = 0; - return; + return 0; } } @@ -3222,16 +2832,34 @@ set_nothrow_function_flags (void) insn = XEXP (insn, 1)) if (can_throw_external (insn)) { - current_function_nothrow = 0; + TREE_NOTHROW (current_function_decl) = 0; - if (GET_CODE (insn) != CALL_INSN || !SIBLING_CALL_P (insn)) + if (!CALL_P (insn) || !SIBLING_CALL_P (insn)) { cfun->all_throwers_are_sibcalls = 0; - return; + return 0; } } + return 0; } +struct tree_opt_pass pass_set_nothrow_function_flags = +{ + NULL, /* name */ + NULL, /* gate */ + set_nothrow_function_flags, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + 0 /* letter */ +}; + /* Various hooks for unwind library. */ @@ -3243,7 +2871,7 @@ expand_builtin_unwind_init (void) { /* Set this so all the registers get saved in our frame; we need to be able to copy the saved values for any registers from frames we unwind. */ - current_function_has_nonlocal_label = 1; + current_function_saves_all_registers = 1; #ifdef SETUP_FRAME_ADDRESSES SETUP_FRAME_ADDRESSES (); @@ -3251,14 +2879,14 @@ expand_builtin_unwind_init (void) } rtx -expand_builtin_eh_return_data_regno (tree arglist) +expand_builtin_eh_return_data_regno (tree exp) { - tree which = TREE_VALUE (arglist); + tree which = CALL_EXPR_ARG (exp, 0); unsigned HOST_WIDE_INT iwhich; if (TREE_CODE (which) != INTEGER_CST) { - error ("argument of `__builtin_eh_return_regno' must be constant"); + error ("argument of %<__builtin_eh_return_regno%> must be constant"); return constm1_rtx; } @@ -3282,7 +2910,7 @@ expand_builtin_eh_return_data_regno (tree arglist) rtx expand_builtin_extract_return_addr (tree addr_tree) { - rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); + rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL); if (GET_MODE (addr) != Pmode && GET_MODE (addr) != VOIDmode) @@ -3314,7 +2942,7 @@ expand_builtin_extract_return_addr (tree addr_tree) rtx expand_builtin_frob_return_addr (tree addr_tree) { - rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0); + rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, EXPAND_NORMAL); addr = convert_memory_address (Pmode, addr); @@ -3336,7 +2964,8 @@ expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED, rtx tmp; #ifdef EH_RETURN_STACKADJ_RTX - tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0); + tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, + VOIDmode, EXPAND_NORMAL); tmp = convert_memory_address (Pmode, tmp); if (!cfun->eh->ehr_stackadj) cfun->eh->ehr_stackadj = copy_to_reg (tmp); @@ -3344,7 +2973,8 @@ expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED, emit_move_insn (cfun->eh->ehr_stackadj, tmp); #endif - tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0); + tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, + VOIDmode, EXPAND_NORMAL); tmp = convert_memory_address (Pmode, tmp); if (!cfun->eh->ehr_handler) cfun->eh->ehr_handler = copy_to_reg (tmp); @@ -3402,7 +3032,7 @@ expand_eh_return (void) rtx expand_builtin_extend_pointer (tree addr_tree) { - rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0); + rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, EXPAND_NORMAL); int extend; #ifdef POINTERS_EXTEND_UNSIGNED @@ -3570,7 +3200,7 @@ collect_one_action_chain (htab_t ar_hash, struct eh_region *region) Add a cleanup action to the chain to catch these. */ else if (next <= 0) next = add_action_record (ar_hash, 0, 0); - + return add_action_record (ar_hash, region->u.allowed.filter, next); case ERT_MUST_NOT_THROW: @@ -3587,7 +3217,7 @@ collect_one_action_chain (htab_t ar_hash, struct eh_region *region) return collect_one_action_chain (ar_hash, region->outer); default: - abort (); + gcc_unreachable (); } } @@ -3618,7 +3248,7 @@ add_call_site (rtx landing_pad, int action) The new note numbers will not refer to region numbers, but instead to call site entries. */ -void +unsigned int convert_to_eh_region_ranges (void) { rtx insn, iter, note; @@ -3630,7 +3260,7 @@ convert_to_eh_region_ranges (void) int call_site = 0; if (USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL) - return; + return 0; VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data"); @@ -3644,14 +3274,14 @@ convert_to_eh_region_ranges (void) rtx this_landing_pad; insn = iter; - if (GET_CODE (insn) == INSN + if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE) insn = XVECEXP (PATTERN (insn), 0, 0); note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); if (!note) { - if (! (GET_CODE (insn) == CALL_INSN + if (! (CALL_P (insn) || (flag_non_call_exceptions && may_trap_p (PATTERN (insn))))) continue; @@ -3662,7 +3292,7 @@ convert_to_eh_region_ranges (void) { if (INTVAL (XEXP (note, 0)) <= 0) continue; - region = cfun->eh->region_array[INTVAL (XEXP (note, 0))]; + region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); this_action = collect_one_action_chain (ar_hash, region); } @@ -3739,8 +3369,26 @@ convert_to_eh_region_ranges (void) } htab_delete (ar_hash); + return 0; } +struct tree_opt_pass pass_convert_to_eh_region_ranges = +{ + "eh-ranges", /* name */ + NULL, /* gate */ + convert_to_eh_region_ranges, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func, /* todo_flags_finish */ + 0 /* letter */ +}; + static void push_uleb128 (varray_type *data_area, unsigned int value) @@ -3814,8 +3462,6 @@ sjlj_size_of_call_site_table (void) static void dw2_output_call_site_table (void) { - const char *const function_start_lab - = IDENTIFIER_POINTER (current_function_func_begin_label); int n = cfun->eh->call_site_data_used; int i; @@ -3838,21 +3484,25 @@ dw2_output_call_site_table (void) /* ??? 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, + dw2_asm_output_delta_uleb128 (reg_start_lab, + current_function_func_begin_label, "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, + dw2_asm_output_delta_uleb128 (landing_pad_lab, + current_function_func_begin_label, "landing pad"); else dw2_asm_output_data_uleb128 (0, "landing pad"); #else - dw2_asm_output_delta (4, reg_start_lab, function_start_lab, + dw2_asm_output_delta (4, reg_start_lab, + current_function_func_begin_label, "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, + dw2_asm_output_delta (4, landing_pad_lab, + current_function_func_begin_label, "landing pad"); else dw2_asm_output_data (4, 0, "landing pad"); @@ -3881,35 +3531,110 @@ sjlj_output_call_site_table (void) call_site_base += n; } -/* Tell assembler to switch to the section for the exception handling - table. */ +#ifndef TARGET_UNWIND_INFO +/* Switch to the section that should be used for exception tables. */ -void -default_exception_section (void) +static void +switch_to_exception_section (const char * ARG_UNUSED (fnname)) { - if (targetm.have_named_sections) + section *s; + + if (exception_section) + s = exception_section; + else { - int flags; -#ifdef HAVE_LD_RO_RW_SECTION_MIXING - int tt_format = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1); - - flags = (! flag_pic - || ((tt_format & 0x70) != DW_EH_PE_absptr - && (tt_format & 0x70) != DW_EH_PE_aligned)) - ? 0 : SECTION_WRITE; -#else - flags = SECTION_WRITE; + /* Compute the section and cache it into exception_section, + unless it depends on the function name. */ + if (targetm.have_named_sections) + { + int flags; + + if (EH_TABLES_CAN_BE_READ_ONLY) + { + int tt_format = + ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1); + flags = ((! flag_pic + || ((tt_format & 0x70) != DW_EH_PE_absptr + && (tt_format & 0x70) != DW_EH_PE_aligned)) + ? 0 : SECTION_WRITE); + } + else + flags = SECTION_WRITE; + +#ifdef HAVE_LD_EH_GC_SECTIONS + if (flag_function_sections) + { + char *section_name = xmalloc (strlen (fnname) + 32); + sprintf (section_name, ".gcc_except_table.%s", fnname); + s = get_section (section_name, flags, NULL); + free (section_name); + } + else +#endif + exception_section + = s = get_section (".gcc_except_table", flags, NULL); + } + else + exception_section + = s = flag_pic ? data_section : readonly_data_section; + } + + switch_to_section (s); +} #endif - named_section_flags (".gcc_except_table", flags); + + +/* Output a reference from an exception table to the type_info object TYPE. + TT_FORMAT and TT_FORMAT_SIZE describe the DWARF encoding method used for + the value. */ + +static void +output_ttype (tree type, int tt_format, int tt_format_size) +{ + rtx value; + bool public = true; + + if (type == NULL_TREE) + value = const0_rtx; + else + { + struct varpool_node *node; + + type = lookup_type_for_runtime (type); + value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER); + + /* Let cgraph know that the rtti decl is used. Not all of the + paths below go through assemble_integer, which would take + care of this for us. */ + STRIP_NOPS (type); + if (TREE_CODE (type) == ADDR_EXPR) + { + type = TREE_OPERAND (type, 0); + if (TREE_CODE (type) == VAR_DECL) + { + node = varpool_node (type); + if (node) + varpool_mark_needed_node (node); + public = TREE_PUBLIC (type); + } + } + else + gcc_assert (TREE_CODE (type) == INTEGER_CST); } - else if (flag_pic) - data_section (); + + /* Allow the target to override the type table entry format. */ + if (targetm.asm_out.ttype (value)) + return; + + if (tt_format == DW_EH_PE_absptr || tt_format == DW_EH_PE_aligned) + assemble_integer (value, tt_format_size, + tt_format_size * BITS_PER_UNIT, 1); else - readonly_data_section (); + dw2_asm_output_encoded_addr_rtx (tt_format, value, public, NULL); } void -output_function_exception_table (void) +output_function_exception_table (const char * ARG_UNUSED (fnname)) { int tt_format, cs_format, lp_format, i, n; #ifdef HAVE_AS_LEB128 @@ -3926,17 +3651,24 @@ output_function_exception_table (void) if (! cfun->uses_eh_lsda) return; -#ifdef IA64_UNWIND_INFO + if (eh_personality_libfunc) + assemble_external_libcall (eh_personality_libfunc); + +#ifdef TARGET_UNWIND_INFO + /* TODO: Move this into target file. */ fputs ("\t.personality\t", asm_out_file); output_addr_const (asm_out_file, eh_personality_libfunc); fputs ("\n\t.handlerdata\n", asm_out_file); /* Note that varasm still thinks we're in the function's code section. The ".endp" directive that will immediately follow will take us back. */ #else - targetm.asm_out.exception_section (); + switch_to_exception_section (fnname); #endif - have_tt_data = (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) > 0 + /* If the target wants a label to begin the table, emit it here. */ + targetm.asm_out.except_table_label (asm_out_file); + + have_tt_data = (VEC_length (tree, cfun->eh->ttype_data) > 0 || VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) > 0); /* Indicate the format of the @TType entries. */ @@ -3999,7 +3731,7 @@ output_function_exception_table (void) 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) + + (VEC_length (tree, cfun->eh->ttype_data) * tt_format_size)); disp = after_disp; @@ -4061,41 +3793,11 @@ output_function_exception_table (void) if (have_tt_data) assemble_align (tt_format_size * BITS_PER_UNIT); - i = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data); + i = VEC_length (tree, cfun->eh->ttype_data); while (i-- > 0) { - tree type = VARRAY_TREE (cfun->eh->ttype_data, i); - rtx value; - - if (type == NULL_TREE) - value = const0_rtx; - else - { - struct cgraph_varpool_node *node; - - type = lookup_type_for_runtime (type); - value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER); - - /* Let cgraph know that the rtti decl is used. Not all of the - paths below go through assemble_integer, which would take - care of this for us. */ - STRIP_NOPS (type); - if (TREE_CODE (type) == ADDR_EXPR) - { - type = TREE_OPERAND (type, 0); - node = cgraph_varpool_node (type); - if (node) - cgraph_varpool_mark_needed_node (node); - } - else if (TREE_CODE (type) != INTEGER_CST) - abort (); - } - - if (tt_format == DW_EH_PE_absptr || tt_format == DW_EH_PE_aligned) - assemble_integer (value, tt_format_size, - tt_format_size * BITS_PER_UNIT, 1); - else - dw2_asm_output_encoded_addr_rtx (tt_format, value, NULL); + tree type = VEC_index (tree, cfun->eh->ttype_data, i); + output_ttype (type, tt_format, tt_format_size); } #ifdef HAVE_AS_LEB128 @@ -4106,10 +3808,208 @@ output_function_exception_table (void) /* ??? 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")); + { + if (targetm.arm_eabi_unwinder) + { + tree type = VARRAY_TREE (cfun->eh->ehspec_data, i); + output_ttype (type, tt_format, tt_format_size); + } + else + dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->ehspec_data, i), + (i ? NULL : "Exception specification table")); + } + + switch_to_section (current_function_section ()); +} + +void +set_eh_throw_stmt_table (struct function *fun, struct htab *table) +{ + fun->eh->throw_stmt_table = table; +} + +htab_t +get_eh_throw_stmt_table (struct function *fun) +{ + return fun->eh->throw_stmt_table; +} + +/* Dump EH information to OUT. */ +void +dump_eh_tree (FILE *out, struct function *fun) +{ + struct eh_region *i; + int depth = 0; + static const char * const type_name[] = {"unknown", "cleanup", "try", "catch", + "allowed_exceptions", "must_not_throw", + "throw"}; + + i = fun->eh->region_tree; + if (! i) + return; + + fprintf (out, "Eh tree:\n"); + while (1) + { + fprintf (out, " %*s %i %s", depth * 2, "", + i->region_number, type_name [(int)i->type]); + if (i->tree_label) + { + fprintf (out, " tree_label:"); + print_generic_expr (out, i->tree_label, 0); + } + fprintf (out, "\n"); + /* If there are sub-regions, process them. */ + if (i->inner) + i = i->inner, depth++; + /* 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; + depth--; + if (i == NULL) + return; + } while (i->next_peer == NULL); + i = i->next_peer; + } + } +} + +/* Verify some basic invariants on EH datastructures. Could be extended to + catch more. */ +void +verify_eh_tree (struct function *fun) +{ + struct eh_region *i, *outer = NULL; + bool err = false; + int nvisited = 0; + int count = 0; + int j; + int depth = 0; + + i = fun->eh->region_tree; + if (! i) + return; + for (j = fun->eh->last_region_number; j > 0; --j) + if ((i = VEC_index (eh_region, cfun->eh->region_array, j))) + { + count++; + if (i->region_number != j) + { + error ("region_array is corrupted for region %i", i->region_number); + err = true; + } + } + + while (1) + { + if (VEC_index (eh_region, cfun->eh->region_array, i->region_number) != i) + { + error ("region_array is corrupted for region %i", i->region_number); + err = true; + } + if (i->outer != outer) + { + error ("outer block of region %i is wrong", i->region_number); + err = true; + } + if (i->may_contain_throw && outer && !outer->may_contain_throw) + { + error ("region %i may contain throw and is contained in region that may not", + i->region_number); + err = true; + } + if (depth < 0) + { + error ("negative nesting depth of region %i", i->region_number); + err = true; + } + nvisited ++; + /* If there are sub-regions, process them. */ + if (i->inner) + outer = i, i = i->inner, depth++; + /* 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; + depth--; + if (i == NULL) + { + if (depth != -1) + { + error ("tree list ends on depth %i", depth + 1); + err = true; + } + if (count != nvisited) + { + error ("array does not match the region tree"); + err = true; + } + if (err) + { + dump_eh_tree (stderr, fun); + internal_error ("verify_eh_tree failed"); + } + return; + } + outer = i->outer; + } while (i->next_peer == NULL); + i = i->next_peer; + } + } +} + +/* Initialize unwind_resume_libfunc. */ + +void +default_init_unwind_resume_libfunc (void) +{ + /* The default c++ routines aren't actually c++ specific, so use those. */ + unwind_resume_libfunc = + init_one_libfunc ( USING_SJLJ_EXCEPTIONS ? "_Unwind_SjLj_Resume" + : "_Unwind_Resume"); +} + + +static bool +gate_handle_eh (void) +{ + return doing_eh (0); +} - function_section (current_function_decl); +/* Complete generation of exception handling code. */ +static unsigned int +rest_of_handle_eh (void) +{ + cleanup_cfg (CLEANUP_NO_INSN_DEL); + finish_eh_generation (); + cleanup_cfg (CLEANUP_NO_INSN_DEL); + return 0; } +struct tree_opt_pass pass_rtl_eh = +{ + "eh", /* name */ + gate_handle_eh, /* gate */ + rest_of_handle_eh, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_JUMP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_func, /* todo_flags_finish */ + 'h' /* letter */ +}; + #include "gt-except.h"