re PR target/65697 (__atomic memory barriers not strong enough for __sync builtins)
[gcc.git] / gcc / tree-cfgcleanup.c
index bc4d83ed6b6b1f41f45f201cc50d9ae1d6613c72..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:
@@ -162,6 +178,23 @@ cleanup_control_expr_graph (basic_block bb, gimple_stmt_iterator gsi)
   return retval;
 }
 
+/* Cleanup the GF_CALL_CTRL_ALTERING flag according to
+   to updated gimple_call_flags.  */
+
+static void
+cleanup_call_ctrl_altering_flag (gimple bb_end)
+{
+  if (!is_gimple_call (bb_end)
+      || !gimple_call_ctrl_altering_p (bb_end))
+    return;
+
+  int flags = gimple_call_flags (bb_end);
+  if (((flags & (ECF_CONST | ECF_PURE))
+       && !(flags & ECF_LOOPING_CONST_OR_PURE))
+      || (flags & ECF_LEAF))
+    gimple_call_set_ctrl_altering (bb_end, false);
+}
+
 /* Try to remove superfluous control structures in basic block BB.  Returns
    true if anything changes.  */
 
@@ -182,6 +215,9 @@ cleanup_control_flow_bb (basic_block bb)
 
   stmt = gsi_stmt (gsi);
 
+  /* Try to cleanup ctrl altering flag for call which ends bb.  */
+  cleanup_call_ctrl_altering_flag (stmt);
+
   if (gimple_code (stmt) == GIMPLE_COND
       || gimple_code (stmt) == GIMPLE_SWITCH)
     retval |= cleanup_control_expr_graph (bb, gsi);
@@ -289,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;
@@ -347,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);
 
@@ -386,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
@@ -448,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);
@@ -472,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)
@@ -531,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)
@@ -545,82 +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_bbs_on_noreturn_calls (void)
-{
-  bool changed = false;
-  gimple stmt;
-  basic_block bb;
-
-  /* Detect cases where a mid-block call is now known not to return.  */
-  if (cfun->gimple_df)
-    while (vec_safe_length (MODIFIED_NORETURN_CALLS (cfun)))
-      {
-       stmt = MODIFIED_NORETURN_CALLS (cfun)->pop ();
-       bb = gimple_bb (stmt);
-       /* BB might be deleted at this point, so verify first
-          BB is present in the cfg.  */
-       if (bb == NULL
-           || bb->index < NUM_FIXED_BLOCKS
-           || bb->index >= last_basic_block_for_fn (cfun)
-           || BASIC_BLOCK_FOR_FN (cfun, bb->index) != bb
-           || !gimple_call_noreturn_p (stmt))
-         continue;
-
-       changed |= fixup_noreturn_call (stmt);
-      }
+  /* Mark the call as altering control flow.  */
+  if (!gimple_call_ctrl_altering_p (stmt))
+    {
+      gimple_call_set_ctrl_altering (stmt, true);
+      changed = true;
+    }
 
   return changed;
 }
 
+
 /* Tries to cleanup cfg in basic block BB.  Returns true if anything
    changes.  */
 
@@ -639,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;
@@ -655,8 +675,6 @@ cleanup_tree_cfg_1 (void)
   basic_block bb;
   unsigned i, n;
 
-  retval |= split_bbs_on_noreturn_calls ();
-
   /* Prepare the worklists of altered blocks.  */
   cfgcleanup_altered_bbs = BITMAP_ALLOC (NULL);
 
@@ -688,10 +706,6 @@ cleanup_tree_cfg_1 (void)
        continue;
 
       retval |= cleanup_tree_cfg_bb (bb);
-
-      /* Rerun split_bbs_on_noreturn_calls, in case we have altered any noreturn
-        calls.  */
-      retval |= split_bbs_on_noreturn_calls ();
     }
 
   end_recording_case_labels ();
@@ -812,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.  */
@@ -827,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)
@@ -859,22 +873,20 @@ 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);
 
          if (TREE_CODE (def) == SSA_NAME)
            {
-             edge_var_map_vector *head;
-             edge_var_map *vm;
-             size_t i;
-
              /* If DEF is one of the results of PHI nodes removed during
                 redirection, replace it with the PHI argument that used
                 to be on E.  */
-             head = redirect_edge_var_map_vector (e);
-             FOR_EACH_VEC_SAFE_ELT (head, i, vm)
+             vec<edge_var_map> *head = redirect_edge_var_map_vector (e);
+             size_t length = head ? head->length () : 0;
+             for (size_t i = 0; i < length; i++)
                {
+                 edge_var_map *vm = &(*head)[i];
                  tree old_arg = redirect_edge_var_map_result (vm);
                  tree new_arg = redirect_edge_var_map_def (vm);
 
@@ -1009,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
@@ -1020,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;
@@ -1079,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 ();