diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-95.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-95.c
new file mode 100644
index 00000000000..9ba1c8c61ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-95.c
@@ -0,0 +1,80 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fre1" } */
+
+extern void bar(void);
+extern void bag(void);
+extern void boo(void);
+extern void bas(void);
+extern void foo(void);
+
+void f (unsigned int a, unsigned int b)
+{
+  if (a == b)
+    {
+      for (unsigned i = 0; i < a; i++)
+	{
+	  bar ();
+	  if (i >= b)
+	    /* Unreachable, cannot be eliminated by VRP. FRE can eliminate this.
+	     */
+	    foo ();
+	}
+    }
+}
+
+void g (unsigned int a, unsigned int b)
+{
+  for (unsigned i = 0; i < a; i++)
+    {
+      if (a == b)
+	{
+	  if (i >= b)
+	    /* Unreachable, cannot be eliminated by VRP. FRE can eliminate this.
+	     */
+	    foo ();
+	}
+	bag ();
+    }
+}
+
+void h (unsigned int a, unsigned int b, unsigned int c)
+{
+  for (unsigned i = 0; i < c; i++)
+    {
+      if (a == b)
+	{
+	  if (b == c)
+	    {
+	      boo ();
+	      if (i >= a)
+		/* Unreachable.   */
+		foo ();
+	    }
+	}
+    }
+}
+
+void k (int a, int b, int x, int y)
+{
+  int c = y;
+  if (a != 0)
+    c = x;
+  while (b < 1000)
+    {
+      if (a != 0)
+	{
+	  if (c > x)
+	    /* Unreachable. */
+	    foo ();
+	}
+      else
+	bas ();
+      b++;
+    }
+}
+
+/* { dg-final { scan-tree-dump-not "foo" "fre1" } } */
+/* { dg-final { scan-tree-dump "bag" "fre1" } } */
+/* { dg-final { scan-tree-dump "bar" "fre1" } } */
+/* { dg-final { scan-tree-dump "boo" "fre1" } } */
+/* { dg-final { scan-tree-dump "bas" "fre1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-96.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-96.c
new file mode 100644
index 00000000000..8e3a133f1ea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-96.c
@@ -0,0 +1,66 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-fre1" } */
+
+extern void bar(void);
+extern void bag(void);
+extern void foo(void);
+
+void f (unsigned int a, unsigned int b)
+{
+  if (a == b)
+    {
+      for (unsigned i = a; i < 100; i++)
+	{
+	  if (i > b)
+	    /* Should not be eliminated.
+	     */
+	    bar ();
+	}
+    }
+}
+
+void g (unsigned int a, unsigned int b)
+{
+  unsigned int c = 100;
+  if (a != 0)
+    {
+      c = b;
+    }
+  for (unsigned i = 0; i < c; i++)
+    {
+      if (a != 0)
+	{
+	  if (i >= b)
+	    /* Should be eliminated.
+	     */
+	    foo ();
+	}
+      i++;
+    }
+}
+
+void h (unsigned int a, unsigned int b)
+{
+  unsigned int c = 100;
+  if (b % 2)
+    {
+      if (a != 0)
+	{
+	  c = b;
+	}
+    }
+  for (unsigned i = 0; i < c; i++)
+    {
+      if (a != 0)
+	{
+	  if (i >= b)
+	    /* Should not be eliminated.
+	     */
+	    bag ();
+	}
+      i++;
+    }
+}
+/* { dg-final { scan-tree-dump "bar" "fre1" } } */
+/* { dg-final { scan-tree-dump "bag" "fre1" } } */
+/* { dg-final { scan-tree-dump-not "foo" "fre1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c
index bafb65a53d6..bea2f1b0de5 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1" } */
+/* { dg-options "-O2 -fdisable-tree-evrp -fdisable-tree-fre1 -fdisable-tree-fre2 -fdisable-tree-fre3 -fdump-tree-vrp1" } */
 
 struct A
 {
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 64e3a707f5c..bcc3ea8ec17 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -3781,6 +3781,366 @@ vn_reference_insert_pieces (tree vuse, alias_set_type set,
   return vr1;
 }
 
+static inline bool
+vn_tracking_edge (edge pred_e)
+{
+  if (! single_pred_p (pred_e->dest))
+    {
+      /* Never record for backedges.  */
+      if (pred_e->flags & EDGE_DFS_BACK)
+	return false;
+      edge_iterator ei;
+      edge e;
+      int cnt = 0;
+      /* Ignore backedges.  */
+      FOR_EACH_EDGE (e, ei, pred_e->dest->preds)
+	if (! dominated_by_p (CDI_DOMINATORS, e->src, e->dest))
+	  cnt++;
+      if (cnt != 1)
+	return false;
+    }
+    return true;
+}
+
+static bool
+dominated_by_p_w_unex (basic_block bb1, basic_block bb2, bool);
+
+static tree
+vn_nary_op_get_predicated_value (vn_nary_op_t vno, basic_block bb)
+{
+  if (! vno->predicated_values)
+    return vno->u.result;
+  for (vn_pval *val = vno->u.values; val; val = val->next)
+    for (unsigned i = 0; i < val->n; ++i)
+      /* Do not handle backedge executability optimistically since
+	 when figuring out whether to iterate we do not consider
+	 changed predication.  */
+      if (dominated_by_p_w_unex
+	    (bb, BASIC_BLOCK_FOR_FN (cfun, val->valid_dominated_by_p[i]),
+	     false))
+	return val->result;
+  return NULL_TREE;
+}
+
+/* hashtable & helpers to record equivalences at given bb.  */
+
+typedef struct val_equiv_s
+{
+  val_equiv_s *next;
+  val_equiv_s *unwind_to;
+  hashval_t hashcode;
+  /* SSA name this val_equiv_s is associated with.  */
+  tree name;
+  /* RESULT in a vn_pval entry is SSA name of a equivalence.  */
+  vn_pval *values;
+} * val_equiv_t;
+
+struct val_equiv_hasher : nofree_ptr_hash<val_equiv_s>
+{
+  static inline hashval_t hash (const val_equiv_s *entry)
+  {
+    return entry->hashcode;
+  }
+  static inline bool equal (const val_equiv_t &entry, const val_equiv_t &other)
+  {
+    return other->name == entry->name;
+  }
+};
+
+static hash_table<val_equiv_hasher> *val_equiv_hash;
+typedef hash_table<val_equiv_hasher>::iterator val_equiv_iterator_type;
+
+static val_equiv_t last_inserted_equiv;
+
+static hashval_t
+compute_single_op_hash (tree name)
+{
+  inchash::hash hstate;
+  inchash::add_expr (name, hstate);
+  return hstate.end ();
+}
+
+static inline bool
+is_vn_qualified_at_bb (vn_pval *val, basic_block bb)
+{
+  for (unsigned i = 0; i < val->n; ++i)
+    {
+      basic_block val_bb
+	= BASIC_BLOCK_FOR_FN (cfun, val->valid_dominated_by_p[i]);
+      if (dominated_by_p (CDI_DOMINATORS, bb, val_bb))
+	{
+	  return true;
+	}
+    }
+  return false;
+}
+
+static val_equiv_t
+val_equiv_insert (tree name, tree equiv, basic_block bb, bool recursive)
+{
+  if (TREE_CODE (name) == SSA_NAME)
+    name = VN_INFO (name)->valnum;
+  if (TREE_CODE (equiv) == SSA_NAME)
+    equiv = VN_INFO (equiv)->valnum;
+  if (name == equiv)
+    return NULL;
+  val_equiv_t new_equiv
+    = (val_equiv_t) obstack_alloc (&vn_tables_obstack, sizeof (val_equiv_s));
+  new_equiv->name = name;
+  new_equiv->values
+    = (vn_pval *) obstack_alloc (&vn_tables_obstack, sizeof (vn_pval));
+  new_equiv->values->next = NULL;
+  new_equiv->values->n = 1;
+  new_equiv->values->valid_dominated_by_p[0] = bb->index;
+  new_equiv->values->result = equiv;
+  new_equiv->hashcode = compute_single_op_hash (name);
+  val_equiv_t *slot
+    = val_equiv_hash->find_slot_with_hash (new_equiv, new_equiv->hashcode,
+					   INSERT);
+  new_equiv->unwind_to = *slot;
+  auto_vec<tree> todo_list;
+
+  if (*slot)
+    {
+      bool found = false;
+      vn_pval *nval = new_equiv->values;
+      vn_pval **next = &(new_equiv->values);
+      for (vn_pval *val = (*slot)->values; val; val = val->next)
+	{
+	  if (val->result == equiv)
+	    {
+	      found = true;
+	      if (is_vn_qualified_at_bb (val, bb))
+		return *slot;
+	      /* Append value.  */
+	      *next = (vn_pval *) obstack_alloc (&vn_tables_obstack,
+						 sizeof (vn_pval)
+						   + val->n * sizeof (int));
+	      (*next)->next = NULL;
+	      (*next)->result = val->result;
+	      (*next)->n = val->n + 1;
+	      memcpy ((*next)->valid_dominated_by_p, val->valid_dominated_by_p,
+		      val->n * sizeof (int));
+	      (*next)->valid_dominated_by_p[val->n] = bb->index;
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		fprintf (dump_file, "Appending equivalence.\n");
+	      next = &(*next)->next;
+	      continue;
+	    }
+	  /* Copy others.  */
+	  unsigned val_size = sizeof (vn_pval) + (val->n - 1) * sizeof (int);
+	  *next = (vn_pval *) obstack_alloc (&vn_tables_obstack, val_size);
+	  memcpy (*next, val, val_size);
+	  (*next)->next = NULL;
+	  next = &(*next)->next;
+	  /* Record transitive equivalences.  */
+	  if (recursive && is_vn_qualified_at_bb (val, bb))
+	      todo_list.safe_push (val->result);
+	}
+      if (!found)
+	// append new equiv at last
+	*next = nval;
+    }
+
+  *slot = new_equiv;
+  new_equiv->next = last_inserted_equiv;
+  last_inserted_equiv = new_equiv;
+  if (recursive)
+    for (unsigned i = 0; i < todo_list.length (); ++i)
+      {
+	val_equiv_insert (todo_list[i], equiv, bb, false);
+	val_equiv_insert (equiv, todo_list[i], bb, false);
+      }
+  return new_equiv;
+}
+
+static tree
+vn_lookup_cond_result (tree lhs, tree rhs, tree_code code, basic_block bb)
+{
+  // Shortcut for gimple_simplify, as it can get confused by the ~X == 1
+  // -> X == 0 transform.
+  if (lhs == rhs)
+    switch (code)
+      {
+      case EQ_EXPR:
+      case LE_EXPR:
+      case GE_EXPR:
+	return boolean_true_node;
+      case LT_EXPR:
+      case GT_EXPR:
+      case NE_EXPR:
+	return boolean_false_node;
+      default:;
+      }
+  tree val;
+  tree ops[2];
+  ops[0] = lhs;
+  ops[1] = rhs;
+  vn_nary_op_t vno;
+  val = vn_nary_op_lookup_pieces (2, code, boolean_type_node, ops, &vno);
+  /* Did we get a predicated value?  */
+  if (!val && vno && vno->predicated_values)
+    val = vn_nary_op_get_predicated_value (vno, bb);
+  return val;
+}
+
+static tree
+find_predicated_value_by_equiv (tree lhs, tree rhs, tree_code code,
+				basic_block bb)
+{
+  if (TREE_CODE (lhs) == SSA_NAME)
+    lhs = VN_INFO (lhs)->valnum;
+  struct val_equiv_s equiv;
+  tree result;
+  equiv.name = lhs;
+  equiv.hashcode = compute_single_op_hash (lhs);
+  val_equiv_t *slot
+    = val_equiv_hash->find_slot_with_hash (&equiv, equiv.hashcode, NO_INSERT);
+  auto_vec<tree> lhs_equivs;
+  // Search by lhs's equivalences.
+  if (slot)
+    {
+      for (vn_pval *val = (*slot)->values; val; val = val->next)
+	{
+	  if (!is_vn_qualified_at_bb (val, bb))
+	    continue;
+	  if ((result = vn_lookup_cond_result (val->result, rhs, code, bb)))
+	    return result;
+	  // Store equivalence to list, in case we need to search by rhs.
+	  lhs_equivs.safe_push (val->result);
+	}
+    }
+  // Search by rhs's equivalences.
+  if (TREE_CODE (rhs) == SSA_NAME)
+    rhs = VN_INFO (rhs)->valnum;
+  equiv.name = rhs;
+  equiv.hashcode = compute_single_op_hash (rhs);
+  slot
+    = val_equiv_hash->find_slot_with_hash (&equiv, equiv.hashcode, NO_INSERT);
+  if (!slot)
+    return NULL_TREE;
+  for (vn_pval *val = (*slot)->values; val; val = val->next)
+    {
+      if (!is_vn_qualified_at_bb (val, bb))
+	continue;
+      if ((result = vn_lookup_cond_result (lhs, val->result, code, bb)))
+	return result;
+      // Search by both sides's equivalences.
+      for (unsigned j = 0; j < lhs_equivs.length (); ++j)
+	if ((result
+	     = vn_lookup_cond_result (lhs_equivs[j], val->result, code, bb)))
+	  return result;
+    }
+  return NULL_TREE;
+}
+
+/* Record equivalence generated by PHI node, on current visiting edge's
+ * destination.  */
+
+static void
+record_equiv_from_previous_edge (basic_block pred, basic_block dest, edge e)
+{
+  if (!vn_tracking_edge (e))
+    return;
+  while (single_succ_p (dest) && single_pred_p (dest))
+    {
+      pred = dest;
+      dest = single_succ (dest);
+    }
+  if (single_pred_p (dest))
+    return;
+  /* Currently situations like this are not handled:
+	  pred
+	  /  \
+	 /t   \f
+	B       C
+	 \    /  \
+	  \  /    \
+      D (PHI node)  E
+	    |
+	    |
+	  current
+	   / \
+	  /t  \f
+	   ...
+  */
+  for (gphi_iterator gsi = gsi_start_nonvirtual_phis (dest); !gsi_end_p (gsi);
+       gsi_next_nonvirtual_phi (&gsi))
+    {
+      gphi *phi = gsi.phi ();
+      tree res = vn_valueize (PHI_RESULT (phi));
+      for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+	{
+	  edge in = gimple_phi_arg_edge (phi, i);
+	  if (in->src == pred)
+	    {
+	      tree arg = vn_valueize (gimple_phi_arg_def (phi, i));
+	      if (arg == res)
+	      	continue;
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  fprintf (dump_file, "Recording equivalence of ");
+		  print_generic_expr (dump_file, res);
+		  fprintf (dump_file, " and ");
+		  print_generic_expr (dump_file, arg);
+		  fprintf (dump_file, " at BB%d\n", e->dest->index);
+		}
+	      val_equiv_insert (res, arg, e->dest, true);
+	      val_equiv_insert (arg, res, e->dest, true);
+	    }
+	}
+    }
+}
+
+static void
+record_equiv_from_previous_cond (vn_nary_op_t vno, edge true_e, edge false_e,
+				 basic_block bb)
+{
+  if (!vno->predicated_values)
+    return;
+  gimple *pred_last;
+  for (vn_pval *val = vno->u.values; val; val = val->next)
+    for (unsigned i = 0; i < val->n; ++i)
+      {
+	basic_block bb1
+	  = BASIC_BLOCK_FOR_FN (cfun, val->valid_dominated_by_p[i]);
+	if (!single_pred_p (bb1))
+	  continue;
+	basic_block p = single_pred (bb1);
+	if (p && dominated_by_p_w_unex (bb, p, false))
+	  {
+	    // Check whether there's an edge "dominates" bb's true/false edge.
+	    pred_last = last_stmt (p);
+	    if (!pred_last || gimple_code (pred_last) != GIMPLE_COND)
+	      continue;
+	    enum tree_code pred_code = gimple_cond_code (pred_last);
+	    enum tree_code pred_icode = swap_tree_comparison (pred_code);
+	    // For now only identical conditions are handled.
+	    // TODO: even if the conditions are not the same, we still have
+	    // one edge that can "derive" one of the current edges.
+	    if (pred_code != vno->opcode && pred_icode != vno->opcode)
+	      continue;
+	    // Find out previous true & false destinies (here "true" and "false"
+	    // are corresponding to current bb.
+	    // First initialize them with bb1 and "p's successor that is not
+	    // bb1" respectively, then switch them if result in VNO is false.
+	    basic_block pre_true_dest = bb1;
+	    basic_block pre_false_dest = EDGE_SUCC (p, 0)->dest != bb1
+					   ? EDGE_SUCC (p, 0)->dest
+					   : EDGE_SUCC (p, 1)->dest;
+	    if (val->result == boolean_false_node)
+	      {
+		pre_true_dest = pre_false_dest;
+		pre_false_dest = bb1;
+	      }
+	    if (true_e)
+	      record_equiv_from_previous_edge (p, pre_true_dest, true_e);
+	    if (false_e)
+	      record_equiv_from_previous_edge (p, pre_false_dest, false_e);
+	  }
+      }
+}
+
 /* Compute and return the hash value for nary operation VBO1.  */
 
 static hashval_t
