re PR target/65697 (__atomic memory barriers not strong enough for __sync builtins)
[gcc.git] / gcc / tree-cfgcleanup.c
index a66ec6e75375a823f059f6d62d5b88ebf69fdfb3..07e2d7465b005c21be42f25ac680667fac3b47aa 100644 (file)
@@ -1,5 +1,5 @@
 /* CFG cleanup for trees.
-   Copyright (C) 2001-2014 Free Software Foundation, Inc.
+   Copyright (C) 2001-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,18 +21,26 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "alias.h"
+#include "symtab.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "tm_p.h"
+#include "predict.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
+#include "cfgcleanup.h"
 #include "basic-block.h"
 #include "diagnostic-core.h"
 #include "flags.h"
-#include "function.h"
 #include "langhooks.h"
 #include "tree-ssa-alias.h"
 #include "internal-fn.h"
 #include "tree-eh.h"
 #include "gimple-expr.h"
-#include "is-a.h"
 #include "gimple.h"
 #include "gimplify.h"
 #include "gimple-iterator.h"
@@ -43,13 +51,21 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "tree-ssanames.h"
 #include "tree-ssa-loop-manip.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
 #include "expr.h"
 #include "tree-dfa.h"
 #include "tree-ssa.h"
 #include "tree-pass.h"
 #include "except.h"
 #include "cfgloop.h"
-#include "hashtab.h"
 #include "tree-ssa-propagate.h"
 #include "tree-scalar-evolution.h"
 
@@ -113,7 +129,7 @@ cleanup_control_expr_graph (basic_block bb, gimple_stmt_iterator gsi)
          break;
 
        case GIMPLE_SWITCH:
-         val = gimple_switch_index (stmt);
+         val = gimple_switch_index (as_a <gswitch *> (stmt));
          break;
 
        default:
@@ -309,7 +325,7 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
       switch (gimple_code (stmt))
        {
        case GIMPLE_LABEL:
-         if (DECL_NONLOCAL (gimple_label_label (stmt)))
+         if (DECL_NONLOCAL (gimple_label_label (as_a <glabel *> (stmt))))
            return false;
          if (optimize == 0 && gimple_location (stmt) != locus)
            return false;
@@ -367,11 +383,11 @@ phi_alternatives_equal (basic_block dest, edge e1, edge e2)
 {
   int n1 = e1->dest_idx;
   int n2 = e2->dest_idx;
-  gimple_stmt_iterator gsi;
+  gphi_iterator gsi;
 
   for (gsi = gsi_start_phis (dest); !gsi_end_p (gsi); gsi_next (&gsi))
     {
-      gimple phi = gsi_stmt (gsi);
+      gphi *phi = gsi.phi ();
       tree val1 = gimple_phi_arg_def (phi, n1);
       tree val2 = gimple_phi_arg_def (phi, n2);
 
@@ -406,11 +422,11 @@ remove_forwarder_block (basic_block bb)
   /* 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
-      && (DECL_NONLOCAL (gimple_label_label (label))
-         || EH_LANDING_PAD_NR (gimple_label_label (label)) != 0))
-    return false;
+  if (label)
+    if (glabel *label_stmt = dyn_cast <glabel *> (label))
+      if (DECL_NONLOCAL (gimple_label_label (label_stmt))
+         || EH_LANDING_PAD_NR (gimple_label_label (label_stmt)) != 0)
+       return false;
 
   /* If there is an abnormal edge to basic block BB, but not into
      dest, problems might occur during removal of the phi node at out
@@ -468,11 +484,11 @@ remove_forwarder_block (basic_block bb)
        {
          /* Create arguments for the phi nodes, since the edge was not
             here before.  */
