OSDN Git Service

* config/alpha/alpha.md (umk_mismatch_args): Don't put a mode on set.
[pf3gnuchains/gcc-fork.git] / gcc / tree-cfgcleanup.c
index 9fb489a..fe073f3 100644 (file)
@@ -1,5 +1,5 @@
 /* CFG cleanup for trees.
 /* CFG cleanup for trees.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
    Free Software Foundation, Inc.
 
 This file is part of GCC.
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -23,23 +23,18 @@ along with GCC; see the file COPYING3.  If not see
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
 #include "coretypes.h"
 #include "tm.h"
 #include "tree.h"
-#include "rtl.h"
 #include "tm_p.h"
 #include "tm_p.h"
-#include "hard-reg-set.h"
 #include "basic-block.h"
 #include "output.h"
 #include "basic-block.h"
 #include "output.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 #include "flags.h"
 #include "function.h"
 #include "flags.h"
 #include "function.h"
-#include "expr.h"
 #include "ggc.h"
 #include "langhooks.h"
 #include "ggc.h"
 #include "langhooks.h"
-#include "diagnostic.h"
 #include "tree-flow.h"
 #include "timevar.h"
 #include "tree-dump.h"
 #include "tree-pass.h"
 #include "tree-flow.h"
 #include "timevar.h"
 #include "tree-dump.h"
 #include "tree-pass.h"
-#include "toplev.h"
 #include "except.h"
 #include "cfgloop.h"
 #include "cfglayout.h"
 #include "except.h"
 #include "cfgloop.h"
 #include "cfglayout.h"
@@ -90,9 +85,55 @@ cleanup_control_expr_graph (basic_block bb, gimple_stmt_iterator gsi)
       edge e;
       edge_iterator ei;
       bool warned;
       edge e;
       edge_iterator ei;
       bool warned;
+      location_t loc;
 
       fold_defer_overflow_warnings ();
 
       fold_defer_overflow_warnings ();
-      val = gimple_fold (stmt);
+      loc = gimple_location (stmt);
+      switch (gimple_code (stmt))
+       {
+       case GIMPLE_COND:
+         {
+           tree lhs = gimple_cond_lhs (stmt);
+           tree rhs = gimple_cond_rhs (stmt);
+           /* For conditions try harder and lookup single-argument
+              PHI nodes.  Only do so from the same basic-block though
+              as other basic-blocks may be dead already.  */
+           if (TREE_CODE (lhs) == SSA_NAME
+               && !name_registered_for_update_p (lhs))
+             {
+               gimple def_stmt = SSA_NAME_DEF_STMT (lhs);
+               if (gimple_code (def_stmt) == GIMPLE_PHI
+                   && gimple_phi_num_args (def_stmt) == 1
+                   && gimple_bb (def_stmt) == gimple_bb (stmt)
+                   && (TREE_CODE (PHI_ARG_DEF (def_stmt, 0)) != SSA_NAME
+                       || !name_registered_for_update_p (PHI_ARG_DEF (def_stmt,
+                                                                      0))))
+                 lhs = PHI_ARG_DEF (def_stmt, 0);
+             }
+           if (TREE_CODE (rhs) == SSA_NAME
+               && !name_registered_for_update_p (rhs))
+             {
+               gimple def_stmt = SSA_NAME_DEF_STMT (rhs);
+               if (gimple_code (def_stmt) == GIMPLE_PHI
+                   && gimple_phi_num_args (def_stmt) == 1
+                   && gimple_bb (def_stmt) == gimple_bb (stmt)
+                   && (TREE_CODE (PHI_ARG_DEF (def_stmt, 0)) != SSA_NAME
+                       || !name_registered_for_update_p (PHI_ARG_DEF (def_stmt,
+                                                                      0))))
+                 rhs = PHI_ARG_DEF (def_stmt, 0);
+             }
+           val = fold_binary_loc (loc, gimple_cond_code (stmt),
+                                  boolean_type_node, lhs, rhs);
+           break;
+         }
+
+       case GIMPLE_SWITCH:
+         val = gimple_switch_index (stmt);
+         break;
+
+       default:
+         val = NULL_TREE;
+       }
       taken_edge = find_taken_edge (bb, val);
       if (!taken_edge)
        {
       taken_edge = find_taken_edge (bb, val);
       if (!taken_edge)
        {
@@ -221,6 +262,7 @@ static bool
 tree_forwarder_block_p (basic_block bb, bool phi_wanted)
 {
   gimple_stmt_iterator gsi;
 tree_forwarder_block_p (basic_block bb, bool phi_wanted)
 {
   gimple_stmt_iterator gsi;
+  location_t locus;
 
   /* BB must have a single outgoing edge.  */
   if (single_succ_p (bb) != 1
 
   /* BB must have a single outgoing edge.  */
   if (single_succ_p (bb) != 1
@@ -235,9 +277,9 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
       || (single_succ_edge (bb)->flags & EDGE_ABNORMAL))
     return false;
 
       || (single_succ_edge (bb)->flags & EDGE_ABNORMAL))
     return false;
 
-#if ENABLE_CHECKING
-  gcc_assert (bb != ENTRY_BLOCK_PTR);
-#endif
+  gcc_checking_assert (bb != ENTRY_BLOCK_PTR);
+
+  locus = single_succ_edge (bb)->goto_locus;
 
   /* There should not be an edge coming from entry, or an EH edge.  */
   {
 
   /* There should not be an edge coming from entry, or an EH edge.  */
   {
@@ -247,6 +289,10 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
     FOR_EACH_EDGE (e, ei, bb->preds)
       if (e->src == ENTRY_BLOCK_PTR || (e->flags & EDGE_EH))
        return false;
     FOR_EACH_EDGE (e, ei, bb->preds)
       if (e->src == ENTRY_BLOCK_PTR || (e->flags & EDGE_EH))
        return false;
+      /* If goto_locus of any of the edges differs, prevent removing
+        the forwarder block for -O0.  */
+      else if (optimize == 0 && e->goto_locus != locus)
+       return false;
   }
 
   /* Now walk through the statements backward.  We can ignore labels,
   }
 
   /* Now walk through the statements backward.  We can ignore labels,
@@ -260,6 +306,8 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
        case GIMPLE_LABEL:
          if (DECL_NONLOCAL (gimple_label_label (stmt)))
            return false;
        case GIMPLE_LABEL:
          if (DECL_NONLOCAL (gimple_label_label (stmt)))
            return false;
+         if (optimize == 0 && gimple_location (stmt) != locus)
+           return false;
          break;
 
          /* ??? For now, hope there's a corresponding debug
          break;
 
          /* ??? For now, hope there's a corresponding debug
@@ -286,21 +334,6 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
   return true;
 }
 
   return true;
 }
 
-/* Return true if BB has at least one abnormal incoming edge.  */
-
-static inline bool
-has_abnormal_incoming_edge_p (basic_block bb)
-{
-  edge e;
-  edge_iterator ei;
-
-  FOR_EACH_EDGE (e, ei, bb->preds)
-    if (e->flags & EDGE_ABNORMAL)
-      return true;
-
-  return false;
-}
-
 /* If all the PHI nodes in DEST have alternatives for E1 and E2 and
    those alternatives are equal in each of the PHI nodes, then return
    true, else return false.  */
 /* If all the PHI nodes in DEST have alternatives for E1 and E2 and
    those alternatives are equal in each of the PHI nodes, then return
    true, else return false.  */
@@ -338,7 +371,7 @@ remove_forwarder_block (basic_block bb)
   gimple label;
   edge_iterator ei;
   gimple_stmt_iterator gsi, gsi_to;
   gimple label;
   edge_iterator ei;
   gimple_stmt_iterator gsi, gsi_to;
-  bool seen_abnormal_edge = false;
+  bool can_move_debug_stmts;
 
   /* We check for infinite loops already in tree_forwarder_block_p.
      However it may happen that the infinite loop is created
 
   /* We check for infinite loops already in tree_forwarder_block_p.
      However it may happen that the infinite loop is created
@@ -346,12 +379,13 @@ remove_forwarder_block (basic_block bb)
   if (dest == bb)
     return false;
 
   if (dest == bb)
     return false;
 
-  /* If the destination block consists of a nonlocal label, do not merge
-     it.  */
+  /* If the destination block consists of a nonlocal label or is a
+     EH landing pad, do not merge it.  */
   label = first_stmt (dest);
   if (label
       && gimple_code (label) == GIMPLE_LABEL
   label = first_stmt (dest);
   if (label
       && gimple_code (label) == GIMPLE_LABEL
-      && DECL_NONLOCAL (gimple_label_label (label)))
+      && (DECL_NONLOCAL (gimple_label_label (label))
+         || EH_LANDING_PAD_NR (gimple_label_label (label)) != 0))
     return false;
 
   /* If there is an abnormal edge to basic block BB, but not into
     return false;
 
   /* If there is an abnormal edge to basic block BB, but not into
@@ -365,14 +399,10 @@ remove_forwarder_block (basic_block bb)
 
      So if there is an abnormal edge to BB, proceed only if there is
      no abnormal edge to DEST and there are no phi nodes in DEST.  */
 
      So if there is an abnormal edge to BB, proceed only if there is
      no abnormal edge to DEST and there are no phi nodes in DEST.  */
-  if (has_abnormal_incoming_edge_p (bb))
-    {
-      seen_abnormal_edge = true;
-
-      if (has_abnormal_incoming_edge_p (dest)
-         || !gimple_seq_empty_p (phi_nodes (dest)))
-       return false;
-    }
+  if (bb_has_abnormal_pred (bb)
+      && (bb_has_abnormal_pred (dest)
+         || !gimple_seq_empty_p (phi_nodes (dest))))
+    return false;
 
   /* If there are phi nodes in DEST, and some of the blocks that are
      predecessors of BB are also predecessors of DEST, check that the
 
   /* If there are phi nodes in DEST, and some of the blocks that are
      predecessors of BB are also predecessors of DEST, check that the
@@ -390,6 +420,8 @@ remove_forwarder_block (basic_block bb)
        }
     }
 
        }
     }
 
+  can_move_debug_stmts = single_pred_p (dest);
+
   /* Redirect the edges.  */
   for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
     {
   /* Redirect the edges.  */
   for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
     {
@@ -419,19 +451,44 @@ remove_forwarder_block (basic_block bb)
        }
     }
 
        }
     }
 
-  if (seen_abnormal_edge)
+  /* Move nonlocal labels and computed goto targets as well as user
+     defined labels and labels with an EH landing pad number to the
+     new block, so that the redirection of the abnormal edges works,
+     jump targets end up in a sane place and debug information for
+     labels is retained.  */
+  gsi_to = gsi_start_bb (dest);
+  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
     {
     {
-      /* Move the labels to the new block, so that the redirection of
-        the abnormal edges works.  */
-      gsi_to = gsi_start_bb (dest);
-      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+      tree decl;
+      label = gsi_stmt (gsi);
+      if (is_gimple_debug (label))
+       break;
+      decl = gimple_label_label (label);
+      if (EH_LANDING_PAD_NR (decl) != 0
+         || DECL_NONLOCAL (decl)
+         || FORCED_LABEL (decl)
+         || !DECL_ARTIFICIAL (decl))
        {
        {
-         label = gsi_stmt (gsi);
-         gcc_assert (gimple_code (label) == GIMPLE_LABEL
-                     || is_gimple_debug (label));
          gsi_remove (&gsi, false);
          gsi_insert_before (&gsi_to, label, GSI_SAME_STMT);
        }
          gsi_remove (&gsi, false);
          gsi_insert_before (&gsi_to, label, GSI_SAME_STMT);
        }
+      else
+       gsi_next (&gsi);
+    }
+
+  /* Move debug statements if the destination has just a single
+     predecessor.  */
+  if (can_move_debug_stmts)
+    {
+      gsi_to = gsi_after_labels (dest);
+      for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); )
+       {
+         gimple debug = gsi_stmt (gsi);
+         if (!is_gimple_debug (debug))
+           break;
+         gsi_remove (&gsi, false);
+         gsi_insert_before (&gsi_to, debug, GSI_SAME_STMT);
+       }
     }
 
   bitmap_set_bit (cfgcleanup_altered_bbs, dest->index);
     }
 
   bitmap_set_bit (cfgcleanup_altered_bbs, dest->index);
@@ -461,6 +518,68 @@ remove_forwarder_block (basic_block bb)
   return true;
 }
 
   return true;
 }
 
