OSDN Git Service

* config/rs6000/rs6000.c (rs6000_va_arg): Replace SPLIT_COMPLEX_ARGS
[pf3gnuchains/gcc-fork.git] / gcc / except.c
index 97e0cb2..809f653 100644 (file)
@@ -1,6 +1,6 @@
 /* Implements exception handling.
    Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
    Contributed by Mike Stump <mrs@cygnus.com>.
 
 This file is part of GCC.
@@ -73,6 +73,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #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.  */
@@ -188,6 +189,7 @@ struct eh_region GTY(())
     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;
 
@@ -410,10 +412,8 @@ init_eh (void)
       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);
@@ -602,10 +602,12 @@ expand_start_all_catch (void)
   emit_jump (region->u.try.continue_label);
 }
 
-/* Begin a catch clause.  TYPE is the type caught, a list of such types, 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.  */
+/* 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 (tree type_or_list)
@@ -889,29 +891,49 @@ collect_eh_region_array (void)
 }
 
 static void
+resolve_one_fixup_region (struct eh_region *fixup)
+{
+  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, j, n = cfun->eh->last_region_number;
+  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 = 0;
 
-      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 && 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);
     }
 }
 
@@ -1026,7 +1048,18 @@ remove_unreachable_regions (rtx insns)
     }
 
   for (insn = insns; insn; insn = NEXT_INSN (insn))
-    reachable[uid_region_num[INSN_UID (insn)]] = true;
+    {
+      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)
     {
@@ -1391,7 +1424,7 @@ static hashval_t
 t2r_hash (const void *pentry)
 {
   tree entry = (tree) pentry;
-  return TYPE_HASH (TREE_PURPOSE (entry));
+  return TREE_HASH (TREE_PURPOSE (entry));
 }
 
 static void
@@ -1400,7 +1433,7 @@ 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);
@@ -1414,7 +1447,7 @@ 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 inserted the data earlier.  */
   return TREE_VALUE (*slot);
@@ -1445,7 +1478,7 @@ static hashval_t
 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
@@ -1472,12 +1505,12 @@ ehspec_filter_hash (const void *pentry)
   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 (htab_t ttypes_hash, tree type)
@@ -1485,7 +1518,7 @@ 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)
     {
@@ -1609,6 +1642,31 @@ assign_filter_values (void)
   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.  */
 
@@ -1682,7 +1740,8 @@ build_post_landing_pads (void)
          seq = get_insns ();
          end_sequence ();
 
-         emit_insn_before (seq, region->u.try.catch->label);
+         emit_to_new_bb_before (seq, region->u.try.catch->label);
+
          break;
 
        case ERT_ALLOWED_EXCEPTIONS:
@@ -1706,7 +1765,7 @@ build_post_landing_pads (void)
          seq = get_insns ();
          end_sequence ();
 
-         emit_insn_before (seq, region->label);
+         emit_to_new_bb_before (seq, region->label);
          break;
 
        case ERT_CLEANUP:
@@ -1738,6 +1797,7 @@ connect_post_landing_pads (void)
       struct eh_region *region = cfun->eh->region_array[i];
       struct eh_region *outer;
       rtx seq;
+      rtx barrier;
 
       /* Mind we don't process a region more than once.  */
       if (!region || region->region_number != i)
@@ -1756,14 +1816,30 @@ connect_post_landing_pads (void)
       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, ptr_mode);
 
       seq = get_insns ();
       end_sequence ();
-      emit_insn_before (seq, region->resume);
+      barrier = emit_insn_before (seq, region->resume);
+      /* Avoid duplicate barrier.  */
+      if (GET_CODE (barrier) != BARRIER)
+       abort ();
+      delete_insn (barrier);
       delete_insn (region->resume);
     }
 }
@@ -1779,7 +1855,9 @@ dw2_build_landing_pads (void)
     {
       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)
@@ -1839,7 +1917,10 @@ dw2_build_landing_pads (void)
       seq = get_insns ();
       end_sequence ();
 