@@ -4155,21 +4515,8 @@ vn_nary_op_insert_pieces_predicated (unsigned int length, enum tree_code code,
 				     edge pred_e)
 {
   /* ???  Currently tracking BBs.  */
-  if (! single_pred_p (pred_e->dest))
-    {
-      /* Never record for backedges.  */
-      if (pred_e->flags & EDGE_DFS_BACK)
-	return NULL;
-      edge_iterator ei;
-      edge e;
-      int cnt = 0;
-      /* Ignore backedges.  */
-      FOR_EACH_EDGE (e, ei, pred_e->dest->preds)
-	if (! dominated_by_p (CDI_DOMINATORS, e->src, e->dest))
-	  cnt++;
-      if (cnt != 1)
+  if (!vn_tracking_edge (pred_e))
 	return NULL;
-    }
   if (dump_file && (dump_flags & TDF_DETAILS)
       /* ???  Fix dumping, but currently we only get comparisons.  */
       && TREE_CODE_CLASS (code) == tcc_comparison)
@@ -4182,6 +4529,12 @@ vn_nary_op_insert_pieces_predicated (unsigned int length, enum tree_code code,
       fprintf (dump_file, " == %s\n",
 	       integer_zerop (result) ? "false" : "true");
     }
+  if (code == EQ_EXPR && result == boolean_true_node)
+    /* NE_EXPRs with false result are handled by inverted condition.  */
+    {
+      val_equiv_insert (ops[0], ops[1], pred_e->dest, true);
+      val_equiv_insert (ops[1], ops[0], pred_e->dest, true);
+    }
   vn_nary_op_t vno1 = alloc_vn_nary_op (length, NULL_TREE, value_id);
   init_vn_nary_op_from_pieces (vno1, length, code, type, ops);
   vno1->predicated_values = 1;
@@ -4194,26 +4547,6 @@ vn_nary_op_insert_pieces_predicated (unsigned int length, enum tree_code code,
   return vn_nary_op_insert_into (vno1, valid_info->nary, true);
 }
 
-static bool
-dominated_by_p_w_unex (basic_block bb1, basic_block bb2, bool);
-
-static tree
-vn_nary_op_get_predicated_value (vn_nary_op_t vno, basic_block bb)
-{
-  if (! vno->predicated_values)
-    return vno->u.result;
-  for (vn_pval *val = vno->u.values; val; val = val->next)
-    for (unsigned i = 0; i < val->n; ++i)
-      /* Do not handle backedge executability optimistically since
-	 when figuring out whether to iterate we do not consider
-	 changed predication.  */
-      if (dominated_by_p_w_unex
-	    (bb, BASIC_BLOCK_FOR_FN (cfun, val->valid_dominated_by_p[i]),
-	     false))
-	return val->result;
-  return NULL_TREE;
-}
-
 /* Insert the rhs of STMT into the current hash table with a value number of
    RESULT.  */
 
@@ -6863,6 +7196,8 @@ free_rpo_vn (void)
       release_ssa_name (info->name);
   obstack_free (&vn_ssa_aux_obstack, NULL);
   delete vn_ssa_aux_hash;
+  delete val_equiv_hash;
+  val_equiv_hash = NULL;
 
   delete constant_to_value_id;
   constant_to_value_id = NULL;
@@ -7241,11 +7576,11 @@ process_bb (rpo_elim &avail, basic_block bb,
 	    tree val = gimple_simplify (gimple_cond_code (last),
 					boolean_type_node, lhs, rhs,
 					NULL, vn_valueize);
+	    vn_nary_op_t vnresult = NULL;
 	    /* If the condition didn't simplfy see if we have recorded
 	       an expression from sofar taken edges.  */
 	    if (! val || TREE_CODE (val) != INTEGER_CST)
 	      {
-		vn_nary_op_t vnresult;
 		tree ops[2];
 		ops[0] = lhs;
 		ops[1] = rhs;
@@ -7264,6 +7599,21 @@ process_bb (rpo_elim &avail, basic_block bb,
 			print_gimple_stmt (dump_file, last, TDF_SLIM);
 		      }
 		  }
+		if (!val && iterate)
+		  {
+		    // Try to find equivalences in val_equiv_hash and search
+		    // again.
+		    val
+		      = find_predicated_value_by_equiv (lhs, rhs,
+							gimple_cond_code (last),
+							bb);
+		    if (val && dump_file && (dump_flags & TDF_DETAILS))
+		      {
+			fprintf (dump_file, "Found predicated value ");
+			print_generic_expr (dump_file, val, TDF_SLIM);
+			fprintf (dump_file, " by equivalence.\n");
+		      }
+		  }
 	      }
 	    if (val)
 	      e = find_taken_edge (bb, val);
@@ -7316,6 +7666,17 @@ process_bb (rpo_elim &avail, basic_block bb,
 		    if (false_e)
 		      insert_related_predicates_on_edge (icode, ops, false_e);
 		  }
+
+		/* If we got a previous condition, but no predicated
+		   value, try to record what we know from previous true & false
+		   branches on current true & false edges.  This might provide
+		   some opportunities for futher prediction.
+		   For now we only record equivalences generated by PHI nodes,
+		   that depends on the previous condition.
+		    */
+		if (iterate && vnresult && (true_e || false_e))
+		  record_equiv_from_previous_cond (vnresult, true_e, false_e,
+						   bb);
 	      }
 	    break;
 	  }