+/* STMT is a call that has been discovered noreturn.  Fixup the CFG
+   and remove LHS.  Return true if something changed.  */
+
+bool
+fixup_noreturn_call (gimple stmt)
+{
+  basic_block bb = gimple_bb (stmt);
+  bool changed = false;
+
+  if (gimple_call_builtin_p (stmt, BUILT_IN_RETURN))
+    return false;
+
+  /* First split basic block if stmt is not last.  */
+  if (stmt != gsi_stmt (gsi_last_bb (bb)))
+    split_block (bb, stmt);
+
+  changed |= remove_fallthru_edge (bb->succs);
+
+  /* If there is LHS, remove it.  */
+  if (gimple_call_lhs (stmt))
+    {
+      tree op = gimple_call_lhs (stmt);
+      gimple_call_set_lhs (stmt, NULL_TREE);
+
+      /* We need to remove SSA name to avoid checking errors.
+        All uses are dominated by the noreturn and thus will
+        be removed afterwards.
+        We proactively remove affected non-PHI statements to avoid
+        fixup_cfg from trying to update them and crashing.  */
+      if (TREE_CODE (op) == SSA_NAME)
+       {
+         use_operand_p use_p;
+          imm_use_iterator iter;
+         gimple use_stmt;
+         bitmap_iterator bi;
+         unsigned int bb_index;
+
+         bitmap blocks = BITMAP_ALLOC (NULL);
+
+          FOR_EACH_IMM_USE_STMT (use_stmt, iter, op)
+           {
+             if (gimple_code (use_stmt) != GIMPLE_PHI)
+               bitmap_set_bit (blocks, gimple_bb (use_stmt)->index);
+             else
+               FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+                 SET_USE (use_p, error_mark_node);
+           }
+         EXECUTE_IF_SET_IN_BITMAP (blocks, 0, bb_index, bi)
+           delete_basic_block (BASIC_BLOCK (bb_index));
+         BITMAP_FREE (blocks);
+         release_ssa_name (op);
+       }
+      update_stmt (stmt);
+      changed = true;
+    }
+  /* Similarly remove VDEF if there is any.  */
+  else if (gimple_vdef (stmt))
+    update_stmt (stmt);
+  return changed;
+}
+
+
 /* Split basic blocks on calls in the middle of a basic block that are now
    known not to return, and remove the unreachable code.  */
 
 /* Split basic blocks on calls in the middle of a basic block that are now
    known not to return, and remove the unreachable code.  */
 
