OSDN Git Service

Add NIOS2 support. Code from SourceyG++.
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-sink.c
index 9deec26..0222dac 100644 (file)
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "bitmap.h"
 #include "langhooks.h"
 #include "cfgloop.h"
+#include "params.h"
 
 /* TODO:
    1. Sinking store only using scalar promotion (IE without moving the RHS):
@@ -191,7 +192,8 @@ is_hidden_global_store (gimple stmt)
 
        }
       else if (INDIRECT_REF_P (lhs)
-              || TREE_CODE (lhs) == MEM_REF)
+              || TREE_CODE (lhs) == MEM_REF
+              || TREE_CODE (lhs) == TARGET_MEM_REF)
        return ptr_deref_may_alias_global_p (TREE_OPERAND (lhs, 0));
       else if (CONSTANT_CLASS_P (lhs))
        return true;
@@ -257,6 +259,71 @@ nearest_common_dominator_of_uses (gimple stmt, bool *debug_stmts)
   return commondom;
 }
 
+/* Given EARLY_BB and LATE_BB, two blocks in a path through the dominator
+   tree, return the best basic block between them (inclusive) to place
+   statements.
+
+   We want the most control dependent block in the shallowest loop nest.
+
+   If the resulting block is in a shallower loop nest, then use it.  Else
+   only use the resulting block if it has significantly lower execution
+   frequency than EARLY_BB to avoid gratutious statement movement.  We
+   consider statements with VOPS more desirable to move.
+
+   This pass would obviously benefit from PDO as it utilizes block
+   frequencies.  It would also benefit from recomputing frequencies
+   if profile data is not available since frequencies often get out
+   of sync with reality.  */
+
+static basic_block
+select_best_block (basic_block early_bb,
+                  basic_block late_bb,
+                  gimple stmt)
+{
+  basic_block best_bb = late_bb;
+  basic_block temp_bb = late_bb;
+  int threshold;
+
+  while (temp_bb != early_bb)
+    {
+      /* If we've moved into a lower loop nest, then that becomes
+        our best block.  */
+      if (temp_bb->loop_depth < best_bb->loop_depth)
+       best_bb = temp_bb;
+
+      /* Walk up the dominator tree, hopefully we'll find a shallower
+        loop nest.  */
+      temp_bb = get_immediate_dominator (CDI_DOMINATORS, temp_bb);
+    }
+
+  /* If we found a shallower loop nest, then we always consider that
+     a win.  This will always give us the most control dependent block
+     within that loop nest.  */
+  if (best_bb->loop_depth < early_bb->loop_depth)
+    return best_bb;
+
+  /* Get the sinking threshold.  If the statement to be moved has memory
+     operands, then increase the threshold by 7% as those are even more
+     profitable to avoid, clamping at 100%.  */
+  threshold = PARAM_VALUE (PARAM_SINK_FREQUENCY_THRESHOLD);
+  if (gimple_vuse (stmt) || gimple_vdef (stmt))
+    {
+      threshold += 7;
+      if (threshold > 100)
+       threshold = 100;
+    }
+
+  /* If BEST_BB is at the same nesting level, then require it to have
+     significantly lower execution frequency to avoid gratutious movement.  */
+  if (best_bb->loop_depth == early_bb->loop_depth
+      && best_bb->frequency < (early_bb->frequency * threshold / 100.0))
+    return best_bb;
+
+  /* No better block found, so return EARLY_BB, which happens to be the
+     statement's original block.  */
+  return early_bb;
+}
+
 /* Given a statement (STMT) and the basic block it is currently in (FROMBB),
    determine the location to sink the statement to, if any.
    Returns true if there is such location; in that case, TOGSI points to the
@@ -267,7 +334,6 @@ statement_sink_location (gimple stmt, basic_block frombb,
                         gimple_stmt_iterator *togsi)
 {
   gimple use;
-  tree def;
   use_operand_p one_use = NULL_USE_OPERAND_P;
   basic_block sinkbb;
   use_operand_p use_p;
@@ -275,24 +341,17 @@ statement_sink_location (gimple stmt, basic_block frombb,
   ssa_op_iter iter;
   imm_use_iterator imm_iter;
 
-  FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
-    {
-      FOR_EACH_IMM_USE_FAST (one_use, imm_iter, def)
-       {
-         if (is_gimple_debug (USE_STMT (one_use)))
-           continue;
-
-         break;
-       }
-      if (one_use != NULL_USE_OPERAND_P)
-        break;
-    }
+  /* We only can sink assignments.  */
+  if (!is_gimple_assign (stmt))
+    return false;
 
