OSDN Git Service

PR middle-end/41674
[pf3gnuchains/gcc-fork.git] / gcc / tree-eh.c
index 21da534..e5d76c8 100644 (file)
@@ -1,5 +1,5 @@
 /* Exception handling semantics and decomposition for trees.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -23,11 +23,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
-#include "rtl.h"
-#include "tm_p.h"
 #include "flags.h"
 #include "function.h"
 #include "except.h"
+#include "pointer-set.h"
 #include "tree-flow.h"
 #include "tree-dump.h"
 #include "tree-inline.h"
@@ -563,6 +562,7 @@ replace_goto_queue (struct leh_tf_state *tf)
   if (tf->goto_queue_active == 0)
     return;
   replace_goto_queue_stmt_list (tf->top_p_seq, tf);
+  replace_goto_queue_stmt_list (eh_seq, tf);
 }
 
 /* Add a new record to the goto queue contained in TF. NEW_STMT is the
@@ -643,7 +643,6 @@ record_in_goto_queue_label (struct leh_tf_state *tf, treemple stmt, tree label)
      labels. */
   new_stmt = stmt;
   record_in_goto_queue (tf, new_stmt, index, true);
-
 }
 
 /* For any GIMPLE_GOTO or GIMPLE_RETURN, decide whether it leaves a try_finally
@@ -1518,6 +1517,20 @@ decide_copy_try_finally (int ndests, gimple_seq finally)
     return f_estimate < 40 || f_estimate * 2 < sw_estimate * 3;
 }
 
+/* REG is the enclosing region for a possible cleanup region, or the region
+   itself.  Returns TRUE if such a region would be unreachable.
+
+   Cleanup regions within a must-not-throw region aren't actually reachable
+   even if there are throwing stmts within them, because the personality
+   routine will call terminate before unwinding.  */
+
+static bool
+cleanup_is_dead_in (eh_region reg)
+{
+  while (reg && reg->type == ERT_CLEANUP)
+    reg = reg->outer;
+  return (reg && reg->type == ERT_MUST_NOT_THROW);
+}
 
 /* A subroutine of lower_eh_constructs_1.  Lower a GIMPLE_TRY_FINALLY nodes
    to a sequence of labels and blocks, plus the exception region trees
@@ -1530,6 +1543,7 @@ lower_try_finally (struct leh_state *state, gimple tp)
   struct leh_tf_state this_tf;
   struct leh_state this_state;
   int ndests;
+  gimple_seq old_eh_seq;
 
   /* Process the try block.  */
 
@@ -1537,22 +1551,30 @@ lower_try_finally (struct leh_state *state, gimple tp)
   this_tf.try_finally_expr = tp;
   this_tf.top_p = tp;
   this_tf.outer = state;
-  if (using_eh_for_cleanups_p)
-    this_tf.region = gen_eh_region_cleanup (state->cur_region);
+  if (using_eh_for_cleanups_p && !cleanup_is_dead_in (state->cur_region))
+    {
+      this_tf.region = gen_eh_region_cleanup (state->cur_region);
+      this_state.cur_region = this_tf.region;
+    }
   else
-    this_tf.region = NULL;
+    {
+      this_tf.region = NULL;
+      this_state.cur_region = state->cur_region;
+    }
 
-  this_state.cur_region = this_tf.region;
   this_state.ehp_region = state->ehp_region;
   this_state.tf = &this_tf;
 
+  old_eh_seq = eh_seq;
+  eh_seq = NULL;
+
   lower_eh_constructs_1 (&this_state, gimple_try_eval(tp));
 
   /* Determine if the try block is escaped through the bottom.  */
   this_tf.may_fallthru = gimple_seq_may_fallthru (gimple_try_eval (tp));
 
   /* Determine if any exceptions are possible within the try block.  */
-  if (using_eh_for_cleanups_p)
+  if (this_tf.region)
     this_tf.may_throw = eh_region_may_contain_throw (this_tf.region);
   if (this_tf.may_throw)
     honor_protect_cleanup_actions (state, &this_state, &this_tf);
@@ -1601,6 +1623,20 @@ lower_try_finally (struct leh_state *state, gimple tp)
   if (this_tf.goto_queue_map)
     pointer_map_destroy (this_tf.goto_queue_map);
 
+  /* If there was an old (aka outer) eh_seq, append the current eh_seq.
+     If there was no old eh_seq, then the append is trivially already done.  */
+  if (old_eh_seq)
+    {
+      if (eh_seq == NULL)
+       eh_seq = old_eh_seq;
+      else
+       {
+         gimple_seq new_eh_seq = eh_seq;
+         eh_seq = old_eh_seq;
+         gimple_seq_add_seq(&eh_seq, new_eh_seq);
+       }
+    }
+
   return this_tf.top_p_seq;
 }
 
@@ -1666,6 +1702,8 @@ lower_catch (struct leh_state *state, gimple tp)
          x = gimple_build_goto (out_label);
          gimple_seq_add_stmt (&new_seq, x);
        }
+      if (!c->type_list)
+       break;
     }
 
   gimple_try_set_cleanup (tp, new_seq);
@@ -1760,8 +1798,9 @@ lower_cleanup (struct leh_state *state, gimple tp)
   eh_region this_region = NULL;
   struct leh_tf_state fake_tf;
   gimple_seq result;
+  bool cleanup_dead = cleanup_is_dead_in (state->cur_region);
 
