[multiple changes]
[gcc.git] / gcc / tree-ssa-dom.c
index fc87c4604c730473f1b518fb5a0c2b873bf91333..d650d95f23e4f8259b9cbbadbe10b5e5da04f4d1 100644 (file)
@@ -1,6 +1,5 @@
 /* SSA Dominator optimizations for trees
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2001-2013 Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
 This file is part of GCC.
@@ -28,12 +27,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "basic-block.h"
 #include "cfgloop.h"
-#include "output.h"
 #include "function.h"
-#include "tree-pretty-print.h"
 #include "gimple-pretty-print.h"
-#include "timevar.h"
-#include "tree-dump.h"
 #include "tree-flow.h"
 #include "domwalk.h"
 #include "tree-pass.h"
@@ -52,7 +47,8 @@ enum expr_kind
   EXPR_UNARY,
   EXPR_BINARY,
   EXPR_TERNARY,
-  EXPR_CALL
+  EXPR_CALL,
+  EXPR_PHI
 };
 
 struct hashable_expr
@@ -64,7 +60,8 @@ struct hashable_expr
     struct { enum tree_code op;  tree opnd; } unary;
     struct { enum tree_code op;  tree opnd0, opnd1; } binary;
     struct { enum tree_code op;  tree opnd0, opnd1, opnd2; } ternary;
-    struct { tree fn; bool pure; size_t nargs; tree *args; } call;
+    struct { gimple fn_from; bool pure; size_t nargs; tree *args; } call;
+    struct { size_t nargs; tree *args; } phi;
   } ops;
 };
 
@@ -77,8 +74,6 @@ typedef struct cond_equivalence_s
   tree value;
 } cond_equivalence;
 
-DEF_VEC_O(cond_equivalence);
-DEF_VEC_ALLOC_O(cond_equivalence,heap);
 
 /* Structure for recording edge equivalences as well as any pending
    edge redirections during the dominator optimizer.
@@ -103,7 +98,7 @@ struct edge_info
 
   /* Traversing an edge may also indicate one or more particular conditions
      are true or false.  */
-  VEC(cond_equivalence, heap) *cond_equivalences;
+  vec<cond_equivalence> cond_equivalences;
 };
 
 /* Hash table with expressions made available during the renaming process.
@@ -121,10 +116,8 @@ static htab_t avail_exprs;
    remove the expressions from the global hash table until we hit the
    marker.  */
 typedef struct expr_hash_elt * expr_hash_elt_t;
-DEF_VEC_P(expr_hash_elt_t);
-DEF_VEC_ALLOC_P(expr_hash_elt_t,heap);
 
-static VEC(expr_hash_elt_t,heap) *avail_exprs_stack;
+static vec<expr_hash_elt_t> avail_exprs_stack;
 
 /* Structure for entries in the expression hash table.  */
 
@@ -151,7 +144,7 @@ struct expr_hash_elt
 
    A NULL entry is used to mark the end of pairs which need to be
    restored during finalization of this block.  */
-static VEC(tree,heap) *const_and_copies_stack;
+static vec<tree> const_and_copies_stack;
 
 /* Track whether or not we have changed the control flow graph.  */
 static bool cfg_altered;
