OSDN Git Service

* lib/compat.exp (compat-execute): Break up long lines.
[pf3gnuchains/gcc-fork.git] / gcc / except.c
index 5404f49..2dfd073 100644 (file)
@@ -1,24 +1,24 @@
 /* Implements exception handling.
-   Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
    Contributed by Mike Stump <mrs@cygnus.com>.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+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.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 
 /* An exception is an event that can be signaled from within a
@@ -49,11 +49,14 @@ Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "tree.h"
 #include "flags.h"
 #include "function.h"
 #include "expr.h"
+#include "libfuncs.h"
 #include "insn-config.h"
 #include "except.h"
 #include "integrate.h"
@@ -62,21 +65,18 @@ Boston, MA 02111-1307, USA.  */
 #include "output.h"
 #include "dwarf2asm.h"
 #include "dwarf2out.h"
+#include "dwarf2.h"
 #include "toplev.h"
 #include "hashtab.h"
 #include "intl.h"
 #include "ggc.h"
 #include "tm_p.h"
-
+#include "target.h"
+#include "langhooks.h"
+#include "cgraph.h"
 
 /* Provide defaults for stuff that may not be defined when using
    sjlj exceptions.  */
-#ifndef EH_RETURN_STACKADJ_RTX
-#define EH_RETURN_STACKADJ_RTX 0
-#endif
-#ifndef EH_RETURN_HANDLER_RTX
-#define EH_RETURN_HANDLER_RTX 0
-#endif
 #ifndef EH_RETURN_DATA_REGNO
 #define EH_RETURN_DATA_REGNO(N) INVALID_REGNUM
 #endif
@@ -87,23 +87,28 @@ int flag_non_call_exceptions;
 
 /* Protect cleanup actions with must-not-throw regions, with a call
    to the given failure handler.  */
-tree protect_cleanup_actions;
+tree (*lang_protect_cleanup_actions) (void);
 
 /* Return true if type A catches type B.  */
-int (*lang_eh_type_covers) PARAMS ((tree a, tree b));
+int (*lang_eh_type_covers) (tree a, tree b);
 
 /* Map a type to a runtime object to match type.  */
-tree (*lang_eh_runtime_type) PARAMS ((tree));
+tree (*lang_eh_runtime_type) (tree);
+
+/* A hash table of label to region number.  */
 
-/* A list of labels used for exception handlers.  */
-rtx exception_handler_labels;
+struct ehl_map_entry GTY(())
+{
+  rtx label;
+  struct eh_region *region;
+};
 
-static int call_site_base;
-static int sjlj_funcdef_number;
-static htab_t type_to_runtime_map;
+static GTY(()) int call_site_base;
+static GTY ((param_is (union tree_node)))
+  htab_t type_to_runtime_map;
 
 /* Describe the SjLj_Function_Context structure.  */
-static tree sjlj_fc_type_node;
+static GTY(()) tree sjlj_fc_type_node;
 static int sjlj_fc_call_site_ofs;
 static int sjlj_fc_data_ofs;
 static int sjlj_fc_personality_ofs;
@@ -111,7 +116,7 @@ static int sjlj_fc_lsda_ofs;
 static int sjlj_fc_jbuf_ofs;
 \f
 /* Describes one exception region.  */
-struct eh_region
+struct eh_region GTY(())
 {
   /* The immediately surrounding region.  */
   struct eh_region *outer;
@@ -123,10 +128,15 @@ struct eh_region
   /* An identifier for this region.  */
   int region_number;
 
+  /* When a region is deleted, its parents inherit the REG_EH_REGION
+     numbers already assigned.  */
+  bitmap aka;
+
   /* Each region does exactly one thing.  */
   enum eh_region_type
-  {
-    ERT_CLEANUP = 1,
+  { 
+    ERT_UNKNOWN = 0,
+    ERT_CLEANUP,
     ERT_TRY,
     ERT_CATCH,
     ERT_ALLOWED_EXCEPTIONS,
@@ -135,70 +145,86 @@ struct eh_region
     ERT_FIXUP
   } type;
 
-  /* Holds the action to perform based on the preceeding type.  */
-  union {
+  /* Holds the action to perform based on the preceding type.  */
+  union eh_region_u {
     /* A list of catch blocks, a surrounding try block,
        and the label for continuing after a catch.  */
-    struct {
+    struct eh_region_u_try {
       struct eh_region *catch;
       struct eh_region *last_catch;
       struct eh_region *prev_try;
       rtx continue_label;
-    } try;
+    } GTY ((tag ("ERT_TRY"))) try;
 
-    /* The list through the catch handlers, the type object
-       matched, and a pointer to the generated code.  */
-    struct {
+    /* The list through the catch handlers, the list of type objects
+       matched, and the list of associated filters.  */
+    struct eh_region_u_catch {
       struct eh_region *next_catch;
       struct eh_region *prev_catch;
-      tree type;
-      int filter;
-    } catch;
+      tree type_list;
+      tree filter_list;
+    } GTY ((tag ("ERT_CATCH"))) catch;
 
     /* A tree_list of allowed types.  */
-    struct {
+    struct eh_region_u_allowed {
       tree type_list;
       int filter;
-    } allowed;
+    } GTY ((tag ("ERT_ALLOWED_EXCEPTIONS"))) allowed;
 
-    /* The type given by a call to "throw foo();", or discovered 
+    /* The type given by a call to "throw foo();", or discovered
        for a throw.  */
-    struct {
+    struct eh_region_u_throw {
       tree type;
-    } throw;
+    } GTY ((tag ("ERT_THROW"))) throw;
 
     /* Retain the cleanup expression even after expansion so that
        we can match up fixup regions.  */
-    struct {
+    struct eh_region_u_cleanup {
       tree exp;
-    } cleanup;
+      struct eh_region *prev_try;
+    } GTY ((tag ("ERT_CLEANUP"))) cleanup;
 
     /* The real region (by expression and by pointer) that fixup code
        should live in.  */
-    struct {
+    struct eh_region_u_fixup {
       tree cleanup_exp;
       struct eh_region *real_region;
-    } fixup;
-  } u;
+      bool resolved;
+    } GTY ((tag ("ERT_FIXUP"))) fixup;
+  } GTY ((desc ("%0.type"))) u;
 
-  /* The region of code generated, or contained within, the region.  */
-  rtx label, last;
+  /* Entry point for this region's handler before landing pads are built.  */
+  rtx label;
+  tree tree_label;
 
-  /* Entry point for this region from the runtime eh library.  */
+  /* Entry point for this region's handler from the runtime eh library.  */
   rtx landing_pad;
 
-  /* Entry point for this region from an inner region.  */
+  /* Entry point for this region's handler from an inner region.  */
   rtx post_landing_pad;
+
+  /* The RESX insn for handing off control to the next outermost handler,
+     if appropriate.  */
+  rtx resume;
+
+  /* True if something in this region may throw.  */
+  unsigned may_contain_throw : 1;
+};
+
+struct call_site_record GTY(())
+{
+  rtx landing_pad;
+  int action;
 };
 
 /* Used to save exception status for each function.  */
-struct eh_status
+struct eh_status GTY(())
 {
   /* The tree of all regions for this function.  */
   struct eh_region *region_tree;
 
   /* The same information as an indexable array.  */
-  struct eh_region **region_array;
+  struct eh_region ** GTY ((length ("%h.last_region_number"))) region_array;
 
   /* The most recently open region.  */
   struct eh_region *cur_region;
@@ -206,12 +232,6 @@ struct eh_status
   /* This is the region for which we are processing catch blocks.  */
   struct eh_region *try_region;
 
-  /* A stack (TREE_LIST) of lists of handlers.  The TREE_VALUE of each
-     node is itself a TREE_CHAINed list of handlers for regions that
-     are not yet closed. The TREE_VALUE of each entry contains the
-     handler for the corresponding entry on the ehstack.  */
-  tree protect_list;
-
   rtx filter;
   rtx exc_ptr;
 
@@ -222,11 +242,10 @@ struct eh_status
   varray_type ehspec_data;
   varray_type action_record_data;
 
-  struct call_site_record
-  {
-    rtx landing_pad;
-    int action;
-  } *call_site_data;
+  htab_t GTY ((param_is (struct ehl_map_entry))) exception_handler_label_map;
+
+  struct call_site_record * GTY ((length ("%h.call_site_data_used")))
+    call_site_data;
   int call_site_data_used;
   int call_site_data_size;
 
@@ -239,58 +258,47 @@ struct eh_status
 };
 
 \f
-static void mark_eh_region                     PARAMS ((struct eh_region *));
-
-static int t2r_eq                              PARAMS ((const PTR,
-                                                        const PTR));
-static hashval_t t2r_hash                      PARAMS ((const PTR));
-static int t2r_mark_1                          PARAMS ((PTR *, PTR));
-static void t2r_mark                           PARAMS ((PTR));
-static void add_type_for_runtime               PARAMS ((tree));
-static tree lookup_type_for_runtime            PARAMS ((tree));
-
-static struct eh_region *expand_eh_region_end  PARAMS ((void));
-
-static void collect_eh_region_array            PARAMS ((void));
-static void resolve_fixup_regions              PARAMS ((void));
-static void remove_fixup_regions               PARAMS ((void));
-static void convert_from_eh_region_ranges_1    PARAMS ((rtx *, int *, int));
-
-static struct eh_region *duplicate_eh_region_1 PARAMS ((struct eh_region *,
-                                                    struct inline_remap *));
-static void duplicate_eh_region_2              PARAMS ((struct eh_region *,
-                                                        struct eh_region **));
-static int ttypes_filter_eq                    PARAMS ((const PTR,
-                                                        const PTR));
-static hashval_t ttypes_filter_hash            PARAMS ((const PTR));
-static int ehspec_filter_eq                    PARAMS ((const PTR,
-                                                        const PTR));
-static hashval_t ehspec_filter_hash            PARAMS ((const PTR));
-static int add_ttypes_entry                    PARAMS ((htab_t, tree));
-static int add_ehspec_entry                    PARAMS ((htab_t, htab_t,
-                                                        tree));
-static void assign_filter_values               PARAMS ((void));
-static void build_post_landing_pads            PARAMS ((void));
-static void connect_post_landing_pads          PARAMS ((void));
-static void dw2_build_landing_pads             PARAMS ((void));
+static int t2r_eq (const void *, const void *);
+static hashval_t t2r_hash (const void *);
+static void add_type_for_runtime (tree);
+static tree lookup_type_for_runtime (tree);
+
+static struct eh_region *expand_eh_region_end (void);
+
+static void resolve_fixup_regions (void);
+static void remove_fixup_regions (void);
+static void remove_unreachable_regions (rtx);
+static void convert_from_eh_region_ranges_1 (rtx *, int *, int);
+
+static struct eh_region *duplicate_eh_region_1 (struct eh_region *,
+                                               struct inline_remap *);
+static void duplicate_eh_region_2 (struct eh_region *, struct eh_region **);
+static int ttypes_filter_eq (const void *, const void *);
+static hashval_t ttypes_filter_hash (const void *);
+static int ehspec_filter_eq (const void *, const void *);
+static hashval_t ehspec_filter_hash (const void *);
+static int add_ttypes_entry (htab_t, tree);
+static int add_ehspec_entry (htab_t, htab_t, tree);
+static void assign_filter_values (void);
+static void build_post_landing_pads (void);
+static void connect_post_landing_pads (void);
+static void dw2_build_landing_pads (void);
 
 struct sjlj_lp_info;
-static bool sjlj_find_directly_reachable_regions
-     PARAMS ((struct sjlj_lp_info *));
-static void sjlj_assign_call_site_values
-     PARAMS ((rtx, struct sjlj_lp_info *));
-static void sjlj_mark_call_sites
-     PARAMS ((struct sjlj_lp_info *));
-static void sjlj_emit_function_enter           PARAMS ((rtx));
-static void sjlj_emit_function_exit            PARAMS ((void));
-static void sjlj_emit_dispatch_table
-     PARAMS ((rtx, struct sjlj_lp_info *));
-static void sjlj_build_landing_pads            PARAMS ((void));
-
-static void remove_exception_handler_label     PARAMS ((rtx));
-static void remove_eh_handler                  PARAMS ((struct eh_region *));
-
-struct reachable_info;
+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 *);
 
 /* The return value of reachable_next_level.  */
 enum reachable_code
@@ -305,43 +313,35 @@ enum reachable_code
   RNL_BLOCKED
 };
 
-static int check_handled                       PARAMS ((tree, tree));
-static void add_reachable_handler
-     PARAMS ((struct reachable_info *, struct eh_region *,
-             struct eh_region *));
-static enum reachable_code reachable_next_level
-     PARAMS ((struct eh_region *, tree, struct reachable_info *));
-
-static int action_record_eq                    PARAMS ((const PTR,
-                                                        const PTR));
-static hashval_t action_record_hash            PARAMS ((const PTR));
-static int add_action_record                   PARAMS ((htab_t, int, int));
-static int collect_one_action_chain            PARAMS ((htab_t,
-                                                        struct eh_region *));
-static int add_call_site                       PARAMS ((rtx, int));
-
-static void push_uleb128                       PARAMS ((varray_type *,
-                                                        unsigned int));
-static void push_sleb128                       PARAMS ((varray_type *, int));
-static const char *eh_data_format_name         PARAMS ((int));
+struct reachable_info;
+static enum reachable_code reachable_next_level (struct eh_region *, tree,
+                                                struct reachable_info *);
+
+static int action_record_eq (const void *, const void *);
+static hashval_t action_record_hash (const void *);
+static int add_action_record (htab_t, int, int);
+static int collect_one_action_chain (htab_t, struct eh_region *);
+static int add_call_site (rtx, int);
+
+static void push_uleb128 (varray_type *, unsigned int);
+static void push_sleb128 (varray_type *, int);
 #ifndef HAVE_AS_LEB128
-static int dw2_size_of_call_site_table         PARAMS ((void));
-static int sjlj_size_of_call_site_table                PARAMS ((void));
+static int dw2_size_of_call_site_table (void);
+static int sjlj_size_of_call_site_table (void);
 #endif
-static void dw2_output_call_site_table         PARAMS ((void));
-static void sjlj_output_call_site_table                PARAMS ((void));
+static void dw2_output_call_site_table (void);
+static void sjlj_output_call_site_table (void);
 
 \f
 /* Routine to see if exception handling is turned on.
-   DO_WARN is non-zero if we want to inform the user that exception
-   handling is turned off. 
+   DO_WARN is nonzero if we want to inform the user that exception
+   handling is turned off.
 
    This is used to ensure that -fexceptions has been specified if the
    compiler tries to use any exception-specific functions.  */
 
 int