-         for (gsi = gsi_start_phis (dest);
-              !gsi_end_p (gsi);
-              gsi_next (&gsi))
+         for (gphi_iterator psi = gsi_start_phis (dest);
+              !gsi_end_p (psi);
+              gsi_next (&psi))
            {
-             gimple phi = gsi_stmt (gsi);
+             gphi *phi = psi.phi ();
              source_location l = gimple_phi_arg_location_from_edge (phi, succ);
              tree def = gimple_phi_arg_def (phi, succ->dest_idx);
              add_phi_arg (phi, unshare_expr (def), s, l);
@@ -492,7 +508,7 @@ remove_forwarder_block (basic_block bb)
       label = gsi_stmt (gsi);
       if (is_gimple_debug (label))
        break;
-      decl = gimple_label_label (label);
+      decl = gimple_label_label (as_a <glabel *> (label));
       if (EH_LANDING_PAD_NR (decl) != 0
          || DECL_NONLOCAL (decl)
          || FORCED_LABEL (decl)
@@ -551,8 +567,9 @@ remove_forwarder_block (basic_block bb)
   return true;
 }
 
-/* STMT is a call that has been discovered noreturn.  Fixup the CFG
-   and remove LHS.  Return true if something changed.  */
+/* STMT is a call that has been discovered noreturn.  Split the
+   block to prepare fixing up the CFG and remove LHS.
+   Return true if cleanup-cfg needs to run.  */
 
 bool
 fixup_noreturn_call (gimple stmt)
@@ -565,76 +582,55 @@ fixup_noreturn_call (gimple stmt)
 
   /* 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 (stmt == gsi_stmt (gsi_last_nondebug_bb (bb)))
+       {
+         /* Don't split if there are only debug stmts
+            after stmt, that can result in -fcompare-debug
+            failures.  Remove the debug stmts instead,
+            they should be all unreachable anyway.  */
+         gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+         for (gsi_next (&gsi); !gsi_end_p (gsi); )
+           gsi_remove (&gsi, true);
+       }
+      else
+       {
+         split_block (bb, stmt);
+         changed = true;
+       }
+    }
 
-  /* If there is LHS, remove it.  */
-  if (gimple_call_lhs (stmt))
+  /* If there is an LHS, remove it, but only if its type has fixed size.
+     The LHS will need to be recreated during RTL expansion and creating
+     temporaries of variable-sized types is not supported.  */
+  tree lhs = gimple_call_lhs (stmt);
+  if (lhs && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (lhs))) == INTEGER_CST)
     {
-      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)
+      /* We need to fix up the SSA name to avoid checking errors.  */
+      if (TREE_CODE (lhs) == 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_FOR_FN (cfun, bb_index));
-         BITMAP_FREE (blocks);
-         release_ssa_name (op);
+         tree new_var = create_tmp_reg (TREE_TYPE (lhs));
+         SET_SSA_NAME_VAR_OR_IDENTIFIER (lhs, new_var);
+         SSA_NAME_DEF_STMT (lhs) = gimple_build_nop ();
+         set_ssa_default_def (cfun, new_var, lhs);
        }
+
       update_stmt (stmt);
-      changed = true;
     }
-  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.  */
-
-static bool
-split_bb_on_noreturn_calls (basic_block bb)
-{
-  bool changed = false;
-  gimple_stmt_iterator gsi;
-
-  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+  /* Mark the call as altering control flow.  */
+  if (!gimple_call_ctrl_altering_p (stmt))
     {
-      gimple stmt = gsi_stmt (gsi);
-
-      if (!is_gimple_call (stmt))
-       continue;
-
-      if (gimple_call_noreturn_p (stmt))
-       changed |= fixup_noreturn_call (stmt);
+      gimple_call_set_ctrl_altering (stmt, true);
+      changed = true;
     }
 
-  if (changed)
-    bitmap_set_bit (cfgcleanup_altered_bbs, bb->index);
   return changed;
 }
 
+
 /* Tries to cleanup cfg in basic block BB.  Returns true if anything
    changes.  */
 
