diff options
Diffstat (limited to 'gcc-4.2.1-5666.3/gcc/except.c')
-rw-r--r-- | gcc-4.2.1-5666.3/gcc/except.c | 4033 |
1 files changed, 0 insertions, 4033 deletions
diff --git a/gcc-4.2.1-5666.3/gcc/except.c b/gcc-4.2.1-5666.3/gcc/except.c deleted file mode 100644 index 862b85bfc..000000000 --- a/gcc-4.2.1-5666.3/gcc/except.c +++ /dev/null @@ -1,4033 +0,0 @@ -/* Implements exception handling. - Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. - Contributed by Mike Stump <mrs@cygnus.com>. - -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 -version. - -GCC 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 GCC; see the file COPYING. If not, write to the Free -Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. */ - - -/* An exception is an event that can be signaled from within a - function. This event can then be "caught" or "trapped" by the - callers of this function. This potentially allows program flow to - be transferred to any arbitrary code associated with a function call - several levels up the stack. - - The intended use for this mechanism is for signaling "exceptional - events" in an out-of-band fashion, hence its name. The C++ language - (and many other OO-styled or functional languages) practically - requires such a mechanism, as otherwise it becomes very difficult - or even impossible to signal failure conditions in complex - situations. The traditional C++ example is when an error occurs in - the process of constructing an object; without such a mechanism, it - is impossible to signal that the error occurs without adding global - state variables and error checks around every object construction. - - The act of causing this event to occur is referred to as "throwing - an exception". (Alternate terms include "raising an exception" or - "signaling an exception".) The term "throw" is used because control - is returned to the callers of the function that is signaling the - exception, and thus there is the concept of "throwing" the - exception up the call stack. - - [ Add updated documentation on how to use this. ] */ - - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "function.h" -#include "expr.h" -#include "libfuncs.h" -#include "insn-config.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 "dwarf2.h" -#include "toplev.h" -#include "hashtab.h" -#include "intl.h" -#include "ggc.h" -#include "tm_p.h" -#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. */ -#ifndef EH_RETURN_DATA_REGNO -#define EH_RETURN_DATA_REGNO(N) INVALID_REGNUM -#endif - - -/* Protect cleanup actions with must-not-throw regions, with a call - to the given failure handler. */ -tree (*lang_protect_cleanup_actions) (void); - -/* Return true if type A catches type B. */ -int (*lang_eh_type_covers) (tree a, tree b); - -/* Map a type to a runtime object to match type. */ -tree (*lang_eh_runtime_type) (tree); - -/* A hash table of label to region number. */ - -struct ehl_map_entry GTY(()) -{ - rtx label; - struct eh_region *region; -}; - -static GTY(()) int call_site_base; -static GTY ((param_is (union tree_node))) - htab_t type_to_runtime_map; - -/* Describe the SjLj_Function_Context structure. */ -static GTY(()) 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 GTY(()) -{ - /* The immediately surrounding region. */ - struct eh_region *outer; - - /* The list of immediately contained regions. */ - struct eh_region *inner; - struct eh_region *next_peer; - - /* An identifier for this region. */ - int region_number; - - /* When a region is deleted, its parents inherit the REG_EH_REGION - numbers already assigned. */ - bitmap aka; - - /* 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 - } type; - - /* Holds the action to perform based on the preceding type. */ - union eh_region_u { - /* A list of catch blocks, a surrounding try block, - and the label for continuing after a catch. */ - struct eh_region_u_try { - struct eh_region *catch; - struct eh_region *last_catch; - } GTY ((tag ("ERT_TRY"))) try; - - /* The list through the catch handlers, the list of type objects - matched, and the list of associated filters. */ - struct eh_region_u_catch { - struct eh_region *next_catch; - struct eh_region *prev_catch; - tree type_list; - tree filter_list; - } GTY ((tag ("ERT_CATCH"))) catch; - - /* A tree_list of allowed types. */ - struct eh_region_u_allowed { - tree type_list; - int filter; - } GTY ((tag ("ERT_ALLOWED_EXCEPTIONS"))) allowed; - - /* The type given by a call to "throw foo();", or discovered - for a throw. */ - struct eh_region_u_throw { - tree type; - } GTY ((tag ("ERT_THROW"))) throw; - - /* Retain the cleanup expression even after expansion so that - we can match up fixup regions. */ - struct eh_region_u_cleanup { - struct eh_region *prev_try; - } GTY ((tag ("ERT_CLEANUP"))) cleanup; - } GTY ((desc ("%0.type"))) u; - - /* Entry point for this region's handler before landing pads are built. */ - rtx label; - tree tree_label; - - /* Entry point for this region's handler from the runtime eh library. */ - rtx landing_pad; - - /* Entry point for this region's handler from an inner region. */ - rtx post_landing_pad; - - /* The RESX insn for handing off control to the next outermost handler, - if appropriate. */ - rtx resume; - - /* True if something in this region may throw. */ - 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(()) -{ - /* The tree of all regions for this function. */ - struct eh_region *region_tree; - - /* The same information as an indexable array. */ - VEC(eh_region,gc) *region_array; - - /* The most recently open region. */ - struct eh_region *cur_region; - - /* This is the region for which we are processing catch blocks. */ - struct eh_region *try_region; - - rtx filter; - rtx exc_ptr; - - int built_landing_pads; - int last_region_number; - - VEC(tree,gc) *ttype_data; - varray_type ehspec_data; - varray_type action_record_data; - - htab_t GTY ((param_is (struct ehl_map_entry))) exception_handler_label_map; - - struct call_site_record * GTY ((length ("%h.call_site_data_used"))) - 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; - - 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 void remove_unreachable_regions (rtx); - -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 *); -static hashval_t ehspec_filter_hash (const void *); -static int add_ttypes_entry (htab_t, tree); -static int add_ehspec_entry (htab_t, htab_t, tree); -static void assign_filter_values (void); -static void build_post_landing_pads (void); -static void connect_post_landing_pads (void); -static void dw2_build_landing_pads (void); - -struct sjlj_lp_info; -static bool sjlj_find_directly_reachable_regions (struct sjlj_lp_info *); -static void sjlj_assign_call_site_values (rtx, struct sjlj_lp_info *); -static void sjlj_mark_call_sites (struct sjlj_lp_info *); -/* APPLE LOCAL begin ARM 5051776 */ -static rtx sjlj_generate_setjmp_sequence (rtx, bool); -static void sjlj_emit_function_setjmps (rtx); -/* APPLE LOCAL end ARM 5051776 */ -static void sjlj_emit_function_exit (void); -static void sjlj_emit_dispatch_table (rtx, struct sjlj_lp_info *); -static void sjlj_build_landing_pads (void); - -static hashval_t ehl_hash (const void *); -static int ehl_eq (const void *, const void *); -static void add_ehl_entry (rtx, struct eh_region *); -static void remove_exception_handler_label (rtx); -static void remove_eh_handler (struct eh_region *); -static int for_each_eh_label_1 (void **, void *); - -/* 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 -}; - -struct reachable_info; -static enum reachable_code reachable_next_level (struct eh_region *, tree, - struct reachable_info *); - -static int action_record_eq (const void *, const void *); -static hashval_t action_record_hash (const void *); -static int add_action_record (htab_t, int, int); -static int collect_one_action_chain (htab_t, struct eh_region *); -static int add_call_site (rtx, int); - -static void push_uleb128 (varray_type *, unsigned int); -static void push_sleb128 (varray_type *, int); -#ifndef HAVE_AS_LEB128 -static int dw2_size_of_call_site_table (void); -static int sjlj_size_of_call_site_table (void); -#endif -static void dw2_output_call_site_table (void); -static void sjlj_output_call_site_table (void); - - -/* Routine to see if exception handling is turned on. - DO_WARN is nonzero if we want to inform the user that exception - handling is turned off. - - This is used to ensure that -fexceptions has been specified if the - compiler tries to use any exception-specific functions. */ - -int -doing_eh (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; -} - - -void -init_eh (void) -{ - if (! flag_exceptions) - return; - - type_to_runtime_map = htab_create_ggc (31, t2r_hash, t2r_eq, NULL); - - /* 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; - - sjlj_fc_type_node = lang_hooks.types.make_type (RECORD_TYPE); - - 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; - - f_cs = build_decl (FIELD_DECL, get_identifier ("__call_site"), - integer_type_node); - DECL_FIELD_CONTEXT (f_cs) = sjlj_fc_type_node; - - 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); - DECL_FIELD_CONTEXT (f_data) = sjlj_fc_type_node; - - f_per = build_decl (FIELD_DECL, get_identifier ("__personality"), - ptr_type_node); - DECL_FIELD_CONTEXT (f_per) = sjlj_fc_type_node; - - f_lsda = build_decl (FIELD_DECL, get_identifier ("__lsda"), - ptr_type_node); - DECL_FIELD_CONTEXT (f_lsda) = sjlj_fc_type_node; - -#ifdef DONT_USE_BUILTIN_SETJMP -#ifdef JMP_BUF_SIZE - 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_cst (NULL_TREE, FIRST_PSEUDO_REGISTER + 2 - 1); -#endif -#else - /* builtin_setjmp takes a pointer to 5 words. */ - 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); - 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); - } -} - -void -init_eh_for_function (void) -{ - cfun->eh = ggc_alloc_cleared (sizeof (struct eh_status)); -} - -/* Routines to generate the exception tree somewhat directly. - These are used from tree-eh.c when processing exception related - nodes during tree optimization. */ - -static struct eh_region * -gen_eh_region (enum eh_region_type type, struct eh_region *outer) -{ - struct eh_region *new; - -#ifdef ENABLE_CHECKING - gcc_assert (doing_eh (0)); -#endif - - /* Insert a new blank region as a leaf in the tree. */ - new = ggc_alloc_cleared (sizeof (*new)); - new->type = type; - new->outer = outer; - if (outer) - { - new->next_peer = outer->inner; - outer->inner = new; - } - else - { - new->next_peer = cfun->eh->region_tree; - cfun->eh->region_tree = new; - } - - new->region_number = ++cfun->eh->last_region_number; - - return new; -} - -struct eh_region * -gen_eh_region_cleanup (struct eh_region *outer, struct eh_region *prev_try) -{ - struct eh_region *cleanup = gen_eh_region (ERT_CLEANUP, outer); - cleanup->u.cleanup.prev_try = prev_try; - return cleanup; -} - -struct eh_region * -gen_eh_region_try (struct eh_region *outer) -{ - return gen_eh_region (ERT_TRY, outer); -} - -struct eh_region * -gen_eh_region_catch (struct eh_region *t, tree type_or_list) -{ - struct eh_region *c, *l; - tree type_list, type_node; - - /* Ensure to always end up with a type list to normalize further - processing, then register each type against the runtime types map. */ - type_list = type_or_list; - if (type_or_list) - { - if (TREE_CODE (type_or_list) != TREE_LIST) - type_list = tree_cons (NULL_TREE, type_or_list, NULL_TREE); - - type_node = type_list; - for (; type_node; type_node = TREE_CHAIN (type_node)) - add_type_for_runtime (TREE_VALUE (type_node)); - } - - c = gen_eh_region (ERT_CATCH, t->outer); - c->u.catch.type_list = type_list; - 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; - - return c; -} - -struct eh_region * -gen_eh_region_allowed (struct eh_region *outer, tree allowed) -{ - struct eh_region *region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer); - region->u.allowed.type_list = allowed; - - for (; allowed ; allowed = TREE_CHAIN (allowed)) - add_type_for_runtime (TREE_VALUE (allowed)); - - return region; -} - -struct eh_region * -gen_eh_region_must_not_throw (struct eh_region *outer) -{ - return gen_eh_region (ERT_MUST_NOT_THROW, outer); -} - -int -get_eh_region_number (struct eh_region *region) -{ - return region->region_number; -} - -bool -get_eh_region_may_contain_throw (struct eh_region *region) -{ - return region->may_contain_throw; -} - -tree -get_eh_region_tree_label (struct eh_region *region) -{ - return region->tree_label; -} - -void -set_eh_region_tree_label (struct eh_region *region, tree lab) -{ - region->tree_label = lab; -} - -void -expand_resx_expr (tree exp) -{ - int region_nr = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)); - 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 (); -} - -/* Note that the current EH region (if any) may contain a throw, or a - call to a function which itself may contain a throw. */ - -void -note_eh_region_may_contain_throw (struct eh_region *region) -{ - while (region && !region->may_contain_throw) - { - region->may_contain_throw = 1; - region = region->outer; - } -} - -void -note_current_region_may_contain_throw (void) -{ - note_eh_region_may_contain_throw (cfun->eh->cur_region); -} - - -/* Return an rtl expression for a pointer to the exception object - within a handler. */ - -rtx -get_exception_pointer (struct function *fun) -{ - rtx exc_ptr = fun->eh->exc_ptr; - if (fun == cfun && ! exc_ptr) - { - exc_ptr = gen_reg_rtx (ptr_mode); - fun->eh->exc_ptr = exc_ptr; - } - return exc_ptr; -} - -/* Return an rtl expression for the exception dispatch filter - within a handler. */ - -rtx -get_exception_filter (struct function *fun) -{ - rtx filter = fun->eh->filter; - if (fun == cfun && ! filter) - { - filter = gen_reg_rtx (targetm.eh_return_filter_mode ()); - fun->eh->filter = filter; - } - return filter; -} - -/* This section is for the exception handling specific optimization pass. */ - -/* Random access the exception region tree. */ - -void -collect_eh_region_array (void) -{ - struct eh_region *i; - - i = cfun->eh->region_tree; - if (! i) - return; - - 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) - { - VEC_replace (eh_region, cfun->eh->region_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; - } - } -} - -/* Remove all regions whose labels are not reachable from insns. */ - -static void -remove_unreachable_regions (rtx insns) -{ - int i, *uid_region_num; - bool *reachable; - struct eh_region *r; - rtx insn; - - uid_region_num = xcalloc (get_max_uid (), sizeof(int)); - reachable = xcalloc (cfun->eh->last_region_number + 1, sizeof(bool)); - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - r = VEC_index (eh_region, cfun->eh->region_array, i); - if (!r || r->region_number != i) - continue; - - if (r->resume) - { - gcc_assert (!uid_region_num[INSN_UID (r->resume)]); - uid_region_num[INSN_UID (r->resume)] = i; - } - if (r->label) - { - gcc_assert (!uid_region_num[INSN_UID (r->label)]); - uid_region_num[INSN_UID (r->label)] = i; - } - } - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - reachable[uid_region_num[INSN_UID (insn)]] = true; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - r = VEC_index (eh_region, cfun->eh->region_array, i); - if (r && r->region_number == i && !reachable[i]) - { - bool kill_it = true; - switch (r->type) - { - case ERT_THROW: - /* Don't remove ERT_THROW regions if their outer region - is reachable. */ - if (r->outer && reachable[r->outer->region_number]) - kill_it = false; - break; - - case ERT_MUST_NOT_THROW: - /* MUST_NOT_THROW regions are implementable solely in the - runtime, but their existence continues to affect calls - within that region. Never delete them here. */ - kill_it = false; - break; - - case ERT_TRY: - { - /* TRY regions are reachable if any of its CATCH regions - are reachable. */ - struct eh_region *c; - for (c = r->u.try.catch; c ; c = c->u.catch.next_catch) - if (reachable[c->region_number]) - { - kill_it = false; - break; - } - break; - } - - default: - break; - } - - if (kill_it) - remove_eh_handler (r); - } - } - - free (reachable); - free (uid_region_num); -} - -/* 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; - - /* 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) - { - struct eh_region *region; - - 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); -} - -static void -add_ehl_entry (rtx label, struct eh_region *region) -{ - struct ehl_map_entry **slot, *entry; - - LABEL_PRESERVE_P (label) = 1; - - entry = ggc_alloc (sizeof (*entry)); - entry->label = label; - entry->region = region; - - slot = (struct ehl_map_entry **) - htab_find_slot (cfun->eh->exception_handler_label_map, entry, INSERT); - - /* Before landing pad creation, each exception handler has its own - 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. */ - gcc_assert (!*slot || cfun->eh->built_landing_pads); - - *slot = entry; -} - -void -find_exception_handler_labels (void) -{ - int i; - - if (cfun->eh->exception_handler_label_map) - htab_empty (cfun->eh->exception_handler_label_map); - else - { - /* ??? The expansion factor here (3/2) must be greater than the htab - occupancy factor (4/3) to avoid unnecessary resizing. */ - cfun->eh->exception_handler_label_map - = htab_create_ggc (cfun->eh->last_region_number * 3 / 2, - ehl_hash, ehl_eq, NULL); - } - - if (cfun->eh->region_tree == NULL) - return; - - for (i = cfun->eh->last_region_number; i > 0; --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) - lab = region->landing_pad; - else - lab = region->label; - - if (lab) - add_ehl_entry (lab, region); - } - - /* For sjlj exceptions, need the return label to remain live until - after landing pad generation. */ - if (USING_SJLJ_EXCEPTIONS && ! cfun->eh->built_landing_pads) - add_ehl_entry (return_label, NULL); -} - -/* Returns true if the current function has exception handling regions. */ - -bool -current_function_has_exception_handlers (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region *region; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - if (region - && region->region_number == i - && region->type != ERT_THROW) - return true; - } - - return false; -} - -/* 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) -{ - eh_region ret, n; - - ret = n = ggc_alloc (sizeof (struct eh_region)); - - *n = *old; - n->outer = outer; - n->next_peer = NULL; - gcc_assert (!old->aka); - - n->region_number += eh_offset; - VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n); - - if (old->inner) - { - 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); - } - } - - return ret; -} - -/* 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. */ - -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; - - if (!ifun->eh->region_tree) - return 0; - - /* 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); - } - } - - /* 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 = 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) - { - 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 eh_offset; -} - -/* 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 *rp_a, *rp_b; - - gcc_assert (ifun->eh->last_region_number > 0); - gcc_assert (ifun->eh->region_tree); - - 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); - - do - { - if (rp_a == rp_b) - return true; - rp_b = rp_b->outer; - } - while (rp_b); - - return false; -} - -/* Return region number of region that is outer to both if REGION_A and - REGION_B in IFUN. */ - -int -eh_region_outermost (struct function *ifun, int region_a, int region_b) -{ - struct eh_region *rp_a, *rp_b; - sbitmap b_outer; - - gcc_assert (ifun->eh->last_region_number > 0); - gcc_assert (ifun->eh->region_tree); - - 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); - - b_outer = sbitmap_alloc (ifun->eh->last_region_number + 1); - sbitmap_zero (b_outer); - - do - { - SET_BIT (b_outer, rp_b->region_number); - rp_b = rp_b->outer; - } - while (rp_b); - - do - { - if (TEST_BIT (b_outer, rp_a->region_number)) - { - sbitmap_free (b_outer); - return rp_a->region_number; - } - rp_a = rp_a->outer; - } - while (rp_a); - - sbitmap_free (b_outer); - return -1; -} - -static int -t2r_eq (const void *pentry, const void *pdata) -{ - tree entry = (tree) pentry; - tree data = (tree) pdata; - - return TREE_PURPOSE (entry) == data; -} - -static hashval_t -t2r_hash (const void *pentry) -{ - tree entry = (tree) pentry; - return TREE_HASH (TREE_PURPOSE (entry)); -} - -static void -add_type_for_runtime (tree type) -{ - tree *slot; - - slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type, - TREE_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 (tree type) -{ - tree *slot; - - slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type, - TREE_HASH (type), NO_INSERT); - - /* We should have always inserted the data earlier. */ - return TREE_VALUE (*slot); -} - - -/* Represent an entry in @TTypes for either catch actions - or exception filter actions. */ -struct ttypes_filter GTY(()) -{ - tree t; - int filter; -}; - -/* Compare ENTRY (a ttypes_filter entry in the hash table) with DATA - (a tree) for a @TTypes type node we are thinking about adding. */ - -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; - - return entry->t == data; -} - -static hashval_t -ttypes_filter_hash (const void *pentry) -{ - const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry; - return TREE_HASH (entry->t); -} - -/* 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. */ - -static int -ehspec_filter_eq (const void *pentry, const void *pdata) -{ - 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); -} - -/* Hash function for exception specification lists. */ - -static hashval_t -ehspec_filter_hash (const void *pentry) -{ - 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) + TREE_HASH (TREE_VALUE (list)); - return h; -} - -/* Add TYPE (which may be NULL) to cfun->eh->ttype_data, using TYPES_HASH - to speed up the search. Return the filter value to be used. */ - -static int -add_ttypes_entry (htab_t ttypes_hash, tree type) -{ - struct ttypes_filter **slot, *n; - - slot = (struct ttypes_filter **) - htab_find_slot_with_hash (ttypes_hash, type, TREE_HASH (type), INSERT); - - if ((n = *slot) == NULL) - { - /* Filter value is a 1 based table index. */ - - n = XNEW (struct ttypes_filter); - n->t = type; - n->filter = VEC_length (tree, cfun->eh->ttype_data) + 1; - *slot = n; - - VEC_safe_push (tree, gc, cfun->eh->ttype_data, type); - } - - return n->filter; -} - -/* 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 (htab_t ehspec_hash, htab_t ttypes_hash, tree list) -{ - struct ttypes_filter **slot, *n; - struct ttypes_filter dummy; - - dummy.t = list; - slot = (struct ttypes_filter **) - htab_find_slot (ehspec_hash, &dummy, INSERT); - - if ((n = *slot) == NULL) - { - /* Filter value is a -1 based byte index into a uleb128 buffer. */ - - n = XNEW (struct ttypes_filter); - n->t = list; - n->filter = -(VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) + 1); - *slot = n; - - /* Generate a 0 terminated list of filter values. */ - for (; list ; list = TREE_CHAIN (list)) - { - 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; -} - -/* 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. */ - -static void -assign_filter_values (void) -{ - int i; - htab_t ttypes, ehspec; - - 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; - - 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) - continue; - - switch (r->type) - { - case ERT_CATCH: - /* Whatever type_list is (NULL or true list), we build a list - of filters for the region. */ - r->u.catch.filter_list = NULL_TREE; - - if (r->u.catch.type_list != NULL) - { - /* Get a filter value for each of the types caught and store - them in the region's dedicated list. */ - tree tp_node = r->u.catch.type_list; - - for (;tp_node; tp_node = TREE_CHAIN (tp_node)) - { - int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node)); - 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); - } - } - else - { - /* 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_cst (NULL_TREE, flt); - - r->u.catch.filter_list - = tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list); - } - - break; - - case ERT_ALLOWED_EXCEPTIONS: - r->u.allowed.filter - = add_ehspec_entry (ehspec, ttypes, r->u.allowed.type_list); - break; - - default: - break; - } - } - - htab_delete (ttypes); - htab_delete (ehspec); -} - -/* Emit SEQ into basic block just before INSN (that is assumed to be - first instruction of some existing BB and return the newly - produced block. */ -static basic_block -emit_to_new_bb_before (rtx seq, rtx insn) -{ - rtx last; - basic_block bb; - edge e; - edge_iterator ei; - - /* 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 (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 (BARRIER_P (last)) - last = PREV_INSN (last); - bb = create_basic_block (seq, last, BLOCK_FOR_INSN (insn)->prev_bb); - update_bb_for_insn (bb); - bb->flags |= BB_SUPERBLOCK; - return bb; -} - -/* Generate the code to actually handle exceptions, which will follow the - landing pads. */ - -static void -build_post_landing_pads (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --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; - - 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 intervening cleanups. */ - - region->post_landing_pad = gen_label_rtx (); - - start_sequence (); - - 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) - { - if (c->u.catch.type_list == NULL) - emit_jump (c->label); - else - { - /* Need for one cmp/jump per type caught. Each type - list entry has a matching entry in the filter list - (see assign_filter_values). */ - tree tp_node = c->u.catch.type_list; - tree flt_node = c->u.catch.filter_list; - - for (; tp_node; ) - { - emit_cmp_and_jump_insns - (cfun->eh->filter, - GEN_INT (tree_low_cst (TREE_VALUE (flt_node), 0)), - EQ, NULL_RTX, - targetm.eh_return_filter_mode (), 0, c->label); - - tp_node = TREE_CHAIN (tp_node); - flt_node = TREE_CHAIN (flt_node); - } - } - } - } - - /* 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 (); - - seq = get_insns (); - end_sequence (); - - emit_to_new_bb_before (seq, region->u.try.catch->label); - - break; - - case ERT_ALLOWED_EXCEPTIONS: - region->post_landing_pad = gen_label_rtx (); - - start_sequence (); - - emit_label (region->post_landing_pad); - - emit_cmp_and_jump_insns (cfun->eh->filter, - GEN_INT (region->u.allowed.filter), - 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 - flow data in the meantime. */ - region->resume - = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number)); - emit_barrier (); - - seq = get_insns (); - end_sequence (); - - emit_to_new_bb_before (seq, region->label); - break; - - case ERT_CLEANUP: - case ERT_MUST_NOT_THROW: - region->post_landing_pad = region->label; - break; - - case ERT_CATCH: - case ERT_THROW: - /* Nothing to do. */ - break; - - default: - gcc_unreachable (); - } - } -} - -/* Replace RESX patterns with jumps to the next handler if any, or calls to - _Unwind_Resume otherwise. */ - -static void -connect_post_landing_pads (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --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; - - /* If there is no RESX, or it has been deleted by flow, there's - nothing to fix up. */ - if (! region->resume || INSN_DELETED_P (region->resume)) - continue; - - /* Search for another landing pad in this function. */ - for (outer = region->outer; outer ; outer = outer->outer) - if (outer->post_landing_pad) - break; - - start_sequence (); - - if (outer) - { - edge e; - basic_block src, dest; - - emit_jump (outer->post_landing_pad); - src = BLOCK_FOR_INSN (region->resume); - dest = BLOCK_FOR_INSN (outer->post_landing_pad); - 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; - } - else - { - emit_library_call (unwind_resume_libfunc, LCT_THROW, - VOIDmode, 1, cfun->eh->exc_ptr, ptr_mode); - - /* What we just emitted was a throwing libcall, so it got a - barrier automatically added after it. If the last insn in - the libcall sequence isn't the barrier, it's because the - target emits multiple insns for a call, and there are insns - after the actual call insn (which are redundant and would be - optimized away). The barrier is inserted exactly after the - call insn, so let's go get that and delete the insns after - it, because below we need the barrier to be the last insn in - the sequence. */ - delete_insns_since (NEXT_INSN (last_call_insn ())); - } - - seq = get_insns (); - end_sequence (); - barrier = emit_insn_before (seq, region->resume); - /* Avoid duplicate barrier. */ - gcc_assert (BARRIER_P (barrier)); - delete_insn (barrier); - delete_insn (region->resume); - - /* ??? From tree-ssa we can wind up with catch regions whose - label is not instantiated, but whose resx is present. Now - that we've dealt with the resx, kill the region. */ - if (region->label == NULL && region->type == ERT_CLEANUP) - remove_eh_handler (region); - } -} - - -static void -dw2_build_landing_pads (void) -{ - int i; - - for (i = cfun->eh->last_region_number; i > 0; --i) - { - struct eh_region *region; - rtx seq; - basic_block bb; - 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; - - if (region->type != ERT_CLEANUP - && region->type != ERT_TRY - && region->type != ERT_ALLOWED_EXCEPTIONS) - continue; - - start_sequence (); - - region->landing_pad = gen_label_rtx (); - emit_label (region->landing_pad); - -#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 */ } - - 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 (targetm.eh_return_filter_mode (), - EH_RETURN_DATA_REGNO (1))); - - seq = get_insns (); - end_sequence (); - - bb = emit_to_new_bb_before (seq, region->post_landing_pad); - e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU); - e->count = bb->count; - e->probability = REG_BR_PROB_BASE; - } -} - - -struct sjlj_lp_info -{ - int directly_reachable; - int action_index; - int dispatch_index; - int call_site_index; -}; - -static bool -sjlj_find_directly_reachable_regions (struct sjlj_lp_info *lp_info) -{ - rtx insn; - bool found_one = false; - - for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) - { - struct eh_region *region; - enum reachable_code rc; - tree type_thrown; - rtx note; - - if (! INSN_P (insn)) - continue; - - note = find_reg_note (insn, REG_EH_REGION, NULL_RTX); - if (!note || INTVAL (XEXP (note, 0)) <= 0) - continue; - - region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); - - 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. */ - rc = RNL_NOT_CAUGHT; - for (; region; region = region->outer) - { - rc = reachable_next_level (region, type_thrown, NULL); - if (rc != RNL_NOT_CAUGHT) - break; - } - if (rc == RNL_MAYBE_CAUGHT || rc == RNL_CAUGHT) - { - lp_info[region->region_number].directly_reachable = 1; - found_one = true; - } - } - - return found_one; -} - -static void -sjlj_assign_call_site_values (rtx dispatch_label, struct sjlj_lp_info *lp_info) -{ - htab_t ar_hash; - int i, index; - - /* First task: build the action table. */ - - VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data"); - ar_hash = htab_create (31, action_record_hash, action_record_eq, free); - - for (i = cfun->eh->last_region_number; i > 0; --i) - if (lp_info[i].directly_reachable) - { - 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) - cfun->uses_eh_lsda = 1; - } - - htab_delete (ar_hash); - - /* 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. - - 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 indices. */ - /* ??? Post-landing pad sharing doesn't actually happen at the moment - (see build_post_landing_pads) so we don't bother checking for it. */ - - index = 0; - for (i = cfun->eh->last_region_number; i > 0; --i) - if (lp_info[i].directly_reachable) - lp_info[i].dispatch_index = index++; - - /* 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. */ - - 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; - } -} - -static void -sjlj_mark_call_sites (struct sjlj_lp_info *lp_info) -{ - int last_call_site = -2; - rtx insn, mem; - - for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) - { - struct eh_region *region; - int this_call_site; - rtx note, before, p; - - /* Reset value tracking at extended basic block boundaries. */ - if (LABEL_P (insn)) - last_call_site = -2; - - if (! INSN_P (insn)) - continue; - - 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 (CALL_P (insn) - || (flag_non_call_exceptions - && may_trap_p (PATTERN (insn)))) - this_call_site = -1; - else - continue; - } - else - { - /* Calls that are known to not throw need not be marked. */ - if (INTVAL (XEXP (note, 0)) <= 0) - continue; - - region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); - this_call_site = lp_info[region->region_number].call_site_index; - } - - if (this_call_site == last_call_site) - continue; - - /* Don't separate a call from it's argument loads. */ - before = insn; - if (CALL_P (insn)) - before = find_first_parameter_load (insn, NULL_RTX); - - start_sequence (); - mem = adjust_address (cfun->eh->sjlj_fc, TYPE_MODE (integer_type_node), - sjlj_fc_call_site_ofs); - emit_move_insn (mem, GEN_INT (this_call_site)); - p = get_insns (); - end_sequence (); - - emit_insn_before (p, before); - last_call_site = this_call_site; - } -} - -/* Construct the SjLj_Function_Context. */ - -/* APPLE LOCAL begin ARM 5051776 */ -static rtx -sjlj_generate_setjmp_sequence (rtx dispatch_label, bool unregister_first) -{ - rtx fc, mem, seq; - - fc = cfun->eh->sjlj_fc; - - start_sequence (); - - /* We're reusing the old context, unregister it. */ - if (unregister_first == true) - emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL, VOIDmode, - 1, XEXP (fc, 0), Pmode); - - /* We're storing this libcall's address into memory instead of - calling it directly. Thus, we must call assemble_external_libcall - here, as we can not depend on emit_library_call to do it for us. */ - assemble_external_libcall (eh_personality_libfunc); - mem = adjust_address (fc, Pmode, sjlj_fc_personality_ofs); - emit_move_insn (mem, eh_personality_libfunc); - - mem = adjust_address (fc, Pmode, sjlj_fc_lsda_ofs); - if (cfun->uses_eh_lsda) - { - char buf[20]; - rtx sym; - - ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", current_function_funcdef_no); - sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); - SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_LOCAL; - emit_move_insn (mem, sym); - } - else - emit_move_insn (mem, const0_rtx); - -#ifdef DONT_USE_BUILTIN_SETJMP - { - rtx x, note; - 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); - } -#else - expand_builtin_setjmp_setup (plus_constant (XEXP (fc, 0), sjlj_fc_jbuf_ofs), - dispatch_label); -#endif - - emit_library_call (unwind_sjlj_register_libfunc, LCT_NORMAL, VOIDmode, - 1, XEXP (fc, 0), Pmode); - - seq = get_insns (); - end_sequence (); - return seq; -} - -/* Insert SjLj_Function_Context at the start of the current function, - and following any calls to alloca(). */ - -static void -sjlj_emit_function_setjmps (rtx dispatch_label) -{ - rtx seq, fn_begin; - bool fn_begin_outside_block; - - /* Create a new context for this function. */ - seq = sjlj_generate_setjmp_sequence (dispatch_label, false); - - /* ??? 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. */ - - fn_begin_outside_block = true; - for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin)) - if (NOTE_P (fn_begin)) - { - if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG) - break; - else if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK) - 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); - - /* Any time we call alloca(), we need to re-calculate our stack context. - Ideally, we would only update a minimal context (i.e., stack pointer), - but that is a riskier solution to an uncommon problem. Someday... */ - if (current_function_calls_alloca) - { - for (fn_begin = get_insns (); - fn_begin; - fn_begin = NEXT_INSN (fn_begin)) - if (NOTE_P (fn_begin) - && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_ALLOCA) - { - seq = sjlj_generate_setjmp_sequence (dispatch_label, true); - emit_insn_after (seq, fn_begin); - } - } -} - -/* Call back from expand_function_end to know where we should put - the call to unwind_sjlj_unregister_libfunc if needed. */ - -void -sjlj_emit_function_exit_after (rtx after) -{ - cfun->eh->sjlj_exit_after = after; -} - -static void -sjlj_emit_function_exit (void) -{ - rtx seq; - edge e; - edge_iterator ei; - - start_sequence (); - - emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL, VOIDmode, - 1, XEXP (cfun->eh->sjlj_fc, 0), Pmode); - - seq = get_insns (); - end_sequence (); - - /* ??? 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. */ - - FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds) - if (e->flags & EDGE_FALLTHRU) - break; - if (e) - { - rtx insn; - - /* 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. */ - gcc_assert (e->src->next_bb == EXIT_BLOCK_PTR); - for (insn = BB_HEAD (e->src); ; insn = NEXT_INSN (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); - } -} -/* APPLE LOCAL end ARM 5051776 */ - -static void -sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info) -{ - int i, first_reachable; - rtx mem, dispatch, seq, fc; - rtx before; - basic_block bb; - edge e; - - fc = cfun->eh->sjlj_fc; - - 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 = adjust_address (fc, TYPE_MODE (integer_type_node), - sjlj_fc_call_site_ofs); - dispatch = copy_to_reg (mem); - - mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs); - if (word_mode != ptr_mode) - { -#ifdef POINTERS_EXTEND_UNSIGNED - mem = convert_memory_address (ptr_mode, mem); -#else - mem = convert_to_mode (ptr_mode, mem, 0); -#endif - } - emit_move_insn (cfun->eh->exc_ptr, mem); - - mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs + UNITS_PER_WORD); - emit_move_insn (cfun->eh->filter, mem); - - /* Jump to one of the directly reachable regions. */ - /* ??? This really ought to be using a switch statement. */ - - first_reachable = 0; - for (i = cfun->eh->last_region_number; i > 0; --i) - { - if (! lp_info[i].directly_reachable) - continue; - - if (! first_reachable) - { - first_reachable = i; - continue; - } - - emit_cmp_and_jump_insns (dispatch, GEN_INT (lp_info[i].dispatch_index), - EQ, NULL_RTX, TYPE_MODE (integer_type_node), 0, - ((struct eh_region *)VEC_index (eh_region, cfun->eh->region_array, i)) - ->post_landing_pad); - } - - seq = get_insns (); - end_sequence (); - - 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); - e->count = bb->count; - e->probability = REG_BR_PROB_BASE; -} - -static void -sjlj_build_landing_pads (void) -{ - struct sjlj_lp_info *lp_info; - - lp_info = XCNEWVEC (struct sjlj_lp_info, cfun->eh->last_region_number + 1); - - if (sjlj_find_directly_reachable_regions (lp_info)) - { - rtx dispatch_label = gen_label_rtx (); - - 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)); - - sjlj_assign_call_site_values (dispatch_label, lp_info); - sjlj_mark_call_sites (lp_info); - - /* APPLE LOCAL ARM 5051776 */ - sjlj_emit_function_setjmps (dispatch_label); - sjlj_emit_dispatch_table (dispatch_label, lp_info); - sjlj_emit_function_exit (); - } - - free (lp_info); -} - -void -finish_eh_generation (void) -{ - basic_block bb; - - /* Nothing to do if no regions created. */ - if (cfun->eh->region_tree == NULL) - return; - - /* 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. */ - - /* These registers are used by the landing pads. Make sure they - have been generated. */ - get_exception_pointer (cfun); - get_exception_filter (cfun); - - /* 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 (); - - cfun->eh->built_landing_pads = 1; - - /* We've totally changed the CFG. Start over. */ - find_exception_handler_labels (); - break_superblocks (); - if (USING_SJLJ_EXCEPTIONS) - commit_edge_insertions (); - FOR_EACH_BB (bb) - { - edge e; - edge_iterator ei; - bool eh = false; - for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); ) - { - 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)); - } -} - -static hashval_t -ehl_hash (const void *pentry) -{ - struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry; - - /* 2^32 * ((sqrt(5) - 1) / 2) */ - const hashval_t scaled_golden_ratio = 0x9e3779b9; - return CODE_LABEL_NUMBER (entry->label) * scaled_golden_ratio; -} - -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; - - return entry->label == data->label; -} - -/* This section handles removing dead code for flow. */ - -/* Remove LABEL from exception_handler_label_map. */ - -static void -remove_exception_handler_label (rtx label) -{ - struct ehl_map_entry **slot, tmp; - - /* If exception_handler_label_map was not built yet, - there is nothing to do. */ - if (cfun->eh->exception_handler_label_map == NULL) - return; - - tmp.label = label; - slot = (struct ehl_map_entry **) - htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT); - gcc_assert (slot); - - htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot); -} - -/* Splice REGION from the region tree etc. */ - -static void -remove_eh_handler (struct eh_region *region) -{ - struct eh_region **pp, **pp_start, *p, *outer, *inner; - rtx lab; - - /* 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 a - list of alternate numbers by which we are known. */ - - outer = region->outer; - VEC_replace (eh_region, cfun->eh->region_array, region->region_number, outer); - if (region->aka) - { - 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) - { - if (!outer->aka) - outer->aka = BITMAP_GGC_ALLOC (); - if (region->aka) - bitmap_ior_into (outer->aka, region->aka); - bitmap_set_bit (outer->aka, region->region_number); - } - - if (cfun->eh->built_landing_pads) - lab = region->landing_pad; - else - lab = region->label; - if (lab) - remove_exception_handler_label (lab); - - if (outer) - pp_start = &outer->inner; - else - pp_start = &cfun->eh->region_tree; - for (pp = pp_start, p = *pp; p != region; pp = &p->next_peer, p = *pp) - continue; - *pp = region->next_peer; - - inner = region->inner; - if (inner) - { - for (p = inner; p->next_peer ; p = p->next_peer) - p->outer = outer; - p->outer = outer; - - p->next_peer = *pp_start; - *pp_start = inner; - } - - if (region->type == ERT_CATCH) - { - struct eh_region *try, *next, *prev; - - for (try = region->next_peer; - try->type == ERT_CATCH; - try = try->next_peer) - continue; - gcc_assert (try->type == ERT_TRY); - - next = region->u.catch.next_catch; - prev = region->u.catch.prev_catch; - - 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); - } - } -} - -/* 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. */ - -void -maybe_remove_eh_handler (rtx label) -{ - struct ehl_map_entry **slot, tmp; - struct eh_region *region; - - /* ??? 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; - - tmp.label = label; - slot = (struct ehl_map_entry **) - htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT); - if (! slot) - return; - region = (*slot)->region; - if (! region) - return; - - /* 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) - { - htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot); - region->label = NULL_RTX; - } - else - remove_eh_handler (region); -} - -/* Invokes CALLBACK for every exception handler label. Only used by old - loop hackery; should not be used by new code. */ - -void -for_each_eh_label (void (*callback) (rtx)) -{ - htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1, - (void *) &callback); -} - -static int -for_each_eh_label_1 (void **pentry, void *data) -{ - struct ehl_map_entry *entry = *(struct ehl_map_entry **)pentry; - void (*callback) (rtx) = *(void (**) (rtx)) data; - - (*callback) (entry->label); - return 1; -} - -/* Invoke CALLBACK for every exception region in the current function. */ - -void -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; - - region = VEC_index (eh_region, cfun->eh->region_array, i); - if (region) - (*callback) (region); - } -} - -/* This section describes CFG exception edges for flow. */ - -/* For communicating between calls to reachable_next_level. */ -struct reachable_info -{ - tree types_caught; - tree types_allowed; - void (*callback) (struct eh_region *, void *); - void *callback_data; - bool saw_any_handlers; -}; - -/* A subroutine of reachable_next_level. Return true if TYPE, or a - base class of TYPE, is in HANDLED. */ - -static int -check_handled (tree handled, tree type) -{ - 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 - { - for (t = handled; t ; t = TREE_CHAIN (t)) - if ((*lang_eh_type_covers) (TREE_VALUE (t), type)) - return 1; - } - - return 0; -} - -/* 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 -add_reachable_handler (struct reachable_info *info, - struct eh_region *lp_region, struct eh_region *region) -{ - if (! info) - return; - - info->saw_any_handlers = true; - - if (cfun->eh->built_landing_pads) - info->callback (lp_region, info->callback_data); - else - info->callback (region, info->callback_data); -} - -/* 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. */ - -static enum reachable_code -reachable_next_level (struct eh_region *region, tree type_thrown, - struct reachable_info *info) -{ - 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; - - for (c = region->u.try.catch; c ; c = c->u.catch.next_catch) - { - /* A catch-all handler ends the search. */ - if (c->u.catch.type_list == NULL) - { - add_reachable_handler (info, region, c); - return RNL_CAUGHT; - } - - if (type_thrown) - { - /* If we have at least one type match, end the search. */ - tree tp_node = c->u.catch.type_list; - - for (; tp_node; tp_node = TREE_CHAIN (tp_node)) - { - tree type = TREE_VALUE (tp_node); - - if (type == type_thrown - || (lang_eh_type_covers - && (*lang_eh_type_covers) (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; - } - - /* At this point, we either don't know what type is thrown or - don't have front-end assistance to help deciding if it is - covered by one of the types in the list for this region. - - We'd then like to add this region to the list of reachable - handlers since it is indeed potentially reachable based on the - information we have. - - Actually, this handler is for sure not reachable if all the - types it matches have already been caught. That is, it is only - potentially reachable if at least one of the types it catches - has not been previously caught. */ - - if (! info) - ret = RNL_MAYBE_CAUGHT; - else - { - tree tp_node = c->u.catch.type_list; - bool maybe_reachable = false; - - /* Compute the potential reachability of this handler and - update the list of types caught at the same time. */ - for (; tp_node; tp_node = TREE_CHAIN (tp_node)) - { - tree type = TREE_VALUE (tp_node); - - if (! check_handled (info->types_caught, type)) - { - info->types_caught - = tree_cons (NULL, type, info->types_caught); - - maybe_reachable = true; - } - } - - if (maybe_reachable) - { - add_reachable_handler (info, region, c); - - /* ??? If the catch type is a base class of every allowed - type, then we know we can stop the search. */ - ret = RNL_MAYBE_CAUGHT; - } - } - } - - return ret; - } - - 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; - } - - /* 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 hierarchy, - 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; - } - } - - add_reachable_handler (info, region, region); - return RNL_MAYBE_CAUGHT; - - case ERT_CATCH: - /* Catch regions are handled by their controlling try region. */ - return RNL_NOT_CAUGHT; - - 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. - - 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; - } - else - return RNL_BLOCKED; - - case ERT_THROW: - case ERT_UNKNOWN: - /* Shouldn't see these here. */ - gcc_unreachable (); - break; - default: - gcc_unreachable (); - } -} - -/* Invoke CALLBACK on each region reachable from REGION_NUMBER. */ - -void -foreach_reachable_handler (int region_number, bool is_resx, - void (*callback) (struct eh_region *, void *), - void *callback_data) -{ - struct reachable_info info; - struct eh_region *region; - tree type_thrown; - - memset (&info, 0, sizeof (info)); - info.callback = callback; - info.callback_data = callback_data; - - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - - type_thrown = NULL_TREE; - if (is_resx) - { - /* A RESX leaves a region instead of entering it. Thus the - region itself may have been deleted out from under us. */ - if (region == NULL) - return; - region = region->outer; - } - else if (region->type == ERT_THROW) - { - type_thrown = region->u.throw.type; - region = region->outer; - } - - while (region) - { - if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT) - break; - /* If we have processed one cleanup, there is no point in - processing any more of them. Each cleanup will have an edge - to the next outer cleanup region, so the flow graph will be - accurate. */ - if (region->type == ERT_CLEANUP) - region = region->u.cleanup.prev_try; - else - region = region->outer; - } -} - -/* Retrieve a list of labels of exception handlers which can be - reached by a given insn. */ - -static void -arh_to_landing_pad (struct eh_region *region, void *data) -{ - rtx *p_handlers = data; - if (! *p_handlers) - *p_handlers = alloc_INSN_LIST (region->landing_pad, NULL_RTX); -} - -static void -arh_to_label (struct eh_region *region, void *data) -{ - rtx *p_handlers = data; - *p_handlers = alloc_INSN_LIST (region->label, *p_handlers); -} - -rtx -reachable_handlers (rtx insn) -{ - bool is_resx = false; - rtx handlers = NULL; - int region_number; - - if (JUMP_P (insn) - && GET_CODE (PATTERN (insn)) == RESX) - { - region_number = XINT (PATTERN (insn), 0); - is_resx = true; - } - 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)); - } - - foreach_reachable_handler (region_number, is_resx, - (cfun->eh->built_landing_pads - ? arh_to_landing_pad - : arh_to_label), - &handlers); - - return handlers; -} - -/* Determine if the given INSN can throw an exception that is caught - within the function. */ - -bool -can_throw_internal_1 (int region_number, bool is_resx) -{ - struct eh_region *region; - tree type_thrown; - - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - - type_thrown = NULL_TREE; - if (is_resx) - region = region->outer; - else if (region->type == ERT_THROW) - { - type_thrown = region->u.throw.type; - region = region->outer; - } - - /* 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 false; -} - -bool -can_throw_internal (rtx insn) -{ - rtx note; - - if (! INSN_P (insn)) - return false; - - 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); - - /* 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 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, bool is_resx) -{ - struct eh_region *region; - tree type_thrown; - - region = VEC_index (eh_region, cfun->eh->region_array, region_number); - - type_thrown = NULL_TREE; - if (is_resx) - region = region->outer; - else if (region->type == ERT_THROW) - { - 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; -} - -bool -can_throw_external (rtx insn) -{ - rtx note; - - if (! INSN_P (insn)) - return false; - - 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); - - 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. 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 (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)), false); -} - -/* Set TREE_NOTHROW and cfun->all_throwers_are_sibcalls. */ - -unsigned int -set_nothrow_function_flags (void) -{ - rtx insn; - - /* 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 - CALL_INSNs that are SIBLING_CALL_P, as these are really jumps, - and can't throw. Most CALL_INSNs are not SIBLING_CALL_P, so this - is optimistic. */ - - cfun->all_throwers_are_sibcalls = 1; - - if (! flag_exceptions) - return 0; - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - if (can_throw_external (insn)) - { - TREE_NOTHROW (current_function_decl) = 0; - - if (!CALL_P (insn) || !SIBLING_CALL_P (insn)) - { - cfun->all_throwers_are_sibcalls = 0; - return 0; - } - } - - for (insn = current_function_epilogue_delay_list; insn; - insn = XEXP (insn, 1)) - if (can_throw_external (insn)) - { - TREE_NOTHROW (current_function_decl) = 0; - - if (!CALL_P (insn) || !SIBLING_CALL_P (insn)) - { - cfun->all_throwers_are_sibcalls = 0; - 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. */ - -/* Do any necessary initialization to access arbitrary stack frames. - On the SPARC, this means flushing the register windows. */ - -void -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; - -#ifdef SETUP_FRAME_ADDRESSES - SETUP_FRAME_ADDRESSES (); -#endif -} - -rtx -expand_builtin_eh_return_data_regno (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. */ - -rtx -expand_builtin_extract_return_addr (tree addr_tree) -{ - rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); - - if (GET_MODE (addr) != Pmode - && GET_MODE (addr) != VOIDmode) - { -#ifdef POINTERS_EXTEND_UNSIGNED - addr = convert_memory_address (Pmode, addr); -#else - addr = convert_to_mode (Pmode, addr, 0); -#endif - } - - /* First mask out any unwanted bits. */ -#ifdef MASK_RETURN_ADDR - expand_and (Pmode, 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 - and return the value to be stored in the return address register or - stack slot so the epilogue will return to that address. */ - -rtx -expand_builtin_frob_return_addr (tree addr_tree) -{ - rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0); - - addr = convert_memory_address (Pmode, addr); - -#ifdef RETURN_ADDR_OFFSET - addr = force_reg (Pmode, addr); - addr = plus_constant (addr, -RETURN_ADDR_OFFSET); -#endif - - return addr; -} - -/* Set up the epilogue with the magic bits we'll need to return to the - exception handler. */ - -void -expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED, - tree handler_tree) -{ - rtx tmp; - -#ifdef EH_RETURN_STACKADJ_RTX - tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0); - tmp = convert_memory_address (Pmode, tmp); - if (!cfun->eh->ehr_stackadj) - cfun->eh->ehr_stackadj = copy_to_reg (tmp); - else if (tmp != cfun->eh->ehr_stackadj) - emit_move_insn (cfun->eh->ehr_stackadj, tmp); -#endif - - tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0); - tmp = convert_memory_address (Pmode, tmp); - if (!cfun->eh->ehr_handler) - cfun->eh->ehr_handler = copy_to_reg (tmp); - else if (tmp != cfun->eh->ehr_handler) - emit_move_insn (cfun->eh->ehr_handler, tmp); - - if (!cfun->eh->ehr_label) - cfun->eh->ehr_label = gen_label_rtx (); - emit_jump (cfun->eh->ehr_label); -} - -void -expand_eh_return (void) -{ - rtx around_label; - - if (! cfun->eh->ehr_label) - return; - - current_function_calls_eh_return = 1; - -#ifdef EH_RETURN_STACKADJ_RTX - emit_move_insn (EH_RETURN_STACKADJ_RTX, const0_rtx); -#endif - - around_label = gen_label_rtx (); - emit_jump (around_label); - - emit_label (cfun->eh->ehr_label); - clobber_return_register (); - -#ifdef EH_RETURN_STACKADJ_RTX - emit_move_insn (EH_RETURN_STACKADJ_RTX, cfun->eh->ehr_stackadj); -#endif - -#ifdef HAVE_eh_return - if (HAVE_eh_return) - emit_insn (gen_eh_return (cfun->eh->ehr_handler)); - else -#endif - { -#ifdef EH_RETURN_HANDLER_RTX - emit_move_insn (EH_RETURN_HANDLER_RTX, cfun->eh->ehr_handler); -#else - error ("__builtin_eh_return not supported on this target"); -#endif - } - - emit_label (around_label); -} - -/* Convert a ptr_mode address ADDR_TREE to a Pmode address controlled by - POINTERS_EXTEND_UNSIGNED and return it. */ - -rtx -expand_builtin_extend_pointer (tree addr_tree) -{ - rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0); - int extend; - -#ifdef POINTERS_EXTEND_UNSIGNED - extend = POINTERS_EXTEND_UNSIGNED; -#else - /* The previous EH code did an unsigned extend by default, so we do this also - for consistency. */ - extend = 1; -#endif - - return convert_modes (word_mode, ptr_mode, addr, extend); -} - -/* In the following functions, we represent entries in the action table - as 1-based indices. Special cases are: - - 0: null action record, non-null landing pad; implies cleanups - -1: null action record, null landing pad; implies no action - -2: no call-site entry; implies must_not_throw - -3: we have yet to process outer regions - - Further, no special cases apply to the "next" field of the record. - For next, 0 means end of list. */ - -struct action_record -{ - int offset; - int filter; - int next; -}; - -static int -action_record_eq (const void *pentry, const void *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 hashval_t -action_record_hash (const void *pentry) -{ - const struct action_record *entry = (const struct action_record *) pentry; - return entry->next * 1009 + entry->filter; -} - -static int -add_action_record (htab_t ar_hash, int filter, int next) -{ - struct action_record **slot, *new, tmp; - - tmp.filter = filter; - tmp.next = next; - slot = (struct action_record **) htab_find_slot (ar_hash, &tmp, INSERT); - - if ((new = *slot) == NULL) - { - new = 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 - indices 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); - } - - return new->offset; -} - -static int -collect_one_action_chain (htab_t ar_hash, struct eh_region *region) -{ - struct eh_region *c; - int next; - - /* 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) - { - 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 haven'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_list == NULL) - { - /* Retrieve the filter from the head of the filter list - where we have stored it (see assign_filter_values). */ - int filter - = TREE_INT_CST_LOW (TREE_VALUE (c->u.catch.filter_list)); - - next = add_action_record (ar_hash, filter, 0); - } - else - { - /* Once the outer search is done, trigger an action record for - each filter we have. */ - tree flt_node; - - if (next == -3) - { - next = collect_one_action_chain (ar_hash, region->outer); - - /* If there is no next action, terminate the chain. */ - if (next == -1) - next = 0; - /* If all outer actions are cleanups or must_not_throw, - we'll have no action record for it, since we had wanted - to encode these states in the call-site record directly. - Add a cleanup action to the chain to catch these. */ - else if (next <= 0) - next = add_action_record (ar_hash, 0, 0); - } - - flt_node = c->u.catch.filter_list; - for (; flt_node; flt_node = TREE_CHAIN (flt_node)) - { - int filter = TREE_INT_CST_LOW (TREE_VALUE (flt_node)); - next = add_action_record (ar_hash, 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); - - /* If there is no next action, terminate the chain. */ - if (next == -1) - next = 0; - /* If all outer actions are cleanups or must_not_throw, - we'll have no action record for it, since we had wanted - to encode these states in the call-site record directly. - 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: - /* 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: - gcc_unreachable (); - } -} - -static int -add_call_site (rtx landing_pad, int action) -{ - 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 (used >= size) - { - size = (size ? size * 2 : 64); - data = ggc_realloc (data, sizeof (*data) * size); - cfun->eh->call_site_data = data; - cfun->eh->call_site_data_size = size; - } - - data[used].landing_pad = landing_pad; - data[used].action = action; - - cfun->eh->call_site_data_used = used + 1; - - 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. */ - -unsigned int -convert_to_eh_region_ranges (void) -{ - 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 = 0; - - if (USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL) - return 0; - - VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data"); - - ar_hash = htab_create (31, action_record_hash, action_record_eq, free); - - for (iter = get_insns (); iter ; iter = NEXT_INSN (iter)) - if (INSN_P (iter)) - { - struct eh_region *region; - int this_action; - rtx this_landing_pad; - - insn = iter; - 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 (! (CALL_P (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 = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0))); - this_action = collect_one_action_chain (ar_hash, region); - } - - /* 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; - - /* 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; - - /* 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) - { - note = emit_note_after (NOTE_INSN_EH_REGION_END, last_action_insn); - NOTE_EH_HANDLER (note) = call_site; - } - - 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) -{ - do - { - unsigned char byte = value & 0x7f; - value >>= 7; - if (value) - byte |= 0x80; - VARRAY_PUSH_UCHAR (*data_area, byte); - } - while (value); -} - -static void -push_sleb128 (varray_type *data_area, int value) -{ - unsigned char byte; - int more; - - do - { - 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); -} - - -#ifndef HAVE_AS_LEB128 -static int -dw2_size_of_call_site_table (void) -{ - int n = cfun->eh->call_site_data_used; - int size = n * (4 + 4 + 4); - int i; - - for (i = 0; i < n; ++i) - { - struct call_site_record *cs = &cfun->eh->call_site_data[i]; - size += size_of_uleb128 (cs->action); - } - - return size; -} - -static int -sjlj_size_of_call_site_table (void) -{ - int n = cfun->eh->call_site_data_used; - int size = 0; - int i; - - for (i = 0; i < n; ++i) - { - 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); - } - - return size; -} -#endif - -static void -dw2_output_call_site_table (void) -{ - int n = cfun->eh->call_site_data_used; - int i; - - for (i = 0; i < n; ++i) - { - 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, - 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, - 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, - 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, - current_function_func_begin_label, - "landing pad"); - else - dw2_asm_output_data (4, 0, "landing pad"); -#endif - dw2_asm_output_data_uleb128 (cs->action, "action"); - } - - call_site_base += n; -} - -static void -sjlj_output_call_site_table (void) -{ - int n = cfun->eh->call_site_data_used; - int i; - - for (i = 0; i < n; ++i) - { - struct call_site_record *cs = &cfun->eh->call_site_data[i]; - - dw2_asm_output_data_uleb128 (INTVAL (cs->landing_pad), - "region %d landing pad", i); - dw2_asm_output_data_uleb128 (cs->action, "action"); - } - - call_site_base += n; -} - -#ifndef TARGET_UNWIND_INFO -/* Switch to the section that should be used for exception tables. */ - -static void -switch_to_exception_section (void) -{ - if (exception_section == 0) - { - 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; - exception_section = get_section (".gcc_except_table", flags, NULL); - } - else - exception_section = flag_pic ? data_section : readonly_data_section; - } - switch_to_section (exception_section); -} -#endif - - -/* 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 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); - if (TREE_CODE (type) == VAR_DECL) - { - node = cgraph_varpool_node (type); - if (node) - cgraph_varpool_mark_needed_node (node); - public = TREE_PUBLIC (type); - } - } - else - gcc_assert (TREE_CODE (type) == INTEGER_CST); - } - - /* 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 - dw2_asm_output_encoded_addr_rtx (tt_format, value, public, NULL); -} - -void -output_function_exception_table (void) -{ - int tt_format, cs_format, lp_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 tt_format_size = 0; - - if (eh_personality_libfunc) - assemble_external_libcall (eh_personality_libfunc); - - /* Not all functions need anything. */ - if (! cfun->uses_eh_lsda) - return; - -#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 - switch_to_exception_section (); -#endif - - /* APPLE LOCAL begin 6128170 */ - /* deletion; code moved down 26 lines */ - /* APPLE LOCAL end 6128170 */ - - 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. */ - if (! have_tt_data) - tt_format = DW_EH_PE_omit; - else - { - tt_format = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1); -#ifdef HAVE_AS_LEB128 - ASM_GENERATE_INTERNAL_LABEL (ttype_label, "LLSDATT", - current_function_funcdef_no); -#endif - tt_format_size = size_of_encoded_value (tt_format); - - assemble_align (tt_format_size * BITS_PER_UNIT); - } - - /* APPLE LOCAL begin 6128170 */ - /* If the target wants a label to begin the table, emit it here. */ - targetm.asm_out.except_table_label (asm_out_file); - /* APPLE LOCAL end 6128170 */ - - targetm.asm_out.internal_label (asm_out_file, "LLSDA", - current_function_funcdef_no); - - /* 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. */ - lp_format = DW_EH_PE_omit; - dw2_asm_output_data (1, lp_format, "@LPStart format (%s)", - eh_data_format_name (lp_format)); - - /* @LPStart pointer would go here. */ - - dw2_asm_output_data (1, tt_format, "@TType format (%s)", - eh_data_format_name (tt_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", - current_function_funcdef_no); - 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; - - before_disp = 1 + 1; - after_disp = (1 + size_of_uleb128 (call_site_len) - + call_site_len - + VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) - + (VEC_length (tree, cfun->eh->ttype_data) - * tt_format_size)); - - disp = after_disp; - do - { - unsigned int disp_size, pad; - - last_disp = disp; - disp_size = size_of_uleb128 (disp); - pad = before_disp + disp_size + after_disp; - if (pad % tt_format_size) - pad = tt_format_size - (pad % tt_format_size); - else - pad = 0; - disp = after_disp + pad; - } - while (disp != last_disp); - - dw2_asm_output_data_uleb128 (disp, "@TType base offset"); -#endif - } - - /* Indicate the format of the call-site offsets. */ -#ifdef HAVE_AS_LEB128 - cs_format = DW_EH_PE_uleb128; -#else - cs_format = DW_EH_PE_udata4; -#endif - dw2_asm_output_data (1, cs_format, "call-site format (%s)", - eh_data_format_name (cs_format)); - -#ifdef HAVE_AS_LEB128 - ASM_GENERATE_INTERNAL_LABEL (cs_after_size_label, "LLSDACSB", - current_function_funcdef_no); - ASM_GENERATE_INTERNAL_LABEL (cs_end_label, "LLSDACSE", - current_function_funcdef_no); - 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")); - - if (have_tt_data) - assemble_align (tt_format_size * BITS_PER_UNIT); - - i = VEC_length (tree, cfun->eh->ttype_data); - while (i-- > 0) - { - tree type = VEC_index (tree, cfun->eh->ttype_data, i); - output_ttype (type, tt_format, tt_format_size); - } - -#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) - { - 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); -} - -/* 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" |