-doing_eh (do_warn)
-     int do_warn;
+doing_eh (int do_warn)
 {
   if (! flag_exceptions)
     {
@@ -358,16 +358,12 @@ doing_eh (do_warn)
 
 \f
 void
-init_eh ()
+init_eh (void)
 {
-  ggc_add_rtx_root (&exception_handler_labels, 1);
-  ggc_add_tree_root (&protect_cleanup_actions, 1);
-
   if (! flag_exceptions)
     return;
 
-  type_to_runtime_map = htab_create (31, t2r_hash, t2r_eq, NULL);
-  ggc_add_root (&type_to_runtime_map, 1, sizeof (htab_t), t2r_mark);
+  type_to_runtime_map = htab_create_ggc (31, t2r_hash, t2r_eq, NULL);
 
   /* Create the SjLj_Function_Context structure.  This should match
      the definition in unwind-sjlj.c.  */
@@ -375,8 +371,7 @@ init_eh ()
     {
       tree f_jbuf, f_per, f_lsda, f_prev, f_cs, f_data, tmp;
 
-      sjlj_fc_type_node = make_lang_type (RECORD_TYPE);
-      ggc_add_tree_root (&sjlj_fc_type_node, 1);
+      sjlj_fc_type_node = lang_hooks.types.make_type (RECORD_TYPE);
 
       f_prev = build_decl (FIELD_DECL, get_identifier ("__prev"),
                           build_pointer_type (sjlj_fc_type_node));
@@ -387,7 +382,8 @@ init_eh ()
       DECL_FIELD_CONTEXT (f_cs) = sjlj_fc_type_node;
 
       tmp = build_index_type (build_int_2 (4 - 1, 0));
-      tmp = build_array_type (type_for_mode (word_mode, 1), tmp);
+      tmp = build_array_type (lang_hooks.types.type_for_mode (word_mode, 1),
+                             tmp);
       f_data = build_decl (FIELD_DECL, get_identifier ("__data"), tmp);
       DECL_FIELD_CONTEXT (f_data) = sjlj_fc_type_node;
 
@@ -410,10 +406,8 @@ init_eh ()
       tmp = build_int_2 (FIRST_PSEUDO_REGISTER + 2 - 1, 0);
 #endif
 #else
-      /* This is 2 for builtin_setjmp, plus whatever the target requires
-        via STACK_SAVEAREA_MODE (SAVE_NONLOCAL).  */
-      tmp = build_int_2 ((GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL))
-                         / GET_MODE_SIZE (Pmode)) + 2 - 1, 0);
+      /* builtin_setjmp takes a pointer to 5 words.  */
+      tmp = build_int_2 (5 * BITS_PER_WORD / POINTER_SIZE - 1, 0);
 #endif
       tmp = build_index_type (tmp);
       tmp = build_array_type (ptr_type_node, tmp);
@@ -456,261 +450,212 @@ init_eh ()
 }
 
 void
-init_eh_for_function ()
+init_eh_for_function (void)
 {
-  cfun->eh = (struct eh_status *) xcalloc (1, sizeof (struct eh_status));
+  cfun->eh = ggc_alloc_cleared (sizeof (struct eh_status));
 }
+\f
+/* Routines to generate the exception tree somewhat directly.  
+   These are used from tree-eh.c when processing exception related
+   nodes during tree optimization.  */
 
-/* Mark EH for GC.  */
-
-static void
-mark_eh_region (region)
-     struct eh_region *region;
+static struct eh_region *
+gen_eh_region (enum eh_region_type type, struct eh_region *outer)
 {
-  if (! region)
-    return;
+  struct eh_region *new;
 
-  switch (region->type)
+#ifdef ENABLE_CHECKING
+  if (! doing_eh (0))
+    abort ();
+#endif
+
+  /* Insert a new blank region as a leaf in the tree.  */
+  new = ggc_alloc_cleared (sizeof (*new));
+  new->type = type;
+  new->outer = outer;
+  if (outer)
     {
-    case ERT_CLEANUP:
-      ggc_mark_tree (region->u.cleanup.exp);
-      break;
-    case ERT_TRY:
-      ggc_mark_rtx (region->u.try.continue_label);
-      break;
-    case ERT_CATCH:
-      ggc_mark_tree (region->u.catch.type);
-      break;
-    case ERT_ALLOWED_EXCEPTIONS:
-      ggc_mark_tree (region->u.allowed.type_list);
-      break;
-    case ERT_MUST_NOT_THROW:
-      break;
-    case ERT_THROW:
-      ggc_mark_tree (region->u.throw.type);
-      break;
-    case ERT_FIXUP:
-      ggc_mark_tree (region->u.fixup.cleanup_exp);
-      break;
-    default:
-      abort ();
+      new->next_peer = outer->inner;
+      outer->inner = new;
     }
+  else
+    {
+      new->next_peer = cfun->eh->region_tree;
+      cfun->eh->region_tree = new;
+    }
+
+  new->region_number = ++cfun->eh->last_region_number;
 
-  ggc_mark_rtx (region->label);
-  ggc_mark_rtx (region->last);
-  ggc_mark_rtx (region->landing_pad);
-  ggc_mark_rtx (region->post_landing_pad);
+  return new;
 }
 
-void
-mark_eh_status (eh)
-     struct eh_status *eh;
+struct eh_region *
+gen_eh_region_cleanup (struct eh_region *outer, struct eh_region *prev_try)
 {
-  int i;
+  struct eh_region *cleanup = gen_eh_region (ERT_CLEANUP, outer);
+  cleanup->u.cleanup.prev_try = prev_try;
+  return cleanup;
+}
 
-  if (eh == 0)
-    return;
+struct eh_region *
+gen_eh_region_try (struct eh_region *outer)
+{
+  return gen_eh_region (ERT_TRY, outer);
+}
 
-  /* If we've called collect_eh_region_array, use it.  Otherwise walk
-     the tree non-recursively.  */
-  if (eh->region_array)
-    {
-      for (i = eh->last_region_number; i > 0; --i)
-       {
-         struct eh_region *r = eh->region_array[i];
-         if (r && r->region_number == i)
-           mark_eh_region (r);
-       }
-    }
-  else if (eh->region_tree)
+struct eh_region *
+gen_eh_region_catch (struct eh_region *t, tree type_or_list)
+{
+  struct eh_region *c, *l;
+  tree type_list, type_node;
+
+  /* Ensure to always end up with a type list to normalize further
+     processing, then register each type against the runtime types map.  */
+  type_list = type_or_list;
+  if (type_or_list)
     {
-      struct eh_region *r = eh->region_tree;
-      while (1)
-       {
-         mark_eh_region (r);
-         if (r->inner)
-           r = r->inner;
-         else if (r->next_peer)
-           r = r->next_peer;
-         else
-           {
-             do {
-               r = r->outer;
-               if (r == NULL)
-                 goto tree_done;
-             } while (r->next_peer == NULL);
-             r = r->next_peer;
-           }
-       }
-    tree_done:;
+      if (TREE_CODE (type_or_list) != TREE_LIST)
+       type_list = tree_cons (NULL_TREE, type_or_list, NULL_TREE);
+
+      type_node = type_list;
+      for (; type_node; type_node = TREE_CHAIN (type_node))
+       add_type_for_runtime (TREE_VALUE (type_node));
     }
 
-  ggc_mark_tree (eh->protect_list);
-  ggc_mark_rtx (eh->filter);
-  ggc_mark_rtx (eh->exc_ptr);
-  ggc_mark_tree_varray (eh->ttype_data);
+  c = gen_eh_region (ERT_CATCH, t->outer);
+  c->u.catch.type_list = type_list;
+  l = t->u.try.last_catch;
+  c->u.catch.prev_catch = l;
+  if (l)
+    l->u.catch.next_catch = c;
+  else
+    t->u.try.catch = c;
+  t->u.try.last_catch = c;
 
-  if (eh->call_site_data)
-    {
-      for (i = eh->call_site_data_used - 1; i >= 0; --i)
-       ggc_mark_rtx (eh->call_site_data[i].landing_pad);
-    }
+  return c;
+}
 
-  ggc_mark_rtx (eh->ehr_stackadj);
-  ggc_mark_rtx (eh->ehr_handler);
-  ggc_mark_rtx (eh->ehr_label);
+struct eh_region *
+gen_eh_region_allowed (struct eh_region *outer, tree allowed)
+{
+  struct eh_region *region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer);
+  region->u.allowed.type_list = allowed;
 
-  ggc_mark_rtx (eh->sjlj_fc);
-  ggc_mark_rtx (eh->sjlj_exit_after);
+  for (; allowed ; allowed = TREE_CHAIN (allowed))
+    add_type_for_runtime (TREE_VALUE (allowed));
+
+  return region;
 }
 
-void
-free_eh_status (f)
-     struct function *f;
+struct eh_region *
+gen_eh_region_must_not_throw (struct eh_region *outer)
 {
-  struct eh_status *eh = f->eh;
+  return gen_eh_region (ERT_MUST_NOT_THROW, outer);
+}
 
-  if (eh->region_array)
-    {
-      int i;
-      for (i = eh->last_region_number; i > 0; --i)
-       {
-         struct eh_region *r = eh->region_array[i];
-         /* Mind we don't free a region struct more than once.  */
-         if (r && r->region_number == i)
-           free (r);
-       }
-      free (eh->region_array);
-    }
-  else if (eh->region_tree)
-    {
-      struct eh_region *next, *r = eh->region_tree;
-      while (1)
-       {
-         if (r->inner)
-           r = r->inner;
-         else if (r->next_peer)
-           {
-             next = r->next_peer;
-             free (r);
-             r = next;
-           }
-         else
-           {
-             do {
-               next = r->outer;
-               free (r);
-               r = next;
-               if (r == NULL)
-                 goto tree_done;
-             } while (r->next_peer == NULL);
-             next = r->next_peer;
-             free (r);
-             r = next;
-           }
-       }
-    tree_done:;
-    }
+int
+get_eh_region_number (struct eh_region *region)
+{
+  return region->region_number;
+}
 
-  VARRAY_FREE (eh->ttype_data);
-  VARRAY_FREE (eh->ehspec_data);
-  VARRAY_FREE (eh->action_record_data);
-  if (eh->call_site_data)
-    free (eh->call_site_data);
+bool
+get_eh_region_may_contain_throw (struct eh_region *region)
+{
+  return region->may_contain_throw;
+}
 
-  free (eh);
-  f->eh = NULL;
+tree
+get_eh_region_tree_label (struct eh_region *region)
+{
+  return region->tree_label;
 }
 
+void
+set_eh_region_tree_label (struct eh_region *region, tree lab)
+{
+  region->tree_label = lab;
+}
 \f
 /* Start an exception handling region.  All instructions emitted
    after this point are considered to be part of the region until
    expand_eh_region_end is invoked.  */
 
 void
-expand_eh_region_start ()
+expand_eh_region_start (void)
 {
-  struct eh_region *new_region;
-  struct eh_region *cur_region;
+  struct eh_region *new;
   rtx note;
 
   if (! doing_eh (0))
     return;
 
-  /* We need a new block to record the start and end of the dynamic
-     handler chain.  We also want to prevent jumping into a try block.  */
-  expand_start_bindings (2);
-
-  /* But we don't need or want a new temporary level.  */
-  pop_temp_slots ();
-
-  /* Mark this block as created by expand_eh_region_start.  This is so
-     that we can pop the block with expand_end_bindings automatically.  */
-  mark_block_as_eh_region ();
-
-  /* Insert a new blank region as a leaf in the tree.  */
-  new_region = (struct eh_region *) xcalloc (1, sizeof (*new_region));
-  cur_region = cfun->eh->cur_region;
-  new_region->outer = cur_region;
-  if (cur_region)
-    {
-      new_region->next_peer = cur_region->inner;
-      cur_region->inner = new_region;
-    }
-  else
-    {
-      new_region->next_peer = cfun->eh->region_tree;
-      cfun->eh->region_tree = new_region;
-    }
-  cfun->eh->cur_region = new_region;
+  new = gen_eh_region (ERT_UNKNOWN, cfun->eh->cur_region);
+  cfun->eh->cur_region = new;
 
   /* Create a note marking the start of this region.  */
-  new_region->region_number = ++cfun->eh->last_region_number;
-  note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG);
-  NOTE_EH_HANDLER (note) = new_region->region_number;
+  note = emit_note (NOTE_INSN_EH_REGION_BEG);
+  NOTE_EH_HANDLER (note) = new->region_number;
 }
 
 /* Common code to end a region.  Returns the region just ended.  */
 
 static struct eh_region *
-expand_eh_region_end ()
+expand_eh_region_end (void)
 {
   struct eh_region *cur_region = cfun->eh->cur_region;
   rtx note;
 
-  /* Create a nute marking the end of this region.  */
-  note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END);
+  /* Create a note marking the end of this region.  */
+  note = emit_note (NOTE_INSN_EH_REGION_END);
   NOTE_EH_HANDLER (note) = cur_region->region_number;
 
   /* Pop.  */
   cfun->eh->cur_region = cur_region->outer;
 
-  /* If we have already started ending the bindings, don't recurse.  */
-  if (is_eh_region ())
+  return cur_region;
+}
+
+/* Expand HANDLER, which is the operand 1 of a TRY_CATCH_EXPR.  Catch
+   blocks and C++ exception-specifications are handled specially.  */
+
+void
+expand_eh_handler (tree handler)
+{
+  tree inner = expr_first (handler);
+
+  switch (TREE_CODE (inner))
     {
-      /* Because we don't need or want a new temporary level and
-        because we didn't create one in expand_eh_region_start,
-        create a fake one now to avoid removing one in
-        expand_end_bindings.  */
-      push_temp_slots ();
+    case CATCH_EXPR:
+      expand_start_all_catch ();
+      expand_expr (handler, const0_rtx, VOIDmode, 0);
+      expand_end_all_catch ();
+      break;
 
-      mark_block_as_not_eh_region ();
+    case EH_FILTER_EXPR:
+      if (EH_FILTER_MUST_NOT_THROW (handler))
+       expand_eh_region_end_must_not_throw (EH_FILTER_FAILURE (handler));
+      else
+       expand_eh_region_end_allowed (EH_FILTER_TYPES (handler),
+                                     EH_FILTER_FAILURE (handler));
+      break;
 
-      expand_end_bindings (NULL_TREE, 0, 0);
+    default:
+      expand_eh_region_end_cleanup (handler);
+      break;
     }
-
-  return cur_region;
 }
 
 /* End an exception handling region for a cleanup.  HANDLER is an
    expression to expand for the cleanup.  */
 
 void
-expand_eh_region_end_cleanup (handler)
-     tree handler;
+expand_eh_region_end_cleanup (tree handler)
 {
   struct eh_region *region;
+  tree protect_cleanup_actions;
   rtx around_label;
+  rtx data_save[2];
 
   if (! doing_eh (0))
     return;
@@ -719,36 +664,69 @@ expand_eh_region_end_cleanup (handler)
   region->type = ERT_CLEANUP;
   region->label = gen_label_rtx ();
   region->u.cleanup.exp = handler;
+  region->u.cleanup.prev_try = cfun->eh->try_region;
 
   around_label = gen_label_rtx ();
   emit_jump (around_label);
 
   emit_label (region->label);
 
-  if (protect_cleanup_actions)
-    expand_eh_region_start ();
+  if (flag_non_call_exceptions || region->may_contain_throw)
+    {
+      /* Give the language a chance to specify an action to be taken if an
+        exception is thrown that would propagate out of the HANDLER.  */
+      protect_cleanup_actions
+       = (lang_protect_cleanup_actions
+          ? (*lang_protect_cleanup_actions) ()
+          : NULL_TREE);
+
+      if (protect_cleanup_actions)
+       expand_eh_region_start ();
+
+      /* In case this cleanup involves an inline destructor with a try block in
+        it, we need to save the EH return data registers around it.  */
+      data_save[0] = gen_reg_rtx (ptr_mode);
+      emit_move_insn (data_save[0], get_exception_pointer (cfun));
+      data_save[1] = gen_reg_rtx (word_mode);
+      emit_move_insn (data_save[1], get_exception_filter (cfun));
 
-  expand_expr (handler, const0_rtx, VOIDmode, 0);
+      expand_expr (handler, const0_rtx, VOIDmode, 0);
 
-  if (protect_cleanup_actions)
-    expand_eh_region_end_must_not_throw (protect_cleanup_actions);
+      emit_move_insn (cfun->eh->exc_ptr, data_save[0]);
+      emit_move_insn (cfun->eh->filter, data_save[1]);
+
+      if (protect_cleanup_actions)
+       expand_eh_region_end_must_not_throw (protect_cleanup_actions);
+
+      /* We need any stack adjustment complete before the around_label.  */
+      do_pending_stack_adjust ();
+    }
 
   /* We delay the generation of the _Unwind_Resume until we generate
      landing pads.  We emit a marker here so as to get good control
      flow data in the meantime.  */
-  emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
+  region->resume
+    = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
   emit_barrier ();
 
-  region->last = get_last_insn ();
-
   emit_label (around_label);
 }
 
+void
+expand_resx_expr (tree exp)
+{
+  int region_nr = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0));
+  struct eh_region *reg = cfun->eh->region_array[region_nr];
+
+  reg->resume = emit_jump_insn (gen_rtx_RESX (VOIDmode, region_nr));
+  emit_barrier ();
+}
+
 /* End an exception handling region for a try block, and prepares
    for subsequent calls to expand_start_catch.  */
 
 void
-expand_start_all_catch ()
+expand_start_all_catch (void)
 {
   struct eh_region *region;
 
@@ -765,61 +743,48 @@ expand_start_all_catch ()
   emit_jump (region->u.try.continue_label);
 }
 
-/* Begin a catch clause.  TYPE is the type caught, or null if this is
-   a catch-all clause.  */
+/* Begin a catch clause.  TYPE is the type caught, a list of such
+   types, (in the case of Java) an ADDR_EXPR which points to the
+   runtime type to match, or null if this is a catch-all
+   clause. Providing a type list enables to associate the catch region
+   with potentially several exception types, which is useful e.g. for
+   Ada.  */
 
 void
-expand_start_catch (type)
-     tree type;
+expand_start_catch (tree type_or_list)
 {
-  struct eh_region *t, *c, *l;
+  struct eh_region *c;
+  rtx note;
 
   if (! doing_eh (0))
     return;
 
-  if (type)
-    add_type_for_runtime (type);
-  expand_eh_region_start ();
+  c = gen_eh_region_catch (cfun->eh->try_region, type_or_list);
+  cfun->eh->cur_region = c;
 
-  t = cfun->eh->try_region;
-  c = cfun->eh->cur_region;
-  c->type = ERT_CATCH;
-  c->u.catch.type = type;
   c->label = gen_label_rtx ();
-
-  l = t->u.try.last_catch;
-  c->u.catch.prev_catch = l;
-  if (l)
-    l->u.catch.next_catch = c;
-  else
-    t->u.try.catch = c;
-  t->u.try.last_catch = c;
-
   emit_label (c->label);
+
+  note = emit_note (NOTE_INSN_EH_REGION_BEG);
+  NOTE_EH_HANDLER (note) = c->region_number;
 }
 
 /* End a catch clause.  Control will resume after the try/catch block.  */
 
 void
-expand_end_catch ()
+expand_end_catch (void)
 {
-  struct eh_region *try_region, *catch_region;
-
   if (! doing_eh (0))
     return;
 
-  catch_region = expand_eh_region_end ();
-  try_region = cfun->eh->try_region;
-
-  emit_jump (try_region->u.try.continue_label);
-
-  catch_region->last = get_last_insn ();
+  expand_eh_region_end ();
+  emit_jump (cfun->eh->try_region->u.try.continue_label);
 }
 
 /* End a sequence of catch handlers for a try block.  */
 
 void