@@ -483,13 +602,10 @@ split_bbs_on_noreturn_calls (void)
            || bb->index < NUM_FIXED_BLOCKS
            || bb->index >= n_basic_blocks
            || BASIC_BLOCK (bb->index) != bb
            || bb->index < NUM_FIXED_BLOCKS
            || bb->index >= n_basic_blocks
            || BASIC_BLOCK (bb->index) != bb
-           || last_stmt (bb) == stmt
            || !gimple_call_noreturn_p (stmt))
          continue;
 
            || !gimple_call_noreturn_p (stmt))
          continue;
 
-       changed = true;
-       split_block (bb, stmt);
-       remove_fallthru_edge (bb->succs);
+       changed |= fixup_noreturn_call (stmt);
       }
 
   return changed;
       }
 
   return changed;
@@ -538,11 +654,7 @@ cleanup_tree_cfg_bb (basic_block bb)
 
   retval = cleanup_control_flow_bb (bb);
 
 
   retval = cleanup_control_flow_bb (bb);
 
-  /* Forwarder blocks can carry line number information which is
-     useful when debugging, so we only clean them up when
-     optimizing.  */
-  if (optimize > 0
-      && tree_forwarder_block_p (bb, false)
+  if (tree_forwarder_block_p (bb, false)
       && remove_forwarder_block (bb))
     return true;
 
       && remove_forwarder_block (bb))
     return true;
 
