OSDN Git Service

* loop-invariant.c: New file.
[pf3gnuchains/gcc-fork.git] / gcc / except.c
index d7daec3..21ade90 100644 (file)
@@ -82,9 +82,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #endif
 
 
-/* Nonzero means enable synchronous exceptions for non-call instructions.  */
-int flag_non_call_exceptions;
-
 /* Protect cleanup actions with must-not-throw regions, with a call
    to the given failure handler.  */
 tree (*lang_protect_cleanup_actions) (void);
@@ -134,7 +131,7 @@ struct eh_region GTY(())
 
   /* Each region does exactly one thing.  */
   enum eh_region_type
-  {
+  { 
     ERT_UNKNOWN = 0,
     ERT_CLEANUP,
     ERT_TRY,
@@ -195,6 +192,7 @@ struct eh_region GTY(())
 
   /* Entry point for this region's handler before landing pads are built.  */
   rtx label;
+  tree tree_label;
 
   /* Entry point for this region's handler from the runtime eh library.  */
   rtx landing_pad;
@@ -264,9 +262,6 @@ static tree lookup_type_for_runtime (tree);
 
 static struct eh_region *expand_eh_region_end (void);
 
-static rtx get_exception_filter (struct function *);
-
-static void collect_eh_region_array (void);
 static void resolve_fixup_regions (void);
 static void remove_fixup_regions (void);
 static void remove_unreachable_regions (rtx);
@@ -302,8 +297,6 @@ static void remove_exception_handler_label (rtx);
 static void remove_eh_handler (struct eh_region *);
 static int for_each_eh_label_1 (void **, void *);
 
-struct reachable_info;
-
 /* The return value of reachable_next_level.  */
 enum reachable_code
 {
@@ -317,9 +310,7 @@ enum reachable_code
   RNL_BLOCKED
 };
 
-static int check_handled (tree, tree);
-static void add_reachable_handler (struct reachable_info *,
-                                  struct eh_region *, struct eh_region *);
+struct reachable_info;
 static enum reachable_code reachable_next_level (struct eh_region *, tree,
                                                 struct reachable_info *);
 
@@ -461,6 +452,128 @@ init_eh_for_function (void)
   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.  */
+
+static struct eh_region *
+gen_eh_region (enum eh_region_type type, struct eh_region *outer)
+{
+  struct eh_region *new;
+
+#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)
+    {
+      new->next_peer = outer->inner;
+      outer->inner = new;
+    }
+  else
+    {
+      new->next_peer = cfun->eh->region_tree;
+      cfun->eh->region_tree = new;
+    }
+
+  new->region_number = ++cfun->eh->last_region_number;
+
+  return new;
+}
+
+struct eh_region *
+gen_eh_region_cleanup (struct eh_region *outer, struct eh_region *prev_try)
+{
+  struct eh_region *cleanup = gen_eh_region (ERT_CLEANUP, outer);
+  cleanup->u.cleanup.prev_try = prev_try;
+  return cleanup;
+}
+
+struct eh_region *
+gen_eh_region_try (struct eh_region *outer)
+{
+  return gen_eh_region (ERT_TRY, outer);
+}
+
+struct eh_region *
+gen_eh_region_catch (struct eh_region *t, tree type_or_list)
+{
+  struct eh_region *c, *l;
+  tree type_list, type_node;
+
+  /* Ensure to always end up with a type list to normalize further
+     processing, then register each type against the runtime types map.  */
+  type_list = type_or_list;
+  if (type_or_list)
+    {
+      if (TREE_CODE (type_or_list) != TREE_LIST)
+       type_list = tree_cons (NULL_TREE, type_or_list, NULL_TREE);
+
+      type_node = type_list;
+      for (; type_node; type_node = TREE_CHAIN (type_node))
+       add_type_for_runtime (TREE_VALUE (type_node));
+    }
+
+  c = gen_eh_region (ERT_CATCH, t->outer);
+  c->u.catch.type_list = type_list;
+  l = t->u.try.last_catch;
+  c->u.catch.prev_catch = l;
+  if (l)
+    l->u.catch.next_catch = c;
+  else
+    t->u.try.catch = c;
+  t->u.try.last_catch = c;
+
+  return c;
+}
+
+struct eh_region *
+gen_eh_region_allowed (struct eh_region *outer, tree allowed)
+{
+  struct eh_region *region = gen_eh_region (ERT_ALLOWED_EXCEPTIONS, outer);
+  region->u.allowed.type_list = allowed;
+
+  for (; allowed ; allowed = TREE_CHAIN (allowed))
+    add_type_for_runtime (TREE_VALUE (allowed));
+
+  return region;
+}
+
+struct eh_region *
+gen_eh_region_must_not_throw (struct eh_region *outer)
+{
+  return gen_eh_region (ERT_MUST_NOT_THROW, outer);
+}
+
+int
+get_eh_region_number (struct eh_region *region)
+{
+  return region->region_number;
+}
+
+bool
+get_eh_region_may_contain_throw (struct eh_region *region)
+{
+  return region->may_contain_throw;
+}
+
+tree
+get_eh_region_tree_label (struct eh_region *region)
+{
+  return region->tree_label;
+}
+
+void
+set_eh_region_tree_label (struct eh_region *region, tree lab)
+{
+  region->tree_label = lab;
+}
+\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.  */
@@ -468,33 +581,18 @@ init_eh_for_function (void)
 void
 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;
 
-  /* Insert a new blank region as a leaf in the tree.  */
-  new_region = ggc_alloc_cleared (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 (NOTE_INSN_EH_REGION_BEG);
-  NOTE_EH_HANDLER (note) = new_region->region_number;
+  NOTE_EH_HANDLER (note) = new->region_number;
 }
 
 /* Common code to end a region.  Returns the region just ended.  */
@@ -515,6 +613,36 @@ expand_eh_region_end (void)
   return cur_region;
 }
 
+/* Expand HANDLER, which is the operand 1 of a TRY_CATCH_EXPR.  Catch
+   blocks and C++ exception-specifications are handled specially.  */
+
+void
+expand_eh_handler (tree handler)
+{
+  tree inner = expr_first (handler);
+
+  switch (TREE_CODE (inner))
+    {
+    case CATCH_EXPR:
+      expand_start_all_catch ();
+      expand_expr (handler, const0_rtx, VOIDmode, 0);
+      expand_end_all_catch ();
+      break;
+
+    case EH_FILTER_EXPR:
+      if (EH_FILTER_MUST_NOT_THROW (handler))
+       expand_eh_region_end_must_not_throw (EH_FILTER_FAILURE (handler));
+      else
+       expand_eh_region_end_allowed (EH_FILTER_TYPES (handler),
+                                     EH_FILTER_FAILURE (handler));
+      break;
+
+    default:
+      expand_eh_region_end_cleanup (handler);
+      break;
+    }
+}
+
 /* End an exception handling region for a cleanup.  HANDLER is an
    expression to expand for the cleanup.  */
 
@@ -581,6 +709,16 @@ expand_eh_region_end_cleanup (tree handler)
   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.  */
 
@@ -612,46 +750,20 @@ expand_start_all_catch (void)
 void
 expand_start_catch (tree type_or_list)
 {
-  struct eh_region *t, *c, *l;
-  tree type_list;
+  struct eh_region *c;
+  rtx note;
 
   if (! doing_eh (0))
     return;
 
-  type_list = type_or_list;
-
-  if (type_or_list)
-    {
-      /* Ensure to always end up with a type list to normalize further
-         processing, then register each type against the runtime types
-         map.  */
-      tree type_node;
-
-      if (TREE_CODE (type_or_list) != TREE_LIST)
-       type_list = tree_cons (NULL_TREE, type_or_list, NULL_TREE);
+  c = gen_eh_region_catch (cfun->eh->try_region, type_or_list);
+  cfun->eh->cur_region = c;
 
-      type_node = type_list;
-      for (; type_node; type_node = TREE_CHAIN (type_node))
-       add_type_for_runtime (TREE_VALUE (type_node));
-    }
-
-  expand_eh_region_start ();
-
-  t = cfun->eh->try_region;
-  c = cfun->eh->cur_region;
-  c->type = ERT_CATCH;
-  c->u.catch.type_list = type_list;
   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.  */
@@ -659,15 +771,11 @@ expand_start_catch (tree type_or_list)
 void
 expand_end_catch (void)
 {
-  struct eh_region *try_region;
-
   if (! doing_eh (0))
     return;
 
   expand_eh_region_end ();
-  try_region = cfun->eh->try_region;
-
-  emit_jump (try_region->u.try.continue_label);
+  emit_jump (cfun->eh->try_region->u.try.continue_label);
 }
 
 /* End a sequence of catch handlers for a try block.  */
@@ -806,11 +914,8 @@ expand_eh_region_end_fixup (tree handler)
    call to a function which itself may contain a throw.  */
 
 void
-note_eh_region_may_contain_throw (void)
+note_eh_region_may_contain_throw (struct eh_region *region)
 {
-  struct eh_region *region;
-
-  region = cfun->eh->cur_region;
   while (region && !region->may_contain_throw)
     {
       region->may_contain_throw = 1;
@@ -818,6 +923,13 @@ note_eh_region_may_contain_throw (void)
     }
 }
 
+void
+note_current_region_may_contain_throw (void)
+{
+  note_eh_region_may_contain_throw (cfun->eh->cur_region);
+}
+
+
 /* Return an rtl expression for a pointer to the exception object
    within a handler.  */
 
@@ -836,7 +948,7 @@ get_exception_pointer (struct function *fun)
 /* Return an rtl expression for the exception dispatch filter
    within a handler.  */
 
-static rtx
+rtx
 get_exception_filter (struct function *fun)
 {
   rtx filter = fun->eh->filter;
@@ -854,7 +966,7 @@ get_exception_filter (struct function *fun)
    collect the regions this way as in expand_eh_region_start, but
    without having to realloc memory.  */
 
-static void
+void
 collect_eh_region_array (void)
 {
   struct eh_region **array, *i;
@@ -1039,41 +1151,53 @@ remove_unreachable_regions (rtx insns)
            abort ();
          uid_region_num[INSN_UID (r->label)] = i;
        }
-      if (r->type == ERT_TRY && r->u.try.continue_label)
-       {
-         if (uid_region_num[INSN_UID (r->u.try.continue_label)])
-           abort ();
-         uid_region_num[INSN_UID (r->u.try.continue_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;
-         }
-    }
+    reachable[uid_region_num[INSN_UID (insn)]] = 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])
        {
-         /* Don't remove ERT_THROW regions if their outer region
-            is reachable.  */
-         if (r->type == ERT_THROW
-             && r->outer
-             && reachable[r->outer->region_number])
-           continue;
+         bool kill_it = true;
+         switch (r->type)
+           {
+           case ERT_THROW:
+             /* Don't remove ERT_THROW regions if their outer region
+                is reachable.  */
+             if (r->outer && reachable[r->outer->region_number])
+               kill_it = false;
+             break;
+
+           case ERT_MUST_NOT_THROW:
+             /* MUST_NOT_THROW regions are implementable solely in the
+                runtime, but their existence continues to affect calls
+                within that region.  Never delete them here.  */
+             kill_it = false;
+             break;
+
+           case ERT_TRY:
+             {
+               /* TRY regions are reachable if any of its CATCH regions
+                  are reachable.  */
+               struct eh_region *c;
+               for (c = r->u.try.catch; c ; c = c->u.catch.next_catch)
+                 if (reachable[c->region_number])
+                   {
+                     kill_it = false;
+                     break;
+                   }
+               break;
+             }
 
-         remove_eh_handler (r);
+           default:
+             break;
+           }
+             
+         if (kill_it)
+           remove_eh_handler (r);
        }
     }
 
@@ -1121,8 +1245,6 @@ convert_from_eh_region_ranges_1 (rtx *pinsns, int *orig_sp, int cur)
              else
                cur = *--sp;
 
-             /* Removing the first insn of a CALL_PLACEHOLDER sequence
-                requires extra care to adjust sequence start.  */
              if (insn == *pinsns)
                *pinsns = next;
              remove_insn (insn);
@@ -1147,17 +1269,6 @@ convert_from_eh_region_ranges_1 (rtx *pinsns, int *orig_sp, int cur)
              REG_NOTES (insn) = alloc_EXPR_LIST (REG_EH_REGION, GEN_INT (cur),
                                                  REG_NOTES (insn));
            }
