OSDN Git Service

PR c++/51318
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-propagate.c
index ec0ecf3..80b33e1 100644 (file)
@@ -1,5 +1,6 @@
 /* Generic SSA value propagation engine.
-   Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
    This file is part of GCC.
 #include "tm.h"
 #include "tree.h"
 #include "flags.h"
-#include "rtl.h"
 #include "tm_p.h"
-#include "ggc.h"
 #include "basic-block.h"
 #include "output.h"
-#include "expr.h"
 #include "function.h"
-#include "diagnostic.h"
+#include "gimple-pretty-print.h"
 #include "timevar.h"
 #include "tree-dump.h"
 #include "tree-flow.h"
 #include "tree-pass.h"
 #include "tree-ssa-propagate.h"
 #include "langhooks.h"
-#include "varray.h"
 #include "vec.h"
 #include "value-prof.h"
 #include "gimple.h"
@@ -177,7 +174,7 @@ cfg_blocks_empty_p (void)
 /* Add a basic block to the worklist.  The block must not be already
    in the worklist, and it must not be the ENTRY or EXIT block.  */
 
-static void 
+static void
 cfg_blocks_add (basic_block bb)
 {
   bool head = false;
@@ -512,7 +509,7 @@ ssa_prop_init (void)
 
       for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
        gimple_set_plf (gsi_stmt (si), STMT_IN_SSA_EDGE_WORKLIST, false);
-    
+
       for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
        gimple_set_plf (gsi_stmt (si), STMT_IN_SSA_EDGE_WORKLIST, false);
 
@@ -604,20 +601,17 @@ valid_gimple_rhs_p (tree expr)
           }
           break;
 
-       case TRUTH_NOT_EXPR:
-         if (!is_gimple_val (TREE_OPERAND (expr, 0)))
-           return false;
-         break;
-
-       case TRUTH_AND_EXPR:
-       case TRUTH_XOR_EXPR:
-       case TRUTH_OR_EXPR:
-         if (!is_gimple_val (TREE_OPERAND (expr, 0))
-             || !is_gimple_val (TREE_OPERAND (expr, 1)))
-           return false;
-         break;
-
        default:
+         if (get_gimple_rhs_class (code) == GIMPLE_TERNARY_RHS)
+           {
+             if (((code == VEC_COND_EXPR || code == COND_EXPR)
+                  ? !is_gimple_condexpr (TREE_OPERAND (expr, 0))
+                  : !is_gimple_val (TREE_OPERAND (expr, 0)))
+                 || !is_gimple_val (TREE_OPERAND (expr, 1))
+                 || !is_gimple_val (TREE_OPERAND (expr, 2)))
+               return false;
+             break;
+           }
          return false;
        }
       break;
@@ -642,7 +636,7 @@ valid_gimple_rhs_p (tree expr)
    as a single GIMPLE_CALL statement.  If the arguments require
    further gimplification, return false.  */
 
-bool
+static bool
 valid_gimple_call_p (tree expr)
 {
   unsigned i, nargs;
@@ -652,8 +646,17 @@ valid_gimple_call_p (tree expr)
 
   nargs = call_expr_nargs (expr);
   for (i = 0; i < nargs; i++)
-    if (! is_gimple_operand (CALL_EXPR_ARG (expr, i)))
-      return false;
+    {
+      tree arg = CALL_EXPR_ARG (expr, i);
+      if (is_gimple_reg_type (arg))
+       {
+         if (!is_gimple_val (arg))
+           return false;
+       }
+      else
+       if (!is_gimple_lvalue (arg))
+         return false;
+    }
 
   return true;
 }
@@ -680,6 +683,40 @@ move_ssa_defining_stmt_for_defs (gimple new_stmt, gimple old_stmt)
     }
 }
 