-  if (flag_exceptions)
+  if (flag_exceptions && !cleanup_dead)
     {
       this_region = gen_eh_region_cleanup (state->cur_region);
       this_state.cur_region = this_region;
@@ -1769,7 +1808,7 @@ lower_cleanup (struct leh_state *state, gimple tp)
 
   lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
 
-  if (!eh_region_may_contain_throw (this_region))
+  if (cleanup_dead || !eh_region_may_contain_throw (this_region))
     return gimple_try_eval (tp);
 
   /* Build enough of a try-finally state so that we can reuse
@@ -1868,9 +1907,12 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
     case GIMPLE_ASSIGN:
       /* If the stmt can throw use a new temporary for the assignment
          to a LHS.  This makes sure the old value of the LHS is
-        available on the EH edge.  */
+        available on the EH edge.  Only do so for statements that
+        potentially fall thru (no noreturn calls e.g.), otherwise
+        this new assignment might create fake fallthru regions.  */
       if (stmt_could_throw_p (stmt)
          && gimple_has_lhs (stmt)
+         && gimple_stmt_may_fallthru (stmt)
          && !tree_could_throw_p (gimple_get_lhs (stmt))
          && is_gimple_reg_type (TREE_TYPE (gimple_get_lhs (stmt))))
        {
@@ -3033,9 +3075,10 @@ struct gimple_opt_pass pass_lower_resx =
 };
 
 
-/* At the end of inlining, we can lower EH_DISPATCH.  */
+/* At the end of inlining, we can lower EH_DISPATCH.  Return true when 
+   we have found some duplicate labels and removed some edges.  */
 
-static void
+static bool
 lower_eh_dispatch (basic_block src, gimple stmt)
 {
   gimple_stmt_iterator gsi;
@@ -3043,6 +3086,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
   eh_region r;
   tree filter, fn;
   gimple x;
+  bool redirected = false;
 
   region_nr = gimple_eh_dispatch_region (stmt);
   r = get_eh_region_from_number (region_nr);
@@ -3058,6 +3102,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
        eh_catch c;
        edge_iterator ei;
        edge e;
+       struct pointer_set_t *seen_values = pointer_set_create ();
 
        /* Collect the labels for a switch.  Zero the post_landing_pad
           field becase we'll no longer have anything keeping these labels
@@ -3066,6 +3111,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
        for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
          {
            tree tp_node, flt_node, lab = c->label;
+           bool have_label = false;
 
            c->label = NULL;
            tp_node = c->type_list;
@@ -3078,14 +3124,29 @@ lower_eh_dispatch (basic_block src, gimple stmt)
              }
            do
              {
-               tree t = build3 (CASE_LABEL_EXPR, void_type_node,
-                                TREE_VALUE (flt_node), NULL, lab);
-               VEC_safe_push (tree, heap, labels, t);
+               /* Filter out duplicate labels that arise when this handler 
+                  is shadowed by an earlier one.  When no labels are 
+                  attached to the handler anymore, we remove 
+                  the corresponding edge and then we delete unreachable 
+                  blocks at the end of this pass.  */
+               if (! pointer_set_contains (seen_values, TREE_VALUE (flt_node)))
+                 {
+                   tree t = build3 (CASE_LABEL_EXPR, void_type_node,
+                                    TREE_VALUE (flt_node), NULL, lab);
+                   VEC_safe_push (tree, heap, labels, t);
+                   pointer_set_insert (seen_values, TREE_VALUE (flt_node));
+                   have_label = true;
+                 }
 
                tp_node = TREE_CHAIN (tp_node);
                flt_node = TREE_CHAIN (flt_node);
              }
            while (tp_node);
+           if (! have_label)
+             {
+               remove_edge (find_edge (src, label_to_block (lab)));
+               redirected = true;
+             }
          }
 
        /* Clean up the edge flags.  */
@@ -3127,6 +3188,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
 
            VEC_free (tree, heap, labels);
          }
+       pointer_set_destroy (seen_values);
       }
       break;
 
@@ -3160,6 +3222,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
 
   /* Replace the EH_DISPATCH with the SWITCH or COND generated above.  */
   gsi_remove (&gsi, true);
+  return redirected;
 }
 
 static unsigned
@@ -3167,6 +3230,7 @@ execute_lower_eh_dispatch (void)
 {
   basic_block bb;
   bool any_rewritten = false;
+  bool redirected = false;
 
   assign_filter_values ();
 
@@ -3175,11 +3239,13 @@ execute_lower_eh_dispatch (void)
       gimple last = last_stmt (bb);
       if (last && gimple_code (last) == GIMPLE_EH_DISPATCH)
        {
-         lower_eh_dispatch (bb, last);
+         redirected |= lower_eh_dispatch (bb, last);
          any_rewritten = true;
        }
     }
 
+  if (redirected)
+    delete_unreachable_blocks ();
   return any_rewritten ? TODO_update_ssa_only_virtuals : 0;
 }
 
@@ -3345,8 +3411,11 @@ unsplit_eh (eh_landing_pad lp)
   if ((e_in->flags & EDGE_EH) == 0 || (e_out->flags & EDGE_EH) != 0)
     return false;
 
-  /* The block must be empty except for the labels.  */
-  if (!gsi_end_p (gsi_after_labels (bb)))
+  /* The block must be empty except for the labels and debug insns.  */
+  gsi = gsi_after_labels (bb);
+  if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+    gsi_next_nondebug (&gsi);
+  if (!gsi_end_p (gsi))
     return false;
 
   /* The destination block must not already have a landing pad