-  /* Return if there are no immediate uses of this stmt.  */
-  if (one_use == NULL_USE_OPERAND_P)
+  /* We only can sink stmts with a single definition.  */
+  def_p = single_ssa_def_operand (stmt, SSA_OP_ALL_DEFS);
+  if (def_p == NULL_DEF_OPERAND_P)
     return false;
 
-  if (gimple_code (stmt) != GIMPLE_ASSIGN)
+  /* Return if there are no immediate uses of this stmt.  */
+  if (has_zero_uses (DEF_FROM_PTR (def_p)))
     return false;
 
   /* There are a few classes of things we can't or don't move, some because we
@@ -322,20 +381,14 @@ statement_sink_location (gimple stmt, basic_block frombb,
   */
   if (stmt_ends_bb_p (stmt)
       || gimple_has_side_effects (stmt)
-      || is_hidden_global_store (stmt)
       || gimple_has_volatile_ops (stmt)
-      || gimple_vuse (stmt)
+      || (gimple_vuse (stmt) && !gimple_vdef (stmt))
       || (cfun->has_local_explicit_reg_vars
          && TYPE_MODE (TREE_TYPE (gimple_assign_lhs (stmt))) == BLKmode))
     return false;
 
-  FOR_EACH_SSA_DEF_OPERAND (def_p, stmt, iter, SSA_OP_ALL_DEFS)
-    {
-      tree def = DEF_FROM_PTR (def_p);
-      if (is_global_var (SSA_NAME_VAR (def))
-         || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def))
-       return false;
-    }
+  if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (DEF_FROM_PTR (def_p)))
+    return false;
 
   FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES)
     {
@@ -344,11 +397,48 @@ statement_sink_location (gimple stmt, basic_block frombb,
        return false;
     }
 
+  use = NULL;
+
+  /* If stmt is a store the one and only use needs to be the VOP
+     merging PHI node.  */
+  if (gimple_vdef (stmt))
+    {
+      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, DEF_FROM_PTR (def_p))
+       {
+         gimple use_stmt = USE_STMT (use_p);
+
+         /* A killing definition is not a use.  */
+         if (gimple_assign_single_p (use_stmt)
+             && gimple_vdef (use_stmt)
+             && operand_equal_p (gimple_assign_lhs (stmt),
+                                 gimple_assign_lhs (use_stmt), 0))
+           {
+             /* If use_stmt is or might be a nop assignment then USE_STMT
+                acts as a use as well as definition.  */
+             if (stmt != use_stmt
+                 && ref_maybe_used_by_stmt_p (use_stmt,
+                                              gimple_assign_lhs (stmt)))
+               return false;
+             continue;
+           }
+
+         if (gimple_code (use_stmt) != GIMPLE_PHI)
+           return false;
+
+         if (use
+             && use != use_stmt)
+           return false;
+
+         use = use_stmt;
+       }
+      if (!use)
+       return false;
+    }
   /* If all the immediate uses are not in the same place, find the nearest
      common dominator of all the immediate uses.  For PHI nodes, we have to
      find the nearest common dominator of all of the predecessor blocks, since
      that is where insertion would have to take place.  */