@@ -653,8 +649,18 @@ cleanup_tree_cfg_bb (basic_block bb)
   if (single_succ_p (bb)
       && can_merge_blocks_p (bb, single_succ (bb)))
     {
-      merge_blocks (bb, single_succ (bb));
-      return true;
+      /* If there is a merge opportunity with the predecessor
+         do nothing now but wait until we process the predecessor.
+        This happens when we visit BBs in a non-optimal order and
+        avoids quadratic behavior with adjusting stmts BB pointer.  */
+      if (single_pred_p (bb)
+         && can_merge_blocks_p (single_pred (bb), bb))
+       ;
+      else
+       {
+         merge_blocks (bb, single_succ (bb));
+         return true;
+       }
     }
 
   return retval;
@@ -684,10 +690,7 @@ cleanup_tree_cfg_1 (void)
     {
       bb = BASIC_BLOCK_FOR_FN (cfun, i);
       if (bb)
-       {
-         retval |= cleanup_tree_cfg_bb (bb);
-         retval |= split_bb_on_noreturn_calls (bb);
-       }
+       retval |= cleanup_tree_cfg_bb (bb);
     }
 
   /* Now process the altered blocks, as long as any are available.  */
@@ -703,10 +706,6 @@ cleanup_tree_cfg_1 (void)
        continue;
 
       retval |= cleanup_tree_cfg_bb (bb);
-
-      /* Rerun split_bb_on_noreturn_calls, in case we have altered any noreturn
-        calls.  */
-      retval |= split_bb_on_noreturn_calls (bb);
     }
 
   end_recording_case_labels ();
@@ -827,10 +826,10 @@ remove_forwarder_block_with_phi (basic_block bb)
   /* If the destination block consists of a nonlocal label, do not
      merge it.  */
   label = first_stmt (dest);
-  if (label
-      && gimple_code (label) == GIMPLE_LABEL
-      && DECL_NONLOCAL (gimple_label_label (label)))
-    return false;
+  if (label)
+    if (glabel *label_stmt = dyn_cast <glabel *> (label))
+      if (DECL_NONLOCAL (gimple_label_label (label_stmt)))
+       return false;
 
   /* Record BB's single pred in case we need to update the father
      loop's latch information later.  */
@@ -842,7 +841,7 @@ remove_forwarder_block_with_phi (basic_block bb)
   while (EDGE_COUNT (bb->preds) > 0)
     {
       edge e = EDGE_PRED (bb, 0), s;
-      gimple_stmt_iterator gsi;
+      gphi_iterator gsi;
 
       s = find_edge (e->src, dest);
       if (s)
@@ -874,7 +873,7 @@ remove_forwarder_block_with_phi (basic_block bb)
           !gsi_end_p (gsi);
           gsi_next (&gsi))
        {
-         gimple phi = gsi_stmt (gsi);
+         gphi *phi = gsi.phi ();
          tree def = gimple_phi_arg_def (phi, succ->dest_idx);
          source_location locus = gimple_phi_arg_location_from_edge (phi, succ);
 
@@ -1022,7 +1021,7 @@ pass_merge_phi::execute (function *fun)
        }
       else
        {
-         gimple_stmt_iterator gsi;
+         gphi_iterator gsi;
          unsigned int dest_idx = single_succ_edge (bb)->dest_idx;
 
          /* BB dominates DEST.  There may be many users of the PHI
@@ -1033,7 +1032,7 @@ pass_merge_phi::execute (function *fun)
          for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
               gsi_next (&gsi))
            {
-             gimple phi = gsi_stmt (gsi);
+             gphi *phi = gsi.phi ();
              tree result = gimple_phi_result (phi);
              use_operand_p imm_use;
              gimple use_stmt;
@@ -1092,9 +1091,12 @@ make_pass_merge_phi (gcc::context *ctxt)
 static unsigned int
 execute_cleanup_cfg_post_optimizing (void)
 {
-  unsigned int todo = 0;
+  unsigned int todo = execute_fixup_cfg ();
   if (cleanup_tree_cfg ())
-    todo |= TODO_update_ssa;
+    {
+      todo &= ~TODO_cleanup_cfg;
+      todo |= TODO_update_ssa;
+    }
   maybe_remove_unreachable_handlers ();
   cleanup_dead_labels ();
   group_case_labels ();