@@ -208,12 +201,11 @@ initialize_hash_element (gimple stmt, tree lhs,
     {
       enum tree_code subcode = gimple_assign_rhs_code (stmt);
 
-      expr->type = NULL_TREE;
-
       switch (get_gimple_rhs_class (subcode))
         {
         case GIMPLE_SINGLE_RHS:
          expr->kind = EXPR_SINGLE;
+         expr->type = TREE_TYPE (gimple_assign_rhs1 (stmt));
          expr->ops.single.rhs = gimple_assign_rhs1 (stmt);
          break;
         case GIMPLE_UNARY_RHS:
@@ -258,7 +250,7 @@ initialize_hash_element (gimple stmt, tree lhs,
 
       expr->type = TREE_TYPE (gimple_call_lhs (stmt));
       expr->kind = EXPR_CALL;
-      expr->ops.call.fn = gimple_call_fn (stmt);
+      expr->ops.call.fn_from = stmt;
 
       if (gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE))
         expr->ops.call.pure = true;
@@ -266,7 +258,7 @@ initialize_hash_element (gimple stmt, tree lhs,
         expr->ops.call.pure = false;
 
       expr->ops.call.nargs = nargs;
-      expr->ops.call.args = (tree *) xcalloc (nargs, sizeof (tree));
+      expr->ops.call.args = XCNEWVEC (tree, nargs);
       for (i = 0; i < nargs; i++)
         expr->ops.call.args[i] = gimple_call_arg (stmt, i);
     }
@@ -282,6 +274,19 @@ initialize_hash_element (gimple stmt, tree lhs,
       expr->kind = EXPR_SINGLE;
       expr->ops.single.rhs = gimple_goto_dest (stmt);
     }
+  else if (code == GIMPLE_PHI)
+    {
+      size_t nargs = gimple_phi_num_args (stmt);
+      size_t i;
+
+      expr->type = TREE_TYPE (gimple_phi_result (stmt));
+      expr->kind = EXPR_PHI;
+      expr->ops.phi.nargs = nargs;
+      expr->ops.phi.args = XCNEWVEC (tree, nargs);
+
+      for (i = 0; i < nargs; i++)
+        expr->ops.phi.args[i] = gimple_phi_arg_def (stmt, i);
+    }
   else
     gcc_unreachable ();
 
@@ -422,8 +427,8 @@ hashable_expr_equal_p (const struct hashable_expr *expr0,
 
         /* If the calls are to different functions, then they
            clearly cannot be equal.  */
-        if (! operand_equal_p (expr0->ops.call.fn,
-                               expr1->ops.call.fn, 0))
+        if (!gimple_call_same_target_p (expr0->ops.call.fn_from,
+                                        expr1->ops.call.fn_from))
           return false;
 
         if (! expr0->ops.call.pure)
@@ -440,6 +445,21 @@ hashable_expr_equal_p (const struct hashable_expr *expr0,
         return true;
       }
 
+    case EXPR_PHI:
+      {
+        size_t i;
+
+        if (expr0->ops.phi.nargs !=  expr1->ops.phi.nargs)
+          return false;
+
+        for (i = 0; i < expr0->ops.phi.nargs; i++)
+          if (! operand_equal_p (expr0->ops.phi.args[i],
+                                 expr1->ops.phi.args[i], 0))
+            return false;
+
+        return true;
+      }
+
     default:
       gcc_unreachable ();
     }
@@ -503,14 +523,29 @@ iterative_hash_hashable_expr (const struct hashable_expr *expr, hashval_t val)
       {
         size_t i;
         enum tree_code code = CALL_EXPR;
+        gimple fn_from;
 
         val = iterative_hash_object (code, val);
-        val = iterative_hash_expr (expr->ops.call.fn, val);
+        fn_from = expr->ops.call.fn_from;
+        if (gimple_call_internal_p (fn_from))
+          val = iterative_hash_hashval_t
+            ((hashval_t) gimple_call_internal_fn (fn_from), val);
+        else
+          val = iterative_hash_expr (gimple_call_fn (fn_from), val);
         for (i = 0; i < expr->ops.call.nargs; i++)
           val = iterative_hash_expr (expr->ops.call.args[i], val);
       }
       break;
 
+    case EXPR_PHI:
+      {
+        size_t i;
+
+        for (i = 0; i < expr->ops.phi.nargs; i++)
+          val = iterative_hash_expr (expr->ops.phi.args[i], val);
+      }
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -565,8 +600,14 @@ print_expr_hash_elt (FILE * stream, const struct expr_hash_elt *element)
         {
           size_t i;
           size_t nargs = element->expr.ops.call.nargs;
-
-          print_generic_expr (stream, element->expr.ops.call.fn, 0);
+          gimple fn_from;
+
+          fn_from = element->expr.ops.call.fn_from;
+          if (gimple_call_internal_p (fn_from))
+            fputs (internal_fn_name (gimple_call_internal_fn (fn_from)),
+                   stream);
+          else
+            print_generic_expr (stream, gimple_call_fn (fn_from), 0);
           fprintf (stream, " (");
           for (i = 0; i < nargs; i++)
             {
@@ -577,6 +618,22 @@ print_expr_hash_elt (FILE * stream, const struct expr_hash_elt *element)
           fprintf (stream, ")");
         }
         break;
+
+      case EXPR_PHI:
+        {
+          size_t i;
+          size_t nargs = element->expr.ops.phi.nargs;
+
+          fprintf (stream, "PHI <");
+          for (i = 0; i < nargs; i++)
+            {
+              print_generic_expr (stream, element->expr.ops.phi.args[i], 0);
+              if (i + 1 < nargs)
+                fprintf (stream, ", ");
+            }
+          fprintf (stream, ">");
+        }
+        break;
     }
   fprintf (stream, "\n");
 
@@ -587,16 +644,24 @@ print_expr_hash_elt (FILE * stream, const struct expr_hash_elt *element)
     }
 }
 
-/* Delete an expr_hash_elt and reclaim its storage.  */
+/* Delete variable sized pieces of the expr_hash_elt ELEMENT.  */
 
 static void
-free_expr_hash_elt (void *elt)
+free_expr_hash_elt_contents (struct expr_hash_elt *element)
 {
-  struct expr_hash_elt *element = ((struct expr_hash_elt *)elt);
-
   if (element->expr.kind == EXPR_CALL)
     free (element->expr.ops.call.args);
+  else if (element->expr.kind == EXPR_PHI)
+    free (element->expr.ops.phi.args);
+}
+
+/* Delete an expr_hash_elt and reclaim its storage.  */
 
+static void
+free_expr_hash_elt (void *elt)
+{
+  struct expr_hash_elt *element = ((struct expr_hash_elt *)elt);
+  free_expr_hash_elt_contents (element);
   free (element);
 }
 
@@ -635,8 +700,7 @@ free_all_edge_infos (void)
 
          if (edge_info)
            {
-             if (edge_info->cond_equivalences)
-               VEC_free (cond_equivalence, heap, edge_info->cond_equivalences);
+             edge_info->cond_equivalences.release ();
              free (edge_info);
              e->aux = NULL;
            }
@@ -659,8 +723,8 @@ tree_ssa_dominator_optimize (void)
 
   /* Create our hash tables.  */
   avail_exprs = htab_create (1024, real_avail_expr_hash, avail_expr_eq, free_expr_hash_elt);
-  avail_exprs_stack = VEC_alloc (expr_hash_elt_t, heap, 20);
-  const_and_copies_stack = VEC_alloc (tree, heap, 20);
+  avail_exprs_stack.create (20);
+  const_and_copies_stack.create (20);
   need_eh_cleanup = BITMAP_ALLOC (NULL);
 
   /* Setup callbacks for the generic dominator tree walker.  */
@@ -701,7 +765,8 @@ tree_ssa_dominator_optimize (void)
     gimple_stmt_iterator gsi;
     basic_block bb;
     FOR_EACH_BB (bb)
-      {for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+      {
+       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
          update_stmt_if_modified (gsi_stmt (gsi));
       }
   }
@@ -730,20 +795,25 @@ tree_ssa_dominator_optimize (void)
 
       /* Jump threading may have created forwarder blocks from blocks
         needing EH cleanup; the new successor of these blocks, which
-        has inherited from the original block, needs the cleanup.  */
+        has inherited from the original block, needs the cleanup.
+        Don't clear bits in the bitmap, as that can break the bitmap
+        iterator.  */
       EXECUTE_IF_SET_IN_BITMAP (need_eh_cleanup, 0, i, bi)
        {
          basic_block bb = BASIC_BLOCK (i);
-         if (single_succ_p (bb) == 1
-             && (single_succ_edge (bb)->flags & EDGE_EH) == 0)
-           {
-             bitmap_clear_bit (need_eh_cleanup, i);
-             bitmap_set_bit (need_eh_cleanup, single_succ (bb)->index);
-           }
+         if (bb == NULL)
+           continue;
+         while (single_succ_p (bb)
+                && (single_succ_edge (bb)->flags & EDGE_EH) == 0)
+           bb = single_succ (bb);
+         if (bb == EXIT_BLOCK_PTR)
+           continue;
+         if ((unsigned) bb->index != i)
+           bitmap_set_bit (need_eh_cleanup, bb->index);
        }
 
       gimple_purge_all_dead_eh_edges (need_eh_cleanup);
-      bitmap_zero (need_eh_cleanup);
+      bitmap_clear (need_eh_cleanup);
     }
 
   statistics_counter_event (cfun, "Redundant expressions eliminated",
@@ -768,12 +838,12 @@ tree_ssa_dominator_optimize (void)
   /* Free asserted bitmaps and stacks.  */
   BITMAP_FREE (need_eh_cleanup);
 
-  VEC_free (expr_hash_elt_t, heap, avail_exprs_stack);
-  VEC_free (tree, heap, const_and_copies_stack);
+  avail_exprs_stack.release ();
+  const_and_copies_stack.release ();
 
   /* Free the value-handle array.  */
   threadedge_finalize_values ();
-  ssa_name_values = NULL;
+  ssa_name_values.release ();
 
   return 0;
 }