-expand_end_all_catch ()
+expand_end_all_catch (void)
 {
   struct eh_region *try_region;
 
@@ -834,11 +799,14 @@ expand_end_all_catch ()
 
 /* End an exception region for an exception type filter.  ALLOWED is a
    TREE_LIST of types to be matched by the runtime.  FAILURE is an
-   expression to invoke if a mismatch ocurrs.  */
+   expression to invoke if a mismatch occurs.
+
+   ??? We could use these semantics for calls to rethrow, too; if we can
+   see the surrounding catch clause, we know that the exception we're
+   rethrowing satisfies the "filter" of the catch type.  */
 
 void
-expand_eh_region_end_allowed (allowed, failure)
-     tree allowed, failure;
+expand_eh_region_end_allowed (tree allowed, tree failure)
 {
   struct eh_region *region;
   rtx around_label;
@@ -863,8 +831,10 @@ expand_eh_region_end_allowed (allowed, failure)
 
   emit_label (region->label);
   expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-  region->last = get_last_insn ();
+  /* We must adjust the stack before we reach the AROUND_LABEL because
+     the call to FAILURE does not occur on all paths to the
+     AROUND_LABEL.  */
+  do_pending_stack_adjust ();
 
   emit_label (around_label);
 }
@@ -878,8 +848,7 @@ expand_eh_region_end_allowed (allowed, failure)
    the C++ LSDA.  */
 
 void
-expand_eh_region_end_must_not_throw (failure)
-     tree failure;
+expand_eh_region_end_must_not_throw (tree failure)
 {
   struct eh_region *region;
   rtx around_label;
@@ -901,8 +870,6 @@ expand_eh_region_end_must_not_throw (failure)
   emit_label (region->label);
   expand_expr (failure, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  region->last = get_last_insn ();
-
   emit_label (around_label);
 }
 
@@ -911,8 +878,7 @@ expand_eh_region_end_must_not_throw (failure)
    is being thrown.  */
 
 void
-expand_eh_region_end_throw (type)
-     tree type;
+expand_eh_region_end_throw (tree type)
 {
   struct eh_region *region;
 
@@ -935,8 +901,7 @@ expand_eh_region_end_throw (type)
    the proper notion of "enclosing" in convert_from_eh_region_ranges.  */
 
 void
-expand_eh_region_end_fixup (handler)
-     tree handler;
+expand_eh_region_end_fixup (tree handler)
 {
   struct eh_region *fixup;
 
@@ -948,81 +913,55 @@ expand_eh_region_end_fixup (handler)
   fixup->u.fixup.cleanup_exp = handler;
 }
 
-/* Return a tree expression for a pointer to the exception object
-   within a handler.  */
+/* Note that the current EH region (if any) may contain a throw, or a
+   call to a function which itself may contain a throw.  */
 
-rtx
-get_exception_pointer ()
+void
+note_eh_region_may_contain_throw (struct eh_region *region)
 {
-  rtx exc_ptr = cfun->eh->exc_ptr;
-  if (! exc_ptr)
+  while (region && !region->may_contain_throw)
     {
-      exc_ptr = gen_reg_rtx (Pmode);
-      cfun->eh->exc_ptr = exc_ptr;
+      region->may_contain_throw = 1;
+      region = region->outer;
     }
-  return exc_ptr;
 }
 
-\f
-/* Begin a region that will contain entries created with
-   add_partial_entry.  */
-
 void
-begin_protect_partials ()
+note_current_region_may_contain_throw (void)
 {
-  /* Push room for a new list.  */
-  cfun->eh->protect_list
-    = tree_cons (NULL_TREE, NULL_TREE, cfun->eh->protect_list);
+  note_eh_region_may_contain_throw (cfun->eh->cur_region);
 }
 
-/* Start a new exception region for a region of code that has a
-   cleanup action and push the HANDLER for the region onto
-   protect_list. All of the regions created with add_partial_entry
-   will be ended when end_protect_partials is invoked.  */
 
-void
-add_partial_entry (handler)
-     tree handler;
-{
-  expand_eh_region_start ();
-
-  /* ??? This comment was old before the most recent rewrite.  We
-     really ought to fix the callers at some point.  */
-  /* For backwards compatibility, we allow callers to omit calls to
-     begin_protect_partials for the outermost region.  So, we must
-     explicitly do so here.  */
-  if (!cfun->eh->protect_list)
-    begin_protect_partials ();
+/* Return an rtl expression for a pointer to the exception object
+   within a handler.  */
 
-  /* Add this entry to the front of the list.  */
-  TREE_VALUE (cfun->eh->protect_list) 
-    = tree_cons (NULL_TREE, handler, TREE_VALUE (cfun->eh->protect_list));
+rtx
+get_exception_pointer (struct function *fun)
+{
+  rtx exc_ptr = fun->eh->exc_ptr;
+  if (fun == cfun && ! exc_ptr)
+    {
+      exc_ptr = gen_reg_rtx (ptr_mode);
+      fun->eh->exc_ptr = exc_ptr;
+    }
+  return exc_ptr;
 }
 
-/* End all the pending exception regions on protect_list.  */
+/* Return an rtl expression for the exception dispatch filter
+   within a handler.  */
 
-void
-end_protect_partials ()
+rtx
+get_exception_filter (struct function *fun)
 {
-  tree t;
-
-  /* ??? This comment was old before the most recent rewrite.  We
-     really ought to fix the callers at some point.  */
-  /* For backwards compatibility, we allow callers to omit the call to
-     begin_protect_partials for the outermost region.  So,
-     PROTECT_LIST may be NULL.  */
-  if (!cfun->eh->protect_list)
-    return;
-
-  /* Pop the topmost entry.  */
-  t = TREE_VALUE (cfun->eh->protect_list);
-  cfun->eh->protect_list = TREE_CHAIN (cfun->eh->protect_list);
-
-  /* End all the exception regions.  */
-  for (; t; t = TREE_CHAIN (t))
-    expand_eh_region_end_cleanup (TREE_VALUE (t));
+  rtx filter = fun->eh->filter;
+  if (fun == cfun && ! filter)
+    {
+      filter = gen_reg_rtx (word_mode);
+      fun->eh->filter = filter;
+    }
+  return filter;
 }
-
 \f
 /* This section is for the exception handling specific optimization pass.  */
 
@@ -1030,8 +969,8 @@ end_protect_partials ()
    collect the regions this way as in expand_eh_region_start, but
    without having to realloc memory.  */
 
-static void
-collect_eh_region_array ()
+void
+collect_eh_region_array (void)
 {
   struct eh_region **array, *i;
 
@@ -1039,7 +978,8 @@ collect_eh_region_array ()
   if (! i)
     return;
 
-  array = xcalloc (cfun->eh->last_region_number + 1, sizeof (*array));
+  array = ggc_alloc_cleared ((cfun->eh->last_region_number + 1)
+                            * sizeof (*array));
   cfun->eh->region_array = array;
 
   while (1)
@@ -1066,29 +1006,49 @@ collect_eh_region_array ()
 }
 
 static void
-resolve_fixup_regions ()
+resolve_one_fixup_region (struct eh_region *fixup)
 {
-  int i, j, n = cfun->eh->last_region_number;
+  struct eh_region *cleanup, *real;
+  int j, n;
+
+  n = cfun->eh->last_region_number;
+  cleanup = 0;
+
+  for (j = 1; j <= n; ++j)
+    {
+      cleanup = cfun->eh->region_array[j];
+      if (cleanup && cleanup->type == ERT_CLEANUP
+         && cleanup->u.cleanup.exp == fixup->u.fixup.cleanup_exp)
+       break;
+    }
+  if (j > n)
+    abort ();
+
+  real = cleanup->outer;
+  if (real && real->type == ERT_FIXUP)
+    {
+      if (!real->u.fixup.resolved)
+       resolve_one_fixup_region (real);
+      real = real->u.fixup.real_region;
+    }
+
+  fixup->u.fixup.real_region = real;
+  fixup->u.fixup.resolved = true;
+}
+
+static void
+resolve_fixup_regions (void)
+{
+  int i, n = cfun->eh->last_region_number;
 
   for (i = 1; i <= n; ++i)
     {
       struct eh_region *fixup = cfun->eh->region_array[i];
-      struct eh_region *cleanup;
 
-      if (! fixup || fixup->type != ERT_FIXUP)
+      if (!fixup || fixup->type != ERT_FIXUP || fixup->u.fixup.resolved)
        continue;
 
-      for (j = 1; j <= n; ++j)
-       {
-         cleanup = cfun->eh->region_array[j];
-         if (cleanup->type == ERT_CLEANUP
-             && cleanup->u.cleanup.exp == fixup->u.fixup.cleanup_exp)
-           break;
-       }
-      if (j > n)
-       abort ();
-
-      fixup->u.fixup.real_region = cleanup->outer;
+      resolve_one_fixup_region (fixup);
     }
 }
 
@@ -1096,20 +1056,39 @@ resolve_fixup_regions ()
    we can shuffle pointers and remove them from the tree.  */
 
 static void
-remove_fixup_regions ()
+remove_fixup_regions (void)
 {
   int i;
+  rtx insn, note;
+  struct eh_region *fixup;
 
+  /* Walk the insn chain and adjust the REG_EH_REGION numbers
+     for instructions referencing fixup regions.  This is only
+     strictly necessary for fixup regions with no parent, but
+     doesn't hurt to do it for all regions.  */
+  for (insn = get_insns(); insn ; insn = NEXT_INSN (insn))
+    if (INSN_P (insn)
+       && (note = find_reg_note (insn, REG_EH_REGION, NULL))
+       && INTVAL (XEXP (note, 0)) > 0
+       && (fixup = cfun->eh->region_array[INTVAL (XEXP (note, 0))])
+       && fixup->type == ERT_FIXUP)
+      {
+       if (fixup->u.fixup.real_region)
+         XEXP (note, 0) = GEN_INT (fixup->u.fixup.real_region->region_number);
+       else
+         remove_note (insn, note);
+      }
+
+  /* Remove the fixup regions from the tree.  */
   for (i = cfun->eh->last_region_number; i > 0; --i)
     {
-      struct eh_region *fixup = cfun->eh->region_array[i];
-
+      fixup = cfun->eh->region_array[i];
       if (! fixup)
        continue;
 
       /* Allow GC to maybe free some memory.  */
       if (fixup->type == ERT_CLEANUP)
-        fixup->u.cleanup.exp = NULL_TREE;
+       fixup->u.cleanup.exp = NULL_TREE;
 
       if (fixup->type != ERT_FIXUP)
        continue;
@@ -1144,14 +1123,107 @@ remove_fixup_regions ()
     }
 }
 
+/* Remove all regions whose labels are not reachable from insns.  */
+
+static void
+remove_unreachable_regions (rtx insns)
+{
+  int i, *uid_region_num;
+  bool *reachable;
+  struct eh_region *r;
+  rtx insn;
+
+  uid_region_num = xcalloc (get_max_uid (), sizeof(int));
+  reachable = xcalloc (cfun->eh->last_region_number + 1, sizeof(bool));
+
+  for (i = cfun->eh->last_region_number; i > 0; --i)
+    {
+      r = cfun->eh->region_array[i];
+      if (!r || r->region_number != i)
+       continue;
+
+      if (r->resume)
+       {
+         if (uid_region_num[INSN_UID (r->resume)])
+           abort ();
+         uid_region_num[INSN_UID (r->resume)] = i;
+       }
+      if (r->label)
+       {
+         if (uid_region_num[INSN_UID (r->label)])
+           abort ();
+         uid_region_num[INSN_UID (r->label)] = i;
+       }
+    }
+
+  for (insn = insns; insn; insn = NEXT_INSN (insn))
+    {
+      reachable[uid_region_num[INSN_UID (insn)]] = true;
+
+      if (GET_CODE (insn) == CALL_INSN
+         && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+       for (i = 0; i < 3; i++)
+         {
+           rtx sub = XEXP (PATTERN (insn), i);
+           for (; sub ; sub = NEXT_INSN (sub))
+             reachable[uid_region_num[INSN_UID (sub)]] = true;
+         }
+    }
+
+  for (i = cfun->eh->last_region_number; i > 0; --i)
+    {
+      r = cfun->eh->region_array[i];
+      if (r && r->region_number == i && !reachable[i])
+       {
+         bool kill_it = true;
+         switch (r->type)
+           {
+           case ERT_THROW:
+             /* Don't remove ERT_THROW regions if their outer region
+                is reachable.  */
+             if (r->outer && reachable[r->outer->region_number])
+               kill_it = false;
+             break;
+
+           case ERT_MUST_NOT_THROW:
+             /* MUST_NOT_THROW regions are implementable solely in the
+                runtime, but their existance continues to affect calls
+                within that region.  Never delete them here.  */
+             kill_it = false;
+             break;
+
+           case ERT_TRY:
+             {
+               /* TRY regions are reachable if any of its CATCH regions
+                  are reachable.  */
+               struct eh_region *c;
+               for (c = r->u.try.catch; c ; c = c->u.catch.next_catch)
+                 if (reachable[c->region_number])
+                   {
+                     kill_it = false;
+                     break;
+                   }
+               break;
+             }
+
+           default:
+             break;
+           }
+             
+         if (kill_it)
+           remove_eh_handler (r);
+       }
+    }
+
+  free (reachable);
+  free (uid_region_num);
+}
+
 /* Turn NOTE_INSN_EH_REGION notes into REG_EH_REGION notes for each
    can_throw instruction in the region.  */
 
 static void
-convert_from_eh_region_ranges_1 (pinsns, orig_sp, cur)
-     rtx *pinsns;
-     int *orig_sp;
-     int cur;
+convert_from_eh_region_ranges_1 (rtx *pinsns, int *orig_sp, int cur)
 {
   int *sp = orig_sp;
   rtx insn, next;
@@ -1206,6 +1278,8 @@ convert_from_eh_region_ranges_1 (pinsns, orig_sp, cur)
                  /* If we wanted exceptions for non-call insns, then
                     any may_trap_p instruction could throw.  */
                  || (flag_non_call_exceptions
+                     && GET_CODE (PATTERN (insn)) != CLOBBER
+                     && GET_CODE (PATTERN (insn)) != USE
                      && may_trap_p (PATTERN (insn)))))
            {
              REG_NOTES (insn) = alloc_EXPR_LIST (REG_EH_REGION, GEN_INT (cur),
@@ -1229,30 +1303,87 @@ convert_from_eh_region_ranges_1 (pinsns, orig_sp, cur)
     abort ();
 }
 
+static void
+collect_rtl_labels_from_trees (void)
+{
+  int i, n = cfun->eh->last_region_number;
+  for (i = 1; i <= n; ++i)
+    {
+      struct eh_region *reg = cfun->eh->region_array[i];
+      if (reg && reg->tree_label)
+       reg->label = DECL_RTL_IF_SET (reg->tree_label);
+    }
+}
+
 void
-convert_from_eh_region_ranges ()
+convert_from_eh_region_ranges (void)
+{
+  rtx insns = get_insns ();
+
+  if (cfun->eh->region_array)
+    {
+      /* If the region array already exists, assume we're coming from
+        optimize_function_tree.  In this case all we need to do is
+        collect the rtl labels that correspond to the tree labels
+        that we allocated earlier.  */
+      collect_rtl_labels_from_trees ();
+    }
+  else
+    {
+      int *stack;
+
+      collect_eh_region_array ();
+      resolve_fixup_regions ();
+
+      stack = xmalloc (sizeof (int) * (cfun->eh->last_region_number + 1));
+      convert_from_eh_region_ranges_1 (&insns, stack, 0);
+      free (stack);
+
+      remove_fixup_regions ();
+    }
+
+  remove_unreachable_regions (insns);
+}
+
+static void
+add_ehl_entry (rtx label, struct eh_region *region)
 {
-  int *stack;
-  rtx insns;
+  struct ehl_map_entry **slot, *entry;
+
+  LABEL_PRESERVE_P (label) = 1;
 
-  collect_eh_region_array ();
-  resolve_fixup_regions ();
+  entry = ggc_alloc (sizeof (*entry));
+  entry->label = label;
+  entry->region = region;
 
-  stack = xmalloc (sizeof (int) * (cfun->eh->last_region_number + 1));
-  insns = get_insns ();
-  convert_from_eh_region_ranges_1 (&insns, stack, 0);
-  free (stack);
+  slot = (struct ehl_map_entry **)
+    htab_find_slot (cfun->eh->exception_handler_label_map, entry, INSERT);
 
-  remove_fixup_regions ();
+  /* 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.  */
+  if (*slot && !cfun->eh->built_landing_pads)
+    abort ();
+
+  *slot = entry;
 }
 
 void
-find_exception_handler_labels ()
+find_exception_handler_labels (void)
 {
-  rtx list = NULL_RTX;
   int i;
 
-  free_EXPR_LIST_list (&exception_handler_labels);
+  if (cfun->eh->exception_handler_label_map)
+    htab_empty (cfun->eh->exception_handler_label_map);
+  else
+    {
+      /* ??? The expansion factor here (3/2) must be greater than the htab
+        occupancy factor (4/3) to avoid unnecessary resizing.  */
+      cfun->eh->exception_handler_label_map
+        = htab_create_ggc (cfun->eh->last_region_number * 3 / 2,
+                          ehl_hash, ehl_eq, NULL);
+    }
 
   if (cfun->eh->region_tree == NULL)
     return;
@@ -1262,7 +1393,7 @@ find_exception_handler_labels ()
       struct eh_region *region = cfun->eh->region_array[i];
       rtx lab;
 
-      if (! region)
+      if (! region || region->region_number != i)
        continue;
       if (cfun->eh->built_landing_pads)
        lab = region->landing_pad;
@@ -1270,25 +1401,37 @@ find_exception_handler_labels ()
        lab = region->label;
 
       if (lab)
-       list = alloc_EXPR_LIST (0, lab, list);
+       add_ehl_entry (lab, region);
     }
 
   /* For sjlj exceptions, need the return label to remain live until
      after landing pad generation.  */
   if (USING_SJLJ_EXCEPTIONS && ! cfun->eh->built_landing_pads)
-    list = alloc_EXPR_LIST (0, return_label, list);
-
-  exception_handler_labels = list;
+    add_ehl_entry (return_label, NULL);
 }
 
+bool
+current_function_has_exception_handlers (void)
+{
+  int i;
+
+  for (i = cfun->eh->last_region_number; i > 0; --i)
+    {
+      struct eh_region *region = cfun->eh->region_array[i];
+
+      if (! region || region->region_number != i)
+       continue;
+      if (region->type != ERT_THROW)
+       return true;
+    }
+
+  return false;
+}
 \f
 static struct eh_region *
-duplicate_eh_region_1 (o, map)
-     struct eh_region *o;
-     struct inline_remap *map;
+duplicate_eh_region_1 (struct eh_region *o, struct inline_remap *map)
 {
-  struct eh_region *n
-    = (struct eh_region *) xcalloc (1, sizeof (struct eh_region));
+  struct eh_region *n = ggc_alloc_cleared (sizeof (struct eh_region));
 
   n->region_number = o->region_number + cfun->eh->last_region_number;
   n->type = o->type;
@@ -1307,7 +1450,7 @@ duplicate_eh_region_1 (o, map)
       break;
 
     case ERT_CATCH:
-      n->u.catch.type = o->u.catch.type;
+      n->u.catch.type_list = o->u.catch.type_list;
       break;
 
     case ERT_ALLOWED_EXCEPTIONS:
@@ -1316,17 +1459,17 @@ duplicate_eh_region_1 (o, map)
 
     case ERT_THROW:
       n->u.throw.type = o->u.throw.type;
-      
+
     default:
       abort ();
     }
 
   if (o->label)
     n->label = get_label_from_map (map, CODE_LABEL_NUMBER (o->label));
-  if (o->last)
+  if (o->resume)
     {
-      n->last = map->insn_map[INSN_UID (o->last)];
-      if (n->last == NULL)
+      n->resume = map->insn_map[INSN_UID (o->resume)];
+      if (n->resume == NULL)
        abort ();
     }
 
@@ -1334,9 +1477,7 @@ duplicate_eh_region_1 (o, map)
 }
 
 static void
-duplicate_eh_region_2 (o, n_array)
-     struct eh_region *o;
-     struct eh_region **n_array;
+duplicate_eh_region_2 (struct eh_region *o, struct eh_region **n_array)
 {
   struct eh_region *n = n_array[o->region_number];
 
@@ -1349,9 +1490,9 @@ duplicate_eh_region_2 (o, n_array)
 
     case ERT_CATCH:
       if (o->u.catch.next_catch)
-        n->u.catch.next_catch = n_array[o->u.catch.next_catch->region_number];
+       n->u.catch.next_catch = n_array[o->u.catch.next_catch->region_number];
       if (o->u.catch.prev_catch)
-        n->u.catch.prev_catch = n_array[o->u.catch.prev_catch->region_number];
+       n->u.catch.prev_catch = n_array[o->u.catch.prev_catch->region_number];
       break;
 
     default:
@@ -1364,12 +1505,10 @@ duplicate_eh_region_2 (o, n_array)
     n->inner = n_array[o->inner->region_number];
   if (o->next_peer)
     n->next_peer = n_array[o->next_peer->region_number];
-}    
+}
 
 int
-duplicate_eh_regions (ifun, map)
-     struct function *ifun;
-     struct inline_remap *map;
+duplicate_eh_regions (struct function *ifun, struct inline_remap *map)
 {
   int ifun_last_region_number = ifun->eh->last_region_number;
   struct eh_region **n_array, *root, *cur;
@@ -1410,7 +1549,7 @@ duplicate_eh_regions (ifun, map)
        cur->inner = root;
 
       for (i = 1; i <= ifun_last_region_number; ++i)
-       if (n_array[i]->outer == NULL)
+       if (n_array[i] && n_array[i]->outer == NULL)
          n_array[i]->outer = cur;
     }
   else
@@ -1434,13 +1573,8 @@ duplicate_eh_regions (ifun, map)
 }
 
 \f
-/* ??? Move from tree.c to tree.h.  */
-#define TYPE_HASH(TYPE) ((HOST_WIDE_INT) (TYPE) & 0777777)
-
 static int
-t2r_eq (pentry, pdata)
-     const PTR pentry;
-     const PTR pdata;
+t2r_eq (const void *pentry, const void *pdata)
 {
   tree entry = (tree) pentry;
   tree data = (tree) pdata;
@@ -1449,62 +1583,42 @@ t2r_eq (pentry, pdata)
 }
 
 static hashval_t
-t2r_hash (pentry)
-     const PTR pentry;
+t2r_hash (const void *pentry)
 {
   tree entry = (tree) pentry;
-  return TYPE_HASH (TREE_PURPOSE (entry));
-}
-
-static int
-t2r_mark_1 (slot, data)
-     PTR *slot;
-     PTR data ATTRIBUTE_UNUSED;
-{
-  tree contents = (tree) *slot;
-  ggc_mark_tree (contents);
-  return 1;
+  return TREE_HASH (TREE_PURPOSE (entry));
 }
 
 static void
-t2r_mark (addr)
-     PTR addr;
-{
-  htab_traverse (*(htab_t *)addr, t2r_mark_1, NULL);
-}
-
-static void
-add_type_for_runtime (type)
-     tree type;
+add_type_for_runtime (tree type)
 {
   tree *slot;
 
   slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
-                                           TYPE_HASH (type), INSERT);
+                                           TREE_HASH (type), INSERT);
   if (*slot == NULL)
     {
       tree runtime = (*lang_eh_runtime_type) (type);
       *slot = tree_cons (type, runtime, NULL_TREE);
     }
 }