-
-         if (GET_CODE (insn) == CALL_INSN
-             && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
-           {
-             convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 0),
-                                              sp, cur);
-             convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 1),
-                                              sp, cur);
-             convert_from_eh_region_ranges_1 (&XEXP (PATTERN (insn), 2),
-                                              sp, cur);
-           }
        }
     }
 
@@ -1165,21 +1276,45 @@ convert_from_eh_region_ranges_1 (rtx *pinsns, int *orig_sp, int 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 (void)
 {
-  int *stack;
-  rtx insns;
+  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 ();
 
-  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);
 
-  stack = xmalloc (sizeof (int) * (cfun->eh->last_region_number + 1));
-  insns = get_insns ();
-  convert_from_eh_region_ranges_1 (&insns, stack, 0);
-  free (stack);
+      remove_fixup_regions ();
+    }
 
-  remove_fixup_regions ();
   remove_unreachable_regions (insns);
 }
 
@@ -1854,6 +1989,12 @@ connect_post_landing_pads (void)
        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);
     }
 }
 
@@ -1980,7 +2121,7 @@ sjlj_find_directly_reachable_regions (struct sjlj_lp_info *lp_info)
       rc = RNL_NOT_CAUGHT;
       for (; region; region = region->outer)
        {
-         rc = reachable_next_level (region, type_thrown, 0);
+         rc = reachable_next_level (region, type_thrown, NULL);
          if (rc != RNL_NOT_CAUGHT)
            break;
        }
