OSDN Git Service

2005-06-03 Richard Guenther <rguenth@gcc.gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / except.c
index 56bc2d7..c10fc88 100644 (file)
@@ -1,6 +1,6 @@
 /* Implements exception handling.
    Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
    Contributed by Mike Stump <mrs@cygnus.com>.
 
 This file is part of GCC.
@@ -74,6 +74,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "target.h"
 #include "langhooks.h"
 #include "cgraph.h"
+#include "diagnostic.h"
 
 /* Provide defaults for stuff that may not be defined when using
    sjlj exceptions.  */
@@ -138,8 +139,7 @@ struct eh_region GTY(())
     ERT_CATCH,
     ERT_ALLOWED_EXCEPTIONS,
     ERT_MUST_NOT_THROW,
-    ERT_THROW,
-    ERT_FIXUP
+    ERT_THROW
   } type;
 
   /* Holds the action to perform based on the preceding type.  */
@@ -149,8 +149,6 @@ struct eh_region GTY(())
     struct eh_region_u_try {
       struct eh_region *catch;
       struct eh_region *last_catch;
-      struct eh_region *prev_try;
-      rtx continue_label;
     } GTY ((tag ("ERT_TRY"))) try;
 
     /* The list through the catch handlers, the list of type objects
@@ -177,17 +175,8 @@ struct eh_region GTY(())
     /* Retain the cleanup expression even after expansion so that
        we can match up fixup regions.  */
     struct eh_region_u_cleanup {
-      tree exp;
       struct eh_region *prev_try;
     } GTY ((tag ("ERT_CLEANUP"))) cleanup;
-
-    /* The real region (by expression and by pointer) that fixup code
-       should live in.  */
-    struct eh_region_u_fixup {
-      tree cleanup_exp;
-      struct eh_region *real_region;
-      bool resolved;
-    } GTY ((tag ("ERT_FIXUP"))) fixup;
   } GTY ((desc ("%0.type"))) u;
 
   /* Entry point for this region's handler before landing pads are built.  */
@@ -235,7 +224,7 @@ struct eh_status GTY(())
   int built_landing_pads;
   int last_region_number;
 
-  varray_type ttype_data;
+  VEC(tree,gc) *ttype_data;
   varray_type ehspec_data;
   varray_type action_record_data;
 
@@ -252,6 +241,8 @@ struct eh_status GTY(())
 
   rtx sjlj_fc;
   rtx sjlj_exit_after;
+
+  htab_t GTY((param_is (struct throw_stmt_node))) throw_stmt_table;
 };
 
 \f
@@ -852,6 +843,148 @@ current_function_has_exception_handlers (void)
   return false;
 }
 \f