@@ -789,6 +859,7 @@ struct gimple_opt_pass pass_dominator =
  {
   GIMPLE_PASS,
   "dom",                               /* name */
+  OPTGROUP_NONE,                        /* optinfo_flags */
   gate_dominator,                      /* gate */
   tree_ssa_dominator_optimize,         /* execute */
   NULL,                                        /* sub */
@@ -802,8 +873,7 @@ struct gimple_opt_pass pass_dominator =
   TODO_cleanup_cfg
     | TODO_update_ssa
     | TODO_verify_ssa
-    | TODO_verify_flow
-    | TODO_dump_func                   /* todo_flags_finish */
+    | TODO_verify_flow                 /* todo_flags_finish */
  }
 };
 
@@ -862,9 +932,9 @@ static void
 remove_local_expressions_from_table (void)
 {
   /* Remove all the expressions made available in this block.  */
-  while (VEC_length (expr_hash_elt_t, avail_exprs_stack) > 0)
+  while (avail_exprs_stack.length () > 0)
     {
-      expr_hash_elt_t victim = VEC_pop (expr_hash_elt_t, avail_exprs_stack);
+      expr_hash_elt_t victim = avail_exprs_stack.pop ();
       void **slot;
 
       if (victim == NULL)
@@ -893,11 +963,11 @@ remove_local_expressions_from_table (void)
 static void
 restore_vars_to_original_value (void)
 {
-  while (VEC_length (tree, const_and_copies_stack) > 0)
+  while (const_and_copies_stack.length () > 0)
     {
       tree prev_value, dest;
 
-      dest = VEC_pop (tree, const_and_copies_stack);
+      dest = const_and_copies_stack.pop ();
 
       if (dest == NULL)
        break;
@@ -911,7 +981,7 @@ restore_vars_to_original_value (void)
          fprintf (dump_file, "\n");
        }
 
-      prev_value = VEC_pop (tree, const_and_copies_stack);
+      prev_value = const_and_copies_stack.pop ();
       set_ssa_name_value (dest, prev_value);
     }
 }
@@ -1065,8 +1135,40 @@ record_equivalences_from_incoming_edge (basic_block bb)
          if (lhs)
            record_equality (lhs, rhs);
 
-         for (i = 0; VEC_iterate (cond_equivalence,
-                                  edge_info->cond_equivalences, i, eq); ++i)
+         /* If LHS is an SSA_NAME and RHS is a constant integer and LHS was
+            set via a widening type conversion, then we may be able to record
+            additional equivalences.  */
+         if (lhs
+             && TREE_CODE (lhs) == SSA_NAME
+             && is_gimple_constant (rhs)
+             && TREE_CODE (rhs) == INTEGER_CST)
+           {
+             gimple defstmt = SSA_NAME_DEF_STMT (lhs);
+
+             if (defstmt
+                 && is_gimple_assign (defstmt)
+                 && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (defstmt)))
+               {
+                 tree old_rhs = gimple_assign_rhs1 (defstmt);
+
+                 /* If the conversion widens the original value and
+                    the constant is in the range of the type of OLD_RHS,
+                    then convert the constant and record the equivalence. 
+
+                    Note that int_fits_type_p does not check the precision
+                    if the upper and lower bounds are OK.  */
+                 if (INTEGRAL_TYPE_P (TREE_TYPE (old_rhs))
+                     && (TYPE_PRECISION (TREE_TYPE (lhs))
+                         > TYPE_PRECISION (TREE_TYPE (old_rhs)))
+                     && int_fits_type_p (rhs, TREE_TYPE (old_rhs)))
+                   {
+                     tree newval = fold_convert (TREE_TYPE (old_rhs), rhs);
+                     record_equality (old_rhs, newval);
+                   }
+               }
+           }
+
+         for (i = 0; edge_info->cond_equivalences.iterate (i, &eq); ++i)
            record_cond (eq);
        }
     }