-      emit_insn_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;
     }
 }
 
@@ -2046,8 +2127,12 @@ sjlj_emit_function_enter (rtx dispatch_label)
   if (cfun->uses_eh_lsda)
     {
       char buf[20];
+      rtx sym;
+
       ASM_GENERATE_INTERNAL_LABEL (buf, "LLSDA", current_function_funcdef_no);
-      emit_move_insn (mem, gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)));
+      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);
@@ -2083,9 +2168,21 @@ sjlj_emit_function_enter (rtx 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_insn_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
@@ -2101,6 +2198,7 @@ static void
 sjlj_emit_function_exit (void)
 {
   rtx seq;
+  edge e;
 
   start_sequence ();
 
@@ -2114,7 +2212,31 @@ sjlj_emit_function_exit (void)
      post-dominates all can_throw_internal instructions.  This is
      the last possible moment.  */
 
-  emit_insn_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
@@ -2122,6 +2244,9 @@ 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;
 
@@ -2176,8 +2301,12 @@ sjlj_emit_dispatch_table (rtx dispatch_label, struct sjlj_lp_info *lp_info)
   seq = get_insns ();
   end_sequence ();
 
-  emit_insn_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
@@ -2211,6 +2340,8 @@ sjlj_build_landing_pads (void)
 void
 finish_eh_generation (void)
 {
+  basic_block bb;
+
   /* Nothing to do if no regions created.  */
   if (cfun->eh->region_tree == NULL)
     return;
@@ -2245,8 +2376,25 @@ finish_eh_generation (void)
 
   /* We've totally changed the CFG.  Start over.  */
   find_exception_handler_labels ();
-  rebuild_jump_labels (get_insns ());
-  find_basic_blocks (get_insns (), max_reg_num (), 0);
+  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)
+       make_eh_edge (NULL, bb, BB_END (bb));
+    }
   cleanup_cfg (CLEANUP_PRE_LOOP | CLEANUP_NO_INSN_DEL);
 }
 \f
@@ -2425,14 +2573,14 @@ void
 for_each_eh_label (void (*callback) (rtx))
 {
   htab_traverse (cfun->eh->exception_handler_label_map, for_each_eh_label_1,
-                (void *)callback);
+                (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;
+  void (*callback) (rtx) = *(void (**) (rtx)) data;
 
   (*callback) (entry->label);
   return 1;
@@ -2973,10 +3121,7 @@ expand_builtin_frob_return_addr (tree addr_tree)
 {
   rtx addr = expand_expr (addr_tree, NULL_RTX, ptr_mode, 0);
 
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (addr) != Pmode)
-    addr = convert_memory_address (Pmode, addr);
-#endif
+  addr = convert_memory_address (Pmode, addr);
 
 #ifdef RETURN_ADDR_OFFSET
   addr = force_reg (Pmode, addr);
@@ -2997,10 +3142,7 @@ expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED,
 
 #ifdef EH_RETURN_STACKADJ_RTX
   tmp = expand_expr (stackadj_tree, cfun->eh->ehr_stackadj, VOIDmode, 0);
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (tmp) != Pmode)
-    tmp = convert_memory_address (Pmode, tmp);
-#endif
+  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)
@@ -3008,10 +3150,7 @@ expand_builtin_eh_return (tree stackadj_tree ATTRIBUTE_UNUSED,
 #endif
 
   tmp = expand_expr (handler_tree, cfun->eh->ehr_handler, VOIDmode, 0);
-#ifdef POINTERS_EXTEND_UNSIGNED
-  if (GET_MODE (tmp) != Pmode)
-    tmp = convert_memory_address (Pmode, tmp);
-#endif
+  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)
@@ -3061,6 +3200,26 @@ expand_eh_return (void)
 
   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:
@@ -3704,11 +3863,28 @@ output_function_exception_table (void)
       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);
+
+         /* 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.  */
+         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 ();
+       }
 
-      value = expand_expr (type, NULL_RTX, VOIDmode, EXPAND_INITIALIZER);
       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);