+static struct eh_region *
+duplicate_eh_region_1 (struct eh_region *o)
+{
+  struct eh_region *n = ggc_alloc_cleared (sizeof (struct eh_region));
+
+  *n = *o;
+  
+  n->region_number = o->region_number + cfun->eh->last_region_number;
+  gcc_assert (!o->aka);
+  
+  return n;
+}
+
+static void
+duplicate_eh_region_2 (struct eh_region *o, struct eh_region **n_array,
+                      struct eh_region *prev_try)
+{
+  struct eh_region *n = n_array[o->region_number];
+  
+  switch (n->type)
+    {
+    case ERT_TRY:
+      if (o->u.try.catch)
+        n->u.try.catch = n_array[o->u.try.catch->region_number];
+      if (o->u.try.last_catch)
+        n->u.try.last_catch = n_array[o->u.try.last_catch->region_number];
+      break;
+      
+    case ERT_CATCH:
+      if (o->u.catch.next_catch)
+       n->u.catch.next_catch = n_array[o->u.catch.next_catch->region_number];
+      if (o->u.catch.prev_catch)
+       n->u.catch.prev_catch = n_array[o->u.catch.prev_catch->region_number];
+      break;
+
+    case ERT_CLEANUP:
+      if (o->u.cleanup.prev_try)
+       n->u.cleanup.prev_try = n_array[o->u.cleanup.prev_try->region_number];
+      else
+        n->u.cleanup.prev_try = prev_try;
+      break;
+      
+    default:
+      break;
+    }
+  
+  if (o->outer)
+    n->outer = n_array[o->outer->region_number];
+  if (o->inner)
+    n->inner = n_array[o->inner->region_number];
+  if (o->next_peer)
+    n->next_peer = n_array[o->next_peer->region_number];
+}
+
+/* Duplicate the EH regions of IFUN into current function, root the tree in
+   OUTER_REGION and remap labels using MAP callback.  */
+int
+duplicate_eh_regions (struct function *ifun, duplicate_eh_regions_map map,
+                     void *data, int outer_region)
+{
+  int ifun_last_region_number = ifun->eh->last_region_number;
+  struct eh_region **n_array, *root, *cur, *prev_try;
+  int i;
+  
+  if (ifun_last_region_number == 0 || !ifun->eh->region_tree)
+    return 0;
+  
+  n_array = xcalloc (ifun_last_region_number + 1, sizeof (*n_array));
+  
+  /* Search for the containing ERT_TRY region to fix up
+     the prev_try short-cuts for ERT_CLEANUP regions.  */
+  prev_try = NULL;
+  if (outer_region > 0)
+    for (prev_try = cfun->eh->region_array[outer_region];
+         prev_try && prev_try->type != ERT_TRY;
+        prev_try = prev_try->outer)
+      ;
+
+  for (i = 1; i <= ifun_last_region_number; ++i)
+    {
+      cur = ifun->eh->region_array[i];
+      if (!cur || cur->region_number != i)
+       continue;
+      n_array[i] = duplicate_eh_region_1 (cur);
+      if (cur->tree_label)
+       {
+         tree newlabel = map (cur->tree_label, data);
+         n_array[i]->tree_label = newlabel;
+       }
+      else
+       n_array[i]->tree_label = NULL;
+    }
+  for (i = 1; i <= ifun_last_region_number; ++i)
+    {
+      cur = ifun->eh->region_array[i];
+      if (!cur || cur->region_number != i)
+       continue;
+      duplicate_eh_region_2 (cur, n_array, prev_try);
+    }
+  
+  root = n_array[ifun->eh->region_tree->region_number];
+  gcc_assert (root->outer == NULL);
+  if (outer_region > 0)
+    {
+      struct eh_region *cur = cfun->eh->region_array[outer_region];
+      struct eh_region *p = cur->inner;
+
+      if (p)
+       {
+         while (p->next_peer)
+           p = p->next_peer;
+         p->next_peer = root;
+       }
+      else
+        cur->inner = root;
+      for (i = 1; i <= ifun_last_region_number; ++i)
+       if (n_array[i] && n_array[i]->outer == NULL)
+         n_array[i]->outer = cur;
+    }
+  else
+    {
+      struct eh_region *p = cfun->eh->region_tree;
+      if (p)
+       {
+         while (p->next_peer)
+           p = p->next_peer;
+         p->next_peer = root;
+       }
+      else
+        cfun->eh->region_tree = root;
+    }
+  
+  free (n_array);
+  
+  i = cfun->eh->last_region_number;
+  cfun->eh->last_region_number = i + ifun_last_region_number;
+  
+  collect_eh_region_array ();
+  
+  return i;
+}
+\f
 static int
 t2r_eq (const void *pentry, const void *pdata)
 {
@@ -967,10 +1100,10 @@ add_ttypes_entry (htab_t ttypes_hash, tree type)
 
       n = xmalloc (sizeof (*n));
       n->t = type;
-      n->filter = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) + 1;
+      n->filter = VEC_length (tree, cfun->eh->ttype_data) + 1;
       *slot = n;
 
-      VARRAY_PUSH_TREE (cfun->eh->ttype_data, type);
+      VEC_safe_push (tree, gc, cfun->eh->ttype_data, type);
     }
 
   return n->filter;