@@ -1134,10 +1236,10 @@ record_cond (cond_equivalence *p)
           print_expr_hash_elt (dump_file, element);
         }
 
-      VEC_safe_push (expr_hash_elt_t, heap, avail_exprs_stack, element);
+      avail_exprs_stack.safe_push (element);
     }
   else
-    free (element);
+    free_expr_hash_elt (element);
 }
 
 /* Build a cond_equivalence record indicating that the comparison
@@ -1146,7 +1248,7 @@ record_cond (cond_equivalence *p)
 static void
 build_and_record_new_cond (enum tree_code code,
                            tree op0, tree op1,
-                           VEC(cond_equivalence, heap) **p)
+                           vec<cond_equivalence> *p)
 {
   cond_equivalence c;
   struct hashable_expr *cond = &c.cond;
@@ -1160,7 +1262,7 @@ build_and_record_new_cond (enum tree_code code,
   cond->ops.binary.opnd1 = op1;
 
   c.value = boolean_true_node;
-  VEC_safe_push (cond_equivalence, heap, *p, &c);
+  p->safe_push (c);
 }
 
 /* Record that COND is true and INVERTED is false into the edge information
@@ -1267,7 +1369,7 @@ record_conditions (struct edge_info *edge_info, tree cond, tree inverted)
      two slots.  */
   initialize_expr_from_cond (cond, &c.cond);
   c.value = boolean_true_node;
-  VEC_safe_push (cond_equivalence, heap, edge_info->cond_equivalences, &c);
+  edge_info->cond_equivalences.safe_push (c);
 
   /* It is possible for INVERTED to be the negation of a comparison,
      and not a valid RHS or GIMPLE_COND condition.  This happens because
@@ -1276,7 +1378,7 @@ record_conditions (struct edge_info *edge_info, tree cond, tree inverted)
      obey the trichotomy law.  */
   initialize_expr_from_cond (inverted, &c.cond);
   c.value = boolean_false_node;