+/* Helper function for update_gimple_call and update_call_from_tree.
+   A GIMPLE_CALL STMT is being replaced with GIMPLE_CALL NEW_STMT.  */
+
+static void
+finish_update_gimple_call (gimple_stmt_iterator *si_p, gimple new_stmt,
+                          gimple stmt)
+{
+  gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
+  move_ssa_defining_stmt_for_defs (new_stmt, stmt);
+  gimple_set_vuse (new_stmt, gimple_vuse (stmt));
+  gimple_set_vdef (new_stmt, gimple_vdef (stmt));
+  gimple_set_location (new_stmt, gimple_location (stmt));
+  if (gimple_block (new_stmt) == NULL_TREE)
+    gimple_set_block (new_stmt, gimple_block (stmt));
+  gsi_replace (si_p, new_stmt, false);
+}
+
+/* Update a GIMPLE_CALL statement at iterator *SI_P to call to FN
+   with number of arguments NARGS, where the arguments in GIMPLE form
+   follow NARGS argument.  */
+
+bool
+update_gimple_call (gimple_stmt_iterator *si_p, tree fn, int nargs, ...)
+{
+  va_list ap;
+  gimple new_stmt, stmt = gsi_stmt (*si_p);
+
+  gcc_assert (is_gimple_call (stmt));
+  va_start (ap, nargs);
+  new_stmt = gimple_build_call_valist (fn, nargs, ap);
+  finish_update_gimple_call (si_p, new_stmt, stmt);
+  va_end (ap);
+  return true;
+}
 
 /* Update a GIMPLE_CALL statement at iterator *SI_P to reflect the
    value of EXPR, which is expected to be the result of folding the
@@ -696,14 +733,8 @@ move_ssa_defining_stmt_for_defs (gimple new_stmt, gimple old_stmt)
 bool
 update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
 {
-  tree lhs;
-
   gimple stmt = gsi_stmt (*si_p);
 
-  gcc_assert (is_gimple_call (stmt));
-
-  lhs = gimple_call_lhs (stmt);
-
   if (valid_gimple_call_p (expr))
     {
       /* The call has simplified to another call.  */
@@ -717,24 +748,20 @@ update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
         {
           args = VEC_alloc (tree, heap, nargs);
           VEC_safe_grow (tree, heap, args, nargs);
-      
+
           for (i = 0; i < nargs; i++)
             VEC_replace (tree, args, i, CALL_EXPR_ARG (expr, i));
         }
 
       new_stmt = gimple_build_call_vec (fn, args);