-  if (!all_immediate_uses_same_place (stmt))
+  else if (!all_immediate_uses_same_place (stmt))
     {
       bool debug_stmts = false;
       basic_block commondom = nearest_common_dominator_of_uses (stmt,
@@ -363,74 +453,53 @@ statement_sink_location (gimple stmt, basic_block frombb,
       if (!dominated_by_p (CDI_DOMINATORS, commondom, frombb))
        return false;
 
-      /* It doesn't make sense to move to a dominator that post-dominates
-        frombb, because it means we've just moved it into a path that always
-        executes if frombb executes, instead of reducing the number of
-        executions .  */
-      if (dominated_by_p (CDI_POST_DOMINATORS, frombb, commondom))
-       {
-         if (dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "Not moving store, common dominator post-dominates from block.\n");
-         return false;
-       }
+      commondom = select_best_block (frombb, commondom, stmt);
 
-      if (commondom == frombb || commondom->loop_depth > frombb->loop_depth)
-       return false;
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       {
-         fprintf (dump_file, "Common dominator of all uses is %d\n",
-                  commondom->index);
-       }
+      if (commondom == frombb)
+       return false;   
 
       *togsi = gsi_after_labels (commondom);
 
       return true;
     }
-
-  use = USE_STMT (one_use);
-  if (gimple_code (use) != GIMPLE_PHI)
+  else
     {
-      sinkbb = gimple_bb (use);
-      if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
-         || sinkbb->loop_father != frombb->loop_father)
-       return false;
+      FOR_EACH_IMM_USE_FAST (one_use, imm_iter, DEF_FROM_PTR (def_p))
+       {
+         if (is_gimple_debug (USE_STMT (one_use)))
+           continue;
+         break;
+       }
+      use = USE_STMT (one_use);
 
-      /* Move the expression to a post dominator can't reduce the number of
-         executions.  */
-      if (dominated_by_p (CDI_POST_DOMINATORS, frombb, sinkbb))
-        return false;
+      if (gimple_code (use) != GIMPLE_PHI)
+       {
+         sinkbb = gimple_bb (use);
+         sinkbb = select_best_block (frombb, gimple_bb (use), stmt);
 
-      *togsi = gsi_for_stmt (use);
+         if (sinkbb == frombb)
+           return false;
 
-      return true;
+         *togsi = gsi_for_stmt (use);
+
+         return true;
+       }
     }
 
-  /* Note that at this point, all uses must be in the same statement, so it
-     doesn't matter which def op we choose, pick the first one.  */
-  FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
-    break;
+  sinkbb = find_bb_for_arg (use, DEF_FROM_PTR (def_p));
 
-  sinkbb = find_bb_for_arg (use, def);
+  /* This can happen if there are multiple uses in a PHI.  */
   if (!sinkbb)
     return false;
-
-  /* This will happen when you have
-     a_3 = PHI <a_13, a_26>
-
-     a_26 = VDEF <a_3>
-
-     If the use is a phi, and is in the same bb as the def,
-     we can't sink it.  */
-
-  if (gimple_bb (use) == frombb)
-    return false;
-  if (sinkbb == frombb || sinkbb->loop_depth > frombb->loop_depth
-      || sinkbb->loop_father != frombb->loop_father)
+  
+  sinkbb = select_best_block (frombb, sinkbb, stmt);
+  if (!sinkbb || sinkbb == frombb)
     return false;
 
-  /* Move the expression to a post dominator can't reduce the number of
-     executions.  */
-  if (dominated_by_p (CDI_POST_DOMINATORS, frombb, sinkbb))
+  /* If the latch block is empty, don't make it non-empty by sinking
+     something into it.  */
+  if (sinkbb == frombb->loop_father->latch
+      && empty_block_p (sinkbb))
     return false;
 
   *togsi = gsi_after_labels (sinkbb);
@@ -479,6 +548,20 @@ sink_code_in_bb (basic_block bb)
                   bb->index, (gsi_bb (togsi))->index);
        }
 
+      /* Update virtual operands of statements in the path we
+         do not sink to.  */
+      if (gimple_vdef (stmt))
+       {
+         imm_use_iterator iter;
+         use_operand_p use_p;
+         gimple vuse_stmt;
+
+         FOR_EACH_IMM_USE_STMT (vuse_stmt, iter, gimple_vdef (stmt))
+           if (gimple_code (vuse_stmt) != GIMPLE_PHI)
+             FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+               SET_USE (use_p, gimple_vuse (stmt));
+       }
+
       /* If this is the end of the basic block, we need to insert at the end
          of the basic block.  */
       if (gsi_end_p (togsi))
@@ -553,7 +636,7 @@ static void
 execute_sink_code (void)
 {
   loop_optimizer_init (LOOPS_NORMAL);
-
+  split_critical_edges ();
   connect_infinite_loops_to_exit ();
   memset (&sink_stats, 0, sizeof (sink_stats));
   calculate_dominance_info (CDI_DOMINATORS);
@@ -597,8 +680,8 @@ struct gimple_opt_pass pass_sink_code =
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
   TODO_update_ssa
-    | TODO_dump_func
-    | TODO_ggc_collect
-    | TODO_verify_ssa                  /* todo_flags_finish */
+    | TODO_verify_ssa
+    | TODO_verify_flow
+    | TODO_ggc_collect                 /* todo_flags_finish */
  }
 };