-  
+
 static tree
-lookup_type_for_runtime (type)
-     tree type;
+lookup_type_for_runtime (tree type)
 {
   tree *slot;
 
   slot = (tree *) htab_find_slot_with_hash (type_to_runtime_map, type,
-                                           TYPE_HASH (type), NO_INSERT);
+                                           TREE_HASH (type), NO_INSERT);
 
-  /* We should have always inserrted the data earlier.  */
+  /* We should have always inserted the data earlier.  */
   return TREE_VALUE (*slot);
 }
 
 \f
 /* Represent an entry in @TTypes for either catch actions
    or exception filter actions.  */
-struct ttypes_filter
+struct ttypes_filter GTY(())
 {
   tree t;
   int filter;
@@ -1514,9 +1628,7 @@ struct ttypes_filter
    (a tree) for a @TTypes type node we are thinking about adding.  */
 
 static int
-ttypes_filter_eq (pentry, pdata)
-     const PTR pentry;
-     const PTR pdata;
+ttypes_filter_eq (const void *pentry, const void *pdata)
 {
   const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
   tree data = (tree) pdata;
@@ -1525,11 +1637,10 @@ ttypes_filter_eq (pentry, pdata)
 }
 
 static hashval_t
-ttypes_filter_hash (pentry)
-     const PTR pentry;
+ttypes_filter_hash (const void *pentry)
 {
   const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
-  return TYPE_HASH (entry->t);
+  return TREE_HASH (entry->t);
 }
 
 /* Compare ENTRY with DATA (both struct ttypes_filter) for a @TTypes
@@ -1538,9 +1649,7 @@ ttypes_filter_hash (pentry)
    should put these in some canonical order.  */
 
 static int
-ehspec_filter_eq (pentry, pdata)
-     const PTR pentry;
-     const PTR pdata;
+ehspec_filter_eq (const void *pentry, const void *pdata)
 {
   const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
   const struct ttypes_filter *data = (const struct ttypes_filter *) pdata;
@@ -1551,36 +1660,33 @@ ehspec_filter_eq (pentry, pdata)
 /* Hash function for exception specification lists.  */
 
 static hashval_t
-ehspec_filter_hash (pentry)
-     const PTR pentry;
+ehspec_filter_hash (const void *pentry)
 {
   const struct ttypes_filter *entry = (const struct ttypes_filter *) pentry;
   hashval_t h = 0;
   tree list;
 
   for (list = entry->t; list ; list = TREE_CHAIN (list))
-    h = (h << 5) + (h >> 27) + TYPE_HASH (TREE_VALUE (list));
+    h = (h << 5) + (h >> 27) + TREE_HASH (TREE_VALUE (list));
   return h;
 }
 
-/* Add TYPE to cfun->eh->ttype_data, using TYPES_HASH to speed
-   up the search.  Return the filter value to be used.  */
+/* Add TYPE (which may be NULL) to cfun->eh->ttype_data, using TYPES_HASH
+   to speed up the search.  Return the filter value to be used.  */
 
 static int
-add_ttypes_entry (ttypes_hash, type)
-     htab_t ttypes_hash;
-     tree type;
+add_ttypes_entry (htab_t ttypes_hash, tree type)
 {
   struct ttypes_filter **slot, *n;
 
   slot = (struct ttypes_filter **)
-    htab_find_slot_with_hash (ttypes_hash, type, TYPE_HASH (type), INSERT);
+    htab_find_slot_with_hash (ttypes_hash, type, TREE_HASH (type), INSERT);
 
   if ((n = *slot) == NULL)
     {
       /* Filter value is a 1 based table index.  */
 
-      n = (struct ttypes_filter *) xmalloc (sizeof (*n));
+      n = xmalloc (sizeof (*n));
       n->t = type;
       n->filter = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) + 1;
       *slot = n;
@@ -1595,10 +1701,7 @@ add_ttypes_entry (ttypes_hash, type)
    to speed up the search.  Return the filter value to be used.  */
 
 static int
-add_ehspec_entry (ehspec_hash, ttypes_hash, list)
-     htab_t ehspec_hash;
-     htab_t ttypes_hash;
-     tree list;
+add_ehspec_entry (htab_t ehspec_hash, htab_t ttypes_hash, tree list)
 {
   struct ttypes_filter **slot, *n;
   struct ttypes_filter dummy;
@@ -1611,7 +1714,7 @@ add_ehspec_entry (ehspec_hash, ttypes_hash, list)
     {
       /* Filter value is a -1 based byte index into a uleb128 buffer.  */
 
-      n = (struct ttypes_filter *) xmalloc (sizeof (*n));
+      n = xmalloc (sizeof (*n));
       n->t = list;
       n->filter = -(VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) + 1);
       *slot = n;
@@ -1619,7 +1722,7 @@ add_ehspec_entry (ehspec_hash, ttypes_hash, list)
       /* Look up each type in the list and encode its filter
         value as a uleb128.  Terminate the list with 0.  */
       for (; list ; list = TREE_CHAIN (list))
-       push_uleb128 (&cfun->eh->ehspec_data, 
+       push_uleb128 (&cfun->eh->ehspec_data,
                      add_ttypes_entry (ttypes_hash, TREE_VALUE (list)));
       VARRAY_PUSH_UCHAR (cfun->eh->ehspec_data, 0);
     }
@@ -1633,7 +1736,7 @@ add_ehspec_entry (ehspec_hash, ttypes_hash, list)
    the same filter value, which saves table space.  */
 
 static void
-assign_filter_values ()
+assign_filter_values (void)
 {
   int i;
   htab_t ttypes, ehspec;
@@ -1655,7 +1758,36 @@ assign_filter_values ()
       switch (r->type)
        {
        case ERT_CATCH:
-         r->u.catch.filter = add_ttypes_entry (ttypes, r->u.catch.type);
+         /* Whatever type_list is (NULL or true list), we build a list
+            of filters for the region.  */
+         r->u.catch.filter_list = NULL_TREE;
+
+         if (r->u.catch.type_list != NULL)
+           {
+             /* Get a filter value for each of the types caught and store
+                them in the region's dedicated list.  */
+             tree tp_node = r->u.catch.type_list;
+
+             for (;tp_node; tp_node = TREE_CHAIN (tp_node))
+               {
+                 int flt = add_ttypes_entry (ttypes, TREE_VALUE (tp_node));
+                 tree flt_node = build_int_2 (flt, 0);
+
+                 r->u.catch.filter_list
+                   = tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list);
+               }
+           }
+         else
+           {
+             /* Get a filter value for the NULL list also since it will need
+                an action record anyway.  */
+             int flt = add_ttypes_entry (ttypes, NULL);
+             tree flt_node = build_int_2 (flt, 0);
+
+             r->u.catch.filter_list
+               = tree_cons (NULL_TREE, flt_node, r->u.catch.filter_list);
+           }
+
          break;
 
        case ERT_ALLOWED_EXCEPTIONS:
@@ -1672,8 +1804,36 @@ assign_filter_values ()
   htab_delete (ehspec);
 }
 
+/* Emit SEQ into basic block just before INSN (that is assumed to be
+   first instruction of some existing BB and return the newly
+   produced block.  */
+static basic_block
+emit_to_new_bb_before (rtx seq, rtx insn)
+{
+  rtx last;
+  basic_block bb;
+  edge e;
+
+  /* If there happens to be an fallthru edge (possibly created by cleanup_cfg
+     call), we don't want it to go into newly created landing pad or other EH 
+     construct.  */
+  for (e = BLOCK_FOR_INSN (insn)->pred; e; e = e->pred_next)
+    if (e->flags & EDGE_FALLTHRU)
+      force_nonfallthru (e);
+  last = emit_insn_before (seq, insn);
+  if (GET_CODE (last) == BARRIER)
+    last = PREV_INSN (last);
+  bb = create_basic_block (seq, last, BLOCK_FOR_INSN (insn)->prev_bb);
+  update_bb_for_insn (bb);
+  bb->flags |= BB_SUPERBLOCK;
+  return bb;
+}
+
+/* Generate the code to actually handle exceptions, which will follow the
+   landing pads.  */
+
 static void
-build_post_landing_pads ()
+build_post_landing_pads (void)
 {
   int i;
 
@@ -1693,7 +1853,7 @@ build_post_landing_pads ()
               all the way up the chain until blocked by a cleanup.  */
          /* ??? Outer try regions can share landing pads with inner
             try regions if the types are completely non-overlapping,
-            and there are no interveaning cleanups.  */
+            and there are no intervening cleanups.  */
 
          region->post_landing_pad = gen_label_rtx ();
 
@@ -1708,21 +1868,42 @@ build_post_landing_pads ()
            struct eh_region *c;
            for (c = region->u.try.catch; c ; c = c->u.catch.next_catch)
              {
-               /* ??? _Unwind_ForcedUnwind wants no match here.  */
-               if (c->u.catch.type == NULL)
+               if (c->u.catch.type_list == NULL)
                  emit_jump (c->label);
                else
-                 emit_cmp_and_jump_insns (cfun->eh->filter,
-                                          GEN_INT (c->u.catch.filter),
-                                          EQ, NULL_RTX, word_mode,
-                                          0, 0, c->label);
+                 {
+                   /* Need for one cmp/jump per type caught. Each type
+                      list entry has a matching entry in the filter list
+                      (see assign_filter_values).  */
+                   tree tp_node = c->u.catch.type_list;
+                   tree flt_node = c->u.catch.filter_list;
+
+                   for (; tp_node; )
+                     {
+                       emit_cmp_and_jump_insns
+                         (cfun->eh->filter,
+                          GEN_INT (tree_low_cst (TREE_VALUE (flt_node), 0)),
+                          EQ, NULL_RTX, word_mode, 0, c->label);
+
+                       tp_node = TREE_CHAIN (tp_node);
+                       flt_node = TREE_CHAIN (flt_node);
+                     }
+                 }
              }
          }
 
+         /* We delay the generation of the _Unwind_Resume until we generate
+            landing pads.  We emit a marker here so as to get good control
+            flow data in the meantime.  */
+         region->resume
+           = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
+         emit_barrier ();
+
          seq = get_insns ();
          end_sequence ();
 
-         region->last = emit_insns_before (seq, region->u.try.catch->label);
+         emit_to_new_bb_before (seq, region->u.try.catch->label);
+
          break;
 
        case ERT_ALLOWED_EXCEPTIONS:
@@ -1734,13 +1915,19 @@ build_post_landing_pads ()
 
          emit_cmp_and_jump_insns (cfun->eh->filter,
                                   GEN_INT (region->u.allowed.filter),
-                                  EQ, NULL_RTX, word_mode, 0, 0,
-                                  region->label);
+                                  EQ, NULL_RTX, word_mode, 0, region->label);
+
+         /* We delay the generation of the _Unwind_Resume until we generate
+            landing pads.  We emit a marker here so as to get good control
+            flow data in the meantime.  */
+         region->resume
+           = emit_jump_insn (gen_rtx_RESX (VOIDmode, region->region_number));
+         emit_barrier ();
 
          seq = get_insns ();
          end_sequence ();
 
-         region->last = emit_insns_before (seq, region->label);
+         emit_to_new_bb_before (seq, region->label);
          break;
 
        case ERT_CLEANUP:
@@ -1759,8 +1946,11 @@ build_post_landing_pads ()
     }
 }
 
+/* Replace RESX patterns with jumps to the next handler if any, or calls to
+   _Unwind_Resume otherwise.  */
+
 static void
-connect_post_landing_pads ()
+connect_post_landing_pads (void)
 {
   int i;
 
@@ -1768,44 +1958,16 @@ connect_post_landing_pads ()
     {
       struct eh_region *region = cfun->eh->region_array[i];
       struct eh_region *outer;
-      rtx before = NULL_RTX, after = NULL_RTX, seq;
+      rtx seq;
+      rtx barrier;
 
       /* Mind we don't process a region more than once.  */
       if (!region || region->region_number != i)
        continue;
 
-      switch (region->type)
-       {
-       case ERT_CLEANUP:
-         after = region->last;
-         if (GET_CODE (after) == BARRIER
-             && GET_CODE (PREV_INSN (after)) == JUMP_INSN
-             && GET_CODE (PATTERN (PREV_INSN (after))) == RESX)
-           {
-             before = PREV_INSN (after);
-             after = NULL_RTX;
-           }
-         break;
-
-       case ERT_TRY:
-         after = region->last;
-         break;
-
-       case ERT_ALLOWED_EXCEPTIONS:
-         before = region->label;
-         break;
-
-       case ERT_MUST_NOT_THROW:
-       case ERT_CATCH:
-       case ERT_THROW:
-         continue;
-
-       default:
-         abort ();
-       }
-
-      /* If there's no fallthru, no need to add branches.  */
-      if (after && GET_CODE (after) == BARRIER)
+      /* 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.  */
@@ -1816,30 +1978,67 @@ connect_post_landing_pads ()
       start_sequence ();
 
       if (outer)
-       emit_jump (outer->post_landing_pad);
+       {
+         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 (src->succ)
+           remove_edge (src->succ);
+         e = make_edge (src, dest, 0);
+         e->probability = REG_BR_PROB_BASE;
+         e->count = src->count;
+       }
       else
-       emit_library_call (unwind_resume_libfunc, LCT_THROW,
-                          VOIDmode, 1, cfun->eh->exc_ptr, Pmode);
+       {
+         emit_library_call (unwind_resume_libfunc, LCT_THROW,
+                            VOIDmode, 1, cfun->eh->exc_ptr, ptr_mode);
+
+         /* What we just emitted was a throwing libcall, so it got a
+            barrier automatically added after it.  If the last insn in
+            the libcall sequence isn't the barrier, it's because the
+            target emits multiple insns for a call, and there are insns
+            after the actual call insn (which are redundant and would be
+            optimized away).  The barrier is inserted exactly after the
+            call insn, so let's go get that and delete the insns after
+            it, because below we need the barrier to be the last insn in
+            the sequence.  */
+         delete_insns_since (NEXT_INSN (last_call_insn ()));
+       }
 
       seq = get_insns ();
       end_sequence ();
-      if (before)
-       emit_insns_before (seq, before);
-      else
-        emit_insns_after (seq, after);
+      barrier = emit_insn_before (seq, region->resume);
+      /* Avoid duplicate barrier.  */
+      if (GET_CODE (barrier) != BARRIER)
+       abort ();
+      delete_insn (barrier);
+      delete_insn (region->resume);
+
+      /* ??? From tree-ssa we can wind up with catch regions whose
+        label is not instantiated, but whose resx is present.  Now
+        that we've dealt with the resx, kill the region.  */
+      if (region->label == NULL && region->type == ERT_CLEANUP)
+       remove_eh_handler (region);
     }
 }
 
 \f
 static void
-dw2_build_landing_pads ()
+dw2_build_landing_pads (void)
 {
-  int i, j;
+  int i;
+  unsigned int j;
 
   for (i = cfun->eh->last_region_number; i > 0; --i)
     {
       struct eh_region *region = cfun->eh->region_array[i];
       rtx seq;
+      basic_block bb;
+      bool clobbers_hard_regs = false;
+      edge e;
 
       /* Mind we don't process a region more than once.  */
       if (!region || region->region_number != i)
@@ -1876,18 +2075,33 @@ dw2_build_landing_pads ()
          if (r == INVALID_REGNUM)
            break;
          if (! call_used_regs[r])
-           emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, r)));
+           {
+             emit_insn (gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, r)));
+             clobbers_hard_regs = true;
+           }
+       }
+
+      if (clobbers_hard_regs)
+       {
+         /* @@@ This is a kludge.  Not all machine descriptions define a
+            blockage insn, but we must not allow the code we just generated
+            to be reordered by scheduling.  So emit an ASM_INPUT to act as
+            blockage insn.  */
+         emit_insn (gen_rtx_ASM_INPUT (VOIDmode, ""));
        }
 
       emit_move_insn (cfun->eh->exc_ptr,
-                     gen_rtx_REG (Pmode, EH_RETURN_DATA_REGNO (0)));
+                     gen_rtx_REG (ptr_mode, EH_RETURN_DATA_REGNO (0)));
       emit_move_insn (cfun->eh->filter,
-                     gen_rtx_REG (Pmode, EH_RETURN_DATA_REGNO (1)));
+                     gen_rtx_REG (word_mode, EH_RETURN_DATA_REGNO (1)));
 
       seq = get_insns ();
       end_sequence ();
 