@@ -1020,7 +1153,7 @@ assign_filter_values (void)
   int i;
   htab_t ttypes, ehspec;
 
-  VARRAY_TREE_INIT (cfun->eh->ttype_data, 16, "ttype_data");
+  cfun->eh->ttype_data = VEC_alloc (tree, gc, 16);
   VARRAY_UCHAR_INIT (cfun->eh->ehspec_data, 64, "ehspec_data");
 
   ttypes = htab_create (31, ttypes_filter_hash, ttypes_filter_eq, free);
@@ -1637,10 +1770,10 @@ sjlj_emit_function_enter (rtx dispatch_label)
            || NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_BASIC_BLOCK))
       break;
   if (NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
-    insert_insn_on_edge (seq, EDGE_SUCC (ENTRY_BLOCK_PTR, 0));
+    insert_insn_on_edge (seq, single_succ_edge (ENTRY_BLOCK_PTR));
   else
     {
-      rtx last = BB_END (EDGE_SUCC (ENTRY_BLOCK_PTR, 0)->dest);
+      rtx last = BB_END (single_succ (ENTRY_BLOCK_PTR));
       for (; ; fn_begin = NEXT_INSN (fn_begin))
        if ((NOTE_P (fn_begin)
             && NOTE_LINE_NUMBER (fn_begin) == NOTE_INSN_FUNCTION_BEG)
@@ -2082,7 +2215,7 @@ struct reachable_info
 /* A subroutine of reachable_next_level.  Return true if TYPE, or a
    base class of TYPE, is in HANDLED.  */
 
-int
+static int
 check_handled (tree handled, tree type)
 {
   tree t;
@@ -2272,8 +2405,13 @@ reachable_next_level (struct eh_region *region, tree type_thrown,
       /* Here we end our search, since no exceptions may propagate.
         If we've touched down at some landing pad previous, then the
         explicit function call we generated may be used.  Otherwise
-        the call is made by the runtime.  */
-      if (info && info->saw_any_handlers)
+        the call is made by the runtime. 
+
+         Before inlining, do not perform this optimization.  We may
+        inline a subroutine that contains handlers, and that will
+        change the value of saw_any_handlers.  */
+
+      if ((info && info->saw_any_handlers) || !cfun->after_inlining)
        {
          add_reachable_handler (info, region, region);
          return RNL_CAUGHT;
@@ -2282,7 +2420,6 @@ reachable_next_level (struct eh_region *region, tree type_thrown,
        return RNL_BLOCKED;
 
     case ERT_THROW:
-    case ERT_FIXUP:
     case ERT_UNKNOWN:
       /* Shouldn't see these here.  */
       gcc_unreachable ();
@@ -2391,7 +2528,7 @@ reachable_handlers (rtx insn)
    within the function.  */
 
 bool
-can_throw_internal_1 (int region_number)
+can_throw_internal_1 (int region_number, bool is_resx)
 {
   struct eh_region *region;
   tree type_thrown;
@@ -2399,7 +2536,9 @@ can_throw_internal_1 (int region_number)
   region = cfun->eh->region_array[region_number];
 
   type_thrown = NULL_TREE;
-  if (region->type == ERT_THROW)
+  if (is_resx)
+    region = region->outer;
+  else if (region->type == ERT_THROW)
     {
       type_thrown = region->u.throw.type;
       region = region->outer;
@@ -2431,7 +2570,7 @@ can_throw_internal (rtx insn)
   if (JUMP_P (insn)
       && GET_CODE (PATTERN (insn)) == RESX
       && XINT (PATTERN (insn), 0) > 0)
-    return can_throw_internal_1 (XINT (PATTERN (insn), 0));
+    return can_throw_internal_1 (XINT (PATTERN (insn), 0), true);
 
   if (NONJUMP_INSN_P (insn)
       && GET_CODE (PATTERN (insn)) == SEQUENCE)
@@ -2442,14 +2581,14 @@ can_throw_internal (rtx insn)
   if (!note || INTVAL (XEXP (note, 0)) <= 0)
     return false;
 
-  return can_throw_internal_1 (INTVAL (XEXP (note, 0)));
+  return can_throw_internal_1 (INTVAL (XEXP (note, 0)), false);
 }
 
 /* Determine if the given INSN can throw an exception that is
    visible outside the function.  */
 
 bool
-can_throw_external_1 (int region_number)
+can_throw_external_1 (int region_number, bool is_resx)
 {
   struct eh_region *region;
   tree type_thrown;
@@ -2457,7 +2596,9 @@ can_throw_external_1 (int region_number)
   region = cfun->eh->region_array[region_number];
 
   type_thrown = NULL_TREE;
-  if (region->type == ERT_THROW)
+  if (is_resx)
+    region = region->outer;
+  else if (region->type == ERT_THROW)
     {
       type_thrown = region->u.throw.type;
       region = region->outer;
@@ -2480,6 +2621,11 @@ can_throw_external (rtx insn)
   if (! INSN_P (insn))
     return false;
 
+  if (JUMP_P (insn)
+      && GET_CODE (PATTERN (insn)) == RESX
+      && XINT (PATTERN (insn), 0) > 0)
+    return can_throw_external_1 (XINT (PATTERN (insn), 0), true);
+
   if (NONJUMP_INSN_P (insn)
       && GET_CODE (PATTERN (insn)) == SEQUENCE)
     insn = XVECEXP (PATTERN (insn), 0, 0);
@@ -2499,7 +2645,7 @@ can_throw_external (rtx insn)
   if (INTVAL (XEXP (note, 0)) <= 0)
     return false;
 
-  return can_throw_external_1 (INTVAL (XEXP (note, 0)));
+  return can_throw_external_1 (INTVAL (XEXP (note, 0)), false);
 }
 
 /* Set TREE_NOTHROW and cfun->all_throwers_are_sibcalls.  */
@@ -3248,6 +3394,7 @@ output_function_exception_table (void)
 
 #ifdef TARGET_UNWIND_INFO
   /* TODO: Move this into target file.  */
+  assemble_external_libcall (eh_personality_libfunc);
   fputs ("\t.personality\t", asm_out_file);
   output_addr_const (asm_out_file, eh_personality_libfunc);
   fputs ("\n\t.handlerdata\n", asm_out_file);
@@ -3257,7 +3404,7 @@ output_function_exception_table (void)
   targetm.asm_out.exception_section ();
 #endif
 
-  have_tt_data = (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data) > 0
+  have_tt_data = (VEC_length (tree, cfun->eh->ttype_data) > 0
                  || VARRAY_ACTIVE_SIZE (cfun->eh->ehspec_data) > 0);
 
   /* Indicate the format of the @TType entries.  */
@@ -3320,7 +3467,7 @@ output_function_exception_table (void)
       after_disp = (1 + size_of_uleb128 (call_site_len)
                    + call_site_len
                    + VARRAY_ACTIVE_SIZE (cfun->eh->action_record_data)
-                   + (VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data)
+                   + (VEC_length (tree, cfun->eh->ttype_data)
                       * tt_format_size));
 
       disp = after_disp;
@@ -3382,10 +3529,10 @@ output_function_exception_table (void)
   if (have_tt_data)
     assemble_align (tt_format_size * BITS_PER_UNIT);
 
-  i = VARRAY_ACTIVE_SIZE (cfun->eh->ttype_data);
+  i = VEC_length (tree, cfun->eh->ttype_data);
   while (i-- > 0)
     {
-      tree type = VARRAY_TREE (cfun->eh->ttype_data, i);
+      tree type = VEC_index (tree, cfun->eh->ttype_data, i);
       rtx value;
 
       if (type == NULL_TREE)
@@ -3433,7 +3580,151 @@ output_function_exception_table (void)
     dw2_asm_output_data (1, VARRAY_UCHAR (cfun->eh->ehspec_data, i),
                         (i ? NULL : "Exception specification table"));
 
-  function_section (current_function_decl);
+  current_function_section (current_function_decl);
+}
+
+void
+set_eh_throw_stmt_table (struct function *fun, struct htab *table)
+{
+  fun->eh->throw_stmt_table = table;
+}
+
+htab_t
+get_eh_throw_stmt_table (struct function *fun)
+{
+  return fun->eh->throw_stmt_table;
+}
+
+/* Dump EH information to OUT.  */
+void 
+dump_eh_tree (FILE *out, struct function *fun)
+{
+  struct eh_region *i;
+  int depth = 0;
+  static const char * const type_name[] = {"unknown", "cleanup", "try", "catch",
+                                          "allowed_exceptions", "must_not_throw",
+                                          "throw"};
+
+  i = fun->eh->region_tree;
+  if (! i)
+    return;
+
+  fprintf (out, "Eh tree:\n");
+  while (1)
+    {
+      fprintf (out, "  %*s %i %s", depth * 2, "",
+              i->region_number, type_name [(int)i->type]);
+      if (i->tree_label)
+       {
+          fprintf (out, " tree_label:");
+         print_generic_expr (out, i->tree_label, 0);
+       }
+      fprintf (out, "\n");
+      /* If there are sub-regions, process them.  */
+      if (i->inner)
+       i = i->inner, depth++;
+      /* If there are peers, process them.  */
+      else if (i->next_peer)
+       i = i->next_peer;
+      /* Otherwise, step back up the tree to the next peer.  */
+      else
+       {
+         do {
+           i = i->outer;
+           depth--;
+           if (i == NULL)
+             return;
+         } while (i->next_peer == NULL);
+         i = i->next_peer;
+       }
+    }
 }
 
+/* Verify some basic invariants on EH datastructures.  Could be extended to
+   catch more.  */
+void 
+verify_eh_tree (struct function *fun)
+{
+  struct eh_region *i, *outer = NULL;
+  bool err = false;
+  int nvisited = 0;
+  int count = 0;
+  int j;
+  int depth = 0;
+
+  i = fun->eh->region_tree;
+  if (! i)
+    return;
+  for (j = fun->eh->last_region_number; j > 0; --j)
+    if (fun->eh->region_array[j])
+      {
+       count++;
+       if (fun->eh->region_array[j]->region_number != j)
+         {
+           error ("region_array is corrupted for region %i", i->region_number);
+           err = true;
+         }
+      }
+
+  while (1)
+    {
+      if (fun->eh->region_array[i->region_number] != i)
+       {
+         error ("region_array is corrupted for region %i", i->region_number);
+         err = true;
+       }
+      if (i->outer != outer)
+       {
+         error ("outer block of region %i is wrong", i->region_number);
+         err = true;
+       }
+      if (i->may_contain_throw && outer && !outer->may_contain_throw)
+       {
+         error ("region %i may contain throw and is contained in region that may not",
+                i->region_number);
+         err = true;
+       }
+      if (depth < 0)
+       {
+         error ("negative nesting depth of region %i", i->region_number);
+         err = true;
+       }
+      nvisited ++;
+      /* If there are sub-regions, process them.  */
+      if (i->inner)
+       outer = i, i = i->inner, depth++;
+      /* If there are peers, process them.  */
+      else if (i->next_peer)
+       i = i->next_peer;
+      /* Otherwise, step back up the tree to the next peer.  */
+      else
+       {
+         do {
+           i = i->outer;
+           depth--;
+           if (i == NULL)
+             {
+               if (depth != -1)
+                 {
+                   error ("Tree list ends on depth %i", depth + 1);
+                   err = true;
+                 }
+               if (count != nvisited)
+                 {
+                   error ("array does not match the region tree");
+                   err = true;
+                 }
+               if (err)
+                 {
+                   dump_eh_tree (stderr, fun);
+                   internal_error ("verify_eh_tree failed.");
+                 }
+               return;
+             }
+           outer = i->outer;
+         } while (i->next_peer == NULL);
+         i = i->next_peer;
+       }
+    }
+}
 #include "gt-except.h"