@@ -7437,6 +7798,7 @@ struct unwind_state
   vn_reference_t ref_top;
   vn_phi_t phi_top;
   vn_nary_op_t nary_top;
+  val_equiv_t equiv_top;
   vn_avail *avail_top;
 };
 
@@ -7475,6 +7837,18 @@ do_unwind (unwind_state *to, rpo_elim &avail)
       (*slot)->operands.release ();
       valid_info->references->clear_slot (slot);
     }
+  for (; last_inserted_equiv != to->equiv_top;
+       last_inserted_equiv = last_inserted_equiv->next)
+    {
+      val_equiv_t *slot;
+      slot = val_equiv_hash->find_slot_with_hash (last_inserted_equiv,
+						  last_inserted_equiv->hashcode,
+						  NO_INSERT);
+      if ((*slot)->unwind_to)
+	*slot = (*slot)->unwind_to;
+      else
+	val_equiv_hash->clear_slot (slot);
+    }
   obstack_free (&vn_tables_obstack, to->ob_top);
 
   /* Prune [rpo_idx, ] from avail.  */
@@ -7590,6 +7964,7 @@ do_rpo_vn (function *fn, edge entry, bitmap exit_bbs,
   next_constant_value_id = -1;
 
   vn_ssa_aux_hash = new hash_table <vn_ssa_aux_hasher> (region_size * 2);
+  val_equiv_hash = new hash_table <val_equiv_hasher> (5);
   gcc_obstack_init (&vn_ssa_aux_obstack);
 
   gcc_obstack_init (&vn_tables_obstack);
@@ -7600,6 +7975,7 @@ do_rpo_vn (function *fn, edge entry, bitmap exit_bbs,
   last_inserted_phi = NULL;
   last_inserted_nary = NULL;
   last_pushed_avail = NULL;
+  last_inserted_equiv = NULL;
 
   vn_valueize = rpo_vn_valueize;
 
@@ -7693,6 +8069,7 @@ do_rpo_vn (function *fn, edge entry, bitmap exit_bbs,
 	    rpo_state[idx].ref_top = last_inserted_ref;
 	    rpo_state[idx].phi_top = last_inserted_phi;
 	    rpo_state[idx].nary_top = last_inserted_nary;
+	    rpo_state[idx].equiv_top = last_inserted_equiv;
 	    rpo_state[idx].avail_top
 	      = last_pushed_avail ? last_pushed_avail->avail : NULL;
 	  }