-      emit_insns_before (seq, region->post_landing_pad);
+      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;
     }
 }
 
@@ -1901,8 +2115,7 @@ struct sjlj_lp_info
 };
 
 static bool
-sjlj_find_directly_reachable_regions (lp_info)
-     struct sjlj_lp_info *lp_info;
+sjlj_find_directly_reachable_regions (struct sjlj_lp_info *lp_info)
 {
   rtx insn;
   bool found_one = false;
@@ -1910,6 +2123,7 @@ sjlj_find_directly_reachable_regions (lp_info)
   for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
     {
       struct eh_region *region;
+      enum reachable_code rc;
       tree type_thrown;
       rtx note;
 
@@ -1931,11 +2145,14 @@ sjlj_find_directly_reachable_regions (lp_info)
 
       /* 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)
-       if (reachable_next_level (region, type_thrown, 0) != RNL_NOT_CAUGHT)
-         break;
-
-      if (region)
+       {
+         rc = reachable_next_level (region, type_thrown, NULL);
+         if (rc != RNL_NOT_CAUGHT)
+           break;
+       }
+      if (rc == RNL_MAYBE_CAUGHT || rc == RNL_CAUGHT)
        {
          lp_info[region->region_number].directly_reachable = 1;
          found_one = true;
@@ -1946,9 +2163,7 @@ sjlj_find_directly_reachable_regions (lp_info)
 }
 
 static void
-sjlj_assign_call_site_values (dispatch_label, lp_info)
-     rtx dispatch_label;
-     struct sjlj_lp_info *lp_info;
+sjlj_assign_call_site_values (rtx dispatch_label, struct sjlj_lp_info *lp_info)
 {
   htab_t ar_hash;
   int i, index;
@@ -1976,14 +2191,13 @@ sjlj_assign_call_site_values (dispatch_label, lp_info)
 
      A region receives a dispatch index if it is directly reachable
      and requires in-function processing.  Regions that share post-landing
-     pads may share dispatch indicies.  */
+     pads may share dispatch indices.  */
   /* ??? Post-landing pad sharing doesn't actually happen at the moment
      (see build_post_landing_pads) so we don't bother checking for it.  */
 
   index = 0;
   for (i = cfun->eh->last_region_number; i > 0; --i)
-    if (lp_info[i].directly_reachable
-       && lp_info[i].action_index >= 0)
+    if (lp_info[i].directly_reachable)
       lp_info[i].dispatch_index = index++;
 
   /* Finally: assign call-site values.  If dwarf2 terms, this would be
@@ -2011,16 +2225,11 @@ sjlj_assign_call_site_values (dispatch_label, lp_info)
 }
 
 static void
-sjlj_mark_call_sites (lp_info)
-     struct sjlj_lp_info *lp_info;
+sjlj_mark_call_sites (struct sjlj_lp_info *lp_info)
 {
   int last_call_site = -2;
   rtx insn, mem;
 
-  mem = change_address (cfun->eh->sjlj_fc, TYPE_MODE (integer_type_node),
-                       plus_constant (XEXP (cfun->eh->sjlj_fc, 0),
-                                      sjlj_fc_call_site_ofs));
-
   for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
     {
       struct eh_region *region;
@@ -2063,53 +2272,16 @@ sjlj_mark_call_sites (lp_info)
       /* Don't separate a call from it's argument loads.  */
       before = insn;
       if (GET_CODE (insn) == CALL_INSN)
-       {
-         HARD_REG_SET parm_regs;
-         int nparm_regs;
-         
-         /* Since different machines initialize their parameter registers
-            in different orders, assume nothing.  Collect the set of all
-            parameter registers.  */
-         CLEAR_HARD_REG_SET (parm_regs);
-         nparm_regs = 0;
-         for (p = CALL_INSN_FUNCTION_USAGE (insn); p ; p = XEXP (p, 1))
-           if (GET_CODE (XEXP (p, 0)) == USE
-               && GET_CODE (XEXP (XEXP (p, 0), 0)) == REG)
-             {
-               if (REGNO (XEXP (XEXP (p, 0), 0)) >= FIRST_PSEUDO_REGISTER)
-                 abort ();
-
-               SET_HARD_REG_BIT (parm_regs, REGNO (XEXP (XEXP (p, 0), 0)));
-               nparm_regs++;
-             }
-
-         /* Search backward for the first set of a register in this set.  */
-         while (nparm_regs)
-           {
-             before = PREV_INSN (before);
-
-             /* Given that we've done no other optimizations yet,
-                the arguments should be immediately available.  */
-             if (GET_CODE (before) == CODE_LABEL)
-               abort ();
-
-             p = single_set (before);
-             if (p && GET_CODE (SET_DEST (p)) == REG
-                 && REGNO (SET_DEST (p)) < FIRST_PSEUDO_REGISTER
-                 && TEST_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p))))
-               {
-                 CLEAR_HARD_REG_BIT (parm_regs, REGNO (SET_DEST (p)));
-                 nparm_regs--;
-               }
-           }
-       }
+       before = find_first_parameter_load (insn, NULL_RTX);
 
       start_sequence ();
+      mem = adjust_address (cfun->eh->sjlj_fc, TYPE_MODE (integer_type_node),
+                           sjlj_fc_call_site_ofs);
       emit_move_insn (mem, GEN_INT (this_call_site));
       p = get_insns ();
       end_sequence ();
 
-      emit_insns_before (p, before);
+      emit_insn_before (p, before);
       last_call_site = this_call_site;
     }
 }
@@ -2117,8 +2289,7 @@ sjlj_mark_call_sites (lp_info)
 /* Construct the SjLj_Function_Context.  */
 
 static void
-sjlj_emit_function_enter (dispatch_label)
-     rtx dispatch_label;
+sjlj_emit_function_enter (rtx dispatch_label)
 {
   rtx fn_begin, fc, mem, seq;
 
@@ -2126,35 +2297,40 @@ sjlj_emit_function_enter (dispatch_label)
 
   start_sequence ();
 
-  mem = change_address (fc, Pmode,
-                       plus_constant (XEXP (fc, 0), sjlj_fc_personality_ofs));
+  /* We're storing this libcall's address into memory instead of
+     calling it directly.  Thus, we must call assemble_external_libcall
+     here, as we can not depend on emit_library_call to do it for us.  */
+  assemble_external_libcall (eh_personality_libfunc);
+  mem = adjust_address (fc, Pmode, sjlj_fc_personality_ofs);
   emit_move_insn (mem, eh_personality_libfunc);
 
-  mem = change_address (fc, Pmode,
-                       plus_constant (XEXP (fc, 0), sjlj_fc_lsda_ofs));
+  mem = adjust_address (fc, Pmode, sjlj_fc_lsda_ofs);
   if (cfun->uses_eh_lsda)
     {
       char buf[20];
-      ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", sjlj_funcdef_number);
-      emit_move_insn (mem, gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)));
+      rtx sym;
+
+      ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", current_function_funcdef_no);
+      sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
+      SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_LOCAL;
+      emit_move_insn (mem, sym);
     }
   else
     emit_move_insn (mem, const0_rtx);
-  
+
 #ifdef DONT_USE_BUILTIN_SETJMP
   {
     rtx x, note;
-    x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_NORMAL,
+    x = emit_library_call_value (setjmp_libfunc, NULL_RTX, LCT_RETURNS_TWICE,
                                 TYPE_MODE (integer_type_node), 1,
                                 plus_constant (XEXP (fc, 0),
                                                sjlj_fc_jbuf_ofs), Pmode);
 
-    note = emit_note (NULL, NOTE_INSN_EXPECTED_VALUE);
+    note = emit_note (NOTE_INSN_EXPECTED_VALUE);
     NOTE_EXPECTED_VALUE (note) = gen_rtx_EQ (VOIDmode, x, const0_rtx);
 
     emit_cmp_and_jump_insns (x, const0_rtx, NE, 0,
-                            TYPE_MODE (integer_type_node), 0, 0,
-                            dispatch_label);
+                            TYPE_MODE (integer_type_node), 0, dispatch_label);
   }
 #else
   expand_builtin_setjmp_setup (plus_constant (XEXP (fc, 0), sjlj_fc_jbuf_ofs),
@@ -2173,25 +2349,37 @@ sjlj_emit_function_enter (dispatch_label)
 
   for (fn_begin = get_insns (); ; fn_begin = NEXT_INSN (fn_begin))
     if (GET_CODE (fn_begin) == NOTE
-       && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+       && (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG
+           || NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK))
       break;
-  emit_insns_after (seq, fn_begin);
+  if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+    insert_insn_on_edge (seq, ENTRY_BLOCK_PTR->succ);
+  else
+    {
+      rtx last = BB_END (ENTRY_BLOCK_PTR->succ->dest);
+      for (; ; fn_begin = NEXT_INSN (fn_begin))
+       if ((GET_CODE (fn_begin) == NOTE
+            && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
+           || fn_begin == last)
+         break;
+      emit_insn_after (seq, fn_begin);
+    }
 }
 
 /* Call back from expand_function_end to know where we should put
    the call to unwind_sjlj_unregister_libfunc if needed.  */
 
 void
-sjlj_emit_function_exit_after (after)
-     rtx after;
+sjlj_emit_function_exit_after (rtx after)
 {
   cfun->eh->sjlj_exit_after = after;
 }
 
 static void
-sjlj_emit_function_exit ()
+sjlj_emit_function_exit (void)
 {
   rtx seq;
+  edge e;
 
   start_sequence ();
 
@@ -2205,48 +2393,70 @@ sjlj_emit_function_exit ()
      post-dominates all can_throw_internal instructions.  This is
      the last possible moment.  */
 
-  emit_insns_after (seq, cfun->eh->sjlj_exit_after);
+  for (e = EXIT_BLOCK_PTR->pred; e; e = e->pred_next)
+    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.  */
+      if (e->src->next_bb != EXIT_BLOCK_PTR)
+       abort ();
+      for (insn = NEXT_INSN (BB_END (e->src)); insn; insn = NEXT_INSN (insn))
+       if (insn == cfun->eh->sjlj_exit_after)
+         break;
+      if (insn)
+       insert_insn_on_edge (seq, e);
+      else
+       {
+         insn = cfun->eh->sjlj_exit_after;
+         if (GET_CODE (insn) == CODE_LABEL)
+           insn = NEXT_INSN (insn);
+         emit_insn_after (seq, insn);
+       }
+    }
 }
 
 static void
-sjlj_emit_dispatch_table (dispatch_label, lp_info)
-     rtx dispatch_label;
-     struct sjlj_lp_info *lp_info;
+sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info)
 {
   int i, first_reachable;
   rtx mem, dispatch, seq, fc;
+  rtx before;
+  basic_block bb;
+  edge e;
 
   fc = cfun->eh->sjlj_fc;
 
   start_sequence ();
 
   emit_label (dispatch_label);
-  
+
 #ifndef DONT_USE_BUILTIN_SETJMP
   expand_builtin_setjmp_receiver (dispatch_label);
 #endif
 
   /* Load up dispatch index, exc_ptr and filter values from the
      function context.  */
-  mem = change_address (fc, TYPE_MODE (integer_type_node),
-                       plus_constant (XEXP (fc, 0), sjlj_fc_call_site_ofs));
+  mem = adjust_address (fc, TYPE_MODE (integer_type_node),
+                       sjlj_fc_call_site_ofs);
   dispatch = copy_to_reg (mem);
 
-  mem = change_address (fc, word_mode,
-                       plus_constant (XEXP (fc, 0), sjlj_fc_data_ofs));
-  if (word_mode != Pmode)
+  mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs);
+  if (word_mode != ptr_mode)
     {
 #ifdef POINTERS_EXTEND_UNSIGNED
-      mem = convert_memory_address (Pmode, mem);
+      mem = convert_memory_address (ptr_mode, mem);
 #else
-      mem = convert_to_mode (Pmode, mem, 0);
+      mem = convert_to_mode (ptr_mode, mem, 0);
 #endif
     }
   emit_move_insn (cfun->eh->exc_ptr, mem);
 
-  mem = change_address (fc, word_mode,
-                       plus_constant (XEXP (fc, 0),
-                                      sjlj_fc_data_ofs + UNITS_PER_WORD));
+  mem = adjust_address (fc, word_mode, sjlj_fc_data_ofs + UNITS_PER_WORD);
   emit_move_insn (cfun->eh->filter, mem);
 
   /* Jump to one of the directly reachable regions.  */