-      gimple_call_set_lhs (new_stmt, lhs);
-      move_ssa_defining_stmt_for_defs (new_stmt, stmt);
-      gimple_set_vuse (new_stmt, gimple_vuse (stmt));
-      gimple_set_vdef (new_stmt, gimple_vdef (stmt));
-      gimple_set_location (new_stmt, gimple_location (stmt));
-      gsi_replace (si_p, new_stmt, false);
+      finish_update_gimple_call (si_p, new_stmt, stmt);
       VEC_free (tree, heap, args);
 
       return true;
     }
   else if (valid_gimple_rhs_p (expr))
     {
+      tree lhs = gimple_call_lhs (stmt);
       gimple new_stmt;
 
       /* The call has simplified to an expression
@@ -754,8 +781,11 @@ update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
           /* No value is expected, and EXPR has no effect.
              Replace it with an empty statement.  */
           new_stmt = gimple_build_nop ();
-         unlink_stmt_vdef (stmt);
-         release_defs (stmt);
+         if (gimple_in_ssa_p (cfun))
+           {
+             unlink_stmt_vdef (stmt);
+             release_defs (stmt);
+           }
         }
       else
         {
@@ -767,7 +797,8 @@ update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
           lhs = create_tmp_var (TREE_TYPE (expr), NULL);
           new_stmt = gimple_build_assign (lhs, expr);
           add_referenced_var (lhs);
-          lhs = make_ssa_name (lhs, new_stmt);
+         if (gimple_in_ssa_p (cfun))
+           lhs = make_ssa_name (lhs, new_stmt);
           gimple_assign_set_lhs (new_stmt, lhs);
          gimple_set_vuse (new_stmt, gimple_vuse (stmt));
          gimple_set_vdef (new_stmt, gimple_vdef (stmt));
@@ -799,7 +830,7 @@ ssa_propagate (ssa_prop_visit_stmt_fn visit_stmt,
   ssa_prop_init ();
 
   /* Iterate until the worklists are empty.  */
-  while (!cfg_blocks_empty_p () 
+  while (!cfg_blocks_empty_p ()
         || VEC_length (gimple, interesting_ssa_edges) > 0
         || VEC_length (gimple, varying_ssa_edges) > 0)
     {
@@ -866,7 +897,7 @@ static struct prop_stats_d prop_stats;
    PROP_VALUE. Return true if at least one reference was replaced.  */
 
 static bool
-replace_uses_in (gimple stmt, prop_value_t *prop_value)
+replace_uses_in (gimple stmt, ssa_prop_get_value_fn get_value)
 {
   bool replaced = false;
   use_operand_p use;
@@ -875,7 +906,7 @@ replace_uses_in (gimple stmt, prop_value_t *prop_value)
   FOR_EACH_SSA_USE_OPERAND (use, stmt, iter, SSA_OP_USE)
     {
       tree tuse = USE_FROM_PTR (use);
-      tree val = prop_value[SSA_NAME_VERSION (tuse)].value;
+      tree val = (*get_value) (tuse);
 
       if (val == tuse || val == NULL_TREE)
        continue;
@@ -905,7 +936,7 @@ replace_uses_in (gimple stmt, prop_value_t *prop_value)
    values from PROP_VALUE.  */
 
 static void
-replace_phi_args_in (gimple phi, prop_value_t *prop_value)
+replace_phi_args_in (gimple phi, ssa_prop_get_value_fn get_value)
 {
   size_t i;
   bool replaced = false;
@@ -922,7 +953,7 @@ replace_phi_args_in (gimple phi, prop_value_t *prop_value)
 
       if (TREE_CODE (arg) == SSA_NAME)
        {
-         tree val = prop_value[SSA_NAME_VERSION (arg)].value;
+         tree val = (*get_value) (arg);
 
          if (val && val != arg && may_propagate_copy (arg, val))
            {
@@ -943,7 +974,7 @@ replace_phi_args_in (gimple phi, prop_value_t *prop_value)
            }
        }
     }
-  
+
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       if (!replaced)
@@ -967,15 +998,23 @@ replace_phi_args_in (gimple phi, prop_value_t *prop_value)
    If FOLD_FN is non-NULL the function will be invoked on all statements
    before propagating values for pass specific simplification.
 
+   DO_DCE is true if trivially dead stmts can be removed.
+
+   If DO_DCE is true, the statements within a BB are walked from
+   last to first element.  Otherwise we scan from first to last element.
+
    Return TRUE when something changed.  */
 
 bool
-substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
+substitute_and_fold (ssa_prop_get_value_fn get_value_fn,
+                    ssa_prop_fold_stmt_fn fold_fn,
+                    bool do_dce)
 {
   basic_block bb;
   bool something_changed = false;
+  unsigned i;
 
-  if (prop_value == NULL && !fold_fn)
+  if (!get_value_fn && !fold_fn)
     return false;
 
   if (dump_file && (dump_flags & TDF_DETAILS))
@@ -983,24 +1022,83 @@ substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
 
   memset (&prop_stats, 0, sizeof (prop_stats));
 
-  /* Substitute values in every statement of every basic block.  */
+  /* Substitute lattice values at definition sites.  */
+  if (get_value_fn)
+    for (i = 1; i < num_ssa_names; ++i)
+      {
+       tree name = ssa_name (i);
+       tree val;
+       gimple def_stmt;
+       gimple_stmt_iterator gsi;
+
+       if (!name
+           || !is_gimple_reg (name))
+         continue;
+
+       def_stmt = SSA_NAME_DEF_STMT (name);
+       if (gimple_nop_p (def_stmt)
+           /* Do not substitute ASSERT_EXPR rhs, this will confuse VRP.  */
+           || (gimple_assign_single_p (def_stmt)
+               && gimple_assign_rhs_code (def_stmt) == ASSERT_EXPR)
+           || !(val = (*get_value_fn) (name))
+           || !may_propagate_copy (name, val))
+         continue;
+
+       gsi = gsi_for_stmt (def_stmt);
+       if (is_gimple_assign (def_stmt))
+         {
+           gimple_assign_set_rhs_with_ops (&gsi, TREE_CODE (val),
+                                           val, NULL_TREE);
+           gcc_assert (gsi_stmt (gsi) == def_stmt);
+           if (maybe_clean_eh_stmt (def_stmt))
+             gimple_purge_dead_eh_edges (gimple_bb (def_stmt));
+           update_stmt (def_stmt);
+         }
+       else if (is_gimple_call (def_stmt))
+         {
+           if (update_call_from_tree (&gsi, val)
+               && maybe_clean_or_replace_eh_stmt (def_stmt, gsi_stmt (gsi)))
+             gimple_purge_dead_eh_edges (gimple_bb (gsi_stmt (gsi)));
+         }
+       else if (gimple_code (def_stmt) == GIMPLE_PHI)
+         {
+           gimple new_stmt = gimple_build_assign (name, val);
+           gimple_stmt_iterator gsi2;
+           SSA_NAME_DEF_STMT (name) = new_stmt;
+           gsi2 = gsi_after_labels (gimple_bb (def_stmt));
+           gsi_insert_before (&gsi2, new_stmt, GSI_SAME_STMT);
+           remove_phi_node (&gsi, false);
+         }
+
+       something_changed = true;
+      }
+
+  /* Propagate into all uses and fold.  */
   FOR_EACH_BB (bb)
     {
       gimple_stmt_iterator i;
 
       /* Propagate known values into PHI nodes.  */
-      if (prop_value)
+      if (get_value_fn)
        for (i = gsi_start_phis (bb); !gsi_end_p (i); gsi_next (&i))
-         replace_phi_args_in (gsi_stmt (i), prop_value);
+         replace_phi_args_in (gsi_stmt (i), get_value_fn);
 
-      /* Propagate known values into stmts.  Do a backward walk to expose
-        more trivially deletable stmts.  */
-      for (i = gsi_last_bb (bb); !gsi_end_p (i);)
+      /* Propagate known values into stmts.  Do a backward walk if
+         do_dce is true. In some case it exposes
+        more trivially deletable stmts to walk backward.  */
+      for (i = (do_dce ? gsi_last_bb (bb) : gsi_start_bb (bb)); !gsi_end_p (i);)
        {
           bool did_replace;
          gimple stmt = gsi_stmt (i);
          gimple old_stmt;
          enum gimple_code code = gimple_code (stmt);
+         gimple_stmt_iterator oldi;
+
+         oldi = i;
+         if (do_dce)
+           gsi_prev (&i);
+         else
+           gsi_next (&i);
 
          /* Ignore ASSERT_EXPRs.  They are used by VRP to generate
             range information for names and they are discarded
@@ -1008,14 +1106,15 @@ substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
 
          if (code == GIMPLE_ASSIGN
              && TREE_CODE (gimple_assign_rhs1 (stmt)) == ASSERT_EXPR)
-           {
-             gsi_prev (&i);
-             continue;
-           }
+           continue;
 
          /* No point propagating into a stmt whose result is not used,
-            but instead we might be able to remove a trivially dead stmt.  */
-         if (gimple_get_lhs (stmt)
+            but instead we might be able to remove a trivially dead stmt.
+            Don't do this when called from VRP, since the SSA_NAME which
+            is going to be released could be still referenced in VRP
+            ranges.  */
+         if (do_dce
+             && gimple_get_lhs (stmt)
              && TREE_CODE (gimple_get_lhs (stmt)) == SSA_NAME
              && has_zero_uses (gimple_get_lhs (stmt))
              && !stmt_could_throw_p (stmt)
@@ -1030,7 +1129,6 @@ substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
                  fprintf (dump_file, "\n");
                }
              prop_stats.num_dce++;
-             gsi_prev (&i);
              i2 = gsi_for_stmt (stmt);
              gsi_remove (&i2, true);
              release_defs (stmt);
@@ -1052,26 +1150,26 @@ substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
             specific information.  Do this before propagating
             into the stmt to not disturb pass specific information.  */
          if (fold_fn
-             && (*fold_fn)(&i))
+             && (*fold_fn)(&oldi))
            {
              did_replace = true;
              prop_stats.num_stmts_folded++;
+             stmt = gsi_stmt (oldi);
+             update_stmt (stmt);
            }
 
-         /* Only replace real uses if we couldn't fold the
-            statement using value range information.  */
-         if (prop_value
-             && !did_replace)
-           did_replace |= replace_uses_in (stmt, prop_value);
+         /* Replace real uses in the statement.  */
+         if (get_value_fn)
+           did_replace |= replace_uses_in (stmt, get_value_fn);
 
          /* If we made a replacement, fold the statement.  */
          if (did_replace)
-           fold_stmt (&i);
+           fold_stmt (&oldi);
 
          /* Now cleanup.  */
          if (did_replace)
            {
-             stmt = gsi_stmt (i);
+             stmt = gsi_stmt (oldi);
 
               /* If we cleaned up EH information from the statement,
                  remove EH edges.  */
@@ -1083,7 +1181,7 @@ substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
                       == GIMPLE_SINGLE_RHS))
               {
                 tree rhs = gimple_assign_rhs1 (stmt);
-                
+
                 if (TREE_CODE (rhs) == ADDR_EXPR)
                   recompute_tree_invariant_for_addr_expr (rhs);
               }
@@ -1105,8 +1203,6 @@ substitute_and_fold (prop_value_t *prop_value, ssa_prop_fold_stmt_fn fold_fn)
              else
                fprintf (dump_file, "Not folded\n");
            }
-
-         gsi_prev (&i);
        }
     }