-  VEC_safe_push (cond_equivalence, heap, edge_info->cond_equivalences, &c);
+  edge_info->cond_equivalences.safe_push (c);
 }
 
 /* A helper function for record_const_or_copy and record_equality.
@@ -1296,9 +1398,9 @@ record_const_or_copy_1 (tree x, tree y, tree prev_x)
       fprintf (dump_file, "\n");
     }
 
-  VEC_reserve (tree, heap, const_and_copies_stack, 2);
-  VEC_quick_push (tree, const_and_copies_stack, prev_x);
-  VEC_quick_push (tree, const_and_copies_stack, x);
+  const_and_copies_stack.reserve (2);
+  const_and_copies_stack.quick_push (prev_x);
+  const_and_copies_stack.quick_push (x);
 }
 
 /* Return the loop depth of the basic block of the defining statement of X.
@@ -1325,7 +1427,7 @@ loop_depth_of_name (tree x)
   if (!defbb)
     return 0;
 
-  return defbb->loop_depth;
+  return bb_loop_depth (defbb);
 }
 
 /* Record that X is equal to Y in const_and_copies.  Record undo
@@ -1397,9 +1499,10 @@ record_equality (tree x, tree y)
    i_1 = phi (..., i_2)
    i_2 = i_1 +/- ...  */
 
-static bool
+bool
 simple_iv_increment_p (gimple stmt)
 {
+  enum tree_code code;
   tree lhs, preinc;
   gimple phi;
   size_t i;
@@ -1411,12 +1514,13 @@ simple_iv_increment_p (gimple stmt)
   if (TREE_CODE (lhs) != SSA_NAME)
     return false;
 
-  if (gimple_assign_rhs_code (stmt) != PLUS_EXPR
-      && gimple_assign_rhs_code (stmt) != MINUS_EXPR)
+  code = gimple_assign_rhs_code (stmt);
+  if (code != PLUS_EXPR
+      && code != MINUS_EXPR
+      && code != POINTER_PLUS_EXPR)
     return false;
 
   preinc = gimple_assign_rhs1 (stmt);
-
   if (TREE_CODE (preinc) != SSA_NAME)
     return false;
 
@@ -1596,12 +1700,15 @@ record_edge_info (basic_block bb)
             {
               tree cond = build2 (code, boolean_type_node, op0, op1);
               tree inverted = invert_truthvalue_loc (loc, cond);
+              bool can_infer_simple_equiv
+                = !(HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (op0)))
+                    && real_zerop (op0));
               struct edge_info *edge_info;
 
               edge_info = allocate_edge_info (true_edge);
               record_conditions (edge_info, cond, inverted);
 