@@ -2255,8 +2465,7 @@ sjlj_emit_dispatch_table (dispatch_label, lp_info)
   first_reachable = 0;
   for (i = cfun->eh->last_region_number; i > 0; --i)
     {
-      if (! lp_info[i].directly_reachable
-         || lp_info[i].action_index < 0)
+      if (! lp_info[i].directly_reachable)
        continue;
 
       if (! first_reachable)
@@ -2265,26 +2474,29 @@ sjlj_emit_dispatch_table (dispatch_label, lp_info)
          continue;
        }
 
-      emit_cmp_and_jump_insns (dispatch,
-                              GEN_INT (lp_info[i].dispatch_index), EQ,
-                              NULL_RTX, TYPE_MODE (integer_type_node), 0, 0,
+      emit_cmp_and_jump_insns (dispatch, GEN_INT (lp_info[i].dispatch_index),
+                              EQ, NULL_RTX, TYPE_MODE (integer_type_node), 0,
                               cfun->eh->region_array[i]->post_landing_pad);
     }
 
   seq = get_insns ();
   end_sequence ();
 
-  emit_insns_before (seq, (cfun->eh->region_array[first_reachable]
-                          ->post_landing_pad));
+  before = cfun->eh->region_array[first_reachable]->post_landing_pad;
+
+  bb = emit_to_new_bb_before (seq, before);
+  e = make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+  e->count = bb->count;
+  e->probability = REG_BR_PROB_BASE;
 }
 
 static void
-sjlj_build_landing_pads ()
+sjlj_build_landing_pads (void)
 {
   struct sjlj_lp_info *lp_info;
 
-  lp_info = (struct sjlj_lp_info *) xcalloc (cfun->eh->last_region_number + 1,
-                                            sizeof (struct sjlj_lp_info));
+  lp_info = xcalloc (cfun->eh->last_region_number + 1,
+                    sizeof (struct sjlj_lp_info));
 
   if (sjlj_find_directly_reachable_regions (lp_info))
     {
@@ -2307,8 +2519,10 @@ sjlj_build_landing_pads ()
 }
 
 void
-finish_eh_generation ()
+finish_eh_generation (void)
 {
+  basic_block bb;
+
   /* Nothing to do if no regions created.  */
   if (cfun->eh->region_tree == NULL)
     return;
@@ -2322,14 +2536,12 @@ finish_eh_generation ()
      connect many of the handlers, and then type information will not
      be effective.  Still, this is a win over previous implementations.  */
 
-  jump_optimize_minimal (get_insns ());
-  find_basic_blocks (get_insns (), max_reg_num (), 0);
-  cleanup_cfg ();
+  cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
 
   /* These registers are used by the landing pads.  Make sure they
      have been generated.  */
-  get_exception_pointer ();
-  cfun->eh->filter = gen_reg_rtx (word_mode);
+  get_exception_pointer (cfun);
+  get_exception_filter (cfun);
 
   /* Construct the landing pads.  */
 
@@ -2345,48 +2557,101 @@ finish_eh_generation ()
 
   /* We've totally changed the CFG.  Start over.  */
   find_exception_handler_labels ();
-  jump_optimize_minimal (get_insns ());
-  find_basic_blocks (get_insns (), max_reg_num (), 0);
-  cleanup_cfg ();
+  break_superblocks ();
+  if (USING_SJLJ_EXCEPTIONS)
+    commit_edge_insertions ();
+  FOR_EACH_BB (bb)
+    {
+      edge e, next;
+      bool eh = false;
+      for (e = bb->succ; e; e = next)
+       {
+         next = e->succ_next;
+         if (e->flags & EDGE_EH)
+           {
+             remove_edge (e);
+             eh = true;
+           }
+       }
+      if (eh)
+       rtl_make_eh_edge (NULL, bb, BB_END (bb));
+    }
+  cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
 }
 \f
+static hashval_t
+ehl_hash (const void *pentry)
+{
+  struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry;
+
+  /* 2^32 * ((sqrt(5) - 1) / 2) */
+  const hashval_t scaled_golden_ratio = 0x9e3779b9;
+  return CODE_LABEL_NUMBER (entry->label) * scaled_golden_ratio;
+}
+
+static int
+ehl_eq (const void *pentry, const void *pdata)
+{
+  struct ehl_map_entry *entry = (struct ehl_map_entry *) pentry;
+  struct ehl_map_entry *data = (struct ehl_map_entry *) pdata;
+
+  return entry->label == data->label;
+}
+
 /* This section handles removing dead code for flow.  */
 
-/* Remove LABEL from the exception_handler_labels list.  */
+/* Remove LABEL from exception_handler_label_map.  */
 
 static void
-remove_exception_handler_label (label)
-     rtx label;
+remove_exception_handler_label (rtx label)
 {
-  rtx *pl, l;
+  struct ehl_map_entry **slot, tmp;
 
-  for (pl = &exception_handler_labels, l = *pl;
-       XEXP (l, 0) != label;
-       pl = &XEXP (l, 1), l = *pl)
-    continue;
+  /* If exception_handler_label_map was not built yet,
+     there is nothing to do.  */
+  if (cfun->eh->exception_handler_label_map == NULL)
+    return;
 
-  *pl = XEXP (l, 1);
-  free_EXPR_LIST_node (l);
+  tmp.label = label;
+  slot = (struct ehl_map_entry **)
+    htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT);
+  if (! slot)
+    abort ();
+
+  htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
 }
 
 /* Splice REGION from the region tree etc.  */
 
 static void
-remove_eh_handler (region)
-     struct eh_region *region;
+remove_eh_handler (struct eh_region *region)
 {
-  struct eh_region **pp, *p;
+  struct eh_region **pp, **pp_start, *p, *outer, *inner;
   rtx lab;
-  int i;
 
   /* For the benefit of efficiently handling REG_EH_REGION notes,
      replace this region in the region array with its containing
      region.  Note that previous region deletions may result in
-     multiple copies of this region in the array, so we have to
-     search the whole thing.  */
-  for (i = cfun->eh->last_region_number; i > 0; --i)
-    if (cfun->eh->region_array[i] == region)
-      cfun->eh->region_array[i] = region->outer;
+     multiple copies of this region in the array, so we have a
+     list of alternate numbers by which we are known.  */
+
+  outer = region->outer;
+  cfun->eh->region_array[region->region_number] = outer;
+  if (region->aka)
+    {
+      int i;
+      EXECUTE_IF_SET_IN_BITMAP (region->aka, 0, i,
+       { cfun->eh->region_array[i] = outer; });
+    }
+
+  if (outer)
+    {
+      if (!outer->aka)
+        outer->aka = BITMAP_GGC_ALLOC ();
+      if (region->aka)
+       bitmap_a_or_b (outer->aka, outer->aka, region->aka);
+      bitmap_set_bit (outer->aka, region->region_number);
+    }
 
   if (cfun->eh->built_landing_pads)
     lab = region->landing_pad;
@@ -2395,23 +2660,24 @@ remove_eh_handler (region)
   if (lab)
     remove_exception_handler_label (lab);
 
-  if (region->outer)
-    pp = &region->outer->inner;
+  if (outer)
+    pp_start = &outer->inner;
   else
-    pp = &cfun->eh->region_tree;
-  for (p = *pp; p != region; pp = &p->next_peer, p = *pp)
+    pp_start = &cfun->eh->region_tree;
+  for (pp = pp_start, p = *pp; p != region; pp = &p->next_peer, p = *pp)
     continue;
+  *pp = region->next_peer;
 
-  if (region->inner)
+  inner = region->inner;
+  if (inner)
     {
-      for (p = region->inner; p->next_peer ; p = p->next_peer)
-       p->outer = region->outer;
-      p->next_peer = region->next_peer;
-      p->outer = region->outer;
-      *pp = region->inner;
+      for (p = inner; p->next_peer ; p = p->next_peer)
+       p->outer = outer;
+      p->outer = outer;
+
+      p->next_peer = *pp_start;
+      *pp_start = inner;
     }
-  else
-    *pp = region->next_peer;
 
   if (region->type == ERT_CATCH)
     {
@@ -2440,8 +2706,6 @@ remove_eh_handler (region)
            remove_eh_handler (try);
        }
     }
-
-  free (region);
 }
 
 /* LABEL heads a basic block that is about to be deleted.  If this
@@ -2449,10 +2713,10 @@ remove_eh_handler (region)
    delete the region.  */
 
 void
-maybe_remove_eh_handler (label)
-     rtx label;
+maybe_remove_eh_handler (rtx label)
 {
-  int i;
+  struct ehl_map_entry **slot, tmp;
+  struct eh_region *region;
 
   /* ??? After generating landing pads, it's not so simple to determine
      if the region data is completely unused.  One must examine the
@@ -2461,27 +2725,47 @@ maybe_remove_eh_handler (label)
   if (cfun->eh->built_landing_pads)
     return;
 
-  for (i = cfun->eh->last_region_number; i > 0; --i)
+  tmp.label = label;
+  slot = (struct ehl_map_entry **)
+    htab_find_slot (cfun->eh->exception_handler_label_map, &tmp, NO_INSERT);
+  if (! slot)
+    return;
+  region = (*slot)->region;
+  if (! region)
+    return;
+
+  /* Flow will want to remove MUST_NOT_THROW regions as unreachable
+     because there is no path to the fallback call to terminate.
+     But the region continues to affect call-site data until there
+     are no more contained calls, which we don't see here.  */
+  if (region->type == ERT_MUST_NOT_THROW)
     {
-      struct eh_region *region = cfun->eh->region_array[i];
-      if (region && region->label == label)
-       {
-         /* Flow will want to remove MUST_NOT_THROW regions as unreachable
-            because there is no path to the fallback call to terminate.
-            But the region continues to affect call-site data until there
-            are no more contained calls, which we don't see here.  */
-         if (region->type == ERT_MUST_NOT_THROW)
-           {
-             remove_exception_handler_label (region->label);
-             region->label = NULL_RTX;
-           }
-         else
-           remove_eh_handler (region);
-         break;
-       }
+      htab_clear_slot (cfun->eh->exception_handler_label_map, (void **) slot);
+      region->label = NULL_RTX;
     }
+  else
+    remove_eh_handler (region);
+}
+
+/* Invokes CALLBACK for every exception handler label.  Only used by old
+   loop hackery; should not be used by new code.  */
+
+void
+for_each_eh_label (void (*callback) (rtx))
+{
+  htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1,
+                (void *) &callback);
 }
 
+static int
+for_each_eh_label_1 (void **pentry, void *data)
+{
+  struct ehl_map_entry *entry = *(struct ehl_map_entry **)pentry;
+  void (*callback) (rtx) = *(void (**) (rtx)) data;
+
+  (*callback) (entry->label);
+  return 1;
+}
 \f
 /* This section describes CFG exception edges for flow.  */
 
@@ -2490,15 +2774,16 @@ struct reachable_info
 {
   tree types_caught;
   tree types_allowed;
-  rtx handlers;
+  void (*callback) (struct eh_region *, void *);
+  void *callback_data;
+  bool saw_any_handlers;
 };
 
 /* A subroutine of reachable_next_level.  Return true if TYPE, or a
    base class of TYPE, is in HANDLED.  */
 
-static int
-check_handled (handled, type)
-     tree handled, type;
+int
+check_handled (tree handled, tree type)
 {
   tree t;
 
@@ -2522,39 +2807,34 @@ check_handled (handled, type)
 /* 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. 
+   all wired together, so by referencing one, we've got them all.
    Before landing pad generation we reference each handler individually.
 
    LP_REGION contains the landing pad; REGION is the handler.  */
 
 static void
-add_reachable_handler (info, lp_region, region)
-     struct reachable_info *info;
-     struct eh_region *lp_region;
-     struct eh_region *region;
+add_reachable_handler (struct reachable_info *info,
+                      struct eh_region *lp_region, struct eh_region *region)
 {
   if (! info)
     return;
 
+  info->saw_any_handlers = true;
+
   if (cfun->eh->built_landing_pads)
-    {
-      if (! info->handlers)
-       info->handlers = alloc_INSN_LIST (lp_region->landing_pad, NULL_RTX);
-    }
+    info->callback (lp_region, info->callback_data);
   else
-    info->handlers = alloc_INSN_LIST (region->label, info->handlers);
+    info->callback (region, info->callback_data);
 }
 
-/* Process one level of exception regions for reachability.  
+/* Process one level of exception regions for reachability.
    If TYPE_THROWN is non-null, then it is the *exact* type being
    propagated.  If INFO is non-null, then collect handler labels
    and caught/allowed type information between invocations.  */
 
 static enum reachable_code
-reachable_next_level (region, type_thrown, info)
-     struct eh_region *region;
-     tree type_thrown;
-     struct reachable_info *info;
+reachable_next_level (struct eh_region *region, tree type_thrown,
+                     struct reachable_info *info)
 {
   switch (region->type)
     {
@@ -2573,9 +2853,7 @@ reachable_next_level (region, type_thrown, info)
        for (c = region->u.try.catch; c ; c = c->u.catch.next_catch)
          {
            /* A catch-all handler ends the search.  */
-           /* ??? _Unwind_ForcedUnwind will want outer cleanups
-              to be run as well.  */
-           if (c->u.catch.type == NULL)
+           if (c->u.catch.type_list == NULL)
              {
                add_reachable_handler (info, region, c);
                return RNL_CAUGHT;
@@ -2583,14 +2861,20 @@ reachable_next_level (region, type_thrown, info)
 
            if (type_thrown)
              {
-               /* If we have a type match, end the search.  */
-               if (c->u.catch.type == type_thrown
-                   || (lang_eh_type_covers
-                       && (*lang_eh_type_covers) (c->u.catch.type,
-                                                  type_thrown)))
+               /* 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))
                  {
-                   add_reachable_handler (info, region, c);
-                   return RNL_CAUGHT;
+                   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,
@@ -2599,19 +2883,49 @@ reachable_next_level (region, type_thrown, info)
                  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;
-
-           /* A type must not have been previously caught.  */
-           else if (! check_handled (info->types_caught, c->u.catch.type))
+           else
              {
-               add_reachable_handler (info, region, c);
-               info->types_caught = tree_cons (NULL, c->u.catch.type,
-                                               info->types_caught);
+               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 the catch type is a base class of every allowed
-                  type, then we know we can stop the search.  */
-               ret = RNL_MAYBE_CAUGHT;
+                   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;
+                 }
              }
          }
 
@@ -2632,8 +2946,8 @@ reachable_next_level (region, type_thrown, info)
        info->types_allowed = tree_cons (NULL_TREE,
                                         region->u.allowed.type_list,
                                         info->types_allowed);
-           
-      /* If we have definitive information about the type heirarchy,
+
+      /* 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)
@@ -2651,7 +2965,7 @@ reachable_next_level (region, type_thrown, info)
       return RNL_MAYBE_CAUGHT;
 
     case ERT_CATCH:
-      /* Catch regions are handled by their controling try region.  */
+      /* Catch regions are handled by their controlling try region.  */
       return RNL_NOT_CAUGHT;
 
     case ERT_MUST_NOT_THROW:
@@ -2659,16 +2973,17 @@ reachable_next_level (region, type_thrown, info)
         If we've touched down at some landing pad previous, then the
         explicit function call we generated may be used.  Otherwise
         the call is made by the runtime.  */
-      if (info && info->handlers)
+      if (info && info->saw_any_handlers)
        {
          add_reachable_handler (info, region, region);
-          return RNL_CAUGHT;
+         return RNL_CAUGHT;
        }
       else
        return RNL_BLOCKED;
 
     case ERT_THROW:
     case ERT_FIXUP:
+    case ERT_UNKNOWN:
       /* Shouldn't see these here.  */
       break;
     }
@@ -2676,21 +2991,84 @@ reachable_next_level (region, type_thrown, info)
   abort ();
 }
 
-/* Retrieve a list of labels of exception handlers which can be
-   reached by a given insn.  */
+/* Invoke CALLBACK on each region reachable from REGION_NUMBER.  */
 
-rtx
-reachable_handlers (insn)
-     rtx insn;
+void
+foreach_reachable_handler (int region_number, bool is_resx,
+                          void (*callback) (struct eh_region *, void *),
+                          void *callback_data)
 {
   struct reachable_info info;
   struct eh_region *region;
   tree type_thrown;
+
+  memset (&info, 0, sizeof (info));
+  info.callback = callback;
+  info.callback_data = callback_data;
+
+  region = cfun->eh->region_array[region_number];
+
+  type_thrown = NULL_TREE;
+  if (is_resx)
+    {
+      /* A RESX leaves a region instead of entering it.  Thus the
+        region itself may have been deleted out from under us.  */
+      if (region == NULL)
+       return;
+      region = region->outer;
+    }
+  else if (region->type == ERT_THROW)
+    {
+      type_thrown = region->u.throw.type;
+      region = region->outer;
+    }
+
+  while (region)
+    {
+      if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT)
+       break;
+      /* If we have processed one cleanup, there is no point in
+        processing any more of them.  Each cleanup will have an edge
+        to the next outer cleanup region, so the flow graph will be
+        accurate.  */
+      if (region->type == ERT_CLEANUP)
+       region = region->u.cleanup.prev_try;
+      else
+       region = region->outer;
+    }
+}
+
+/* Retrieve a list of labels of exception handlers which can be
+   reached by a given insn.  */
+
+static void
+arh_to_landing_pad (struct eh_region *region, void *data)
+{
+  rtx *p_handlers = data;
+  if (! *p_handlers)
+    *p_handlers = alloc_INSN_LIST (region->landing_pad, NULL_RTX);
+}
+
+static void
+arh_to_label (struct eh_region *region, void *data)
+{
+  rtx *p_handlers = data;
+  *p_handlers = alloc_INSN_LIST (region->label, *p_handlers);
+}
+
+rtx
+reachable_handlers (rtx insn)
+{
+  bool is_resx = false;
+  rtx handlers = NULL;
   int region_number;
 
   if (GET_CODE (insn) == JUMP_INSN
       && GET_CODE (PATTERN (insn)) == RESX)
-    region_number = XINT (PATTERN (insn), 0);
+    {
+      region_number = XINT (PATTERN (insn), 0);
+      is_resx = true;
+    }
   else
     {
       rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
@@ -2699,7 +3077,23 @@ reachable_handlers (insn)
       region_number = INTVAL (XEXP (note, 0));
     }
 
-  memset (&info, 0, sizeof (info));
+  foreach_reachable_handler (region_number, is_resx,
+                            (cfun->eh->built_landing_pads
+                             ? arh_to_landing_pad
+                             : arh_to_label),
+                            &handlers);
+
+  return handlers;
+}
+
+/* Determine if the given INSN can throw an exception that is caught
+   within the function.  */
+
+bool
+can_throw_internal_1 (int region_number)
+{
+  struct eh_region *region;
+  tree type_thrown;
 
   region = cfun->eh->region_array[region_number];
 
@@ -2710,22 +3104,24 @@ reachable_handlers (insn)
       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)
-    if (reachable_next_level (region, type_thrown, &info) >= RNL_CAUGHT)
-      break;
+    {
+      enum reachable_code how = reachable_next_level (region, type_thrown, 0);
+      if (how == RNL_BLOCKED)
+       return false;
+      if (how != RNL_NOT_CAUGHT)
+       return true;
+    }
 
-  return info.handlers;
+  return false;
 }
 