@@ -2406,7 +2547,7 @@ finish_eh_generation (void)
            }
        }
       if (eh)
-       make_eh_edge (NULL, bb, BB_END (bb));
+       rtl_make_eh_edge (NULL, bb, BB_END (bb));
     }
   cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
 }
@@ -2598,21 +2739,37 @@ for_each_eh_label_1 (void **pentry, void *data)
   (*callback) (entry->label);
   return 1;
 }
+
+/* Invoke CALLBACK for every exception region in the current function.  */
+
+void
+for_each_eh_region (void (*callback) (struct eh_region *))
+{
+  int i, n = cfun->eh->last_region_number;
+  for (i = 1; i <= n; ++i)
+    {
+      struct eh_region *region = cfun->eh->region_array[i];
+      if (region)
+       (*callback) (region);
+    }
+}
 \f
 /* This section describes CFG exception edges for flow.  */
 
 /* For communicating between calls to reachable_next_level.  */
-struct reachable_info GTY(())
+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
+int
 check_handled (tree handled, tree type)
 {
   tree t;
@@ -2643,18 +2800,18 @@ check_handled (tree handled, tree type)
    LP_REGION contains the landing pad; REGION is the handler.  */
 
 static void
-add_reachable_handler (struct reachable_info *info, struct eh_region *lp_region, struct eh_region *region)
+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.
@@ -2803,7 +2960,7 @@ reachable_next_level (struct eh_region *region, tree type_thrown,
         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;
@@ -2821,40 +2978,30 @@ reachable_next_level (struct eh_region *region, tree type_thrown,
   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 (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;
-  int region_number;
-
-  if (GET_CODE (insn) == JUMP_INSN
-      && GET_CODE (PATTERN (insn)) == RESX)
-    region_number = XINT (PATTERN (insn), 0);
-  else
-    {
-      rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
-      if (!note || INTVAL (XEXP (note, 0)) <= 0)
-       return NULL;
-      region_number = INTVAL (XEXP (note, 0));
-    }
 
   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 (GET_CODE (insn) == JUMP_INSN
-      && GET_CODE (PATTERN (insn)) == RESX)
+  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 NULL;
+       return;
       region = region->outer;
     }
   else if (region->type == ERT_THROW)
@@ -2876,47 +3023,66 @@ reachable_handlers (rtx insn)
       else
        region = region->outer;
     }
-
-  return info.handlers;
 }
 
-/* Determine if the given INSN can throw an exception that is caught
-   within the function.  */
+/* Retrieve a list of labels of exception handlers which can be
+   reached by a given insn.  */
 
-bool
-can_throw_internal (rtx insn)
+static void
+arh_to_landing_pad (struct eh_region *region, void *data)
 {
-  struct eh_region *region;
-  tree type_thrown;
-  rtx note;
+  rtx *p_handlers = data;
+  if (! *p_handlers)
+    *p_handlers = alloc_INSN_LIST (region->landing_pad, NULL_RTX);
+}
 
-  if (! INSN_P (insn))
-    return false;
+static void
+arh_to_label (struct eh_region *region, void *data)
+{
+  rtx *p_handlers = data;
+  *p_handlers = alloc_INSN_LIST (region->label, *p_handlers);
+}
 
-  if (GET_CODE (insn) == INSN
-      && GET_CODE (PATTERN (insn)) == SEQUENCE)
-    insn = XVECEXP (PATTERN (insn), 0, 0);
+rtx
+reachable_handlers (rtx insn)
+{
+  bool is_resx = false;
+  rtx handlers = NULL;
+  int region_number;
 
-  if (GET_CODE (insn) == CALL_INSN
-      && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
+  if (GET_CODE (insn) == JUMP_INSN
+      && GET_CODE (PATTERN (insn)) == RESX)
     {
-      int i;
-      for (i = 0; i < 3; ++i)
-       {
-         rtx sub = XEXP (PATTERN (insn), i);
-         for (; sub ; sub = NEXT_INSN (sub))
-           if (can_throw_internal (sub))
-             return true;
-       }
-      return false;
+      region_number = XINT (PATTERN (insn), 0);
+      is_resx = true;
+    }
+  else
+    {
+      rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+      if (!note || INTVAL (XEXP (note, 0)) <= 0)
+       return NULL;
+      region_number = INTVAL (XEXP (note, 0));
     }
 
-  /* Every insn that might throw has an EH_REGION note.  */
-  note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
-  if (!note || INTVAL (XEXP (note, 0)) <= 0)
-    return false;
+  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.  */
 
-  region = cfun->eh->region_array[INTVAL (XEXP (note, 0))];
+bool
+can_throw_internal_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)
@@ -2940,14 +3106,61 @@ can_throw_internal (rtx insn)
   return false;
 }
 
+bool
+can_throw_internal (rtx insn)
+{
+  rtx note;
+
+  if (! INSN_P (insn))
+    return false;
+
+  if (GET_CODE (insn) == JUMP_INSN
+      && GET_CODE (PATTERN (insn)) == RESX
+      && XINT (PATTERN (insn), 0) > 0)
+    return can_throw_internal_1 (XINT (PATTERN (insn), 0));
+
+  if (GET_CODE (insn) == INSN
+      && GET_CODE (PATTERN (insn)) == SEQUENCE)
+    insn = XVECEXP (PATTERN (insn), 0, 0);
+
+  /* Every insn that might throw has an EH_REGION note.  */
+  note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+  if (!note || INTVAL (XEXP (note, 0)) <= 0)
+    return false;
+
+  return can_throw_internal_1 (INTVAL (XEXP (note, 0)));
+}
+
 /* Determine if the given INSN can throw an exception that is
    visible outside the function.  */
 
 bool
-can_throw_external (rtx insn)
+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)
+    {
+      type_thrown = region->u.throw.type;
+      region = region->outer;
+    }
+
+  /* If the exception is caught or blocked by any containing region,
+     then it is not seen by any calling function.  */
+  for (; region ; region = region->outer)
+    if (reachable_next_level (region, type_thrown, NULL) >= RNL_CAUGHT)
+      return false;
+
+  return true;
+}
+
+bool
+can_throw_external (rtx insn)
+{
   rtx note;
 
   if (! INSN_P (insn))
@@ -2957,20 +3170,6 @@ can_throw_external (rtx insn)
       && GET_CODE (PATTERN (insn)) == SEQUENCE)
     insn = XVECEXP (PATTERN (insn), 0, 0);
 
-  if (GET_CODE (insn) == CALL_INSN
-      && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER)
-    {
-      int i;
-      for (i = 0; i < 3; ++i)
-       {
-         rtx sub = XEXP (PATTERN (insn), i);
-         for (; sub ; sub = NEXT_INSN (sub))
-           if (can_throw_external (sub))
-             return true;
-       }
-      return false;
-    }
-
   note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
   if (!note)
     {
@@ -2986,22 +3185,7 @@ can_throw_external (rtx 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)));
 }
 
 /* Set current_function_nothrow and cfun->all_throwers_are_sibcalls.  */
@@ -3897,12 +4081,16 @@ output_function_exception_table (void)
          /* 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);
+             if (TREE_CODE (type) == VAR_DECL)
+               {
+                 node = cgraph_varpool_node (type);
+                 if (node)
+                   cgraph_varpool_mark_needed_node (node);
+               }
            }
          else if (TREE_CODE (type) != INTEGER_CST)
            abort ();