-              if (code == EQ_EXPR)
+              if (can_infer_simple_equiv && code == EQ_EXPR)
                 {
                   edge_info->lhs = op1;
                   edge_info->rhs = op0;
@@ -1610,7 +1717,7 @@ record_edge_info (basic_block bb)
               edge_info = allocate_edge_info (false_edge);
               record_conditions (edge_info, inverted, cond);
 
-              if (TREE_CODE (inverted) == EQ_EXPR)
+              if (can_infer_simple_equiv && TREE_CODE (inverted) == EQ_EXPR)
                 {
                   edge_info->lhs = op1;
                   edge_info->rhs = op0;
@@ -1618,17 +1725,20 @@ record_edge_info (basic_block bb)
             }
 
           else if (TREE_CODE (op0) == SSA_NAME
-                   && (is_gimple_min_invariant (op1)
-                       || TREE_CODE (op1) == SSA_NAME))
+                   && (TREE_CODE (op1) == SSA_NAME
+                       || is_gimple_min_invariant (op1)))
             {
               tree cond = build2 (code, boolean_type_node, op0, op1);
               tree inverted = invert_truthvalue_loc (loc, cond);
+              bool can_infer_simple_equiv
+                = !(HONOR_SIGNED_ZEROS (TYPE_MODE (TREE_TYPE (op1)))
+                    && (TREE_CODE (op1) == SSA_NAME || real_zerop (op1)));
               struct edge_info *edge_info;
 
               edge_info = allocate_edge_info (true_edge);
               record_conditions (edge_info, cond, inverted);
 
-              if (code == EQ_EXPR)
+              if (can_infer_simple_equiv && code == EQ_EXPR)
                 {
                   edge_info->lhs = op0;
                   edge_info->rhs = op1;
@@ -1637,7 +1747,7 @@ record_edge_info (basic_block bb)
               edge_info = allocate_edge_info (false_edge);
               record_conditions (edge_info, inverted, cond);
 
-              if (TREE_CODE (inverted) == EQ_EXPR)
+              if (can_infer_simple_equiv && TREE_CODE (inverted) == EQ_EXPR)
                 {
                   edge_info->lhs = op0;
                   edge_info->rhs = op1;
@@ -1660,14 +1770,22 @@ dom_opt_enter_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
 
   /* Push a marker on the stacks of local information so that we know how
      far to unwind when we finalize this block.  */
-  VEC_safe_push (expr_hash_elt_t, heap, avail_exprs_stack, NULL);
-  VEC_safe_push (tree, heap, const_and_copies_stack, NULL_TREE);
+  avail_exprs_stack.safe_push (NULL);
+  const_and_copies_stack.safe_push (NULL_TREE);
 
   record_equivalences_from_incoming_edge (bb);
 
   /* PHI nodes can create equivalences too.  */
   record_equivalences_from_phis (bb);
 
+  /* Create equivalences from redundant PHIs.  PHIs are only truly
+     redundant when they exist in the same block, so push another
+     marker and unwind right afterwards.  */
+  avail_exprs_stack.safe_push (NULL);
+  for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+    eliminate_redundant_computations (&gsi);
+  remove_local_expressions_from_table ();
+
   for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     optimize_stmt (bb, gsi);
 
@@ -1693,6 +1811,9 @@ dom_opt_leave_block (struct dom_walk_data *walk_data, basic_block bb)
       && (single_succ_edge (bb)->flags & EDGE_ABNORMAL) == 0
       && potentially_threadable_block (single_succ (bb)))
     {
+      /* Push a marker on the stack, which thread_across_edge expects
+        and will remove.  */
+      const_and_copies_stack.safe_push (NULL_TREE);
       dom_thread_across_edge (walk_data, single_succ_edge (bb));
     }
   else if ((last = last_stmt (bb))
@@ -1715,8 +1836,8 @@ dom_opt_leave_block (struct dom_walk_data *walk_data, basic_block bb)
          /* Push a marker onto the available expression stack so that we
             unwind any expressions related to the TRUE arm before processing
             the false arm below.  */
-          VEC_safe_push (expr_hash_elt_t, heap, avail_exprs_stack, NULL);
-         VEC_safe_push (tree, heap, const_and_copies_stack, NULL_TREE);
+          avail_exprs_stack.safe_push (NULL);
+         const_and_copies_stack.safe_push (NULL_TREE);
 
          edge_info = (struct edge_info *) true_edge->aux;
 
@@ -1734,8 +1855,7 @@ dom_opt_leave_block (struct dom_walk_data *walk_data, basic_block bb)
 
              /* If we have 0 = COND or 1 = COND equivalences, record them
                 into our expression hash tables.  */
-             for (i = 0; VEC_iterate (cond_equivalence,
-                                      edge_info->cond_equivalences, i, eq); ++i)
+             for (i = 0; edge_info->cond_equivalences.iterate (i, &eq); ++i)
                record_cond (eq);
            }
 
@@ -1752,7 +1872,7 @@ dom_opt_leave_block (struct dom_walk_data *walk_data, basic_block bb)
          struct edge_info *edge_info;
          unsigned int i;
 
-         VEC_safe_push (tree, heap, const_and_copies_stack, NULL_TREE);
+         const_and_copies_stack.safe_push (NULL_TREE);
          edge_info = (struct edge_info *) false_edge->aux;
 
          /* If we have info associated with this edge, record it into
@@ -1769,8 +1889,7 @@ dom_opt_leave_block (struct dom_walk_data *walk_data, basic_block bb)
 
              /* If we have 0 = COND or 1 = COND equivalences, record them
                 into our expression hash tables.  */
-             for (i = 0; VEC_iterate (cond_equivalence,
-                                      edge_info->cond_equivalences, i, eq); ++i)
+             for (i = 0; edge_info->cond_equivalences.iterate (i, &eq); ++i)
                record_cond (eq);
            }
 
@@ -1798,12 +1917,16 @@ eliminate_redundant_computations (gimple_stmt_iterator* gsi)
 {
   tree expr_type;
   tree cached_lhs;
+  tree def;
   bool insert = true;
   bool assigns_var_p = false;
 
   gimple stmt = gsi_stmt (*gsi);
 
-  tree def = gimple_get_lhs (stmt);
+  if (gimple_code (stmt) == GIMPLE_PHI)
+    def = gimple_phi_result (stmt);
+  else
+    def = gimple_get_lhs (stmt);
 
   /* Certain expressions on the RHS can be optimized away, but can not
      themselves be entered into the hash tables.  */
@@ -1837,6 +1960,16 @@ eliminate_redundant_computations (gimple_stmt_iterator* gsi)
     }
   else if (gimple_code (stmt) == GIMPLE_SWITCH)
     expr_type = TREE_TYPE (gimple_switch_index (stmt));
+  else if (gimple_code (stmt) == GIMPLE_PHI)
+    /* We can't propagate into a phi, so the logic below doesn't apply.
+       Instead record an equivalence between the cached LHS and the
+       PHI result of this statement, provided they are in the same block.
+       This should be sufficient to kill the redundant phi.  */
+    {
+      if (def && cached_lhs)
+       record_const_or_copy (def, cached_lhs);
+      return;
+    }
   else
     gcc_unreachable ();
 
@@ -1981,17 +2114,6 @@ cprop_operand (gimple stmt, use_operand_p op_p)
   val = SSA_NAME_VALUE (op);
   if (val && val != op)
     {
-      /* Do not change the base variable in the virtual operand
-        tables.  That would make it impossible to reconstruct
-        the renamed virtual operand if we later modify this
-        statement.  Also only allow the new value to be an SSA_NAME
-        for propagation into virtual operands.  */
-      if (!is_gimple_reg (op)
-         && (TREE_CODE (val) != SSA_NAME
-             || is_gimple_reg (val)
-             || get_virtual_var (val) != get_virtual_var (op)))
-       return;
-
       /* Do not replace hard register operands in asm statements.  */
       if (gimple_code (stmt) == GIMPLE_ASM
          && !may_propagate_copy_into_asm (op))
@@ -2062,11 +2184,8 @@ cprop_into_stmt (gimple stmt)
   use_operand_p op_p;
   ssa_op_iter iter;
 
-  FOR_EACH_SSA_USE_OPERAND (op_p, stmt, iter, SSA_OP_ALL_USES)
-    {
-      if (TREE_CODE (USE_FROM_PTR (op_p)) == SSA_NAME)
-       cprop_operand (stmt, op_p);
-    }
+  FOR_EACH_SSA_USE_OPERAND (op_p, stmt, iter, SSA_OP_USE)
+    cprop_operand (stmt, op_p);
 }
 
 /* Optimize the statement pointed to by iterator SI.
@@ -2093,18 +2212,18 @@ optimize_stmt (basic_block bb, gimple_stmt_iterator si)
 
   old_stmt = stmt = gsi_stmt (si);
 
-  if (gimple_code (stmt) == GIMPLE_COND)
-    canonicalize_comparison (stmt);
-
-  update_stmt_if_modified (stmt);
-  opt_stats.num_stmts++;
-
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       fprintf (dump_file, "Optimizing statement ");
       print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
     }
 
+  if (gimple_code (stmt) == GIMPLE_COND)
+    canonicalize_comparison (stmt);
+
+  update_stmt_if_modified (stmt);
+  opt_stats.num_stmts++;
+
   /* Const/copy propagate into USES, VUSES and the RHS of VDEFs.  */
   cprop_into_stmt (stmt);
 
@@ -2148,12 +2267,10 @@ optimize_stmt (basic_block bb, gimple_stmt_iterator si)
 
   /* Check for redundant computations.  Do this optimization only
      for assignments that have no volatile ops and conditionals.  */
-  may_optimize_p = (!gimple_has_volatile_ops (stmt)
-                    && ((is_gimple_assign (stmt)
-                         && !gimple_rhs_has_side_effects (stmt))
+  may_optimize_p = (!gimple_has_side_effects (stmt)
+                    && (is_gimple_assign (stmt)
                         || (is_gimple_call (stmt)
-                            && gimple_call_lhs (stmt) != NULL_TREE
-                            && !gimple_rhs_has_side_effects (stmt))
+                            && gimple_call_lhs (stmt) != NULL_TREE)
                         || gimple_code (stmt) == GIMPLE_COND
                         || gimple_code (stmt) == GIMPLE_SWITCH));
 
@@ -2207,15 +2324,14 @@ optimize_stmt (basic_block bb, gimple_stmt_iterator si)
              && rhs == cached_lhs)
            {
              basic_block bb = gimple_bb (stmt);
-             int lp_nr = lookup_stmt_eh_lp (stmt);
              unlink_stmt_vdef (stmt);
-             gsi_remove (&si, true);
-             if (lp_nr != 0)
+             if (gsi_remove (&si, true))
                {
                  bitmap_set_bit (need_eh_cleanup, bb->index);
                  if (dump_file && (dump_flags & TDF_DETAILS))
                    fprintf (dump_file, "  Flagged to clear EH edges.\n");
                }
+             release_defs (stmt);
              return;
            }
        }