@@ -663,7 +775,10 @@ cleanup_tree_cfg_noloop (void)
 static void
 repair_loop_structures (void)
 {
 static void
 repair_loop_structures (void)
 {
-  bitmap changed_bbs = BITMAP_ALLOC (NULL);
+  bitmap changed_bbs;
+
+  timevar_push (TV_REPAIR_LOOPS);
+  changed_bbs = BITMAP_ALLOC (NULL);
   fix_loop_structure (changed_bbs);
 
   /* This usually does nothing.  But sometimes parts of cfg that originally
   fix_loop_structure (changed_bbs);
 
   /* This usually does nothing.  But sometimes parts of cfg that originally
@@ -680,6 +795,7 @@ repair_loop_structures (void)
   scev_reset ();
 
   loops_state_clear (LOOPS_NEED_FIXUP);
   scev_reset ();
 
   loops_state_clear (LOOPS_NEED_FIXUP);
+  timevar_pop (TV_REPAIR_LOOPS);
 }
 
 /* Cleanup cfg and repair loop structures.  */
 }
 
 /* Cleanup cfg and repair loop structures.  */
@@ -770,7 +886,7 @@ remove_forwarder_block_with_phi (basic_block bb)
                 redirection, replace it with the PHI argument that used
                 to be on E.  */
              head = redirect_edge_var_map_vector (e);
                 redirection, replace it with the PHI argument that used
                 to be on E.  */
              head = redirect_edge_var_map_vector (e);
-             for (i = 0; VEC_iterate (edge_var_map, head, i, vm); ++i)
+             FOR_EACH_VEC_ELT (edge_var_map, head, i, vm)
                {
                  tree old_arg = redirect_edge_var_map_result (vm);
                  tree new_arg = redirect_edge_var_map_def (vm);
                {
                  tree old_arg = redirect_edge_var_map_result (vm);
                  tree new_arg = redirect_edge_var_map_def (vm);
@@ -856,10 +972,10 @@ merge_phi_nodes (void)
 
       /* We have to feed into another basic block with PHI
         nodes.  */
 
       /* We have to feed into another basic block with PHI
         nodes.  */
-      if (!phi_nodes (dest)
+      if (gimple_seq_empty_p (phi_nodes (dest))
          /* We don't want to deal with a basic block with
             abnormal edges.  */
          /* We don't want to deal with a basic block with
             abnormal edges.  */
-         || has_abnormal_incoming_edge_p (bb))
+         || bb_has_abnormal_pred (bb))
        continue;
 
       if (!dominated_by_p (CDI_DOMINATORS, dest, bb))
        continue;
 
       if (!dominated_by_p (CDI_DOMINATORS, dest, bb))