-/* Determine if the given INSN can throw an exception that is caught
-   within the function.  */
-
 bool
-can_throw_internal (insn)
-     rtx insn;
+can_throw_internal (rtx insn)
 {
-  struct eh_region *region;
-  tree type_thrown;
   rtx note;
 
   if (! INSN_P (insn))
@@ -2754,7 +3150,19 @@ can_throw_internal (insn)
   if (!note || INTVAL (XEXP (note, 0)) <= 0)
     return false;
 
-  region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
+  return can_throw_internal_1 (INTVAL (XEXP (note, 0)));
+}
+
+/* Determine if the given INSN can throw an exception that is
+   visible outside the function.  */
+
+bool
+can_throw_external_1 (int region_number)
+{
+  struct eh_region *region;
+  tree type_thrown;
+
+  region = cfun->eh->region_array[region_number];
 
   type_thrown = NULL_TREE;
   if (region->type == ERT_THROW)
@@ -2763,30 +3171,18 @@ can_throw_internal (insn)
       region = region->outer;
     }
 
-  /* If this exception is ignored by each and every containing region,
-     then control passes straight out.  The runtime may handle some
-     regions, which also do not require processing internally.  */
-  for (; region; region = region->outer)
-    {
-      enum reachable_code how = reachable_next_level (region, type_thrown, 0);
-      if (how == RNL_BLOCKED)
-       return false;
-      if (how != RNL_NOT_CAUGHT)
-        return true;
-    }
+  /* 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 false;
+  return true;
 }
 
-/* Determine if the given INSN can throw an exception that is
-   visible outside the function.  */
-
 bool
-can_throw_external (insn)
-     rtx insn;
+can_throw_external (rtx insn)
 {
-  struct eh_region *region;
-  tree type_thrown;
   rtx note;
 
   if (! INSN_P (insn))
@@ -2825,43 +3221,53 @@ can_throw_external (insn)
   if (INTVAL (XEXP (note, 0)) <= 0)
     return false;
 
-  region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
-
-  type_thrown = NULL_TREE;
-  if (region->type == ERT_THROW)
-    {
-      type_thrown = region->u.throw.type;
-      region = region->outer;
-    }
-
-  /* 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;
+  return can_throw_external_1 (INTVAL (XEXP (note, 0)));
 }
 
-/* True if nothing in this function can throw outside this function.  */
+/* Set current_function_nothrow and cfun->all_throwers_are_sibcalls.  */
 
-bool
-nothrow_function_p ()
+void
+set_nothrow_function_flags (void)
 {
   rtx insn;
 
+  current_function_nothrow = 1;
+
+  /* Assume cfun->all_throwers_are_sibcalls until we encounter
+     something that can throw an exception.  We specifically exempt
+     CALL_INSNs that are SIBLING_CALL_P, as these are really jumps,
+     and can't throw.  Most CALL_INSNs are not SIBLING_CALL_P, so this
+     is optimistic.  */
+
+  cfun->all_throwers_are_sibcalls = 1;
+
   if (! flag_exceptions)
-    return true;
+    return;
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     if (can_throw_external (insn))
-      return false;
+      {
+       current_function_nothrow = 0;
+
+       if (GET_CODE (insn) != CALL_INSN || !SIBLING_CALL_P (insn))
+         {
+           cfun->all_throwers_are_sibcalls = 0;
+           return;
+         }
+      }
+
   for (insn = current_function_epilogue_delay_list; insn;
        insn = XEXP (insn, 1))
     if (can_throw_external (insn))
-      return false;
+      {
+       current_function_nothrow = 0;
 
-  return true;
+       if (GET_CODE (insn) != CALL_INSN || !SIBLING_CALL_P (insn))
+         {
+           cfun->all_throwers_are_sibcalls = 0;
+           return;
+         }
+      }
 }
 
 \f
@@ -2871,10 +3277,10 @@ nothrow_function_p ()
    On the SPARC, this means flushing the register windows.  */
 
 void
-expand_builtin_unwind_init ()
+expand_builtin_unwind_init (void)
 {
   /* Set this so all the registers get saved in our frame; we need to be
-     able to copy the saved values for any registers from frames we unwind. */
+     able to copy the saved values for any registers from frames we unwind.  */
   current_function_has_nonlocal_label = 1;
 
 #ifdef SETUP_FRAME_ADDRESSES
@@ -2883,8 +3289,7 @@ expand_builtin_unwind_init ()
 }
 
 rtx
-expand_builtin_eh_return_data_regno (arglist)
-     tree arglist;
+expand_builtin_eh_return_data_regno (tree arglist)
 {
   tree which = TREE_VALUE (arglist);
   unsigned HOST_WIDE_INT iwhich;
@@ -2906,21 +3311,30 @@ expand_builtin_eh_return_data_regno (arglist)
   iwhich = DBX_REGISTER_NUMBER (iwhich);
 #endif
 
-  return GEN_INT (iwhich);      
+  return GEN_INT (iwhich);
 }
 
 /* Given a value extracted from the return address register or stack slot,
    return the actual address encoded in that value.  */
 
 rtx
-expand_builtin_extract_return_addr (addr_tree)
-     tree addr_tree;
+expand_builtin_extract_return_addr (tree addr_tree)
 {
   rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0);
 
+  if (GET_MODE (addr) != Pmode
+      && GET_MODE (addr) != VOIDmode)
+    {
+#ifdef POINTERS_EXTEND_UNSIGNED
+      addr = convert_memory_address (Pmode, addr);
+#else
+      addr = convert_to_mode (Pmode, addr, 0);
+#endif
+    }
+
   /* First mask out any unwanted bits.  */
 #ifdef MASK_RETURN_ADDR
-  expand_and (addr, MASK_RETURN_ADDR, addr);
+  expand_and (Pmode, addr, MASK_RETURN_ADDR, addr);
 #endif
 
   /* Then adjust to find the real return address.  */
@@ -2936,10 +3350,11 @@ expand_builtin_extract_return_addr (addr_tree)
    stack slot so the epilogue will return to that address.  */
 
 rtx
-expand_builtin_frob_return_addr (addr_tree)
-     tree addr_tree;
+expand_builtin_frob_return_addr (tree addr_tree)
 {
-  rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0);
+  rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
+
+  addr = convert_memory_address (Pmode, addr);
 
 #ifdef RETURN_ADDR_OFFSET
   addr = force_reg (Pmode, addr);
@@ -2953,75 +3368,103 @@ expand_builtin_frob_return_addr (addr_tree)
    exception handler.  */
 
 void
-expand_builtin_eh_return (stackadj_tree, handler_tree)
-    tree stackadj_tree, handler_tree;
+expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED,
+                         tree handler_tree)
 {
-  rtx stackadj, handler;
-
-  stackadj = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0);
-  handler = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0);
+  rtx tmp;
+
+#ifdef EH_RETURN_STACKADJ_RTX
+  tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0);
+  tmp = convert_memory_address (Pmode, tmp);
+  if (!cfun->eh->ehr_stackadj)
+    cfun->eh->ehr_stackadj = copy_to_reg (tmp);
+  else if (tmp != cfun->eh->ehr_stackadj)
+    emit_move_insn (cfun->eh->ehr_stackadj, tmp);
+#endif
 
-  if (! cfun->eh->ehr_label)
-    {
-      cfun->eh->ehr_stackadj = copy_to_reg (stackadj);
-      cfun->eh->ehr_handler = copy_to_reg (handler);
-      cfun->eh->ehr_label = gen_label_rtx ();
-    }
-  else
-    {
-      if (stackadj != cfun->eh->ehr_stackadj)
-       emit_move_insn (cfun->eh->ehr_stackadj, stackadj);
-      if (handler != cfun->eh->ehr_handler)
-       emit_move_insn (cfun->eh->ehr_handler, handler);
-    }
+  tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0);
+  tmp = convert_memory_address (Pmode, tmp);
+  if (!cfun->eh->ehr_handler)
+    cfun->eh->ehr_handler = copy_to_reg (tmp);
+  else if (tmp != cfun->eh->ehr_handler)
+    emit_move_insn (cfun->eh->ehr_handler, tmp);
 
+  if (!cfun->eh->ehr_label)
+    cfun->eh->ehr_label = gen_label_rtx ();
   emit_jump (cfun->eh->ehr_label);
 }
 
 void
-expand_eh_return ()
+expand_eh_return (void)
 {
-  rtx sa, ra, around_label;
+  rtx around_label;
 
   if (! cfun->eh->ehr_label)
     return;
 
-  sa = EH_RETURN_STACKADJ_RTX;
-  if (! sa)
-    {
-      error ("__builtin_eh_return not supported on this target");
-      return;
-    }
-
   current_function_calls_eh_return = 1;
 
+#ifdef EH_RETURN_STACKADJ_RTX
+  emit_move_insn (EH_RETURN_STACKADJ_RTX, const0_rtx);
+#endif
+
   around_label = gen_label_rtx ();
-  emit_move_insn (sa, const0_rtx);
   emit_jump (around_label);
 
   emit_label (cfun->eh->ehr_label);
   clobber_return_register ();
 
+#ifdef EH_RETURN_STACKADJ_RTX
+  emit_move_insn (EH_RETURN_STACKADJ_RTX, cfun->eh->ehr_stackadj);
+#endif
+
 #ifdef HAVE_eh_return
   if (HAVE_eh_return)
-    emit_insn (gen_eh_return (cfun->eh->ehr_stackadj, cfun->eh->ehr_handler));
+    emit_insn (gen_eh_return (cfun->eh->ehr_handler));
   else
 #endif
     {
-      ra = EH_RETURN_HANDLER_RTX;
-      if (! ra)
-       {
-         error ("__builtin_eh_return not supported on this target");
-         ra = gen_reg_rtx (Pmode);
-       }
-
-      emit_move_insn (sa, cfun->eh->ehr_stackadj);
-      emit_move_insn (ra, cfun->eh->ehr_handler);
+#ifdef EH_RETURN_HANDLER_RTX
+      emit_move_insn (EH_RETURN_HANDLER_RTX, cfun->eh->ehr_handler);
+#else
+      error ("__builtin_eh_return not supported on this target");
+#endif
     }
 
   emit_label (around_label);
 }
+
+/* Convert a ptr_mode address ADDR_TREE to a Pmode address controlled by
+   POINTERS_EXTEND_UNSIGNED and return it.  */
+
+rtx
+expand_builtin_extend_pointer (tree addr_tree)
+{
+  rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
+  int extend;
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+  extend = POINTERS_EXTEND_UNSIGNED;
+#else
+  /* The previous EH code did an unsigned extend by default, so we do this also
+     for consistency.  */
+  extend = 1;
+#endif
+
+  return convert_modes (word_mode, ptr_mode, addr, extend);
+}
 \f
+/* In the following functions, we represent entries in the action table
+   as 1-based indices.  Special cases are:
+
+        0:     null action record, non-null landing pad; implies cleanups
+       -1:     null action record, null landing pad; implies no action
+       -2:     no call-site entry; implies must_not_throw
+       -3:     we have yet to process outer regions
+
+   Further, no special cases apply to the "next" field of the record.
+   For next, 0 means end of list.  */
+
 struct action_record
 {
   int offset;
@@ -3030,9 +3473,7 @@ struct action_record
 };
 
 static int
-action_record_eq (pentry, pdata)
-     const PTR pentry;
-     const PTR pdata;
+action_record_eq (const void *pentry, const void *pdata)
 {
   const struct action_record *entry = (const struct action_record *) pentry;
   const struct action_record *data = (const struct action_record *) pdata;
@@ -3040,17 +3481,14 @@ action_record_eq (pentry, pdata)
 }
 
 static hashval_t
-action_record_hash (pentry)
-     const PTR pentry;
+action_record_hash (const void *pentry)
 {
   const struct action_record *entry = (const struct action_record *) pentry;
   return entry->next * 1009 + entry->filter;
 }
 
 static int
-add_action_record (ar_hash, filter, next)
-     htab_t ar_hash;
-     int filter, next;
+add_action_record (htab_t ar_hash, int filter, int next)
 {
   struct action_record **slot, *new, tmp;
 
@@ -3060,7 +3498,7 @@ add_action_record (ar_hash, filter, next)
 
   if ((new = *slot) == NULL)
     {
-      new = (struct action_record *) xmalloc (sizeof (*new));
+      new = xmalloc (sizeof (*new));
       new->offset = VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data) + 1;
       new->filter = filter;
       new->next = next;
@@ -3069,7 +3507,7 @@ add_action_record (ar_hash, filter, next)
       /* The filter value goes in untouched.  The link to the next
         record is a "self-relative" byte offset, or zero to indicate
         that there is no next record.  So convert the absolute 1 based
-        indicies we've been carrying around into a displacement.  */
+        indices we've been carrying around into a displacement.  */
 
       push_sleb128 (&cfun->eh->action_record_data, filter);
       if (next)
@@ -3081,9 +3519,7 @@ add_action_record (ar_hash, filter, next)
 }
 
 static int
-collect_one_action_chain (ar_hash, region)
-     htab_t ar_hash;
-     struct eh_region *region;
+collect_one_action_chain (htab_t ar_hash, struct eh_region *region)
 {
   struct eh_region *c;
   int next;
@@ -3114,21 +3550,46 @@ collect_one_action_chain (ar_hash, region)
       /* Process the associated catch regions in reverse order.
         If there's a catch-all handler, then we don't need to
         search outer regions.  Use a magic -3 value to record
-        that we havn't done the outer search.  */
+        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 == NULL)
-           next = add_action_record (ar_hash, c->u.catch.filter, 0);
+         if (c->u.catch.type_list == NULL)
+           {
+             /* Retrieve the filter from the head of the filter list
+                where we have stored it (see assign_filter_values).  */
+             int filter
+               = TREE_INT_CST_LOW (TREE_VALUE (c->u.catch.filter_list));
+
+             next = add_action_record (ar_hash, filter, 0);
+           }
          else
            {
+             /* Once the outer search is done, trigger an action record for
+                 each filter we have.  */
+             tree flt_node;
+
              if (next == -3)
                {
                  next = collect_one_action_chain (ar_hash, region->outer);
-                 if (next < 0)
+
+                 /* If there is no next action, terminate the chain.  */
+                 if (next == -1)
                    next = 0;
+                 /* If all outer actions are cleanups or must_not_throw,
+                    we'll have no action record for it, since we had wanted
+                    to encode these states in the call-site record directly.
+                    Add a cleanup action to the chain to catch these.  */
+                 else if (next <= 0)
+                   next = add_action_record (ar_hash, 0, 0);
+               }
+
+             flt_node = c->u.catch.filter_list;
+             for (; flt_node; flt_node = TREE_CHAIN (flt_node))
+               {
+                 int filter = TREE_INT_CST_LOW (TREE_VALUE (flt_node));
+                 next = add_action_record (ar_hash, filter, next);
                }
-             next = add_action_record (ar_hash, c->u.catch.filter, next);
            }
        }
       return next;
@@ -3137,8 +3598,18 @@ collect_one_action_chain (ar_hash, region)
       /* An exception specification adds its filter to the
         beginning of the chain.  */
       next = collect_one_action_chain (ar_hash, region->outer);
