1999-08-26 09:30:50 +00:00
|
|
|
|
/* Implements exception handling.
|
2002-02-01 18:16:02 +00:00
|
|
|
|
Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
|
2007-05-19 01:19:51 +00:00
|
|
|
|
1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
Contributed by Mike Stump <mrs@cygnus.com>.
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
This file is part of GCC.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2002-02-01 18:16:02 +00:00
|
|
|
|
along with GCC; see the file COPYING. If not, write to the Free
|
2007-05-19 01:19:51 +00:00
|
|
|
|
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
|
02110-1301, USA. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
[ Add updated documentation on how to use this. ] */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "system.h"
|
2004-07-28 03:11:36 +00:00
|
|
|
|
#include "coretypes.h"
|
|
|
|
|
#include "tm.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "rtl.h"
|
|
|
|
|
#include "tree.h"
|
|
|
|
|
#include "flags.h"
|
|
|
|
|
#include "function.h"
|
|
|
|
|
#include "expr.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "libfuncs.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "insn-config.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "except.h"
|
|
|
|
|
#include "integrate.h"
|
|
|
|
|
#include "hard-reg-set.h"
|
|
|
|
|
#include "basic-block.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "output.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "dwarf2asm.h"
|
|
|
|
|
#include "dwarf2out.h"
|
|
|
|
|
#include "dwarf2.h"
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#include "toplev.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "hashtab.h"
|
1999-10-16 06:09:09 +00:00
|
|
|
|
#include "intl.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#include "ggc.h"
|
|
|
|
|
#include "tm_p.h"
|
|
|
|
|
#include "target.h"
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#include "langhooks.h"
|
2004-07-28 03:11:36 +00:00
|
|
|
|
#include "cgraph.h"
|
2007-05-19 01:19:51 +00:00
|
|
|
|
#include "diagnostic.h"
|
|
|
|
|
#include "tree-pass.h"
|
|
|
|
|
#include "timevar.h"
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* 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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Protect cleanup actions with must-not-throw regions, with a call
|
|
|
|
|
to the given failure handler. */
|
2004-07-28 03:11:36 +00:00
|
|
|
|
tree (*lang_protect_cleanup_actions) (void);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Return true if type A catches type B. */
|
2004-07-28 03:11:36 +00:00
|
|
|
|
int (*lang_eh_type_covers) (tree a, tree b);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Map a type to a runtime object to match type. */
|
2004-07-28 03:11:36 +00:00
|
|
|
|
tree (*lang_eh_runtime_type) (tree);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
/* A hash table of label to region number. */
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct ehl_map_entry GTY(())
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
|
|
|
|
rtx label;
|
|
|
|
|
struct eh_region *region;
|
|
|
|
|
};
|
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
static GTY(()) int call_site_base;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
static GTY ((param_is (union tree_node)))
|
|
|
|
|
htab_t type_to_runtime_map;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Describe the SjLj_Function_Context structure. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
static GTY(()) tree sjlj_fc_type_node;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_region GTY(())
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
/* The immediately surrounding region. */
|
|
|
|
|
struct eh_region *outer;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* The list of immediately contained regions. */
|
|
|
|
|
struct eh_region *inner;
|
|
|
|
|
struct eh_region *next_peer;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* An identifier for this region. */
|
|
|
|
|
int region_number;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
/* When a region is deleted, its parents inherit the REG_EH_REGION
|
|
|
|
|
numbers already assigned. */
|
|
|
|
|
bitmap aka;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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,
|
2007-05-19 01:19:51 +00:00
|
|
|
|
ERT_THROW
|
2002-02-01 18:16:02 +00:00
|
|
|
|
} type;
|
|
|
|
|
|
|
|
|
|
/* Holds the action to perform based on the preceding type. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
union eh_region_u {
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* A list of catch blocks, a surrounding try block,
|
|
|
|
|
and the label for continuing after a catch. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_region_u_try {
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct eh_region *catch;
|
|
|
|
|
struct eh_region *last_catch;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
} GTY ((tag ("ERT_TRY"))) try;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* The list through the catch handlers, the list of type objects
|
|
|
|
|
matched, and the list of associated filters. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_region_u_catch {
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct eh_region *next_catch;
|
|
|
|
|
struct eh_region *prev_catch;
|
|
|
|
|
tree type_list;
|
|
|
|
|
tree filter_list;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
} GTY ((tag ("ERT_CATCH"))) catch;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* A tree_list of allowed types. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_region_u_allowed {
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree type_list;
|
|
|
|
|
int filter;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
} GTY ((tag ("ERT_ALLOWED_EXCEPTIONS"))) allowed;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* The type given by a call to "throw foo();", or discovered
|
|
|
|
|
for a throw. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_region_u_throw {
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree type;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
} GTY ((tag ("ERT_THROW"))) throw;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Retain the cleanup expression even after expansion so that
|
|
|
|
|
we can match up fixup regions. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_region_u_cleanup {
|
|
|
|
|
struct eh_region *prev_try;
|
|
|
|
|
} GTY ((tag ("ERT_CLEANUP"))) cleanup;
|
|
|
|
|
} GTY ((desc ("%0.type"))) u;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Entry point for this region's handler before landing pads are built. */
|
|
|
|
|
rtx label;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tree tree_label;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Entry point for this region's handler from the runtime eh library. */
|
|
|
|
|
rtx landing_pad;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Entry point for this region's handler from an inner region. */
|
|
|
|
|
rtx post_landing_pad;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* The RESX insn for handing off control to the next outermost handler,
|
|
|
|
|
if appropriate. */
|
|
|
|
|
rtx resume;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
|
|
|
|
|
/* True if something in this region may throw. */
|
|
|
|
|
unsigned may_contain_throw : 1;
|
|
|
|
|
};
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
typedef struct eh_region *eh_region;
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct call_site_record GTY(())
|
|
|
|
|
{
|
|
|
|
|
rtx landing_pad;
|
|
|
|
|
int action;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
};
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
DEF_VEC_P(eh_region);
|
|
|
|
|
DEF_VEC_ALLOC_P(eh_region, gc);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Used to save exception status for each function. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct eh_status GTY(())
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
/* The tree of all regions for this function. */
|
|
|
|
|
struct eh_region *region_tree;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* The same information as an indexable array. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
VEC(eh_region,gc) *region_array;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* The most recently open region. */
|
|
|
|
|
struct eh_region *cur_region;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* This is the region for which we are processing catch blocks. */
|
|
|
|
|
struct eh_region *try_region;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx filter;
|
|
|
|
|
rtx exc_ptr;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int built_landing_pads;
|
|
|
|
|
int last_region_number;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
VEC(tree,gc) *ttype_data;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
varray_type ehspec_data;
|
|
|
|
|
varray_type action_record_data;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_t GTY ((param_is (struct ehl_map_entry))) exception_handler_label_map;
|
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
struct call_site_record * GTY ((length ("%h.call_site_data_used")))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
call_site_data;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
htab_t GTY((param_is (struct throw_stmt_node))) throw_stmt_table;
|
|
|
|
|
};
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
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);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
struct sjlj_lp_info;
|
2004-07-28 03:11:36 +00:00
|
|
|
|
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 *);
|
|
|
|
|
static void sjlj_emit_function_enter (rtx);
|
|
|
|
|
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 *);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
};
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct reachable_info;
|
2004-07-28 03:11:36 +00:00
|
|
|
|
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);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#ifndef HAVE_AS_LEB128
|
2004-07-28 03:11:36 +00:00
|
|
|
|
static int dw2_size_of_call_site_table (void);
|
|
|
|
|
static int sjlj_size_of_call_site_table (void);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#endif
|
2004-07-28 03:11:36 +00:00
|
|
|
|
static void dw2_output_call_site_table (void);
|
|
|
|
|
static void sjlj_output_call_site_table (void);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Routine to see if exception handling is turned on.
|
2003-07-11 03:40:53 +00:00
|
|
|
|
DO_WARN is nonzero if we want to inform the user that exception
|
2002-02-01 18:16:02 +00:00
|
|
|
|
handling is turned off.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
This is used to ensure that -fexceptions has been specified if the
|
|
|
|
|
compiler tries to use any exception-specific functions. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
doing_eh (int do_warn)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
init_eh (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
if (! flag_exceptions)
|
|
|
|
|
return;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
type_to_runtime_map = htab_create_ggc (31, t2r_hash, t2r_eq, NULL);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
sjlj_fc_type_node = lang_hooks.types.make_type (RECORD_TYPE);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
f_cs = build_decl (FIELD_DECL, get_identifier ("__call_site"),
|
|
|
|
|
integer_type_node);
|
|
|
|
|
DECL_FIELD_CONTEXT (f_cs) = sjlj_fc_type_node;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tmp = build_index_type (build_int_cst (NULL_TREE, 4 - 1));
|
|
|
|
|
tmp = build_array_type (lang_hooks.types.type_for_mode (word_mode, 1),
|
2003-07-11 03:40:53 +00:00
|
|
|
|
tmp);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
f_data = build_decl (FIELD_DECL, get_identifier ("__data"), tmp);
|
|
|
|
|
DECL_FIELD_CONTEXT (f_data) = sjlj_fc_type_node;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
f_per = build_decl (FIELD_DECL, get_identifier ("__personality"),
|
|
|
|
|
ptr_type_node);
|
|
|
|
|
DECL_FIELD_CONTEXT (f_per) = sjlj_fc_type_node;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
f_lsda = build_decl (FIELD_DECL, get_identifier ("__lsda"),
|
|
|
|
|
ptr_type_node);
|
|
|
|
|
DECL_FIELD_CONTEXT (f_lsda) = sjlj_fc_type_node;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#ifdef DONT_USE_BUILTIN_SETJMP
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#ifdef JMP_BUF_SIZE
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tmp = build_int_cst (NULL_TREE, JMP_BUF_SIZE - 1);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#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. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tmp = build_int_cst (NULL_TREE, FIRST_PSEUDO_REGISTER + 2 - 1);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#endif
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#else
|
2004-07-28 03:11:36 +00:00
|
|
|
|
/* builtin_setjmp takes a pointer to 5 words. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tmp = build_int_cst (NULL_TREE, 5 * BITS_PER_WORD / POINTER_SIZE - 1);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#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);
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
init_eh_for_function (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2004-07-28 03:11:36 +00:00
|
|
|
|
cfun->eh = ggc_alloc_cleared (sizeof (struct eh_status));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Routines to generate the exception tree somewhat directly.
|
|
|
|
|
These are used from tree-eh.c when processing exception related
|
|
|
|
|
nodes during tree optimization. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
static struct eh_region *
|
|
|
|
|
gen_eh_region (enum eh_region_type type, struct eh_region *outer)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *new;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
#ifdef ENABLE_CHECKING
|
|
|
|
|
gcc_assert (doing_eh (0));
|
|
|
|
|
#endif
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Insert a new blank region as a leaf in the tree. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
new = ggc_alloc_cleared (sizeof (*new));
|
|
|
|
|
new->type = type;
|
|
|
|
|
new->outer = outer;
|
|
|
|
|
if (outer)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
new->next_peer = outer->inner;
|
|
|
|
|
outer->inner = new;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
new->next_peer = cfun->eh->region_tree;
|
|
|
|
|
cfun->eh->region_tree = new;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
new->region_number = ++cfun->eh->last_region_number;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return new;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *
|
|
|
|
|
gen_eh_region_cleanup (struct eh_region *outer, struct eh_region *prev_try)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *cleanup = gen_eh_region (ERT_CLEANUP, outer);
|
|
|
|
|
cleanup->u.cleanup.prev_try = prev_try;
|
|
|
|
|
return cleanup;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *
|
|
|
|
|
gen_eh_region_try (struct eh_region *outer)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return gen_eh_region (ERT_TRY, outer);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *
|
|
|
|
|
gen_eh_region_catch (struct eh_region *t, tree type_or_list)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *c, *l;
|
|
|
|
|
tree type_list, type_node;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Ensure to always end up with a type list to normalize further
|
|
|
|
|
processing, then register each type against the runtime types map. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
type_list = type_or_list;
|
|
|
|
|
if (type_or_list)
|
|
|
|
|
{
|
|
|
|
|
if (TREE_CODE (type_or_list) != TREE_LIST)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
type_list = tree_cons (NULL_TREE, type_or_list, NULL_TREE);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
type_node = type_list;
|
|
|
|
|
for (; type_node; type_node = TREE_CHAIN (type_node))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
add_type_for_runtime (TREE_VALUE (type_node));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
c = gen_eh_region (ERT_CATCH, t->outer);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return c;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *
|
|
|
|
|
gen_eh_region_allowed (struct eh_region *outer, tree allowed)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer);
|
|
|
|
|
region->u.allowed.type_list = allowed;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
for (; allowed ; allowed = TREE_CHAIN (allowed))
|
|
|
|
|
add_type_for_runtime (TREE_VALUE (allowed));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return region;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *
|
|
|
|
|
gen_eh_region_must_not_throw (struct eh_region *outer)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return gen_eh_region (ERT_MUST_NOT_THROW, outer);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
int
|
|
|
|
|
get_eh_region_number (struct eh_region *region)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return region->region_number;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
bool
|
|
|
|
|
get_eh_region_may_contain_throw (struct eh_region *region)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return region->may_contain_throw;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tree
|
|
|
|
|
get_eh_region_tree_label (struct eh_region *region)
|
|
|
|
|
{
|
|
|
|
|
return region->tree_label;
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
2007-05-19 01:19:51 +00:00
|
|
|
|
set_eh_region_tree_label (struct eh_region *region, tree lab)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region->tree_label = lab;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
2007-05-19 01:19:51 +00:00
|
|
|
|
expand_resx_expr (tree exp)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_assert (!reg->resume);
|
|
|
|
|
reg->resume = emit_jump_insn (gen_rtx_RESX (VOIDmode, region_nr));
|
|
|
|
|
emit_barrier ();
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
/* 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
|
2007-05-19 01:19:51 +00:00
|
|
|
|
note_eh_region_may_contain_throw (struct eh_region *region)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
|
|
|
|
while (region && !region->may_contain_throw)
|
|
|
|
|
{
|
|
|
|
|
region->may_contain_throw = 1;
|
|
|
|
|
region = region->outer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
void
|
|
|
|
|
note_current_region_may_contain_throw (void)
|
|
|
|
|
{
|
|
|
|
|
note_eh_region_may_contain_throw (cfun->eh->cur_region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Return an rtl expression for a pointer to the exception object
|
|
|
|
|
within a handler. */
|
|
|
|
|
|
|
|
|
|
rtx
|
2004-07-28 03:11:36 +00:00
|
|
|
|
get_exception_pointer (struct function *fun)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx exc_ptr = fun->eh->exc_ptr;
|
|
|
|
|
if (fun == cfun && ! exc_ptr)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2003-07-11 03:40:53 +00:00
|
|
|
|
exc_ptr = gen_reg_rtx (ptr_mode);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fun->eh->exc_ptr = exc_ptr;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return exc_ptr;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Return an rtl expression for the exception dispatch filter
|
|
|
|
|
within a handler. */
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
rtx
|
2004-07-28 03:11:36 +00:00
|
|
|
|
get_exception_filter (struct function *fun)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
rtx filter = fun->eh->filter;
|
|
|
|
|
if (fun == cfun && ! filter)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
filter = gen_reg_rtx (targetm.eh_return_filter_mode ());
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fun->eh->filter = filter;
|
|
|
|
|
}
|
|
|
|
|
return filter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This section is for the exception handling specific optimization pass. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Random access the exception region tree. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
collect_eh_region_array (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *i;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
i = cfun->eh->region_tree;
|
|
|
|
|
if (! i)
|
|
|
|
|
return;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
while (1)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
VEC_replace (eh_region, cfun->eh->region_array, i->region_number, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Remove all regions whose labels are not reachable from insns. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
remove_unreachable_regions (rtx insns)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i, *uid_region_num;
|
|
|
|
|
bool *reachable;
|
|
|
|
|
struct eh_region *r;
|
|
|
|
|
rtx insn;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
uid_region_num = xcalloc (get_max_uid (), sizeof(int));
|
|
|
|
|
reachable = xcalloc (cfun->eh->last_region_number + 1, sizeof(bool));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
r = VEC_index (eh_region, cfun->eh->region_array, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (!r || r->region_number != i)
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (r->resume)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_assert (!uid_region_num[INSN_UID (r->resume)]);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
uid_region_num[INSN_UID (r->resume)] = i;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (r->label)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_assert (!uid_region_num[INSN_UID (r->label)]);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
uid_region_num[INSN_UID (r->label)] = i;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (insn = insns; insn; insn = NEXT_INSN (insn))
|
2007-05-19 01:19:51 +00:00
|
|
|
|
reachable[uid_region_num[INSN_UID (insn)]] = true;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
r = VEC_index (eh_region, cfun->eh->region_array, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (r && r->region_number == i && !reachable[i])
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
bool kill_it = true;
|
|
|
|
|
switch (r->type)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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])
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
kill_it = false;
|
|
|
|
|
break;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
if (kill_it)
|
|
|
|
|
remove_eh_handler (r);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
free (reachable);
|
|
|
|
|
free (uid_region_num);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Set up EH labels for RTL. */
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
convert_from_eh_region_ranges (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
rtx insns = get_insns ();
|
|
|
|
|
int i, n = cfun->eh->last_region_number;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, i);
|
|
|
|
|
if (region && region->tree_label)
|
|
|
|
|
region->label = DECL_RTL_IF_SET (region->tree_label);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
remove_unreachable_regions (insns);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
add_ehl_entry (rtx label, struct eh_region *region)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
|
|
|
|
struct ehl_map_entry **slot, *entry;
|
|
|
|
|
|
|
|
|
|
LABEL_PRESERVE_P (label) = 1;
|
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
entry = ggc_alloc (sizeof (*entry));
|
2002-05-09 20:02:13 +00:00
|
|
|
|
entry->label = label;
|
|
|
|
|
entry->region = region;
|
|
|
|
|
|
|
|
|
|
slot = (struct ehl_map_entry **)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_find_slot (cfun->eh->exception_handler_label_map, entry, INSERT);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
|
|
|
|
|
/* 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. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_assert (!*slot || cfun->eh->built_landing_pads);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
|
|
|
|
|
*slot = entry;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
find_exception_handler_labels (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
if (cfun->eh->exception_handler_label_map)
|
|
|
|
|
htab_empty (cfun->eh->exception_handler_label_map);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* ??? The expansion factor here (3/2) must be greater than the htab
|
|
|
|
|
occupancy factor (4/3) to avoid unnecessary resizing. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
cfun->eh->exception_handler_label_map
|
|
|
|
|
= htab_create_ggc (cfun->eh->last_region_number * 3 / 2,
|
|
|
|
|
ehl_hash, ehl_eq, NULL);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (cfun->eh->region_tree == NULL)
|
|
|
|
|
return;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *region;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx lab;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! region || region->region_number != i)
|
|
|
|
|
continue;
|
|
|
|
|
if (cfun->eh->built_landing_pads)
|
|
|
|
|
lab = region->landing_pad;
|
|
|
|
|
else
|
|
|
|
|
lab = region->label;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (lab)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
add_ehl_entry (lab, region);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* For sjlj exceptions, need the return label to remain live until
|
|
|
|
|
after landing pad generation. */
|
|
|
|
|
if (USING_SJLJ_EXCEPTIONS && ! cfun->eh->built_landing_pads)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
add_ehl_entry (return_label, NULL);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Returns true if the current function has exception handling regions. */
|
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
bool
|
2004-07-28 03:11:36 +00:00
|
|
|
|
current_function_has_exception_handlers (void)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *region;
|
2002-05-09 20:02:13 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, i);
|
|
|
|
|
if (region
|
|
|
|
|
&& region->region_number == i
|
|
|
|
|
&& region->type != ERT_THROW)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* A subroutine of duplicate_eh_regions. Search the region tree under O
|
|
|
|
|
for the minimum and maximum region numbers. Update *MIN and *MAX. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (o->inner)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
static eh_region
|
|
|
|
|
duplicate_eh_regions_1 (eh_region old, eh_region outer, int eh_offset)
|
|
|
|
|
{
|
|
|
|
|
eh_region ret, n;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
ret = n = ggc_alloc (sizeof (struct eh_region));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
*n = *old;
|
|
|
|
|
n->outer = outer;
|
|
|
|
|
n->next_peer = NULL;
|
|
|
|
|
gcc_assert (!old->aka);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
n->region_number += eh_offset;
|
|
|
|
|
VEC_replace (eh_region, cfun->eh->region_array, n->region_number, n);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (old->inner)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return ret;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
eh_region cur, prev_try, outer, *splice;
|
|
|
|
|
int i, min_region, max_region, eh_offset, cfun_last_region_number;
|
|
|
|
|
int num_regions;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (!ifun->eh->region_tree)
|
|
|
|
|
return 0;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
cur = VEC_index (eh_region, ifun->eh->region_array, copy_region);
|
|
|
|
|
duplicate_eh_regions_0 (cur, &min_region, &max_region);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Locate the spot at which to insert the new tree. */
|
|
|
|
|
if (outer_region > 0)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
outer = VEC_index (eh_region, cfun->eh->region_array, outer_region);
|
|
|
|
|
splice = &outer->inner;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
outer = NULL;
|
|
|
|
|
splice = &cfun->eh->region_tree;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
while (*splice)
|
|
|
|
|
splice = &(*splice)->next_peer;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Copy all the regions in the subtree. */
|
|
|
|
|
if (copy_region > 0)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
cur = VEC_index (eh_region, ifun->eh->region_array, copy_region);
|
|
|
|
|
*splice = duplicate_eh_regions_1 (cur, outer, eh_offset);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
eh_region n;
|
|
|
|
|
|
|
|
|
|
cur = ifun->eh->region_tree;
|
|
|
|
|
*splice = n = duplicate_eh_regions_1 (cur, outer, eh_offset);
|
|
|
|
|
while (cur->next_peer)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
cur = cur->next_peer;
|
|
|
|
|
n = n->next_peer = duplicate_eh_regions_1 (cur, outer, eh_offset);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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)
|
2007-08-14 02:45:23 +00:00
|
|
|
|
if (prev_try->type == ERT_MUST_NOT_THROW)
|
|
|
|
|
{
|
|
|
|
|
prev_try = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
t2r_eq (const void *pentry, const void *pdata)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
tree entry = (tree) pentry;
|
|
|
|
|
tree data = (tree) pdata;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return TREE_PURPOSE (entry) == data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static hashval_t
|
2004-07-28 03:11:36 +00:00
|
|
|
|
t2r_hash (const void *pentry)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
tree entry = (tree) pentry;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return TREE_HASH (TREE_PURPOSE (entry));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
add_type_for_runtime (tree type)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree *slot;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
|
2007-05-19 01:19:51 +00:00
|
|
|
|
TREE_HASH (type), INSERT);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (*slot == NULL)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
tree runtime = (*lang_eh_runtime_type) (type);
|
|
|
|
|
*slot = tree_cons (type, runtime, NULL_TREE);
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static tree
|
2004-07-28 03:11:36 +00:00
|
|
|
|
lookup_type_for_runtime (tree type)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
tree *slot;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
|
2007-05-19 01:19:51 +00:00
|
|
|
|
TREE_HASH (type), NO_INSERT);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* We should have always inserted the data earlier. */
|
|
|
|
|
return TREE_VALUE (*slot);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* Represent an entry in @TTypes for either catch actions
|
|
|
|
|
or exception filter actions. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
struct ttypes_filter GTY(())
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
tree t;
|
|
|
|
|
int filter;
|
|
|
|
|
};
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Compare ENTRY (a ttypes_filter entry in the hash table) with DATA
|
|
|
|
|
(a tree) for a @TTypes type node we are thinking about adding. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
ttypes_filter_eq (const void *pentry, const void *pdata)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
|
|
|
|
|
tree data = (tree) pdata;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return entry->t == data;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static hashval_t
|
2004-07-28 03:11:36 +00:00
|
|
|
|
ttypes_filter_hash (const void *pentry)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return TREE_HASH (entry->t);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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
|
2004-07-28 03:11:36 +00:00
|
|
|
|
ehspec_filter_eq (const void *pentry, const void *pdata)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
|
|
|
|
|
const struct ttypes_filter *data = (const struct ttypes_filter *) pdata;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return type_list_equal (entry->t, data->t);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Hash function for exception specification lists. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static hashval_t
|
2004-07-28 03:11:36 +00:00
|
|
|
|
ehspec_filter_hash (const void *pentry)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
|
|
|
|
|
hashval_t h = 0;
|
|
|
|
|
tree list;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (list = entry->t; list ; list = TREE_CHAIN (list))
|
2007-05-19 01:19:51 +00:00
|
|
|
|
h = (h << 5) + (h >> 27) + TREE_HASH (TREE_VALUE (list));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return h;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
add_ttypes_entry (htab_t ttypes_hash, tree type)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
struct ttypes_filter **slot, *n;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
slot = (struct ttypes_filter **)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
htab_find_slot_with_hash (ttypes_hash, type, TREE_HASH (type), INSERT);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if ((n = *slot) == NULL)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Filter value is a 1 based table index. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
n = XNEW (struct ttypes_filter);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
n->t = type;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
n->filter = VEC_length (tree, cfun->eh->ttype_data) + 1;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
*slot = n;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
VEC_safe_push (tree, gc, cfun->eh->ttype_data, type);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
return n->filter;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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. */
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
add_ehspec_entry (htab_t ehspec_hash, htab_t ttypes_hash, tree list)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct ttypes_filter **slot, *n;
|
|
|
|
|
struct ttypes_filter dummy;
|
|
|
|
|
|
|
|
|
|
dummy.t = list;
|
|
|
|
|
slot = (struct ttypes_filter **)
|
|
|
|
|
htab_find_slot (ehspec_hash, &dummy, INSERT);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if ((n = *slot) == NULL)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Filter value is a -1 based byte index into a uleb128 buffer. */
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
n = XNEW (struct ttypes_filter);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
n->t = list;
|
|
|
|
|
n->filter = -(VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) + 1);
|
|
|
|
|
*slot = n;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Generate a 0 terminated list of filter values. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (; list ; list = TREE_CHAIN (list))
|
2007-05-19 01:19:51 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return n->filter;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
assign_filter_values (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
|
|
|
|
htab_t ttypes, ehspec;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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");
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
ttypes = htab_create (31, ttypes_filter_hash, ttypes_filter_eq, free);
|
|
|
|
|
ehspec = htab_create (31, ehspec_filter_hash, ehspec_filter_eq, free);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *r;
|
|
|
|
|
|
|
|
|
|
r = VEC_index (eh_region, cfun->eh->region_array, i);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Mind we don't process a region more than once. */
|
|
|
|
|
if (!r || r->region_number != i)
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (;tp_node; tp_node = TREE_CHAIN (tp_node))
|
|
|
|
|
{
|
|
|
|
|
int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node));
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tree flt_node = build_int_cst (NULL_TREE, flt);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tree flt_node = build_int_cst (NULL_TREE, flt);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
r->u.catch.filter_list
|
|
|
|
|
= tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case ERT_ALLOWED_EXCEPTIONS:
|
|
|
|
|
r->u.allowed.filter
|
|
|
|
|
= add_ehspec_entry (ehspec, ttypes, r->u.allowed.type_list);
|
|
|
|
|
break;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
htab_delete (ttypes);
|
|
|
|
|
htab_delete (ehspec);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
/* Generate the code to actually handle exceptions, which will follow the
|
|
|
|
|
landing pads. */
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
build_post_landing_pads (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *region;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx seq;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Mind we don't process a region more than once. */
|
|
|
|
|
if (!region || region->region_number != i)
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
region->post_landing_pad = gen_label_rtx ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_label (region->post_landing_pad);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* ??? 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)),
|
2007-05-19 01:19:51 +00:00
|
|
|
|
EQ, NULL_RTX,
|
|
|
|
|
targetm.eh_return_filter_mode (), 0, c->label);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
tp_node = TREE_CHAIN (tp_node);
|
|
|
|
|
flt_node = TREE_CHAIN (flt_node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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 ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
emit_to_new_bb_before (seq, region->u.try.catch->label);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case ERT_ALLOWED_EXCEPTIONS:
|
|
|
|
|
region->post_landing_pad = gen_label_rtx ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_label (region->post_landing_pad);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_cmp_and_jump_insns (cfun->eh->filter,
|
|
|
|
|
GEN_INT (region->u.allowed.filter),
|
2007-05-19 01:19:51 +00:00
|
|
|
|
EQ, NULL_RTX,
|
|
|
|
|
targetm.eh_return_filter_mode (), 0, region->label);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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 ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
emit_to_new_bb_before (seq, region->label);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case ERT_CLEANUP:
|
|
|
|
|
case ERT_MUST_NOT_THROW:
|
|
|
|
|
region->post_landing_pad = region->label;
|
|
|
|
|
break;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case ERT_CATCH:
|
|
|
|
|
case ERT_THROW:
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
break;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
default:
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_unreachable ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Replace RESX patterns with jumps to the next handler if any, or calls to
|
|
|
|
|
_Unwind_Resume otherwise. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
connect_post_landing_pads (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *region;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct eh_region *outer;
|
|
|
|
|
rtx seq;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
rtx barrier;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (outer)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
else
|
2007-05-19 01:19:51 +00:00
|
|
|
|
{
|
|
|
|
|
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 ()));
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
2007-05-19 01:19:51 +00:00
|
|
|
|
barrier = emit_insn_before (seq, region->resume);
|
|
|
|
|
/* Avoid duplicate barrier. */
|
|
|
|
|
gcc_assert (BARRIER_P (barrier));
|
|
|
|
|
delete_insn (barrier);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
delete_insn (region->resume);
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
/* ??? 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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
dw2_build_landing_pads (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int i;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *region;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx seq;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
basic_block bb;
|
|
|
|
|
edge e;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, i);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Mind we don't process a region more than once. */
|
|
|
|
|
if (!region || region->region_number != i)
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (region->type != ERT_CLEANUP
|
|
|
|
|
&& region->type != ERT_TRY
|
|
|
|
|
&& region->type != ERT_ALLOWED_EXCEPTIONS)
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
region->landing_pad = gen_label_rtx ();
|
|
|
|
|
emit_label (region->landing_pad);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#ifdef HAVE_exception_receiver
|
|
|
|
|
if (HAVE_exception_receiver)
|
|
|
|
|
emit_insn (gen_exception_receiver ());
|
1999-10-16 06:09:09 +00:00
|
|
|
|
else
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef HAVE_nonlocal_goto_receiver
|
|
|
|
|
if (HAVE_nonlocal_goto_receiver)
|
|
|
|
|
emit_insn (gen_nonlocal_goto_receiver ());
|
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
{ /* Nothing */ }
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_move_insn (cfun->eh->exc_ptr,
|
2003-07-11 03:40:53 +00:00
|
|
|
|
gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0)));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_move_insn (cfun->eh->filter,
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gen_rtx_REG (targetm.eh_return_filter_mode (),
|
|
|
|
|
EH_RETURN_DATA_REGNO (1)));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
struct sjlj_lp_info
|
|
|
|
|
{
|
|
|
|
|
int directly_reachable;
|
|
|
|
|
int action_index;
|
|
|
|
|
int dispatch_index;
|
|
|
|
|
int call_site_index;
|
|
|
|
|
};
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static bool
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_find_directly_reachable_regions (struct sjlj_lp_info *lp_info)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx insn;
|
|
|
|
|
bool found_one = false;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
|
|
|
|
{
|
|
|
|
|
struct eh_region *region;
|
|
|
|
|
enum reachable_code rc;
|
|
|
|
|
tree type_thrown;
|
|
|
|
|
rtx note;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! INSN_P (insn))
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
|
|
|
|
|
if (!note || INTVAL (XEXP (note, 0)) <= 0)
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0)));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
type_thrown = NULL_TREE;
|
|
|
|
|
if (region->type == ERT_THROW)
|
|
|
|
|
{
|
|
|
|
|
type_thrown = region->u.throw.type;
|
|
|
|
|
region = region->outer;
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
rc = reachable_next_level (region, type_thrown, NULL);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return found_one;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_assign_call_site_values (rtx dispatch_label, struct sjlj_lp_info *lp_info)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
htab_t ar_hash;
|
|
|
|
|
int i, index;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* First task: build the action table. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
VARRAY_UCHAR_INIT (cfun->eh->action_record_data, 64, "action_record_data");
|
|
|
|
|
ar_hash = htab_create (31, action_record_hash, action_record_eq, free);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
|
|
|
|
if (lp_info[i].directly_reachable)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct eh_region *r = VEC_index (eh_region, cfun->eh->region_array, i);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
htab_delete (ar_hash);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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.
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_mark_call_sites (struct sjlj_lp_info *lp_info)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int last_call_site = -2;
|
|
|
|
|
rtx insn, mem;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
|
|
|
|
{
|
|
|
|
|
struct eh_region *region;
|
|
|
|
|
int this_call_site;
|
|
|
|
|
rtx note, before, p;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Reset value tracking at extended basic block boundaries. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (LABEL_P (insn))
|
2002-02-01 18:16:02 +00:00
|
|
|
|
last_call_site = -2;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! INSN_P (insn))
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
|
|
|
|
|
if (!note)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Calls (and trapping insns) without notes are outside any
|
|
|
|
|
exception handling region in this function. Mark them as
|
|
|
|
|
no action. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (CALL_P (insn)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|| (flag_non_call_exceptions
|
|
|
|
|
&& may_trap_p (PATTERN (insn))))
|
|
|
|
|
this_call_site = -1;
|
|
|
|
|
else
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Calls that are known to not throw need not be marked. */
|
|
|
|
|
if (INTVAL (XEXP (note, 0)) <= 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0)));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (CALL_P (insn))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
before = find_first_parameter_load (insn, NULL_RTX);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
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 ();
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
emit_insn_before (p, before);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
last_call_site = this_call_site;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Construct the SjLj_Function_Context. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_emit_function_enter (rtx dispatch_label)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx fn_begin, fc, mem, seq;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
bool fn_begin_outside_block;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fc = cfun->eh->sjlj_fc;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
mem = adjust_address (fc, Pmode, sjlj_fc_lsda_ofs);
|
|
|
|
|
if (cfun->uses_eh_lsda)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
char buf[20];
|
2004-07-28 03:11:36 +00:00
|
|
|
|
rtx sym;
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", current_function_funcdef_no);
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
|
|
|
|
|
SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_LOCAL;
|
|
|
|
|
emit_move_insn (mem, sym);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
emit_move_insn (mem, const0_rtx);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
note = emit_note (NOTE_INSN_EXPECTED_VALUE);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, x, const0_rtx);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_library_call (unwind_sjlj_register_libfunc, LCT_NORMAL, VOIDmode,
|
|
|
|
|
1, XEXP (fc, 0), Pmode);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* ??? 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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
fn_begin_outside_block = true;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin))
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Call back from expand_function_end to know where we should put
|
|
|
|
|
the call to unwind_sjlj_unregister_libfunc if needed. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_emit_function_exit_after (rtx after)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
cfun->eh->sjlj_exit_after = after;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_emit_function_exit (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx seq;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
edge e;
|
|
|
|
|
edge_iterator ei;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_library_call (unwind_sjlj_unregister_libfunc, LCT_NORMAL, VOIDmode,
|
|
|
|
|
1, XEXP (cfun->eh->sjlj_fc, 0), Pmode);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* ??? 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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
int i, first_reachable;
|
|
|
|
|
rtx mem, dispatch, seq, fc;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
rtx before;
|
|
|
|
|
basic_block bb;
|
|
|
|
|
edge e;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
fc = cfun->eh->sjlj_fc;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
start_sequence ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_label (dispatch_label);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#ifndef DONT_USE_BUILTIN_SETJMP
|
|
|
|
|
expand_builtin_setjmp_receiver (dispatch_label);
|
|
|
|
|
#endif
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs);
|
2004-07-28 03:11:36 +00:00
|
|
|
|
if (word_mode != ptr_mode)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
#ifdef POINTERS_EXTEND_UNSIGNED
|
2004-07-28 03:11:36 +00:00
|
|
|
|
mem = convert_memory_address (ptr_mode, mem);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#else
|
2004-07-28 03:11:36 +00:00
|
|
|
|
mem = convert_to_mode (ptr_mode, mem, 0);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
emit_move_insn (cfun->eh->exc_ptr, mem);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs + UNITS_PER_WORD);
|
|
|
|
|
emit_move_insn (cfun->eh->filter, mem);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Jump to one of the directly reachable regions. */
|
|
|
|
|
/* ??? This really ought to be using a switch statement. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
first_reachable = 0;
|
|
|
|
|
for (i = cfun->eh->last_region_number; i > 0; --i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (! lp_info[i].directly_reachable)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (! first_reachable)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
first_reachable = i;
|
|
|
|
|
continue;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_cmp_and_jump_insns (dispatch, GEN_INT (lp_info[i].dispatch_index),
|
|
|
|
|
EQ, NULL_RTX, TYPE_MODE (integer_type_node), 0,
|
2007-05-19 01:19:51 +00:00
|
|
|
|
((struct eh_region *)VEC_index (eh_region, cfun->eh->region_array, i))
|
|
|
|
|
->post_landing_pad);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seq = get_insns ();
|
|
|
|
|
end_sequence ();
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_build_landing_pads (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
struct sjlj_lp_info *lp_info;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
lp_info = XCNEWVEC (struct sjlj_lp_info, cfun->eh->last_region_number + 1);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
sjlj_emit_function_enter (dispatch_label);
|
|
|
|
|
sjlj_emit_dispatch_table (dispatch_label, lp_info);
|
|
|
|
|
sjlj_emit_function_exit ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (lp_info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
finish_eh_generation (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
basic_block bb;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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 ();
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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));
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
static hashval_t
|
2004-07-28 03:11:36 +00:00
|
|
|
|
ehl_hash (const void *pentry)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2004-07-28 03:11:36 +00:00
|
|
|
|
ehl_eq (const void *pentry, const void *pdata)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* This section handles removing dead code for flow. */
|
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
/* Remove LABEL from exception_handler_label_map. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
remove_exception_handler_label (rtx label)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2002-05-09 20:02:13 +00:00
|
|
|
|
struct ehl_map_entry **slot, tmp;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
/* If exception_handler_label_map was not built yet,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
there is nothing to do. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
if (cfun->eh->exception_handler_label_map == NULL)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
tmp.label = label;
|
|
|
|
|
slot = (struct ehl_map_entry **)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT);
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_assert (slot);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Splice REGION from the region tree etc. */
|
|
|
|
|
|
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
remove_eh_handler (struct eh_region *region)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2002-05-09 20:02:13 +00:00
|
|
|
|
struct eh_region **pp, **pp_start, *p, *outer, *inner;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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
|
2002-05-09 20:02:13 +00:00
|
|
|
|
multiple copies of this region in the array, so we have a
|
|
|
|
|
list of alternate numbers by which we are known. */
|
|
|
|
|
|
|
|
|
|
outer = region->outer;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
VEC_replace (eh_region, cfun->eh->region_array, region->region_number, outer);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
if (region->aka)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2002-05-09 20:02:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outer)
|
|
|
|
|
{
|
|
|
|
|
if (!outer->aka)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
outer->aka = BITMAP_GGC_ALLOC ();
|
2002-05-09 20:02:13 +00:00
|
|
|
|
if (region->aka)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
bitmap_ior_into (outer->aka, region->aka);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
bitmap_set_bit (outer->aka, region->region_number);
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
if (cfun->eh->built_landing_pads)
|
|
|
|
|
lab = region->landing_pad;
|
|
|
|
|
else
|
|
|
|
|
lab = region->label;
|
|
|
|
|
if (lab)
|
|
|
|
|
remove_exception_handler_label (lab);
|
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
if (outer)
|
|
|
|
|
pp_start = &outer->inner;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
2002-05-09 20:02:13 +00:00
|
|
|
|
pp_start = &cfun->eh->region_tree;
|
|
|
|
|
for (pp = pp_start, p = *pp; p != region; pp = &p->next_peer, p = *pp)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
continue;
|
2002-05-09 20:02:13 +00:00
|
|
|
|
*pp = region->next_peer;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
inner = region->inner;
|
|
|
|
|
if (inner)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2002-05-09 20:02:13 +00:00
|
|
|
|
for (p = inner; p->next_peer ; p = p->next_peer)
|
|
|
|
|
p->outer = outer;
|
|
|
|
|
p->outer = outer;
|
|
|
|
|
|
|
|
|
|
p->next_peer = *pp_start;
|
|
|
|
|
*pp_start = inner;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_assert (try->type == ERT_TRY);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
try->u.try.catch = next;
|
|
|
|
|
if (! next)
|
|
|
|
|
remove_eh_handler (try);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2004-07-28 03:11:36 +00:00
|
|
|
|
maybe_remove_eh_handler (rtx label)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2002-05-09 20:02:13 +00:00
|
|
|
|
struct ehl_map_entry **slot, tmp;
|
|
|
|
|
struct eh_region *region;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* ??? 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;
|
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
tmp.label = label;
|
|
|
|
|
slot = (struct ehl_map_entry **)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
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)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
region->label = NULL_RTX;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-05-09 20:02:13 +00:00
|
|
|
|
else
|
|
|
|
|
remove_eh_handler (region);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-05-09 20:02:13 +00:00
|
|
|
|
/* Invokes CALLBACK for every exception handler label. Only used by old
|
|
|
|
|
loop hackery; should not be used by new code. */
|
|
|
|
|
|
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
for_each_eh_label (void (*callback) (rtx))
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
2003-07-11 03:40:53 +00:00
|
|
|
|
htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1,
|
2004-07-28 03:11:36 +00:00
|
|
|
|
(void *) &callback);
|
2002-05-09 20:02:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
for_each_eh_label_1 (void **pentry, void *data)
|
2002-05-09 20:02:13 +00:00
|
|
|
|
{
|
|
|
|
|
struct ehl_map_entry *entry = *(struct ehl_map_entry **)pentry;
|
2004-07-28 03:11:36 +00:00
|
|
|
|
void (*callback) (rtx) = *(void (**) (rtx)) data;
|
2002-05-09 20:02:13 +00:00
|
|
|
|
|
|
|
|
|
(*callback) (entry->label);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* This section describes CFG exception edges for flow. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* For communicating between calls to reachable_next_level. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
struct reachable_info
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
tree types_caught;
|
|
|
|
|
tree types_allowed;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
void (*callback) (struct eh_region *, void *);
|
|
|
|
|
void *callback_data;
|
|
|
|
|
bool saw_any_handlers;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* A subroutine of reachable_next_level. Return true if TYPE, or a
|
|
|
|
|
base class of TYPE, is in HANDLED. */
|
|
|
|
|
|
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
check_handled (tree handled, tree type)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
2007-05-19 01:19:51 +00:00
|
|
|
|
add_reachable_handler (struct reachable_info *info,
|
|
|
|
|
struct eh_region *lp_region, struct eh_region *region)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
if (! info)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
info->saw_any_handlers = true;
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if (cfun->eh->built_landing_pads)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
info->callback (lp_region, info->callback_data);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
else
|
2007-05-19 01:19:51 +00:00
|
|
|
|
info->callback (region, info->callback_data);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2004-07-28 03:11:36 +00:00
|
|
|
|
reachable_next_level (struct eh_region *region, tree type_thrown,
|
|
|
|
|
struct reachable_info *info)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
switch (region->type)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
add_reachable_handler (info, region, region);
|
|
|
|
|
return RNL_CAUGHT;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
add_reachable_handler (info, region, region);
|
|
|
|
|
return RNL_CAUGHT;
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
add_reachable_handler (info, region, region);
|
|
|
|
|
return RNL_MAYBE_CAUGHT;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
case ERT_CATCH:
|
2004-07-28 03:11:36 +00:00
|
|
|
|
/* Catch regions are handled by their controlling try region. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
add_reachable_handler (info, region, region);
|
2003-07-11 03:40:53 +00:00
|
|
|
|
return RNL_CAUGHT;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
return RNL_BLOCKED;
|
|
|
|
|
|
|
|
|
|
case ERT_THROW:
|
|
|
|
|
case ERT_UNKNOWN:
|
|
|
|
|
/* Shouldn't see these here. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_unreachable ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
break;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
default:
|
|
|
|
|
gcc_unreachable ();
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Invoke CALLBACK on each region reachable from REGION_NUMBER. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
void
|
|
|
|
|
foreach_reachable_handler (int region_number, bool is_resx,
|
|
|
|
|
void (*callback) (struct eh_region *, void *),
|
|
|
|
|
void *callback_data)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
struct reachable_info info;
|
|
|
|
|
struct eh_region *region;
|
|
|
|
|
tree type_thrown;
|
|
|
|
|
|
|
|
|
|
memset (&info, 0, sizeof (info));
|
2007-05-19 01:19:51 +00:00
|
|
|
|
info.callback = callback;
|
|
|
|
|
info.callback_data = callback_data;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, region_number);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
type_thrown = NULL_TREE;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (is_resx)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
/* A RESX leaves a region instead of entering it. Thus the
|
|
|
|
|
region itself may have been deleted out from under us. */
|
|
|
|
|
if (region == NULL)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
region = region->outer;
|
|
|
|
|
}
|
|
|
|
|
else if (region->type == ERT_THROW)
|
|
|
|
|
{
|
|
|
|
|
type_thrown = region->u.throw.type;
|
|
|
|
|
region = region->outer;
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
}
|
2004-07-28 03:11:36 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
foreach_reachable_handler (region_number, is_resx,
|
|
|
|
|
(cfun->eh->built_landing_pads
|
|
|
|
|
? arh_to_landing_pad
|
|
|
|
|
: arh_to_label),
|
|
|
|
|
&handlers);
|
|
|
|
|
|
|
|
|
|
return handlers;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine if the given INSN can throw an exception that is caught
|
|
|
|
|
within the function. */
|
|
|
|
|
|
|
|
|
|
bool
|
2007-05-19 01:19:51 +00:00
|
|
|
|
can_throw_internal_1 (int region_number, bool is_resx)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct eh_region *region;
|
|
|
|
|
tree type_thrown;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, region_number);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
type_thrown = NULL_TREE;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (is_resx)
|
|
|
|
|
region = region->outer;
|
|
|
|
|
else if (region->type == ERT_THROW)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
enum reachable_code how = reachable_next_level (region, type_thrown, 0);
|
|
|
|
|
if (how == RNL_BLOCKED)
|
|
|
|
|
return false;
|
|
|
|
|
if (how != RNL_NOT_CAUGHT)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
return true;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Determine if the given INSN can throw an exception that is
|
|
|
|
|
visible outside the function. */
|
|
|
|
|
|
|
|
|
|
bool
|
2007-05-19 01:19:51 +00:00
|
|
|
|
can_throw_external_1 (int region_number, bool is_resx)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
struct eh_region *region;
|
|
|
|
|
tree type_thrown;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx note;
|
|
|
|
|
|
|
|
|
|
if (! INSN_P (insn))
|
|
|
|
|
return false;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
&& 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. */
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return (CALL_P (insn)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|| (flag_non_call_exceptions
|
|
|
|
|
&& may_trap_p (PATTERN (insn))));
|
|
|
|
|
}
|
|
|
|
|
if (INTVAL (XEXP (note, 0)) <= 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return can_throw_external_1 (INTVAL (XEXP (note, 0)), false);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* Set TREE_NOTHROW and cfun->all_throwers_are_sibcalls. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
unsigned int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
set_nothrow_function_flags (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
rtx insn;
|
2004-07-28 03:11:36 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
/* 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. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
cfun->all_throwers_are_sibcalls = 1;
|
|
|
|
|
|
|
|
|
|
if (! flag_exceptions)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return 0;
|
2004-07-28 03:11:36 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
|
|
|
|
|
if (can_throw_external (insn))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
TREE_NOTHROW (current_function_decl) = 0;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (!CALL_P (insn) || !SIBLING_CALL_P (insn))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
|
|
|
|
cfun->all_throwers_are_sibcalls = 0;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return 0;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (insn = current_function_epilogue_delay_list; insn;
|
|
|
|
|
insn = XEXP (insn, 1))
|
|
|
|
|
if (can_throw_external (insn))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
TREE_NOTHROW (current_function_decl) = 0;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (!CALL_P (insn) || !SIBLING_CALL_P (insn))
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
|
|
|
|
cfun->all_throwers_are_sibcalls = 0;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return 0;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return 0;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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 */
|
|
|
|
|
};
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Various hooks for unwind library. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
|
|
|
|
/* Do any necessary initialization to access arbitrary stack frames.
|
|
|
|
|
On the SPARC, this means flushing the register windows. */
|
|
|
|
|
|
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
expand_builtin_unwind_init (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
|
|
|
|
/* Set this so all the registers get saved in our frame; we need to be
|
2002-02-01 18:16:02 +00:00
|
|
|
|
able to copy the saved values for any registers from frames we unwind. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
current_function_has_nonlocal_label = 1;
|
|
|
|
|
|
|
|
|
|
#ifdef SETUP_FRAME_ADDRESSES
|
|
|
|
|
SETUP_FRAME_ADDRESSES ();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx
|
2004-07-28 03:11:36 +00:00
|
|
|
|
expand_builtin_eh_return_data_regno (tree arglist)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
tree which = TREE_VALUE (arglist);
|
|
|
|
|
unsigned HOST_WIDE_INT iwhich;
|
|
|
|
|
|
|
|
|
|
if (TREE_CODE (which) != INTEGER_CST)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
error ("argument of %<__builtin_eh_return_regno%> must be constant");
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
/* Given a value extracted from the return address register or stack slot,
|
|
|
|
|
return the actual address encoded in that value. */
|
|
|
|
|
|
|
|
|
|
rtx
|
2004-07-28 03:11:36 +00:00
|
|
|
|
expand_builtin_extract_return_addr (tree addr_tree)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
|
|
|
|
rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* First mask out any unwanted bits. */
|
|
|
|
|
#ifdef MASK_RETURN_ADDR
|
2002-05-09 20:02:13 +00:00
|
|
|
|
expand_and (Pmode, addr, MASK_RETURN_ADDR, addr);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Then adjust to find the real return address. */
|
|
|
|
|
#if defined (RETURN_ADDR_OFFSET)
|
|
|
|
|
addr = plus_constant (addr, RETURN_ADDR_OFFSET);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return addr;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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
|
2004-07-28 03:11:36 +00:00
|
|
|
|
expand_builtin_frob_return_addr (tree addr_tree)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
|
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
addr = convert_memory_address (Pmode, addr);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#ifdef RETURN_ADDR_OFFSET
|
2002-02-01 18:16:02 +00:00
|
|
|
|
addr = force_reg (Pmode, addr);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
addr = plus_constant (addr, -RETURN_ADDR_OFFSET);
|
|
|
|
|
#endif
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
return addr;
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Set up the epilogue with the magic bits we'll need to return to the
|
|
|
|
|
exception handler. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED,
|
|
|
|
|
tree handler_tree)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2003-07-11 03:40:53 +00:00
|
|
|
|
rtx tmp;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#ifdef EH_RETURN_STACKADJ_RTX
|
|
|
|
|
tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0);
|
2004-07-28 03:11:36 +00:00
|
|
|
|
tmp = convert_memory_address (Pmode, tmp);
|
2003-07-11 03:40:53 +00:00
|
|
|
|
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);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0);
|
2004-07-28 03:11:36 +00:00
|
|
|
|
tmp = convert_memory_address (Pmode, tmp);
|
2003-07-11 03:40:53 +00:00
|
|
|
|
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);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
if (!cfun->eh->ehr_label)
|
|
|
|
|
cfun->eh->ehr_label = gen_label_rtx ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_jump (cfun->eh->ehr_label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
expand_eh_return (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
2003-07-11 03:40:53 +00:00
|
|
|
|
rtx around_label;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
if (! cfun->eh->ehr_label)
|
|
|
|
|
return;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
current_function_calls_eh_return = 1;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#ifdef EH_RETURN_STACKADJ_RTX
|
|
|
|
|
emit_move_insn (EH_RETURN_STACKADJ_RTX, const0_rtx);
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
around_label = gen_label_rtx ();
|
|
|
|
|
emit_jump (around_label);
|
|
|
|
|
|
|
|
|
|
emit_label (cfun->eh->ehr_label);
|
|
|
|
|
clobber_return_register ();
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#ifdef EH_RETURN_STACKADJ_RTX
|
|
|
|
|
emit_move_insn (EH_RETURN_STACKADJ_RTX, cfun->eh->ehr_stackadj);
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#ifdef HAVE_eh_return
|
|
|
|
|
if (HAVE_eh_return)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
emit_insn (gen_eh_return (cfun->eh->ehr_handler));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
else
|
|
|
|
|
#endif
|
|
|
|
|
{
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#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
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
emit_label (around_label);
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
2004-07-28 03:11:36 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
/* 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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
Further, no special cases apply to the "next" field of the record.
|
|
|
|
|
For next, 0 means end of list. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct action_record
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int offset;
|
|
|
|
|
int filter;
|
|
|
|
|
int next;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
action_record_eq (const void *pentry, const void *pdata)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static hashval_t
|
2004-07-28 03:11:36 +00:00
|
|
|
|
action_record_hash (const void *pentry)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
const struct action_record *entry = (const struct action_record *) pentry;
|
|
|
|
|
return entry->next * 1009 + entry->filter;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
add_action_record (htab_t ar_hash, int filter, int next)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct action_record **slot, *new, tmp;
|
|
|
|
|
|
|
|
|
|
tmp.filter = filter;
|
|
|
|
|
tmp.next = next;
|
|
|
|
|
slot = (struct action_record **) htab_find_slot (ar_hash, &tmp, INSERT);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
if ((new = *slot) == NULL)
|
|
|
|
|
{
|
2004-07-28 03:11:36 +00:00
|
|
|
|
new = xmalloc (sizeof (*new));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
collect_one_action_chain (htab_t ar_hash, struct eh_region *region)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct eh_region *c;
|
|
|
|
|
int next;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
2004-07-28 03:11:36 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
2004-07-28 03:11:36 +00:00
|
|
|
|
return add_action_record (ar_hash, region->u.allowed.filter, next);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
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:
|
2007-05-19 01:19:51 +00:00
|
|
|
|
gcc_unreachable ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
add_call_site (rtx landing_pad, int action)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
1999-10-16 06:09:09 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
size = (size ? size * 2 : 64);
|
2004-07-28 03:11:36 +00:00
|
|
|
|
data = ggc_realloc (data, sizeof (*data) * size);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
cfun->eh->call_site_data = data;
|
|
|
|
|
cfun->eh->call_site_data_size = size;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
data[used].landing_pad = landing_pad;
|
|
|
|
|
data[used].action = action;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
cfun->eh->call_site_data_used = used + 1;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
return used + call_site_base;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
unsigned int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
convert_to_eh_region_ranges (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return 0;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (NONJUMP_INSN_P (insn)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
&& GET_CODE (PATTERN (insn)) == SEQUENCE)
|
|
|
|
|
insn = XVECEXP (PATTERN (insn), 0, 0);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
|
|
|
|
|
if (!note)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (! (CALL_P (insn)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|| (flag_non_call_exceptions
|
|
|
|
|
&& may_trap_p (PATTERN (insn)))))
|
|
|
|
|
continue;
|
|
|
|
|
this_action = -1;
|
|
|
|
|
region = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (INTVAL (XEXP (note, 0)) <= 0)
|
|
|
|
|
continue;
|
2007-05-19 01:19:51 +00:00
|
|
|
|
region = VEC_index (eh_region, cfun->eh->region_array, INTVAL (XEXP (note, 0)));
|
2002-02-01 18:16:02 +00:00
|
|
|
|
this_action = collect_one_action_chain (ar_hash, region);
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
2007-05-19 01:19:51 +00:00
|
|
|
|
return 0;
|
1999-10-16 06:09:09 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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 */
|
|
|
|
|
};
|
|
|
|
|
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
push_uleb128 (varray_type *data_area, unsigned int value)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
unsigned char byte = value & 0x7f;
|
|
|
|
|
value >>= 7;
|
|
|
|
|
if (value)
|
|
|
|
|
byte |= 0x80;
|
|
|
|
|
VARRAY_PUSH_UCHAR (*data_area, byte);
|
|
|
|
|
}
|
|
|
|
|
while (value);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
push_sleb128 (varray_type *data_area, int value)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned char byte;
|
|
|
|
|
int more;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
#ifndef HAVE_AS_LEB128
|
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
dw2_size_of_call_site_table (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int n = cfun->eh->call_site_data_used;
|
|
|
|
|
int size = n * (4 + 4 + 4);
|
|
|
|
|
int i;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < n; ++i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
struct call_site_record *cs = &cfun->eh->call_site_data[i];
|
|
|
|
|
size += size_of_uleb128 (cs->action);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
return size;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static int
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_size_of_call_site_table (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
dw2_output_call_site_table (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
int n = cfun->eh->call_site_data_used;
|
|
|
|
|
int i;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
for (i = 0; i < n; ++i)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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
|
2007-05-19 01:19:51 +00:00
|
|
|
|
dw2_asm_output_delta_uleb128 (reg_start_lab,
|
|
|
|
|
current_function_func_begin_label,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
"region %d start", i);
|
|
|
|
|
dw2_asm_output_delta_uleb128 (reg_end_lab, reg_start_lab,
|
|
|
|
|
"length");
|
|
|
|
|
if (cs->landing_pad)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
dw2_asm_output_delta_uleb128 (landing_pad_lab,
|
|
|
|
|
current_function_func_begin_label,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
"landing pad");
|
|
|
|
|
else
|
|
|
|
|
dw2_asm_output_data_uleb128 (0, "landing pad");
|
|
|
|
|
#else
|
2007-05-19 01:19:51 +00:00
|
|
|
|
dw2_asm_output_delta (4, reg_start_lab,
|
|
|
|
|
current_function_func_begin_label,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
"region %d start", i);
|
|
|
|
|
dw2_asm_output_delta (4, reg_end_lab, reg_start_lab, "length");
|
|
|
|
|
if (cs->landing_pad)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
dw2_asm_output_delta (4, landing_pad_lab,
|
|
|
|
|
current_function_func_begin_label,
|
2002-02-01 18:16:02 +00:00
|
|
|
|
"landing pad");
|
|
|
|
|
else
|
|
|
|
|
dw2_asm_output_data (4, 0, "landing pad");
|
|
|
|
|
#endif
|
|
|
|
|
dw2_asm_output_data_uleb128 (cs->action, "action");
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
|
|
|
|
call_site_base += n;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
static void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
sjlj_output_call_site_table (void)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
#ifndef TARGET_UNWIND_INFO
|
|
|
|
|
/* Switch to the section that should be used for exception tables. */
|
2003-07-11 03:40:53 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
static void
|
|
|
|
|
switch_to_exception_section (void)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (exception_section == 0)
|
2003-07-11 03:40:53 +00:00
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (targetm.have_named_sections)
|
|
|
|
|
{
|
|
|
|
|
int flags;
|
2003-07-11 03:40:53 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#endif
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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);
|
2003-07-11 03:40:53 +00:00
|
|
|
|
}
|
2007-05-19 01:19:51 +00:00
|
|
|
|
|
|
|
|
|
/* 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);
|
2003-07-11 03:40:53 +00:00
|
|
|
|
else
|
2007-05-19 01:19:51 +00:00
|
|
|
|
dw2_asm_output_encoded_addr_rtx (tt_format, value, public, NULL);
|
2003-07-11 03:40:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
void
|
2004-07-28 03:11:36 +00:00
|
|
|
|
output_function_exception_table (void)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
{
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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;
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
if (eh_personality_libfunc)
|
|
|
|
|
assemble_external_libcall (eh_personality_libfunc);
|
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* Not all functions need anything. */
|
|
|
|
|
if (! cfun->uses_eh_lsda)
|
1999-08-26 09:30:50 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
#ifdef TARGET_UNWIND_INFO
|
|
|
|
|
/* TODO: Move this into target file. */
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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
|
2007-05-19 01:19:51 +00:00
|
|
|
|
switch_to_exception_section ();
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#endif
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* If the target wants a label to begin the table, emit it here. */
|
|
|
|
|
targetm.asm_out.except_table_label (asm_out_file);
|
|
|
|
|
|
|
|
|
|
have_tt_data = (VEC_length (tree, cfun->eh->ttype_data) > 0
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|| VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) > 0);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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
|
2003-07-11 03:40:53 +00:00
|
|
|
|
ASM_GENERATE_INTERNAL_LABEL (ttype_label, "LLSDATT",
|
|
|
|
|
current_function_funcdef_no);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#endif
|
|
|
|
|
tt_format_size = size_of_encoded_value (tt_format);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
assemble_align (tt_format_size * BITS_PER_UNIT);
|
|
|
|
|
}
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
targetm.asm_out.internal_label (asm_out_file, "LLSDA",
|
2003-07-11 03:40:53 +00:00
|
|
|
|
current_function_funcdef_no);
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* The LSDA header. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* @LPStart pointer would go here. */
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
dw2_asm_output_data (1, tt_format, "@TType format (%s)",
|
|
|
|
|
eh_data_format_name (tt_format));
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
#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
|
1999-08-26 09:30:50 +00:00
|
|
|
|
|
2002-02-01 18:16:02 +00:00
|
|
|
|
/* 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",
|
2003-07-11 03:40:53 +00:00
|
|
|
|
current_function_funcdef_no);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
+ (VEC_length (tree, cfun->eh->ttype_data)
|
2002-02-01 18:16:02 +00:00
|
|
|
|
* 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",
|
2003-07-11 03:40:53 +00:00
|
|
|
|
current_function_funcdef_no);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
ASM_GENERATE_INTERNAL_LABEL (cs_end_label, "LLSDACSE",
|
2003-07-11 03:40:53 +00:00
|
|
|
|
current_function_funcdef_no);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
i = VEC_length (tree, cfun->eh->ttype_data);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
2007-05-19 01:19:51 +00:00
|
|
|
|
tree type = VEC_index (tree, cfun->eh->ttype_data, i);
|
|
|
|
|
output_ttype (type, tt_format, tt_format_size);
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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)
|
2007-05-19 01:19:51 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2002-02-01 18:16:02 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
/* 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;
|
2002-02-01 18:16:02 +00:00
|
|
|
|
}
|
2003-07-11 03:40:53 +00:00
|
|
|
|
|
2007-05-19 01:19:51 +00:00
|
|
|
|
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 */
|
|
|
|
|
};
|
|
|
|
|
|
2003-07-11 03:40:53 +00:00
|
|
|
|
#include "gt-except.h"
|