@@ -2293,8 +2409,11 @@ lookup_avail_expr (gimple stmt, bool insert)
   tree temp;
   struct expr_hash_elt element;
 
-  /* Get LHS of assignment or call, else NULL_TREE.  */
-  lhs = gimple_get_lhs (stmt);
+  /* Get LHS of phi, assignment, or call; else NULL_TREE.  */
+  if (gimple_code (stmt) == GIMPLE_PHI)
+    lhs = gimple_phi_result (stmt);
+  else
+    lhs = gimple_get_lhs (stmt);
 
   initialize_hash_element (stmt, lhs, &element);
 
@@ -2316,9 +2435,11 @@ lookup_avail_expr (gimple stmt, bool insert)
   slot = htab_find_slot_with_hash (avail_exprs, &element, element.hash,
                                   (insert ? INSERT : NO_INSERT));
   if (slot == NULL)
-    return NULL_TREE;
-
-  if (*slot == NULL)
+    {
+      free_expr_hash_elt_contents (&element);
+      return NULL_TREE;
+    }
+  else if (*slot == NULL)
     {
       struct expr_hash_elt *element2 = XNEW (struct expr_hash_elt);
       *element2 = element;
@@ -2331,9 +2452,11 @@ lookup_avail_expr (gimple stmt, bool insert)
           print_expr_hash_elt (dump_file, element2);
         }
 