-      return add_action_record (ar_hash, region->u.allowed.filter,
-                               next < 0 ? 0 : next);
+
+      /* If there is no next action, terminate the chain.  */
+      if (next == -1)
+       next = 0;
+      /* If all outer actions are cleanups or must_not_throw,
+        we'll have no action record for it, since we had wanted
+        to encode these states in the call-site record directly.
+        Add a cleanup action to the chain to catch these.  */
+      else if (next <= 0)
+       next = add_action_record (ar_hash, 0, 0);
+      
+      return add_action_record (ar_hash, region->u.allowed.filter, next);
 
     case ERT_MUST_NOT_THROW:
       /* A must-not-throw region with no inner handlers or cleanups
@@ -3159,9 +3630,7 @@ collect_one_action_chain (ar_hash, region)
 }
 
 static int
-add_call_site (landing_pad, action)
-     rtx landing_pad;
-     int action;
+add_call_site (rtx landing_pad, int action)
 {
   struct call_site_record *data = cfun->eh->call_site_data;
   int used = cfun->eh->call_site_data_used;
@@ -3170,8 +3639,7 @@ add_call_site (landing_pad, action)
   if (used >= size)
     {
       size = (size ? size * 2 : 64);
-      data = (struct call_site_record *)
-       xrealloc (data, sizeof (*data) * size);
+      data = ggc_realloc (data, sizeof (*data) * size);
       cfun->eh->call_site_data = data;
       cfun->eh->call_site_data_size = size;
     }
@@ -3189,7 +3657,7 @@ add_call_site (landing_pad, action)
    instead to call site entries.  */
 
 void
-convert_to_eh_region_ranges ()
+convert_to_eh_region_ranges (void)
 {
   rtx insn, iter, note;
   htab_t ar_hash;
@@ -3197,7 +3665,7 @@ convert_to_eh_region_ranges ()
   rtx last_action_insn = NULL_RTX;
   rtx last_landing_pad = NULL_RTX;
   rtx first_no_action_insn = NULL_RTX;
-  int call_site;
+  int call_site = 0;
 
   if (USING_SJLJ_EXCEPTIONS || cfun->eh->region_tree == NULL)
     return;
@@ -3290,7 +3758,7 @@ convert_to_eh_region_ranges ()
               are created.  */
            if (this_action >= -1)
              {
-               call_site = add_call_site (this_landing_pad, 
+               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;
@@ -3313,9 +3781,7 @@ convert_to_eh_region_ranges ()
 
 \f
 static void
-push_uleb128 (data_area, value)
-     varray_type *data_area;
-     unsigned int value;
+push_uleb128 (varray_type *data_area, unsigned int value)
 {
   do
     {
@@ -3329,9 +3795,7 @@ push_uleb128 (data_area, value)
 }
 
 static void
-push_sleb128 (data_area, value)
-     varray_type *data_area;
-     int value;
+push_sleb128 (varray_type *data_area, int value)
 {
   unsigned char byte;
   int more;
@@ -3350,86 +3814,9 @@ push_sleb128 (data_area, value)
 }
 
 \f
-#define DW_EH_PE_absptr                0x00
-#define DW_EH_PE_omit          0xff
-
-#define DW_EH_PE_uleb128       0x01
-#define DW_EH_PE_udata2                0x02
-#define DW_EH_PE_udata4                0x03
-#define DW_EH_PE_udata8                0x04
-#define DW_EH_PE_sleb128       0x09
-#define DW_EH_PE_sdata2                0x0A
-#define DW_EH_PE_sdata4                0x0B
-#define DW_EH_PE_sdata8                0x0C
-#define DW_EH_PE_signed                0x08
-
-#define DW_EH_PE_pcrel         0x10
-#define DW_EH_PE_textrel       0x20
-#define DW_EH_PE_datarel       0x30
-#define DW_EH_PE_funcrel       0x40
-
-static const char *
-eh_data_format_name (format)
-     int format;
-{
-  switch (format)
-    {
-    case DW_EH_PE_absptr:      return "absolute";
-    case DW_EH_PE_omit:                return "omit";
-
-    case DW_EH_PE_uleb128:     return "uleb128";
-    case DW_EH_PE_udata2:      return "udata2";
-    case DW_EH_PE_udata4:      return "udata4";
-    case DW_EH_PE_udata8:      return "udata8";
-    case DW_EH_PE_sleb128:     return "sleb128";
-    case DW_EH_PE_sdata2:      return "sdata2";
-    case DW_EH_PE_sdata4:      return "sdata4";
-    case DW_EH_PE_sdata8:      return "sdata8";
-
-    case DW_EH_PE_uleb128 | DW_EH_PE_pcrel:    return "pcrel uleb128";
-    case DW_EH_PE_udata2 | DW_EH_PE_pcrel:     return "pcrel udata2";
-    case DW_EH_PE_udata4 | DW_EH_PE_pcrel:     return "pcrel udata4";
-    case DW_EH_PE_udata8 | DW_EH_PE_pcrel:     return "pcrel udata8";
-    case DW_EH_PE_sleb128 | DW_EH_PE_pcrel:    return "pcrel sleb128";
-    case DW_EH_PE_sdata2 | DW_EH_PE_pcrel:     return "pcrel sdata2";
-    case DW_EH_PE_sdata4 | DW_EH_PE_pcrel:     return "pcrel sdata4";
-    case DW_EH_PE_sdata8 | DW_EH_PE_pcrel:     return "pcrel sdata8";
-
-    case DW_EH_PE_uleb128 | DW_EH_PE_textrel:  return "textrel uleb128";
-    case DW_EH_PE_udata2 | DW_EH_PE_textrel:   return "textrel udata2";
-    case DW_EH_PE_udata4 | DW_EH_PE_textrel:   return "textrel udata4";
-    case DW_EH_PE_udata8 | DW_EH_PE_textrel:   return "textrel udata8";
-    case DW_EH_PE_sleb128 | DW_EH_PE_textrel:  return "textrel sleb128";
-    case DW_EH_PE_sdata2 | DW_EH_PE_textrel:   return "textrel sdata2";
-    case DW_EH_PE_sdata4 | DW_EH_PE_textrel:   return "textrel sdata4";
-    case DW_EH_PE_sdata8 | DW_EH_PE_textrel:   return "textrel sdata8";
-
-    case DW_EH_PE_uleb128 | DW_EH_PE_datarel:  return "datarel uleb128";
-    case DW_EH_PE_udata2 | DW_EH_PE_datarel:   return "datarel udata2";
-    case DW_EH_PE_udata4 | DW_EH_PE_datarel:   return "datarel udata4";
-    case DW_EH_PE_udata8 | DW_EH_PE_datarel:   return "datarel udata8";
-    case DW_EH_PE_sleb128 | DW_EH_PE_datarel:  return "datarel sleb128";
-    case DW_EH_PE_sdata2 | DW_EH_PE_datarel:   return "datarel sdata2";
-    case DW_EH_PE_sdata4 | DW_EH_PE_datarel:   return "datarel sdata4";
-    case DW_EH_PE_sdata8 | DW_EH_PE_datarel:   return "datarel sdata8";
-
-    case DW_EH_PE_uleb128 | DW_EH_PE_funcrel:  return "funcrel uleb128";
-    case DW_EH_PE_udata2 | DW_EH_PE_funcrel:   return "funcrel udata2";
-    case DW_EH_PE_udata4 | DW_EH_PE_funcrel:   return "funcrel udata4";
-    case DW_EH_PE_udata8 | DW_EH_PE_funcrel:   return "funcrel udata8";
-    case DW_EH_PE_sleb128 | DW_EH_PE_funcrel:  return "funcrel sleb128";
-    case DW_EH_PE_sdata2 | DW_EH_PE_funcrel:   return "funcrel sdata2";
-    case DW_EH_PE_sdata4 | DW_EH_PE_funcrel:   return "funcrel sdata4";
-    case DW_EH_PE_sdata8 | DW_EH_PE_funcrel:   return "funcrel sdata8";
-
-    default:
-      abort ();
-    }
-}
-
 #ifndef HAVE_AS_LEB128
 static int
-dw2_size_of_call_site_table ()
+dw2_size_of_call_site_table (void)
 {
   int n = cfun->eh->call_site_data_used;
   int size = n * (4 + 4 + 4);
@@ -3445,7 +3832,7 @@ dw2_size_of_call_site_table ()
 }
 
 static int
-sjlj_size_of_call_site_table ()
+sjlj_size_of_call_site_table (void)
 {
   int n = cfun->eh->call_site_data_used;
   int size = 0;
@@ -3463,9 +3850,9 @@ sjlj_size_of_call_site_table ()
 #endif
 
 static void
-dw2_output_call_site_table ()
+dw2_output_call_site_table (void)
 {
-  const char *function_start_lab
+  const char *const function_start_lab
     = IDENTIFIER_POINTER (current_function_func_begin_label);
   int n = cfun->eh->call_site_data_used;
   int i;
@@ -3515,7 +3902,7 @@ dw2_output_call_site_table ()
 }
 
 static void
-sjlj_output_call_site_table ()
+sjlj_output_call_site_table (void)
 {
   int n = cfun->eh->call_site_data_used;
   int i;
@@ -3532,10 +3919,37 @@ sjlj_output_call_site_table ()
   call_site_base += n;
 }
 
+/* Tell assembler to switch to the section for the exception handling
+   table.  */
+
+void
+default_exception_section (void)
+{
+  if (targetm.have_named_sections)
+    {
+      int flags;
+#ifdef HAVE_LD_RO_RW_SECTION_MIXING
+      int tt_format = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1);
+
+      flags = (! flag_pic
+              || ((tt_format & 0x70) != DW_EH_PE_absptr
+                  && (tt_format & 0x70) != DW_EH_PE_aligned))
+             ? 0 : SECTION_WRITE;
+#else
+      flags = SECTION_WRITE;
+#endif
+      named_section_flags (".gcc_except_table", flags);
+    }
+  else if (flag_pic)
+    data_section ();
+  else
+    readonly_data_section ();
+}
+
 void
-output_function_exception_table ()
+output_function_exception_table (void)
 {
-  int format, i, n;
+  int tt_format, cs_format, lp_format, i, n;
 #ifdef HAVE_AS_LEB128
   char ttype_label[32];
   char cs_after_size_label[32];
@@ -3544,25 +3958,42 @@ output_function_exception_table ()
   int call_site_len;
 #endif
   int have_tt_data;
-  int funcdef_number;
+  int tt_format_size = 0;
 
   /* Not all functions need anything.  */
   if (! cfun->uses_eh_lsda)
     return;
 
-  funcdef_number = (USING_SJLJ_EXCEPTIONS
-                   ? sjlj_funcdef_number
-                   : current_funcdef_number);
-
-  exception_section ();
+#ifdef IA64_UNWIND_INFO
+  fputs ("\t.personality\t", asm_out_file);
+  output_addr_const (asm_out_file, eh_personality_libfunc);
+  fputs ("\n\t.handlerdata\n", asm_out_file);
+  /* Note that varasm still thinks we're in the function's code section.
+     The ".endp" directive that will immediately follow will take us back.  */
+#else
+  targetm.asm_out.exception_section ();
+#endif
 
   have_tt_data = (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) > 0
                  || VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) > 0);
 
-  if (have_tt_data)
-    assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode));
+  /* Indicate the format of the @TType entries.  */
+  if (! have_tt_data)
+    tt_format = DW_EH_PE_omit;
+  else
+    {
+      tt_format = ASM_PREFERRED_EH_DATA_FORMAT (/*code=*/0, /*global=*/1);
+#ifdef HAVE_AS_LEB128
+      ASM_GENERATE_INTERNAL_LABEL (ttype_label, "LLSDATT",
+                                  current_function_funcdef_no);
+#endif
+      tt_format_size = size_of_encoded_value (tt_format);
+
+      assemble_align (tt_format_size * BITS_PER_UNIT);
+    }
 
-  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LLSDA", funcdef_number);
+  targetm.asm_out.internal_label (asm_out_file, "LLSDA",
+                            current_function_funcdef_no);
 
   /* The LSDA header.  */
 
@@ -3572,26 +4003,14 @@ output_function_exception_table ()
      be most useful in moving the landing pads completely out of
      line to another section, but it could also be used to minimize
      the size of uleb128 landing pad offsets.  */
-  format = DW_EH_PE_omit;
-  dw2_asm_output_data (1, format, "@LPStart format (%s)",
-                      eh_data_format_name (format));
+  lp_format = DW_EH_PE_omit;
+  dw2_asm_output_data (1, lp_format, "@LPStart format (%s)",
+                      eh_data_format_name (lp_format));
 
   /* @LPStart pointer would go here.  */
 
-  /* Indicate the format of the @TType entries.  */
-  if (! have_tt_data)
-    format = DW_EH_PE_omit;
-  else
-    {
-      /* ??? Define a ASM_PREFERRED_DATA_FORMAT to say what 
-        sort of dynamic-relocation-free reference to emit.  */
-      format = 0;
-#ifdef HAVE_AS_LEB128
-      ASM_GENERATE_INTERNAL_LABEL (ttype_label, "LLSDATT", funcdef_number);
-#endif
-    }
-  dw2_asm_output_data (1, format, "@TType format (%s)",
-                      eh_data_format_name (format));
+  dw2_asm_output_data (1, tt_format, "@TType format (%s)",
+                      eh_data_format_name (tt_format));
 
 #ifndef HAVE_AS_LEB128
   if (USING_SJLJ_EXCEPTIONS)
@@ -3605,21 +4024,21 @@ output_function_exception_table ()
     {
 #ifdef HAVE_AS_LEB128
       char ttype_after_disp_label[32];
-      ASM_GENERATE_INTERNAL_LABEL (ttype_after_disp_label, "LLSDATTD", 
-                                  funcdef_number);
+      ASM_GENERATE_INTERNAL_LABEL (ttype_after_disp_label, "LLSDATTD",
+                                  current_function_funcdef_no);
       dw2_asm_output_delta_uleb128 (ttype_label, ttype_after_disp_label,
                                    "@TType base offset");
       ASM_OUTPUT_LABEL (asm_out_file, ttype_after_disp_label);
 #else
       /* Ug.  Alignment queers things.  */
-      unsigned int before_disp, after_disp, last_disp, disp, align;
+      unsigned int before_disp, after_disp, last_disp, disp;
 
-      align = POINTER_SIZE / BITS_PER_UNIT;
       before_disp = 1 + 1;
       after_disp = (1 + size_of_uleb128 (call_site_len)
                    + call_site_len
                    + VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data)
-                   + VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) * align);
+                   + (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data)
+                      * tt_format_size));
 
       disp = after_disp;
       do
@@ -3629,8 +4048,8 @@ output_function_exception_table ()
          last_disp = disp;
          disp_size = size_of_uleb128 (disp);
          pad = before_disp + disp_size + after_disp;
-         if (pad % align)
-           pad = align - (pad % align);
+         if (pad % tt_format_size)
+           pad = tt_format_size - (pad % tt_format_size);
          else
            pad = 0;
          disp = after_disp + pad;
@@ -3643,18 +4062,18 @@ output_function_exception_table ()
 
   /* Indicate the format of the call-site offsets.  */
 #ifdef HAVE_AS_LEB128
-  format = DW_EH_PE_uleb128;
+  cs_format = DW_EH_PE_uleb128;
 #else
-  format = DW_EH_PE_udata4;
+  cs_format = DW_EH_PE_udata4;
 #endif
-  dw2_asm_output_data (1, format, "call-site format (%s)",
-                      eh_data_format_name (format));
+  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",
-                              funcdef_number);
+                              current_function_funcdef_no);
   ASM_GENERATE_INTERNAL_LABEL (cs_end_label, "LLSDACSE",
-                              funcdef_number);
+                              current_function_funcdef_no);
   dw2_asm_output_delta_uleb128 (cs_end_label, cs_after_size_label,
                                "Call-site table length");
   ASM_OUTPUT_LABEL (asm_out_file, cs_after_size_label);
@@ -3678,20 +4097,43 @@ output_function_exception_table ()
                         (i ? NULL : "Action record table"));
 
   if (have_tt_data)
-    assemble_eh_align (GET_MODE_ALIGNMENT (ptr_mode));
+    assemble_align (tt_format_size * BITS_PER_UNIT);
 
   i = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data);
   while (i-- > 0)
     {
       tree type = VARRAY_TREE (cfun->eh->ttype_data, i);
+      rtx value;
 
       if (type == NULL_TREE)
-       type = integer_zero_node;
+       value = const0_rtx;
       else
-       type = lookup_type_for_runtime (type);
+       {
+         struct cgraph_varpool_node *node;
+
+         type = lookup_type_for_runtime (type);
+         value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER);
 
-      /* ??? Handle ASM_PREFERRED_DATA_FORMAT.  */
-      output_constant (type, GET_MODE_SIZE (ptr_mode));
+         /* Let cgraph know that the rtti decl is used.  Not all of the
+            paths below go through assemble_integer, which would take
+            care of this for us.  */
+         STRIP_NOPS (type);
+         if (TREE_CODE (type) == ADDR_EXPR)
+           {
+             type = TREE_OPERAND (type, 0);
+             node = cgraph_varpool_node (type);
+             if (node)
+               cgraph_varpool_mark_needed_node (node);
+           }
+         else if (TREE_CODE (type) != INTEGER_CST)
+           abort ();
+       }
+
+      if (tt_format == DW_EH_PE_absptr || tt_format == DW_EH_PE_aligned)
+       assemble_integer (value, tt_format_size,
+                         tt_format_size * BITS_PER_UNIT, 1);
+      else
+       dw2_asm_output_encoded_addr_rtx (tt_format, value, NULL);
     }
 
 #ifdef HAVE_AS_LEB128
@@ -3706,7 +4148,6 @@ output_function_exception_table ()
                         (i ? NULL : "Exception specification table"));
 
   function_section (current_function_decl);
-
-  if (USING_SJLJ_EXCEPTIONS)
-    sjlj_funcdef_number += 1;
 }
+
+#include "gt-except.h"