-      VEC_safe_push (expr_hash_elt_t, heap, avail_exprs_stack, element2);
+      avail_exprs_stack.safe_push (element2);
       return NULL_TREE;
     }
+  else
+    free_expr_hash_elt_contents (&element);
 
   /* Extract the LHS of the assignment so that it can be used as the current
      definition of another variable.  */
@@ -2602,18 +2725,13 @@ propagate_rhs_into_lhs (gimple stmt, tree lhs, tree rhs, bitmap interesting_name
          /* Special cases to avoid useless calls into the folding
             routines, operand scanning, etc.
 
-            First, propagation into a PHI may cause the PHI to become
+            Propagation into a PHI may cause the PHI to become
             a degenerate, so mark the PHI as interesting.  No other
-            actions are necessary.
-
-            Second, if we're propagating a virtual operand and the
-            propagation does not change the underlying _DECL node for
-            the virtual operand, then no further actions are necessary.  */
-         if (gimple_code (use_stmt) == GIMPLE_PHI
-             || (! is_gimple_reg (lhs)
-                 && TREE_CODE (rhs) == SSA_NAME
-                 && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (rhs)))
+            actions are necessary.  */
+         if (gimple_code (use_stmt) == GIMPLE_PHI)
            {
+             tree result;
+
              /* Dump details.  */
              if (dump_file && (dump_flags & TDF_DETAILS))
                {
@@ -2621,14 +2739,8 @@ propagate_rhs_into_lhs (gimple stmt, tree lhs, tree rhs, bitmap interesting_name
                  print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
                }
 
-             /* Propagation into a PHI may expose new degenerate PHIs,
-                so mark the result of the PHI as interesting.  */
-             if (gimple_code (use_stmt) == GIMPLE_PHI)
-               {
-                 tree result = get_lhs_or_phi_result (use_stmt);
-                 bitmap_set_bit (interesting_names, SSA_NAME_VERSION (result));
-               }
-
+             result = get_lhs_or_phi_result (use_stmt);
+             bitmap_set_bit (interesting_names, SSA_NAME_VERSION (result));
              continue;
            }
 
@@ -2642,7 +2754,10 @@ propagate_rhs_into_lhs (gimple stmt, tree lhs, tree rhs, bitmap interesting_name
              GIMPLE_ASSIGN, and there is no way to effect such a
              transformation in-place.  We might want to consider
              using the more general fold_stmt here.  */
-         fold_stmt_inplace (use_stmt);
+           {
+             gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
+             fold_stmt_inplace (&gsi);
+           }
 
          /* Sometimes propagation can expose new operands to the
             renamer.  */
@@ -2924,7 +3039,12 @@ eliminate_degenerate_phis (void)
     }
 
   if (cfg_altered)
-    free_dominance_info (CDI_DOMINATORS);
+    {
+      free_dominance_info (CDI_DOMINATORS);
+      /* If we changed the CFG schedule loops for fixup by cfgcleanup.  */
+      if (current_loops)
+       loops_state_set (LOOPS_NEED_FIXUP);
+    }
 
   /* Propagation of const and copies may make some EH edges dead.  Purge
      such edges from the CFG as needed.  */
@@ -2944,6 +3064,7 @@ struct gimple_opt_pass pass_phi_only_cprop =
  {
   GIMPLE_PASS,
   "phicprop",                           /* name */
+  OPTGROUP_NONE,                        /* optinfo_flags */
   gate_dominator,                       /* gate */
   eliminate_degenerate_phis,            /* execute */
   NULL,                                 /* sub */
@@ -2955,8 +3076,6 @@ struct gimple_opt_pass pass_phi_only_cprop =
   0,                                   /* properties_destroyed */
   0,                                    /* todo_flags_start */
   TODO_cleanup_cfg
-    | TODO_dump_func
-    | TODO_ggc_collect
     | TODO_verify_ssa
     | TODO_verify_stmts
     | TODO_update_ssa                  /* todo_flags_finish */